Pony.ai の DevOps チームがまとめたこの記事では、Terraform を使用してクラウドインフラストラクチャを完全に自動化するための戦略、意思決定、実装プロセスについて詳しく説明します。
背景
2016 年に設立された Pony.ai は、シリコンバレー、広州、北京、上海、深センに R&D センターを置く、グローバルな自動運転技術企業です。同社は、米国と中国の複数の拠点で自動運転のテストおよび運用許可を取得しており、トヨタ、ヒュンダイ、FAW Group、GAC Group などの大手自動車メーカーと協力しています。Pony.ai の中国事業は、データラベリング、Robotaxi、Robotruck プラットフォームなどの重要なビジネスサービスをホストするために Alibaba Cloud に大きく依存しています。これらのサービスは、Elastic Compute Service (ECS)、ApsaraDB RDS (RDS)、Server Load Balancer (SLB)、Bastionhost、Security Center など、さまざまなクラウドプロダクトを利用しています。これらのコンポーネントを管理する複雑さは、DevOps チームにとって大きな課題でした。この課題に対処するため、チームは 3 つの主要な目標を定義しました。
レビュー可能なデプロイメント: 要件収集、アーキテクチャ設計からコーディング、デプロイメントに至るまで、運用活動のすべての段階が正確であり、会社の基準に準拠していることを保証します。
バージョン管理されたデプロイメント: すべてのインフラストラクチャ変更の完全で追跡可能な履歴を維持し、緊急時に特定のバージョンに迅速にロールバックしてビジネスへの影響を最小限に抑えることを可能にします。
一貫したマルチ環境デプロイメント: 異なる環境 (例: 開発、ステージング、本番) 間の不一致をなくし、環境の不整合による障害を防ぎます。
技術選定
チームは、パブリッククラウドリソースを管理するための 3 つの主流なアプローチを評価しました。
クラウドプロバイダーのコンソール: GUI を介した直接管理。
カスタム管理システム: クラウドプロバイダーの API と対話するシステムを開発または購入する。
Infrastructure as Code (IaC) フレームワーク: コードを使用してインフラストラクチャを定義および管理する。

IaC は、グローバルな技術コミュニティにおけるインフラストラクチャ自動化の確立された標準となっており、マルチクラウド管理で最も広く使用されているフレームワークです。国際的な経験を活かし、Pony.ai は IaC アプローチの利点を認識しました。バージョン管理のために Git と組み合わせることで、IaC は追跡可能性とバージョン管理という目標に対する明確なソリューションを提供しました。すべてのデプロイメントと変更はコードとして管理でき、ロールバックは以前の Git ブランチに戻すだけで実行できます。
IaC エコシステムの中で、Terraform はエンタープライズの本番環境で実績のある、最高のオープンソースツールとして際立っています。Terraform を採用することで、DevOps チームは、カスタムのオーケストレーションツールをゼロから構築するのではなく、インフラストラクチャのビジネスロジックの記述に集中できました。
このアプローチは、さまざまなクラウド API 上に独自のソリューションを開発するよりも、はるかに複雑さが少なく、よりアジャイルであることが証明されました。
最終的に、Pony.ai のマルチクラウド戦略とハイブリッドクラウドアーキテクチャを考慮して、チームは標準化、使いやすさ、活発なコミュニティサポートを理由に Terraform を選択しました。
アーキテクチャと実装
Pony.ai チームは、次の図に示すように、Git 中心のワークフローを持つ Terraform ベースの IaC ソリューションを実装しました。

