コンテナイメージのビルド&プッシュと、AWS Serverless Application Model (SAM)のビルド&デプロイを、GitHub Actionsで設定してみた

コンテナイメージのビルド&プッシュと、AWS Serverless Application Model (SAM)のビルド&デプロイを、GitHub Actionsで設定してみた

GitHub Actionsで複数システムのCI/CDを構築したので、紹介しています。再利用可能なワークフローを活用し、コンテナのビルド・プッシュ、SAMによるデプロイ、ECSサービス更新を自動化する実装・設定方法をまとめました。
Clock Icon2025.04.30

コンテナとCI/CDをGitHub Actionsで設定したい

おのやんです。

みなさん、CI/CD設定してますか?(挨拶

今回、同一のGitHub リポジトリで管理している複数システムのCI/CDを、GitHub Actionsで設定する機会がありました。GitHub Actionsのワークフローファイルはそこまで複雑なロジックもなく、設計図通りに書けるイメージでしたが、意外と動作確認などに手こずりました。また、似たような実装をする際のテンプレートも欲しいな、という気持ちにもなりました。

ということで、「最低限これを設定すれば動くようになるだろう」というワークフローファイルを、本ブログにて残しておこうと思います。

なお、今回はGitHub Actionsの記法などには言及せず、全体的な視点でお話ししようと思います。実際に筆者がGitHub Actionsをキャッチアップする際にお世話になった記事を載せておきますので、よければご確認ください。

https://zenn.dev/praha/articles/9e561bdaac1d23

https://zenn.dev/kiwichan101kg/articles/2d6850ff72bc98

IAMロールは既に作ってあるものとします

コードブロック中に出てくる${{ inputs.env-name }}-role-github-actions-oicdというIAMロールは、こちらのブログを参考に作成したOICD用IAMロールになりますので、ご留意いただければと思います。

https://dev.classmethod.jp/articles/setup-aws-sam-deployment-environment-using-github-actions/

GitHub Actionsワークフロー構造

GitHub Actionsのワークフローファイルは、今回次のように配置しました。
GitHub Actionsの再利用可能なワークフロー(actionsディレクトリ配下)を用いて、メインのワークフロー(workflowsディレクトリ配下)を実行する際に、メインのワークフローから再利用可能なワークフローをそれぞれ呼び出してしています。

├── actions
│   ├── container
│   │   └── action.yaml
│   ├── sam
│   │   └── action.yaml
│   └── update-ecs-service
│       └── action.yaml
└── workflows
    ├── deploy-system1-to-dev.yaml

同様の仕組みは次のブログも参考にしています。

https://zenn.dev/tetoteto/articles/github_workflow_call

各ワークフローファイルの紹介

それぞれのワークフローは、再利用可能なワークフローを順番に呼び出すことで構成されています。

今回は、system1というプロジェクトの中に、system1-component1(Amazon ECS(以下、ECS)上で動作しているシステム)と、system1-component2(AWS Lambda(以下、Lambda)上で動作しているシステム)の、2つのサブシステムに分けています。2つのサブシステムのAWS基盤は、ともに1つのSAMテンプレートにて管理しています。

メインで実行されるワークフロー

メインで実行されるワークフローファイルは、次のようになります。

deploy-system1-to-dev.yaml
name: Deploy system1 to dev environment

on:
  push:
    branches:
      - feature/**
    paths:
      - system1/**
  pull_request:
    branches:
      - develop
    types:
      - closed
    paths:
      - sysutem1/**

env:
  AWS_DEV_ACCOUNT_ID: xxxxxxxxxxxx
  AWS_ENV_NAME: dev

permissions:
  id-token: write
  actions: write
  contents: read
  pull-requests: read

jobs:

  #===================================================#
  # system1-component1のコンテナイメージを開発環境にプッシュ
  #===================================================#

  push_system1-component1_image_to_dev:
    needs:
      - cfn_lint_system1
      - pytest_system1-component1
      - pytest_system1-component2
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Push system1 component1 container image
        uses: ./.github/actions/container
        with:
          aws-account-id: ${{ env.AWS_DEV_ACCOUNT_ID }}
          env-name: ${{ env.AWS_ENV_NAME }}
          working-directory: system1
          image-name: system1-component1
          dockerfile-path: ./system1-component1
          image-tag: 'latest'

  #===================================================#
  # System1を開発環境にデプロイ
  #===================================================#

  deploy_system1_to_dev:
    needs:
      - push_system1-component1_image_to_dev
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Deploy System1
        uses: ./.github/actions/sam
        with:
          aws-account-id: ${{ env.AWS_DEV_ACCOUNT_ID }}
          env-name: ${{ env.AWS_ENV_NAME }}
          working-directory: system1

  #===================================================#
  # ECSサービスを更新
  #===================================================#

  update_system1_ecs_service:
    needs:
      - deploy_system1_to_dev
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Update ECS Service
        uses: ./.github/actions/update-ecs-service
        with:
          aws-account-id: ${{ env.AWS_DEV_ACCOUNT_ID }}
          env-name: ${{ env.AWS_ENV_NAME }}
          system-name: system1-component1

コンテナのワークフロー

続いて、それぞれのワークフローを見てみます。container/action.yamlでは、実行対象のディレクトリのDockerfileを参考にして、コンテナイメージをビルドし、Amazon ECR(以下、ECR)リポジトリにpushしています。

Dockerコンテナをいい感じにビルド&プッシュしてくれる有志のワークフローもあったのですが、もともと手動で運用していたコンテナ関係のコマンドが3行のみでしたので、実直にコマンドを直書きしても運用工数は変わらないだろう、ということで、run:配下にコマンドを記述しています。

container/action.yaml
name: Build container image and push it to ECR

inputs:
  aws-account-id:
    type: string
    description: AWS account id

  env-name:
    type: choice
    description: The environment name to deploy
    options:
      - dev
      - prd

  working-directory:
    type: choice
    description: The working directory to run the commands

  dockerfile-path:
    type: string
    description: Path to Dockerfile relative to working directory

  image-tag:
    type: string
    description: Image tag to push
    default: 001

  image-name:
    type: string
    description: Image name to push

runs:
  using: composite
  steps:
    - name: Get AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.env-name }}-role-github-actions-oicd
        aws-region: ap-northeast-1

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

    - name: Push image to ECR
      shell: bash
      working-directory: ${{ inputs.working-directory }}
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: ${{ inputs.env-name }}-${{ inputs.image-name }}-ecr
        IMAGE_TAG: ${{ inputs.image-tag }}
      run: |
        # Build Docker image
        docker build --platform linux/amd64 -t $ECR_REPOSITORY ${{ inputs.dockerfile-path }}
        # Tag Docker image for ECR
        docker tag $ECR_REPOSITORY:latest $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        # Push Docker image to ECR
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

SAMのワークフロー

SAMのワークフローも同じく、SAMビルドとSAMデプロイのコマンドを素直に実行するワークフローとして作成しました

sam/action.yam;
name: Build and deploy SAM application

inputs:
  aws-account-id:
    type: string
    description: AWS account id

  env-name:
    type: choice
    description: The environment name to deploy
    options:
      - dev
      - prd

  working-directory:
    type: choice
    description: The working directory to run the commands

runs:
  using: composite
  steps:
    - name: Setup Python
      uses: actions/setup-python@v5
      with:
        python-version: 3.12

    - name: Setup SAM
      uses: aws-actions/setup-sam@v2

    - name: Get credential
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.env-name }}-role-github-actions-oicd
        aws-region: ap-northeast-1

    - name: SAM build
      run: sam build
      shell: bash
      working-directory: ${{ inputs.working-directory }}

    - name: SAM deploy
      run: sam deploy --config-env ${{ inputs.env-name }} --no-confirm-changeset --no-fail-on-empty-changeset
      shell: bash
      working-directory: ${{ inputs.working-directory }}

Amazon ECSサービスの更新ワークフロー

今回SAMで管理しているAWSサービスの中に、Amazon ECS(以下、ECS)があります。上記のコンテナpush、SAMアプリケーションデプロイの後に、Amazon ECR(以下、ECR)上のコンテナイメージをもとにECSのタスクを再作成する必要がありましたので、ここもGitHub Actionsの処理に加えています。

name: Update ECS Service

inputs:
  aws-account-id:
    type: string
    description: AWS account id
    required: true

  env-name:
    type: choice
    description: The environment name to deploy
    required: true
    options:
      - dev
      - prd

  system-name:
    type: string
    description: System name to update
    required: true

runs:
  using: composite
  steps:
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ inputs.env-name }}-role-github-actions-oicd
        aws-region: ap-northeast-1

    - name: Update ECS Service
      run: |
        aws ecs update-service \
          --cluster ${{ inputs.env-name }}-${{ inputs.system-name }}-ecs-cluster \
          --service ${{ inputs.env-name }}-${{ inputs.system-name }}-ecs-service \
          --force-new-deployment
      shell: bash

こちらの実装は、次の記事を参考にしています

https://zenn.dev/y_taiki/articles/deploy_with_githubactions

実装に移す際のテンプレート的な例が欲しかった

今回、GitHub Actionsで複数の処理を各Jobで順番に実行する例を作ってみました。冒頭でも紹介しましたが、この記事は、「細かい文法は理解したので素早く実装に移れるサンプルなどないかな」と思って探したが結局見つからずに自前で実装した経緯があって作成しました。

この記事がどなたかの参考になれば幸いです。では!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.