今回はAWS CodeCommit/CodeBuildとFluxを使い、GitHub Flowを実現することを目指して構成を考えてみました。
GitHub Flowとは
GitHub Flowは開発ワークフローの1つであり、以下のようなルールが定められた、比較的シンプルなワークフローになります。
- master ブランチは常にデプロイ可能である
- 作業用ブランチをmasterから作成する
- 作業用ブランチをリモートブランチに定期的にプッシュする
- フィードバックや助言が欲しい時、ブランチをマージしたいときは、 プルリクエスト を作成する
- プルリクエストが承認されたらmasterへマージする
- masterへのマージが完了したら直ちにデプロイを行う
GitHub Flowでは、運用するコードは1つのバージョンだけを持ち、常にデプロイ可能なメインラインに頻繁にインテグレーションすることを目指します。また通常の機能開発と本番環境向けの修正を同じフローで対応します。このためGit Flowと異なり、リリース専用のreleaseブランチや、本番環境のバグ修正に使うHotfixブランチなどは利用しません。
※参考:
- A little space for Scott - GitHub Flow
- martinfowler.com - Patterns for Managing Source Code Branches: GitHub Flow
パイプラインの全体像
今回用意したパイプラインの全体像は以下の通りです。今回、アプリケーションコードのテストやコンテナイメージのビルドはAWS CodeBuild、EKS環境へのデプロイは Flux
を採用しています。
なお今回GitHub Flowを適用しているのはアプリケーションコードの開発フローのみになります。マニフェストの開発には特にフローを設定していません。
AWS CodeBuildの部分は、以前試した構成を再利用しています。
- 【メモ】AWS CodeCommit / CodeBuildと ECR / EKSを利用したCI/CDパイプラインの例
- 【メモ】AWS CodeCommitのブランチへの操作制限と承認ルールテンプレートの作り方
EKSなどKubernetesクラスターへのマニフェストの自動デプロイは、以下の2つのどちらかのアプローチを採用するかと思います。今回は2つ目の、GitOpsのアプローチで実行しています。
CIOps
: アプリケーション変更のマージ時に、コンテナイメージビルドと合わせてkubectl
等でデプロイをするGitOps
: コンテナイメージの更新を検知してテスト環境への自動デプロイを実行する
※参考:
- Weaveworks Blog - Guide To GitOps
- Weaveworks Blog - Kubernetes anti-patterns: Let's do GitOps, not CIOps!
ここでは Flux というCI/CD用ツールを利用します。Fluxの利用方法については 以前紹介したことがあるので、そちらをご覧ください。
実際の開発フローは、以下のような流れを想定しています。
- 開発者はアプリケーションの機能追加・修正のためにmainブランチからfeatureブランチを作成する
- featureブランチ上で開発を行い、mainブランチに対するPRを作成する
- PRの作成を検知して自動テストが実行される
- テストに合格したらレビューを依頼し、修正などを経てmainブランチへマージする
- mainブランチへマージされると、コンテナイメージのビルドを実行する
- 新しいコンテナイメージがPushされるのを検知し、マニフェストのイメージタグの書き換えと、テスト環境への自動デプロイが実行される
- テスト環境で問題がないことを確認したら、対象のコミットに対しGitタグを付与する
- Gitタグを検知し、本番環境への自動デプロイが実行される
※参考:
開発フローの流れ
開発者はアプリケーションの機能追加・修正のためにmainブランチからfeatureブランチを作成する
AWSではIAMユーザー・グループに対してIAMポリシーを適用し、CodeCommitに対する操作制限をかけることができます。
今回は testuser
というIAMユーザーを作成しました。このユーザーは testgroup
というIAMグループに属しており、testgroup
にはあらかじめ main
ブランチへの直接PushができないようIAMロールを付与しています。
ここでは testuser
で codecommit-ci
というCodeCommitリポジトリをCloneし、作業用のブランチを作成します。
なお、IAMユーザー作成後はAWSマネジメントコンソールにアクセスし、CodeCommit用のアクセス情報を取得します。今回はHTTPS認証を利用しました。
※参考:
featureブランチ上で開発を行い、mainブランチに対するPRを作成する
featureブランチ上でソースコードなどに修正を行い、mainブランチへのPRを作成します。
PRの作成を検知して自動テストが実行される
codecommit-ci
でPRが作成されると、AWS EventBridgeを経由して codebuild-ci-test
というCodeBuildプロジェクトのジョブが起動します。 codebuild-ci-test
ではテストが実行され、実行結果はSlackに通知されます。
テストに合格したらレビューを依頼し、修正などを経てmainブランチへマージする
CodeBuildによるテストが無事通ったら、レビューを依頼してPRのチェックをします。今回は承認ルールテンプレートも作成しており、PRに対して1件の承認がないとマージできないよう設定しています。
※参考:
mainブランチへマージされると、コンテナイメージのビルドを実行する
PRのレビューで問題がなければ承認・マージを行います。EventBridgeがマージを検知すると codebuild-ci-build
というCodeBuildプロジェクトがジョブを開始します。このジョブではDockerfileを用いたイメージのビルド、ECRリポジトリへのPushを行います。ジョブの結果はSlackへ通知されます。
なお今回コンテナイメージのタグは main-<date>-<Commit IDの先頭7文字>
という形式にしています。
※参考:
新しいコンテナイメージがPushされるのを検知し、マニフェストのイメージタグの書き換えと、テスト環境への自動デプロイが実行される
ECRに新しいイメージがPushされると、あらかじめEKSにインストールされたFluxがイメージの更新を検知し、codecommit-cd
というCodeCommitリポジトリ上のマニフェストファイルを書き換えます。
マニフェストファイルが書き換わると、Fluxがリポジトリ上の変更を検知し、EKSクラスターへ自動的に更新を行います。これにより、コンテナイメージが更新されることで、テスト環境への自動デプロイを実現します。
※参考:
テスト環境で問題がないことを確認したら、対象のコミットに対しGitタグを付与する
今回は作成しませんでしたが、テスト環境での動作確認が取れたら、特定のGitタグを付与することで本番環境のPodが更新されることを想定しています。ここでもFluxを利用し、以下のようなマニフェストファイルを使うことで、Gitタグを検知してアップデートを行います。
apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: prod namespace: flux-system spec: interval: 1m url: https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/codecommit-cd ref: semver: ">=3.1.0-rc.1 <3.2.0" # Gitタグの形式を指定 gitImplementation: libgit2 secretRef: name: https-credentials
※参考:
Gitタグを検知し、本番環境への自動デプロイが実行される
前項の通り、FluxがGitタグを検知して自動デプロイを実行します。
ここまでで、今回のGitHub Flowの流れは終了です。
環境構築
最後に、今回の環境構築についての紹介です。
CodeCommit / CodeBuildの作成
CodeCommit / CodeBuild関連のリソースは、以下のマニフェストファイルで作成します。ここでは承認ルールテンプレートなども合わせて作成します。承認ルールテンプレートの作成は以前試したこちらの記事をご確認ください。
sample-resources.yaml
AWSTemplateFormatVersion: '2010-09-09' Description: Sample resources for GitHub flow Parameters: CodeCommitRepoNameForCI: Type: String Default: "codecommit-ci" CodeBuildProjectForCITest: Type: String Default: "codebuild-ci-test" CodeBuildProjectForCIBuild: Type: String Default: "codebuild-ci-build" RuleForCITest: Type: String Default: "rule-ci-test" RuleForCIBuild: Type: String Default: "rule-ci-build" CodeCommitRepoNameForCD: Type: String Default: "codecommit-cd" ECRRepoName: Type: String Default: "test-ecr-cicd" NotifySlackChannel: Type: String Description: Slack Channel ID Default: "xxxxxxxxxxx" NotifyChatbotWorkspaceId: Type: String Description: Chatbot Workspace ID Default: "xxxxxxxxxxx" UserName: Type: String Default: "testuser" UserPassword: Type: String Default: "testuser@1234" GroupName: Type: String Default: "testgroup" RuleTemplateName: Type: String Default: "testrule" Resources: ########################### # Sample resources for CI # ########################### # CodeCommit for CI CodeCommitForCI: Type: AWS::CodeCommit::Repository Properties: RepositoryName: !Ref CodeCommitRepoNameForCI RepositoryDescription: CodeCommit repository for CI # CodeBuild + IAM for CI CodeBuildForCITest: Type: AWS::CodeBuild::Project Properties: Name: !Ref CodeBuildProjectForCITest Description: CodeBuild project for CI test ServiceRole: !GetAtt BuildRoleForCITest.Arn Artifacts: Type: NO_ARTIFACTS Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/amazonlinux2-x86_64-standard:3.0 Source: Location: !GetAtt CodeCommitForCI.CloneUrlHttp Type: CODECOMMIT BuildSpec: | version: 0.2 phases: install: runtime-versions: golang: 1.14 build: commands: - echo Test the Go code - go test -v $CODEBUILD_SRC_DIR/app BuildRoleForCITest: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: "Allow" Action: 'sts:AssumeRole' Principal: Service: codebuild.amazonaws.com BuildPoliciesForCITest: Type: "AWS::IAM::Policy" Properties: PolicyName: Policy-for-ci-test PolicyDocument: Version: "2012-10-17" Statement: - Action: - logs:* Resource: '*' Effect: Allow - Resource: "*" Effect: Allow Action: - 'codebuild:*' - 'codecommit:*' - 'events:*' - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'iam:*' Roles: - Ref: BuildRoleForCITest CodeBuildForCIBuild: Type: AWS::CodeBuild::Project Properties: Name: !Ref CodeBuildProjectForCIBuild Description: CodeBuild project for CI build ServiceRole: !GetAtt BuildRoleForCIBuild.Arn Artifacts: Type: NO_ARTIFACTS Environment: Type: LINUX_CONTAINER ComputeType: BUILD_GENERAL1_SMALL Image: aws/codebuild/docker:18.09.0 # please set the variables EnvironmentVariables: - Name: "ECR_REPO" Value: !Ref ECRRepoName - Name: "BRANCH" Value: "main" Source: Location: !GetAtt CodeCommitForCI.CloneUrlHttp Type: CODECOMMIT BuildSpec: | version: 0.2 phases: pre_build: commands: - echo Login to ECR - AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query 'Account' --output text) - $(aws ecr get-login --no-include-email --region ap-northeast-1) - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$ECR_REPO - echo Set IMAGE_TAG - DATE=$(date "+%Y%m%d%H%M") - COMMITID=$(echo ${CODEBUILD_RESOLVED_SOURCE_VERSION} | head -c 7) - IMAGE_TAG=$BRANCH-$DATE-$COMMITID build: commands: - echo Build Docker image - docker build -t $REPOSITORY_URI:$IMAGE_TAG app/ post_build: commands: - echo Push to ECR - docker push $REPOSITORY_URI:$IMAGE_TAG BuildRoleForCIBuild: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: "Allow" Action: 'sts:AssumeRole' Principal: Service: codebuild.amazonaws.com BuildPoliciesForCIBuild: Type: "AWS::IAM::Policy" Properties: PolicyName: Policy-for-ci-build PolicyDocument: Version: "2012-10-17" Statement: - Action: - logs:* Resource: '*' Effect: Allow - Resource: "*" Effect: Allow Action: - 'ecr:*' - 'codebuild:*' - 'codecommit:*' - 'events:*' - 'logs:CreateLogGroup' - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'iam:*' Roles: - Ref: "BuildRoleForCIBuild" # EventBridge for CI EventBridgeForCITest: Type: AWS::Events::Rule Properties: Description: EventBridge for PR test EventPattern: source: - "aws.codecommit" resources: - !GetAtt CodeCommitForCI.Arn detail-type: - "CodeCommit Pull Request State Change" detail: event: - "pullRequestCreated" pullRequestStatus: - "Open" destinationReference: - "refs/heads/main" Name: !Ref RuleForCITest State: "ENABLED" Targets: - Arn: !GetAtt CodeBuildForCITest.Arn Id: AppPRTest RoleArn: !GetAtt EventBridgeRoleForCITest.Arn EventBridgeRoleForCITest: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: Policy-eventbridge-for-ci-test PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Resource: !GetAtt CodeBuildForCITest.Arn Action: - codebuild:StartBuild EventBridgeForCIBuild: Type: AWS::Events::Rule Properties: Description: EventBridge for PR build EventPattern: source: - "aws.codecommit" resources: - !GetAtt CodeCommitForCI.Arn detail-type: - "CodeCommit Pull Request State Change" detail: event: - "pullRequestMergeStatusUpdated" pullRequestStatus: - "Closed" destinationReference: - "refs/heads/main" Name: !Ref RuleForCIBuild State: "ENABLED" Targets: - Arn: !GetAtt CodeBuildForCIBuild.Arn Id: AppPRTest RoleArn: !GetAtt EventBridgeRoleForCIBuild.Arn EventBridgeRoleForCIBuild: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - events.amazonaws.com Action: sts:AssumeRole Path: / Policies: - PolicyName: Policy-eventbridge-for-ci-build PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Resource: !GetAtt CodeBuildForCIBuild.Arn Action: - codebuild:StartBuild # CodeCommit for CD CodeCommitForCD: Type: AWS::CodeCommit::Repository Properties: RepositoryName: !Ref CodeCommitRepoNameForCD RepositoryDescription: CodeCommit repository for CD ########################### # Sample resources common # ########################### # ECR ECRForCICD: Type: AWS::ECR::Repository Properties: RepositoryName: !Ref ECRRepoName ImageScanningConfiguration: ScanOnPush: "true" # Chatbot ChatbotSlackConfiguration: Type: AWS::Chatbot::SlackChannelConfiguration Properties: ConfigurationName: chatbot-slack-configuration IamRoleArn: !GetAtt ChatbotRole.Arn LoggingLevel: ERROR SlackChannelId: !Ref NotifySlackChannel SlackWorkspaceId: !Ref NotifyChatbotWorkspaceId ChatbotRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - chatbot.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess # CodeStar Notification Rule NotificationRoleForCITest: Type: 'AWS::CodeStarNotifications::NotificationRule' Properties: Name: 'notifications for CI test' DetailType: FULL Resource: !GetAtt CodeBuildForCITest.Arn EventTypeIds: - codebuild-project-build-state-failed - codebuild-project-build-state-succeeded Targets: - TargetType: AWSChatbotSlack TargetAddress: !GetAtt ChatbotSlackConfiguration.Arn NotificationRoleForCIBuild: Type: 'AWS::CodeStarNotifications::NotificationRule' Properties: Name: 'notifications for PR build' DetailType: FULL Resource: !GetAtt CodeBuildForCIBuild.Arn EventTypeIds: - codebuild-project-build-state-failed - codebuild-project-build-state-succeeded Targets: - TargetType: AWSChatbotSlack TargetAddress: !GetAtt ChatbotSlackConfiguration.Arn # IAM User IAMPolicy: Type: 'AWS::IAM::ManagedPolicy' Properties: Description: prohibit direct push to codecommit Path: / PolicyDocument: Version: "2012-10-17" Statement: - Effect: Deny Action: 'codecommit:GitPush' Resource: '*' Condition: "StringEqualsIfExists": "codecommit:References": - "refs/heads/main" "Null": "codecommit:References": - "false" IAMUser: Type: AWS::IAM::User Properties: Groups: - !Ref IAMGroup UserName: !Ref UserName LoginProfile: Password: !Ref UserPassword PasswordResetRequired: "false" IAMGroup: Type: AWS::IAM::Group Properties: GroupName: !Ref GroupName ManagedPolicyArns: - !Ref IAMPolicy - "arn:aws:iam::aws:policy/AWSCodeCommitPowerUser" # Approval Rule Template RuleTemplate: Type: Community::CodeCommit::ApprovalRuleTemplate Properties: Name: !Ref RuleTemplateName Description: test rule Content: Version: "2018-11-08" DestinationReferences: - "refs/heads/main" Statements: - Type: "Approvers" NumberOfApprovalsNeeded: 1 ApprovalPoolMembers: - "*" RepoAssociation: Type: Community::CodeCommit::RepositoryAssociation Properties: ApprovalRuleTemplateArn: !Ref RuleTemplate RepositoryNames: - !Ref CodeCommitRepoNameForCI
ソースコード・Dockerfileの配置
CodeCommitを作成したら、各リポジトリに README.md
を適当に配置し、ローカルへクローンします。クローンしたら、ソースコード・Dockerfileは codecommit-ci
リポジトリの app
ディレクトリに、Kubernetesマニフェストファイルは codecommit-cd
リポジトリの manifest
ディレクトリに配置します。
今回利用したソースコード・Dockerfileはこちらです。
main.go
package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "<html><body>hello AWS CI/CD</body></html>\n") } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
main_test.go
package main import ( "net/http" "net/http/httptest" "testing" ) func TestHandler(t *testing.T) { testserver := httptest.NewServer(http.HandlerFunc(handler)) defer testserver.Close() res, err := http.Get(testserver.URL) defer res.Body.Close() if err != nil { t.Fatalf("failed test %#v", err) } if res.StatusCode != 200 { t.Error("response code is not 200") } }
Dockerfile
FROM golang:1.15.6 as builder WORKDIR /go/src COPY ./main.go ./ ARG CGO_ENABLED=0 ARG GOOS=linux ARG GOARCH=amd64 RUN go build -o /go/bin/main FROM scratch as runner COPY --from=builder /go/bin/main /app/main ENTRYPOINT ["/app/main"]
マニフェストファイルはこちらです。Fluxがコンテナイメージタグを検索できるよう、 # {"$imagepolicy": "flux-system:test-policy"}
という記述を追加しています。
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: sample-app namespace: default labels: app: sample spec: replicas: 1 selector: matchLabels: app: sample template: metadata: labels: app: sample spec: containers: - name: app image: 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/test-ecr-cicd:main-000000000000-0123abc # {"$imagepolicy": "flux-system:test-policy"} ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: sample-app namespace: default spec: selector: app: sample ports: - protocol: TCP name: http port: 80 targetPort: 8080
※参考:
EKSクラスターの作成
次にEKSクラスターを作成します。今回は eksctl
コマンドで作成しました。
$ eksctl version 0.77.0 $ eksctl create cluster -f eks-clusterconfig.yml
Fluxのインストール
次にEKSクラスターへFluxをインストールします。今回は flux install
コマンドを利用しました。
$ flux --version flux version 0.24.1 $ flux install --components-extra=image-reflector-controller,image-automation-controller
次にFlux用のリソースを作成します。今回はコンテナイメージリポジトリのモニタリングも行うので、以下のように複数のCustom Resourceを利用しています。
GitRepository
Kustomization
ImageRepository
ImagePolicy
ImageUpdateAutomation
GitRepository
Kustomization
のファイルはこちら。
gitrepo.yaml
--- apiVersion: source.toolkit.fluxcd.io/v1beta1 kind: GitRepository metadata: name: codecommit-cd namespace: flux-system spec: interval: 1m url: https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/codecommit-cd ref: branch: main gitImplementation: libgit2 secretRef: name: https-credentials --- apiVersion: v1 kind: Secret metadata: name: https-credentials namespace: flux-system type: Opaque data: username: <base64 encoded username> password: <base64 encoded password> --- apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 kind: Kustomization metadata: name: codecommit-cd namespace: flux-system spec: interval: 5m0s path: ./manifest prune: true sourceRef: kind: GitRepository name: codecommit-cd validation: client
今回はソースコードのリポジトリにCodeCommitを利用し、アクセスはHTTPS認証を使っています。FluxからCodeCommitを利用する場合は、こちらのリンク先に書かれている通り、Secret
リソースにアクセス情報を定義します。また、Fluxがリポジトリからマニフェストファイルを利用するため、 Kustomization
リソースも合わせて作成します。
※参考:
ImageRepository
ImagePolicy
ImageUpdateAutomation
のファイルはこちら。
imagerepo.yaml
--- apiVersion: image.toolkit.fluxcd.io/v1beta1 kind: ImageRepository metadata: name: test-ecr-cicd namespace: flux-system spec: interval: 1m0s image: 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/test-ecr-cicd secretRef: name: ecr-credentials --- apiVersion: image.toolkit.fluxcd.io/v1beta1 kind: ImagePolicy metadata: name: test-policy namespace: flux-system spec: imageRepositoryRef: name: test-ecr-cicd filterTags: pattern: '^main-[0-9].*-[a-f0-9].*' policy: alphabetical: order: asc --- apiVersion: image.toolkit.fluxcd.io/v1beta1 kind: ImageUpdateAutomation metadata: name: test-automation namespace: flux-system spec: interval: 1m0s sourceRef: kind: GitRepository name: codecommit-cd git: checkout: ref: branch: main commit: author: email: fluxcdbot@users.noreply.github.com name: fluxcdbot messageTemplate: '{{range .Updated.Images}}{{println .}}{{end}}' push: branch: main update: path: ./manifest strategy: Setters
secret-cronjob.yaml
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: ecr-credentials-sync namespace: flux-system rules: - apiGroups: [""] resources: - secrets verbs: - get - create - patch --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: ecr-credentials-sync namespace: flux-system subjects: - kind: ServiceAccount name: ecr-credentials-sync roleRef: kind: Role name: ecr-credentials-sync apiGroup: "" --- apiVersion: v1 kind: ServiceAccount metadata: name: ecr-credentials-sync namespace: flux-system --- apiVersion: v1 kind: Secret metadata: name: aws-credentials namespace: flux-system type: Opaque data: AWS_ACCESS_KEY_ID: <base64 encoded AWS Access Key> AWS_SECRET_ACCESS_KEY: <base64 encoded AWS Secret Access Key> --- apiVersion: batch/v1 kind: CronJob metadata: name: ecr-credentials-sync namespace: flux-system spec: suspend: false schedule: 0 */6 * * * failedJobsHistoryLimit: 1 successfulJobsHistoryLimit: 1 jobTemplate: spec: template: spec: serviceAccountName: ecr-credentials-sync restartPolicy: Never volumes: - name: token emptyDir: medium: Memory initContainers: - image: amazon/aws-cli name: get-token imagePullPolicy: IfNotPresent envFrom: - secretRef: name: aws-credentials env: - name: REGION value: ap-northeast-1 volumeMounts: - mountPath: /token name: token command: - /bin/sh - -ce - aws ecr get-login-password --region ${REGION} > /token/ecr-token containers: - image: bitnami/kubectl name: create-secret imagePullPolicy: IfNotPresent env: - name: SECRET_NAME value: ecr-credentials - name: ECR_REGISTRY value: 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com volumeMounts: - mountPath: /token name: token command: - /bin/bash - -ce - |- kubectl create secret docker-registry $SECRET_NAME \ --dry-run=client \ --docker-server="$ECR_REGISTRY" \ --docker-username=AWS \ --docker-password="$(</token/ecr-token)" \ -o yaml | kubectl apply -f -
Amazon ECRを利用する場合は、12時間ごとにアクセストークンが更新されるため、定期的にアクセス情報を更新するための CronJob
を用意します。とりあえずアクセス用の Secret リソースを作成したかったので、CronJobからJobを作成する以下のコマンドを実行し、Secretリソースを作成しました。
$ kubectl create job docker-registry –from=cronjob/ecr-credentials-sync $ kubectl get job -n flux-system NAME COMPLETIONS DURATION AGE docker-registry 1/1 22s 5h6m
※参考:
- Flux Docs - Image Repositories
- Flux Docs - Image Policies
- Flux Docs - Image Update Automations
- Flux Docs - Automate image updates to Git: AWS Elastic Container Registry
パイプラインの実行
ここまでで必要なリソースの作成は完了しました。この状態で codebuild-ci-build
のジョブが実行されると、ECRへの新規イメージのPushを検知し、CodeCommit上のマニフェストファイルを書き換えます。
# Image Reflector Controllerのログ $ kubectl logs image-reflector-controller-6d94666b7d-962x8 -n flux-system (中略) {"level":"info","ts":"2022-01-16T06:16:31.391Z","logger":"controller.imagerepository","msg":"reconciliation finished in 43.928032ms, next run in 1m0s","reconciler group":"image.toolkit.fluxcd.io","reconciler kind":"ImageRepository","name":"test-ecr-cicd","namespace":"flux-system"}
マニフェストファイルの変更を検知すると、FluxはEKSクラスターへのデプロイを実行し、新しいコンテナイメージを持つDeploymentが起動します。
# sample-appのイメージタグが変更されている $ kubectl get pods NAME READY STATUS RESTARTS AGE sample-app-7689d7759c-7h56f 1/1 Running 0 83m $ kubectl describe pod sample-app-7689d7759c-7h56f (中略) Containers: app: Container ID: docker://b94f50458c881f8b11bbf767dc04b9c12bb5f837392e79651c92e278e745d875 Image: 000000000000.dkr.ecr.ap-northeast-1.amazonaws.com/test-ecr-cicd:main-202201160619-0ce5ebf