RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

【CI】GitHub Actions + AWS CodeBuild + Amazon ECRでCIパイプラインを構築してみた

はじめに

皆さんはじめまして! 今回 Rakus Developers Blog 初投稿となる株式会社ラクスの大原(kzak_24)と申します。
インフラ開発部 SRE課に所属しております。
どうぞよろしくお願いいたします。
先日、アサインされているプロジェクトにて、CIツールにGitHub Actionsを採用する運びとなり、AWSのサービスと連携しての技術検証を終えました。結論、想像していたよりも簡単にCIパイプラインを構築することができ、非常に便利だと感じました。そこで今回は、検証内容や実装ポイントなどをご紹介していきたいと思います。

情報量が多く、すべてをお伝えすることはできませんが、GitHub ActionsとAWSを使って、CIを始めたいという方のご参考となれば幸いです。

目次

GitHub Actions とは?

GitHubのドキュメントには次のように記載されています。

GitHub Actions is a continuous integration and continuous delivery (CI/CD) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository, or deploy merged pull requests to production.

Google先生による翻訳

GitHub Actionsは、継続的インテグレーションおよび継続的デリバリー(CI / CD)プラットフォームであり、ビルド、テスト、およびデプロイのパイプラインを自動化できます。リポジトリへのすべてのプルリクエストをビルドしてテストするワークフローを作成したり、マージされたプルリクエストを本番環境にデプロイしたりできます。

簡単に言うと、GitHubリポジトリで起きるイベントをトリガーにしたCI/CDパイプラインを構築できるプラットフォームということになります。

GitHub Actionsには以下のような構成要素があります。

  • Workflow
    • GitHub Actionsの一連の処理をまとめた単位。1つのファイルで1つのワークフローを定義できる
  • Event
    • ワークフローを実行するトリガー
  • Job
    • ランナー上で実行される処理の単位。ジョブが分かれると別の環境で処理が実行される
  • Step
    • ジョブが実行する処理の集合。
  • Actions
    • ワークフローの最小構成要素。コマンドの実行やアクションという一連の処置がまとまったライブラリのようなものを実行することができる
  • Runner
    • 処理を実行する環境

検証環境の構成

今回の検証では以下のような構成でCIパイプラインを構築しました。

開発者のPR作成から、最終的にAmazon ECRにコンテナイメージをプッシュするところまで、このパイプラインでは実行します。

CIは以下のような流れで実行されます。

  1. 開発者がアプリケーションリポジトリでPRを作成
  2. PRマージというEventを検知し、CIが起動
  3. Test Workflowが実行される
  4. Build Workflowが実行され、AWS CodeBuildが実行される
  5. CodeBuildで作成されたコンテナイメージをAmazon ECRにプッシュ

事前準備

それでは、CIを動作させる為の準備をしていきましょう。
今回必要なものは以下の通りです。

※ 今回は以下の項目の作成、取得については完了済みという前提で、説明を省略します。

ワークフローの定義ファイルの作成

  1. ワークフローを実行するアプリケーションのルートディレクトリに.github/workflows/というディレクトリを作成します。
  2. test_workflow.yamlbuild_workflow.yaml.github/workflows/配下に作成します。

GitHub ActionsのワークフローはYAML形式で定義し、.github/workflows/に配置することで、ワークフローとして管理されます。

個人用アクセストークンの取得

GitHub Actionsの中でリポジトリに特定の操作を行う際、アクセストークンが必要になる為、個人用アクセストークンを取得します。(今回は後述するrepository_dispatchという処理で使用します)。

  1. まずGitHubのページにアクセスし、ページ右上隅のプロフィールをクリックし、「Settings」をクリックします。
  2. スライダー最下の「Developer settings」をクリックします。
  3. スライダーから、「Personal access tokens」をクリックします。
  4. 「Generate new token」をクリックします。
  5. パスワード入力を求められるので、入力します。
  6. 作成するアクセストークンの説明や有効期限、権限スコープを設定します。
    • 今回はリポジトリに関する操作権限をスコープに設定します。
  7. ページ下部の「Generate token」をクリックします。
  8. 作成したアクセストークンの値が表示されます。
    • このタイミングでしか表示されないので、注意してください。
  9. 再度アクセストークンのページにアクセスすると、トークンが作成されていることが確認できます。
    • 有効期限の延長や権限スコープの変更もここから行えます。

リポジトリのSecrets作成

GitHub Actionsの中で使用するAWSのクレデンシャルやアクセストークンなどの情報は、Secretsという機能で暗号化したシークレットとして使用します。 取得したAWSクレデンシャルと個人用アクセストークンをSecretsに登録します。

  1. リポジトリのメインページにアクセスします。
  2. リポジトリ名の下の「Settings」をクリックします。
  3. スライダーから「Secrets」をクリックし、「Actions」をクリックします。
  4. シークレットの管理ページが表示されるので、「New repository secret」をクリックします。
  5. Nameにシークレットの名前(任意)を設定し、Valueに取得したアクセスキーIDの値を入力します。
  6. 「Add Secrets」をクリックし、Repository secretsに作成したシークレットがあることを確認します。
  7. 同様にシークレットアクセスキー、個人用アクセストークンも登録します。

