Alternativas ao Docker Compose
O Docker Compose não é uma má ferramenta, e a maioria dos sistemas que lhe ficam grandes nunca teve a forma para a qual foi pensado. Esta página é sobre o ponto em que deixa de compensar, e sobre aquilo a que cada alternativa renuncia de verdade —incluída a nossa, que não é a resposta certa para toda a gente—.
Quando o Compose continua a ser a resposta
Se o teu ambiente local é «uma base de dados, uma fila e dois serviços», o Compose é um ficheiro que escreves uma vez e esqueces. Está em todo o lado, toda a gente lê YAML e não é preciso entrar uma ferramenta nova na equipa. Não o substituas por moda.
Onde começa a doer
- O teu código tem de ser uma imagem. Cada mudança significa reconstruir, ou um bind mount mais um truque de reload por linguagem, inventado uma vez em cada repositório.
- É tudo ou nada. O Compose levanta o que o ficheiro diz. Para levantar três serviços e apontar o resto a um ambiente partilhado, editas o ficheiro ou manténs vários.
- Depurar é uma maçada. Um depurador contra um contentor precisa de uma porta, de um mapeamento e de uma configuração do IDE que alguém tem de manter viva.
- O ficheiro desatualiza-se. Vive num repositório, descreve serviços de outros e não é tarefa de ninguém mantê-lo.
- Não sabe nada de saúde.
depends_onespera por um contentor, não por um serviço pronto a responder.
O mesmo ambiente, nos dois sítios
Isto é a forma em que acaba um docker-compose.yml de um sistema pequeno —dois
serviços e a sua infraestrutura— quando alguém já lutou com o ciclo de mudança:
services:
postgres:
image: postgres:16
environment: { POSTGRES_PASSWORD: dev }
ports: ['5432:5432']
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
kafka:
image: bitnami/kafka:3.7
environment:
KAFKA_CFG_NODE_ID: '0'
KAFKA_CFG_PROCESS_ROLES: controller,broker
KAFKA_CFG_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093
KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093
ports: ['9092:9092']
orders:
build: ../orders # reconstruir a cada mudança
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/orders
PAYMENTS_URL: http://payments:8081
SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:9092
ports: ['8080:8080', '5005:5005'] # a segunda, para o depurador
depends_on:
postgres: { condition: service_healthy }
payments:
build: ../payments
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/payments
ports: ['8081:8081']
Funciona. O que custa não está no ficheiro, está à volta: cada mudança em
orders passa por um build; a porta 5005 tem de ser combinada,
mapeada e configurada no IDE de cada um; PAYMENTS_URL está escrito à mão e
apontar payments à nuvem significa editar o ficheiro (e lembrar-se de não o
comitar); e este ficheiro vive num repositório que não é o de payments, por isso
ninguém o atualiza quando payments muda de variável.
O equivalente no Aseptic não é outro ficheiro como este: é que os serviços não são
empacotados. orders e payments arrancam como processos, com o hot
reload que o seu framework já traz e o depurador ligado como a qualquer processo local. O
único que continua em Docker é a infraestrutura —Postgres e Kafka—, que a app levanta e cujas
portas remapeia se estiverem ocupadas. E os URLs entre serviços não se escrevem: injetam-se ao
levantar, conforme para onde apontaste cada dependência.
orders ← processo local, porta 8080
├── postgres → infraestrutura em Docker (levanta-a o Aseptic)
├── kafka → infraestrutura em Docker
└── payments → local | nuvem | mock ← isto muda-se num clique
A mudança que importa é a da última linha. No Compose, «hoje quero payments
contra o ambiente da nuvem» é editar YAML; aqui é mudar o destino de uma dependência, e o
resto do cenário fica a saber sozinho. E nada disto se guarda dentro dos teus repositórios: o
cenário vive fora, por isso não há ficheiro de ambiente para rever num pull request.
As alternativas, honestamente
| Ferramenta | O que é na realidade | Custo de entrada |
|---|---|---|
| Tilt | Um ciclo interno rápido contra Kubernetes, com uma interface que mostra builds, logs e saúde | Um cluster, imagens e um Tiltfile por repo |
| Skaffold | Build, push e deploy para Kubernetes num só comando, com perfis | Um cluster, imagens, manifestos e um skaffold.yaml |
| DevSpace | O teu código a correr dentro de um contentor de desenvolvimento no cluster, com sincronização de ficheiros | Um cluster, e estar à vontade a depurar do outro lado |
| Garden | Um grafo de dependências para construir, fazer deploy e testar, partilhado por desenvolvimento e CI | Uma decisão de plataforma, e alguém que tome conta do grafo |
| Telepresence | Um processo local metido num cluster real a intercetar o seu tráfego | Um cluster partilhado saudável, e ligação até ele |
| Kubernetes local (kind, minikube) | As primitivas a sério na tua máquina, para que o que testas tenha a forma de produção | Memória, e o ciclo de deploy a cada mudança |
| Aseptic | Serviços como processos nativos, cada dependência apontada a local, nuvem ou um mock | Instalá-lo; Docker só para a infraestrutura comum |
O padrão que há por trás da lista
Cinco das sete respondem a «o Compose não chega» com «então usa Kubernetes em local». É uma resposta coerente, e se fazes deploy em Kubernetes compra-te uma semelhança que nenhuma outra coisa te dá. Também traz o ciclo de construir e fazer deploy do cluster para o ciclo que repetes duzentas vezes por dia.
O Aseptic é o estranho da lista de propósito: dá por assente que não queres um cluster na tua máquina para testar um fluxo. Os teus serviços correm como processos, a infraestrutura comum —Kafka, Redis, PostgreSQL— é levantada em Docker por ti, e cada dependência resolve-se para um serviço local, para o vosso ambiente partilhado na nuvem ou para um mock, com os URLs reescritos ao levantar para não tocar em nenhum repositório.
Onde o Aseptic é a escolha errada
Se o que precisas de testar é a parte do Kubernetes —ingress, network policies, sidecars, probes, limites de recursos—, o Aseptic não executa nada disso e não te serve. Se os teus serviços só arrancam dentro de um namespace preparado, executá-los fora não é um atalho: é uma reescrita. E se a verificação diária de que as tuas imagens e os teus manifestos continuam a funcionar vale para ti mais do que um ciclo rápido, isso dão-to o Skaffold ou o Tilt e o Aseptic não.
Está além disso em beta pública, publica-se para Windows, macOS e Linux, e não é código aberto. Todas as outras ferramentas desta lista são de código aberto, e para algumas equipas isso já decide tudo.
Como escolher sem uma folha de cálculo
- Os teus serviços arrancam com variáveis de ambiente e uma base de dados? Se sim, executá-los nativos é o ciclo mais barato que podes ter.
- Estás a testar o teu código ou o teu deploy? O teu código não precisa de um cluster. O teu deploy não precisa de outra coisa.
- O sistema cabe numa máquina? Se de facto não cabe, o Telepresence ou um cluster partilhado é a resposta honesta.
- Quem mantém a configuração? Uma ferramenta cuja configuração não é de ninguém estará abandonada em dois trimestres, escolhas o que escolheres.
O comparativo amplo põe o Docker Compose, um Kubernetes local e o Aseptic lado a lado com mais detalhe.
Perguntas frequentes
Qual é a alternativa ao Docker Compose para microsserviços?
Depende do que te está a custar. Se é a semelhança com produção, um Kubernetes local com Tilt ou Skaffold; se é o ciclo diário de trabalhar sobre vários serviços, um orquestrador local. A página compara as cinco abordagens e onde cada uma falha.
O Docker Compose fica curto para microsserviços?
Não para a infraestrutura, que é o que faz melhor. Fica curto quando dentro do compose estão os teus serviços: construir uma imagem por cada mudança, sem hot reload cómodo, e com a comunicação entre eles resolvida à mão.
Há alguma opção que não precise de Kubernetes?
Sim: continuar com o Compose, ou um orquestrador local como o Aseptic, que usa Docker para a infraestrutura e corre os teus serviços como processos da máquina.