アプリケーションにおける CI/CD 設定

読む時間の目安: 5 分

本ページでは Docker コンテナーを利用した GitHub アクションの CI/CD パイプライン(pipeline)の設定について説明します。 新たなパイプラインを設定する前に、CI/CD のベストプラクティスとして Ben のブログ を読んでおくことをお勧めします。

本ガイドでは以下の手順を説明します。

  1. 例としてサンプルの Docker プロジェクトを用いて GitHub アクションの設定を行います。
  2. GitHub アクションのワークフローを設定します。
  3. ワークフローを最適化してプルリクエスト数と総ビルド時間を減らします。
  4. 指定したバージョンのみを Docker Hub にプッシュします。

Docker プロジェクトの設定

でははじめていきます。 本ガイドでは例として簡単な Docker プロジェクトを使います。 SimpleWhaleDemo リポジトリに Nginx の alpine イメージがあります。 このリポジトリをクローンして利用するか、あるいは独自の Docker プロジェクトを利用してください。

SimpleWhaleDemo

作業を進める前の確認として、生成したワークフローからは必ず Docker Hub に接続できることを、以下のようにして確認します。

  1. Docker ID を Secret として GitHub に追加します。 自分の GitHub リポジトリにアクセスして Settings > Secrets > New secret をクリックします。

  2. DOCKER_HUB_USERNAMEという名前の Secret を新規生成します。 そして値には自分の Docker ID を設定します。

  3. 新たにパーソナルアクセストークン(Personal Access Token; PAT)を生成します。 新たなトークン生成は Docker Hub Settings にアクセスして、New Access Token をクリックします。

  4. このトークンを simplewhaleci(simple whale ci=簡単なクジラの CI)と命名することにします。

    新規のアクセストークン

  5. そこでこのパーソナルアクセストークン(PAT)を 2 つめの Secret として GitHub Secrets 画面に追加します。 名称はDOCKER_HUB_ACCESS_TOKENとします。

    GitHub Secrets

GitHub アクションワークフローの設定

前節において PAT を生成し GitHub に追加しました。 こうすることでどのワークフローからでも Docker Hub にアクセスできるようになりました。 そこで GitHub アクションワークフローを設定して、Docker Hub 内においてイメージビルドと保存を行うようにします。 これを実施するために、以下の Docker 操作を行います。

  1. 1 つめの操作は、Docker Hub に Secret を使ってログインできるようにします。 その Secret は、上で GitHub リポジトリに保存したものを用います。
  2. 2 つめの操作は、ビルドアクションとプッシュアクションです。

この例ではプッシュも行いたいため、プッシュフラグをtrueに設定します。 そしてタグを追加して、常に最新バージョンへアクセスするように設定します。 またイメージダイジェスト値を表示することで、何がプッシュされたかがわかるようにします。

ワークフローは以下のようにして設定します。

  1. 自分の GitHub リポジトリにアクセスして Actions > New workflow をクリックします。
  2. set up a workflow yourself(ワークフローの独自設定)をクリックして、これ以降の手順で進めていきます。

まずはこのワークフローを以下のように命名します。

name: CI to Docker Hub

そしてこのワークフローをいつ動作させるかを選びます。 本例では、プロジェクトの main ブランチに対してプッシュが行われるたびに動作させるものとします。

on:
  push:
    branches: [ main ]

アクションの中では具体的にどのようなことを実現したいか(何のジョブとするか)を定めることが必要です。 そこでビルドアクションを追加して、利用可能な最新の Ubuntu インスタンス上で実行するようにします。

jobs:

  build:
    runs-on: ubuntu-latest

必要となる steps を追加することにします。 1 つめは$GITHUB_WORKSPACE配下にあるリポジトリからチェックアウトを行うことです。 これによってワークフローからのアクセスが可能になります。 2 つめは Docker Hub にログインする際の PAT とユーザー名を指定することです。 3 つめはビルド処理です。 このアクションでは、Buildx アクションの設定を通じて内部的に BuildKit を利用します。

    steps:

      - name: Check Out Repo
        uses: actions/checkout@v2

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}

      - name: Set up Docker Buildx
        id: buildx
        uses: docker/setup-buildx-action@v1

      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: ./
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest

      - name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

では一度このワークフローを実行してみます。 その後に Dockerfile を調整して、CI が実行されていること、新たなイメージの変更がプッシュされていることを確認します。

CI to Docker Hub

ワークフローの最適化

次に行うのは、ビルドキャッシュを通じて GitHub アクションワークフローの最適化を行っていきます。 ここには以下の 2 つの利点があります。

  1. ビルドキャッシュがあるとビルド時間が短縮されます。 つまりイメージすべてを毎回ダウンロードする必要がなくなります。
  2. また Docker Hub に対して行うプルの数を減らすことができます。 ただしこれを活用するためには GitHub キャッシュを有効にしておく必要があります。

そこでビルドキャッシュを使ったビルダーを設定します。 まずはビルダーに対してキャッシュ設定が必要です。 本例ではパスと鍵を指定して、GitHub キャッシュを用いるものとして保存します。

      - name: Cache Docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-

