L'équipe DevOps de Pony.ai explique comment elle a adopté Terraform pour automatiser entièrement son infrastructure cloud. Cet article détaille le processus de sélection technologique, les décisions architecturales et les solutions développées pour gérer la réutilisation du code ainsi que les déploiements multi-environnements.
Contexte
Fondée en 2016, Pony.ai est une entreprise mondiale spécialisée dans la technologie des véhicules autonomes, avec des centres de R&D dans la Silicon Valley, à Guangzhou, Beijing, Shanghai et Shenzhen. L'entreprise détient des permis de test et d'exploitation pour la conduite autonome dans plusieurs régions des États-Unis et de Chine, et collabore avec des constructeurs automobiles tels que Toyota, Hyundai, FAW Group et GAC Group.
Les opérations de Pony.ai en Chine s'appuient sur Alibaba Cloud pour héberger des services critiques, notamment les plateformes d'étiquetage des données, Robotaxi et Robotruck. Ces services dépendent d'Elastic Compute Service (ECS), d'ApsaraDB RDS (RDS), de Server Load Balancer (SLB), de Bastionhost et de Security Center, ce qui rend la gestion de l'infrastructure de plus en plus complexe pour l'équipe DevOps.
Pour répondre à ces défis, l'équipe a défini trois objectifs principaux :
Déploiements vérifiables : chaque étape d'une opération, de la collecte des besoins et de la conception architecturale au codage et au déploiement, doit être précise et traçable.
Déploiements versionnés : un historique complet et auditable de toutes les modifications de l'infrastructure, avec la possibilité de revenir à une version spécifique.
Déploiements cohérents sur plusieurs environnements : aucune divergence entre les environnements de développement, de préproduction et de production susceptible de provoquer des pannes spécifiques à un environnement.
Sélection technologique
L'équipe a évalué trois approches courantes pour la gestion des ressources de cloud public :
Console du fournisseur cloud : gestion directe via l'interface graphique.
Système de gestion personnalisé : développement ou achat d'un système appelant les API du fournisseur cloud.
Frameworks Infrastructure as Code (IaC) : utilisation de code pour définir et gérer l'infrastructure.

L'IaC constitue la norme établie pour l'automatisation de l'infrastructure et le framework le plus utilisé pour la gestion multi-cloud. Couplé à Git, l'IaC répond directement aux objectifs de traçabilité et de gestion des versions : chaque déploiement et chaque modification sont gérés sous forme de code, et les retours arrière sont aussi simples qu'un retour à une branche Git précédente.
Dans l'écosystème IaC, Terraform est un outil open source de premier plan, éprouvé dans les environnements de production d'entreprise. En adoptant Terraform, l'équipe DevOps a pu se concentrer sur l'écriture de la logique métier de son infrastructure plutôt que de construire un système d'orchestration personnalisé à partir de zéro.
Compte tenu de la stratégie multi-cloud et de l'architecture hybride de Pony.ai, la standardisation, la facilité d'utilisation et la communauté active de Terraform en ont fait le choix évident.
Architecture et implémentation
L'équipe a mis en œuvre une solution IaC basée sur Terraform avec un flux de travail centré sur Git, comme illustré ci-dessous :