構成ファイルについては、チームは既存の JSON ベースのアプリケーションとの一貫性を維持し、コードレビュープロセスを簡素化するために、HashiCorp Configuration Language (HCL) よりも JSON を選択しました。
コードはビジネスサービスごとに整理されています。たとえば、サービスが SLB、証明書、ECS インスタンスを必要とする場合、これらのリソースはすべて、そのサービス用の単一の Terraform ファイル内で定義されます。以下は、そのようなファイルの簡略化された例です。
{
"output": {
"ecs_instance_1-private-ip": {
"value": "${alicloud_instance.ecs_instance_1.private_ip}"
},
"ecs_instance_2-private-ip": {
"value": "${alicloud_instance.ecs_instance_2.private_ip}"
},
"ponyai_business_1-slb-address": {
"value": "${alicloud_slb.ponyai_business_1-slb.address}"
}
},
"provider": {
"alicloud": {
"region": "alicloud_region"
}
},
"resource": {
"alicloud_instance": {
"ecs_instance_1": {
"availability_zone": "availability_zone_1",
"data_disks": [
{
"category": "cloud_essd",
"name": "data_volume",
"size": "xx"
}
],
"host_name": "ecs_instance_1",
"image_id": "image_id_1",
"instance_name": "ecs_instance_1",
"instance_type": "ecs_instance_type",
"internet_charge_type": "PayByTraffic",
"internet_max_bandwidth_out": 10,
"key_name": "key_name_1",
"security_groups": [
"security_groups_1"
],
"system_disk_category": "cloud_essd",
"system_disk_size": "xx",
"tags": {
"host_name": "ecs_instance_1"
},
"vswitch_id": "vswitch_id_1"
},
"ecs_instance_2": {
"availability_zone": "availability_zone_2",
"data_disks": [
{
"category": "cloud_essd",
"name": "data_volume",
"size": "xx"
}
],
"host_name": "availability_zone_2",
"image_id": "image_id_1",
"instance_name": "availability_zone_2",
"instance_type": "ecs_instance_type",
"internet_charge_type": "PayByTraffic",
"internet_max_bandwidth_out": 10,
"key_name": "key_name_1",
"security_groups": [
"security_groups_1"
],
"system_disk_category": "cloud_essd",
"system_disk_size": "xx",
"tags": {
"host_name": "availability_zone_2"
},
"vswitch_id": "vswitch_id_2"
}
},
"alicloud_slb": {
"slb-1": {
"address_type": "internet",
"internet_charge_type": "PayByTraffic",
"name": "slb_name",
"specification": "slb_specification"
}
},
"alicloud_slb_listener": {
"slb-listener-1": {
"backend_port": "xx",
"bandwidth": -1,
"frontend_port": "xx",
"health_check": "on",
"health_check_connect_port": "xx",
"health_check_domain": "domain_name",
"health_check_type": "check_type",
"health_check_uri": "uri_1",
"load_balancer_id": "${alicloud_slb.slb-1.id}",
"protocol": "protocol_1",
"scheduler": "scheduler_1",
"server_certificate_id": "${alicloud_slb_server_certificate.slb-certificate-1.id}",
"server_group_id": "${alicloud_slb_server_group.slb-server-group-1.id}"
}
},
"alicloud_slb_server_certificate": {
"slb-certificate-1": {
"alicloud_certificate_id": "xx",
"alicloud_certificate_name": "xx",
"name": "certificate_1"
}
},
"alicloud_slb_server_group": {
"slb-server-group-1": {
"load_balancer_id": "${alicloud_slb.slb-1.id}",
"name": "slb-server-group",
"servers": {
"port": "xx",
"server_ids": [
"${alicloud_instance.ecs_instance_1.id}",
"${alicloud_instance.ecs_instance_2.id}"
]
}
}
}
},
"terraform": {
"backend": {
"s3": {
"bucket": "bucket_name",
"dynamodb_table": "table",
"key": "key_1",
"profile": "profile_1",
"region": "region_1"
}
},
"required_providers": {
"alicloud": {
"source": "aliyun/alicloud",
"version": "xx"
}
}
}
}ビジネス上の課題
チームが Terraform コードをより多く書くにつれて、コードの再利用性と可読性に関連する課題に直面しました。主な懸念事項は次のとおりです。
ECS インスタンスなどの特定のリソースについて、チームは主に instance_type、instance_name、availability_zone などのパラメーターのサブセットに関心がありました。
異なる環境に同じサービスをデプロイする場合、本番環境で slb.s2.medium の SLB インスタンスを使用し、テスト環境で slb.s1.small を使用するなど、わずかなパラメーターの変更のみで同一の構成になることがよくありました。各環境のコードを書き直すと、可読性と保守性が低下しました。
ソリューション
これを解決するために、チームはオープンソースのデータテンプレート言語である Jsonnet を導入して、Terraform JSON ファイルを生成しました。これにより、反復的な定型文を抽象化し、ユーティリティ関数のライブラリを構築することで再利用可能なモジュールを作成できました。たとえば、generateEcs 関数を作成しました。
generateEcs(instance_name,
availability_zone,
vswitch_id,
security_groups,
instance_type,
host_name,
data_volume_size=null,
system_disk_size=null,
internet_charge_type="PayByTraffic",
image_id="ubuntu_18_04_x64_20G_alibase_20200914.vhd",
key_name="bootstrap-bot",
system_disk_category="cloud_essd",
internet_max_bandwidth_out=10,
data_disk_category="cloud_essd"): {
instance_name: instance_name,
availability_zone: availability_zone,
vswitch_id: vswitch_id,
security_groups: security_groups,
instance_type: instance_type,
internet_charge_type: internet_charge_type,
image_id: image_id,
system_disk_category: system_disk_category,
[if system_disk_size != null then "system_disk_size"]:
system_disk_size,
key_name: key_name,
internet_max_bandwidth_out: internet_max_bandwidth_out,
host_name: host_name,
data_disks: if data_volume_size != null then [
{
name: "data_volume",
size: data_volume_size,
category: data_disk_category,
},
] else [],
tags: {
host_name: host_name,
},
}この抽象化により、エンジニアは関数を呼び出すだけで必要な構成を生成でき、複数のインスタンスをプロビジョニングするために必要なコードが劇的に簡素化されます。
alicloud_instance: {
[host_config.host_name]:
ecsUtils.generateEcs(
instance_name=host_config.host_name,
availability_zone=host_config.az,
security_groups=$.ecs_security_groups,
host_name=host_config.host_name,
instance_type=$.ecs_instance_type,
vswitch_id=vpc_output["vswitch-public-" + host_config.az].value,
data_volume_size=$.ecs_data_volume_size,
system_disk_size=$.ecs_system_disk_size
)
for host_config in host_configs
},調整は対応するユーティリティ関数で直接行うことができ、各インフラストラクチャコンポーネントを個別に変更する必要がありません。
いくつかのコンポーネントパラメーターのみが異なるさまざまな環境 (例: 本番、ステージング、テスト) を処理するために、チームはベーステンプレートを定義します。各環境の構成は、このベーステンプレートをインポートし、必要なパラメーターをオーバーライドするだけです。
このアプローチにより、Pony.ai は次のディレクトリ構造を使用して、複数の環境にサービスをデプロイできます。
「generated/main.tf.json」は Terraform が実行するファイルで、「main.tf.json.jsonnet」から Jsonnet ツールによって生成された JSON ファイルです。「main.tf.json.jsonnet.output」ファイルには、Terraform が構成を適用した後に生成された出力が含まれます。
├── alicloud-region
│ ├── dev
│ │ ├── generated
│ │ │ └── main.tf.json
│ │ ├── main.tf.json.jsonnet
│ │ └── main.tf.json.jsonnet.output
│ ├── prod
│ │ ├── generated
│ │ │ └── main.tf.json
│ │ ├── main.tf.json.jsonnet
│ │ └── main.tf.json.jsonnet.output
│ └── staging
│ ├── generated
│ │ └── main.tf.json
│ ├── main.tf.json.jsonnet
│ └── main.tf.json.jsonnet.output
└── ponyai_business_1_base.libsonnetSLB の仕様を例にとると、本番環境とテスト環境は、ベーステンプレートをインポートして異なる値を提供するだけです。本番環境の場合:
local base = import "../../ponyai_business_1_base.libsonnet";
base {
name: "ponyai_business_1_prod",
environment: "prod",
region: "alicloud_region",
slb_specification: "slb.s2.medium"
}テスト環境では、仕様と名前に関連するフィールドのみが変更されます。
local base = import "../../ponyai_business_1_base.libsonnet";
base {
name: "ponyai_business_1_dev",
environment: "dev",
region: "alicloud_region",
slb_specification: "slb.s1.small"
}このメソッドは、コードの可読性と再利用性を大幅に向上させます。さらに、Jsonnet はインフラストラクチャコンポーネント間の依存関係の問題もエレガントに解決します。たとえば、Alibaba Cloud 上で ECS インスタンスを作成するには Virtual Private Cloud (VPC) ID が必要ですが、VPC は多くの場合、別の Terraform ファイルで定義および管理されます。したがって、ECS 構成は、VPC ファイルによって生成された VPC ID を参照する必要があります。Pony.ai では、「main.tf.json」ファイルが VPC を作成し、その ID は独自のディレクトリにある出力ファイル (「main.tf.json.jsonnet.output」) に書き込まれます。
├── ali-cloud-region
│ ├── dev
│ │ ├── generated
│ │ │ └── main.tf.json
│ │ ├── main.tf.json.jsonnet
│ │ └── main.tf.json.jsonnet.output
│ └── prod
│ ├── generated
│ │ └── main.tf.json
│ ├── main.tf.json.jsonnet
│ └── main.tf.json.jsonnet.output結果として得られる「main.tf.json.jsonnet.output」ファイルは次のようになります。
{
"vpc_id": {
"sensitive": false,
"type": "string",
"value": "vpc_id_for_ponyai"
},
"vswitch-id": {
"sensitive": false,
"type": "string",
"value": "vswitch_public_id_for_ponyai"
}
}これらの値を必要とする他のサービスは、単純なインポート文を使用して簡単に参照でき、生成された値をコードベースに直接ハードコーディングすることを回避できます。
{
"ali-cloud-region": {
prod: import "./ali-cloud-region/prod/main.tf.json.jsonnet.output",
}
}この階層化された技術的抽象化を通じて、Pony.ai の DevOps チームは Terraform エコシステムの力を活用しながら、サービス呼び出しの複雑さを解決し、全体的な運用効率を大幅に向上させました。
ビジネス上の成果
Terraform と Git を使用した IaC 手法を採用することで、大きなメリットがもたらされました。すべてのインフラストラクチャパラメーターがコードで明示的に定義されるようになりました。サービスが定義されたディスク構成を持つ特定のサイズの ECS インスタンスを 2 つ必要とする場合、それはすべてファイルにキャプチャされます。
このコードベースのアプローチにより、すべての変更が標準の Git プルリクエスト (PR) プロセスを通じてレビュー可能になり、デプロイメント前に十分な議論と検証が可能になります。
このプロセスにより、最終的なデプロイメントが初期設計と完全に一致することが保証されます。逸脱はコーディング段階で検出され、タイムリーな調整が促されます。PR 提出前の必須の自己テストにより、レビューサイクルがさらに短縮されます。

