CI/CDサービス「GitHub Actions」で「コンテナビルド」を自動化してみよう

2025年9月2日(火)
田中 智明
第12回の今回は、「GitHub Actions」によるコンテナビルドと「ghcr」への自動プッシュ、「Trivy」によるセキュリティスキャンについて解説します。

はじめに

現代の開発現場では、コンテナを活用して開発を効率化する動きが活発です。アプリの動作環境をコンテナにパッケージ化し、それを開発者全体で使用すれば環境の差異に悩まされることがなくなります。

しかし、コンテナは「1度作ればそれで終わり」というものではありません。コンテナ内で使用しているライブラリやミドルウェアのアップデートなど、誰かがメンテナンスを行う必要が出てきます。この作業を手動で行う場合は人的ミスの温床となることでしょう。また、チーム開発では「誰がビルドするか」「どのタイミングでリリースするか」といった調整コストも発生します。

こうした課題を解決するのが、「CI/CD」(継続的インテグレーション/継続的デリバリー)による自動化です。コードの変更を自動で検知し、ビルド・テスト・デプロイまでを自動化することで、開発者はコード品質の向上に集中できるようになります。

本記事では、GitHubが提供するCI/CDサービス「GitHub Actions」を使ってコンテナビルドを自動化し、GitHub Container Registry(ghcr)へのプッシュまでを実践します。GitHub Actionsを活用すれば、外部のCI/CDサービスを導入することなく、GitHub上で完結したワークフローを構築できます。

コードをプッシュするだけで自動的にコンテナイメージが作成・配布される環境を構築できるようになることを目指します。

GitHub Actionsとは

GitHub Actionsは、GitHubが提供するワークフロー自動化プラットフォームです。リポジトリ内で発生するイベント(プッシュ、プルリクエストなど)や決められた時刻をトリガーに、あらかじめ定義したジョブを実行できます。

基本的な構成要素と名称

GitHub Actionsを理解するには、以下の基本概念を押さえておく必要があります。

ワークフロー(Workflow)
.github/workflows/ディレクトリ内のYAMLファイルで定義される自動化処理全体のことです。「どのイベントで」「どんな処理を実行するか」を記述したレシピのような存在です。

ワークフローをトリガーするイベントについては、公式ドキュメントに詳しく記載されています。

ジョブ(Job)
1つのワークフロー内に複数のジョブを定義でき、デフォルトでは並列実行されます。依存関係を設定すれば順次実行も可能です。

ステップ(Step)
ジョブ内で実行される個々のタスクです。シェルコマンドの実行や、Actionsの呼び出し(後述)などが該当します。

アクション(Actions)
再利用可能な処理のパッケージです。GitHub Marketplaceで公開されているものや、自作のActionを利用できます。例えば「チェックアウト」「Docker ビルド」「通知送信」といったよくある処理はActionとして提供されているため、自分で再実装することなく手軽に機能を利用できます。

主な特徴

1. GitHubとの統合
リポジトリと緊密に連携し、コードの変更に応じて自動実行されます。外部のCI/CDサービスを導入する必要がなく、GitHub上で完結できます。また、GitHubのサービス(ghcrやIssue、PRなど)を簡単に使用できるのも特徴です。

2. 豊富なActionsエコシステム
GitHub Marketplaceには数千ものActionが公開されており、Docker、AWS、Kubernetesなど様々な技術との連携が簡単に行えます。

3. 柔軟な実行環境
Ubuntu、Windows、macOSの実行環境を選択でき、コンテナ上での実行も可能です。

GitHub Actionsのはじめ方

GitHub Actionsを有効にする

GitHub Actionsは、GitHubリポジトリで自動的に利用可能となっていますが、組織やリポジトリの設定によっては手動で有効化が必要な場合があります。

1. リポジトリでの有効化

  • GitHubリポジトリのページで「Settings」タブをクリック
  • 左側のメニューから「Actions」→「General」を選択
  • 「Actions permissions」で以下のいずれかを選択:
    • Allow all actions and reusable workflows(すべてのActionを許可)
    • Allow OWNER, and select non-OWNER, actions and reusable workflows(リポジトリ所有者のActionを許可し、非所有者のActionは選択的に許可)

GitHub Actionsの設定方法については、公式ドキュメントで詳しく説明されています。

2. 組織レベルでの設定
組織のオーナーの場合、組織全体でのActions設定も確認できます:

  • 組織のSettingsページで「Actions」→「General」を選択
  • リポジトリごとの権限設定を確認・変更

3. 初回実行の確認
新しいリポジトリでは、初回のワークフロー実行時に確認画面が表示される場合があります。「I understand my workflows, go ahead and enable them」ボタンをクリックして有効化します。

ワークフローファイルの配置

GitHub Actionsを使うには、リポジトリの.github/workflows/ディレクトリにYAMLファイルを配置します。ファイル名は任意ですが、.ymlまたは.yaml拡張子が必要です。

複数のワークフローを用意して、条件によって使い分けることもできます。また、あるワークフローから別のワークフローを呼び出すこともできるため、同じような処理は1つのワークフローにまとめておくのも良いでしょう。

基本的なワークフロー例

まずは、シンプルなワークフローから始めてみましょう。

.github/workflows/hello.yaml
name: Hello World
on:
  push:
    branches:
      - main

jobs:
  hello:
    runs-on: ubuntu-latest
    steps:
      - name: Say hello
        run: echo "Hello, GitHub Actions!"

この例では、mainブランチへのpushをトリガーに「Hello, GitHub Actions!」を出力するだけのシンプルなジョブが実行されます。

実行結果は、GitHub上のActionsタブから確認できます。

トリガーの種類

