ブログ

【AWS Code シリーズ】Github Actions で AWS の CodeCommit / CodeBuild を代替えしてみた

最近急に寒くなってきましたが、皆様体調はいかがでしょうか。私は既に冬服に衣替えしました。

さて、今回のテーマですがサービス終了/停止に伴う代替サービスについてです。

よくGoogleで話題になるサービス停止ですが、AWSでも利用率の低いものなどについてはサービス停止することがあります。その中で最近新規提供を停止したCodeCommitに触れていきたいと思います。ちなみに新規提供を停止しただけで、既存の利用者はまだ利用できる状態なのでご安心ください。

悲報!AWS CodeCommit が新規発行停止に

AWSのマネージドサービスであるCodeCommitが新規発行の停止を発表したことは、多くの開発者にとって残念なニュースでした。CodeCommitはAWS自身が開発・運用を行うGitベースのリポジトリサービスで、AWSリソースとのシームレスな連携が最大の特徴でした。

特に、CodePipeline、CodeBuild、CodeDeployなどのCI/CDサービスとの連携がスムーズに行えたことから、AWS環境でのアプリケーション開発の中核を担ってきました。マネージドサービスということもあり、運用の手間が少なく、セキュリティ面での心配も少ないといったメリットもありました。

しかし近年、GitHubやGitLabなどのオープンソースのGitリポジトリホスティングサービスが急速に普及し、AWSのCI/CDサービスともネイティブに連携できるようになってきました。提供中止の理由は発表されていませんが、CodeCommitの需要が低下したことから、AWSはCodeCommitの新機能開発を中止し、GitHubやGitLabにその役割を任せ、連携強化に注力することを決めたように思います。

既存のCodeCommitリポジトリは引き続き利用可能ですが、新規プロジェクトではCodeCommitが利用できなくなるため、開発者は代替ツールを検討する必要があります。移行期間を考えると、できるだけ早期にGitHubやGitLabなどの移行計画を立てることが賢明でしょう。

じゃあ Github 使ってみよう

CodeCommitの新規発行停止を受け、今回はGitHubを代替ソースコード管理ツールとして利用してみることにしました。GitHubは世界で最も広く利用されているオープンソースのGitリポジトリホスティングサービスです。

GitHubには以下のようなメリットがあります。

  • オープンソースコミュニティが活発で、多くのプロジェクトが公開されている
  • Pull Requestによるコードレビューがスムーズに行える
  • Issue管理機能が備わっており、タスク管理が容易
  • GitHub Actionsによる継続的インテグレーション/継続的デプロイ(CI/CD)がサポートされている
  • AWSとの連携が用意されており、CodePipeline、CodeBuild、CodeDeployなどと組み合わせて利用できる
  • セキュリティ面での配慮が行き届いており、2要素認証や組織ポリシーなどの機能が充実している

特にCI/CDパイプラインの構築が容易なことと、AWSサービスとのネイティブな連携がサポートされていることは大きな利点です。GitHub ActionsはGitHubが提供するCI/CDプラットフォームで、リポジトリへのPushやPull Requestの作成などのイベントをトリガーにして、カスタマイズ可能なワークフローを実行できます。

また、Github内でコンテナイメージのビルド機能も備えており、無料枠もFreeプランで2000分が付いてきます。そんなに頻繁にデプロイしなかったり、軽いテストのみで済んだりするアプリケーションであれば、無料枠だけで済むのではないでしょうか。

価格については以下のURLをご参照ください:
https://docs.github.com/ja/billing/managing-billing-for-your-products/managing-billing-for-github-actions/about-billing-for-github-actions

今回は、GitHub ActionsからECRへのコンテナイメージのプッシュを行い、続いてCodeDeployを使ってECSクラスターへのBlue/Greenデプロイを実現します。

どうやって Github と AWS を連携させるか

GithubとAWSを連携させるには主に2つあり、伝統的な(と言っていいのか分かりませんが)IAM

ユーザをGithubで利用する方法と、OIDCを利用した方法があります。

OIDCは、認証と認可のための業界標準のプロトコルです。GitHubとAWSの連携においては、OIDCを使うことで、GitHub ActionsからAWSリソースへのアクセスを安全かつ簡単に管理できるようになります。

この連携により、以下のようなメリットが得られます。

  • セキュリティの向上: AWSアクセスキーをコード内に保存する必要がなくなり、機密情報の漏洩リスクが低減されます。
  • コンプライアンスの強化: アクセス許可の管理が集中化され、監査性が高まります。
  • 運用の簡素化: GitHub ActionsからAWSリソースへのアクセスが簡単になり、DevOpsワークフローが効率化されます。
  • スケーラビリティの向上: リポジトリやAWSリソースが増えても、認証情報の管理が容易になります。