Pour les fichiers de configuration, l'équipe a choisi JSON plutôt que HashiCorp Configuration Language (HCL) afin de rester cohérente avec ses applications existantes basées sur JSON et de simplifier la revue de code.
Le code est organisé par service métier. Si un service nécessite un SLB, des certificats et des instances ECS, toutes ces ressources sont définies dans un seul fichier Terraform dédié à ce service. Voici un exemple simplifié :
{
"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"
}
}
}
}
Défis métier
À mesure que la base de code Terraform s'est étoffée, deux problèmes sont apparus :
Pour des ressources telles que les instances ECS, les ingénieurs ne se souciaient que d'un petit ensemble de paramètres —
instance_type,instance_name,availability_zone— mais devaient définir la configuration complète à chaque fois.Le déploiement du même service sur différents environnements (développement, préproduction, production) impliquait des configurations quasi identiques, avec seulement quelques différences mineures, comme l'utilisation d'un SLB
slb.s2.mediumen production contreslb.s1.smallen test. La copie et la réécriture du code pour chaque environnement nuisaient à la lisibilité et à la maintenabilité.
Solutions
Pour résoudre ces problèmes, l'équipe a introduit Jsonnet, un langage de modélisation de données open source, afin de générer les fichiers JSON Terraform. Jsonnet permet d'abstraire le code répétitif dans une bibliothèque réutilisable de fonctions utilitaires. Par exemple, l'équipe a créé une fonction generateEcs qui encapsule tous les paramètres ECS par défaut :
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,
},
}
Les ingénieurs appellent cette fonction pour générer la configuration complète, réduisant ainsi le code nécessaire au provisionnement de plusieurs instances à une boucle concise :
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
},
Toute modification de paramètre s'effectue dans la fonction utilitaire partagée plutôt que dans chaque définition de ressource individuelle.
Configuration multi-environnement
Pour les services déployés sur plusieurs environnements où seuls quelques paramètres diffèrent, l'équipe définit un modèle de base. La configuration de chaque environnement importe ce modèle de base et ne remplace que les champs qui changent.
Cela aboutit à la structure de répertoire suivante par service :
generated/main.tf.json est le fichier JSON exécuté par Terraform. Il est généré par Jsonnet à partir de main.tf.json.jsonnet. Le fichier main.tf.json.jsonnet.output contient les sorties produites après l'application de la configuration par 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.libsonnet
L'environnement de production importe le modèle de base et définit sa propre spécification SLB :
local base = import "../../ponyai_business_1_base.libsonnet";
base {
name: "ponyai_business_1_prod",
environment: "prod",
region: "alicloud_region",
slb_specification: "slb.s2.medium"
}
L'environnement de développement importe le même modèle de base et ne remplace que le nom et la spécification :
local base = import "../../ponyai_business_1_base.libsonnet";
base {
name: "ponyai_business_1_dev",
environment: "dev",
region: "alicloud_region",
slb_specification: "slb.s1.small"
}
Références inter-stacks
Jsonnet résout également le problème des dépendances entre les composants d'infrastructure. La création d'une instance ECS nécessite un ID de Virtual Private Cloud (VPC), mais le VPC est généralement géré dans un fichier Terraform distinct. Au lieu de coder en dur l'ID VPC généré, le flux de travail de Pony.ai le lit depuis le fichier de sortie de la stack VPC.
Un fichier main.tf.json crée le VPC et écrit son ID dans main.tf.json.jsonnet.output dans son propre répertoire :
├── 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
Le fichier main.tf.json.jsonnet.output résultant ressemble à ceci :
{
"vpc_id": {
"sensitive": false,
"type": "string",
"value": "vpc_id_for_ponyai"
},
"vswitch-id": {
"sensitive": false,
"type": "string",
"value": "vswitch_public_id_for_ponyai"
}
}
Tout service ayant besoin de ces valeurs importe directement le fichier de sortie :
{
"ali-cloud-region": {
prod: import "./ali-cloud-region/prod/main.tf.json.jsonnet.output",
}
}
Cette abstraction en couches permet à l'équipe DevOps de Pony.ai de tirer pleinement parti de l'écosystème Terraform tout en maintenant des dépendances inter-services propres et explicites.
Résultats métier
L'adoption de l'IaC avec Terraform et Git a apporté des améliorations concrètes tout au long du cycle de vie de l'infrastructure. Tous les paramètres d'infrastructure sont explicitement définis dans le code : lorsqu'un service nécessite deux instances ECS avec un dimensionnement et des configurations de disque spécifiques, chaque détail figure dans un seul fichier.
Avant chaque déploiement, l'équipe examine la pull request (PR) complète dans Git et discute des modifications. Cette étape de revue crée également un historique de chaque changement d'infrastructure, assurant une traçabilité complète.
Étant donné que les modifications sont vérifiables au niveau des paramètres, l'équipe peut confirmer que le déploiement final correspond à la conception initiale. Les divergences détectées lors de la phase de codage Terraform peuvent être corrigées avant d'atteindre la production. Les développeurs doivent également effectuer des tests automatiques avant d'ouvrir une PR, évitant ainsi plusieurs cycles de revue sur du code défectueux.
Cette transformation a produit quatre résultats clés :
Plus rapide : le provisionnement de l'infrastructure n'est plus une séquence d'opérations manuelles répétitives dans la console. Le cycle de production plus court permet à l'entreprise de répondre plus rapidement aux décisions commerciales et aux opportunités du marché.
Plus contrôlable : chaque modification est versionnée et auditable, faisant passer l'organisation des opérations manuelles via la console à un système de gestion traçable et fiable.
Plus efficace : la collaboration entre les équipes, y compris celles distribuées à l'international, s'est considérablement améliorée, réduisant les retards dus aux différences de fuseaux horaires et aux styles de travail variés.
Plus sécurisé : les erreurs humaines en production sont beaucoup moins probables. La combinaison de processus automatisés pilotés par le code avec des workflows d'approbation spécifiques à chaque environnement offre à Pony.ai une protection robuste pour ses opérations commerciales.
Conclusion
Modernisation du modèle de gestion
La philosophie IaC sous-tend désormais tout le développement de l'infrastructure chez Pony.ai. L'équipe a abstrait un large éventail de composants Alibaba Cloud — notamment ECS, VPC, Object Storage Service (OSS), Public DNS, Resource Access Management (RAM), Simple Log Service et la gestion des utilisateurs — en fonctions internes réutilisables.
Modernisation du modèle métier
Lorsqu'une équipe a besoin d'une ressource cloud, elle appelle l'une de ces fonctions préconstruites et validées. L'équipe DevOps se concentre sur la maintenance et l'amélioration de la bibliothèque interne, ce qui entraîne une augmentation soutenue de l'efficacité opérationnelle dans toute l'organisation.
Modernisation du modèle opérationnel
Aujourd'hui, plus de 20 unités commerciales chez Pony.ai sont déployées et gérées à 100 % via ce flux de travail IaC. Chaque composant dispose d'un historique clair et versionné ; les modifications passent par une revue rigoureuse ; et l'équipe peut rejeter les déploiements non conformes avant qu'ils n'atteignent la production. Le résultat est un environnement en ligne propre, fiable et évolutif.
À propos de l'auteur
L'équipe DevOps de Pony.ai
Cet article a été rédigé par un auteur externe, et tous les droits d'auteur lui appartiennent. Alibaba Cloud n'assume aucune responsabilité quant au contenu.