そしてビルダーとビルドキャッシュをアクションファイルの冒頭に加えたら、ビルドおよびプッシュの手順に必要な属性を追加します。 それは以下のものです。

buildx 処理の出力をビルダーにおいて利用するように設定します。 そして上で設定したキャッシュを用いて、保存と抽出を行うようにします。

      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          context: ./
          file: ./Dockerfile
          builder: ${{ steps.buildx.outputs.name }}
          push: true
          tags: ${{ secrets.DOCKER_HUB_USERNAME }}/simplewhale:latest
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache
      - name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

そしてワークフローを再度実行して、ビルドキャッシュが利用されていることを確認してください。

Docker Hub へのタグつきバージョンのプッシュ

前節においては Docker プロジェクトに対して GitHub アクションワークフローを設定する方法を見てきました。 そしてビルダーがビルドキャッシュを利用するように設定することで、このワークフローの最適化を行いました。 ここからさらに改善していきます。 具体的にはタグつきバージョンを利用するものであり、これを用いて master へのコミットにおいて異なる処理を実現します。 つまり Docker Hub に対して最新バージョンがコミット対象となるのではなく、特定バージョンだけをプッシュするようにします。

この方法を活用して、たとえばコミットがローカルレジストリに向くようにして、毎日のテスト用として利用することが考えられます。 こうしておくと、Docker Hub に対してリリースするタグつきバージョンは温存しつつ、テストを続行していくことができます。

これを実現するには以下の 2 つの手順で進めます。

  1. GitHub ワークフローを修正して、Docker Hub において特定タグに対するコミットのみをプッシュするようにします。
  2. 最新コミットを GitHub レジストリ内のイメージとして保存するように GitHub アクションファイルを設定します。

まず特定のタグが存在しているものとして、それまでの GitHub ワークフローにおいて Docker Hub へのプッシュだけを行うように修正します。 たとえば以下のようになります。

on:
  push:
    tags:
      - "v*.*.*"

上のようにすると CI コマンドからはV.n.n.n.というタグづけを行っているコミットのみが処理されます。 確認してみましょう。 たとえば以下のようなコマンドを実行します。

$ git tag -a v1.0.2
$ git push origin v1.0.2

GitHub にアクセスしてアクションを確認します。

タグづきバージョンのプッシュ

では次に 2 つめの GitHub アクションファイルを設定します。 これは最新コミットを、 GitHub レジストリ内のイメージとして保存するようにします。 こういった状況を利用したくなるのは、以下のような状況です。

  1. 日々のテストあるいは定期的なテストを実行する場合。
  2. 開発者間で開発途上イメージを共有する場合。

では 1 つめの GitHub アクションをコピーして、すべてのプッシュに対するロジックを追加し直します。 ここでワークフローファイルが 2 つになりました。 それまでのワークフローと、今から設定していく新しいワークフローです。 そこで今回は Docker Hub ログインを変更して、GitHub コンテナーレジストリログインとします。

        if: github.event_name != 'pull_request'
        uses: docker/login-action@v1
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

GitHub Container Registry に対して認証を行うには、十分なセキュリティを得るために GITHUB_TOKEN を用いてください。

コンテナー設定において、レジストリに対する manage write and read access of GitHub Actions(GitHub Action の読み書き権限の管理)を行う必要があります。

また appropriate scopes(適切なスコープ)を使って、パーソナルアクセストークン (personal access token; PAT) を利用することもできます。 イメージへのタグづけを変更することを忘れないでください。 以下の例においては「latest」のまま、これを唯一のタグとします。 必要に応じてタグ追加を行ってください。

  tags: ghcr.io/${{ github.repository_owner }}/simplewhale:latest

タグつきイメージの更新

ここまでに 2 つの異なるフローを作り出しました。 1 つは master への変更用、もう 1 つはプルリクエスト用です。 次はこれまで生成してきたフローを変更して、プルリクエストのプッシュ先を Docker Hub ではなく GitHub レジストリにすることが必要です。

次のステップ

本節では、既存の Docker プロジェクトに対しての GitHub アクションワークフローの設定方法を学びました。 次にワークフローを最適化して、ビルド時間の改善とプルリクエスト数の削減を行いました。 そして特定のバージョンのみを Docker Hub にプッシュするようにしました。 これにより最新タグに対しての日々のテストを設定したり、各 PR を確認したりすることができます。 あるいはそのタグを利用して、何かの手順に活用することもできます。 イメージ内のタグを Git のタグとして利用することもできます。

そこでこのアプリケーションをデプロイすることにしましょう。 詳しい手順は以下です。

アプリのデプロイ

フィードバック

本トピック改善のためにフィードバックをお寄せください。 お気づきの点があれば Docker Docs の GitHub リポジトリに issue をあげてください。 あるいは PR の生成 により変更の提案を行ってください。


CI/CD, GitHub Actions, NodeJS, local, development