こんにちは!開発本部 SINIS for X 開発チームの西野(@fingerEase24)です。
今回はAmazon ECRを利用したCIの高速化を実施したので、その手順についてお話しします。
なお、CIにはGitHub Actionsを利用しており、インフラ環境についてはTerraformによるIaC管理を行っています。
想定読者
- Dockerを利用したコンテナ化を行っている
- CIの実行時間を短縮したいと考えている
実施内容
以下の流れで作業を行いました。
- CIに利用するイメージ用のECRリポジトリを作成する
- OpenID Connectを利用したAWS認証の設定
- CIで最新のイメージをECRにpushするワークフローを追加
- CIの各ワークフローでECRからイメージをpullして利用
それぞれ、コード例とともに解説します。
CIに利用するイメージ用のECRリポジトリを作成する
CIでイメージをpush, pullするためのECRリポジトリを作成します。
CIでDockerイメージを利用するGitHubリポジトリごとに作成すると良いでしょう。
以下はバックエンド用の作成例です。
resource "aws_ecr_repository" "example_backend_ci" { name = "example-backend-ci" image_scanning_configuration { scan_on_push = true } encryption_configuration { encryption_type = "KMS" } }
OpenID Connectを利用したAWS認証の設定
GitHub ActionsでサポートされているOpenID Connect(OIDC)を利用したAWS認証を行い、作成したECRリポジトリにアクセスできるようにします。
詳細については以下の公式ドキュメントをご参照ください。
まず、IDプロバイダを作成します。
data "http" "github_actions_openid_configuration" { url = "https://token.actions.githubusercontent.com/.well-known/openid-configuration" } data "tls_certificate" "github_actions" { url = jsondecode(data.http.github_actions_openid_configuration.response_body).jwks_uri } resource "aws_iam_openid_connect_provider" "github_actions" { url = "https://token.actions.githubusercontent.com" client_id_list = ["sts.amazonaws.com"] thumbprint_list = data.tls_certificate.github_actions.certificates[*].sha1_fingerprint }
次に、IAMポリシーとロールを作成します。
data "aws_iam_policy_document" "example_backend_assume_role_policy" { statement { effect = "Allow" actions = ["sts:AssumeRoleWithWebIdentity"] principals { type = "Federated" identifiers = ["arn:aws:iam::${your-account-id}:oidc-provider/token.actions.githubusercontent.com"] } condition { test = "StringEquals" variable = "token.actions.githubusercontent.com:aud" values = ["sts.amazonaws.com"] } condition { test = "StringLike" variable = "token.actions.githubusercontent.com:sub" values = ["repo:${your-repository-url}:*"] # 対象リポジトリの全てのワークフローで認証を許可 } } } resource "aws_iam_role" "example_backend" { name = "oidc-example-backend-role" assume_role_policy = data.aws_iam_policy_document.example_backend_assume_role_policy.json } resource "aws_iam_role_policy_attachment" "example_backend_ecr_power_user" { role = aws_iam_role.example_backend.name policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser" }
IAMポリシーとロールは、対象のGitHubリポジトリの数だけ作成しておきます。
CIで最新のイメージをECRにpushするワークフローを追加
これでGitHub Actionsから作成したECRリポジトリにアクセスできるようになったため、まずはDockerfileに更新があった場合にイメージをビルドしてpushするCIワークフローを追加します。
name: Push Docker image on: push: # mainブランチにマージされた変更差分にDockerfileが含まれていた場合に実行 branches: [main] paths: - "src/Dockerfile" workflow_dispatch: # 手動でも実行可能にする concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-and-push: runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: id-token: write contents: read steps: - name: Checkout uses: actions/checkout@v4 - name: AWS authentication uses: aws-actions/configure-aws-credentials@v4 with: aws-region: "ap-northeast-1" role-to-assume: "arn:aws:iam::${your-account-id}:role/oidc-example-backend-role" - name: Login to ECR uses: aws-actions/amazon-ecr-login@v2 id: login-ecr - name: Build and push Docker image to ECR working-directory: ./src env: REGISTRY: ${{ steps.login-ecr.outputs.registry }} REPOSITORY: "example-backend-ci" run: | docker build . -t $REGISTRY/$REPOSITORY:latest docker push $REGISTRY/$REPOSITORY:latest
初回作成時には手動でこのワークフローを実行し、対象のECRリポジトリにイメージが作成されていることを確認します。
CIの各ワークフローでECRからイメージをpullして利用
実際にDockerイメージを利用するワークフローで、ECRからイメージを取得してくるようにします。
これにより、該当ワークフローの実行ごとにDockerイメージをビルドする必要がなくなり、CI実行時間が短縮されます。
以下は、対象ワークフローにおけるジョブの設定例です。
jobs: api: runs-on: ubuntu-24.04 timeout-minutes: 10 permissions: id-token: write contents: read steps: - name: Checkout uses: actions/checkout@v4 - name: AWS authentication uses: aws-actions/configure-aws-credentials@v4 with: aws-region: "ap-northeast-1" role-to-assume: "arn:aws:iam::${your-account-id}:role/oidc-example-backend-role" - name: Login to ECR uses: aws-actions/amazon-ecr-login@v2 id: login-ecr - name: Pull Docker image from ECR env: REGISTRY: ${{ steps.login-ecr.outputs.registry }} REPOSITORY: "example-backend-ci" run: | docker pull $REGISTRY/$REPOSITORY:latest docker tag $REGISTRY/$REPOSITORY:latest example_backend # compose.ymlで指定しているイメージタグを設定 # 以下後続の処理
成果
実際にバックエンドリポジトリで実行しているCIの実行時間を前後で比較します。
Before: 約3分
After: 2分弱
1回あたりおおよそ1分強の実行時間短縮につながりました。
運用ルールと注意点
Dockerイメージに加えた変更は、mainブランチにマージされて初めて適用されるため、基本的にDockerイメージに変更を加える際はそれ単体でPull Requestを作成するようにしています。
これは、できる限りPull Requestの粒度を小さくするという点においても良く作用します。
ただし、場合によってはDockerfileの変更差分だけで分離できないこともあるため、その場合は対象の作業ブランチ上でワークフローの手動実行を行うことによって対応します。
この際に既存のPull Requestがある場合は、それらのCIで利用するイメージにも影響が及ぶため注意が必要です。
まとめ
今回の記事では、Amazon ECRを利用したCIの高速化について解説しました。
GitHub Actionsのキャッシュを利用することでも同様の高速化が見込めますが、継続的デプロイのプロセスでECRを利用している場合、CIのワークフローでも同様にすることでCI/CD間で一貫性が出て見通しが良くなると思います。
CI実行時のフローや運用ルールについては、プロダクトやチーム規模の拡大に伴い、必要に応じて随時見直していければと思います。
テテマーチでは、一緒にサービスを作り上げてくれる仲間を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください! herp.careers
エンジニアチームガイドはこちら! tetemarche01.notion.site