ヤフー株式会社は、2023年10月1日にLINEヤフー株式会社になりました。LINEヤフー株式会社の新しいブログはこちらです。LINEヤフー Tech Blog

テクノロジー

Kubernetes環境でのカナリアリリース戦略の実装

Yahoo! JAPAN Advent Calendar 2022の12日目の記事です。

データプラットフォーム本部でエンジニアインターンに参加している杉浦です。

VMからKubernetesへ移行を予定しているプロダクトで、Kubernetes環境におけるカナリアリリース戦略を実装しました。これは本番環境でテストする手法の一つです。本記事がみなさんのリリース業務を円滑にする一例として参考になれば幸いです。

本番環境でも、できるだけ安全にテストしたい

Kubernete環境に限らず、アプリケーションの変更は複数の検証ステップを通して品質を保証します。ほとんどの変更は、単体テストやステージング環境におけるQAで品質を十分に確認することができ、安心して本番環境へリリースできます。

一方で、本番環境上でテストするしかない変更も存在します。例えばデータベースのエンドポイントの変更、各種サービスへのアクセスキーを含んだシークレットに対する変更などです。これらは本番環境でテストする必要があります。本番環境におけるテスト方法として、アプリケーションのデプロイとテストの戦略 | Cloud アーキテクチャセンター | Google Cloudでは以下の三つの方法が紹介されています。

  • カナリアテスト パターン
  • A/B テストパターン
  • シャドーテスト パターン

それぞれにメリットとデメリットがありますが、その詳細な説明は上記リンク先のページを参照してください。

私たちのプロダクトでは、三つのうちから次のように選択しました。

まず、A/Bテストパターンは選択肢から外れました。
前提として、プロダクトはデータ分析基盤のAPIです。各種Yahoo! JAPANのサービスからリクエストを受け取って整形し、後続のキューイングサーバー(Apache Kafka)にメッセージを送る役割を果たしています。そのため、ユーザーの反応などをテストするためのA/Bテストを行う必要がありません。

次に、シャドーテストパターンも選択肢から外れました。
このAPIは各種Yahoo! JAPANのサービスがオンライン処理を行っている最中にも呼ばれています。そのため、レイテンシは可能な限り小さくしなければなりません。シャドーテストパターンでは、トラフィックをミラーして新規バージョンのアプリをテストします。このトラフィックのミラーリングが、レイテンシを許容できないレベルで上げてしまうことが事前調査でわかっていました。また、プロダクトは後続のサービスにトラフィックを流すため、シャドーテストパターンではテストしきれない、または非常に複雑なテストシステムを組まなければならないことが予想されました。

最後に残ったカナリアテストパターンは、検証対象のPodを数個だけデプロイして性能評価・動作検証を行うテスト手法です。完璧な安全性があるわけではないものの、カナリアのPodの割合を絞れば影響範囲を小さく抑えることが可能です。私たちのプロダクトでは、全体のPod数が数千個になるため、1,2個のPodをデプロイすれば1%を切るトラフィックしか受け付けないことになり、この割合は十分低いといえます。

3つのテスト方法を比較し、カナリアテストパターンが一番用途に適していたため、こちらによる実装を進めることになりました。

カナリアリリースとは

カナリアテストパターン、もといカナリアリリースとは、プロダクション環境で新バージョンのアプリケーションが正常に動作しているか検証する方法です。稼働中のプロダクション環境に新バージョンのアプリケーションを配置し、実際のトラフィックを流すことで動作を検証します。カナリアリリースにもメリットとデメリットがあります。カナリアリリースは本番環境を用いて検証を行うため、例えばアプリの設定値の変更やシークレットの値の変更など、本番環境でしか再現できない検証を行うことに秀でています。一方で、カナリアをリリースしてから検証が完了するまで数時間を要するため、日々のアプリケーションコードの変更などをテストするのには向いていません。リリースするまでの期間が伸びてしまうためです。

Kubernetesにおけるカナリアリリースは、大体以下のようなプロセスで行われます。

  1. カナリアリリース開始
    1. カナリアをデプロイする。
    2. 実際にトラフィックを流し始める。
  2. 待機
    1. 各種メトリクスを監視する。
    2. もし異常があればロールバックして、終了。
  3. カナリアリリース終了
    1. カナリアだったものを、stableとしてロールアウトする。

カナリアリリースとロールバックの実装

カナリアリリースを行うために、私たちは以下のことを行える仕組みを用意しました。

  • canary版のデプロイ
  • canary版の監視
  • 異常時におけるcanary版の自動ロールバック

それぞれ詳しく見ていきます。

canary版のデプロイ

