GitHub Actions で docker build して ECR に push する

22407 ワード

ちょくちょく詰まりポイントがあったので、備忘録として。

IAMリソースの作成

まず、GitHub Actions からAWSリソースを操作するための認証を行う必要があります。

最近追加された、GitHub Actions の OpenID Connect (OIDC) を使うのがよさそう。

参考: https://zenn.dev/miyajan/articles/github-actions-support-openid-connect

公式テンプレートを参考に、CloudFormation でIAMリソースを作成していきます。

cfn-iam.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: IAM resource
Parameters:
  GitHubOrg:
    Type: String
    Default: hukurouo # GitHub のアカウント名
  RepositoryName:
    Type: String
    Default: github-actions-ecr-push-test # リポジトリ名 
  AccountId:
    Type: String

Resources:
  GitHubActionsRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: github-actions-role
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Action: sts:AssumeRoleWithWebIdentity
            Principal:
              Federated: !Ref GithubOidc
            Condition:
              StringLike:
                token.actions.githubusercontent.com:sub: !Sub repo:${GitHubOrg}/${RepositoryName}:*
      Policies:
        - PolicyName: ECRTestPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - ecr:GetAuthorizationToken
                Resource: '*'
              - Effect: Allow
                Action:
                  - ecr:GetDownloadUrlForLayer
                  - ecr:BatchGetImage
                  - ecr:InitiateLayerUpload
                  - ecr:PutImage
                  - ecr:UploadLayerPart
                  - ecr:ListImages
                  - ecr:CompleteLayerUpload
                  - ecr:BatchCheckLayerAvailability
                Resource: !Sub arn:aws:ecr:ap-northeast-1:${AccountId}:repository/${RepositoryName}
  GithubOidc:
    Type: AWS::IAM::OIDCProvider
    Properties:
      Url: https://token.actions.githubusercontent.com
      ClientIdList: 
        - sts.amazonaws.com
      ThumbprintList:
        - 6938fd4d98bab03faadb97b34396831e3780aea1

必要な権限については、多分これが最小構成だと思います。

  • 認証
    • ecr:GetAuthorizationToken
  • イメージの取得(キャッシュ利用時に使う)
    • ecr:GetDownloadUrlForLayer
    • ecr:BatchGetImage
  • イメージの更新
    • ecr:InitiateLayerUpload
    • ecr:PutImage
    • ecr:UploadLayerPart
    • ecr:ListImages
    • ecr:CompleteLayerUpload
    • ecr:BatchCheckLayerAvailability

また、ThumbprintListは書き変わることがあるらしいです。上記の公式テンプレートに倣えば基本は大丈夫そうですが、留意しておく必要はありそうです。

ECRリポジトリの作成

こちらも CloudFormation で作ってしまいます。

cfn-ecr.yml
AWSTemplateFormatVersion: "2010-09-09"
Description: ECR resource
Parameters:
  ServiceName:
    Type: String
    Default: github-actions-ecr-push-test

Resources:
  ECR:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName: !Ref ServiceName
      ImageScanningConfiguration:
        ScanOnPush: true

Workflow の作成

コード全文はこのような感じです。

build.yml
name: code build
on: 
  workflow_dispatch:
  push:
    branches:
      - main
      - 'feature/**'
jobs:
  build:
    name: Build image
    env:
      SERVICE_NAME: github-actions-ecr-push-test
    runs-on: ubuntu-latest
    # These permissions are needed to interact with GitHub's OIDC Token endpoint.
    permissions:
      id-token: write
      contents: read
    steps:
    - name: Checkout
      uses: actions/checkout@v2

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

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

    - name: Configure AWS credentials from Test account
      uses: aws-actions/configure-aws-credentials@v1
      with:
        role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-role
        aws-region: ap-northeast-1

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - uses: docker/build-push-action@v2
      id: build-image
      with:
        push: true
        file: deploy/development/Dockerfile
        tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.SERVICE_NAME }}:latest
        cache-from: type=local,src=/tmp/.buildx-cache
        cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max

    - name: Move cache
      run: |
        rm -rf /tmp/.buildx-cache
        mv /tmp/.buildx-cache-new /tmp/.buildx-cache

詳しく見ていきます。

on: 
  workflow_dispatch:
  push:
    branches:
      - main
      - 'feature/**'

まず on: ブロックでは、GitHub Actions の起動条件を設定します。featureブランチにpushされたら自動で job が実行されるようなっており、feature ブランチに何かしらの更新をpushしたとき、自動で開発環境に反映させるような流れをイメージしています。

ここでmainブランチも指定しているのは、GitHub Action のキャッシュには「スコープ」という概念があり、デフォルトブランチでキャッシュを残しておく必要があるためです。

参考: https://zenn.dev/mallowlabs/articles/github-actions-cache-scope

workflow_dispatch: はUIから手動実行できるようにするオプションです。

以下の jobs: build: の処理については、docker/build-push-action@v2 という上手いことキャッシュを利用しながら build + push を行ってくれる GitHub Action に乗っかっている感じです。

キャッシュ周りは local-cache パターンのテンプレートをECR用に書き換えています。

また、AWS_ACCOUNT_ID については Secrets から読み込むようにしているので、

    - name: Configure AWS credentials from Test account
      uses: aws-actions/configure-aws-credentials@v1
      with:
        role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-role
        aws-region: ap-northeast-1

リポジトリの Settings 欄から設定しておく必要があります。

おわりに

キャッシュを利用することで、bundle install などで5分ほどかかっていたビルド時間を短縮することができました。