workflowファイル

今回の検証で作成したワークフローファイルは以下のようになっています。

test_workflow.yaml

name: Test Workflow
on:
  pull_request:
    branches:
      - main
    types:
      - closed

jobs:
  Lint-Test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v2
      - name: Run Lint
        run: |
         docker-compose up -d --remove-orphans
         docker-compose exec -T next npm run lint
         echo "Lint succees!."
      - name: Run Test
        run: |
         docker-compose exec -T next npm run test
         echo "Test succees!."
      - name: Dispatch Build Workflow
        uses: peter-evans/repository-dispatch@v1
        with:
          token: ${{secrets.REPO_ACCESS_TOKEN}}
          repository: 検証用のリポジトリ
          event-type: ci-sample

build_workflow.yaml

name: Build Workflow
on:
  repository_dispatch:
    types:
      - ci-sample

env:
  REGION: ap-northeast-1
  PROJECT_NAME: example_CI_build_project
  IMAGE_TAG: ci-sample

jobs:
  Build:
    runs-on: ubuntu-latest
    steps:
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
          aws-region: ${{ env.REGION }}
      - name: Run CodeBuild
        uses: aws-actions/aws-codebuild-run-build@v1
        with:
          project-name: ${{ env.PROJECT_NAME }}
          env-vars-for-codebuild: IMAGE_TAG
        env:
          IMAGE_TAG: ${{ env.IMAGE_TAG }}

それでは、中身について説明していきます。

Eventの定義

まずは、ワークフローを実行するトリガーとなるイベントの定義です。

test_workflow.yaml(一部抜粋)

on:
  pull_request:
    branches:
      - main
    types:
      - closed

GitHub Actionsでは、onセクションにイベントを定義します。
上記のように定義することで、mainブランチへPRをマージしたことをトリガーにワークフローを実行できます。
pull_requestの他にも、pushや手動実行を行うworkflow_dispatchというイベントも定義できます。
また、repository_dispatchというイベントを定義することで、あるワークフローから別のワークフローを実行するイベントを送信できます。

test_workflow.yaml(一部抜粋)

- name: Dispatch Build Workflow
   uses: peter-evans/repository-dispatch@v1
     with:
       token: ${{secrets.REPO_ACCESS_TOKEN}}
       repository: 検証用のリポジトリ
       event-type: ci-sample

build_workflow.yaml(一部抜粋)

on:
  repository_dispatch:
    types:
      - ci-sample

テスト用ワークフローの中でci-sampleというパラメーターが設定されたrepository_dispatchイベントをビルド用ワークフローに送信しています。
指定されたイベントが送信されたことをトリガーにビルド用のワークフローが実行されるようになっています。

また、usesを使用することで、アクションという一連の処置をまとめたライブラリのような機能を使用できます。
テスト用ワークフローではpeter-evans/repository-dispatchというアクションを使用して、repository_dispatchイベントを送信しています。

Jobの定義

続いて、ジョブの定義について見ていきましょう。

test_workflow.yaml(一部抜粋)

jobs:
  Lint-Test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v2
      - name: Run Lint
        run: |
         docker-compose up -d --remove-orphans
         docker-compose exec -T next npm run lint
         echo "Lint succees!."

ジョブはjobsセクションに定義します。
Lint-Testでジョブの名前を定義しており。ジョブは複数作成することが可能です。 runs-onでそのジョブが実行されるランナーの仮想環境を指定できます。
stepsセクションでジョブが実行するステップを定義します。stepsnameでそれぞれ名前を指定することができ、順次実行されます。
usesrunで実行するアクションやコマンドを定義しています。

AWSサービスとの連携

続いて、AWSサービスと連携する処理について見ていきましょう。

build_workflow.yaml(一部抜粋)

- name: Configure AWS Credentials
  uses: aws-actions/configure-aws-credentials@v1
    with:
      aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }}
      aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }}
      aws-region: ${{ env.REGION }}

ここでは、aws-actions/configure-aws-credentialsというアクションを使用して、GitHub ActionsのランナーからAWSサービスを操作できるように、AWSクレデンシャルを設定しています。

secrets.AWS_ACCESS_KEYリポジトリに設定したSecretsから値を取得しています。

build_workflow.yaml(一部抜粋)

- name: Run CodeBuild
  uses: aws-actions/aws-codebuild-run-build@v1
    with:
      project-name: ${{ env.PROJECT_NAME }}
      env-vars-for-codebuild: IMAGE_TAG
    env:
      IMAGE_TAG: ${{ env.IMAGE_TAG }}

