By

GKEによる無限ステージング基盤の構築

こんにちは。
エンジニアの平形( @bigplants )です。
最近の趣味はランニング、筋トレです。
三ヶ月で15kgほど絞りました。
今は細マッチョを目指してボディを磨いております^^

今日は、私が弊社で最近構築した無限ステージング基盤のお話をします。


はじめに

今回、弊社ではステージング環境を刷新する為にGCPのGoogle Kubernetes Engineを利用する事にしました。
技術的な詳細の内容に踏み込むと、このブログでは伝えきれないので概要だけを書きました。
技術的な内容は近日中にQiita(bigplants)で公開します。


Kubernetesとは?

Kubernetesはコンテナ化したアプリケーションのデプロイ、スケーリング、および管理を行うための、オープンソースのコンテナオーケストレーションのツールです。

Kubernetesについては、こちらの記事で詳しく解説されています。
今さら人に聞けない Kubernetes とは?


無限ステージング??

無限に新たにステージング環境を構築できる事を勝手に「無限ステージング」と呼んでいます。
「無限」というところがミソです。


無限ステージング基盤の要件

  • 無限にステージング環境が作れる
  • ユニークなURLを払い出す
  • コンテナ、データは完全に各環境毎に隔離されている
  • SSL対応
  • コマンド1発「ッターン!」で素早く環境作成が完了する
  • Gitの特定のブランチをデプロイできる
  • なるべく金銭的コストは抑えたい

これらの要件を満たすのにKubernetesが最適と判断しました。


そもそも何の為に必要だったの?

弊社では以下のような課題がありました。
それを解決する新しい仕組みが必要でした。

デプロイ待ち問題

元々、AWSにステージング環境は用意してありました。
しかし、チケット単位でQAを行う必要があるので、QA完了するまでデプロイを待たないといけない局面がありました。
これは中々に大きいボトルネックとなっていました。

チケット差し戻し問題

QAから差し戻されたチケットが場合によっては予定していたリリースに乗らない事も珍しくありません。
そんな時にどういう対応をしてるかというと、すでにマージされたブランチをリバートする事になります。
リバートするマージが直近のものならそこまで問題になる事はありませんが、過去のリビジョンだった場合、その依存を解決するのは容易ではありません。
とても骨が折れる苦痛が伴います。。

テストデータの問題

ステージング環境のDBはRDSを利用しておりました。
テストデータはテスト完了後に綺麗にする必要が出てくる場合があります。
あとDBマイグレーションを伴う更新の場合はマイグレーション前の状態からテストするケースもあります。
こんな時に実施する事は、RDSのスナップショットを作成しておき、そこからDBをリストアします。
しかしこの方法は時間と手間とお金がかかります。

ステージング環境の金銭的コスト

弊社では、AWSの以下のサービスのリソースを利用しています。

  • EC2 (各種サービスホスティング台数分)
  • ELB
  • RDS
  • ElastiCache
  • Elasticsearch Service
  • S3

1環境を用意するだけでも結構コストがかかります。
なので複数セット用意するのは現実的ではありませんでした。
それにマイクロサービスが複数あるので、デプロイも個別に実行する事になり、大変です。

こういった諸々の課題をGKEで解決しました。


利用したツール等

  • Google Kubernetes Engine
  • Google Cloud DNS
  • Google Cloud Storage
  • Google Cloud Build
  • Helm

どうやって実現したのか?

GKEにKubernetesクラスタを構築

Kubernetes(以降k8s)のオートスケールでNode(クラスタを構成するインスタンス)がスケールするようにした。
この機能はk8sが標準で備えているものなので閾値の設定だけしました。
これで実質、「無限」という事になります。

Google Cloud Buildでdockerイメージを継続ビルド

Google Cloud BuildがGitHubのpushを検知しdockerイメージのビルドをするようにしました。
ここは他のCIツールでも置き換え可能です。CircleCIは弊社では以前から利用していたので、これを利用する事もできましたが、ビルド時間がかかる為、採用しませんでした。
GKEならGoogle Cloud Buildを利用するのが金額面でも導入コストの面でもリーズナブルです。
あと、何より同じGCP上なのでイメージの転送速度が速いです。これはコンテナのオーケストレーション的なメリットが高いです。

Google Cloud DNSにAWS Route53から管理移譲

先ほども書きましたが、弊社は以前からAWSを利用していました。
今回GCPをステージング環境に採用し、ドメインをどう管理するか?が課題でした。
私たちは、Route53で新しいステージング基盤の為のサブドメインを発行し、それの管理をGCPのCloudDNSに移譲する方法を採用しました。
そのサブドメインのグローバルのレコードを作成しています。

Helmを使って隔離された環境を作成

k8sで複数環境を同じクラスタ内に構築するのにどうしても外せない概念はk8sのnamespaceです。
k8sは基本的にnamespaceを跨いで内部のリソースにアクセスする事はできません。
これを利用してリソースを隔離しました。
namespaceを分けて同じk8sのマニフェストを適用するだけならk8sだけで解決できます。
しかし、我々のシステムの要件では以下のような対応が必要になりました。

  • Ingressにユニークな複数ドメインの設定を行う
  • データセットのファイル名の指定

