GKEとIstioを使ってテスト環境を増殖させた話 | Geppoプロダクトブログ
はじめに
こんにちは鹿島(@kashitaka)です。今までリクルートライフスタイルにてAirの開発をしていましたが、4月からリクルートとサイバーエージェントのジョイントベンチャーの株式会社ヒューマンキャピタルテクノロジー(HCT)にてプロダクト開発グループの責任者をやってるものです。
弊社では従業員のコンディション可視化・改善ツール「Geppo」を運営しており、今回はそんなGeppoに参画して最初に着手した改善の1つ「テスト環境増殖プロジェクト」について書きます。
何に困っていたか
まず始めに参画当初の状態としてはこんな感じでした。
■状態
・製品はAWS Elastic Beanstalk(以下EB)というPaaSにホストしているアプリケーション
・クラウドのテスト環境はEB環境が2つのみ。あとは各個人のローカル開発端末
・テスト環境へのデプロイ作業は操作権限を持ったサーバーサイドエンジニアが手動で行う
そのため、課題としては大きく分けると以下の2つ
■課題1: テスト環境の少なさ
・テスト環境はQAのために次回リリースブランチが占有されてることが多い
・他の変更をテストする際には、今テスト中のものが終わるまで待つか、中断する。またはテスト中のブランチにマージする
・ちょっとしたアイデアや改善を気軽にチームにお披露目する場所がない
■課題2: デプロイ作業の面倒さ
・デプロイ作業にはアカウント発行やAWS CLIの準備の他、インフラやセキュリティに関する知識も必要で、フロントエンドチームやQAが作業するのは現実的ではない
・どの環境にどの変更が出ているかはデプロイした担当者のみが知っている
・EBのデプロイに時間がかかる(だいたい1時間半くらい ※これはEBの問題というより弊社の構成が最適化されていない)
簡単にいうとテスト環境利用のための調整コスト・学習コストが高く、作業も煩雑でした。こんな状態をどう変えたいかというと
■持っていきたい状態
・誰でもテスト環境の利用状態がわかる
・誰でも簡単にテスト環境にデプロイできる
・誰でも自分のアイデアや改善を気軽にチームにお披露目できる
という感じにして楽々開発にしたいというものです。
ステップ1: まずはローカル開発環境のDocker Compose化
これは本記事とは別の課題で着手したものでしたが、以降のステップで利用するので最初に触れておきます。
課題としてはローカル開発環境の構築手順が、Ruby, Bundler, Node.js(2バージョン共存), ImageMagic, PostgreSQLなどなど入れるものが多く、新規参画者が毎回苦労してたのでコンテナ化&Docker Compose化したものでした。コンテナの説明は別サイトにも多いのでここでは割愛します。Docker Composeとは複数のコンテナを設定し、動かすためのツールです。各コンテナの依存関係やHost名を定義できるようになるので、コンテナ間の通信などが管理しやすくなったりします。
構成はこんな感じです。
これによって新規参画者はDockerのインストールのみで開発環境を作れるようになりました!
ステップ2: テスト環境増殖のインフラ選定
さて本題のテスト環境増殖プロジェクトですが、まずはインフラの選定として、次のような候補を検討しました。
① EB環境を増やす案:既に動いている2環境をそのままの構成で増やせばいいので構成の再利用ができますが、1環境ごとにEC2クラスタとRDS, Managed Redisの利用料がかかるためテスト環境にしてはコストがかかるので不採用
② EC2, GCEなどのサーバーを立てる案:サーバーを運用したくない・また環境分だけサーバー台数増やすのも無駄が多いので不採用
③ Google Cloud Run案:個人的な推しはこれでした。コンテナを上げれば勝手にやってくれる感が素晴らしいですよね。ただGeppoの仕様としてマルチテナンシーなドメインをサポートしており、Cloud Runで実現できなかったので断念。(詳しくは省略※注1)
④ Google Kubernetes Engine(GKE)案:Kubernetesは(ご存知の方も多いと思いますが)コンテナの運用を見越した高機能なコンテナオーケストレーションツールです。サーバー(ノードと言う)とその上に乗るアプリケーションが分離されて抽象化されており、計算リソースも有効活用できるし環境が枯渇したら増やす・余ったら減らすのが簡単。設定は複雑だが柔軟。そしてコストをざっと試算したところ圧倒的に安かったので採用!(コスト結果は記事の最後に発表)
ステップ3: テスト環境作り
インフラが決まったので次は開発です。結果的にはこのような構成になりました。ここではポイントを説明していきます。
■GCLB(図の左側)
まずインターネットからのアクセスはGoogle Cloud Load Balancing(GCLB)で終端させています。その際Cloud ArmorでIP制限をかけ、社内のIPのみを許可するようにしています。
この構成のいいところは外部からのパケットがサーバーに到達することがないのでKubernetesのIngressでリクエストをいきなり受けるよりも、設定ミスなどによるうっかりサービス公開の懸念がなくなります。
■ Istio Ingress Gateway(図の真ん中あたり)
GCLBを通ったリクエストはIstioのIngress GatewayとVirtual Serviceのルーティング設定に従い、リクエストのHostヘッダーに応じて対応する名前空間(後述)のWebサービスに転送されます。
例えば
dev1.hoge.comでアクセスすると名前空間dev1のWebへ
dev2.hoge.comでアクセスすると名前空間dev2のWebへ
ルーティングするという具合です。
■環境と名前空間(図の右側)
環境の区切りはKubernetesの名前空間を利用します。テスト環境ごとにdev1, dev2, ...といった具合に名前空間を作っていきます。dev1 ~ devNの中に配置するコンテナは全て同じ構成にすることで環境間でインフラの差分が無くなります。
定義ファイルの構成も上のように各環境で使い回す設定はまとめます。そうすると環境を増やす時や、構成に変更があった時は上の例だと
kubectl -n dev99 apply -f ./application
のように -n で名前空間を指定してディレクトリごと反映させれば、コマンド一発で環境構築できるので楽です。(この瞬間がめちゃめちゃ気持ちいい)
■各環境の中身
名前空間の中に配置するコンテナの構成は基本的にステップ1で作ったDocker Composeのものと同じです(PostgreSQLやRedisにGCPのマネージド製品を使わず、素のPodにしている理由はコスト安のため)。なので基本的にここでの作業はDocker Composeの設定をKubernetesの記法に移植していくだけになります。いきなりKubernetesから書くと難しい場合はDocker Composeでコンテナオーケストレーションツールのイメージを着けるとわかりやすいと思います。
ステップ4: CI作り
誰でも簡単にデプロイできるようにCIから適用させるようにします。
GitHubのブランチにpushするとそのブランチでコンテナを作り、指定された環境のPodのイメージを更新するような動きです。
デプロイ先の環境はGitHubのラベルで指定します。こうすることでGitHub のPR一覧を確認すれば、誰がどの変更をどの環境に適用しているかが一発でわかります。またエンジニア以外でもPR番号を伝えれば環境を利用できます。(※これは以前Airのプロジェクトにいた時にチームメンバーが発明した方式です)
結果
ということで、チームのメンバーはステップ4のラベルのデプロイ方法さえ学べば誰でも変更内容をチームにお披露目できるようになりました。ちょっとしたバグ修正や、障害対応などの変更の動作確認のスピードが上がりましたし、複数の機能開発を並行してQAできるようになりました。
またGKEはリソースの効率性がよく、同一クラスのGCEを利用することによるインスタンス継続利用割引と、全ノードをプリエンプティブルVMで運用することで、テスト環境を5環境に増やしても月13,000円くらいとなっています。やったぜ!
コンウェイの法則でもシステムと組織の密接さについてよく言及されますが、インフラ構成は開発プロセスや意思決定にすごく影響を与えると感じており、今回は参画最初のスピードアップ施策としてインフラ改善に突っ込ませていただきました。
今後もHCTの開発チームは自分のアイデアを高速にアウトプットできる環境を目指します!こんな感じで少しずつですが今後もGeppoプロダクトブログを続けていきます。ではでは〜👋
----------------------
※注1 Cloud Runを諦めた経緯について:Geppoは企業アカウントごとにサブドメインを発行するマルチテナントなアプリです(SlackのURLみたいに企業ごとにドメインが違う)。WebアプリはリクエストのHostヘッダーを元にどの企業アカウントからのアクセスかを判別するのですが、Knativeが最新リビジョンのpodへルーティングする上でHostヘッダーをIngress Gatewayで書き換えるので、Webアプリに到達するリクエストは元のHostヘッダーがわからなくなります。ワークアラウンドとしてカスタムドメイン機能を利用企業の数だけ登録するというのがありますが、登録件数やSSL証明書の適用など考えると複雑になってしまいます。