トランスクリプト
Protsenko氏:私の名前はMykytaです。Netflixで働いています。私の仕事は基本的に、他の開発者が遅くまで職場に残らなくてもいいようにすることです。彼らが午後5時に退社しても生産的であることが私の実現したいことです。私はプラットフォーム組織、つまり生産性エンジニアリング部門で働いており、他のエンジニアのために労力を抽象化しようとしているのです。エンジニアが同じ退屈な技術的問題に何度も対処するのではなく、ビジネス上の問題の解決に集中できるようにします。
いくつか質問させてください。あなたたちのうち何人が、自分で作って自分で動かすという哲学を実践している会社で働いてますか?生産現場との間にゲートキーパーがいないこと、機能や修正をより早く提供できることに満足している人はどれくらいいますか?本番環境で発生したインシデントに対処しているときに、どうすればいいのか分からず、他の人の問題であってほしいと思ったことがある人は何人いますか?正直に言いましょう、私自身も何度か経験したからです。しかし、開発、QA、運用の間に大きな古い壁があり、あなたがコードを書いてQAに投げ、QAはバグを見つけます。そしてQAがバグを発見し、それをまたあなたに投げ返すのです。そして、そのコードをまた別の壁に投げつけてオペレーションに任せ、ユーザーに公開します。そしてユーザーがバグを見つける、このプロセスが繰り返されるのです。
加速する:DevOpsの現状 2021
私の意見だけではなく、DevOpsの現状調査でDevOpsの導入が進んでいる企業ほど業績が良いことが毎年明らかにされている。変更の失敗率が低いのだ。一般的な企業に比べデプロイメント回数が多いにもかかわらず、基本的に失敗が少ないためだ。しかしながらこれには代償が伴う。なぜならプロダクションで自分のコードを運用するということは、何か発生したら即対応する必要があるからだ。インシデントに対応する準備が必要なのだ。貴重なフィードバックを提供してくれるかもしれない実際の顧客と実際にコミュニケーションを取る必要もあるかもしれないが、彼らにはより多くの時間と注意力が必要となる。その結果、あなたは間違いなくコードをより速くリリースでき、コードの品質は向上する。さらに運用業務の負担は無視できないものになる。開発工程の前倒しの動きが加速するにつれて、開発者の負担は増えていくばかりだ。
工程の前倒し
それはどのように起こるのか?テスト工程を前倒しする例でおさらいしてみよう。伝統的なソフトウェア開発ライフサイクルでは、テスト活動は最後のステップのひとつである。テストの大部分は、コードが書かれた後に行われるのだ。テスト駆動開発のようなものを使い、テストを自動化することで、テスト活動の大部分を初期の段階にシフトできる。バグを早期に発見できれば、その分費用も少なくて済むからである。開発段階でバグを発見できれば、ユーザーがそのバグで苦しむ必要はなくなる。計画段階でバグが見つかれば、欠陥のあるコードを書くために時間とリソースを浪費する必要もなくなるのだ。
もちろん、それは簡単なことではない。そのためには、開発者はテストフレームワークやテスト方法論、JUnitテスト駆動開発、JenkinsのパイプラインDSL、GitHub Actionsといったものを学ばなければならない。これらは今や開発者の日常業務の一部となっている。しかし、工程の前倒しはテストだけでは終わらない。次のステップはセキュリティに関する工程を前倒しすることかもしれない、なぜなら開発者が本番環境でコード設計をして、デプロイし、操作する自由を与えることになるのだから。その一方で、開発者とセキュリティ専門家が協力し、開発者は静的コード解析や脆弱性スキャンのようなセキュリティに関連することを学び、対処しなければならない。前倒しされるのはそれだけにとどまらない、さらに多くの工程で前倒しが行われている。例えば、データガバナンスのようなものだ。一方では、DevOpsというコンセプトが、こうしたさまざまなプラクティスで工程を早めるという問題がある。それは障壁を取り除くものだ。そのおかげでより速く、より信頼性の高いコードを提供できる。その一方で、私たちはまったく新しい悩みの種を手に入れた。私たちは今、両極端の間を行き来する必要がある。開発やコード出荷の邪魔になるような厳格なプロセスは導入したくないが、この絶え間ない変更の流れを確保するために支払う代償は避けられない。しかし、代償を最小限に抑える、手ごろな価格にできるし、そうするべきだ。その方法について話そう。DevOpsの複雑さ、工程前倒しの複雑さをより管理しやすくするためにはどうすればいいのか?その過程で直面する可能性のある問題は何か?
歴史を振り返る
まずこれまでの経緯を振り返り、パターンを特定できるかどうか、解決策を思いつくかどうかを見てみよう。これまでのDevOpsのステップをいくつかの点に注意して振り返ろうと思う。基本的にはコミュニケーションに注目するつもりだ。私たちが直面している問題は、私たちの関わり方にどのような影響を与えているのだろうか?そもそも誰が誰とコミュニケーションしているのか?ツールの使い方を見ていこうと思う。それぞれのステップにおいて、どんなツールが最適なのか。そのツールをどのように設定するのか?ブートストラップ中、新しいプロジェクトの作成中、そしてマイグレーション中に起こる認知的負荷について見ていこうと思う。基本的には、セットアップがどれだけ簡単か、変更がどれだけ簡単かを見ていくつもりだ。また、私が話していることが単なる手探りのものでないことを確認するために、できる限り私の日々の仕事からいくつかの例を挙げてその道のりを説明しようと思う。
アドホックDevOps
DevOpsの導入はどのように始まるのだろうか?小さな会社やスタートアップ、あるいは大企業の独立したチームなどで、人為的な障壁を築くのではなく、迅速にリリースを進めたいと考える人々がいるかもしれない。彼らは、反復的な作業を特定し、自動化し、それらを排除したいと考えている。現在では、いくつかのサービスに関して一緒に働く部門横断的なチームがあるかもしれない。このチームのメンバーは、主に非公式な会話で情報を交換する。たとえ文書があったとしても、たいていはとても簡単なものだ。GoogleドキュメントやConfluenceページがあり、必要に応じてそれを参照できる。自動化のツールも、かなりアドホックだ。
簡単な例を挙げよう。まず、コードを保存するGitHubリポジトリをいくつか立ち上げる。Jenkinsサービスのインスタンスを作成して、これらのリポジトリの変更を監視し、ビルド・ジョブを実行してDockerイメージを作成し、Artifactoryに公開する。コードをテストするためのJenkinsジョブや、KubernetesクラスタにデプロイするためのJenkinsジョブをセットアップできる。この段階では、これらのJenkinsジョブやKubernetesクラスタの管理は、手動でも半手動でも、大した労力ではないので全く問題ない。文字通り、数行のコードや設定をあちこちにコピーペーストするだけだからだ。この時点では、ペインポイントを見つけ、トイルを自動化し、ソフトウェア開発プロセスからこの手作業を取り除くことが目標だ。あなたがセットアップした数個のスクリプトとジョブは、明らかな利益をもたらす。自動化されたビルドとデプロイメント・プロセスは、手作業でデプロイするよりはるかに優れている。さらに、JenkinsとKubernetesによる自動管理に注力することは、手作業による小さな変更に比べ、より多くの時間を必要とするかもしれない。結局のところ、この時点では、いくつかのプロジェクトと数人が一緒に働いているだけだ。何かがデプロイされれば、誰もが何が起こったかを知ることができる。何百ものプロジェクトを監視しなければならないわけではないし、実験的なもの、長期的にうまくいくかどうかも不明なものを自動化するために多くの時間を投資するのは全く割に合わないかもしれない。
マイグレーションについて変更を加えるのはどうだろう?もちろん、刺激的なものにしよう。通常のKubernetesデプロイでは不十分だと判断したらどうだろう?高度なカナリア分析が欲しくて、Jenkinsの代わりにSpinnakerを使うことにしたらどうだろう?なぜなら、Spinnakerはすぐにカナリアをサポートしてくれるからだ。素晴らしいツールだ。それでもかなり簡単だ。実験すればいい。プロジェクトのひとつを選び、すべてがうまくいくまでいじり続け、残りのサービスを続ければいい。プロジェクトごとに、Jenkinsのジョブを無効にし、Spinnakerのパイプラインを有効にし続けることができる。何かがうまくいかなくて、壊れて不完全な状態で放置されるリスクは低いからだ。なぜなら、変更を加える必要がある場所の数は文字通り片手で数えることができ、この変更を1つのシートでできるからだ。5つのマイクロサービスがあり、それぞれのパイプラインをアップグレードする必要がある場合、オーナーを見つけるのはそれほど難しくない。チームや会社の数人かもしれない。ツールについては、問題を解決するものであれば、基本的に何でもいい。どのようにセットアップするかは、基本的にはまだ問題ではない。たしかに、関係者全員にとって多少の追加作業は発生するが、労力自体の規模が限られているため、まだ忙殺されるようなものではない。すぐに目に見える利点がある。開発者は、手作業のステップや反復作業を心配する必要がなく、コーディングに集中できる。このアプローチは、ほんの一握りのサービスしかない現段階では、開発者の認知的負荷を軽減する。
実際の例を挙げよう。たくさんのベストプラクティス、自動化が行われているNetflix社であっても手動管理の余地はまだある。例えば、このアプリケーションにはいくつかのキャッシュがあり、本番トラフィックの受け入れを開始する前にアプリケーションがキャッシュを事前にもっておく必要がある。そうしないと、パフォーマンスが劇的に低下する。このパイプラインは、このアプリケーションを所有するチームによって手動で作成されたものであり、実トラフィックを受け入れ始める前に遅延に備えるものだ。これは単一のアプリケーションに対応する単一のパイプラインであるため、メンテナンスとサポートは大きな問題ではない。このパイプラインを管理するチームは小規模なので、メンテナンスの責任者を見つけるのは難しくない。パイプラインはかなり自己完結しているので、更新もかなり簡単だ。パイプラインはほとんど自己完結しているからだ。必要な変更を加えるだけでいいのだ。この手動でメンテナンスされるパイプラインは、即座に利益をもたらす。コールドキャッシュに起因するパフォーマンス上の問題を解決し、運用業務をシフトすることで生じる認知的負荷を取り除くことができる。
次のステップは?
しかし、アプリケーションの数が増え続け、複雑になるにつれ、ユーザーが何を求めているかを把握することが難しくなり、新たな課題に直面している。Rubyで書かれたアプリケーションがいくつかあり、残りはJavaで書かれたアプリケーションだ。Javaアプリケーションの一部はJava 11を使用しているかもしれない。Javaアプリケーションの中には、すでにJava 17を使っているものもあるかもしれない。その上、それらのアプリケーションを管理する人の数も増える。ブートストラップを使ったとしても、新しいサービスを作りたい新しい開発者がいる場合、どんな内容であれその開発者が正しい例を見つけるのは簡単ではない。このことについて話す適切な人を見つけることさえ難しいかもしれない。
アプリケーションの保守についても問題になる。というのも、全フリートで共有しなければならない改良がある場合、全サービスと全オーナーを探し出す必要があるからだ。それはもう簡単なことではない。例えば、Spinnakerパイプラインをカナリア・デプロイメントでセットアップし、すべてのJenkinsジョブを無効にし続けるというマイグレーション・シナリオを考えてみよう。しかし、このアプローチをスケールし始めると、すぐに次の問題にぶつかる。何十、何百ものアプリケーションをマイグレーションしようとすると、手作業でこれを続けることはできない。なぜなら注意すべきことが多すぎるし、うまくいかないことも多すぎるからだ。何百ものアプリケーションがあれば、誰かが何かを忘れるだろう。誰かがこの設定を台無しにしてしまう。マイグレーションを始めて、気が散って、不完全で壊れた状態を放置してしまう。そのことを忘れてしまうのだ。本番環境での事故が発生し、アプリケーション基盤がどのような状態にあるのかを把握する必要が出てきたときのことを想像してみてほしい。人々はこの作業量に驚くだろう。すべての変更を追跡し、すべての努力を調整することは、もはや簡単な問題ではない。それこそが、私たちが始めた問題なのだ。私たちは、こうした運用の仕事を任せてしまったことで、開発者により多くの認知的負荷を与えてしまった。
舗装された道
ここで、一つの概念に触れる。これはさまざまな名前で知られている。ある会社では舗装路と呼ばれている。他の会社ではゴールデンパスと呼ばれているが、内容は同じである。舗装された道とは、昔ながらのゲートキーピングと自由奔放なアプローチとのバランスを見出そうとする試みである。通常、自由奔放なアプローチがあまりに多くの混乱を生み、あまりに多くの認知的負荷を生み出すことが明らかになると、人々は代替案や指針を探し始める。より良い解決策、より中央集権的な解決策を探し始めるのだ。人々は、自分たちのために問題を解決してくれる専門家の必要性を感じ始める。そのような専門家は、もともと数人のボランティアかもしれないし、別のプラットフォーム・チームを構成するかもしれない。それらの専門家は、キュレーターが展覧会のために美術品を選ぶようにソリューションを選択し、キュレーションされたソリューションを開発の残りの部分に提供できる。ここから責任の分離が始まる。専門家は、すぐに機能するソリューションを選択可能だ。彼らは一定レベルのサポートを保証する。開発者は、実験の結果を理解し、実験に責任を持つ限り、それらのソリューションを使うことも、他のツールで実験することも自由なのだ。
例えば、Cassandraがキュレーションされたソリューションとして提供され、Cassandraクラスタに何か問題があった場合、開発者は問題を報せるメトリクスやアラートを理解しているエキスパートに頼ることができる。開発者はエキスパートの診断を基にCassandra固有の問題を修正できる。基本的に、開発者自身がCassandraのエキスパートにならなくてもよいのだ。それでも、開発者は標準以外のソリューションを自由に使用できる。自分たちの特定のアプリケーションにNeo4jを使う必要があると感じたら、自分たちで面倒を見ようとする限り、そうすることが可能だ。基本的に、我々は相互作用の変化を見る。この段階は、責任の明確な分離が必要となる。この分離が形式的なものであるかどうかはまだ問題ではなく、全員が理解し、2つの異なる役割の間に明確な期待が設定されている限りである。
この段階でのツールについてはどうだろうか?ブートストラップから始めよう。ブートストラップ、つまり新しいサービスを作るとき、開発者はアプリケーションのそのやり方や、新しいサービスを作るたびに新しいインフラを作る方法をもう一度探す心配をしたくない。ここで役立つのが、単一のエントリー・ポイントを持つことだ。アプリケーションを見つけ、操作し、作成するために毎日使用する開発者ポータルやコマンドラインツールがあれば、舗装された道を見つけることが簡単になる。このツールやポータルを毎日使う。筋肉を記憶させるのだ。常識的に考えて、開発者が新しいサービスを作る経験は、既存のサービスを保守・運用する経験よりも少ないのが普通だ。サービスの作成と運用の両方が同じエントリーポイントを使うのであれば、開発者は、それを作成し、関連するすべてのインフラをセットアップするという、あまり頻繁に行われないことのやり方を覚える必要はない。そう、開発者はすべての手順を覚えているわけではないかもしれない。しかし少なくとも、「重要な要件」があることを覚えていて、新しいサービスを作成し、それがどこにあるのかを知っている。あるいは、コマンドラインツールがあれば、開発者はヘルプコマンドを実行し、実際に実行する必要があるコマンドを見つける方法を知ることができる。
次のステップについて話そう。このシングル・ポイント・オブ・エントリーを利用して、プロジェクトのブートストラップに必要なインフラやリソースを作ることができる。具体的には何をするのか?それを適切にするには、インフラをコードとして扱わなければならない。Kubernetesクラスタ、ロードバランサー、データベースといった運用インフラだけでなく、すべてのインフラを扱う必要がある。ビルドやデプロイ、テスト、セキュリティなど、ソフトウェア開発のさまざまな前倒しされた工程をカバーするインフラも含まれる。今なら複数のツールがあるので間違いなくできる。GitHub ActionsやJenkins pipeline DSLのようなものもあるし、コードとして設定するだけで問題に対処できる他のツールもある。なぜそれが重要なのか?なぜそのコードが必要なのか?そうすれば、同じコードを別のプロジェクトで再利用できるからだ。コードをインプットとして受け取ることができるようになることで、実装の詳細を抽象化できる。開発者にハイレベルな質問に答えさせ、その答えがコードによってインプットされることで必要なインフラを作ることができる。インフラが手作業で管理されるときに存在する認知的負荷を取り除くことが可能なのだ。
では、どのようなコードが最適なのだろうか?私の経験では、命令型コードは合わないかもしれない。なぜなら、例えば新しいデプロイメント・パイプラインを作成するスクリプトを書くことができるからだ。とはいえ命令型コードでも同じであるがあらゆる極端な例について考慮しなくてはならない。というのも、基本的にあなたは、世界の現在の状態をこのように変えてください、と言っているのだから。もし現在の状態が期待したものでなければ、エラーに直面するかもしれない。なぜなら、作成しようとしているデプロイメント・パイプラインがすでに存在していたらどうするか?存在しており、構成が違う場合にはどうするのか?それらの状態をどのように調整するのか?それらを考慮する必要がある。宣言的なアプローチは、インフラストラクチャーにはとても有効だと思う。宣言的アプローチでは、基本的にどんなリソースであれ、インフラリソースを記述できる。そうすれば、ツールはその特定の目標を達成するために必要なあらゆる変更を見つけ出す。宣言型ツールは、何度実行しても、実行するたびに世界を同じ状態にしてくれる、冪等性という利点がある。独自のデプロイスクリプトを作成する必要も、何かを変更するたびにスクリプトを調整する必要もない。変更が必要な場合は、宣言そのものを変更すれば、それを実現するために必要なあらゆるステップを宣言型ツールが処理してくれる。パイプラインをプロビジョニングする必要があり、それが存在しない場合、宣言的アプローチでは、それが作成され、すでに存在する場合は何も起こらない。存在するが、コンフィギュレーションの不一致がある場合、それは調整されるのだ、特別に必要なことはない。
このアプローチを、開発者が使用できるソリューションに取り組む専門家がいる、責任の分離がなされている我々の舗装されたパスに適用してみよう。開発者は、宣言型ツールを使って舗装された道を定義する。例えば、宣言的パイプラインDSLを使ってJenkinsのジョブを定義して、ジョブをコードとして記述できる。そのコードを舗装された道となるリポジトリに自動的にコミットするようにすれば開発者は何もする必要がない。ただ動き出すだけだ。マイグレーションに関しては、エキスパートがカナリア・デプロイメントの導入を決めたとき、Jenkinsの代わりにSpinnakerへのマイグレーションを決めたときの例に戻ることができる。Spinnakerのパイプラインを記述する別の宣言的コードを設定し、同じアプローチを使い続けることができる。基本的に、開発者に労力をかけないマイグレーションを提供し続けることが可能だ。宣言文を変更するだけで、インフラは透過的に変更される。そう、私たちは、構造化されていない手作業によるソリューションが生み出す深刻な認知的負荷を軽減するために、一握りの専門家が対処しなければならない作業を少し追加したのである。
この例をもっと具体的にしてみよう。シングル・ポイント・オブ・エントリーのコンセプトに戻ろう。Netflixでは、Newtというコマンドラインツールを使っている。Newtは、さまざまな種類のプロジェクトで一般的なアクションを実行するためのエントリーポイントを提供してくれるからだ。JavaプロジェクトやNode.jsプロジェクトのようなプロジェクトでも、Newtを使ってビルドし、ローカルで実行してデバッグしできる。新しいプロジェクトを作成するためのブートストラップコマンドを見つけるのは簡単だ。それを覚えていなくても、いつでもNewtのヘルプを呼び出すことができる。新しいプロジェクトが作成されると、Newtは人間が読みやすい質問をたくさんして、例えばJenkins Jobsのように、私たちが話したインフラに変換するのだ。その裏では、社内の別の宣言型ツールと統合され、そのツールがJenkinsやSpinnaker、その他のソリューションとのやり取りをする。専門家は、JenkinsジョブやSpinnakerパイプラインのように、作成する必要のあるリソースをコード化し、テンプレート化できる。ツールは、開発者が特定のアプリごとにJenkinsジョブを個別に設定するのを助けることができるのだ。アプリケーション名やGitリポジトリURLのようないくつかの設定は、新しく作成されたジョブで自動的に設定可能である。その上、ブロック全体、プロセスのステップ、ジョブの一部を、開発者が提供した入力を使って条件付きで設定可能だ。これはテンプレート化されたエンジンのほんの一部に過ぎない。そして、これらのテンプレートは、Jenkinsのようなシステムが必要とする出力を提供するためにレンダリングされ、Jenkinsのジョブを記述するためにXMLを使う。JSONを使ってSpinnakerのパイプラインも記述可能だ。
そして、宣言的なツールは必要に応じてリソースの作成や更新できる。ここでは、入力を受けて適切なJenkinsジョブを生成可能だ、テンプレート化されたXMLの一部を用意した。これはブートストラップの問題に加えて、マイグレーションの問題も解決する。もしエキスパートがインフラに変更を加える必要があれば、この特定のテンプレートに変更を加えればいい。それは、我々の理論的な例のように、JenkinsジョブをSpinnakerパイプラインに置き換えるような複雑なものであることもあれば、我々がここで行っているような特定のJenkinsジョブでJenkinsプラグインを置き換えるような、もっと的を絞ったものである可能性もある。あるいは、この例のようにAWSメタデータサービスのセキュリティ問題に対処できる。アプリケーション群の管理していると、このような変更は定期的で、頻繁に起こることがわかる。毎日ではないにせよ、毎週起こるのだ。もし手作業で行わなければならないのであれば、それは何千回も切り刻まれるようなものだ。。もしあなたがこの変更を自動化できて、ただ宣言を更新し、それを全社に展開できれば、あなたは不可能なことを何日も何週間も続けて、開発者を通常の仕事やコーディング、ビジネス上の問題解決から引き離そうとせずに済む。もう開発者の職務ではないインフラ移行を、開発者に強いる必要はない。数十のアプリケーションがあれば、それはすでに大きな勝利であり、数百以上のアプリケーションがあれば、このアプローチは救世主となる。
舗装された1本の道?
しかし、会社の規模が大きくなれば、舗装された1本の道では十分ではなくなってくる。なぜなら、より均質な企業、より均質なチームでは、すべての開発者が同じ問題群に取り組んでいるのであれば、同じソリューション群に従うことはまったく理にかなっているからだ。そう、会社の規模が大きくなるとそうではなくなるかもしれない。例えば、Netflixでは、ストリーミング・プロジェクトに携わる開発者は、高負荷、高可用性のシステム、明確なトラフィックのピークに対処しなければならない。なぜなら多くの人が仕事を終えて帰宅し東部時間の午後6時から映画を見始めるからだ。一方、スタジオ・オートメーションに携わる開発者たちは、それに劣らず重要な問題を解決している。これらの問題は、まったく異なる側面、異なるトラフィックパターン、異なる可用性要件、異なるレイテンシー要件などを持っている。スタジオではうまくいっても、ストリーミングではうまくいかないこともあるし、その逆もある。例えば、カナリア・デプロイメントでは、サービスの新バージョンをユーザーベースのわずか数パーセントに展開する。ストリーミングではうまくいくが、スタジオでは信頼性の高い兆候を確認できないかもしれない。なぜならユーザーベースの規模が数桁違うかもしれず、規模があまりにも違うからだ。
規模に応じた舗装道路
このような状況では、単一の共有パスはもはや機能しない。さらに状況を複雑にするのは、異なる舗装された道が交差する可能性があることだ。例えばスタジオの場合、ストリーミングがバックエンドにJavaを好むなら、舗装されたパスのその部分だけを一度だけ実装し、その後必要な部分を合成するのが理にかなっている。最善の方法は何だろうか?舗装された道をどのように構成するようにアプローチすればいいのか?そのためには何を実装すればいいのか?まず第一に、単一の特定分野の専門家集団はもう機能しないだろう。なぜなら、確かに専門家の何人かは、全体にわたって再利用可能な部分に取り組むことができるし、そうすべきだからだ。複数の舗装道路には、実は専門的な知識が必要だ。特定の領域と組織の問題に精通した専門家が必要になる。私たちはここで、さらなる職務分掌を目にすることになる。組織レベルの専門家は、特定のチーム、特定の組織で使われるソリューションに取り組むことができる。その一方で、これらの専門家は、プラットフォーム組織のような企業レベルの専門家が提供する共通のプラットフォーム・ソリューションに頼ることができる。ストリーミング対スタジオの例に戻れば、ストリーミングに特化した問題を解決するストリーミングの専門家がいるかもしれない。例えば、定期的または毎日のトラフィックの急増に対応しながら、大量のサーバーを安全に再配置するには何が必要か?プラットフォームの専門家は、ストリーミングとスタジオの両方で再利用可能なビルディングブロックを提供できる。例えば、マネージド・データベース・ソリューションやマネージド・セキュリティ・ソリューションを提供可能なのだ。
第二に、実際の実装に関しては、単体テストの実行のようなこれまでと同じ道と、高トラフィックのアプリケーションに効果的なカナリアデプロイのような異なる道がある。それぞれを別々に実装することは、同じものを2回実装するリスクを生むことになるのだ。これはメンテナンスの負担を増やすことにもなる、なぜなら開発初期段階で2回実装しなければならないからだ。また、変更も重複して行わなければならない。標準的な舗装道路のアプローチでは、宣言型ツールを使ってベストプラクティスを成文化すればよい。しかし、複数の舗装された道があり、それらが特定のプラクティスを共有している場合、それらの異なるプラクティスを分離するためにコードも分割する必要がある。例えば、ビルドとデプロイのような明確なステップを持つ舗装された道がある場合、それぞれのステップを別々にコード化し、バージョン管理する必要がある。デプロイはビルドに依存するのであって、その逆ではないからだ。
こうすることで、手っ取り早く勝利を得ることができる。なぜなら、どのアプリにどのバージョンのインフラが使われているかがわかれば、アプリxの中で何が起きているのかわからないというような混乱を避けることができるからだ。このように書かれているからこうでなければならない、他の方法はありえない、というような厳格な審査を避けることができる。異なる舗装路を分け、異なる方法を提供できる。もっとも重要なのは、私たちには1つの舗装された道しかなく、これがあるべき姿だから、それに対して何もできないのですべてを移行しようというような、大きな悪しき移行を避けるのである。その代わりに、新しい解決策を試すことができる。いくつかのアプリを選んで試してみて、問題があるかどうかを確認できる。もし問題があれば、宣言的アプローチを使っているのだから、安全に以前の良いバージョンに戻ることができる。私たちは、このような世界にしたい、と言っているのだ。既存のリソースをxからyに変更するために、zのアクションを実行するというような命令型のアプローチはしない。なぜなら、この命令的アプローチでは、何か問題が発生した場合に、前の状態に戻るために別の命令型のマイグレーションが必要になるかもしれないからだ。特に、元のロールアウトが部分的に成功しただけで、実際の状態がxとyの間にあるような場合は厄介である。
デプロイメントパイプラインをJenkinsからSpinnakerに移行する、元のマイグレーションの例で、このアプローチがどうなるか見てみよう。ビルドとデプロイのような2つの大きなインフラブロックを持つことができる。それらを別々にコード化できる。別々にバージョン管理もできる。バージョン要件を定義できる。私たちは破壊的変更を行なっているので、デプロイステップのメジャーバージョンをインクリメントして、アプリケーションのごく一部のサブセットだけに最初にロールアウトすることも可能だ。そして、何も壊さないことを確認したら、全体に展開できる。こうすることで、影響範囲を最小化し、信頼性を最大化できるのだ。なぜなら、インフラ・コードの各部分を個別にバージョン管理することで、見通しが良くなり管理しやすくなるからである。もし舗装された道を2つ以上に分割し続けることに決めたら、共通のパスを重複排除し、別々のパスを別々に保つことができる。基本的には、標準的なコーディング・プラクティス、バージョン管理、コードの再利用、管理されたロールアウトを適用し、舗装された道をコードとして表現するのだ。このアプローチは、デプロイメント・パイプラインの更新に限らない。宣言的にコード化できるソリューションであれば、セキュリティであれ、テストであれ、他の何かであれ、この方法で管理できる。その結果、開発者と専門家の両方にとって、移行時の認知的負荷が軽減される。組織レベルの専門家は、個別の道ごとに車輪を再発明する必要はない。共通のビルディング・ブロックを再利用し、特定のチーム、組織や企業の特定の部分に共通する問題解決やカスタマイズされたソリューションの提供に集中できるのだ。
例:gRPCとGraphQLの比較
私のNetflixでの経験から挙げることができる1つの例は、ストリーミングで主に使用される通常のgRPCアプリケーションと、スタジオで一般的なGraphQLアプリケーションを2つの異なる道に分けることだ。こうすることで、組織レベルの専門家は、単一の舗装された道が肥大化し、保守や管理が複雑になることを心配することなく、特定のタスクの解決に集中できる。この2つの道には大きな違いがある。ここでは、それらの大きく異なる部分だけを示している。例えば、gRPCアプリケーションは、クライアントのパブリッシングとプロモーション・プロセスの一部として、プロトバフ、gRPCスキャナ、gRPCコントラクトをパブリッシュする。これは実際にクライアントのjar(候補jarやリリースjarなど)に含まれる。これらのアプリケーションは通常、より予測可能なトラフィックを持つため、カナリアに依存する。一方、GraphQLアプリケーションのデプロイプロセスはよりシンプルだ。これらのアプリケーションは、GraphQLスキーマやGraphQLコントラクトを公開するための特別なパイプラインステージに依存する必要がある。なぜならそれらはjarとして公開されるのではなく、スタンドアロンのGraphQLレジストリとして公開されるからです。すべてのソリューションをスパゲッティのように敷き詰められた道の一部にしておくとしたら、構成の選択肢はなく、このアプローチは不可能に近いだろう。これらの経路を分離し、バラバラにすることで、異なる組織の専門家がお互いのつま先を踏むことなく独立して作業できるようになった。
気をつけるべきこと
聞こえはいいが、デメリットはないのか?何がうまくいかない可能性があるのか?と聞かれるかもしれない。良い質問だが、難しい質問でもある。というのも、どんなモデルも理論的には素晴らしく見えるが、実際には常に不意打ちを食らうことがあるからだ。ソフトウェア・エンジニアリングにおけるもっとも一般的な問題のひとつは、実際に抱えている問題を解決するのではなく、興味深い問題を解決してしまうことである。なぜなら、多くの人はGoogleがやっているようなこと、あるいはNetflixがやっているようなことをやりたがる、それが正しいことであることが多いからだ。多くの場合、他の企業より小さな世界の企業は、大企業が苦しんでいるような同じ種類の問題を抱えていないかもしれない。DevOpsに関して言えば、適切なツールを選び、適切なアプローチを選ぶという点では同じだ。我々は、DevOpsの旅のさまざまな段階について話してきた。必要なツールも異なる。まず、始めたばかりで、管理するサービスがほんの一握りしかないような状況を確認することは、間違いなく意味がある。もしかしたら、すでに数百のサービスがあるかもしれない。また、ダイナミクスをチェックすることも意味がある。もしあなたが爆発的な成長を遂げようとしていて、15のサービスがすぐに200になる可能性が高いと思うなら、近い将来に足枷となってしまうようなソリューションを利用しないよう、次のステップのための計画を立て始めることは意味があるかもしれない。
また、適切な抽象化レベルを見つけたい。抽象化しすぎるのは良いことではないかもしれない。具体的でなくなることで、日々の業務に不可欠でないものが見えなくなり、それが認知的負荷を減らす素晴らしいアイデアだと感じるようになるかもしれない。そのようなことは、本番のインシデントに対処するときには重要かもしれない。インシデントを調査するときだ。壊れたサービスを見ているとき。何が問題なのかを突き止めようとしているとき。私の同僚であるLorin氏が以前Twitterで指摘していたように、すべての人に通用する抽象化を見つけることは不可能だ。抽象化のレイヤーをナビゲートし、実際に何が起きているのかを理解できる。どのようにして抽象化をナビゲートするのか?そこには2つのことが重要だ。まず、自身がする変更の影響を理解する必要がある。この変更をしたら何が起こるのか?どのシステムが影響を受けるのか?次に、すでに起こった変化の原因を理解する必要がある。自分のシステムがこのような特定の状態にあるのを見たら、その原因となる変化は何だったのか、といったようなことを理解する必要があるのだ。もし抽象化がそれを助けてくれるなら、その抽象化のレベルは正しいということになる。私が強調したかったのは、抽象化において観測可能性を確保することで、日々のオペレーションにおいても、本番インシデント発生時の調査においても、認知的負荷が実際に改善される可能性である。
何から始めればいいのか?
どこから始めればいいのか?これも良い質問だ。私は特定のツールを推奨するつもりはない。私は、あなたが始めることができるいくつかの例を与えることができ、あなたはそれらを研究できる。競合他社が自分に合うかどうか調べればいい。Terraformから始めてもいい。インフラストラクチャー・アズ・コードに関してはよく知られた存在だ。プラグインシステムは拡張可能だ。カスタムソリューションを追加する必要があれば、プラグインシステムで簡単にできる。モジュール化、バージョニングアプローチは一見の価値がある。これによりインフラをバージョン管理し、舗装された道を管理する方法を提供できる。私が注目したいもう一つの宣言型ツールは、Spinnakerのマネージド・デリバリーだ。これは継続的デリバリーと関連インフラに焦点を当てている。これは確かに調べる価値があると思う。もちろん、GitHub ActionsやJenkins pipeline DSLのようなものもある。これらはより汎用的だ。あなたの特定の問題を解決するには、他のツールが必要になるかもしれず柔軟性も高い。そこから始めることができる。それらのツールを研究し、競合他社を研究し、何があなたの特定の状況にもっとも適しているかを見ることができる。
私は正しい道を進んでいるのだろうか?
また、自分のソリューション、インフラ、ビルド、デプロイ、テスト、セキュリティなど、あなたが解決しようとしているどんな問題にも取り組み始めている、と尋ねるかもしれない。あなたは私が開発者のエクスペリエンスを向上させ、認知的負荷を減らすことに取り組んでいると言いたいのだろう。自分の取り組みが正しいかどうか、どうやって判断すればいいのだろう?自分の取り組みが実際に役に立っているかどうか、どうすればわかるのか?自分の努力が正しい結果を生んでいるかどうかを見るために使える指標がある。代表的な指標は変更率と変更失敗率である。これによりデプロイの変更速度をチェックできる。デプロイが失敗する頻度はどれくらいか?これらはプロキシメトリクスだ。これらは他の要因に影響されるが、品質の高いコードを提供する能力を測るというような、本当に重要なことも測ることができる。別の指標として、サービスを作成する時間、サービスを起動する時間、サービスを再構成する時間がある。インフラを更新するのにかかる時間や、マイグレーションを実行するのにかかる時間も測定できる。適切な抽象化を選択すれば、単一のサービスでも、サービス群でも測定できる。実際に認知的負荷を軽減すれば、これらの時間も短縮されるはずであり、逆もまた然りである。変更に時間がかかる場合は、抽象化機能の調整が必要かもしれない。
質問と回答
Tucker氏:Netflixでは、実験的なカスタムソリューションから、全社に展開されたソリューションになる道はありますか?また、草の根的な展開ソリューションという概念はあるのでしょうか?
Protsenko氏:この問題には二つの側面があります。間違いなく、Netflixの開発者たちがあるソリューションに取り組み始め、それが自分たちのチームにとって有効であることに気づき、規模を拡大した例が思い浮かびます。Netflixでは、ローカル・セントラル・チームという概念があり、専門知識を持つ組織レベルの専門家のように、特定の組織の開発者を支援する責任を負うチームや開発者がいます。これは、草の根的な問題からソリューションをスケールさせるということに関連しているのです。質問にはなかったが、詳しく説明したいことがあります。そう、もしあなたがツールを用意し、インフラを定義し、それを拡張できるなら、基本的には、自作ソリューションを自由に拡張できるかもしれないことです。しかし注意点があります。それは、特定の技術的なソリューションやアプローチを拡張させるということだけではない、ということです。なぜならこのソリューションは維持されないとならないからです。開発者は専門家の帽子をかぶらなければならず、カスタマーサポートや移行の責任も負わなければなりませんがそれが障壁になることもあります。
残念ながら、障壁を取り除く魔法のような方法は存在しない。しかし敷居を低くする努力は可能であり、特に大規模なサポートを扱う場合はそうである。専任のサポート・チームが開発者や専門家をサポートすることで、アプローチの規模を拡大できるが、この領域に入る前に意識しておかなければならないことがある。もうひとつ、舗装された道のソリューションとしてエンコードされ、使用されている例を見たが、それは舗装された道にエスケープハッチがある場合である。舗装された道の中に、カスタムフックやカスタムプラグインが定義されていて、他の人がそれを利用できる場合になる。専門家が、このカスタムフックの中でJenkinsジョブが定義され、他の顧客によって呼び出されるのを見たとき、そしてその必要性が多くあるとき、私たちはその負担を軽減し、自分たちでそれを組み込んで保守すべきかもしれない。それはツールの問題ではない。それよりも、コミュニケーションとサポートの負担の方が問題で、私がここで強調したかったのはそういうことである。
Tucker氏: あなたが説明したビルディング・ブロックを使って単純化し、可能にした実際の移行の例を教えてくれますか?どんな具合でしたか?その影響はどうでしたか?
Protsenko氏:AWSのメタデータサービスが新しいセキュリティオプションを導入し、それを全フリートで有効にする必要がありました。数百、数千のアプリケーションでこのフラグを変更するだけで、手作業ならおそらく数カ月かかります。これらのアプリケーションがコード化されている場合、このフラグを変更してサービスにIPv6を導入して、Spinnakerのパイプライン内の特定のステージを置き換える必要がある特定のタイプのカナリアを有効にできます。新しいタイプのカナリアが導入されるなど、変更自体は難しいことではありません。カナリアはすでに組み込まれており、ただ置き換えるだけです。開発者にメールを送ってお願いするのは、大きな遅れにつながります。彼らには他にもっとやるべきなのです。すべてが成文化されている場合、私たちはただ変更をして、それを一連のアプリケーションに展開し、何も問題がないことを確認します。それから、この舗装された経路を使って、残りのアプリケーションに変更を反映させるのです。
Tucker氏:舗装された道は、チームが責任を負う製品だと考えていますか?それについて少し詳しく説明してほしいです。ストリーミングやスタジオのような事例で、、誰がその舗装されたパスを管理する責任を負っていたのか、説明してくれますか?それはどのように行われたのでしょうか?
Protsenko氏:どちらの例でも、私たちはそれぞれの舗装された経路を維持する責任を負う別々のチームを持っています。プロダクト・マネージャーの肩書があることをすでに述べたので、私はプロダクトの部分に焦点を当てたいと思います。正直なところ、ストリーミング・ソリューションのキュレーションを実際に担当しているチームの一員として、私たちは常に専任のプロダクト・マネージャーにアクセスできるわけではありません。私たちはその役目を担う必要がありました。私たちは実際に足を運び、顧客にインタビューをしたのです。実際にフレームワークを作り、さまざまな問題の重要性を評価しようとしました。特定の問題がどれだけ重要であるかというように、影響度という側面から見たのです。特定の問題による被害、影響、顧客数など影響度で評価しました。私たちはただ、主要な悩みが何であれ、それを分類しようとしたのです。なぜなら、いつでも解決できる問題はもっとたくさんあります。実際に舗装道路に組み込む前に、それらを分類し、優先順位をつけることが重要なのです。それが、製品の道筋からアプローチされるときの最大の難関なのです。
講演録をもっと見る