Kubernetesで実現したYahoo! JAPANの次世代開発環境

稲津和磨氏(以下、稲津):こんにちは。「Kubernetesで実現したYahoo! JAPANの次世代開発環境」と題しまして、稲津と勝田が発表させていただきます。

まずは自己紹介をさせてください。私は稲津和磨といいます。

2010年にヤフー株式会社に入社して、フロントエンドからバックエンドまでさまざまな開発に携わってきました。2015年、ゼットラボ株式会社というところに出向して今に至ります。ゼットラボについてはこの次のスライドで説明いたします。

少し個人的な話をしますと、私の趣味は電子工作で、いろんなガジェットを作ったりしています。また、Yahoo! JAPANが主催しているハッカソンイベントやHack Dayなどにもメンバーに参加したりしています。よろしくお願いします。

ゼットラボですが、2015年に設立されたYahoo! JAPANの100パーセント子会社です。

主な業務内容はインフラ基盤の開発及び技術研究になっています。このあと説明しますが、Yahoo! JAPAN向けのKubernetes-as-a-Serviceの開発が、現在行っている主な業務です。

そういえば会場のみなさんにお聞きしたいのですが、この発表のタイトルにある「Kubernetes」というものについて、聞いたことがある人は手を挙げてもらっていいですか?

(会場挙手)

すごい。けっこう人気ですね。では次に、エンジニアの方に聞きたいんですけど、「Kubernetesを触った」くらいの人はどのくらいいますか?

(会場挙手)

あぁ。聞いただけの人はさっきくらいで、触った人はこのくらいですね。もしエンジニアの方でサービスを提供されているんだったら、本番環境で使っている方。バリバリKubernetesわかりますよ、みたいな方はどのくらいいますか?

(会場挙手)

あ、1人……くらいじゃないですか。

(会場笑)

わかりました。今回、「Kubernetesって聞いたことあるよ」みたいな方に向けて、Yahoo! JAPANでどのような使い方をしているかをメインに話していこうと思っています。よろしくお願いします。

勝田広樹氏(以下、勝田):もう1人、私勝田広樹が発表させていただきます。

自分は2013年にヤフー株式会社に入社して、インフラ側の作業に携わっていました。一応フロントエンドも触っていますが、今はインフラ側で2017年の10月ごろからKubernetesチームに入って、インフラエンジニアとして活動しています。

CKA(Certified Kubernetes Administrator)というKubernetesの管理者資格みたいなものを取ったので、今回の発表させていただくことになりました。

なぜ2人で発表するかというと、先ほども言いましたが稲津がゼットラボ側でのKaaS、Kubernetes-as-a-Serviceの開発を担当している視点で発表して、自分はYahoo! JAPANの担当として、このKubernetes-as-a-Serviceの運用をして、サービスを利用者に提供している立場になります。そのため、主に開発の視点と運用の視点として、2人で発表させていただきます。

繰り返しになりますが、この図にありますようなYahoo! JAPANのいろんなサービスで、Kubernetesを使った新しい開発環境を使って運営をしています。そのKubernetesを使った開発環境がどういうものなのかというのを紹介させていただきます。

アジェンダです。

最初にYahoo! JAPANが、どんな次世代開発環境を目指したのかというのを話します。この「次世代開発環境」がどんな定義なのかということを話して、それによってできあがったKubernetes-as-a-Serviceについて解説します。

最初に、Yahoo! JAPAN視点の次世代開発環境のお話をしてから、ゼットラボ側でどう考えて何を作ろうとしたのか。そこから生まれてきたKubernetes-as-a-Serviceがどんなものなのかというのを紹介します。それをYahoo! JAPAN側に持ってきて、実際に利用しているサービス側の視点のお話をして、最後にまとめます。

早く、安定したServiceを届けるために

では「Yahoo! JAPANが目指した次世代開発環境」として始めます。やりたいこととしては、より早く・より安定したサービスを届けるということです。

Yahoo! JAPANはいろいろなサービスをみなさんに提供させていただいていて、安定して事故等の起きない、ちゃんとしたものとして提供できるものを出したい、ということがサービスを提供する側としてやりたいことです。

