STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

デプロイコマンドを作りながら気をつけたもの

はじめに

こんにちは。 CTO 室の id:riseshia です。 最近は STORES の開発環境とか運用周りの改善する仕事をしており、今回の記事ではデプロイプロセスの改善の話をします。

やったことはデプロイ用の Slack コマンドを用意しただけなのですが、このような ChatOps を導入しました記事は数年前から(具体的な手段は時期によって変わるものの)世の中にたくさんあり、 大変面白みが不足すると思ったので、今回はどのような要求があり、どのような点に気をつけながら進めていったかを解説します。

背景

となれば、まずは導入前のデプロイプロセスに関する解説が先ですね。

今までは決められた Slack チャンネルで「デプロイ宣言」というワークフローを使っていました。 CI が通ったのを確認して該当ワークフローを実行するとフォームが現れます。 そのフォームにデプロイに関する情報(i.e. 環境、サービス名、デプロイする差分の説明など)を埋めて提出するとそれがチャンネルへ流れます。 その後 VPN に切り替え Rundeck へアクセスし、該当ジョブを人が実行する、という流れでした。

このプロセスは次のような点が良かったと思います。

  • デプロイすることを開発チームへ周知している
  • どのような差分がデプロイされるのか情報(PR/Issue)を提供している
  • Rundeck へのアクセスはデプロイ権限を持つ人に制限されており権限制御もしている
  • 誰がデプロイしているか記録が残っている

一方で不便な点もいくつかありました。

  • 人がデプロイの差分を頑張って記述しないといけないので記述漏れが起こり得る
  • Rundeck へのアクセスには VPN が必要で、デプロイをする毎に VPN に接続したり切ったりする必要がある
  • 連続マージが発生すると意図してない差分が混ざっても気づきづらいし、場合によってはデプロイができない

このような状況を踏まえ、次のような目標を立てて作業をはじめました。

  • デプロイ差分を自動で抽出して宣言を生成したい
  • 現状のセキュリティを損なわない
  • 連続マージでも問題なくデプロイができる
  • デプロイ毎に Rundeck へアクセスしなくても済むようにする

システムの設計

デプロイコマンドの設計図

設計はこんな感じになりました。 前述したとおり設計に関しては特に面白いものはないですが、1つだけあげるとしたら Slack からコマンドを受信した Lambda 関数は要求されたタスクにすぐ取り掛からないことがあります。 代わりに Slack 側へコマンドを受け取ったというレシートだけを返します。 これはデプロイすべきターゲットリビジョンを決めて、現在のリビジョンとの差分に含まれている PR 一覧を生成する作業におおよそ 2-3秒かかるため、 Slack App が要求するレスポンスタイムの上限に引っかかる(Confirming receipt - Slack)からです。

作りながら気をつけたもの

ここでは実装しながら注意していた点をあげていきます。

コマンドだけで完結させること

実はもともと想定していた使い方としては /deploy storesjp-production のように引数を渡す方式でした。 ですが、設計中に /deploy-storesjp-production のように1サービスに対して1コマンドを用意するほうが Slack 側の自動完成も効くしかつ説明も乗せられるので便利だよね、と id:hogelog さんから提案されて、たしかにそうだなと思い、その提案を採用しました。

もし同じことをしたい場合は Slack コマンド名の長さは最大 32 文字であることに注意してください。

ユーザーマニュアルと運用マニュアルを分ける

当たり前のようですが、利用者に必要なものと運用者に必要な情報は違います。 どのような違いがあるかというと、利用者にはシステムの使い方がメインで、運用者にはそれに加えシステムを運用するための論理的な知識も必要です。 ちょっとそれっぽい表現をすると利用者には How-to とチュートリアルが必要で、運用者にはアーキテクチャの説明のようなレファレンスが必要です。

ですので今回は想定読者にあわせてドキュメントを2つに分けました。 利用者向けのマニュアルには利用可能なコマンドとその説明と期待とおりに動かない時の問い合わせ先を、運用者向けのマニュアルにはシステム設計への説明と依頼が来たときにどのような状況である可能性があるか、設計時点で予想されている問題の一覧とそのトラブルシューティングの仕方などを解説しています。

利用者に迷いが無いように振る舞う

デプロイコマンドは一見簡単にみえますが、正常なワークフローでも失敗するケースがあります。 例えば、 CI がまだ終わってなくデプロイ可能なリビジョンがない、デプロイ用のチャンネルではないところでデプロイを実行しようとしている、古いリビジョンをデプロイしようとするなどなどです。 こういう場合は何が起きているかを利用者へ伝え、取るべきアクションを案内するようにしています。例をあげてみます。

  • 秒で返せない処理をする場合、処理を進めているというメッセージを先に返す
  • 1回しか押せないボタンは押したら消えることで不要に何回も押さないで済むようにする
  • 認証・認可が不足してる場合、どれが足りなくてなにをすべきか教える
  • デプロイチャンネルを間違った場合に正しいチャンネルの案内
  • 古い宣言でボタンポチする場合は宣言し直すことを勧める
  • ネットワークエラーなら再実行をお願いする
  • 利用者側でできることがなさそうならメンテナに問い合わせることを提案する

それとシステム設計のところで触れていますが、差分の計算には時間がかかるので利用者がちゃんと動いているのか不安がらないように適切な反応を返してあげるのも重要です。

コンテキストを残す

実装を始める前に Design Document を書きました。 他の人たちがどのような理由で書いているかはわからないですが、自分はなぜこの変更をする必要があるか、提案している設計の意思決定に関する解説、採用していなかったものに関する解説を残し未来のメンテナに役立つことと、これを書くことにより自分自身でセルフレビューすることを目的として書くことが多いです。

今回はネットワーク周りがやや複雑でそのへんの解説を残す目的と入社直後で自分の現状への理解が正しいかどうかの確認をもらうための目的もありました。 レビューで実装において面倒になりそうな点を指摘してもらえたので、やはり書いてよかったと思いました。

導入後

導入してから後は比較的スムーズにマイグレーションが進みました。

  • あらゆる状態に対して先回りして丁寧なメッセージとネクストアクションを仕込んでおいたのが期待とおりに機能していた
  • 改善しようとしていたところに対してフィードバッグがありました
    • デプロイ実行のために VPN へ切り替える必要がなくて嬉しい
    • デプロイの差分解説が自動生成できて便利
    • などなど

使われている様子を見守りつつ集まった感想はこんな感じで、丁寧に作った甲斐がありました。

まとめ

この記事では STORES のデプロイフロー改善のためにデプロイコマンドを作った話をしました。 その中でもどのような要求があったのか、どのようなところに気をつけながら進めていったのかを解説しました。