Spinnakerを使ってマイクロサービスを安全にデプロイするためのTips

山下慶将氏(以下、山下):こんにちは。お集りいただきありがとうございます。今日は「Spinnakerで実践するマイクロサービスの安全なリリースフローとベストプラクティス」というタイトルで発表いたします。よろしくお願いします。

簡単に自己紹介いたします。自分は山下慶将と申します。メルペイのSREで、今はマイクロサービスプラットフォームのCI/CDチームに所属しています。クラウドに関するプロダクトに興味があって、いろんなサービスを使うのが趣味です。

今日の資料はSpeakerDeckに上げて、今回のカンファレンスのハッシュ先でTwitterに投稿するので、動画を最後まで見られない方も、あとでご覧ください。

今日は主に、Spinnakerを使って、マイクロサービスを安全にデプロイするためのTipsを解説いたします。

アジェンダはこのようになっています。最初にメルカリの継続的デリバリーを紹介します。次に、継続的デリバリーで使っているSpinnakerというプロダクトについて説明いたします。そして、それを使ってどのように安全なリリースフローを目指しているのか、Spinnakerの機能を中心に紹介します。さらに、それを使うにあたってのベストプラクティスを紹介したいと思っています。

最後にまとめをして、発表が終わる予定です。短い間ですが、よろしくお願いします。

メルカリにおける継続的デリバリー

まずは、メルカリにおける継続的デリバリーを紹介したいと思います。そもそも継続的デリバリーに馴染みがない方もいらっしゃると思うので、先に「継続的デリバリーが何であるか」を紹介したいと思います。継続的デリバリー(CD)は、継続的インテグレーションによってできた成果物を高速に、継続的にデプロイするための仕組みのことです。

デリバリーの頻度を多くして1日に何回もデプロイすることによって、自分たちが作った価値をお客さまへ早くデプロイできる仕組みのことです。この仕組みの中では、簡単に、もしくは自動的にデプロイできて、ミスが少ない信頼性の高いシステムが要求されます。

少し余談になってしまうのですが、先ほど継続的インテグレーションという言葉が出たので補足しておきたいと思います。継続的インテグレーション(CI)は、デベロッパーがコード変更を行うと、そのコードに自動的にユニットテスト、結合テストが行われて成果物が作成されたり、その結果が高速的にデベロッパーにフィードバックされたりする仕組みのことです。

この仕組みがあることによって、デベロッパーがコードの変更を行うと、そのコードの解析やテストが行われて、フィードバックをデベロッパーに送ることができます。先ほどの継続的デリバリー(CD)と合わせて、CI/CDと呼ばれることが多いです。ここまでで「継続的デリバリーが何であるか」を少し説明できたと思うので、実際にメルカリの継続的デリバリーを紹介したいと思います。

メルカリのマイクロサービス

まずクラウドの形態ですが、メルカリのマイクロサービスはGoogle Cloud Platform、GCPの1つのサービスであるGoogle Kubernetes Engineの中でホスティングされています。いわゆるマルチテナンシーの形態をとっていて、クラスタが1つしかないので、全体像は非常にシンプルな構成になっていると思っています。

そのGKEクラスタの中で、それぞれのマイクロサービスは、KubernetesのNamespaceと言われるものをもっています。そして、その中に各アプリケーションをホスティングしています。それぞれのマイクロサービスは、アプリケーションだけに限らず、そのマイクロサービスで必要なさまざまなリソース、Kubernetesだとジョブなどをデプロイして使っています。

どのようにデプロイしているかという話ですが、「Spinnaker」という継続的デリバリーを行うためのツールを使っています。これはあとで説明します。このようにしてマイクロサービスは、それぞれデプロイしています。

各マイクロサービスが自律的に自分たちのマイクロサービスをデプロイしています。それぞれのチームが意思決定をして解決策を回しているので、非常にコンパクトに、高速に開発速度を上げられる開発体制になっています。メルカリのマイクロサービスの継続的デリバリーはこのように行われています。

オープンソースの継続的デリバリープラットフォーム「Spinnaker」

次に、継続的デリバリーを支えているSpinnakerを紹介します。先ほども説明したとおり、各マイクロサービスがSpinnakerを使って、それぞれのアプリケーションの継続的デリバリーをしています。Spinnakerは、NetflixやGoogleが中心的に開発している、オープンソースの継続的デリバリープラットフォームです。

