A equipe de DevOps da Pony.ai compartilha como adotou o Terraform para automatizar totalmente sua infraestrutura em nuvem. O relato abrange o processo de seleção de tecnologia, as decisões arquiteturais e as soluções criadas para lidar com a reutilização de código e implantações em múltiplos ambientes.
Contexto
Fundada em 2016, a Pony.ai é uma empresa global de tecnologia para veículos autônomos com centros de P&D no Vale do Silício, Guangzhou, Pequim, Xangai e Shenzhen. A empresa possui licenças de teste e operação de direção autônoma em vários locais nos EUA e na China, além de trabalhar com montadoras como Toyota, Hyundai, FAW Group e GAC Group.
As operações da Pony.ai na China rodam na Alibaba Cloud e hospedam serviços críticos, como plataformas de rotulagem de dados, Robotaxi e Robotruck. Esses serviços dependem do Elastic Compute Service (ECS), ApsaraDB RDS (RDS), Server Load Balancer (SLB), Bastionhost e Security Center. Essa dependência torna o gerenciamento de infraestrutura cada vez mais complexo para a equipe de DevOps.
Para resolver essa questão, a equipe definiu três objetivos principais:
Implantações revisáveis: Cada etapa de uma atividade operacional — desde o levantamento de requisitos e design arquitetônico até a codificação e implantação — deve ser precisa e rastreável.
Implantações com controle de versão: Um histórico completo e auditável de todas as alterações de infraestrutura, com capacidade de reverter para uma versão específica.
Implantações consistentes em múltiplos ambientes: Sem discrepâncias entre os ambientes de desenvolvimento, homologação e produção que possam causar falhas específicas de ambiente.
Seleção de tecnologia
A equipe avaliou três abordagens principais para gerenciar recursos de nuvem pública:
Console do provedor de nuvem: Gerenciamento direto pela GUI.
Sistema de gerenciamento personalizado: Desenvolvimento ou compra de um sistema que chame APIs do provedor de nuvem.
Frameworks de Infraestrutura como Código (IaC): Uso de código para definir e gerenciar a infraestrutura.

O IaC é o padrão estabelecido para automação de infraestrutura e o framework mais utilizado para gerenciamento multicloud. Combinado com o Git, o IaC atende diretamente aos objetivos de rastreabilidade e versionamento. Cada implantação e alteração é gerenciada como código, e as reversões são tão simples quanto voltar para um branch anterior do Git.
No ecossistema de IaC, o Terraform é uma ferramenta open-source líder, com histórico sólido em ambientes de produção empresariais. Ao adotar o Terraform, a equipe de DevOps pôde focar na escrita da lógica de negócios para sua infraestrutura, em vez de criar um sistema de orquestração personalizado do zero.
Considerando a estratégia multicloud e a arquitetura de nuvem híbrida da Pony.ai, a padronização, a facilidade de uso e a comunidade ativa do Terraform tornaram-no a escolha óbvia.
Arquitetura e implementação
A equipe implementou uma solução de IaC baseada em Terraform com um fluxo de trabalho centrado em Git, conforme mostrado abaixo:

Para os arquivos de configuração, a equipe escolheu JSON em vez da HashiCorp Configuration Language (HCL). O objetivo foi manter a consistência com suas aplicações existentes baseadas em JSON e simplificar a revisão de código.
O código é organizado por serviço de negócio. Se um serviço precisa de um SLB, certificados e instâncias ECS, todos esses recursos são definidos em um único arquivo do Terraform para aquele serviço. O exemplo a seguir apresenta uma versão simplificada:
{
"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"
}
}
}
}
Desafios de negócio
À medida que a base de código do Terraform crescia, dois problemas surgiram:
Para recursos como instâncias ECS, os engenheiros precisavam apenas de um pequeno conjunto de parâmetros —
instance_type,instance_name,availability_zone— mas tinham que definir a configuração completa todas as vezes.Implantar o mesmo serviço em diferentes ambientes (desenvolvimento, homologação, produção) exigia configurações quase idênticas, com apenas pequenas diferenças. Por exemplo, usar um SLB
slb.s2.mediumem produção versusslb.s1.smallem testes. Copiar e reescrever o código para cada ambiente prejudicava a legibilidade e a manutenção.
Soluções
Para resolver esses problemas, a equipe introduziu o Jsonnet — uma linguagem de modelagem de dados open-source — para gerar os arquivos JSON do Terraform. O Jsonnet permite abstrair códigos repetitivos em uma biblioteca reutilizável de funções utilitárias. Por exemplo, eles criaram uma função generateEcs que encapsula todos os parâmetros padrão do ECS:
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,
},
}
Os engenheiros chamam essa função para gerar a configuração completa. Isso reduz o código necessário para provisionar múltiplas instâncias a um loop conciso:
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
},
Quaisquer alterações de parâmetros são feitas na função utilitária compartilhada, em vez de em cada definição individual de recurso.
Configuração multiambiente
Para serviços executados em vários ambientes onde apenas alguns parâmetros diferem, a equipe define um modelo base. A configuração de cada ambiente importa esse modelo base e substitui apenas os campos alterados.
Isso resulta na seguinte estrutura de diretórios por serviço:
O arquivo generated/main.tf.json é o arquivo JSON que o Terraform executa. O Jsonnet o gera a partir do main.tf.json.jsonnet. O arquivo main.tf.json.jsonnet.output armazena as saídas produzidas após o Terraform aplicar a configuração.
├── 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
O ambiente de produção importa o modelo base e define sua própria especificação de 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"
}
O ambiente de desenvolvimento importa o mesmo modelo base e substitui apenas o nome e a especificação:
local base = import "../../ponyai_business_1_base.libsonnet";
base {
name: "ponyai_business_1_dev",
environment: "dev",
region: "alicloud_region",
slb_specification: "slb.s1.small"
}
Referências cruzadas entre stacks
O Jsonnet também resolve o problema de dependência entre componentes de infraestrutura. Criar uma instância ECS requer um ID de Virtual Private Cloud (VPC), mas a VPC geralmente é gerenciada em um arquivo separado do Terraform. Em vez de codificar rigidamente o ID da VPC gerado, o fluxo de trabalho da Pony.ai lê esse valor do arquivo de saída da stack da VPC.
Um arquivo main.tf.json cria a VPC e grava seu ID no arquivo main.tf.json.jsonnet.output em seu próprio diretório:
├── 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
O arquivo main.tf.json.jsonnet.output resultante tem a seguinte aparência:
{
"vpc_id": {
"sensitive": false,
"type": "string",
"value": "vpc_id_for_ponyai"
},
"vswitch-id": {
"sensitive": false,
"type": "string",
"value": "vswitch_public_id_for_ponyai"
}
}
Qualquer serviço que precise desses valores importa o arquivo de saída diretamente:
{
"ali-cloud-region": {
prod: import "./ali-cloud-region/prod/main.tf.json.jsonnet.output",
}
}
Essa abstração em camadas permite que a equipe de DevOps da Pony.ai aproveite ao máximo o ecossistema do Terraform, mantendo as dependências entre serviços limpas e explícitas.
Resultados de negócio
A adoção de IaC com Terraform e Git trouxe melhorias concretas em todo o ciclo de vida da infraestrutura. Todos os parâmetros de infraestrutura são definidos explicitamente no código. Quando um serviço precisa de duas instâncias ECS com dimensionamento e configurações de disco específicos, cada detalhe reside em um único arquivo.
Antes de cada implantação, a equipe revisa o pull request (PR) completo no Git e discute as alterações. Essa etapa de revisão também cria um registro histórico de cada mudança na infraestrutura, proporcionando total rastreabilidade.
Como as alterações são revisáveis no nível de parâmetro, a equipe pode confirmar se a implantação final corresponde ao design original. Discrepâncias detectadas durante a fase de codificação do Terraform podem ser corrigidas antes de chegarem à produção. Os desenvolvedores também precisam executar testes próprios antes de abrir um PR, evitando múltiplos ciclos de revisão em código defeituoso.
Essa transformação gerou quatro resultados principais:
Mais rapidez: O provisionamento de infraestrutura deixou de ser uma sequência de operações manuais e repetitivas no console. O ciclo de produção mais curto permite que a empresa responda mais rapidamente a decisões de negócios e oportunidades de mercado.
Maior controle: Cada alteração é versionada e auditável, elevando a organização de operações manuais no console para um sistema de gerenciamento rastreável e confiável.
Mais eficiência: A colaboração entre equipes — incluindo aquelas distribuídas internacionalmente — melhorou significativamente, reduzindo atrasos causados por diferenças de fuso horário e estilos de trabalho variados.
Mais segurança: Erros humanos em produção são muito menos prováveis. Combinar processos automatizados e orientados por código com fluxos de aprovação específicos por ambiente oferece à Pony.ai uma proteção robusta para as operações de negócios.
Conclusão
Atualização do modelo de gerenciamento
A filosofia de IaC agora sustenta todo o desenvolvimento de infraestrutura na Pony.ai. A equipe abstraiu um amplo conjunto de componentes da Alibaba Cloud — incluindo ECS, VPC, Object Storage Service (OSS), Public DNS, Resource Access Management (RAM), Simple Log Service e gerenciamento de usuários — em funções internas reutilizáveis.
Atualização do modelo de negócios
Quando uma equipe precisa de um recurso de nuvem, ela chama uma dessas funções pré-construídas e validadas. A equipe de DevOps foca em manter e melhorar a biblioteca interna, o que impulsiona um aumento sustentado na eficiência operacional em toda a organização.
Atualização do modelo operacional
Hoje, mais de 20 unidades de negócios na Pony.ai são implantadas e gerenciadas 100% por meio desse fluxo de trabalho de IaC. Cada componente possui um histórico claro e versionado; as alterações passam por revisões rigorosas; e a equipe pode rejeitar implantações não conformes antes que cheguem à produção. O resultado é um ambiente online limpo, confiável e escalável.
Sobre o autor
A equipe de DevOps da Pony.ai
Este artigo foi contribuído por um autor externo, e todos os direitos autorais pertencem a ele. A Alibaba Cloud não assume responsabilidade pelo conteúdo.