それを手で実施するのはとても骨が折れる作業です。。
そんな時に使えるのがHelmです。
Helmはk8sのパッケージマネージャです。nodeでいうnpmといったところです。
Helmのパッケージは色々公開されており、自分で書かなくてもパッケージを取り込んでコンテナを起動するといった事が可能になります。

Helmはk8sのマニフェストファイルをテンプレート化し、変数で値を書き換える事ができます。
その特性を活かし環境丸ごとHelmで管理する事にしました。
実際、Helmではテンプレートの機能しか使っておらず導入はとても簡単でした。
手元にあるk8sのマニフェストファイルで特定の箇所だけ変数化していきました。

Gitのブランチ or CommitSHAを指定してデプロイできるようにした

dockerイメージのビルド時にタグにGitのブランチとCommitSHAをつけるようにしました。
これによりデプロイ時にタグを指定してコンテナを起動する事ができるようになります。
ブランチ名として有効な文字種だけどdockerのタグでは禁止されているものをうまく扱う為にちょっと苦労しました。
その話は改めてQiitaに書きます。

特定のコンテナで一度しか実行しないものはデプロイ時のshellで実行するようにしたい

例えば、

  • Railsのdb:migrateその他
  • Elasticsearchのインデックス作成

などの作業です。
これらはコンテナが起動する時に毎回実行されては困るものです。
k8sのリソースでこういったjobを実行する事もできるんですが制約が多いのでshellで実行するのが現実的と判断しました。
デプロイ時にコンテナの起動を監視し、起動したら kubectl exec で実行します。
k8sは自分もまだまだ知らない事が多く、調べるのも時間がかかります。絶賛開発中なものに当たる事も少なくありません。
なんだかんだk8sで頑張らなくても最後はshellでどうにでもなる。と考えると気が楽になります。
あとはk8sのバージョンが上がっていけばk8sのみで解決できる日がそのうち来ると思うので、その時に移行してもいいかなと。

Google Cloud StorageでMySQLなどのデータセットを管理

ステージング環境はテストが目的なので用途に応じたデータセットを用意しておきたいものです。
私が採ったアプローチは以下です。

  1. Google Cloud Storage(以降GCS)にMySQLのダンプファイルとElasticsearchのスナップショットのデータを用意しておく
  2. k8sのInitContainerでコンテナ起動時にGCSからデータをダウンロード
  3. デプロイ時にコンテナにダウンロードされたデータファイルをインポート

k8sのcert-managerを使って証明書を管理

※ 実際にはいい感じにできませんでした。。

cert-managerというhelmパッケージを使って証明書の管理をしようとしました。
cert-managerはLet's encryptを使って自動的に無料で証明書の発行、更新を行うものです。
最初はうまくいったんですが、すぐに問題にぶち当たりました。
最初は各環境毎にcert-managerでワイルドカード証明書を発行するようにしました。
ここでLet's encryptの証明書発行のRate Limitに引っかかってしまったんです。
これは一定期間内の証明書発行数の制限です。
各環境毎の各ドメイン個別に証明書発行しようとしましたが、これも上限にすぐに達しました。
普通に考えればここで出る疑問は、「ワイルドカード証明書だから1つだけ発行して使いまわせないの?」という事だと思います。
しかし、k8sはnamespaceを跨いでリソースを共有できないのです。
それがある以上、無限ステージング基盤に適用できないと判断しました。
一旦、ここに関しては発行できたワイルドカード証明書をSecretとして別途、別ファイルで管理して各環境で使いまわしています。
これも更新する必要があるので面倒ですが。。
cert-managerの今後のアップデートに期待しています。


構築した結果どうなったのか?

  • デプロイ時に他のメンバーの事を気にしないで気軽にできるようになった。
  • すぐに動作を試す事ができるようになった。
  • 素早く作って、いらなくなったらすぐに削除する事ができるようになった。
  • ステージング環境の金銭的コストが5分の1くらいになった。

結果、お釣りが出た上にデプロイ待ちやテスト待ちといったボトルネックが解消され、チケット消化率が上がりました!!


まとめ

昨今、k8sは物凄い勢いでアップデートされており、大変注目が集まっています。
k8sのミートアップ(Kubernetes Meetup Tokyo)は毎回、応募者の倍率が増えており、今では3,4倍くらいになっています。
自分も過去に何度も落選しています。。^^A
k8sはコンテナをリーズナブルにまとめる事ができ、その用途は利用ユーザによって多岐に渡っています。
今回のステージング環境の用途以外にもGKEを使って今後以下のような事を検討しています。

  • ログ収集基盤
  • CI基盤
  • プロダクション環境のGKEへのリプレース

他にもやりたい事は沢山出てくるでしょう。
ワクワクしますね^^


エンジニア募集中!

ビジネスバンクグループではエンジニアを募集中しています。

弊社が採用しているテクノロジや開発環境に興味を持った方は、 ここから是非エントリー を!