こんにちは!SRE部のcw-ozakiです。
長らく携わっていたPHPのレジェントシステムをEC2からKubernetesに移行する作業がひと段落ついたので、どのようにこの移行を進めて行ったのかご紹介していきます。
こちらの記事とこれから書いて行く記事は過去にJapan Container Days V18.12のMeetupとAWS Dev Day Onlineで話をさせていただいた内容の完全版となります。また、12月にあるPHP Conference 2020 Re:bornでも関係した話をしますので楽しみにしてください。
ChatworkはなぜEC2からKubernetesへの移行を踏み切ったのか
ChatworkではScalaに移行します!と宣言をしているとおり、サービスは順次Scalaに置き換えています。しかし、何年もChatworkを支えて来たPHPのレジェントシステムはまだ全体の大部分を占めており、全てをScalaに置き換えるにはまだ時間がかかることがわかっています。
というわけで、PHPのレジェンドシステムの運用はまだまだ続けて行くのですが、Chatworkのサービス規模が拡大する中でデプロイとロールバックが不安定になるという問題が発生してきました。
このプロジェクトを開始した2017年時のデータでは月に100PRほどのリリースを行なっており、そのうち5〜10%ぐらいの確率で変更に失敗していました。そのため、デプロイやロールバックが不安定になるということはChatworkの日々の改善を行えなくなります。また、ロールバックが出来なかったりすると、リリース起因でサービスが止まってしまった際にSLAの稼働率への影響や、MTTRが長くなることでユーザーの体験を損ねてしまいユーザーからの信頼性を減らしてしまいます。
そのため、このデプロイとロールバックはサービスを支えていくにあたってとても重要になってきます。
課題1: EC2インスタンスが40台超えたあたりからデプロイが不安定になる
PHPのレジェンドシステムではcapistranoを使ってデプロイを行なっています。
そのため、大きな流れとしては下記のような流れになります。
- ASGを停止する
- Deployサーバーでcomposerを使って依存ライブラリを解決して、各サーバーに配布する
- Deployサーバーでcapistranoを使ってコードを更新、シムリンクを更新後にNGINX、PHP-FPMを更新
- AMI用インスタンスを使ってAMIを作成
- 新しいLaunch Configurationを作成して、ASGを再開
これがだいたいEC2インスタンス40台ぐらいまでなら安定して動作するのですが、40台を超えだすと全行程に數十分かかりだしたり、デプロイに失敗するケースが増加し始めました。
また、サービスのスループットの上昇に伴い、例えばライブラリのアップグレードによってクラスの削除などの後方互換のない修正を行なった際に、タイミング問題で削除したクラスを読み込もうとして一時的にPHPエラーが上昇するという問題もあります。
課題2: デプロイ状態によってロールバックができなくなる
ロールバックもデプロイと同様にcapistranoを使い、シムリンクで前世代に戻すという仕組みになっています。
これは全サーバーのシムリンクを変えるだけなので1分もかからずに戻すことができますが、サーバーの状態によってはロールバックできないケースがあります。
1つめは各サーバーの世代管理がずれるケースです。
これは各コードの更新が途中で失敗したときにロールバックできずに再度デプロイして成功した場合や、ASGで起動するAMIがずれたときにこの問題が発生します。この場合、ロールバック先の世代が存在しないためロールバックができなくなります。
2つめはロールバックに失敗して世代の指定がずれる場合です。
これはリリースした内容によってCPU使用率がふりきってしまい、ロールバックが実行できなかったときなどに発生します。こうなってしまうとCPU使用率が下がるのを待つか、該当インスタンスを停止する必要があり復旧に時間がかかるようになってしまいます。
過去にはcomposerでライブラリを更新したさいにはロールバックができなくなるというのもありました。このようなロールバックができないときがあるというのは、開発者への負担や信頼性の低下に繋がり、ロールバックで解決できるケースでもロールバック以外の方法で解決しようとすることになります。
Kubernetesという解決策
先に挙げた課題自体はEC2で解決する方法はあると思います。
例えばPush方式ではなくPull方式に変えて並列性をあげることでデプロイ時間を短縮したり、エラー時にリトライをすることで必ず想定した状態にする。AMIを事前に作成して入れ替えることでデプロイやロールバック後の状態を必ず一定にする。Blue Greenデプロイで複数の環境を用意して新旧の切り替えを行うなど、いろいろなやり方はあると思います。
しかし、だいたいEC2/AMIの構築の遅さ、EC2の世代管理という状態を全て揃える必要があるという問題、デプロイ/ロールバック方式の変更にかかるコスパからEC2とcapistranoを維持したままこの問題を解決するのは難しいという結論に至りました。
そこで、ChatworkではKubernetesを使ったデプロイ/ロールバックへの切り替えを決断しました。
考え方的にはAMIを使った方式と同じで、事前にイメージと新しいタグを作成し、ローリングアップデートしていきます。こうすることでロールバックするさいにも一つ前のタグに戻してローリングアップデートすれば、確実に前回のデプロイ状態と同じところに戻すことができます。
AMIと違ってKubernetesを使う利点は
- デプロイ/ロールバックはKubernetesの仕組みに乗れるため構築が不要
- イメージの作成がAMIより高速
- コンテナの起動がEC2より早いため入れ替えが高速
というところになります。
現行のデプロイ/ロールバック方式と比べるとロールバックにかかる時間は増加しますが、より安定して確実なデプロイとロールバックを行え、デプロイ単独でみればより高速にデプロイできます。
もちろん、これまでEC2ベースで組んでいたインフラ周りを全て置き換える形になるので、移行にかかるコストは膨大です。それでも最終的に全体のインフラ構成が良くなり、課題であるデプロイとロールバックの安定するということから、このような決断を行いました。
Kubernetesという選択肢
ところで同じコンテナオーケストレーションを採用するなら、ECSやDocker Swarm、OpenShiftでも良かったと思いませんか?もしくはより高速にデプロイ/ロールバックできるServerlessとしてLambdaでも良かったと思いませんか?
このあたりはChatworkは結構特殊で、過去の記事で公開している通り既にKubernetesクラスタを運用しており、わざわざ別の製品を利用するという選択肢はありませんでした。
まぁ変えても良かったのですが、全体で利用ツールを統一することでできる全体最適を超えるメリットを見出せなかったのでKubernetesを採用しました。
これは余談ですが、SREはEC2とKubernetesの2つの方式を覚える必要があり、どちらも学習コストが高いからチームのスケールへの問題や、複数箇所の修正が必要でありオペレーションコストの増加に繋がるなどいくつかの問題もありました。そういったことからもやはり選択肢としてはKubernetesだけだったと思います。
まとめ
- ChatworkではEC2で動かしてたPHPのレジェントシステムをKubernetesに移行してるよ
- サービスのスケール速度に耐え切れる、高速で安定したリリース基盤が必要だったよ
- すでにKubernetes入ってるというので、だいたいこれ一択だったよ
という訳で今回はなぜEC2からKubernetesに移行するのかという決断の背景でした。
次回はこの続きで、移行にあたっての移行戦略と、移行にあたって既存のインフラの仕様を洗い出した方法について紹介していきます。
ChatworkではSREメンバーを絶賛募集中です!
このKubernetes化の話やChatworkの各種インフラや体制など興味があればカジュアルに話を聞きにきちゃってください。