カナリアリリースは、stable版とcanary版(カナリア版)を共存させ、canary版を検証するために行います。そのため、canary版にトラフィックの一部を流す必要があります。トラフィックの分流は、Kubernetesでは簡単に行うことができます。stable版とcanary版のPodに、同じラベルを設定するだけです。これだけで、stable版とcanary版の両方にトラフィックを流すことができます。なぜこれだけでトラフィックの分流ができるかは、Kubernetes公式のServiceに関するドキュメントに詳細を譲ろうと思います。

私たちはこの仕組みを使い、deploymentを新規作成することによって、カナリアリリースを実装しました。以下にデプロイの様子を示します。

はじめにstable版が存在する環境を用意します。カナリアリリースをおこなっていない、通常時の構成です。

stable版だけが存在する通常時のKubernetes構成図

図中の灰色の破線はリソースの作成・削除を、黒色の実線は通信を表しています。svc, pod, rs, deployはそれぞれKubernetesのService, Pod, ReplicaSet, Deploymentに対応します。

次に、canary版のdeploymentをデプロイします。canary版のPodがServiceのselectorに識別されるよう、stable版のPodと同じラベルをつけています。

canary版も存在するカナリアリリース実施時のKubernetesリソースの構成図

このように、KubernetesのServiceがPodのラベルを識別子としていることを利用してcanary版をデプロイして稼働させています。

canary版の監視とロールバック

canary版は本格運用前の検証のためにデプロイされます。そのため、性能評価のためにメトリクスを監視する必要があります。また、本番環境で実際にトラフィックを受け付けるため、メトリクスに問題があれば即座にロールバックさせる必要があります。

こういった要件を満たすために、メトリクスを監視し異常があればcanary版をロールバックさせる、カナリア監視アプリケーションを開発しました。このアプリケーションは監視対象の数値を得るためのPromQLや異常時の対処方法をCI/CDから受け取って、その情報に基づいて監視を行います。

監視の様子を以下に示します。

カナリアをPrometheusを通して監視アプリケーションが監視する様子

あらかじめ、カナリア監視アプリをデプロイしておきます。カナリアリリースは、次のように行われます。

  1. カナリアリリースのデプロイはCI/CDツールが行います。
    1. カナリアリリースのデプロイジョブは人間が画面上のボタンを押すことによって始まります。
  2. CI/CDツールがカナリア監視アプリケーションに対して、監視ジョブを送信します。
    1. 監視ジョブには、監視対象のメトリクスや監視時間、監視後の挙動などが記されています。
    2. 監視ジョブを受け取ったカナリア監視アプリケーションはPrometheusへのメトリクスのポーリングを開始します。
  3. CI/CDツールがcanary版をKubernetesクラスタにデプロイします。

カナリアのデプロイが完了したら、カナリア監視アプリケーションがあらかじめ決められた時間だけメトリクスの監視を続けます。

カナリア監視アプリケーションがメトリクスに異常を発見した場合、影響を最小限にとどめるために即時ロールバックを開始します。まずカナリア監視アプリケーションがCI/CDツールのロールバックジョブを起動します。次に、そのロールバックジョブがcanaryのDeploymentを削除します。これにより、ロールバックが行われます。(下図参照)

異常検知時のフロー

監視時間中に異常が見つからなかった場合、canary版は正常だったと判断されて監視は終了します。監視終了時は、 canary版のDeploymentはあくまで検証用途であるため、削除されます。また、Slackへカナリアリリースが正常終了したことをテキストで通知します。

正常終了時のフロー

Slackの通知を受けて、運用者がstableを、検証したcanaryの内容にアップデートして、リリースを行います。これにより、カナリアリリースのロールアウトを実現します。このように、カナリアのDeploymentと通常使用されるPodを定義したDeploymentを完全に分離することで、カナリアのロールアウト・ロールバックをkubectl apply -f ...kubectl delete ...のみで行うことができるようになっています。

ロールアウトの手順に出てきた「Slack通知→canaryをstableへ人力で変更してリリース」という作業も自動化できる箇所ではあります。しかし、まだカナリアリリースの運用を行ったことがなく信用度がまだ低いため、人の手を介す方法としています。将来この方法がずっと安定して稼働し、信用度が高まった際には自動化する予定です。

実際の運用方法

ここまではカナリアリリースをどのように実施するかについて、説明してきました。ここでは、そもそもカナリアリリースをいつ使うのか、どのように使うのかについて、私たちのチームを例に挙げて説明します。

カナリアリリースをいつ使うか

カナリアリリースとはで紹介したように、カナリアリリースは時間のかかるテスト戦略です。アプリのリリースのたびに数時間待つようでは、せっかくKubernetesへ移行してリリース速度を上げられてもそのうまみがありません。よって私たちのチームでは、以下のようなシチュエーションでカナリアリリースを行う予定です。

  • アプリケーションの設定値の変更に関する検証・性能評価
  • シークレット変更の検証

