大規模な環境で使用するための Deployment Manager の構造化

「Infrastructure as Code」システムが計画外に「Hello World」の例を超えて成長すると、コードが構造化されない傾向があります。計画外の構成はハードコードされています。メンテナンス性が大幅に低下します。

このドキュメントと付属のコード例を使用すると、デプロイをより効率的かつ大規模に構築できます。

さらに、命名規則と社内のベスト プラクティスをチーム全体に適用します。このドキュメントは技術的に高度な読者を対象としており、Python、Google Cloud インフラストラクチャ、Deployment Manager、および一般的な infrastructure as code を理解していることを前提としています。

始める前に

単一のコードベースの複数の環境

数多くのリソースがある大規模なデプロイの場合、標準的なベスト プラクティスでは、文字列やロジックが汎用テンプレートにハードコーディングされることを避けるために、大量の外部プロパティ(構成パラメータ)を使用する必要があります。こうしたプロパティの多くは、開発環境、テスト環境、本番環境、同様のサービスなどの環境が似ているため、部分的に複製されています。たとえば、すべての標準サービスが同様の LAMP スタック上で実行されます。こうしたベスト プラクティスに従うと、多数の構成プロパティの複製が膨大になるため、メンテナンスが困難になり、人的ミスを招く可能性があります。

次の表は、階層構成とデプロイごとの単一構成の違いを示すコードサンプルです。この表では、単一構成で共通の重複が強調表示されています。階層構成を使用することで、繰り返されるセクションを階層内の上位レベルに移動して繰り返しを回避し、人的エラーの可能性を減らす方法が示されています。

テンプレート 冗長性のない階層構成 冗長性がある単一構成

project_config.py

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP' }

なし

frontend_config.py

config = {'ServiceName': 'frontend'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'frontend' }

backend_config.py

config = {'ServiceName': 'backend'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'backend' }

db_config.py

config = {'ServiceName': 'db'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'db' }

大規模なコードベースを適切に処理するには、構成プロパティのカスケード結合によって構造化された階層レイアウトを使用します。これを行うには、1 つではなく複数のファイルを使用します。また、ヘルパー関数を使用して、組織全体でコードベースの一部を共有します。

このドキュメントに含まれているコード例 Organization_with_departments には、あらかじめ定義されているものの、構造がカスタマイズ可能なコードベース、構成を結合するヘルパー スクリプト、命名のためのヘルパー関数、さらに完全な構成例が含まれています。この活用例は、Deployment Manager のサンプル GitHub リポジトリにあります。

コードを階層的に構造化してカスケードすると、いくつかの利点が得られます。

  • 構成を複数のファイルに分割すると、プロパティの構造と可読性が向上します。また、それらを複製することも回避します。
  • 論理的な方法で値をカスケードする階層の結合を設計し、プロジェクトやコンポーネントにまたがって再利用可能な最上位の構成ファイルを作成します。
  • 各プロパティは一度だけ定義します(上書きは除く)。プロパティ名の名前空間に対処する必要はありません。
  • 適切な変数に基づいて適切な構成が読み込まれるため、テンプレートで実際の環境を特定する必要はありません。

コードベースの階層的な構造化

Deployment Manager のデプロイには、いくつかの Python ファイルとともに YAML 構成ファイルまたはスキーマ ファイルが含まれています。これらのファイルによって、デプロイのコードベースが形成されます。Python ファイルにはさまざまな目的のために使用できます。Python ファイルは、デプロイ テンプレート、汎用コードファイル(ヘルパークラス)、構成プロパティを格納するコードファイルとして使用できます。

コードベースを階層的に構造化するには、標準の構成ファイルではなく、いくつかの Python ファイルを構成ファイルとして使用します。このアプローチでは、デプロイを単一の YAML ファイルにリンクするよりも柔軟性が高くなります。

インフラストラクチャを実際のコードとして扱う

クリーンなコードの重要な原則は、Don't Repeat Yourself(DRY)です。 すべてを一度だけ定義します。このアプローチにより、コードベースがクリーンになり、確認や検証が容易になります。プロパティを変更する必要があるのは 1 か所だけであるため、人的エラーのリスクが低下します。

構成ファイルのサイズを小さくし、重複を最小限に抑えた軽量コードベースの場合は、こうしたガイドラインを使用して DRY の原則に従って構成を構造化します。

組織、部門、環境、モジュール