このように、GitHubとAWSをOIDCで連携させることで、DevOpsにおけるセキュリティ、コンプライアンス、効率性が大幅に改善されます。企業がクラウドネイティブな開発を推進する上で、この連携は非常に重要な役割を果たすと言えるでしょう。

実際にやってみた

それでは実際にやってみましょう!

今回は以下のような構成にしていきます。

フローとしては2つに分かれており、OIDCのやり取りをしてGithub Actionsが設定されたIAMロールへのアクセス権を取得するパートと、実際にIAMロールを利用してECRへのイメージプッシュなどを行うパートです。

今回は既にECSの環境がある状態でやっていきますが、ECSやVPCなどのネットワーク周りは今回メインテーマではないため、詳細は省かせて頂きます。

以下のような流れで実施していきます。

① IDプロバイダの作成
② IAMロールの作成
③ Github Actionsに必要な設定ファイル作成
④ Github Actions実行

① IDプロバイダの作成

まずはAWS側でOIDCのためのIDプロバイダを作成します。

キャプチャの通りIAMコンソールの左ペインから[IDプロバイダ]をクリックし、中央画面の[IDプロバイダの追加]をクリックします。

[IDプロバイダの追加]画面にて以下の通り選択・入力し、最後に[プロバイダを追加]をクリックします。

  • プロバイダタイプ:OpenID Connect
  • プロバイダのURL:https://token.actions.githubusercontent.com
  • 対象者:sts.amazonaws.com

結果、以下のIDプロバイダが作成されますので、プロバイダ一覧から作成したプロバイダ名をクリックします。

中央画面から[エンドポイント検証]タブをクリックし、サムプリント一覧にある文字列があることを確認します。

② IAMロールの作成

作成したIDプロバイダにアタッチするIAMロールを作成します。

IAMコンソールに遷移し、左ペインから[アクセス管理]>[ロール]をクリックし、[ロールを作成]をクリックします。

次の画面で[カスタム信頼ポリシー]をクリックし、入力画面にて以下のように入力して[次へ]をクリックします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::<AWSアカウントID>:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
                    "token.actions.githubusercontent.com:sub": "repo:<GitHubユーザー名>/<GitHubリポジトリ名>:ref:refs/heads/<ブランチ名>"
                }
            }
        }
    ]
}

※”<>”内は適宜任意の値に置き換えてください

特に下記についてはGithubのリポジトリ情報になりますので、ご確認ください。

[許可を追加]の画面で許可ポリシーを選択します。

今回は以下のCodeDeploy・S3・ECRの組み込みポリシーを追加しました。今回は開発環境なので組み込みポリシーを利用しますが、本番環境では適宜権限を絞ってポリシーを作成してください。

ポリシー:

  • AmazonEC2ContainerRegistryPowerUser
  • AWSCodeDeployDeployerAccess
  • AmazonECS_FullAccess

最終の画面でロール名を入力し、設定を確認してから[ロールを作成]をクリックします。

AWS側での操作は一旦完了です。

③ Github Actionsに必要な設定ファイル作成

まずGithubのリポジトリには以下のようなディレクトリ構成で各種ファイルがあります。

.
├── app
│   ├── HELP.md
│   ├── mvnw
│   ├── mvnw.cmd
│   ├── pom.xml
│   ├── src
│   └── target
├── appspec.yaml
└── docker
    └── Dockerfile
└──.github
    └── workflows
        └── main.yml

[app]配下はアプリケーション関連のファイルで、今回はspringbootで作っています。今回はあまり関係がないので省略しています。

[docker]配下はDockerfileのみです。

[.github]配下はGithub Actionsの実行定義ファイルが格納されており、今回の肝となります。github actions二関してはこのディレクトリ構成通りでないと実行できないのでお気をつけください。

今回はCodeDeployをトリガーしてECSに対してBlue/Greenデプロイを行うため、appspec.yamlを用意します。

デプロイに必要な以下のファイルについて解説します。

  • appspec.yaml
  • main.yml

① appsepc.yaml

version: 0.0
Resources:
- TargetService:
    Type: AWS::ECS::Service
    Properties:
      TaskDefinition: "Placeholder: GitHub Actions will fill this in"
      LoadBalancerInfo:
        ContainerName: "demo-container"
        ContainerPort: 8080
      PlatformVersion: "LATEST"

上記はコンテナ名とポートをECSコンソールから各種確認して記入すればOKです。TaskDefinitionは後述のmain.ymlで記述された通りにGithub Actions処理の中で置き換えられますので、そのままでOKです。

