自己紹介とアジェンダ

石田尭大氏(以下、石田):「VMとAWS ECSがメインのインフラにKubernetesを導入した効能」という内容で発表いたします。

初めまして、石田と申します。サイバーエージェントに2016年新卒入社して、子会社であるCyberZにバックエンドエンジニアとして出向しました。メッセージアプリのAPI開発、トーナメントサイトの開発に携わった後にインフラに転向し、OPENREC.tvのインフラおよびその他CyberZ関連子会社で、開発運用しているサービスのインフラを見ています。昨年(2019年)あたりからSREとして働いており、サービスの信頼担保にあたっての改善をメインミッションとしています。

今回は、次の項目についてお話できればと考えています。OPENREC.tvの紹介、コンテナおよびサーバーレス技術の導入状況、Kubernetesを導入するモチベーション、既存との並行運用方法について、稼働しているサービスの紹介、直面した問題、今後の展望、まとめとなります。

また、次の方々へ向けてお話したいと思います。Kubernetesを業務で導入してみたいが踏ん切りがつかない方。Kubernetesを使うからには何か特別なことをしないといけないと思っている方。Kubernetesを導入したいがどこからどこまで手をつければいいかわからない方。古いインフラの移行先にKubernetesを選択したいと思っている方になります。

OPENREC.tvにおけるコンテナとサーバーレス技術の導入状況

まず始めに、今回Kubernetes導入を実施したOPENREC.tvについて少しお話しします。OPENREC.tvは、2015年にスマホアプリのゲーム動画配信SDKと投稿動画管理ツールとして誕生し、2016年にゲーム動画配信プラットフォームとしてリニューアルして、今日に至ります。対応クライアントはWebやiOS、Androidから始まり、Amazon Fire TVなどでも視聴可能です。

配信者スタンプやアンケート機能のOPENRECキットと呼ばれる配信補助ツールなど、配信を盛り上げる機能が充実しており、最近ではアーティストによる配信やサブスク、限定配信などゲーム以外のコンテンツも増加していて、さまざまな動画コンテンツのプラットフォームになっています。

OPENREC.tvにおけるコンテナとサーバーレス技術の導入状況についてお話しします。OPENREC.tvでは、LambdaやDockerの導入に対しては積極的で、現在Lambdaは100以上の関数が稼働しています。主要言語はJava、Python、Node、Goなどです。使用言語の選定は、チームやアプリケーションで統一することはありますが、基本的に当該企業の開発者の裁量に任せられており、比較的自由に言語選定をしています。

ライブ配信のサムネイル画像生成やユーザーごとのタイムライン生成、ECS、イベントレシーバーなどでは主にLambdaを使用し、Dockerに関してはプロトサーバーサイドレンダーを提供するNode.jsサーバーやAWS Batchを用いたバッチ処理から始まり、配信基盤でもECSを全面的に使用したメインのインフラ層として本番稼働しています。

配信状況によりますが、ピーク帯では1クラスタで1,000コンテナ以上が稼働しています。

サーバーレスやコンテナ化したアプリケーションの開発などの開発経験は、メンバーほとんどが所有しています。このような開発組織背景で、今回のKubernetes導入を進めています。

Kubernetesを導入するモチベーション

5年前にOPENREC.tvが誕生した際の仕様技術を列記します。CentOS 6系にPHP、ApatchやMySQLなど典型的なLAMP構成でした。また当時は固定EC2ホストを手動の手順書でセットアップする必要がありました。ちょうどKubernetesの1.0系がリリースされている最中、コンテナ技術やオートスケールも採用しておらず、よく言えば手堅い、悪く言えば非常に枯れきっている技術選定でした。

次に現在本番稼働中の技術の一部を列記します。AmazonLinuxをメインのサーバーOSとして、Dockerの仮想技術、ECSやKubernetesなどのオーケストレーションツールを採用しています。現在ではTypeScriptをCore APIやWebフロントの言語として選定しており、決済サービスや認証APIなどではJavaを使用するようになりました。

WebサーバーもNginxを利用するようになり、データベースもMySQLからAuroraへ移行し、NoSQLのDynamoDBも使用するようになりました。

サービスの利用者が増えるにつれて性能問題が発生し、歴史的負債の返却かつ新技術の採用を繰り返した結果、利用技術がここまで増加しました。もちろんいいことばかりではなく、これだけコンポーネントが増えたことによるデメリットもあります。