GitHub Actionsでは、様々なイベントをトリガーにできます。

  • push: 指定ブランチへのプッシュ
  • pull_request: プルリクエストの作成・更新
  • schedule: 定期実行(cron形式)
  • workflow_dispatch: 手動実行
  • workflow_call: 別のワークフローから呼び出す

コンテナをビルドしてみよう

それでは実際にコンテナをビルドして、GitHub Container Registry(ghcr)にプッシュするワークフローを作成してみましょう。

サンプルアプリケーションの準備

まず、簡単なWebアプリケーションを用意します。

app.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from GitHub Actions!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)
requirements.txt
flask
Dockerfile
FROM python:3.12-slim AS build
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt -t .
COPY . .

FROM python:3.12-slim
WORKDIR /app
COPY --from=build /app /app
RUN useradd -m appuser
USER appuser
CMD ["python", "app.py"]

GitHub Actionsワークフローの作成

次に、コンテナビルドとghcrへのプッシュを行うワークフローを作成します。

.github/workflows/build.yml
name: Build and Push Container

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read      # リポジトリのコードを読み取るため
      packages: write     # GitHub Container Registryへプッシュするため

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

ワークフローの解説

1. トリガー設定
mainブランチへのプッシュとプルリクエストで実行されます。

2. 権限設定
packages: write権限により、ghcrへのプッシュが可能になります。

3. 認証
GITHUB_TOKENを使ってghcrにログインします。これは自動的に提供される認証トークンです。

4. メタデータ抽出
ブランチ名やコミットハッシュを基に、適切なタグを自動生成します。

5. ビルド・プッシュ
Dockerイメージをビルドし、生成されたタグでghcrにプッシュします。

ポイント1: アクションの活用
各ステップでuses: <owner>/<repo>@<version>を定義しています。これはアクションと呼ばれ、ある処理をパッケージ化したものです。ghcrへのログインやコンテナのビルド処理は1から全てを手書きすると大変ですが、アクションを使うことで簡潔にステップを記述できます。

ポイント2: ステップ間でのデータ受け渡し
ステップ3(33行目)のExtract metadataid: metaを指定しています。これは、別のステップからこのステップの実行結果を参照するためのステップ識別子です。ステップ4のBuild and push Docker imageから${{ steps.meta.outputs.tags }}${{ steps.meta.outputs.labels }}を使って参照しています。

実行結果の確認

ワークフローを実行すると、Actions画面でビルドログを確認できます。

成功すると、リポジトリのPackagesタブにコンテナイメージが表示されます。

イメージは、以下のような形式でpullできます:

docker pull ghcr.io/username/repository:main

セキュリティスキャンの追加

コンテナイメージのセキュリティを向上させるため、脆弱性スキャンをワークフローに組み込むことができます。以下は、Trivyを使用したスキャンの例です。

.github/workflows/build-with-scan.yml
name: Build, Scan and Push Container

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    permissions:
      contents: read          # リポジトリのコードを読み取るため
      packages: write         # GitHub Container Registryへプッシュするため
      security-events: write  # セキュリティスキャン結果をアップロードするため

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=sha,prefix={{branch}}-

      - name: Build Docker image
        uses: docker/build-push-action@v5
        id: build
        with:
          context: .
          load: true          # イメージをローカルに保存(スキャン用)
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@v0.32
        with:
          image-ref: ${{ steps.build.outputs.imageid }}
          format: 'sarif'
          output: 'trivy-results.sarif'

      - name: Upload Trivy scan results to GitHub Security tab
        uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: 'trivy-results.sarif'

      - name: Push Docker image
        if: github.event_name != 'pull_request'
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

ワークフローが成功すると、Securityタブにコンテナのセキュリティ情報がアップロードされます。

「View alerts」にこのコンテナが抱える脆弱性の一覧が記載されるので、問題を確認して修正可能な部分から順次修正するのが良いでしょう。

スキャンワークフローの特徴

1. 段階的ビルド
イメージを1度ローカルにビルドしてスキャンを実行します。

2. セキュリティタブ連携
スキャン結果はGitHubのSecurityタブに表示され、脆弱性の詳細を確認できます。

3. プルリクエスト対応
プルリクエストではスキャンのみ実行し、プッシュはスキップします。

おわりに

GitHub Actionsを使うことで、コードの変更と同時にコンテナイメージの自動ビルド・配布が実現できました。これにより、手作業でのビルドやデプロイ作業が不要となり、開発効率の大幅な向上が期待できます。

今回紹介した内容は基本的なものですが、実際のプロジェクトではテストの実行、セキュリティスキャン、複数環境への自動デプロイなど、より高度なワークフローを構築することも可能です。

次回は、いよいよAWSの操作に進んでいきます。AWS ECRを構築し、コンテナイメージをプッシュするところまでを解説していきます。

日本仮想化技術株式会社
ソーシャルゲーム業界で10年間インフラエンジニアとして活動し、現在は日本仮想化技術でOpsエンジニアを担当。DevOps支援サービス「かんたんDevOps」では仕組み作りや導入支援、技術調査などを行っている。

連載バックナンバー

システム開発技術解説
第12回

CI/CDサービス「GitHub Actions」で「コンテナビルド」を自動化してみよう

2025/9/2
第12回の今回は、「GitHub Actions」によるコンテナビルドと「ghcr」への自動プッシュ、「Trivy」によるセキュリティスキャンについて解説します。
システム開発技術解説
第11回

Ops視点で見直す「コンテナビルド」の落とし穴とベストプラクティス

2025/8/5
第11回の今回は、Ops視点から見たコンテナビルドの落とし穴とベストプラクティスを解説します。
システム開発技術解説
第10回

「Docker」を使ってアプリケーションをパッケージングしよう

2025/7/15
第10回の今回は、「Docker」を使ってアプリケーションをパッケージングする方法について、基本からステップバイステップで解説します。

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています