tech.guitarrapc.cóm

Technical updates

Pulumi を CircleCI で Continuous Delivery する

この記事は、Pulumi dotnet Advent Calendar 2019 の13日目です。

qiita.com

Terraform では、PRに連動させてどのようにPlan/Apply をするのかを一度は考え、すでに実践しているはずです。 私のお勧めはAtlantis ですが、CircleCI でもいいでしょう。

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 による通知がされています。

f:id:guitarrapc_tech:20191209023006p:plain

GitHub PR の checks を見ると、circleciの pulumi/approve がPending なのでクリック。

f:id:guitarrapc_tech:20191209023013p:plain

CircleCI Workflow上で approve することで pulumi up が実行される

f:id:guitarrapc_tech:20191209023025p:plain

f:id:guitarrapc_tech:20191209023033p:plain

すべてのジョブが実行されると GitHub のChecks が通るので、PRをマージします。 pulumi up に失敗した場合、ここで検知して修正を commit することができます。

f:id:guitarrapc_tech:20191209023043p:plain

Pulumi が提供している Continuous Delivery

複数のCIを使ったCDを提供しています。

Continuous Delivery | Pulumi

GitHub Actions にもそうそうに対応されていて、フットワークの軽さがあります。

f:id:guitarrapc_tech:20191210095522p:plain

事前準備

事前に4つ準備をします。

  • GitHub Apps のインストール

GitHub Apps - Pulumi

  • dotnet core 3 + node + glibc な Docker Iamge の準備

作成してあるのでご利用ください。

DockerHub - guitarrapc/docker-dotnetsdk-node-aws

  • Pulumi の AccessToken を取得

CircleCI での pulumi の実行に使います。

  • AWS で PulumiがAWSで 実行するユーザーのAccessKey、AccessSecret の準備

GitHub Apps について

Pulumiが公式に提供している Pulumi GitHub Apps をインストールします。

Pulumi GitHub App

これを使うことで、GitHub の PR に Pulumi の Stack変更をコメントしたり Checks API にサマリ表示できるようになります。

Pulumi は、gitリポジトリで Pulumi cli を実行すると、ブランチ、コミットIDをトラッキングしており、Activityに表示されているのがわかります。(一部のCI環境ではPRの番号が自動トラックされます。対応していないCIでも手動で情報を与えることができます。)

f:id:guitarrapc_tech:20191209023053p:plain

GitHub Apps の Pulumiは、この情報に沿ったPRに対して PulumiのStack変更をコメントします。

コメントには、リソースの変更サマリcreated, updated, deleted が含まれており、Pulumi Console へのリンクも含まれます。

f:id:guitarrapc_tech:20191209023100p:plain

また、Checks API と統合されるので、どんな変更があったのかさくっとみることもできます。最高。

f:id:guitarrapc_tech:20191209023107p:plain

なお、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 を使います。

https://github.com/sgerrand/alpine-pkg-glibc

この Dockerimage ではそういう対策も入っています。

CircleCI の構成

Pulumi公式で CircleCI の構成が紹介されています。これに沿って組むといいでしょう。

CircleCI

GitHub へのPulumi Logs の結果通知は GitHub Apps に委託できるので、CircleCI は pulumi の preview適用前のapprovepulumi の 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/refreshpulumi/preview, pulumi/update を使うでしょう。ほかにもテスト環境の構築、削除を行うこともできます。

CircleCI Orb Registry - pulumi/pulumi

AWS の認証credential ファイルの作成は、aws cli orb を使っています。 aws-cli/setup を使うと aws configure 相当の認証作成を行ってくれます。

CircleCI Orb Registry - circleci/aws-cli

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 の実行ログが表示されます。

f:id:guitarrapc_tech:20191209023117p:plain

GitHub Apps があれば無用です。何かに使いたいときにどうぞ。

FAQ

CircleCI でpulumi up が失敗しても具体的なエラーメッセージがPR上に表示されない

現状の仕様では、pulumi upfailed したことはわかりますが、そのエラーメッセージや CircleCI や pulumi console を見ないとわからないです。

GitHub の PR にコメント表示してもいいのではないかと提案しています。

GitHub Apps not show error message when pulumi up failed. · Issue #3617 · pulumi/pulumi

REF

Continuous Delivery | Pulumi