Studyplus Engineering Blog

スタディプラスの開発者が発信するブログ

Kubernetesを本番導入しました

こんにちは、SREチームの栗山(@sheepland)です。 さて、スタディプラスでは2020年9月に念願の Kubernetes本番導入 を果たしました🎉。 本番導入といってもまだ10マイクロサービスあるうちの1つをKubernetes上で稼働させているだけです。しかしそこに至るまで様々な苦労がありました。 今回はKubernetes本番導入をするにあたりやってきたことや使用したツールを紹介したいと思います。

スタディプラスのインフラの現状の課題

Kubernetes導入の話の前にスタディプラスのインフラの現状の課題を簡単にお話したいと思います。

デプロイ速度が遅い

スタディプラスの一番大きなメインのサービスはAWS Elastic Beanstalkで稼働しています。 Elastic Beanstalkを使ってgracefulにデプロイするためには、EC2インスタンスを追加→デプロイ→古いインスタンスを停止といった流れになります。EC2インスタンスを1から起動しセットアップ&デプロイするのでどうしてもデプロイ時間がかかってしまいます(現状10分以上かかっている)。

Elastic Beanstalkのマネージドサービスがゆえの不自由さ

Elastic Beanstalkはあまりサーバーを意識せずに使えるというメリットがありますが、専用の知識が必要でデプロイも遅く設定の更新やロールバックも遅いといった課題があります(他にも細かい不満はたくさんあったりします)。 またローカルで動かすことができないので試行錯誤は実際のElastic Beanstalkを使って行う必要があります。マネージドサービスのつらいところですね。

AMIの更新、OS/ライブラリ/ミドルウェア/言語のバージョンアップが手間がかかる

スタディプラスのメインのサービス以外はEC2上で動いています。 それらのEC2のAMIの更新やOS/ライブラリ/ミドルウェア/言語のバージョンアップをする場合、新しいEC2を立ててリクエストをそっちに流し古いEC2を削除する...といった流れになり手間がかかるという問題があります。

CI/CDパイプラインが複雑

CI/CDパイプラインにはJenkinsおよびHubotを使っていますが、なかなか複雑なフロー&処理内容になっています。 またJenkins自体の管理コストの高さも問題です。

カナリーリリースが手間がかかる

言語、フレームワークのバージョンアップや、大きな機能のリリース時にはカナリーリリースをしたくなるものです。しかし現状、カナリーリリースをするためには、新しいElastic BeanstalkもしくはEC2をたてて、新しい方へ少しずつリクエストを流す必要があります。そして終わったらインスタンスを破棄し…となかなか手間がかかります。

Kubernetesを選んだ理由

上記であげた課題を解決するために、Kubernetesを導入することを決めました。 「アイデアというのは複数の問題を一気に解決するものである」という言葉がありますが、まさにKubernetesは様々な課題をまとめて解決できる可能性を秘めている素晴らしいソリューションです。

他にも、コンテナオーケストレーションツールとしてKubernetesを選んだ理由としては以下があります。

  • 必要な機能が揃っており、やりたことがスマートにできる
  • 自己回復力(セルフヒーリング)の高さ
  • ユーザ数の多さ、人気の高さ
  • 強力なエコシステム
  • 進化の速度が速い
  • 高い拡張性
  • 特定のベンダーに依存しない

導入戦略

いきなり一番大きなメインのサービスをKubernetes移行するのはリスクがあったため、手始めにEC2上で稼働している小さめのサービスをKubernetes上で稼働するようにしました。失敗しても影響が小さいサービスを移行し、そこから得た知見を元に一番大きなサービスを移行するという戦略です。 結果的にはKubernetesの知見を溜めることができ、Kubernetes本番導入までの時間を短くできたので戦略として正解でした。

構成

KubernetesクラスタとしてEKSを使っています。 LBに関してはAWS ALB Ingress Controllerを使って作成をしています。 またシングルテナント(サービスごとにKubernetesクラスタを作成)にすると、Kubernetesクラスタバージョンアップのコストが高くなってしまうため、マルチテナントを選択しました。

監視ツールの移行

監視システムはMackerelを使っています。しかしDatadogのほうが高機能でKubernetesにもフィットするため、Datadogに移行することを決めました。 現在はKubernetes上で稼働しているサービスとAWSサービス(RDS、ALB等々)をDatadogで監視しており、今後全ての監視をDatadogに移行する予定です。

IaCツールの移行

IaCツールはAnsibleをメインで使っています。しかしAnsibleのAWSモジュールはあまりメンテナンスされていないという問題や、現在の構成との差分が取れない、使っている人が少ないため情報が少ないという問題があります。 そこでKubernetes移行のタイミングでAWSリソースをTerraformを使って構成管理していくことに決めました。 まだ全てをTerraform化はできていませんが、Kubernetesクラスタを作成するのはTerraformを使ってできるようになっています。

使っているツール

Kustomize

