taiyoh's memorandum

@ttaiyoh が、技術ネタで気づいたことを書き溜めておきます。

git tagを活用してリリース工程を簡略化する

 このエントリは、SmartDrive Advent Calendar 2019の一環です。今回は僕がメインで担当しているプラットフォームプロダクトでのリリース周りについてお話していきます。
 僕が入社して一年以上経ちますが、リリースに伴うフローは以下のようになっていました。

f:id:sun-basix:20191212110932p:plain

 オレンジの枠が手動で実行する部分で、緑の枠がJenkinsのpipelineによるフローです。リリース時でなければtag打ち等の工程は必要なく、CIが終了して開発環境に自動的にデプロイされるのを待つだけです。一応、これをGitHub flowの名目で続けていました。ただこの「GitHub flow」による開発体制には大きく2つの問題があったことに気付きました。

  1. デプロイを始めるまで定形化した手作業がいくつかのあるのと、開始できるまでのリードタイムが長い
  2. 開発環境によるチェック工程が省かれていて、masterマージしても即リリースできる体制になってなかった

 まず定形作業とリードタイムについて、このフローチャートで示されている諸々の工程を終えてデプロイ作業を開始できるまで、CIの待ち時間含めて15分ほどかかっていました。その為に「大した変更じゃないから次にリリースする誰かに任せよう」という心理がメンバー間で働いた結果、masterブランチなのにリリースとの差分が大きくなることがよくありました(よくない)。
 また検証体制について、開発環境でのチェックののちにリリース、というのをmasterブランチ上のみで行っていたため、必然的にリリースできないmasterの状態というのが度々発生していました。

 これが積み重なった結果、実質週一回のリリースみたいな流れが徐々にできてきて、GitHub flowとは何だったのか。。。という気持ちに。
 一番困ったのは、ご想像の通りmasterと本番との差分がそれなりにある状況で本番側でトラブルが発生し、hotfixを作成しないといけないときでした。その時の手段は割愛します。ただ、割と最近の話です。
 こうした諸々を改善するためいくつかの方策を講じ、現在のフローは下の画像のようになりました。

f:id:sun-basix:20191212111037p:plain

 まず大きくは、もともとGitHub flowを掲げていたんですが完全に形骸化していたので、一時的にですがgit flowに切り替えました。これは現状のリリースのペースを定型化するだけでなく、先述のようなイレギュラーな対応をやりやすくするのと、Jenkinsのpipelineを分解する目的がありました。
 git flowの導入に続いて、Jenkinsのpipelineの分解を行いました。docker imageのビルドはCIを待たずに行っても何も問題ないはずで(仮にCIが落ちるのだとすれば直して再度リリースフローを走らせればいい)、思い切って省いたことでこのリードタイムを大幅に削減しました。このためにrelease tagからdocker imageの作成とpushだけを行う専用jobをJenkins上に作成し、GitHubのWebhookをjob triggerとしています。

f:id:sun-basix:20191212111108p:plain

 WebhookはBranch or tag creationのみチェックをつけています。tagと関係ないブランチの作成時もjobが起動してしまうので、job内のステップでjqでjsonの中身をチェックしていて、ref_type != tagだったら後工程をスキップするようにしています。tag creationだけのイベントトリガーが欲しい。。。
 また、git checkout <タグ名>でコードを固定化した状態でビルドを行うようにしたので、ビルド時のcommit hashは期待したものと確実に一致するようになったのも地味に大きいです。以前の運用だと、masterブランチのcommit hashと指定されたtagのcommit hashが一致しない可能性が常に付き纏っていました。

 あとは11/13に遂に正式リリースとなったGitHub Actionsを採用し、goreleaserを使ったフローは自動化しました。

github.com

 このREADMEに書いてあるYAMLはほぼほぼコピペで採用させてもらっていて、起動トリガーだけtag push時に絞っています。

name: Goreleaser runs when new tag was pushed
on:
  push:
    tags:
      - v*

jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@master
      -
        name: Set up Go
        uses: actions/setup-go@master
        with:
          go-version: '1.13.3'
      -
        name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v1
        with:
          version: latest
          args: release --skip-validate --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

 さて、今回の話の発端なのですが、このtogetterのページを何度か読んでいて、新しいリリースフローへの変更のアイディアというかモチベーションが出てきた、というのが大きいです。ありがとうございます。

togetter.com

 しばらく僕が実質リリースマネージャ的なことをやって、各所との調整に疲れてきたというのもあります。また、最近のGitHub flowは以前と少し違うという話も聞きました。

guides.github.com

 この辺を踏まえて、「tagを打ったらどこの環境に対してもリリースできる」というシンプルなルールを適用できるようにして、デプロイ作業に移れるまでのリードタイムを短くしたというのが今回の取り組みになります。少なくとも僕がこのリリースフローで2〜3回行ってみたところ、例の心理的な億劫さはそれなりに軽減されたと感じました。あとは、PR authorにリリースマネジメントをお願いできれば、リリースに伴う負担を僕一人が負うことも減ってくるので、git flowを終わりにして、もっと素早いデリバリが実現できると睨んでます。この辺は年明けから履行予定です。

 引き続き、ストレスのなるべく少ない、素早いCI/CDについては模索していくつもりです。

 弊社もまた全職種募集しておりますので、是非お気軽にお声がけいただく!

smartdrive.co.jp