先ほど紹介したCI/CDの中で、CDの部分を担っているプロダクトになります。アプリケーションを自動でデプロイするために、必要な機能が実装されています。あとで紹介しますが、いろんなデプロイ手法を行えるので、さまざまなユースケースでマッチしているプロダクトだと思っています。

また、マルチクラウドに対応しているので、特定のクラウドプロバイダ、GCPだったりAWSだったりに制約されることなく、さまざまな環境で採用できるのも、1つの特徴です。

Spinnakerの機能を少し紹介したいと思うんですが、まず大事なところとしては、Kubernetesをサポートしていることが挙げられます。メルカリではGKEを使っていると先ほど紹介しましたが、SpinnakerがKubernetesをサポートすることによって、簡単にSpinnakerを使って、マイクロサービスをGKEクラスタにデプロイできるようになっています。

正確には、このような図のかたちになっています。GCPの中のGoogle Container Registryという、Dockerイメージが置いてあるDockerイメージをSpinnakerが取ってきて、そして、ここにはKubernetes Resource Repositoryと書いていますが、Kubernetesのマニフェストが定義されているリポジトリからYAMLを参照して、実際のGKEクラスタにデプロイしています。

2つ目の機能としては、GUIでパイプラインが作成できる点です。パイプラインは、一般的なデプロイメントパイプラインや、ワークフローと言われるものを定義したものです。この図のように、パイプラインを定義できます。

このパイプラインは、最初に設定ステージがあります。そしてManual Judgmentと書かれている承認ステップがあって、そのあとデプロイしています。非常に直感的に、GUIで簡単に操作できるのがSpinnakerの特徴です。

またSpinnaker自身はプラガブルなCDプラットフォームになっているのが特徴で、いろいろなプロダクトに対応しています。例えば、先ほども言いましたマルチクラウドに対応していて、AWS、GCPに限らずOracleやAzure。コンテナオーケストレーションであるKubernetesに限らず、GCEやGAEなどにもデプロイできるのも強みの1つです。

また、マニフェストの定義も、ただの普通のYAMLだけではなく、Helmやkustomizeと呼ばれるようなツールもサポートしていて、Spinnakerのパイプラインの中でその成果物を作成できます。

通知も、SlackやSNS、メールなどもサポートしていますし、Spinnakerの情報を格納できるストレージもRedisや、MySQLのクラウドマネジメント版だとCloud SQLも使えて、非常に広範囲に汎用的に使えるプロダクトになっているのも特徴です。

また、さまざまなデプロイ手法に対応しています。これは後に紹介しますが、それぞれのユースケースに応じたデプロイ手法を選定できるのではないかなと思っています。

Red/Blackデプロイ

ここまでで、Spinnakerの主要な機能は紹介できたので、よりもっと詳しく紹介していきたいなと思っています。

これからもっと実践的な話をしていきたいと思います。Spinnakerのいろんな機能を見つつ、どのようにしてプラクティカルに機能をうまく使っていけばいいのかの観点を紹介したいと思います。

まず、リリースフローについてです。アプリケーションのリリースは、もっともインシデントが起きやすいです。そのため、デプロイに関しては非常に注意する必要があります。例えば、そもそもデプロイが失敗してしまって、アプリケーションがダウンしてしまうなど。またはデプロイが勝手にされてしまうケースです。

もちろんいろいろなリスクがまだまだあると思うんですが、今回はこれらの問題について取り上げたいと思います。

最初に、デプロイ自体の問題についてです。デプロイ自体が失敗してしまうなら、改めてデプロイ手法について考える必要があると思います。先ほども紹介しましたが、Spinnakerでは、以下のデプロイ手法をサポートしています。「Red/Blackデプロイ」「ローリングアップデート」「カナリアデプロイ」の3種類です。それぞれの手法について簡単に説明します。

最初に、Red/Blackデプロイです。一般的にはBule/Greenデプロイと呼ばれるデプロイ手法ではあるんですが、Spinnakerの用語ではRed/Blackデプロイと呼ばれています。ユースケースとしては、新しいリリースへトラフィックの切り替えを一瞬でしたいときに使用します。

実際に図を用いて説明します。ロードバランサを中心に、V1でトラフィックを受けていると仮定します。まず、ベースラインと呼ばれている、V1の既存バージョンのアプリケーションがデプロイされているとします。Kubernetesだとpod、Kubernetesを使っていない場合だと、サーバーのクラスタなどを想定するとイメージがしやすいと思います。

