PSNの開発と運用はグローバルでいくつかの拠点で行われている。
各拠点が機能を区切って担当している。
東京拠点がゲーム機能を担当。
PS4販売昨年で7000万台以上、ゲームプレイ時間は1週間に8億時間以上。
PSNアクティブユーザー8000万人以上、70カ国にサービス提供。
東京拠点でフルスクラッチでコンテナのCI/CDを実現してサービスインした話。
尖った事や斬新な事は行っていない。
東京拠点の規模
100近いサービスがあり、数千のEC2インスタンスが起動している。
Dev,Ops,Secのチーム構成。
上記のチーム構造だとシステムの構造そのものになってしまう -> コンウェイの法則。
サービスデリバーなどを考えると、EC2インスタンスにとっても開発と運用で違う技術を使ってしまったり、開発が作ったものを組織をまたぐために、一度リリースしてデプロイの準備をしてデプロイを行う。このような仕組みは悪くはない、DevとOpsそれぞれの成果でちゃんと機能するが、100近いサービスがある為、デプロイするのに1つのサービスが1日以上かかる場合がある。例えば100個1日1デプロイすると単純計算で100日かかってしまう。しんどい。
ユーザーにより多くの体験を届けたい。1サービスに1デリバーだと、そんなに多くの体験を届ける事が出来ない。
この時サービスをデリバーする部署が新設された。
(3人のエンジニアが立候補。現在は10名程度のチーム)
目標
外にフォーカスしたCI/CDの仕組みを作る。
アベイラビリティは非常に重要。速度だけを求めるのではなく、アベイラビリティを下げることなく、迅速かつ効率的にサービスをデリバーすることで、1日に100回のリリースとデプロイが出来、本番運用可能な基盤構築を目指した。
開発から運用まで分かれていたものを最適化を図る。
セキュリティも全てにおいて、最優先に扱う。
企画(プロジェクトヘイクト)
へイクトCI/CD。
コンテナの作成、開発、デプロイ、運用に至る全てを提供する。
継続的インテグレーション、継続的又は手動のデプロイ、コンテナの実行環境(開発、本番)、Dockerのベースイメージ、セキュリティにオペレーショナル・エクセレンスが済んだものを構築するプロジェクト。
目標にセキュリティが出てくるのは、セキュリティは重要で、セキュリティに問題が起こると、ユーザーがゲームを楽しむことが出来なくなる。7日間で8億時間なので、1日落ちてしまったら、1億時間ゲームを楽しむ事が出来なくなる。ユーザーに安心して遊んでほしいので、ユーザーのプライバシーを全力で守る。
方針
シンプルかつ必要最低限に。
本当に必要なものだけを作る。
少しのコードを書いて、動かせばそこから運用が始まる。
本当に最後まで運用出来るのか?をちゃんと確認しないと作成しない。
マネージド・サービスを活用する。
色々不便があるが、出来ない事があっても、実はそれは要らなかったりする。
ベンダーロックインに常に意識しているが、それでも便利に利用している。
結果的にAWSの薄いラッパーやフレームワークを作っていることが多い。
迷ったら目標を思い出す。例えば、何かの選択肢があったら、それは1日100回デプロイ出来る選択肢なのか考えたり、あまり悩まずまずは動かしてみようというスタンスだった。
なぜコンテナか
2016年当時、コンテナインスタンスとファンクションがあった。
当時のファンクションはあまり汎用的ではなかった。
AWSが面倒見てくれるのであれば、放おっておけば機能拡張してくれるだろうと思い、
コンテナにフォーカスすることにした。
システム構成
コンテナ基盤
* ECS
* ECR
* ALB
* Route53のサービスディスカバリは未使用
なぜECSなのか
2016年当時はk8s,swamが発展途上だと思った。
一番大切に考えたのがコンテナ化する事。
Dockerを採用してDocker化する事に注力し、将来どこかの時点で、オーケストレーションツールを切り替えるにしても、その時コストは低く抑えられるだろうと考え判断した。
運用監視
ログ、メトリクスをAWSに集約。
ログはkinesisからS3、メトリクスはCloudwatch、これらを処理するのはLambda。
コンテナからはFluentdで送信。
失って良いようなものはS3に、失っていけないようなものは、kinesisからkinesis firefoceに。
CI/CD
Githubの裏でcodepipelineで簡易フレームワークを作成。
エンジニアがコードをcheckinすると、Lambdaがhookされ、Lambdaがcodepipelineをhookし、テスト、DockerImageのマニュピレーションし、一番最後にステージング環境にデプロイする。
いろんな箇所にCodeBuildを使用している。(最初はECSを使用していた)
これらの情報をユーザーにフィードバックする。
ポイント
AWS Well-Architected Frameworkを使用して、設計の良い目次になった。
見逃しているポイントに気付く事が出来る。
基盤の環境構成のポイント
ステージング、本番環境を完全に同一にした。
異なるのはインスタンス数の差異のみ。
ステージング環境と本番環境の構成が違う事によって、構成の違いによる問題を早く気付く事が出来る。又、CloudFormationを使用しているので、容易にデプロイする事が出来る。
AWSVPCModeを使用すると、インスタンス数に更に上に乗せるコンテナまですべてEMIを使ってしまい、LambdaVPCとかを使うと更にインスタンス数より多くのIPアドレスを使用する。こんなにたくさんIPアドレスを使用するからサブネットを大きくすれば良いのかと思うかもしれないが、AWSのサービス毎にアタッチ出来るサブネット数が違ったり、巨大なサブネットを作成してしまうと、NatGatewayが分散出来ないので、NatGatewayに障害が起きてしまうと、サブネットが全滅してしまう可能性がある。それらを考慮して、あまりサブネットをアタッチ出来ないEMPは、大きめのサブネットを使用し、ECSタスクに使用するサブネットは、たくさんうまい具合に分散出来るようなサイズでIPアドレスをたくさん持つように設定を実施した。
ホスト
高速で安定した起動を優先した。
AMIは高速ということでGoldenAMIを使用している。
ECSクラスタは、単一の共通クラスタを作成した。(管理が簡単、リソース効率がいい)
クラスタにはオートスケーリンググループを設定し、スケールイン・アウトしている。
ENI数1インスタンスごとに最大アタッチ数が異なるので、CPUやメモリだけでなくENIも気にしている。
ECSタスクの設定は、全インスタンスで共通のリソース設定にした。
例えばどのサービスであってもTomcatはCPU1コア、メモリ2GBにすると、非常にチューニング、サイジングが簡単になり、性能はスケールアウトで、最適化したいときは後でやればいいと思った。
PersistのAutoScaleは、標準のメトリクスで足りないものが多い。
コンテナ単位で、セッション数、コネクション数だったり、ミドルウェアのスレッド数、コネクション数を取って、スケールイン・アウトを行っているが、設定は慎重に行っている。たくさんのメトリクスがあり、それぞれのメトリクスごとに、スケールアウトがするが、他のCPUをたくさん使用しているのに、メモリをあまり使わなくなったからスケールインをして良いわけではないので、すべてのアラームが収まった時だけスケールインする仕組みを導入している。
PipelineはPRとPushとReleaseの3種類。Githubのライフサイクルに近い。
アプリケーションのビルドとテストをして、DockerImageを作成して、開発環境に登録するものと、ステージング環境までデプロイするもの、本番環境のレジストリにイメージを登録するものがある。PipelineはGitBranchモデルを適用しているので、git-flowを適用しているので、git-flowでうまく動作するように、主要ブランチ毎のPipelineを作成した。1つのpipelineの中で、2度実行すると追い越しが出来なく、3つ目は処理が捨てられたりするので、なるべくbranch、pipelineを分けるようにしている。
デベロッパーへのフィードバックはSlackで情報を集約。
AWSはUIが弱い。デベロッパーエクスペリエンスの向上にはGithub、Slackに情報を集めるのが良い。
サービスデリバーは役割により、異なる人数、異なるソリューションがある。
Devの人は、ステージング環境までのCDパイプラインは、開発者はGithubのマージでデプロイをする、Codepipelineと、ECSのサービスアップデートを用いている。
本番環境のデプロイは、慎重に行う必要があるが、Opsの人は信頼性とガバナンスも非常に重視する。例えば既存の変更管理のプロセスに合致するプロセスが必要で、このような事をマニュアルで扱う独自実装ツールを足して実装した。
本番環境のデプロイツールは、CLIのstep functionのワークフローを使用している。
step functionから実行されるECSやLambda functionは、CloudformationでECSや色々なサービスを操作している。
カナリーテスト、BlueGreenDeployment、AutoScaling、承認者による承認プロセスは、独自実装する必要があった。
なにか問題があればロールバックするし、任意のバージョンにも戻る事が出来る。
進め方
本体の開発をして、一番最初のテストベットは、社内利用サービスを1つ移行してみた。
何か問題が起こってもいつでも切り戻しが可能なのと社内だけで影響範囲が済むから。
次に本番への移行を実施。
3つのサービスを移行した。
なぜすでに本番で動作しているサービスかと言うと、問題があった時、すぐに地盤を切り戻す事が出来るから。
最後のステップとして、最初からコンテナで開発するネイティブ開発を実施した。
やってみて
約2年間かかった。
基盤調査からアーキテクチャ設計と開発までに9ヶ月かかった。
ここから1度サービスを置いてみて、ひたすらチューニングした期間は10ヶ月。
最初の移行サービスのDockerlize期間は7ヶ月。
一番最後のネイティブ開発は、最初のサービスをステージング環境に導入するまで2ヶ月かかった。
Dev,Ops,Secのチームの役割が変わってきた。
一番最初はすべて自分達でやっていたが、徐々にOpsの人に入ってもらい、まずは使ってもらうことにした。最後にDevとOpsがPipelineもコンテナもサービスの構築運用も全部DevとOpsの人が出来るように、自分達で運用出来るところまでもっていった。
組織
たくさんの人との連携が必要だった。
Dockerの導入するときに難しいのは、境界が複雑で曖昧になる。
コンテナやOSにミドルが含まれてしまう、だいたいOSやミドルはOpsが担当することが多いが、デプロイを一部Devが行う点で、DevとOpsの境界が複雑と曖昧になる。なので多くの人と議論を行い、多くの人と理解、協力を得る必要があるので、Dev、Ops、Secの人たちを最初からたくさん巻き込んだ。
Dev
一番最初に巻き込んだ。開発者は新しい事にチャレンジするのに積極的だったが、一番最初に基盤を作って、何か動くというところまで持っていかないと、何もないところから使ってみてと言われても出来ないので、まずアーキテクトの方、今の仕組みに詳しいと協力しながら基盤を作成し、その後に実際にサービスを持っている部に協力してもらった。
最後に基盤に導入すると、既存のフレームワークだったり、既存のソフトウェアのコンポーネントを変えなければならない。環境変数の設定、ECSTaskのIAMロールの対応など。
内製のフレームワークをメンテしてる部に対応してもらった。
開発マネジメントの工数確保は非常に重要。それとは別にスクラムチームの参加があった。
Ops
技術好きが多く、コンテナに興味津々だった。
インフラチームを巻き込んだのは勿論だが、サービス運用チームは、運用マネージャーのアドバイスを受けて、サービス運用チーム内で、有志を募い8名のエンジニアが参加するワーキング・グループを作成し、有志なのでモチベーションが高かったので、元々タスクを持っていたオートスケーリングを実現したり、トラブルが起きた時のhotfix対応どうするか、など巻き取ってOpsのチームが進めてくれた。
AWSの人には導入前後に助けてもらった。
Sec
セキュリティはとても重要だが、最初からちゃんとしたセキュリティを導入すると、導入コスト、作業コストが下がる。最初に相談した時からSecの人は協力してくれた。
セキュリティはとてもむずかしい。専門知識が必要。
インスタンスとコンテナでどうハンドリングを異なるようにするのか。
コンテナの攻撃事例って例えばどういうものがあるのか。
セキュリティのオペレーションにはどんな変更があるのか。
上記らを一緒に相談し、解決した。
Dev,Ops,Secと、分かれた組織であっても、すべての要素が必要だったので、すべて必要な人を最初から巻き込んだのが、今回うまくいった原因だと思っている。
作成したシステムは仕組みが根本から違う。
導入は非常に大変。
開発も運用もみんなの理解を高める必要がある。
伝え広める為に、毎月デモデーを開催し、DevとOpsの人に任意で参加してもらい、進捗、課題、の状況を共有した。
一番最初はCIPipelineの話をした。
開発スクラムの人と一緒に、実際に開発するトライアルを実施し、進捗、課題、の状況を共有し、フィードバックももらうようなコミュニケーションを取った。
専用のSlackチャンネルを用意して、開発、運用の人達とのコミュニケーションの場にしている。
学習コストは非常に高い。
スクラムチームの学習コストを上げるためのオンボーディングプログラムを開発して実施した。
Pipelineを容易に設定出来るスキャフォールディングツールの提供とかそれらを使用したコンテナ開発のハンズオンの実施など。
リスク
リスクを管理して小さくしながら進めた。
一番最初にテストベットを作成し、テストベット移行はOpsの人に任せた。
テストベットから11ヶ月後、2018年3月にお知らせ系のサービスをデプロイした。
2ヶ月後にトロフィー系のサービスをデプロイした。
この時、タスクの数が一気に増えた。
タスク数5、1タスク3コンテナで動作して、1500コンテナが動作。
リスクを減らす為にステージング環境から本番環境へ。
コンテナのサイズは全環境同じで、タスク数が環境毎に異なるくらい。
アプリケーションの設定やネットワークの設定には多少差異がある。
慎重に進める為にやったこと、やらなかったこと
やったこと
移行手順の整理、レビュー
サイジング
何が起きるかわからなかったので余分なキャパシティを確保
カットオーバーの期限
どういう条件になったらサービス運用するのか
パフォーマンスにどういう違いがあるのか
いざという時の為に既存サービスに戻す手順
やらなかったこと
オートスケーリング、まず基盤を動かしてみる。オートスケーリングをやらない代わりに余剰キャパシティを確保した。
結果
トラブルはあったがユーザーに影響はなく、エラーの変動もなく、問題なく進行することが出来た。
トラブルはステージング環境で全て解決出来た。
問題が起こった例
- コンテナが落ちた
- コンテナが生成出来なかった
- クライアントから接続出来なかった
- ALBが同じVPCの中のNodeでしか動けないので、コンテナが登録出来なかった
- 本番環境でも問題が起きたが、リトライと自動復旧で、対処出来た
達成できたこと
速度。今まで1日かかっていたものが、Githubへのマージから、ステージング環境へのデプロイまで、20分で出来るようになった。(テストが長いともっと時間がかかるが、自分達で作成した本番環境へのデプロイツールや、そもそもコンテナの起動が早いので、Opsがする作業も含めて2.5倍早くなっている。
インスタンスからコンテナに変わることで、今までサービスの起動が分単位だったのが、秒単位になった。
ALBのヘルスチェックが結構時間がかかってしまう。
Docker、コンテナ、serverless、は、エンジニアを元気にする。
Slackチャンネルでの盛り上がり、ログインを簡略化するツールの作成してコントリビュートしてもらった。
課題
開発者の開発自体の加速の体験。
ラップトップPCで開発していて開発が早くなったという体験はなかった。
AWSはクライアントへのリーチが弱い。
k8sも良いかもしれない。
コンテナ化したら、今までインスタンスで動いていたものが、コンテナ専用のアーキテクチャを導入しないといけない。たくさんの依存関係がある場合、依存先もコンテナ化しないといけない。テストも大変になる。サービスのデカップリング、コンテナデザイン、service mesh、アプリケーションアーキテクチャの検討もしなくてはいけなくなる。
AWSのサービスをCI化しようとしたら、AWS用のコンテナがない。テスト実行時、マネージドサービスを動作させるのは辛い。
Opsの調査で見るべき箇所が増えた。ログも分散してしまったので、ログを集約したい。
共用クラスタのような複数クラスタをどうやってコスト配分すれば良いのかが今問題になっている。
見やすくなったが、何が問題なのかわからないのも新しい課題である。
AWSのサービスはたくさんありすぎて困っている。どう理解してどう使用するか、AWS提供の機能とかは、簡単なのが多いが、どうしても必要となったら自分達で作る必要がある。AWSの世界では多く便利であるが、Dockerの世界では別のものを作らないといけない。AWSのコンポーネントは似たようなものがあるがどれを使ったらいいのか困る。
AWSの変化は本当に早い。マネージドサービスを導入して、仕組みを作りそのまま放っておけば良いのではなく、継続的に変更をキャッチアップすることが必要。
Bootstrap機構、環境変数の持ち回し、デプロイツール、LaptopPCからタスクを起動するツール、セキュリティのインテグレーション、 ログ周りetc...コンテナ周りは色々作成した。
今後
変化への対応をどうしていくか、2014年に今の環境になっているとはまったく想像していなかった。
未来を予測するのはかなり難しい。
来年、 コンテナとfunction、どちらが優勢になっているかわからない。
変化を受け入れる、変化する前提で進める。
変化しにくいものから交渉していく。
オーケストレーションツールよりもDocker化から。
作りすぎないこと。作れば変化を妨げる。そこから運用が始まる。
捨てる勇気をもつこと。マネージドサービスに同じような機能があったら、マネージドサービスに作ったものを捨ててでも移行しても良い。新しいデファクトスタンダードが出てきたら、勇気をもって採用するのも必要。
Dev/Ops/Sec、CI/CDの流れは、人の変化を必要とする。
DevにしてもOpsにしてもSecにしても、1つとして欠かさず全ての要素が必要。ますます重要になってくる。
CI/CDの流れは、Dev/Ops/Secの境界線を曖昧にし複雑にする。変化に備える必要があるが、人の変化はとても時間がかかる。Devの人はOpsを、Opsの人はSecを、と言ったような自分の持っていない要素を持つ人を見つけたり、何か新しい事へのチャレンジ、一緒に取り組む仲間を見つけると良い。
コミュニケーションはとても重要。
多くの価値を作る事にたくさん時間をかける為に、コンテナとserverlessは非常に良い味方になる。