では、それでいっぱい出したいという時に、開発だけやってればいいかというとそうではありません。そこで注力したいことは、一番は開発生産性の向上です。また、「安定したサービスを届ける」という意味で、持続力もちゃんと維持しようという点。

あとYahoo! JAPANは昔から独自の技術を中で使っていたりして、新しいものがなかなか受け入れられなかったり。あとは持って来たとしても自分たちの技術とマッチするのかという問題もあり、なかなか親和性が取れませんでした。

やはり世の中の開発の進化に合わせて、相対的に変わらなければいけません。Yahoo! JAPAN自体も世間においていかれつつあったので、流行を取り入れつつ、世に提供していくという方向を目指して進めることになりました。

そんな開発等に注力する時に、ほかにどんな作業をしてるのかというのを見直してみると、こういった4つの作業がありました。

最初にリリース・脆弱性対応ということで、システム自体のアップデート作業です。ただ、何か作って出せばいいかというとそうではなくて、安定して提供するために、システムが落ちないように冗長化させて、1個1個適用していって再起動して、という作業をやります。

あとはスケール作業。サービスがたくさん使われてきたら、そのユーザー100人までしか耐えられないというサービスでは価値が提供できません。したがって、ユーザー数に対して適切なシステムの大きさにする作業が必要になります。

それと、どうしても起きてしまうのが障害ですね。ハードウェアというものは、やはり障害が発生してしまうので、それによるサービスのダウンをいかに避けるか。できるだけ避けるようにするのですが、そういった障害が発生してしまったときには、エンジニアがそれを復旧させるという障害対応の作業があります。

これをサービスごとにやっていたのですが、それらを統一化できると考えました。やっていることは甚だ近いものですし、リリースすると言っても先ほど言ったように落ちない状態で冗長化して適用化して、最適化して。スケールも、ユーザーのアクセスに対して適切な状態に持っていく。

可視化に関しても、サービスごとに固有のものを見たいというのはもちろんあると思いますが、基礎的な共通部分に関しては統一化。障害対応ですと、サーバーだったら用意したものを個別に立ち上げて、サービスのアプライをして、ダウンしてしまったものは除外すると。そういった共通化できる部分では最初から提供していきたい、そして開発者は開発業務に集中したい、という課題の解決をゼットラボ側に検討してもらいました。

ということで、ここからはゼットラボ側の稲津がお話しします。

12Factor App

稲津:ゼットラボは先ほども説明しましたように、インフラ基盤の開発・技術研究を行っているヤフー株式会社の子会社になります。先ほどのようなYahoo! JAPANの課題を受けて、ゼットラボで開発に集中できるプラットフォームとは何かというところを考え始めました。最新の技術などを調査して、その内容を整理して検証するところから作業を始めました。

まず調査したのは、最新のアプリケーション設計手法です。

ここに示したのは、12Factor Appというアプリケーション開発の方法論です。WebアプリケーションをSoftware-as-a-serviceとして提供できるようにするための方法論なのですが、「12」とあるとおり、12個の原則から成り立っています。

ここですべてを紹介することはしませんが、例えば「依存関係は明示的に宣言する」であったり、「設定を環境変数に分離する」であったり、「アプリケーション自身はデータを持たない」など、これらの法則を守ってアプリケーションを作りましょうね、という方法論になっています。

これを守ってアプリケーションを作ることで、スケール可能なアプリケーションを実現したり、システムとアプリケーションを分離して、それぞれを独立してアップデートできたり。このような特徴を持ったアプリケーションは継続なデプロイを簡単に進めることができて、頻繁なリリースが実現できるようになるといった特徴があります。

そして、このようなアプリケーションはprivate/publicクラウドやそれらを複合して利用するhybridクラウドにおいても同じようにデプロイする事が可能です。このような特徴を持ったアプリケーションのことを、クラウドネイティブなアプリケーションと呼んだりします。

マイクロサービスの導入

さらにもう1つ注目したのが、マイクロサービスという手法です。

1つのアプリケーションを小さなサービス群の組み合わせとして構築します。ここの図に示した右の例が、マイクロサービスの例になります。アプリケーションごとに機能A・B・Cがそれぞれ実装されると想像してもらえればと思います。