コードベースをクリーンに、階層的に構造化するための基本原則は、組織、部門、環境、モジュールを使用することです。 こうした原則は任意で拡張可能です。これらに従うサンプル コードベースの階層図については、構成の階層をご覧ください。

次の図では、環境内にモジュールが配置されています。構成の結合は、使用されているコンテキストに基づいて、各レベルで適切な構成ファイルを選択します。また、システムと部門も自動的に定義されます。

環境にデプロイされたモジュール

次のリストでは、数字は上書き順序を表します。

  1. 組織のプロパティ

    これは構造内の最上位レベルです。このレベルでは、命名規則で使用する organization_nameorganization_abbreviation などの構成プロパティと、すべてのチームにまたがって共有して使用するヘルパー関数を格納できます。

  2. 部門のプロパティ

    組織内に部門がある場合、構造に部門が含まれます。各部門の構成ファイルで、department_namecost_center など、他の部門で使用されていないプロパティを共有します。

  3. システム(プロジェクト)プロパティ

    各部門にシステムがあります。システムとは、明確に定義されたソフトウェア スタック(e コマース プラットフォームなど)です。これはGoogle Cloudプロジェクトではなく、サービスの機能するエコシステムです。

    システムレベルでは、チームの自律性が、それより上のレベルよりもはるかに高くなっています。ここでは、チーム全体およびシステム全体のパラメータ(たとえば、system_namedefault_instance_sizenaming_prefix)のヘルパー関数(project_name_generator()instance_name_generator()instance_label_generator() など)を定義できます。

  4. 環境プロパティ

    システムに複数の環境(DevTestProd など、任意で QA および Staging)があり、それらが互いにかなり似ていることがあります。それらが同じコードベースを使用していて、構成レベルでのみ異なることが理想的です。環境レベルでは、Prod および QA 構成の default_instance_size などのプロパティを上書きできます。

  5. モジュールのプロパティ

    システムが大規模な場合は、1 つの大きなモノリシック ブロックのままではなく、複数のモジュールに分割します。たとえば、コアのネットワーキングとセキュリティを別々のブロックに移動します。バックエンド、フロントエンド、データベースのレイヤを別々のモジュールに分けることもできます。モジュールは、適切な構成だけを追加するサードパーティによって開発されたテンプレートです。モジュール レベルでは、継承されたシステムレベルのプロパティを上書きするように設計されたプロパティを含めて、特定のモジュールにのみ関連するプロパティを定義できます。環境レベルとモジュール レベルはシステム内で並列している区分ですが、モジュールは結合プロセスで環境に従います。

  6. 環境に固有のモジュール プロパティ

    モジュールのプロパティの中には、インスタンスのサイズ、イメージ、エンドポイントなど、環境に依存するものもあります。環境に固有のモジュール プロパティは、固有のレベルにあり、以前に定義された値を上書きするカスケード結合の最後のポイントにあります。

構成を結合するヘルパークラス

config_merger クラスは、適切な構成ファイルを自動的に読み込み、その内容を 1 つの辞書に結合するヘルパークラスです。

config_merger クラスを使用するには、次の情報を提供する必要があります。

  • モジュール名。
  • グローバル コンテキスト(環境名を含む)。

ConfigContext 静的関数を呼び出すと、結合された構成辞書が返されます。

次のコードは、このクラスの使用方法を示しています。

  • module = "frontend" は、プロパティ ファイルが読み込まれるコンテキストを指定します。
  • 環境は context.properties["envName"] から自動的に選択されます。
  • グローバル構成。

    cc = config_merger.ConfigContext(context.properties, module)
    
    print cc.configs['ServiceName']
    

バックグラウンドでは、このヘルパークラスは構成の構造に合わせてすべてのレベルを正しい順序で読み込み、適切な構成値を上書きする必要があります。レベルや上書き順序を変更するには、構成の結合クラスを変更します。

現実の使用においては通常、このクラスを操作する必要はありません。通常、テンプレートと適切な構成ファイルを編集し、すべての構成で出力辞書を使用します。

サンプルのコードベースには、次の 3 つのハードコードされた構成ファイルが含まれています。

  • org_config.py
  • department_config.py
  • system_config.py

リポジトリの開始時に、組織や部門の構成ファイルをシンボリック リンクとして作成できます。こうしたファイルは、論理的にはプロジェクト チームのコードベースの一部ではなく、組織や部門の全体で共有されるため、別々のコード リポジトリに保存できます。

構成の結合では、構造の残りのレベルと一致するファイルも検索されます。

  • envs/[environment name].py
  • [environment name]/[module name].py
  • modules/[module name].py