マニフェストファイルの管理にはKustomizeを使っています。 Kustomizeの良い点は以下です。

  • 環境差分をシンプルに定義できる
  • ConfigMapやSecretを更新した際にPodも合わせて再生成できる(configMapGenerator、secretGenerator機能)
  • リソースに対してnamespaceやlabelを簡単に一括付与できる

Skaffold

KubernetesへのデプロイツールとしてSkaffoldを使っています。 Skaffoldの良い点は以下です。

  • imageのbuild、imageのpush、マニフェストファイルのapplyをまとめて実行できる
  • ローカルのKubernetesでも、ローカル以外のKubernetesでも同じコマンド(skaffold run)でデプロイできる
    • またローカルの場合はimageのpushがskipされる
  • ローカルではskaffold devコマンドによって、コードの変更を自動検知して imageのbuild、マニフェストファイルのapplyを自動実行してくれる。これによりDockerfileの修正やマニフェストファイルの修正の確認が素早く行える
  • profile機能によって環境差分が定義できる
  • image tagの生成ルールが柔軟に指定できる

その他

kubectx、kubensはcontext、namespaceの切り替えに重宝しています。(今後はkubectlプラグインのほうを使っていきたい) またkube-ps1も、どのcontext、namespaceを使っているかがひと目で分かり重宝しています。

デプロイ

デプロイサービスとしてCircle CIを使っています。 最近のトレンドであるGitOpsも考えましたが、ファーストリリースはできるだけミニマムの構成にしたいというのと、チームの学習コストの増加を避けるために、アプリケーションのCIでも使っているCircle CIを使うことを選択しました。(今後はGitOpsでデプロイすることも検討しています。)

デプロイの流れは以下のようになっています。

  1. masterブランチにPull Requestをmergeする
  2. Circle CIのworkflowが起動する。以降はworkflow内の処理
  3. imageのbuild & ECRへpushがされる
  4. staging環境へデプロイがされる
  5. Slackへ「staging環境へデプロイが終わりました。動作確認をして問題なければ承認ボタンを押して下さい」と通知がされる(Circle CIのジョブへのリンク付き)
  6. Circle CI上の承認ボタンを押したら、production環境へデプロイがされる
  7. デプロイ完了通知をSlackへ通知がされる

細かい工夫としてはproduction環境デプロイ前にkubectl diffを実行して、現在の環境とこれから適用するマニフェストの差分をCircle CI上で実行&出力し、意図した変更が行われるかを確認できるようにしています。(Slackに差分確認ボタン付き通知もしている)

ログ収集

ログ収集&ログ分析は Fluentd + S3 + Athena で行っています。 詳しくは以下の記事をご覧ください。
Kubernetes上でのFluentdを使ったログ収集について

負荷試験

サービスを新しい基盤で動かす際には負荷試験が不可欠です。 今回はLocustを使って負荷試験を行い、本番のリクエスト数の3倍でも問題ないかと、どのくらいまでのRPSを処理できるかをテストしました。 負荷試験によってどこがボトルネックになるかを把握でき、万が一処理限界に達した場合に監視で気付けることを確認できて、安心してリリースすることができました。

クラスタバージョンアップ試験

今後定期的にKubernetesクラスタをバージョンアップしていく必要があります。 その際スムーズにバージョンアップができることを確認するためにバージョンアップ試験をしました。
あえて古いバージョンでクラスタを作成し、作成したバージョンアップ手順書に沿ってバージョンアップ作業を行い、滞りなくバージョンアップができることを確認しました。 この事前のクラスタバージョンアップによって、Terraformのディレクトリ構成の改善(複数バージョンのKubernetesクラスタを定義できるように修正)ができたりしてとても有意義でした。

Kubernetesを組織に浸透させるために

真にKubernetesを組織に浸透させるためには、SREメンバーだけではなくアプリケーションエンジニアもKubernetesに習熟することが肝要です。 しかしKubernetesはそれなりに学習コストがあり、実際に手を動かしてみないと理解が難しいという側面があります。
そこで現在はKubernetes輪講会を開催しており、Kubernetes完全ガイドを教材にして持ち回りで講義を開いています。輪講という形であれば強制的に人に教えられるレベルに引き上げられるため、参加者の負担が大きいですがとても効果的だと感じています。まだ始めたばかりですがKubernetes輪講会の完走を目指して参加メンバー一丸となって頑張っています。

今後について、やりたいこと

ひとまずKubernetes導入は達成できましたが、やりたいことはまだまだあります。
まずはメインシステムのKubernetes移行です。それが終われば、GitOps、 スポットインスタンスの活用、サービスメッシュ、Progressive Deliveryなどを検討していきたいと思っています。

さいごに

今回は一部のサービスをKubernetes移行しましたが今回の作業によって下地ができたため他のサービスのKubernetes移行は楽になっていると思います。今後はKubernetes移行を加速させ、開発速度を上げていき、サービスの価値向上に結びつけていきたいと思っています!