② main.yml

name: Push docker images and deploy to ECS

on:
push:
  branches:
    - main

permissions:
id-token: write
contents: read

jobs:
deploy:
  runs-on: ubuntu-latest
  steps:
    - name: Check out the repository
      uses: actions/checkout@v4

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: ${{ vars.AWS_DEFAULT_REGION }}
        role-to-assume: ${{ secrets.IAM_ROLE_ARN }}

    - name: Set up JDK 21
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'corretto'

    - name: Build with Maven
      run: ./app/mvnw -f ./app/pom.xml package

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

    - name: Docker build and push
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $ECR_REGISTRY/${{ vars.ECR_REPOSITRY }}:$IMAGE_TAG -f ./docker/Dockerfile .
        docker push $ECR_REGISTRY/${{ vars.ECR_REPOSITRY }}:$IMAGE_TAG
        echo "image=$ECR_REGISTRY/${{ vars.ECR_REPOSITRY }}:$IMAGE_TAG" >> $GITHUB_OUTPUT

    - name: Download task definition
      run: |
        aws ecs describe-task-definition --task-definition ${{ vars.TASK_DEF }} --query taskDefinition > task-definition.json

    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: ${{ vars.CONTAINER_NAME }}
        image: ${{ steps.build-image.outputs.image }}

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v2
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: ${{ vars.ECS_SVC }}
        cluster: ${{ vars.ECS_CLR }}
        wait-for-service-stability: true
        codedeploy-appspec: appspec.yaml
        codedeploy-application: ${{ vars.CD_APP }}
        codedeploy-deployment-group: ${{ vars.ECS_GRP }}

上記が全量ですが、今回のポイントだけ掻い摘んで説明します。

下記でロールを指定することでOIDC認証であることを明示しています。値が”vars”だの”secrets”だのとなっているのは、Githubコンソールでリポジトリレベルの環境変数を設定しているので、それを取得しているためです。

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        aws-region: ${{ vars.AWS_DEFAULT_REGION }}
        role-to-assume: ${{ secrets.IAM_ROLE_ARN }}

下記でデプロイする際に必要な現在のタスク定義を取得し、タスク定義内で前段のDocker buildで取得した新しいイメージ名で古いイメージ名を置き換えています。これでタスク定義の準備はでき上がりました。

    - name: Download task definition
      run: |
        aws ecs describe-task-definition --task-definition ${{ vars.TASK_DEF }} --query taskDefinition > task-definition.json

    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: task-definition.json
        container-name: ${{ vars.CONTAINER_NAME }}
        image: ${{ steps.build-image.outputs.image }}

④ Github Actionsの実行

それではGithub Actionsを実行していきます。

main.ymlに定義した通り、mainブランチにプッシュすると実行されるようになっています。Github Actionsの定義ファイル(main.yml)をプッシュするだけでGithub Actionsが設定されるので、非常に楽ですね。

ではアプリケーションファイルの一部を変えてプッシュします(エディタはCursorです)。

Githubコンソールの対象リポジトリに入り、Actionsタブをクリックするとジョブが実行されていることがわかります。CodeDeployのところまで来ましたのでAWSのCodeDeployコンソールに移動しましょう。

ちゃんとデプロイが開始されていますね!今回はGreen環境がデプロイされた後、テストできるように切り替えるまで1時間待つように設定していますので、手動で切り替え開始します。

Blue環境の終了も2時間待つ設定になっているので、こちらも手動で終了させます。

CodeDeployはすべて完了したので、Github Actions側に戻ります。

Github Actions側もすべて成功していました!

Github Actions で AWS の CodeCommit / CodeBuild を代替えしてみた(まとめ)

いかがでしたでしょうか。

GithubでCodeCommitの代用もでき、さらにGithub Actionsを使えばCodeBuildの代用もできるため、非常に便利だと思われたのではないでしょうか。残念ながらGithubのみでBlue/Greenデプロイまではできませんが、CodeDeployと連携させることでそれも解決します。

Githubではビルドするためのランナーの無料枠がFreeプランでも2000分あるため、大体が無料で収まるのではないかと思います。

今回はGithub Actionsの定義の中でコンテナが正常に起動するのを待つようにしていたため、CodeDeployの実行時間も含めると無料枠を食いつぶしていましたが、CodeDeployをトリガーしたらGithub Actionsを続行させる(非同期にさせる)こともできるため、節約することもできます。基本的にCodeDeployをトリガーしたら以後はAWSの画面を見ることになるため、非同期実行でいいと思います。

それでは良きDevOpsライフを!

元記事発行日: 2024年11月27日、最終更新日: 2024年11月27日