構成ファイル

Deployment Manager は 1 つの構成ファイルを使用します。この構成ファイルは、特定のデプロイ用の単一のファイルです。デプロイ間で共有することはできません。

config-merger クラスを使用すると、使用していないため、構成プロパティはこの構成ファイルから完全に切り離されます。代わりに、Python ファイルのコレクションを使用すると、デプロイの柔軟性が向上します。こうしたファイルは、複数のデプロイで共有することもできます。

Python ファイルには変数が含まれているため、構造化された分散した方法で構成を格納できます。最善のアプローチは、合意された構造で辞書を使用することです。構成の結合では、結合チェーン内のすべてのファイルで configs という辞書を検索します。これらの個々の configs が 1 つに統合されます。

結合中に、同じパスと名前のプロパティが辞書に複数回表示されると、構成の結合によって、そのプロパティが上書きされます。 コンテキストに固有の値によってデフォルト値が上書きされる場合など、この動作が役立つことがあります。ただし、プロパティの上書きを回避したい場合も数多くあります。プロパティの上書きを回避するには、別の名前空間を追加して一意にします。次の例では、構成ディクショナリにサブディクショナリを作成する追加のレベルを作成して、名前空間を追加します。

config = {
    'Zip_code': '1234'
    'Count': '3'
    'project_module': {
        'admin': 'Joe',
    }
}

config = {
    'Zip_code': '5555'
    'Count': '5'
    'project_module_prod': {
        'admin': 'Steve',
    }
}

ヘルパークラスと命名規則

命名規則は、Deployment Manager インフラストラクチャを管理する最良の方法です。my projecttest instance などのあいまいな名前や汎用的な名前を使用しないでください。

次の例は、組織全体でのインスタンスの命名規則です。

def getInstanceName(self, name):
  return '-'.join(self.configs['Org_level_configs']['Org_Short_Name'],
                  self.configs['Department_level_configs']['Department_Short_Name'],
                  self.configs['System_short_name'],
                  name,
                  self.configs["envName"])

ヘルパー関数を指定することで、合意された規則に基づいて各インスタンスに簡単に名前を付けることができます。また、インスタンス名がこの関数以外から付けられることはないため、コードの確認が容易になります。この関数では、上位レベルの構成から自動的に名前を取得します。このアプローチは、不必要な入力を避けるのに役立ちます。

これらの命名規則は、ほとんどの Google Cloud リソースやラベルに適用できます。より複雑な関数により、デフォルト ラベルのセットを生成することもできます。

サンプル コードベースのフォルダ構造

サンプル コードベースのフォルダ構造は柔軟でカスタマイズ可能です。ただし、構成の結合と Deployment Manager のスキーマ ファイルに部分的にハードコードされています。つまり、変更を加えた場合、その変更を構成の結合とスキーマ ファイルに反映させる必要があります。

├── global
│   ├── configs
│   └── helper
└── systems
    └── my_ecom_system
        ├── configs
        │   ├── dev
        │   ├── envs
        │   ├── modules
        │   ├── prod
        │   └── test
        ├── helper
        └── templates
    

グローバル フォルダには、複数のプロジェクト チーム間で共有されるファイルが含まれています。説明を簡単にするために、構成フォルダには、組織の構成とすべての部門の構成ファイルが含まれています。この例では、部門ごとのヘルパークラスはありません。ヘルパークラスは、組織レベルまたはシステムレベルで追加できます。

グローバル フォルダは別の Git リポジトリに保存できます。このフォルダ内のファイルを個々のシステムから参照できます。シンボリック リンクを使用することもできますが、特定のオペレーティング システムで混乱や不具合が発生する可能性があります。

├── configs
│   ├── Department_Data_config.py
│   ├── Department_Finance_config.py
│   ├── Department_RandD_config.py
│   └── org_config.py
└── helper
    ├── config_merger.py
    └── naming_helper.py

システム フォルダには、1 つ以上のシステムが含まれています。システムは互いに分離されており、構成を共有していません。

├── configs
│   ├── dev
│   ├── envs
│   ├── modules
│   ├── prod
│   └── test
├── helper
└── templates

構成フォルダには、このシステムに固有のすべての構成ファイルが含まれ、シンボリック リンクによってグローバル構成も参照されます。