ここで述べたシチュエーション以外、例えばアプリケーションコードの変更の場合は通常のリリースを行ったり、あるいは監視時間を非常に短くしたカナリアリリースを行ったりする予定です。

運用手順

実際のカナリアリリースの運用は、カナリア監視アプリケーションとcanary版のDeploymentをいったん削除する手法のおかげで、かなりシンプルな手順となっています。

  1. ジョブを起動する。
  2. しばらく待つ。
  3. 終了時
    1. 異常時:原因を特定して修正し、再度カナリアリリースを実施する。
    2. 正常時:カナリアをstableに変更して通常のリリース手順を実施する。

最後の正常時の手順では、canaryの内容をstableとしてmanifestを変更します。

今後の課題

運用前で、かつ、PoC的に実施されたプロジェクトですが、以下のような課題が見つかりました。

canaryのメトリクスが異常な値を出すと、必要ないPrometheusのアラートが鳴ってしまう課題です。canaryで異常が起きた場合は、カナリア監視アプリケーションがロールバックさせるため、アラート発報する必要がありません。しかし、作成したカナリアリリースのシステム・ロジック自体がまだ荒削りです。そのため、カナリアリリースからの異常を検知した時はアラートを無くすことはできていません。今後カナリアリリースの仕組みの品質を向上させつつ、稼働実績を積む必要があります。

インターンとして取り組んでみて

ここまではいちエンジニアとして記事を書いてきましたが、ここからはいちインターン生として、インターンシップとして取り組んで得た感想を書いていきます。

まずはじめに、あらためてYahoo! JAPANの基盤の大きさ、強さを実感しました。多様なサービスを多くのユーザーの皆様にお届けしているため、当然それらを支えるバックエンドサービスやインフラも強靭なものだと想像はしていました。しかしその想像のはるか上をいく量と質が確保されており、かつ、それらをインターン生でも簡単に使えるようなルールも整備されており、環境の充実度を実感しました。

次に、ドキュメンテーションの充実さを感じました。多くのYahoo! JAPANの社員の方がいろいろなところで言及されていますが、私も実際に体感しました。社内のマネージドKubernetesであるZCPのドキュメントや、CI/CDツールScrewdriverのドキュメントなど、内製システムの社内ユーザーへ向けたドキュメントはもちろん、チームのオンボーディング資料や各マイクロサービスの仕様や運用方法、開発手法に至るまで、ありとあらゆるものが丁寧にドキュメント化されていました。暗黙知が排除されているといってもよい環境だと思います。社内ドキュメントの検索ツールで検索して、知りたいものは全て知ることができたので、スムーズに仕事することができました。

また、全体の規模とは反対に、チーム・プロダクトが小さく保たれているところが印象的でした。組織は10人未満のチームが複数に分かれ、各チームがマイクロサービス化されたプロダクトを開発・運用して国内有数の基盤・サービスを形作っていました。インターン前は漠然と「組織規模が大きいからチームも大きいだろう」と想像していただけに、最初は非常に驚きました。

最後に、チームの方々には、インターンしやすい環境を整えていただきました。何よりも、業務に関わるあらゆることを任せていただき、私を主体として取り組ませていただいたことに感謝しています。いただいたタスクについて疑問があれば相談して改善したり、次に取り組むタスクを選んだりさせていただきました。認識 → 判断 → 行動の流れをすべて任せていただき、必要な箇所では都度丁寧に相談に乗ってくださったりアドバイスしてくださったりしたおかげで、実際にエンジニアとしてはたらく上で必要となる基礎体力を養うことができたなと強く感じています。

メンターからの一言

本インターンにて杉浦さんのメンターをしていた木内です。今回のインターンではKubernetes環境においての運用改善ということで、カナリアテスト戦略の導入に挑戦していただきました。長期インターンということで少し難しめの課題として考えていたのですが、杉浦さんの開発力がとても素晴らしく、想定していたゴールよりも先の開発まで挑戦していただけてとても良かったです。

杉浦さんにも、普段の学生生活や研究ではなかなか考えることがないDevOps手法や、ヤフーの大規模プラットフォームを触っていただき、どのようにヤフーがプラットフォームを開発・運用しているかを知っていただけたかなと思います。

今後もより良いプラットフォーム運用ができるように取り組んでいきます。


Kubernetes Logoは、Creative Commons Attribution 4.0 Internationalライセンス条件の下で、Linux Foundationにより、ライセンスされています。

こちらの記事のご感想を聞かせください。

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました


杉浦 航
バックエンドエンジニア
名古屋大学大学院修士1年。物理学と計算機で、タンパク質の性質について研究しています。趣味で自作キーボードを組んだり設計したりしています。

このページの先頭へ