開発組織が抱えている課題を列挙すると、APIやミドルウェア同士の依存関係に伴うリリース作業の煩雑化。想定外の影響範囲のバグおよびデグレーション。新機能リリースに関わる他API・コンポーネント修正による開発スピードの低下。デバッグの煩雑化。技術的挑戦のハードルがどんどん上がっている……などが挙げられます。

昨今の情勢に伴う配信サービスの勃興やeスポーツに対する関心の上昇に伴う市場の激化など、技術的・ビジネス的にも早いリリースサイクルが求められます。先ほどのような開発に対する障壁がある状態では迅速に機能を提供することが難しくなっていました。

このような状況を打開すべく、開発合宿などを実施し話し合う最中で「マイクロサービスアーキテクチャを採用し徐々に移行してはどうか」という話が持ち上がりました。マイクロサービスアーキテクチャに関しては話の本流に逸れるため今回は深く話しませんが、密結合になっている従来のAPIを疎結合にしたい。各々の影響範囲を狭めたい。デバッグが楽になるのではなどの理由から採用してみようと判断しました。

新規技術を導入したいとエンジニア内部での要求もあり、インフラ層としてKubernetesが候補にも上がりました。

リリース機能を絶対にずらせないのなら、技術的挑戦はやめておく

以上のことからKubernetesの導入に適した状況になったと判断し、検証導入のプロジェクトが発足しました。

ただし導入にあたって以下のことを条件としました。手段にこだわりすぎない。サービス開発がメインミッションであり、Kubernetesの導入はサブミッションであることを忘れない。新規開発を停止するような状況には陥らない。新機能のリリースターゲットをずらさないことを最優先とする。

これらを条件とした理由については、弊社開発組織はあくまでもサービス開発がメインミッションであるため、サービス開発のスケジュールに大きな影響を与えてまでKubernetesを導入するのは、適切ではないと判断したからです。

なんとなく聞こえがいいことを言いましたが、実際には他オーケストレーションのピボットを考慮しながら実数が効率的ではないと判断し、途中からKubernetes導入に完全に振り切りました。Kubernetesに限らずですが、リリース機能を絶対にずらせない機能であれば、性能や仕様上で必要でない限り技術的挑戦をするのはやめておくのが吉でしょう。

今回は開発責任者と案件の温度感などを逐次すり合わせていたため、このような判断ができました。当然ではありますが、開発効率を含めて開発組織に大きな影響を与えるので開発責任者とすり合わせておくことが必須です。

既存APIやインフラ層との並行運用

既存APIやインフラ層との並行運用についてお話しします。今回は既存のAPIサーバー群のBFF likeのバックエンドサービスとして新規APIを構築することにしました。

既存APIのバックエンドサービスとして構築した理由としては次に挙げられます。既存APIで独自のBlue/Green構成を取っており、ネットワーク的にもリリースフロー的にも既存APIと同等の階層に並べることが難しかった。既存APIサーバーをBFFのように扱いたかった。バックエンドサービスが死んでいたとしても、なんらかのレスポンスを返せるようにしたかったなどとなります。

当初はKubernetes上のサービスのAPIが、DMZ層に露出している構成を取ろうかと考えていましたが、今までの実装上、既存APIもいくか世代があり世代ごとにAPIのサブドメインを分けている実装になっていました。これ以上クライアントからコールされるAPI用のサブドメインを新に追加したくなかったこと、先ほど述べたようにBlue/Green構成の兼ね合いがあることなどから、図のような構成を取ることを断念しました。

サービスメッシュでカバーできないかも検討しましたが、既存に対しても大きく変更を入れることになりそうで、導入コストが高いと判断し見送りました。

バックエンドサービスとして開発した場合、APIを増設する際には既存APIの回収とバックエンドサービスの開発の両方が必要になりますが、実際には既存APIは、BFFもしくはプロキシのような役割に変化しているので、ほとんど回収コストがかからず、バックエンドサービスの開発に集中できます。

ビジネスロジックもバックエンドサービスに乗っているので、新規機能のAPI開発だったという理由もありますが、既存APIや他APIとの依存を今までに比べて気にすることなく開発できるようになりました。

万が一バックエンドサービスのどれかに障害が発生してレスポンスを返せなくなっていたとしても、BFF層でデフォルトレスポンスを用意しておけば、クライアントに対しては、なんらかの結果を返すことが可能です。

よって最終構成としては図のかたちに落ち着きました。新規バックエンドサービスはVPC内部に閉じており、既存APIサーバー群からコールされます。

APIを実際にリリースするか否かは既存API側で制御できるので、万が一不具合があった場合にもリリーススイッチの働きを利かせることができます。