├── department_config.py -> ../../../global/configs/Department_Data_config.py
├── org_config.py -> ../../../global/configs/org_config.py
├── system_config.py
├── dev
│   ├── frontend.py
│   └── project.py
├── prod
│   ├── frontend.py
│   └── project.py
├── test
│   ├── frontend.py
│   └── project.py
├── envs
│   ├── dev.py
│   ├── prod.py
│   └── test.py
└── modules
    ├── frontend.py
    └── project.py

Org_config.py:

config = {
  'Org_level_configs': {
    'Org_Name': 'Sample Inc.',
    'Org_Short_Name': 'sampl',
    'HQ_Address': {
      'City': 'London',
      'Country': 'UK'
    }
  }
}

ヘルパー フォルダでは、ヘルパークラスを追加してグローバル クラスを参照できます。

├── config_merger.py -> ../../../global/helper/config_merger.py
└── naming_helper.py -> ../../../global/helper/naming_helper.py

テンプレート フォルダでは、Deployment Manager テンプレートの保存や参照を行えます。シンボリック リンクもここで動作します。

├── project_creation -> ../../../../../../examples/v2/project_creation
└── simple_frontend.py

サンプル コードベースの使用

Infrastructure as Code の基盤としてこの階層的な手法を適用する最善の方法は、サンプル コードベースのクローンを作成し、hierarchical_configuration フォルダのコンテンツをコピーすることです。

  1. サンプル リポジトリを確認します。

    git clone https://github.com/GoogleCloudPlatform/deploymentmanager-samples.git
    cd deploymentmanager-samples/community/hierarchical_configuration/Organization_with_departments/systems/my_ecom_system
    gcloud config set deployment_manager/glob_imports True
    
  2. システムを設定するには、次のシンボリック リンクを使用して、ローカル コンテキストでグローバル ファイルを参照します。

    ln -sf ../../../global/helper/config_merger.py helper/config_merger.py
    ln -sf ../../../global/helper/naming_helper.py helper/naming_helper.py
    ln -sf ../../../global/configs/org_config.py configs/org_config.py
    
  3. グローバル リストから適切な部門を選択します。

    ln -sf ../../../global/configs/Department_Data_config.py configs/department_config.py
    
  4. 適切な環境コンテキストを設定するには、--properties フラグを使用して envName プロパティを指定します。このプロパティを使用すると、同じコマンドで異なる環境をターゲットとする同じコードを実行できます。[MY-PROJECT-ID] は Google Cloud プロジェクトのプロジェクト ID です。

    [MY-PROJECT-ID]
    gcloud deployment-manager deployments create hierarchy-org-example-dev
    --template env_demo_project.py --properties=envName:dev
    gcloud deployment-manager deployments create hierarchy-org-example-test
    --template env_demo_project.py --properties=envName:test
    gcloud deployment-manager deployments create hierarchy-org-example-prod
    --template env_demo_project.py --properties=envName:prod
    

ベスト プラクティス

以下は、コードを階層的に構成するためのベスト プラクティスを示しています。

スキーマ ファイル

スキーマ ファイルでは、デプロイ中に使用する各ファイルを一覧表示するのは Deployment Manager の要件です。フォルダ全体を追加すると、コードが短く、汎用的になります。

  • ヘルパークラス:
- path: helper/*.py
  • 構成ファイル:
- path: configs/*.py
- path: configs/*/*.py
  • 一括(glob スタイル)インポート
gcloud config set deployment_manager/glob_imports True

複数のデプロイ

ネットワーク、ファイアウォール、バックエンド、フロントエンドなどのさまざまなモジュールがあっても、システムに同じ構成のセットを使用する複数のデプロイを含めることがベスト プラクティスです。こうしたデプロイの出力には、別のデプロイからアクセスする必要があります。準備ができたら、デプロイの出力のクエリを行い、構成フォルダに保存できます。こうした構成ファイルは、結合プロセス中に追加できます。

シンボリック リンクは gcloud deployment-manager コマンドでサポートされ、リンクされたファイルは適切に読み込まれます。ただし、シンボリック リンクはすべての OS でサポートされるわけではありません。

構成の階層

次の図に、さまざまなレベルとその関係の概要を示します。各長方形はプロパティ ファイルを表し、ファイル名は赤で示されています。

さまざまなレベルとその関係が示されている構成の階層。

状況に応じた結合順序

構成の結合では、各ファイルが使用されるコンテキストに基づいて、各レベルで適切な構成ファイルを選択します。コンテキストは、環境でデプロイするモジュールです。このコンテキストで、システムと部門が自動的に定義されます。

次の図では、数字は階層内の上書き順序を表します。

上書き順序の図

次のステップ