以下に学習目的で1日で訳してみました。
後半はまた、後日。
お試しなので、何か問題点や気になる点あれば、気軽に声を掛けて下さい。
第15章 - 設定の詳述設定の詳述
本番環境のシステム管理は、SREが組織に価値を提供する多くの道筋の1つです。
本番環境でアプリケーションを設定して実行するタスクには、
それらのシステムがどのように組み合わされているか、
そしてそれらがどのように機能するかについての洞察が必要です。
問題が発生した場合、オンコールエンジニアは設定がどこにあるのか、
またどのように変更するのかを正確に知る必要があります。
チームや組織が設定関連の問題に取り組むことに投資していない場合、
この責任が負担になる可能性があります。
この本ではToilを主題に詳細に渡ってカバーしています(第6章:Toilの排除を参照)。
あなたのSREチームが多くの設定関連のToilを抱えている場合は、
この章で説明するいくつかのアイデアを実行することで、設定変更に費やす時間を取り戻すことができます。
<設定によって誘発されるToil>
プロジェクトのライフサイクルの開始時には、設定は通常比較的軽量で簡単です。
INI、JSON、YAML、XML などのデータ専用フォーマットのファイルがいくつか扱っているかもしれません。
これらのファイルを管理することはたいしたToilではありません。
アプリケーション、サーバー、およびバリエーションの数が時間の経過とともに増加するにつれて、設定は非常に複雑かつ冗長になる可能性があります。
たとえば、あなたが最初は1つの設定ファイルを編集して「設定を変更」したことがありますが、
今度は複数の場所で設定ファイルを更新する必要があるとします。
重要な違いが関連性のない重複した詳細の中に隠されているため、
このような設定は読み取ることも困難です。
この設定関連のtoilを複製のtoilとして特徴付けることができます(設定管理の日常的なタスクはシステム全体に複製されます)。
この種の問題は、大規模な組織や巨大なシステムに限定されません。
特に、独立して設定されたコンポーネントが多数あるマイクロサービスアーキテクチャによく見られます。
エンジニアは、しばしば自動化や設定フレームワークを構築することによって複製されたToilに対応します。
これらは、設定システムの重複を排除し、設定を理解しやすく維持しやすくすることを目的としています。
ソフトウェア工学の手法を再利用して、このアプローチはしばしば「設定言語」を利用します。
GoogleSREは、私たちの最大かつ最も複雑な本番環境(生産システム)のtoilを減らすことを目的として、
いくつかの設定言語を作成しました。
残念ながら、この方法で必ずしも設定の手間が省けるわけではありません。
圧倒的な数の個々の設定から解放されて、プロジェクト(そしてその設定コーパス)は新たなエネルギーで成長します。
必然的に、あなたは複雑なtoilに立ち向かうことになります。
突発的で、時折複雑で自動化された望ましくない挙動に対処するという、チャレンジングで、フラストレーションの溜まるタスクです。
この種のtoilは、通常、大規模な組織(10人以上のエンジニア)で発生し、成長を続けます。
複雑なtoilに取り組むことのは早ければ早いほど良いです(設定のサイズと複雑さは、時が経てば経つほど大きくなります)。
<設定に起因するtoilの削減>
あなたのプロジェクトが設定関連のtoilで惑わされているならば、
状況を改善するためのいくつかの基本的な戦略があります。
ごくまれに、アプリケーションがカスタムビルドされている場合は、
設定を完全に削除することを選ぶかもしれません。
アプリケーションは、設定に関する特定の側面を処理する上で、
設定言語よりも優れている場合があります(アプリケーションは、
マシンに関する情報にアクセスできるためデフォルトを割り当てることや、
負荷に応じて拡張できるため動的に値を変更することに意味があります)。
設定を削除することが選択肢ではなく、レプリケーションの失敗が問題になっている場合は、
設定コーパスの重複を減らすために自動化を検討してください。
新しい設定言語を統合したり、既存の設定を改良したり置き換えたりする必要があるかもしれません。
1次のセクション「設定システムの重要な特性と落とし穴」では、
そのシステムの選択または設計に関するガイダンスをいくつか示します。
新しい設定フレームワークを設定するルートに進む場合は、
設定言語と設定が必要なアプリケーションを統合する必要があります。
既存のアプリケーションの統合:Kubernetesは統合される既存のアプリケーションの例としてKubernetesを使用しています。
カスタムアプリケーションの統合(社内ソフトウェア)はより一般的なアドバイスを提供します。
これらのセクションでは、Jsonnetを使用した例をいくつか紹介します
(これは、説明のために代表的な設定言語として選択しました)。
重複されたtoilへの一助として設定システムを持ったなら-既に既存のソリューションにコミットしていると、
新しい設定言語のベストプラクティスを実装することを選択するとに関わらず
-Effectively Operating a Configuration System, When to Evaluate Configuration, and Guarding Against Abusive Configurationはどの言語を使用しているかに関係なく、
あなたのセットアップを最適化するのに役立つでしょう。
これらのプロセスやツールを採用することで、複雑さを最小限に抑えることができます。
<設定システムの重要な特性と落とし穴>
設定設計とベストプラクティスでは、あらゆる設定システムの重要な特性について概説しました。
軽量、習熟のしやすさ、単純さ、そして表現力のような一般的な理想とされる要件に加えて、
効率的な設定システムは次の条件を満たす必要があります。
・設定ファイルを管理するためのツール(リンター、デバッガー、フォーマッター、IDE統合など)を介して、
設定の健全性、エンジニアの信頼性、および生産性をサポートすること
・ロールバックと一般的なreplayability(繰り返し再生可能性)のための設定のhermetic(外部影響を受けない)評価を提供すること。
・設定とさまざまな設定インターフェースを容易に分析できるように、
設定とデータを分離すること。
これらの特性が重要であることは広く理解されていません、
そして私たちの現在の理解に到達することは確かに、journey(旅)と呼べるものでした。
この旅の間に、Googleはこれらの重要な特性を欠くいくつかの設定システムを発明しました。
ですが、私たちも[そうした発明を行った]唯一の存在ではありませんでした。
多種多様な一般的な設定システムがこれだけあるにもかかわらず、
以下の落とし穴のうちの少なくとも1つにも反しないものを見つけることは困難です。
<落とし穴1:設定をプログラミング言語問題として認識しない>
意図的に言語を設計していないのであれば、「言語」が良い言語になることはほとんどありません。
設定言語は挙動ではなくデータを記述しますが、
それでもプログラミング言語の他の特性を持ちます。
私たちの設定戦略がデータのみのフォーマットを使うという目的から始まるのであれば、
プログラミング言語の機能はバックドアから忍び寄る傾向があります。
データのみの言語に留まるというよりも、むしろフォーマットが難解かつ複雑なプログラミング言語になります。
たとえば、システムによっては、countが属性として仮想マシン(VM)のスキーマに追加してプロビジョニングされています。
この属性はVM自体の性質ではありませんが、代わりに一つ以上の属性が必要であることを示しています。
これ便利はである一方、データ形式ではなく、プログラミング言語の機能であり、
外部のイヴァリュエイターまたはインタープリターが必要なためです。
古典的なプログラミング言語のアプローチでは、より多くのVMを生成するために、
forループやリストの内包表記などを必要に応じて成果物の外部でロジックを使用します。
もう1つの例は、一般式をサポートする代わりに文字列補間規則を採用する設定言語です。
文字列は「単なるデータ」のように見えますが、実際にはデータ構造操作、
チェックサム、base64エンコーディングなどの複雑なコードを含めることができます。
人気のあるYAML + Jinjaソリューションにも欠点があります。
XML、JSON、YAML、およびテキスト形式のprotocol buffersなどの単純な純粋なデータ形式は、
純粋なデータのユースケースに最適です。
同様に、Jinja 2やGoテンプレートなどのテキストテンプレートエンジンは、
HTMLテンプレートに最適です。
しかし、設定言語で組み合わせると、人間とツールの両方にとって保守と分析が困難になります。
これらすべての場合において、この落とし穴は、ツールには適さない複雑かつ難解な「言語」をもたらします。
<落とし穴2:偶発的または臨時の言語機能設計>
SREは通常、システムを大規模にオペレーティングするときに、
設定のユーザビリティの問題を感じます。
新しい言語には優れたツーリングのサポート(IDEサポート、優れたリンター)はありません。
その言語がドキュメントが整っていない、または難解なセマンティクスがある場合、
カスタムツーリングの開発は面倒です。
アドホックプログラミング言語の機能を単純な設定形式に追加すると、
時と共にそれが機能で完結するソリューションになる可能性がありますが、
アドホック言語は形式的に設計された他の同等の諸言語よりも、複雑で表現力が劣ります。
これらの作者は前もって機能間の相互作用を考慮することができなかったので、
彼らはまたハマりどころと特異性を開発するリスクを冒します。
あなたの設定システムが単純なプログラミング設定を必要とするほど複雑にならないことを願う代わりに、
最初の設計段階でこれらの要求を考慮することがより良いです。
<落とし穴3:あまりにも多くのドメイン固有の最適化を構築する>
ユーザーの母体が小さいほど、新たなドメイン固有のソリューションを取る為、
構築ツールを正当化するには十分なユーザー数が蓄積するのを、
より長い間待つ必要があります。
このドメイン以外では適用性がほとんどないため、エンジニアは設定言語を正しく理解するために時間を費やすことを嫌がっています。
Stack Overflowのような学習リソースでは利用が低いです。
<落とし穴4:「設定評価」と「副次的影響」のインターリーブ>
[設定評価の]副次的影響としては、設定の実行中に外部システムに変更を加えること、
または帯域外データソース(DNS、VM ID、最新のビルドバージョン)を参照することなどがあります。
これらの副次的影響を許容するシステムはhermeticity(気密性/)を侵害し、データからの
設定の分離も阻みます。
極端な場合、クラウドリソースを予約することによって、お金をかけずに設定をデバッグすることは不可能です。
設定とデータの分離を可能にするために、最初に設定を評価し、
そして結果として得られたデータをユーザに分析目的で利用可能にし、
そしてその後に副次的影響を考慮に入れます。
<落とし穴5:Python、Ruby、またはLuaのような既存の汎用スクリプト言語の使用>
これは最初の4つの落とし穴を避けるための簡単な方法のように思えますが、
汎用スクリプト言語を使用する実装はヘビー級であり、
そして/またはhermeticity(気密性/)を確実にするために煩わしいサンドボックスを必要とします。
汎用言語はローカルシステムにアクセスできるので、
セキュリティ上の配慮からもサンドボックス化を求められる可能性があります。
また、設定を管理している人がこれらの言語すべてに精通しているとは限りません。
これらの落とし穴を避けたいという願望は、HOCON、Flabbergast、Dhall、
およびJsonnetのような設定のための再利用可能なドメイン固有言語(DSLs/domain-specific languages)の開発につながりました。
既存のDSLを設定に使用することをお勧めします。
DSLがあなたのニーズには強力すぎると思われる場合でも、
ある時点で追加の機能が必要になることがあります。
また、内製の(社内の)スタイルガイドを使用して、言語の機能を常に制限することができます。
Jsonnetはhermetic(密閉型)のオープンソースDSLで、
ライブラリやコマンドラインツールとして使用することにより、
あらゆるアプリケーションに設定を提供できます。
Googleの内外で広く使用されています。
2この言語はプログラマになじみやすいように設計されています。
(Pythonのような構文、オブジェクト指向、および機能的な設定要素を使用します。)
これはJSONの拡張です。
つまり、JSONファイルは、それ自体を出力する単なるJsonnetプログラムです。
Jsonnetは、JSONよりも引用符とカンマで寛容であり、コメントをサポートしています。
さらに重要なことには、それは計算構造を追加します。
この章の残りの部分をフォローするためにJsonnetの構文に特に精通している必要はありませんが、
オンラインチュートリアルを少しの時間を費やして読むことで、正しい方向に向かう一助となり得るでしょう。
<注意>
Googleまたは私たちの読者の方々の母体の間には支配的な設定言語はありませんが、
我々はいくつかの言語を例として提供する必要がありました。
この章ではJsonnetを使用して、「14章の設定の設計とベストプラクティス」で提供されている推奨事項の具体例を示します。
あなたが特定の設定言語にまだコミットしておらず、Jsonnetを使用したい場合は、
この章の例を直接適用できます。
私達は下記のすべてのケースにおいて、コード例から基礎となる教訓を抽象化することが、
皆様にとってできる限り簡単になるように最善を尽くしました。
さらに、いくつかの例では、プログラミングブックで見つけることができると思われる概念(Turing completenessなど)を探ります。
私達は本番環境で私達が実際に手痛い目にあった捉えがたいことを、
説明するのに必要とされるだけ深く潜るように細心の注意を払いました。
ほとんどの複雑なシステムでは(そして設定に関しては確かに)、
失敗はエッジの方で起こります。
<設定言語の統合>
このセクションではJsonnetを使用して、設定言語と設定が必要なアプリケーションを統合する方法について説明しますが、
同じ手法が他の設定言語にも適用されます。
<特定のフォーマットでの設定の生成>
設定言語は、本来は正しい形式で出力される可能性があります。
たとえば、JsonnetはJSONを出力します。これは多くのアプリケーションと互換性があります。
JSONは、JavaScript、YAML、HashiCorpの設定言語など、
JSONを拡張する言語の利用者にとっても十分です。
これがあなたの状況ならば、あなたはそれ以上の統合作業を行う必要はありません。
ネイティブにサポートされていないその他の設定形式の場合
あなたは設定言語の中で設定データを表現する方法を見つける必要があります。
通常、これは難しいことではありません。マップ、リスト、ストリング、その他のプリミティブ値などの設定値は一般的であり、すべての言語で利用可能であるためです。
このデータが設定言語で表現されたら、その言語の設定要素を使用して重複を減らすことができます(結果として、Toilも)。
必要な出力フォーマットのためにシリアライゼーション関数を書く(あるいは再利用する)必要があります。
たとえば、Jsonnet標準ライブラリには、内部のJSONのような表現からINIとXMLを出力するための関数があります。
設定データが設定言語(Bashスクリプトなど)内での表現に抵抗がある場合は、
最後の手段として基本的なストリングテンプレート手法を使用できます。
あなたはhttp://bit.ly/2La0zDeで実用的な例を見つけることができます。
<複数のアプリケーションを動かす>
設定言語から任意の既存のアプリケーションを起動できるようになると、
同じ設定から複数のアプリケーションをターゲットにできる可能性があります。
アプリケーションが異なる設定フォーマットを使用している場合は、いくつかの変換作業を実行する必要があります。
必要なフォーマットで設定を生成できるようになったら、設定コーパス全体で、簡単に統一、同期、繰り返しの削除ができます。
JSONおよびJSONベースのフォーマットが普及しているため、
異なるフォーマットを生成する必要すらないかもしれません。
(たとえば、基盤インフラストラクチャにGCP Deployment Manager、AWS Cloud Formation、またはTerraformをデプロイメントアーキテクチャを使用する場合。
また、コンテナ用のKubernetesの場合も。)
この時点で、次のことができます:
・1回だけポートを定義する単一のJsonnet評価から、
Nginx Webサーバー設定とTerraformファイアウォール設定を出力すること
・同じファイルからモニタリングダッシュボード、保存ポリシー、
およびアラート通知パイプラインを設定すること
・初期化コマンドをあるリストから別のリストに移動することで、
VM起動スクリプトとディスクイメージ構築スクリプトの間のパフォーマンスのトレードオフを管理すること
異種の設定を1か所にまとめた後は、設定を洗練して抽象化する多くの機会があります。
設定はネストすることもできます。
(たとえば、Cassandra設定は、そのベースインフラストラクチャのDeployment Manager設定内またはKubernetes ConfigMap内に埋め込むことができます。)
良い設定言語はどんな厄介な文字列の引用符でも扱うことができ、
一般にこの操作を自然で単純にします。
さまざまなアプリケーション用にさまざまなファイルを簡単に作成できるようにするために、
Jsonnetには、ファイル名をファイルの内容にマッピングする単一のJSONオブジェクト(必要に応じてフォーマット設定)を生成するconfig実行が必要なモードがあります。
文字列から文字列へのマップを出力し、後処理ステップまたはラッパースクリプトを使用してファイルを書き込むことで、この機能を他の設定言語で真似ることができます。
<既存のアプリケーションの統合:Kubernetes>
Kubernetesは、2つの理由で興味深いケーススタディを行います。
・Kubernetesで実行されているジョブは設定する必要があり、
それらの設定は複雑になる可能性があります。
・Kubernetesにはバンドルされた設定言語は付属していません
(ありがたいことに、臨時の[アドホック]言語でさえありません)。
最低限複雑なオブジェクトを持つKubernetesユーザはシンプルにYAMLを使います。
より大きなインフラストラクチャを持つユーザーは、
その規模で必要とされる抽象化機能を提供するためにJsonnetのような言語で彼らのKubernetesワークフローを拡張します。
Kubernetesは、マシンのクラスタ上でコンテナ化されたワークロードを
オーケストレーションする[編成する]ためのオープンソースシステムです。
そのAPIを使用すると、コンテナー自体、コンテナー間の通信、クラスター内外での通信、負荷分散、ストレージ、漸進的なブロールアウト、
および自動スケーリングなどの多くの重要な詳細部分を管理できます。
各設定項目は、APIエンドポイントを介して管理できるJSONオブジェクトで表されます。コマンドラインツールkubectlを使用すると、これらのオブジェクトをディスクから読み取り、それらをAPIに送信できます。
ディスク上では、JSONオブジェクトは実際にはYAMLストリームとしてエンコードされています。
3 YAMLは読みやすく、一般的に利用可能なライブラリを介して簡単にJSONに変換します。すぐに使える[独創的なout-of-the-boxの一般的な意味。]ユーザーエクスペリエンスには、Kubernetesオブジェクトを表すYAMLファイルを作成し、
それらをクラスターにデプロイするためにkubectlを実行することが含まれます。
Kubernetesを設定するためのベストプラクティスについては、
そのトピックに関するKubernetesのドキュメントを参照してください。
YAML、Kubernetes設定へのユーザーインターフェースは、
コメントのようないくつかの単純な機能を提供し、
そしてほとんどの人が好む生のJSONの簡潔な構文を持っています。
ただし、抽象化するとYAMLは不十分になります。
(アンカーのみを提供します4。これは実際にはほとんど役に立たず、
Kubernetesではサポートされていません。)
Kubernetesオブジェクトを異なるネームスペース、ラベル、その他の小さな変数で4回複製したいとします。
イミュータブルインフラストラクチャのベストプラクティスに従って、
4つすべてのバリエーションの設定を保存し、設定の他の同一の側面を複製します。
次のコードの抜粋は、1つの変数を示しています(簡潔にするために、他の3つのファイルは省略しています)。
example1.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: guestbook
tier: frontend
name: frontend
namespace: prod
spec:
externalTrafficPolicy: Cluster
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: guestbook
tier: frontend
sessionAffinity: None
type: NodePort
重要な違いが不透明なため、変数を読み、管理するのは困難です。
設定言語の統合Configuration-Induced Toilで 説明したように、
大量のYAMLファイルを管理するにはかなりの時間がかかります。
設定言語は、この作業を単純化するのに役立ちます。
最も簡単なアプローチは、
Jsonnetの実行ごとに単一のKubernetesオブジェクトを発行し、
次に結果のJSONを直接kubectlにパイプ処理することです。
これにより、JSONはYAMLのように処理されます。
あるいは、YAMLストリーム(そのようなオブジェクトのシーケンス5)または単一のkubectlリストオブジェクトを発行することも、
Jsonnetに同じ設定から複数のファイルを発行させることもできます。
詳細については、JsonnetのWebサイトを参照してください。
開発者達が一般にわかっておくべきこととして、
YAMLはJSONで表現できない(したがってJsonnetでは生成できない)設定を書くことを許可します。
YAML設定はNaNのような例外的なIEEE浮動小数点値、あるいは配列のような非文字列フィールドを持つオブジェクト、
他のオブジェクト、またはnullを含むことができます。
実際には、これらの機能はめったに使用されません、そしてKubernetesはそれらを許可しません。
それは設定がAPIに送られるときJSONエンコードされなければならないからです。
次の抜粋は、JsonnetでのKubernetesの設定例です。
// templates.libsonnet
{
MyTemplate:: {
local service = self,
tier:: error 'Needs tier',
apiVersion: 'v1',
kind: 'Service',
local selector_labels = { app: 'guestbook', tier: service.tier },
metadata: {
labels: selector_labels,
name: 'guestbook-' + service.tier,
namespace: 'default',
},
spec: {
externalTrafficPolicy: 'Cluster',
ports: [{
port: 80,
protocol: 'TCP',
targetPort: 80,
}],
selector: selector_labels,
sessionAffinity: 'None',
type: 'NodePort',
},
},
}
// example1.jsonnet
local templates = import 'templates.libsonnet';
templates.MyTemplate {
tier: 'frontend',
}
// example2.jsonnet
local templates = import 'templates.libsonnet';
templates.MyTemplate {
tier: 'backend',
metadata+: {
namespace: 'prod',
},
}
// example3.jsonnet
local templates = import 'templates.libsonnet';
templates.MyTemplate {
tier: 'frontend',
metadata+: {
namespace: 'prod',
labels+: { foo: 'bar' },
},
}
// example4.jsonnet
local templates = import 'templates.libsonnet';
templates.MyTemplate {
tier: 'backend',
}
次の点に注意してください。
・私達は抽象テンプレートを4回インスタンス化することによって4つすべての変数を表現しましたが、関数抽象化を使用することもできます。
・私達は各インスタンスに別々のJsonnetファイルを使用しましたが、それらを単一のファイルに統合することもできます。
・抽象テンプレートでは、ネームスペースはデフォルトでdefaultに設定、tierはオーバーライドされる必要があります。
・一見すると、Jsonnetはもう少し冗長ですが、テンプレートのインスタンス化の数が増えるにつれてToilが減ります。
MyTemplateの中で、「local」は変数「service」を定義し、
それはself(最も近い囲みオブジェクトへの参照)に初期化されます。
これにより、selfが再定義された、ネストされたオブジェクト内からオブジェクトを参照できます。
このtierフィールドには2つのコロンがあり(通常のJSON単一コロンではなく)、
生成されたJSONでは非表示になっています(出力されません)。
それ以外の場合、Kubernetesはtierを認識されていないフィールドとして拒否します。
隠しフィールドとなっても、オーバーライドして参照することができます。
(この場合はas service.tierとして。)
service.tierの参照によってerror構文が起動され、指定されたテキストでランタイムエラーが発生するため、
テンプレートを単独で使用することはできません。
そのエラーを回避するために、テンプレートの各インスタンスはtierフィールドを他の式でオーバーライドします。
言い換えれば、このパターンは純粋な仮想/抽象メソッドに似たものを表現します。
抽象化に関数を使用するということは、configはパラメータ化されるだけであることを意味します。
対照的に、テンプレートを使用すると、親から[継承した]任意のフィールドをオーバーライドすることができます。
14章設定設計とベストプラクティスで説明されているように、
シンプルさはデザインにとって基本的なものであるべきですが、
シンプルさから脱出する能力は重要です。
テンプレートの上書きは、通常はレベルが低すぎる[カスタマイズできなさ過ぎる]と考えられる特定の詳細を変更するための、便利な非常口を提供します。
例えば:
templates.MyTemplate {
tier: 'frontend',
spec+: {
sessionAffinity: 'ClientIP',
},
}
これは既存のテンプレートをJsonnetに変換する典型的なワークフローです:
YAMLの一種をJSONに変換する
結果のJSONをJsonnetフォーマッターを通して実行する
Jsonnet構造を手動で追加して、コードを抽象化およびインスタンス化します
(例に示したように)
例では、特定のフィールドが異なる状態を保持しながら重複を削除する方法を示しました。
設定言語を使用することは、違いがより不明確になる(例えば、文字列がわずかに異なる)、
または違いを表現するのが難しい(例えば、設定が配列の追加要素のような構造的な違い、または配列のすべての要素に適用される同じ違い)のでより説得力があります。
一般に、異なる設定間で共通点を抽象化することは、関心事の分離を促進し、
プログラミング言語のモジュール性と同じ利点があります。
抽象化機能を利用して、さまざまなユースケースに対応できます:
・単一のチームでほぼ同じ(ただしまったく同じではない)設定の複数のバージョンを作成する必要がある場合があるかもしれません。
(たとえば、さまざまな環境(prod / stage / dev / test)にわたるデプロイの管理、
さまざまなアーキテクチャでのデプロイメントの調整や、地理的に異なる条件でのキャパシティーの調整など)
・組織には、アプリケーションチームが使用する再利用可能なコンポーネント(API提供フレームワーク、
キャッシュサーバー、またはMapReduces)を維持するインフラストラクチャチームがあるかもしれません。
インフラストラクチャチームは、コンポーネントごとに、
そのコンポーネントを大規模に実行するために必要なKubernetesオブジェクトを定義するテンプレートを管理できます。
各アプリケーションチームは、そのテンプレートをインスタンス化してアプリケーションの詳細を追加できます。