この記事は、Pulumi dotnet Advent Calendar 2019 の13日目です。
Terraform では、PRに連動させてどのようにPlan/Apply をするのかを一度は考え、すでに実践しているはずです。 私のお勧めはAtlantis ですが、CircleCI でもいいでしょう。Terraform Cloud もいいですね。
Pulumi には残念ながら Atlantis はないので、PRに連動させて CircleCI との Continuous Delivery を組んでみましょう。
目次
TL;DR
公式の Continuous Delivery にある中から、CircleCI を使ってPRベースで pulumi preview
+ Approve
+ pulumi up
を 実行できるようにしてみましょう。
ここで目指すのは、PR マージまでに pulumi up
ができる仕組です。
Atlantisのように、PR内でその変更のエラーも含めて解決するコンセプトは、ビルドして実行すれば確定するわけではない開発フローにおいて大事です。
Why
Pulumi は State を Web 上に持っているとはいえ、Pulumi をローカルで実行していると terraform をローカルで実行するのと同じ事故が起きます。
環境
Pulumi .NETCore で書いたリソースを AWS 環境に適用します。
- Pulumi 1.6.1
- Pulumi dotnet (C# を利用)
- AWS
完成イメージ
まずは軽量に CircleCI でPRベースで実行プレビュー、手動で許可、実行という流れでやります。
理想的にはAtlantis のような仕組みが欲しいのですが、まずはPRベースを目指します。
開発フローです。
- ブランチを作成して変更をcommit & push
- PRを作成
- PRをトリガーに、CircleCI のWorkflow で Pulumi Preview Jobが実行
- GitHub Appsが、Pulumi Preview 結果を PRにコメント通知
- GitHub の checks から circleci の
pulumi/approve
を選択しWorkflow を開く - Workflow の
approve
をするとpulumi_update
が実行される
実行イメージです。
GitHub PR で Pulumi の GitHub Apps による通知がされています。
GitHub PR の checks を見ると、circleciの pulumi/approve
がPending なのでクリック。
CircleCI Workflow上で approve することで pulumi up
が実行される
すべてのジョブが実行されると GitHub のChecks が通るので、PRをマージします。
pulumi up
に失敗した場合、ここで検知して修正を commit できます。
Pulumi が提供している Continuous Delivery
複数のCIを使ったCDを提供しています。
GitHub Actions にもそうそうに対応されていて、フットワークの軽さがあります。
事前準備
事前に4つ準備をします。
- GitHub Apps のインストール
- dotnet core 3 + node + glibc な Docker Iamge の準備
作成してあるのでご利用ください。
- Pulumi の AccessToken を取得
CircleCI での pulumi の実行に使います。
- AWS で PulumiがAWSで 実行するユーザーのAccessKey、AccessSecret の準備
GitHub Apps について
Pulumiが公式に提供している Pulumi GitHub Apps をインストールします。
これを使うことで、GitHub の PR に Pulumi の Stack変更をコメントしたり Checks API にサマリ表示できるようになります。
Pulumi は、gitリポジトリで Pulumi cli を実行すると、ブランチ、コミットIDをトラッキングしており、Activityに表示されているのがわかります。(一部のCI環境ではPRの番号が自動トラックされます。対応していないCIでも手動で情報を与えることができます。)
GitHub Apps の Pulumiは、この情報に沿ったPRに対して PulumiのStack変更をコメントします。
コメントには、リソースの変更サマリcreated, updated, deleted が含まれており、Pulumi Console へのリンクも含まれます。
また、Checks API と統合されるので、どんな変更があったのかさくっとみることもできます。最高。
なお、Stack に変更がない場合、コメントはありません。Checks APIには変更がなくても書き込みされます。
Pulumi .NETCore 用の docker イメージの用意
guitarrapc/docker-dotnetsdk-node-aws
を用意したので使ってください。
CircleCI 上でDockerコンテナを使って Pulumi CLIを動かします。 ただ、Pulumi .NETCore をAWS環境で実行するには、Pulumi が公式に提供している node イメージでは足りません。
必要なリソースは次の通りです。
- .NET Core SDK 3.0: C# コードのビルド、実行を行うため
- Node: Pulumi 実行のため
- glibc: Pulumi dotnet の gRPC 通信のため
- aws cli: Pulumi-Aws 環境を aws configure するため (configureのみなので、シェルで書いてもok)
Pulumi CLI 自体は、CircleCI Orb によってインストールされるのでバージョン管理をしないためにもインストールしないでおきます。
AWS CLI も同様にバージョン管理したくないものの、結構大きいのでイメージに含めてしまいます。
これらを含めた Dockerイメージはないので、Alpine をベースに作りました。
guitarrapc/docker-dotnetsdk-node-aws: Docker, AWS-CLI, .NET SDK, Node
余談
Pulumiは Aplineで動作する前提ではないものの https://github.com/pulumi/pulumi/issues/1986 を見ると libc6-compat を追加すれば動くとあります。入れると確かに pulumi コマンド自体は動きますが、 Pulumi .NETCore なStackを pulumi up
すると依存ライブラリが見つらずgRPC 実行でこけます。
Error loading native library "/home/dotnet/app/runtimes/linux/native/libgrpc_csharp_ext.x64.so"
これを解消するには、https://github.com/grpc/grpc/issues/18428 を参考に sgerrand/alpine-pkg-glibc を使います。
この Dockerimage ではそういう対策も入っています。
CircleCI の構成
Pulumi公式で CircleCI の構成が紹介されています。これに沿って組むといいでしょう。
GitHub へのPulumi Logs の結果通知は GitHub Apps に委託できるので、CircleCI は pulumi の preview
、適用前のapprove
、pulumi の update
の3ステップに集中できます。
考えるべきこととして、pulumi の update
はどのタイミングで行えばいいでしょうか?
pulumi preview ではビルドエラー、pulumi の仮実行でのエラー検知はできますが、リソース作成時におこるエラーは検知できません。となると、PRのmasterマージ時に pulumi up
の適用は、PRで修正が完結させる保障がないので避けたいところです。今回はAtlantis に習い、「PR のマージ前に適用する」ことでPR内で修正コミットも行って完結もできるようにしましょう。
circleciの config.yaml 全体です。
version: 2.1 # set following EnvironmentVariables # - PULUMI_ACCESS_TOKEN # - AWS_ACCESS_KEY_ID # - AWS_SECRET_ACCESS_KEY # - AWS_DEFAULT_REGION orbs: pulumi: pulumi/pulumi@1.2.0 aws-cli: circleci/aws-cli@0.1.18 executors: dotnetcore3: docker: - image: guitarrapc/docker-dotnetsdk-node-aws:1.16.292 environment: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true NUGET_XMLDOC_MODE: skip # PR will try CI/CD pulumi on any branch w/o master. (please apply pulumi change on PR) # `preview` -> `approve` -> `up` workflows: pulumi: jobs: - pulumi_preview: filters: tags: only: /.*/ - approve: type: approval requires: - pulumi_preview filters: tags: only: /.*/ branches: ignore: master - pulumi_update: requires: - approve filters: tags: only: /.*/ branches: ignore: master jobs: pulumi_preview: executor: dotnetcore3 working_directory: ~/repo/ steps: - checkout - pulumi/login: version: 1.7.1 - pulumi/refresh: stack: STACK - pulumi/preview: stack: STACK pulumi_update: executor: dotnetcore3 working_directory: ~/repo/ steps: - checkout - aws-cli/setup - pulumi/login: version: 1.7.1 - pulumi/refresh: stack: STACK - pulumi/update: stack: STACK skip-preview: true
ポイントを順にみていきます。
環境変数
今回はAWS環境へのリソース展開なので、Jobの環境変数にPulumiのアクセストークンとAWSのアクセスキーを設定します。
- PULUMI_ACCESS_TOKEN
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_DEFAULT_REGION
Pulumi のアクセストークンは、Pulumi Web UIから自分のプロファイルのアクセストークンを利用しました。
Orb
pulumi のコマンド実行は、Pulumi から公式に提供されている pulumi
orb を利用します。
主に、pulumi/login
, pulumi/refresh
、 pulumi/preview
, pulumi/update
を使うでしょう。ほかにもテスト環境の構築、削除を行うこともできます。
AWS の認証credential ファイルの作成は、aws cli
orb を使っています。
aws-cli/setup
を使うと aws configure
相当の認証作成を行ってくれます。
Executors
用意した Docker Image guitarrapc/docker-dotnetsdk-node-aws
を使います。
あとは Workflow と Jobs を書けば完成です。実行してみると、イメージ通りにコメントが飛び、CI/CD が回ります。
余談: GitHub Apps を使わない Pulumi の実行結果のPRコメント
GitHub Apps に気づかず、pulumi preview
の結果を GitHub に投げつける仕組みを作っていたので一応張り付けておきます。
CircleCI で puluimi preview を実行して、その結果を GitHub Comment API で書き込むものです。
jobs: pulumi_preview: working_directory: ~/repo/ steps: - checkout - pulumi/login: version: 1.6.1 - run: pulumi preview --stack master --cwd ~/repo | tee pulumi.log - run: name: post github pr comment command: | # pick up pulumi log's Diagnostics line num, and escape " to ' for JSON validation. PULUMI_PERMALINK=$(tail -n 1 pulumi.log) PULUMI_DIAGNOSTICS_LINESTART=$(cat pulumi.log | grep -x "Diagnostics\:" -n | cut -f 1 -d ":") PULUMI_MESSAGE=$(echo $(tail -n "+$PULUMI_DIAGNOSTICS_LINESTART" pulumi.log | head -n $PULUMI_DIAGNOSTICS_LINESTART | sed -e 's/$/ \\n/g' | sed -e 's/"/'\''/g')) WORKFLOW_URL=https://circleci.com/workflow-run/${CIRCLE_WORKFLOW_ID} PULL_REQUEST_ID=$(echo $CIRCLE_PULL_REQUEST | grep -o -E '[0-9]+$' | head -1 | sed -e 's/^0\+//') GITHUB_PR_COMMENT_URL=https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/issues/${PULL_REQUEST_ID}/comments?access_token=${GITHUB_ACCESS_TOKEN} curl -f -X POST -H 'Content-Type:application/json' -d "{\"body\":\"### WORKFLOW\n\nclick [HERE]($WORKFLOW_URL) and approve pulumi up.\n\n### PULUMI LOG\n\n$PULUMI_MESSAGE\"}" ${GITHUB_PR_COMMENT_URL}
Pulumi の実行結果を取りたかったので、tee しています。 Pulumiの実行ログから、Diagnostics 以下を取得しています。 Workflow のApprove を押す前提なので、Workflow へのリンクも貼るようにしています。
これで、GitHub PR に Workflow へのリンクと Pulumi の実行ログが表示されます。
GitHub Apps があれば無用です。何かに使いたいときにどうぞ。
FAQ
CircleCI でpulumi up が失敗しても具体的なエラーメッセージがPR上に表示されない
現状の仕様では、pulumi up
が failed
したことはわかりますが、そのエラーメッセージや CircleCI や pulumi console を見ないとわからないです。
GitHub の PR にコメント表示してもいいのではないかと提案しています。
GitHub Apps not show error message when pulumi up failed. · Issue #3617 · pulumi/pulumi