対して図の左側にあるような、1つのアプリケーションの中に3つの機能がいっぺんに実装されているようなもの、これをモノリシックなアプリケーションと呼んだりします。従来のアプリケーションは、大半はこのモノリシックな構成をとっていると思います。

しかしこのマイクロサービスのような構成をとることで、それぞれのアプリケーションを小さく保つことができます。小さなアプリケーションというのは当然、少数のメンバーのチームで面倒を見ることができるので、効率的に開発できることが期待されます。

また各アプリケーションの責任範囲が明確になるため、変更した際の影響範囲を最小化することができます。エンジニアの方であれば心当たりあるかもしれませんが、左のようなすべての機能が1つのアプリケーションの中に入っている場合、うまく設計しないとAの機能を直したらBの機能、Cの機能にも影響が出てしまう、みたいなことが予想されて、改修する時の影響範囲の調査がすごく大変になるということがあると思います。

ですが、うまく機能を分離してマイクロサービスのかたちにすることで、責任範囲が明確になって頻繫に変更ができるようなアプリケーションを構築できます。それによって高速なリリースサイクルが実現できると考えました。我々はこれらの手法で、アプリケーションを開発するのに適した次世代開発環境を作ることにしました。

クラウドネイティブなマイクロサービスを実現するために注目したのは、コンテナの技術です。

従来アプリケーションは、仮想マシン上で実行されることが一般的でした。この構成はマイクロサービスの場合、あまり適切ではありません。アプリケーション自体が小さくなってしまうので、それらを1個ずつVMの中に入れて実行するとなると、VMのオーバーヘッドが大きくなってしまって無駄が多くなると考えました。

コンテナは仮想マシンよりも軽量なアプリケーションの実行環境です。この図に示したように1台のマシンの中でいくつものコンテナを実行でき、それらは一般的なプロセスとは違って、互いに依存関係を持ちません。一般的なプロセスの場合は、ホストのファイルシステムやネットワークインターフェースを共有するのですが、コンテナではそれらは独立しているので、開発する上ではVMと同じように独立した環境にあるという特徴があります。

コンテナオーケストレーションの必要性

コンテナ技術を使えばうまくすべて解決できるのかというと、それだけではありません。本番環境というのは、マシンが複数台あります。

Yahoo! JAPANは3台ということはないんですけども(笑)、ここでは簡単なように3つマシンがあって、そこでコンテナが動いている図を示しています。

コンテナ技術は、ある1台のマシンでどのコンテナを動作させるかは管理するのですけども、複数のマシンの場合は、それだけではうまくいきません。

例えばこのように、マシンは突然壊れます。壊れないマシンを作るのは非常に難しいし、いつかは壊れるのでしょうがないことなんですけど(笑)。

このような障害が起きてしまった時に、このバツの付いたマシンの上で動いているコンテナが動かなくなります。例えばYahoo! JAPANのアプリで提供している場合は、Yahoo! JAPANのアプリの一部の機能が動かない、みたいなことになってしまいます。

そのため、このような状態になったら速やかに、ここで動いていたコンテナを別のちゃんと動いてるマシンに移動させる処理をする必要がありますが、先ほど話したコンテナ技術だけではそれを実現することはできません。

また、Yahoo!ショッピングなどでキャンペーンを実施した際など、通常より多くの負荷がかかることがあります。

そのような時には、動作しているコンテナの数を増やして負荷を分散させるということをよく行いますが、そのような際にコンテナを動かすためのマシンが足りなくなることがあります。この図のように新しくマシンを追加して、そこで新しくコンテナを動かそうかな、みたいなことを考えたりもしますが、こういったことももちろんコンテナ技術だけでは実現できません。

こうしたコンテナを動かす複数のマシンを管理する仕組みとして、コンテナオーケストレーションツールというものが存在します。

そこで我々が注目したのは、Kubernetesです。

Kubernetesはオープンソースのコンテナオーケストレーションツールです。我々はこのKubernetesを次世代開発環境で利用しています。Kubernetesはまさに先ほど説明したことを実現するツールで、近年多くのサービス・会社で利用されていて、事実上の業界標準のツールと思います。

Kubernetesが実現すること

