はじめに
少し前のことになりますが、担当しているサービスの基盤をDockerコンテナベースに作りかえました。本番環境はECSを中心とした仕組みで動かしています。
レガシーなWebサービスをコンテナで運用できるようにしたことを振り返り、コンテナで動かすときはこうすべきという教科書的な話は置いておいて、考え方として大切だと思ったことや実際にやってみてよかったことを書いてみます。
似たようなことをしたいけど一歩踏み出せない時の参考になれば幸いです。
背景
話の前提として元の環境やここに至るまでのストーリーを書こうと思いましたが、書いてはいけないことを書きそうな気がしてきたので割愛します🙊
一般論にすりかえると、レガシーなWebサービスってこういうのじゃないかな!ということになります。
- 手作業でのデプロイ
- 構成管理されていない環境
- 本番環境、ステージング(とは言い難い)環境、ローカル開発環境、それぞれが秘伝のタレ化
- ステートフルなWebサーバ
- Webサーバと言う名の何でも屋さんサーバ
- クラウドサービスと言う名の仮想サーバホスティングサービス上で稼働(往々にして、卒倒しそうな月額と年間契約)
一言でいうと「動けばいい」という状態。
考えたこと
インフラもアプリケーションの一部
コンテナベースの環境になると必然的にインフラのコード化がある程度推進されるので、それをどう管理するかを考える必要があります。
その際、運用体制の都合だったりなんとなくだったりで、アプリケーションとインフラを別物と考えて話を進めがちですが、どちらもWebサービスを実行するために必要なもので、同じようにコードで表現できるのなら分けて扱わないほうがよいはずです。
コンテナから少し話がずれますが、クラウドの本質はAPI、つまりインフラをソフトウェアとして扱えることだと思っています。Infrastructure as Codeの考え方はクラウドありき、というと言い過ぎかもしれないですが、文脈としてはそんなに外れていないと思います。
コンテナの運用も、そのへんと地続きで考えるとよさそうです。
ポイント
- 開発プロセスをイメージする
- ビルド、デプロイ方法をイメージする
- ローカル、開発、ステージング、本番...
変化に強い構成にする
AWSなどのクラウドサービスを使う時に気になりがちなのが、いわゆるロックイン問題です。
次々に便利で値段も安いマネージドサービスが登場し、使いたいけどどうしようというケースは多いのではないかと。
この点について真面目に考えると、サービスのライフサイクルや、チーム体制、許容できるランニングコスト等を考慮しながらもろもろ設計が必要になりますが、個人的にはサービスのコアなところがマネージドサービスに依存しすぎていなければ、それほど恐れなくていい気がしています。
アプリケーションの実行環境をコンテナにできていれば、自然とバックエンドサービスと疎な状態が実現できているはずなので、もし他の環境で動かすことになってもそれほど難しくないはずです。
迷った挙句にいろいろなことが停滞してしまうのが一番のリスクです。
導入してみてうまくいかなそうなら元に戻せばよいし、新しくよさげな選択肢が出てきたのならまた取り入れればよいのです。
とはいえ、いきあたりばったりにはならないように注意が必要です。構成管理ができていないまま、ぽちぽちクリックしてたらできました、というのでは次の変更が難しくなってしまいます。
それでは結局いつまでもレガシーなままです。
ポイント
- マネージドサービスをどのくらい使うか
- AWS(など)じゃなくても動かすイメージがつくか
- コンポーネント同士の依存関係は適切か
- s3がないとこのコード動かねえ・・・みたいな状態を避ける
自動化を目的にしない
インフラと同様に、各種運用ツール/サービスもAPIで操作することが一般的になりました。
ソフトウェアのコンポーネントの一部になったということは、どこまででも自動化できるということです。
自動化は楽しいし、あれもこれもという気持ちになりますが、あくまでも自動化は手段です。
それ自体が目的ではなくて、開発と運用をうまく回すことが目的だということを忘れないようにしたいです。
あまり時間をかけすぎずにスモールスタートして、日々の運用の中でここ自動化したほうがよさそうというところを自動化していくくらいのほうが、結果としてチームの実態からずれない仕組みができるのではないかと思います。
ポイント
- 職人技になっていないか
- 職人がいなくなったらブラックボックスになってしまう状態は避ける
- そのチームでやりやすいように
- チームの体制は?
- メンバーのスキルセットは?
- Opsなタスクを分割、整理。そのパーツを作る。ビルド。デプロイ。等。
- パーツができたら、様子を見ながらパイプラインを作っていく
- 一つのことをうまくやる(ref. 『UNIXという考え方』Mike Gancarz 著、芳尾 桂 監訳)
ちょっとしたプラクティス
Twelve-Factor App を読む
説明不要かと思いますが、原理原則を理解していることは大切です。
すべてを守れなくても、知っている上で型から外れるのか、そもそも知らないのかでは大違いです。
今のチームは私以外開発メンバーが中心だったので、コードを書く時もこんなことを考えたほうがいいよねという共通認識を少しでも作るため、週1で輪読会を開催するようにしました。
アプリケーションのコードリポジトリにインフラのコードも含める
アプリケーションのコードとインフラのコードを同じリポジトリで管理すると、以下のようなメリットがあると思います。
- このリポジトリのコードがあれば環境を再現できる
- コンテナのビルド、環境の構築(本番/開発...)、etc.
- CI/CD的なフェーズでの利便性
- アプリケーションとインフラの状態の不整合が起こらない
- 一つのシステムを作っているというチーム感の醸成
今回はコンテナ=アプリケーションの実行環境ととらえて、コンテナのビルドに必要なものを1つのリポジトリにまとめました(+後述のMakefileや、ローカルで開発環境を起動するためのdocker-compose.yml等も含めています)。
なお、その他ECSやバックエンドサービスの定義はterraformで書いて別リポジトリとしましたが、これも一緒にしたほうがCI/CD的なフェーズでいろいろやりやすかったかもと思うところがあります。
ref.『Infrastructure as Code』Kief Morris 著、宮下 剛輔 監訳、長尾 高弘 訳
9.2.6 プラクティス:アプリケーションコードとインフラストラクチャコードを統一的に管理せよ
AWSを使うなら、AWS Developer Tools(開発者用ツール)群を活用する
いわゆるCode3兄弟を中心?とするサービス群のことです。
IAMのおかげでCredentialの持ち方とかを考えなくていいのが最大のメリットです。
ロックインや移植性云々と天秤にかけても、機密情報をどこで持つの的な悩みが減るのはありがたい。
少し前にCodeCommitがプルリクをサポートしたので、ハマるならGithub以外の選択肢として考えてもいいかもしれません。
せこい話ですが、稟議とかが面倒な現場ではAWSだけに支払い先がまとめられるメリットもあります。
今回は基本的にCodeBuildでビルドやデプロイ的な処理を実行するプロジェクトをパーツとして作り、様子を見ながらCodePipelineで繋げていくような方針にしています。
Makefileに書く
運用系のタスクはMakefileに書くようにしました。
パーツをMakefileのターゲットとして定義しておき、例えばローカルでの環境構築は
make local
コンテナのビルドは
make build
といったように実行できるようにしています。
あとは、自分のMacでもCodeBuildでも、それを実行するだけです。
枯れたツールを使うことで基盤に依存しないし、CodeBuildから乗り換える時も影響が少ない。
あと、エラーできっちり止まってくれるところもCI系ツールと相性がいいと思います。
おわりに
で、レガシーじゃなくなったんですか?という質問に対しては、最初に挙げたレガシー一般論は解消されたと言えます。
でも全てが思うようにできたわけではなくて、こういう世界を目指していきたいですねという内容も含まれています。一部偉そうなポエム調になってしまい恐縮ですが、そのへんご理解くださいませ。
読み返してみると、なんかあんまりコンテナ関係ないなこれ・・・と思いつつ、でも世界観としては書きたいことを書けたので結果オーライということで。
乱文にお付き合いいただきありがとうございました。何か一つでもどなたかの参考になれば幸いです!