みなさんさようなら.インフラ部の@h3_potetoです.
CrowdWorksは大きなRailsアプリケーションですが,最近ではこの大きさで管理していくのもう無理な気がしてきて,マイクロサービスっぽくしていこうという動きがあります(が,まだ全然マイクロサービスではないです).
それでも一部を切り出すことには成功していて,多少なりともマイクロサービスの運用っぽいことも必要になってきました.
で,今回は僕の趣味のデプロイの話です.
サービスはDockerに載せたいよ
CrowdWorks本体は,Docker化まで程遠い感じがしているんですが,切り出したマイクロサービスなら,最初からDocker前提で作ることが出来ます. これなら楽にDockerで本番運用まで行けそうな気がしていました.
他のサービスを色々AWS上に構築していることから,Dockerでアプリケーションを動かすのもAWSでなんとかしたいとは思っていました. やっぱり普通にAWS ECSを使うこととなったわけです.
ECSのデプロイどうしよう問題
ECSのデプロイはちょっとめんどくさいです.
awscliで aws ecs update-service
コマンドを叩けばデプロイできます.が,これは全然便利じゃないです.
Dockerのデプロイというのは,
- CI等で新しいDocker Imageを作成し
- それを使ってホスト上で新しいDockerコンテナを起動し
- 古いコンテナを停止する
というような流れが必要になります.
そして,ECSではサービスがどのDocker Imageを使うかという情報をTasks Definitionに記述しています.
そのため,aws ecs update-service
を叩くにしても,その前に最新のDocker Imageを記述したTask Definitionを登録しておく必要があります.
もう一点問題があります.
awscliのecsコマンドは,コマンドの実行はしてくれますが,ECS Taskが完全に切り替わるまで待っていてくれません. そのため,デプロイが成功したのか失敗したのかを,コマンドから判断することができないです.
ecs-deploy
そこで,以前はecs-deployを使っていました.
https://github.com/silinternational/ecs-deploy
これは,Task Definitionの登録もデプロイコマンド内で行い,ECS Taskの切り替わりも待っていてくれます.
そのため,ecs-deployコマンドを叩いて,あとは待っていればデプロイが成功したか失敗したかを返してくれます. というわけで,これをCI内で叩いて,masterのCIが完了した段階でECSへ自動デプロイしていました.
migrationどうしよう
以前はecs-deployで満足していたんですが,最近新しく作ったサービスはDB migrationが必要なものでした.
こうなると,「じゃぁmigrationはいつ,誰が,どこで実行しようか」という話になってきます.
幸い,ECSでもrun-taskができます.aws ecs run-task
で単発のコマンドをECS内のタスクとして実行できます.
ただ,ecs-deployはこのrun-taskをカバーしていません.
また,CI内での自動実行となると,migrationの実行タイミングをコントロールできません.
たまたま,すごく重いmigrationが走ってしまったり,メンテナンスが必要になるような大きなスキーマ変更が入る場合もあります.
やっぱりslack botでデプロイしよう
migrationの実行タイミングのことを考えると,やはりslack botからデプロイしたいです.
そこで先月見たこちらの記事を思い出します.
あ,Interactive Message使ったbot作りたい
もっと仕事でgolang書きたい
主に僕の趣味でgolangが選出されました.
golangの選出はともかく,Interactive Messageを使いたい理由もあって. マイクロサービスを作るたびにデプロイ用のbotを作るなんて馬鹿馬鹿しいです. 全てECSに乗せていくことが前提なら,同じbotで複数のサービスをデプロイしたいですよね.
で,どのサービスをデプロイするかという選択を,まさにInteractive Messageなら上手いことできると思ったので,これを作りたくなりました.
ecs-goploy
botをgolangで作ることは決まりましたが,大事なことはそこからデプロイコマンドを実行できるという要件です.
ecs-deployはシェルスクリプトなので, os/exec
を使えば外部コマンドとして実行できます.
が,それにしてもやっぱりmigrationの単発コマンドは実行できません.
この部分を自分でgolangで書くなら,最初からデプロイコマンド群もgolangのパッケージとして提供してほしい.
そして,botではそのパッケージを import
して使えるようにしたい.
と思ったので,全部自分で作ることにしました.
これはOSSにしてあります. そのままコマンドラインツールとしても使えるようになっているので,その辺の使い方はREADMEを見ていただけると.
だいたいecs-deployを再実装した感じにはしてありますが,コマンド体系は少し変わっています.
$ ./ecs-goploy service
というコマンドを用意してあるので,こちらでECS Serviceのデプロイができます.
実行することとしては,
- 動いているECS Serviceの情報を取得
- 上記の情報から今動いているTask Definitionを割り出す
- Docker Imageの項目だけ新しいimageで上書きしたTask Definitionを登録(もしimageの指定がない場合は元と同じ内容のTask Definitionが再登録される)
- 新しいTask Definitionを指定して
aws ecs update-service
相当のAPIを叩く - 新しいタスクが動き出すのを待つ
- もし新しいタスクが動き出さない場合,元々動いていたTask Definitionで再度
aws ecs update-service
しロールバックする
くらいです. タスクのデプロイ終了条件と,ロールバックに関しては完全にecs-deployと同じ判定条件になっています.
また,単発のrun-taskを実行できるように,
$ ./ecs-goploy task
というサブコマンドも用意しています.
だいたいserviceと同じような手順でaws ecs run-task
相当のことをやります.
- 引数で与えられたTask Definitionが実際に登録されているか確認する
- 引数で与えられたTask DefinitionのDocker Image項目だけ,新しいimageで上書きしたTask Definitionを登録(もしimageの指定がない場合は,引数で与えられたTask Definitionと同じ内容のものが再登録される)
- 新しいTask Definitionを指定して
aws ecs run-task
相当のAPIを叩く - 新しいタスクの実行完了を待つ
- タスクの実行が終了したら,そのタスクの
ExitCode
に応じた出力を返す
こちらもタスクの実行終了を待ち,ExitCode
の確認もやってくれます.
ExitCode
が0以外だった場合,このコマンドもエラー終了となります.
さらに,
import "github.com/crowdworks/ecs-goploy/deploy"
とすることで,packageとしての利用もできます. 軽くgodocも用意してあります.
上記のデプロイコマンド実行順序を変更したい場合や,間に別のタスクをはさみたい場合などに活用してください.
できたもの
packageができれば,あとは渡す情報をどうやって取るかだけなので,slack botの作り込みだけです.
だいたいこのへんは,設定ファイルをいっぱい書いたりしたんですが,ほとんどgolang的筋力で乗り切れます.
で,できたものがこちら.
サービスが増えてきたら,slack botの設定を追加していくだけで,この選択肢が増えていきます.
slack botも今までhubotばかりだったのですが,こうやって勝手にgolangで作ったものを投入していくことで,じわじわと社内のgolang勢が増えています.
We’re Hiring!
クラウドソーシングのクラウドワークス では,デプロイが趣味の変態紳士 を募集中です.仕事でgolangを書きたい方も是非Σ(‘◉⌓◉’)