この変革による主要なビジネス上のメリットは、4 つの分野に要約できます。
より速く: インフラストラクチャのプロビジョニングは、もはや反復的な手動のコンソール操作の連続ではありません。大幅に短縮された生産サイクルにより、企業はビジネス上の意思決定や市場機会により迅速に対応できます。
より制御可能に: インフラストラクチャがコードとして定義されているため、すべての変更がバージョン管理され、監査可能です。これにより、組織は手動のコンソール操作から、より洗練され、信頼性が高く、追跡可能な管理システムへと昇格します。
より効率的に: 国際チームを含む複数のチーム間のコラボレーションが劇的に改善され、タイムゾーンの違いやさまざまなワークスタイルによる遅延が緩和されました。
より安全に: 人為的ミスによる本番環境での事故のリスクが大幅に軽減されます。自動化されたコード駆動のプロセスと環境固有の承認ワークフローを組み合わせることで、Pony.ai はビジネス運用を保護する堅牢なシステムを構築しました。
まとめ
管理モデルのアップグレード
IaC の哲学は、現在 Pony.ai のすべてのインフラストラクチャ開発の基盤となっています。同社は、ECS、VPC、Object Storage Service (OSS)、Public DNS、Resource Access Management (RAM)、Simple Log Service、ユーザーなど、多数の Alibaba Cloud コンポーネントを内部関数にうまく抽象化しました。
ビジネスモデルのアップグレード
チームがクラウドリソースを必要とするときは、これらの事前に構築され、検証された関数を使用するだけです。これにより、DevOps チームは、この内部ライブラリの維持と強化に集中でき、運用効率が大幅に向上します。
運用モデルのアップグレード
現在、Pony.ai の 20 以上の業務部門が、この IaC ワークフローを介して 100% デプロイおよび管理されています。これにより、すべてのコンポーネントの明確でバージョン管理された履歴が提供され、厳格なレビューが容易になり、チームは不適切なデプロイメントを拒否できるようになります。その結果、クリーンで信頼性が高く、スケーラブルなオンライン環境が実現します。
作成者について
Pony.ai DevOps チーム
この記事は外部の作成者によって寄稿されたものであり、すべての著作権は作成者に帰属します。Alibaba Cloud は内容について一切の責任を負いません。