次に、新しいバージョンのV2をデプロイしたとします。まだこちらにはトラフィックを流さず、新しいバージョンが正常に起動できるかをチェックします。もし正常に起動できると、実際にトラフィックを新しいバージョンへ瞬時に切り替えて、そのあと古いバージョンV1のリリースを削除します。

この一連のデプロイをRed/Blackデプロイ、またはBule/Greenデプロイと呼びます。瞬時にアプリケーションを新しいバージョンにトラフィックを切り替えリリースできるため、デプロイ自体が非常に高速に終わることが、この手法の特徴です。

ローリングアップデート

次にローリングアップデートについて説明します。ユースケースとしては、段階的に新しいバージョンへ移行していきたいときに使用されることが多いです。先ほど紹介したRed/Blackデプロイでは、一瞬で新しいバージョンに切り替えるために、失敗したときのリスク、V2が動作しなかったときのリスクが非常に大きいのが懸念点です。

先ほどのものはV2をデプロイして正常に動作していますが、いざロードバランサでトラフィックを受けてみると、意図しない挙動になることもあります。

アプリケーションの使い方によっては、メモリリークの発生によりメモリを大量消費してしまい、OOM Killerにアプリケーションのプロセスが殺されてしまったり、CPUを過剰に使い過ぎてしまい、結果的に動作しなくなってしまったり、レスポンスを返せなくなったり、いろんなケースが想定されると思います。

このようなときに瞬時に全トラフィックを切り替えるRed/Blackデプロイだと、そのV2がダメになってしまうと、お客さまへレスポンスが返せないなど、非常に大きなインシデントになってしまいます。お客さま体験が悪くなり、結果的にビジネスインパクトが発生して、非常に大きな問題になってしまいます。そんなことがないためにも、ローリングアップデートという手法があります。

ローリングアップデートは、段階的に新しいバージョンへトラフィックを移していくリリースフローです。まずV2を一部デプロイして、部分的にトラフィックを流します。その部分的なデプロイがうまくいくと、徐々に新しいバージョンのリリース、デプロイを増やしていくかたちになります。

このような流れでV2をもう1個、V2をもう1個というように、最終的にはすべてのバージョンを新しいバージョンへ移行できるようになります。もし仮に、途中で新しいバージョンのデプロイで問題を抱えていたとしても、古いバージョンへ完全にロールバックできます。

このようにして、ダウンタイムなく古いバージョンに逃げる感じで、お客さまに影響を発生することなく、安全にデプロイできます。非常にリスクの少ないデプロイ手法になっていると思います。

カナリアデプロイ

最後に、カナリアデプロイを紹介いたします。ユースケースとしてはローリングアップデートにすごく似ていますが、より詳細に検証してから、段階的に移行していきたいときに使うデプロイ手法です。

図で説明いたします。ローリングアップデートと同様、一部新しいバージョンをデプロイします。この時、一部のトラフィックを流すことによって、新しいV2が正常に動作するかなどを検証します。この検証項目は組織によりますが、一般的には正常系と、V1と同じような挙動になるか、メモリは使い過ぎていないかなどを検証します。多くの場合は、人が介入して判断することになります。

その検証がうまくいった、異常がないと判定されると、実際にV2に段階的に移行することになります。このように入念に新しいバージョンを検証することによって、より確かに新しいバージョンに移行できます。

ユースケースに応じてデプロイ手法を選択

「Red/Blackデプロイ」「ローリングアップデート」「カナリアデプロイ」の3つの手法を説明しました。しかし、どれもメリット、デメリットがあってどれを採用するべきかを言うのは難しいです。デプロイの速度と安全性は、トレードオフになっています。CI/CDの観点だと、より速くデプロイしたいものの、結果的に安全性を損なってしまうと、本末転倒です。

Red/Blackデプロイでは、サーバーを起動するまでの時間が一番大きくかかるので、その後は安全に、高速にデプロイできるようになります。しかし、一発でトラフィックを移行するリスクが潜在的に存在することを紹介しました。一方で、カナリアデプロイは人間が介する必要があり、検証項目もあって、安全ではあるものの時間がかかってしまいます。

どちらかがいいというわけではなくて、ユースケースに応じてデプロイ手法を選択する必要があります。これらのトレードオフを意識しながら、デプロイ手法を選ぶことが重要なのではないかなと思っています。

(次回につづく)