こんにちは。梅原です。年の瀬ですね、今年1年あっという間でした。
アプリケーションのソースコードをGitHubで管理している方は多いのではないでしょうか。GitHubにはGitHub ActionsというCI/CDを実施できるサービスがあります。
今回は知識0の状態でGitHub ActionsからAmazon ECSへデプロイを試してみます。
(ところどころ命名やコミットメッセージが適当なのは目を瞑ってください)
GitHub Actionsとは
GitHub Actionsとは、GitHub上のイベントをトリガーにアプリケーションのテストやデプロイといったCI/CDパイプラインの実行が可能なサービスです。
GitHub Actionsが実行されるコンピュートリソースはGitHubが用意してくれるので、OSSのようにサーバーを用意する必要がありません。
よく使われるパッケージマネージャーやDockerなどのツール、AWS CLIなど、多くのツールがすでに含まれており簡単に始めることができます。
GitHubのサービスのため、pushやプルリクエストだけでなく、Issueの作成といった多くイベントをトリガーに設定できます。
GitHub Actionsを使うためには、.github/workflows配下に処理を定義したyamlファイルを格納しておくだけで、設定したトリガーがあった際にGitHub Actionsが実行されます。
GitHub Actionsの構成と書き方
GitHub Actionsの構成
GitHub Actionsの構成として、以下4つがあります。
- イベント
実行のトリガーとなるGitHubのアクティビティです。特定ブランチへのpushやプルリクエストはもちろんIssueの作成などGitHub上の多くのアクティビティを設定することが可能です。 - ステップ
実行したい処理の最小単位です。アプリケーションのテストやデプロイといった実際の処理を定義します。 - ジョブ
1つ以上のステップをグルーピングしたものです。 - ワークフロー
一連のジョブの集まりをグルーピングしたものです。
先述のイベントがトリガーとなり、ワークフローが実行されます。
これだけではわからないと思うので、実際にyamlファイルの例を挙げたいと思います。
GitHub Actionsの定義ファイル書き方の例
ワークフローのyamlはこのような形です。
name: sample workflow on: push: branches: - main jobs: helloworld: name: hello world runs-on: ubuntu-latest steps: - run: echo "Hello World" goodmorning: name: greeting runs-on: ubuntu-latest steps: - run: echo "good morning" - name: Checkout uses: actions/checkout@v4.1.1 - name: check dirctory run: | pwd ls -al goodnight: name: goodnight runs-on: ubuntu-latest needs: ["goodmorning", "helloworld"] steps: - run: echo "good night $name!" env: name: k-umehara
.github/workflows/sample.yamlに格納しておきます。ファイル名は任意の名前で問題ありません。 mainブランチへのpushをトリガーにsample workflowという名前のワークフローが実行されます。 ワークフローには、helloworld、goodmorningとgoodnightという3つのジョブが含まれています。
順番に見ていきます。
name: sample workflow on: push: branches: - main
ここではワークフローの名前や、ワークフローのトリガーとなるイベントの定義をします。 特定パスの変更に限定して、ワークフローを実行するといったこともできます。
jobs: helloworld: name: hello world runs-on: ubuntu-latest steps: - run: echo "Hello World"
1つ目のジョブhelloworldでは単一のステップを実行しています。
nameでジョブに名前を付けています。
runs-onでは、ジョブの実行環境であるランナーを指定します。現在ではLinux、MacOS、Windowsに対応しており、LinuxはUbuntu 22.04が対応しています。
stepsには具体的な処理内容を記述します。今回はただechoしているだけですが、本来はアプリケーションのテストやdockerイメージのビルドといった処理内容を定義します。
goodmorning: name: greeting runs-on: ubuntu-latest steps: - run: echo "good morning" - name: Checkout uses: actions/checkout@v4.1.1 - name: check dirctory run: | pwd ls -al
2つ目のジョブgoodmorningでは3つのステップを実行しています。
1つ目と2つ目のステップを見たらわかるように、ステップの定義方法は2つあります。
- run
cliでコマンドを直接指定。処理したい内容のコマンドを記述します。 - uses
事前定義されたアクションの実行。よく利用される一連の処理内容を簡単に呼び出すことができます。 GitHubのマーケットプレイス上で公開されているものを使うか、自分でもアクションを作成することができます。
2つ目のステップで使用しているアクションでは、ソースコードをcloneするときに使うactions/checkoutを使用しています。 自前でコマンドを記述する必要がないです。
3つ目のステップでは、2つ目のアクションの確認として、lsコマンドを実行しています。複数行での実行も可能です。
goodnight: name: goodnight runs-on: ubuntu-latest needs: ["helloworld", "goodmorning"] steps: - run: echo "good night $name!" env: name: k-umehara
3つ目のジョブgoodnightです。
通常、ジョブは並列で実行されますが、needsを使用して依存関係を持たせる事ができます。
今回の例だと、1つ目のhelloworldと2つ目のgoodmorningジョブが完了後に、3つ目のgoodnightを実行させています。
ステップに環境変数をもたせることもできます。
ワークフロー実行確認
実際にmainブランチにpushしてみるとGitHubリポジトリのActionsタブは以下のようになります。
定義したsample workflowという名のワークフローがcommitメッセージとともに存在しています。
ワークフローの詳細ページでは、3つのジョブが存在し、1つ目と2つ目のジョブは並列実行ですが、3つ目のジョブは直列実行されていることが可視化されています。
詳細なジョブ画面に移るとログを見ることができます。usesで指定したアクションが実行され、lsコマンドも正常に出力されていることがわかります。
これまででワークフローの大まかな構成や流れは掴めたのではないでしょうか。
ここからはECSへのデプロイをやってみます。
AWSとの接続
AWS上のリソースを触るときには、AWSの認証情報がもちろん必要です。
クレデンシャルを発行してアクセスキーを使用することもできますが、アクセスキーが流出するおそれがあるので推奨ではありません。
GitHub Actionsでは、OpenID Connectがサポートされているので、IAM IDプロバイダーを使用した認証での接続が推奨です。
IAM IDプロバイダーを使うことで、GitHub Actionsから特定のロールを使用することができます。
認証処理に関しても、先述のアクションを使うことができます。
uses: aws-actions/configure-aws-credentials@v4
設定方法はドキュメントを参照ください。
環境変数と秘匿情報
ワークフロー内で使用したい変数は環境変数を使うことができます。 またジョブ毎にランナーが、ステップ毎にシェルが与えられるので、環境変数のスコープは意識しておく必要があります。 定義ファイルでは以下のような式で参照できます。
aws-region: ${{ env.AWS_REGION }}
秘匿情報はGitHub Actionsの定義ファイルにハードコード、つまりリポジトリに含めてはいけません。 OIDC認証に使用するIAMロール名やDBの接続情報といった秘匿情報を参照する場合は、リポジトリのsettingsから秘匿情報を設定し、そこから読むようにしましょう。 定義ファイルでは以下で参照できます。ステップのログにおいても*でマスクされるので安全です。
role-to-assume: "arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}"
ECSへデプロイしてみる
具体的なアプリケーションの内容やECSへのデプロイは本題ではないので、ここでは省略します。 ECSのデプロイの流れに関してはこちら参照ください。
.github/workflows/ecs.yamlに以下内容を記載してmainブランチにpushします。
name: ECS deploy on: push: branches: "main" env: AWS_REGION: ap-northeast-1 ECR_REPOSITORY: <ECRリポジトリ名> ECS_SERVICE: <ECSサービス名> ECS_CLUSTER: <ECSクラスター名> ECS_TASK_DEFINITION: taskdef.json # ソースコードにtaskdef.jsonを含めておく CONTAINER_NAME: <コンテナ名> permissions: id-token: write contents: read jobs: deploy: name: Deploy runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: aws setup credentials uses: aws-actions/configure-aws-credentials@v4 # AWSのcredentials設定アクション with: role-to-assume: "arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_ROLE_NAME }}" aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR #ECRへログイン id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - name: Build and push docker image to ECR # dockerイメージのbuildとpush id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} IMAGE_TAG: ${{ github.sha }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT - name: Render Task Definition # タスク定義のイメージ部分のレンダリング id: render-taskdefinition uses: aws-actions/amazon-ecs-render-task-definition@v1.2.0 with: task-definition: ${{ env.ECS_TASK_DEFINITION }} container-name: ${{ env.CONTAINER_NAME }} image: ${{ steps.build-image.outputs.image }} - name: Deploy Amazon ECS task definition # タスク定義を元にECSをデプロイ uses: aws-actions/amazon-ecs-deploy-task-definition@v1 with: task-definition: ${{ steps.render-taskdefinition.outputs.task-definition }} service: ${{ env.ECS_SERVICE }} cluster: ${{ env.ECS_CLUSTER }} wait-for-service-stability: true
ECSへのデプロイの流れとして、ECRにログイン、ECRにdockerイメージをpush、イメージURIを書き換えたタスク定義を元にデプロイといった流れが必要です。
これらの一連の処理も、アクションとしてAWSが用意してくれており、入力パラメータを渡してあげるだけで実装できます。
デプロイアクションはECS標準のローリングアップデートはもちろんB/Gデプロイも使用することができます。
github.com
このワークフロー定義ファイルをmainブランチにpushすると、デプロイが開始されます。
ECSサービスの画面にもプロビジョニングステータスのタスクがあります。
デプロイが完了して新しいタスクの画面を見ると、 新しいリビジョンのタスク定義でデプロイされていることがわかます。
また旧タスクが終了しています。
GitHub上でも無事完了していることがわかります。
最後に
用意されているactionをうまく活用することで非常に簡単に実装できました。
今回は実施しませんでしたが、条件分岐やエラーハンドリング、アーティファクトの保存、承認フローといった処理もできます。
また料金に関して、パブリックに公開しているリポジトリは無料です。プライベートに閉じているリポジトリは、無料枠を超えると従量課金となっています。
少し高めな印象を受けたので料金には注意する必要があります。
2024 Japan AWS Jr. Champions