GitHubActionsにCDKのdiff/deployを組み込んだ話

理想と現実

こんばんは、最近GitHubActionsにCDKを組み込んで試行錯誤したので記録に残しておきます(N番煎じですが)

最近、GitHubActionsのGitHubActionsにCDKをCI/CDとして組み込み機会がありました。がっつり組み込む機会は今までなかったので色々やりたかったことを考えてtryしてみました。

GitHubActionsとCDKを使った理想のCI/CD

理想のCI/CDを考えてみました

  • cdkのコードをgitで管理していて、mainブランチのコードが検証環境を表していて、productionブランチが本番環境を表す。
  • mainブランチ/productionブランチへのPRが作成されると、cdk diffが走ってdiffの結果がprettyな状態でPRにコメントされる
    • GitHubと外部ツール連携した時によくあるやーつ
  • mainブランチ/productionブランチにpushされると、cdk diffが走ってcdk deployが走る
    • デプロイに失敗したらslackか何かに通知
    • マージ時に発火させるだけでなく手動での発火も可能

これらの機能が提供されているような、デファクトスタンダードなCDKのActionsがあると思っていたんですが、意外とないんです…

/img/2023-04-27_02.png

aws-cdk-github-actionsは良さげな雰囲気があるのですが、CDKの引数をこのactionに渡す際にエスケープ処理か何かでエラーが出て私の環境では解決できませんでした。確かこんな感じだったと記憶してる

      - name: cdk diff
        uses: youyo/aws-cdk-github-actions@v2
        with:
          cdk_subcommand: 'diff'
          cdk_args: '--require-approval never --app "npx ts-node --prefer-ts-exts bin/app.ts"'
          actions_comment: true

そもそもローカルで実行する時は--appの指定が不要なはずなのになぜCIで必要になるのか理解できてなかったけど、今思うと実行してるdirectoryがズレてただけかもしれない…😂

それに気づかずその時は結局全部自前で実装することにしました。

実際に組んだCI/CD

mainブランチ/productionブランチへのPRが作成されると、cdk diffが走ってdiffの結果がprettyな状態でPRにコメントされる

GithubActionsのyamlのstepsはこんな感じになりました

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'

      - name: ライブラリインストール
        run: npm ci

      - name: diff出力
        run: npm run --silent cdk diff -- --no-color &> diff.txt
        env:
          AWS_DEFAULT_REGION: 'ap-northeast-1'
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: Comment PR
        if: ${{ always() }}
        uses: thollander/actions-comment-pull-request@v2
        with:
          filePath: ./diff.txt
          comment_tag: diffcomment

      - name: Archive diff.txt
        if: ${{ always() }}
        uses: actions/upload-artifact@v3
        with:
          name: cdk-diff
          path: ./diff.txt

cdkのdiffの結果はファイルに出力されるのではなく標準出力と標準エラー出力に出てくるので、意図的にファイルにリダイレクトさせます。
また、テキストを見やすくするため色の出力をとめています。
また、npm経由で実行するとnpmの文字が出力の各行の先頭に出てくるので抑制するために--silentを設定してます

diffの結果をPRにコメント表示するためにComment Pull Requestを利用しました。めちゃ簡単でした
1点残念なのが、diffの結果を全く加工していないのでめちゃくちゃ読みにくくなってしまってることです。

こんな感じです↓

/img/2023-04-27_01.png

`[~]`が取消線として扱われちゃってる

PRにコメントできるようにしたものの、見にくいと困るのでartifactsにも設定するようにしました。ここはGitLabCIと比べて設定がわかりやすくてよかったです

mainブランチ/productionブランチにpushされると、cdk diffが走ってcdk deployが走る

こんな感じになりました

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'

      - name: ライブラリインストール
        run: npm ci

      - name: diff出力
        # ...

      - name: Comment PR
        # ...

      - name: Archive diff.txt
        # ...

      - name: デプロイ
          TIMEOUT=${{ github.event.inputs.deployTimeout }}
          timeout ${TIMEOUT:-"1200"} npm run cdk deploy -- --require-approval never
        env:
          AWS_DEFAULT_REGION: 'ap-northeast-1'
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

CDKをデプロイする際の最大実行時間は3時間らしく(長すぎひん?)なおかつ実行時に自由に設定することはできません。過去にissueは立ってましたが改善されることもないのだと思われます。見た感じはCDKのベースになっているCloudFormation側でもタイムアウトの設定ができないみたいですね。。。

3時間もGitHubActionsを回すわけにはいかないので途中で打ち切りたいですよね(打ち切ると言ってもCloudFormationのスタックは動いたままですけど)
そのためにtimeoutコマンドを利用して、タイムアウト期間はデフォルトで20分としてます。

バックエンドのコードが変わった際のデプロイだと10分程度で終わるんでしょうけど、(頻度は少ないですが)大きくインフラ構成を変える場合には1時間ぐらいかかるかもしれません。また、実行が失敗した場合には再実行したいはずです。
なので、手動による再実行時にはタイムアウト時間を可変に設定できるようにしています。トリガーの設定はこんなかんじ

on:
  push:
    branches:
      - main
      - production
  workflow_dispatch:
    inputs:
      deployTimeout:
        default: ""
        type: string
        description: デプロイ完了/失敗を待つ秒数

また、デプロイの結果は重要なトピックなので開発者に必ず通知されないといけないと思ったので、slackに通知するようにしました。
slackにGitHubアプリをインストールして/github subscribe owner/repo workflows:{name: "cdk-deploy"}するだけで設定できるので便利です

https://developer.mamezou-tech.com/blogs/2022/12/12/notify-github-actions-workflow-to-slack/

できてないこと・課題だと思ったこと

  • diffがprettyではないのでとても見にくい
    • ファイルの前後に```を入れるだけでだいぶ見やすくなるはず…。
  • CDKのcliからタイムアウトの設定ができるようになってほしい
    • 上記の仕組みのままでは、タイムアウトした際にCloudFormationに移動して結果を自分で監視する必要がありとても時間の無駄です。
    • Cloudformationの正常終了時・以上終了時にEventBridgeのトリガーが発火するみたいなのでそれをもとにslack通知ができるともっと良くなるかなと感じました
  • DeletionPolicy、最初はDELETEにすべきだった
    • RETAINのままdeployを試行錯誤するのはめちゃくちゃめんどくさかった🤮

See also