ではKubernetesを用いた例を説明していきます。Kubernetesを用いることで、複数のマシンをクラスタとして扱い、そのクラスタを構成するマシンのどこかで指定した数のコンテナを実行することができます。

図の上に示した開発者がKubernetesに「このコンテナを9個動かしてくれ」と伝えると、Kubernetesはクラスタを構成するマシンの中で、指定したコンテナを9個動かしてくれるという仕組みですね。これがKubernetesです。

先ほど説明したように、クラスタを構成するマシンが不調になった場合を例に説明します。

このようにバツの付いたマシンの調子が悪いとなったら、Kubernetesはこのマシンをクラスタから切り離して、ほかの正常に動いているマシンでコンテナの再実行を自動的に行います。例えばこの場合は、このまずいマシンのコンテナは動いていないので、新しく正常に動いているマシンにコンテナを移動させ、実行してくれます。

この際、スライドの上の方に示している運用者はとくになにも行っていないことに注目してください。夜中にマシンに障害が起きてコンテナが動かなくなっても、このように自動的に復旧が行われるため、エンジニアはゆっくり寝ていられます。

また、コンテナの個数を負荷に合わせて増減することもできます。

例えばこのような状態で、負荷が上がってきたぞというのを認識すると、コンテナの数を増やしてくれます。

というように、人間はコンテナの作成と、そのコンテナを何個利用したいかを伝えることで、Kubernetesがコンテナのデプロイとその維持を行ってくれます。これを使うことで、人間がコンテナの運用作業をする必要はなくなります。

Kubernetesクラスタの管理

ここまでの話を整理すると、我々はマイクロサービス化やクラウドネイティブなアプリケーションの設計に注目して、コンテナ技術を利用することにしました。またコンテナ技術を用いてアプリケーションの運用を行うために、コンテナオーケストレーションツールであるKubernetesを利用することにしました。

Kubernetesを利用することで、アプリケーションを運用する大半の作業はKubernetesが自動的に行ってくれるようになりました。しかしこれだけでは足らないと考えました。Kubernetesクラスタ自身の管理をどのようにするかという問題です。

例えば何度も出てきますが、このKubernetesクラスタのマシンが1台壊れた場合、Kubernetesは壊れたマシンをクラスタから切り離してくれるのですが、どこかのタイミングで人がマシンを新たに追加しないと、クラスタを構成しているマシンがどんどん減ってしまうという現象が起きてしまいます。Kubernetesだけではこの部分はサポートできないので、この例のように人がマシンを作ってKubernetesクラスタに1個足す、みたいなことをする必要があります。

また、Kubernetes自体のバージョンアップについても考える必要があります。クラスタ上で動作しているアプリケーションを異常終了させないように、安全にすべてのマシンのコンポーネントをバージョンアップする必要があるのですが、それもKubernetesだけでは実現することができません。

また、このKubernetesクラスタ、この絵では1つしかありませんが、1つとは限りません。とくに弊社は非常に多くのサービスを持っていて、例えばそれぞれのサービスがKubernetesクラスタを1つずつ使うとしても、相当な数のKubernetesクラスタを管理する必要があります。

また、サービスによっては、「開発環境と本番環境で違うクラスタを使いたい」といった要求があるかもしれません。そうなると本当にたくさんの数のクラスタを管理する必要ができて、人の手で管理するのは非常に難しくなってきます。

また先ほど示した、クラスタにマシンを追加したりとか、バージョンアップをするといった作業は、一定のルールに従った作業です。そのため、人間が実施するのはナンセンスで、人間はもっと創造的な仕事に注力したいということで、何か解決策はないかと考えました。

Kubernetes-as-a-Serviceとは何か?

そこで出てくるのが、最初に少しお話したKubernetes-as-a-Serviceです。省略してKaaSと呼んだりもしています。

これは先ほど説明した、Kubernetesを管理するためのサービスです。簡単なクラスタの作成に始まり、クラスタを構成するマシンの自動回復や、無停止でのクラスタのアップデートをサポートしているものです。これを使うことで、人間はKubernetesを管理する必要もなくなります。

Kubernetes-as-a-Serviceの動作を簡単に説明します。まずクラスタの作成です。

