GitHub Actions + Argo CDでプッシュ型のGitOpsを構築してみた
1. はじめに
CIとしてのGitHub ActionsとCDとしてのArgo CDを利用して、下図のようなプッシュ型のGitOpsを構築してみました。この記事では、特にGitHub Actionsワークフローに焦点を当てて、実装例を紹介していきます。
2. GitHub Actionsのワークフロー
今回構築したプッシュ型のGitOpsでは、以下の2点をGitHub Actionsが実行します。
- アプリケーションイメージのビルド&ECRリポジトリへのプッシュ
- マニフェストに記述されたイメージタグを書き換えて、コミット&プッシュ
上記を実現するのが以下のYAML定義であり、これをアプリケーションリポジトリ内のワークフローとして配置しています。
name: Build and Push Docker Images to ECR Repositories
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ap-northeast-1
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/role-github
role-duration-seconds: 1800
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
docker build -t $ECR_REGISTRY/app-repo:$IMAGE_TAG -f ./build/app/Dockerfile.prod .
docker build -t $ECR_REGISTRY/web-repo:$IMAGE_TAG -f ./build/web/Dockerfile .
docker build -t $ECR_REGISTRY/migration-repo:$IMAGE_TAG -f ./build/migration/Dockerfile .
docker push $ECR_REGISTRY/web-repo:$IMAGE_TAG
docker push $ECR_REGISTRY/app-repo:$IMAGE_TAG
docker push $ECR_REGISTRY/migration-repo:$IMAGE_TAG
- name: Checkout config repository
uses: actions/checkout@v3
with:
repository: your_account/your_repository
ref: branch_name
token: ${{ secrets.PAT }}
path: your_repository
- name: Update image tag
id: update-image-tag
working-directory: your_repository
continue-on-error: true
run: |
yq -i '.front.imageTag = "'`printenv IMAGE_TAG`'"' ./main/values.yaml
git diff --name-only --exit-code
- name: Commit and push
working-directory: your_repository
if: steps.update-image-tag.outcome == 'failure'
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git add .
git commit --author=. -m "Update image tag to "`printenv IMAGE_TAG`
git push
3. 解説
[1]
3-1. OIDCを利用したAWSへのアクセス#~ 省略 ~
permissions:
id-token: write
contents: read
#~ 省略 ~
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: ap-northeast-1
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT }}:role/role-github
role-duration-seconds: 1800
GitHub ActionsからOIDCを利用してAWSへアクセスさせる方法は簡単で、Configure AWS Credentialsアクションでaws-access-key-id
とweb-identity-token-file
を指定せずにrole-to-assume
を指定すれば、これが合図となります[2]。ですので、OIDC認証に成功したGitHubが引き受けるIAMロールを用意してrole-to-assume
に指定するだけで、GitHubからAWSへOIDCを利用したアクセスが可能となります。注意点としては、ワークフローのジョブ自体にid-token: write
とcontents: read
の権限を与えることです。ワークフローのジョブ内でOIDC認証を利用してAWSへアクセスするにはこれらの権限が必要なので、前述した宣言を忘れずに追加しておきましょう。
ちなみに、GitHubが引き受けるIAMロール(role-github
)は以下のように定義しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<AWSアカウントID>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:アカウント名/*",
},
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
}
}
]
}
また、role-github
にアタッチしているポリシーは以下の通りで、DockerイメージをECRリポジトリへプッシュするための権限のみを付与しています。[3]
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:UploadLayerPart",
"ecr:InitiateLayerUpload",
"ecr:BatchCheckLayerAvailability",
"ecr:PutImage"
],
"Resource": "*"
}
]
}
3-2. イメージのビルドとECRリポジトリへのプッシュ
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
docker build -t $ECR_REGISTRY/app-repo:$IMAGE_TAG -f ./build/app/Dockerfile.prod .
docker build -t $ECR_REGISTRY/web-repo:$IMAGE_TAG -f ./build/web/Dockerfile .
docker build -t $ECR_REGISTRY/migration-repo:$IMAGE_TAG -f ./build/migration/Dockerfile .
docker push $ECR_REGISTRY/web-repo:$IMAGE_TAG
docker push $ECR_REGISTRY/app-repo:$IMAGE_TAG
docker push $ECR_REGISTRY/migration-repo:$IMAGE_TAG
公式ドキュメントのExamples of Usageとほぼ同じ構成ですので、詳しくはそちらをご参照ください。
[4]
3-3. マニフェストリポジトリへのチェックアウト- name: Checkout config repository
uses: actions/checkout@v3
with:
repository: your_account/your_repository
ref: branch_name
token: ${{ secrets.PAT }}
path: your_repository
ここでは、Checkout V3アクションに対してrepository
とref
パラメータを指定し、チェックアウトするk8sマニフェスト用のリポジトリとブランチを指定しています。このとき、token
パラメータにPersonal Access Token (= PAT)を指定しておくことで、プライベートリポジトリへのアクセスが可能となります。そして、path
パラメータを指定することによって、チェックアウトしたリポジトリを指定したパスに配置しています。
[5]
3-4. マニフェストのイメージタグを書き換えてコミット&プッシュ- name: Update image tag
id: update-image-tag
working-directory: your_repository
continue-on-error: true
run: |
yq -i '.front.imageTag = "'`printenv IMAGE_TAG`'"' ./main/values.yaml
git diff --name-only --exit-code
- name: Commit and push
working-directory: your_repository
if: steps.update-image-tag.outcome == 'failure'
run: |
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
git add .
git commit --author=. -m "Update image tag to "`printenv IMAGE_TAG`
git push
まず、Update image tag
ステップでは、working-directory: your_repository
を指定して、前のステップでチェックアウトしたk8sマニフェスト用リポジトリを作業ディレクトリに指定しています。次に、YAMLファイルを良い感じに編集してくれるyqコマンドを利用して、main/values.yaml
の.front.imageTag
の値を書き換えています。そして、git diff
コマンドを実行することで、yq
コマンドにより差分が生じているかどうかを確認しています。このとき、--exit-code
オプションを指定しているので、差分がある場合には終了ステータスが1となる一方で、差分がない場合には終了ステータスが0となり、差分の有無が判定可能となります。ただし、このままでは差分が生じたときの終了ステータスが1となってGitHubワークフロー自体がエラー判定となって停止してしまうので、continue-on-error: true
というワークフロー構文を宣言して、エラー判定となった場合でもワークフローを継続するように設定しています。ちなみに、git diff
コマンドの--name-only
オプションはなくても構わないのですが、yq
コマンドにより差分が生じたファイル名をログ出力させるために、ここでは指定しています。
次に、Commit and push
ステップはUpdate image tag
ステップの終了ステータスが1となって出力がfailure
となっている場合、つまり、yq
コマンドによりファイル差分が発生している場合に実行されます。このステップも、引き続きk8sマニフェスト用リポジトリで作業していくので、working-directory: your_repository
を指定しています。そして、通常のコミット&プッシュと同様のフローでコマンドを実行して処理を進めていきます。ここで、git config user.name github-actions[bot]
やgit config user.email 41898282+github-actions[bot]@users.noreply.github.com
を指定していますが、これはちょっとしたオシャレのためです。これらを指定することでコミットする主体をGitHub Actions
と明記した上で、アイコンまで付けてくれるようになります。こんな感じで。
4. まとめ
ここでは、GitHub Actions + Argo CDでプッシュ型のGitOpsを構築してみたので、GitHub Actionsにフォーカスして構築したワークフローを紹介しました。GitOpsを構築する中で、GitHub Actionsワークフロー全体を紹介する記事はあまり多くないように感じたので、記録として残しておこうと思った次第です。誰かの何かのお役に立てれば幸いです^^
-
詳しくはRequired IAM permissions for pushing an imageをご参照ください。 ↩︎
-
ワークフローを配置しているリポジトリとは異なるプライベートリポジトリへチェックアウトする方法は公式ドキュメントのCheckout multiple repos (private)が参考になります。 ↩︎
-
この部分を実装するにあたっては、変更があるときだけコミット [GitHub Actions]を参考にさせていただきました。 ↩︎
Discussion