ここでは、aws-actions/aws-codebuild-run-buildというアクションを使用して、GitHub ActionsからAWS CodeBuildを実行しています。

また、env-vars-for-codebuild環境変数を渡すと、CodeBuild側の環境変数として設定してくれるので、ワークフローで定義した値がコンテナイメージにタグ付けされます(今回の場合、ci-sampleがタグ付けされます)。

buildspec

ワークフローを実行する前に、buildspecについて、少し説明します。
buildspecとはCodeBuildで実行する上でのビルド仕様のことです。
buildspec.yamlを作成し、プロジェクトのルートディレクトリに配置することで、CodeBuildは自動でbuildspec.yamlを検知し、定義された内容通りに処理を実行してくれます。


今回の検証で作成したbuildspecファイルは以下の通りになります。

buildspec.yaml

version: 0.2
env:
  parameter-store:
    DOCKER_USER: dockerhub-user
    DOCKER_TOKEN: dockerhub-token
phases:
  install:
    runtime-versions:
      docker: 19
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region ${AWS_DEFAULT_REGION} | docker login --username AWS --password-stdin ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com
      - echo $DOCKER_TOKEN | docker login -u $DOCKER_USER --password-stdin
  build:
    commands:
      - echo Build start
      - echo Building the Docker image...
      - docker-compose build
      - docker tag リポジトリ名_next:latest ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}
  post_build:
    commands:
      - echo Build completed
      - echo Pushing the Docker image...
      - docker push ${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_DEFAULT_REGION}.amazonaws.com/${IMAGE_REPO_NAME}:${IMAGE_TAG}

少し省略しますが、各セクションがそれぞれ何をしているか、簡単に説明します。

  • pre_build: ECRにログイン
  • build: docker-composeでコンテナイメージをビルドし、ワークフローから渡された値でタグ付け
  • post_build: buildセクションで作成したコンテナイメージをECRにプッシュ

ワークフローの実行

それではワークフローを実行し、ECRプッシュまで実行できているか確認していきましょう!
今回はリポジトリページ上で確認していきます。

テスト用ワークフローを実行

ますはテスト用ワークフローについて確認していきましょう。

  1. mainブランチに対して、PRを作成します。

  2. PRをマージします。

  3. リポジトリのトップページから、「Actions」をクリックします。

  4. Workflowの一覧から、「Test workflow」をクリックします。

    • mainブランチへのPRマージをトリガーにワークフローが実行されていることが確認できます。
    • 赤枠部分(実行中のワークフロー)をクリックします。
  5. 実行中のジョブ一覧が表示されるので、赤枠部分(実行中のジョブ)をクリックします。
    • 経過時間やステータスなどを確認できます。
  6. 実行中のStep一覧が表示されます。
    • Stepごとにコマンドやアクションのログを確認できます。
    • Lintとテストが正常に完了していることが確認できます。
    • Stepがすべて正常に終了すると、緑色のチェックが入ります。

Dispatch Build WorkflowというStepで、ビルド用のワークフローが実行されるトリガーとなるrepository_dispatchイベントを送信している為、Test workflowの実行が終了すると、Build workflowが自動的に実行されます。

ビルド用ワークフローを実行

続いてビルド用ワークフローについて確認していきましょう。

前述した通り、ビルド用のワークフローはテスト用のワークフローの中で、repository_dispatchイベントが送信されることをトリガーに実行されます。

処理の確認の流れは先ほどのテスト用ワークフローと同様です。

  1. リポジトリのトップページから、「Actions」をクリックします。

  2. Workflowの一覧から、「Build workflow」をクリックします。

    • repository_dispatchイベントをトリガーにビルド用ワークフローが起動していることが確認できます(Repository dispatch triggered と記載されていますね)
    • 赤枠部分(実行中のワークフロー)をクリックします。
  3. 実行中のジョブ一覧が表示されるので、赤枠部分(実行中のジョブ)をクリックします。
  4. 実行中のStep一覧が表示されます。
    • Run CodeBuildのログからCodeBuildが実行されていることが確認できます(GitHub ActionsからCodeBuildのログを確認できます)。
    • ECRへのプッシュが完了していることが確認できます
  5. ECRにプッシュされたコンテナイメージを確認します。
    • 想定通り、タグにci-sampleが設定されたイメージがプッシュされていることを確認できました!

終わりに

最後までご覧いただきありがとうございます。
今回はGitHub ActionsとAWSサービスを連携したCIパイプラインの構築について、ご紹介しました。
CIパイプラインの整備は開発サイクルの高速化に繋がるので、今後もしっかりと学んでいきたいと思います。

GitHub Actionsでは他にもPR作成やコミットの自動化など、便利な機能がたくさんあるので、今後も記事の発信を継続していきたいです

参考文献

  

◆TECH PLAY
techplay.jp

◆connpass
rakus.connpass.com

Copyright © RAKUS Co., Ltd. All rights reserved.