運用者がクラスタを作成したい旨をKaaSに伝えると、KaaSはOpenStackのAPIを呼び出して、クラスタを構成するマシンを作成します。各マシンはKaaSにより適切に設定が行われて、このようにKubernetesクラスタを構築します。

KaaSはKubernetesクラスタを作って終わりではありません。作成したマシンを常に監視していて、異常が起きた際はすぐに対応してくれます。例えば、マシンが壊れた場合ですね。KaaSはこの障害を検知します。そしてOpenStackのAPIを呼び出して、もう1つのマシンを作成してくれます。

そして新しく作ったマシンをKubernetesのクラスタに参加させます。クラスタはKubernetesで管理されているので、動かなくなったマシンで動いていたコンテナは、例えば新しく作ったマシンに再配置されます。障害が起きていたマシンは自動的に削除されます。この際、運用者は本当になにもしていません。

そのため、マシンの障害が起きた時には、運用者はなにもする必要ありませんし、なにか起きた時に翌営業日にマシンを追加するという対応さえする必要がなくなりました。

無停止でクラスタをバージョンアップ

さらに、KaaSで実現している無停止でのクラスタバージョンアップについて説明します。まず「バージョンアップしたい」ということを、運用者がKaaSに伝えます。

そうするとまずはOpenStackのAPIを呼び出し、新しいバージョンのマシンを作成します。新しいバージョンのマシンはよくわかるように紫色で示しています。

新しいバージョンのマシンが正常に作成できたら、古いマシンに対してコンテナの退避命令を実行します。

この命令によりKubernetesは、コンテナを安全に終了させます。安全に終了させるとコンテナの数が足りなくなるので、Kubernetesは自身の自動回復機能によってコンテナを別のマシンに動かそうとします。この例では新しく作ったマシンで動作しています。そしてもうどのコンテナも動いていない古いバージョンのマシンは、OpenStackにより削除されます。

この一連の動作をすべてのマシンのバージョンが上がるまで繰り返すことで、無停止でのクラスタのアップグレードを実現しています。新しいマシンを1つ作って、古いバージョンのマシンに退避命令を出して、コンテナを安全に終了させつつ、新しいマシンでコンテナを実行させ、そして空っぽになった古いマシンを削除します。

もう1回、もう1回、もう1回……ここまでやると、すべてのマシンが紫色になって、新しいバージョンに上がっています。また、コンテナも安全に終了され、Kubernetesによりコンテナの個数が維持されているため、アプリケーションはすべて異常終了することなくバージョンアップが完了していることがわかります。このようにしてKaaSはKubernetesクラスタを管理しています。

プリインストールされているアドオン

ここまではどうやってKubernetesクラスタを管理するかという話をしました。実際にYahoo! JAPANのエンジニアはこうやって作られているKubernetesの上でアプリケーションを作っていくことになります。

そのため、アプリケーションの開発環境についてはこれでいいんですけども、アプリケーションを作っていく中で、サービスの内容とは関係なくどのサービスにも共通して必要になるアプリケーションが存在することに気づきました。

例えば各マシンのメモリやCPUを監視するようなアプリケーションです。

この例では、各マシンのメモリ・CPUを監視するようなコンテナと、それらの情報を集めるようなコンテナを紹介します。メモリ使用率がやばくなったり、CPU使用率がやばくなったりした場合はアラートを上げたりすると。こういうアプリケーションは、おそらくどのサービスでも使いたいでしょう。

また、コンテナのログを収集して社内のログ管理システムに転送するアプリケーションなども、どのサービスでも使いたくなるアプリケーションの1つです。

このようなアプリケーションを各チームでメンテナンスすると、余計なコストがかかります。そしてノウハウが分散してしまい、あちこちで同じ問題に遭遇してしまうという無駄が発生すると考えました。

そこで、このようなどのサービスでも必要となるようなアプリケーションをアドオンと呼んで用意して、専門のチームが常に最新の状態にメンテナンスすることで業務の効率化を図っています。

このように、ゼットラボではKaaS及びアドオンを開発しました。Yahoo! JAPANではこれらを利用して、実際に開発環境を構築しています。

ということで、実際にYahoo! JAPANでどのように利用されているかということを説明してもらうため、Yahoo! JAPANの勝田にバトンタッチします。