Alternative a Docker Compose
Docker Compose non è un cattivo strumento, e la maggior parte dei sistemi a cui sta stretto non ha mai avuto la forma per cui è stato pensato. Questa pagina parla del punto in cui smette di convenire, e di ciò a cui davvero rinuncia ogni alternativa —inclusa la nostra, che non è la risposta giusta per tutti—.
Quando Compose è ancora la risposta
Se il tuo ambiente locale è «un database, una coda e due servizi», Compose è un file che scrivi una volta e dimentichi. È ovunque, tutti leggono YAML e non serve far entrare uno strumento nuovo nel team. Non sostituirlo per moda.
Dove comincia a far male
- Il tuo codice deve essere un'immagine. Ogni modifica significa ricostruire, o un bind mount più un trucco di reload per linguaggio, inventato una volta in ogni repository.
- È tutto o niente. Compose avvia ciò che dice il file. Per avviare tre servizi e puntare il resto a un ambiente condiviso, modifichi il file o ne mantieni diversi.
- Fare debug è una scocciatura. Un debugger contro un container ha bisogno di una porta, di una mappatura e di una configurazione dell'IDE che qualcuno deve tenere in vita.
- Il file si disallinea. Vive in un repository, descrive servizi di altri e non è compito di nessuno mantenerlo.
- Non sa nulla di salute.
depends_onaspetta un container, non un servizio pronto a rispondere.
Lo stesso ambiente, in tutti e due i posti
Questa è la forma in cui finisce un docker-compose.yml di un sistema piccolo —due
servizi e la loro infrastruttura— quando qualcuno ha già combattuto con il ciclo di modifica:
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 # ricostruire a ogni modifica
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'] # la seconda, per il debugger
depends_on:
postgres: { condition: service_healthy }
payments:
build: ../payments
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/payments
ports: ['8081:8081']
Funziona. Quello che costa non è nel file, è intorno: ogni modifica in orders passa
per un build; sulla porta 5005 bisogna accordarsi, mapparla e configurarla
nell'IDE di ciascuno; PAYMENTS_URL è scritto a mano e puntare
payments al cloud significa modificare il file (e ricordarsi di non committarlo);
e questo file vive in un repository che non è quello di payments, quindi nessuno lo
aggiorna quando payments cambia variabile.
L'equivalente in Aseptic non è un altro file come questo: è che i servizi non vengono
impacchettati. orders e payments partono come processi, con l'hot
reload che il loro framework già porta e il debugger agganciato come a qualsiasi processo
locale. L'unica cosa che resta in Docker è l'infrastruttura —Postgres e Kafka—, che l'app avvia
e di cui rimappa le porte se sono occupate. E gli URL fra i servizi non si scrivono: vengono
iniettati all'avvio, a seconda di dove hai puntato ogni dipendenza.
orders ← processo locale, porta 8080
├── postgres → infrastruttura in Docker (la avvia Aseptic)
├── kafka → infrastruttura in Docker
└── payments → locale | cloud | mock ← questo si cambia con un clic
Il cambiamento che conta è quello dell'ultima riga. In Compose, «oggi voglio
payments contro l'ambiente cloud» significa modificare YAML; qui significa cambiare
la destinazione di una dipendenza, e il resto dello scenario se ne accorge da solo. E niente di
tutto questo si salva dentro i tuoi repository: lo scenario vive fuori, quindi non c'è un file
di ambiente da rivedere in una pull request.
Le alternative, onestamente
| Strumento | Cos'è in realtà | Costo d'ingresso |
|---|---|---|
| Tilt | Un ciclo interno rapido contro Kubernetes, con un'interfaccia che mostra build, log e salute | Un cluster, immagini e un Tiltfile per repo |
| Skaffold | Build, push e deploy su Kubernetes in un solo comando, con profili | Un cluster, immagini, manifest e uno skaffold.yaml |
| DevSpace | Il tuo codice in esecuzione dentro un container di sviluppo nel cluster, con sincronizzazione dei file | Un cluster, e sentirsi a proprio agio a fare debug dall'altra parte |
| Garden | Un grafo di dipendenze per costruire, distribuire e testare, condiviso da sviluppo e CI | Una decisione di piattaforma, e qualcuno che si faccia carico del grafo |
| Telepresence | Un processo locale inserito in un cluster reale che ne intercetta il traffico | Un cluster condiviso sano, e connettività fino a lui |
| Kubernetes locale (kind, minikube) | Le primitive vere sulla tua macchina, perché quello che provi abbia la forma della produzione | Memoria, e il ciclo di deploy a ogni modifica |
| Aseptic | Servizi come processi nativi, ogni dipendenza puntata a locale, cloud o un mock | Installarlo; Docker solo per l'infrastruttura comune |
Il modello che sta dietro alla lista
Cinque delle sette rispondono a «Compose non basta» con «allora usa Kubernetes in locale». È una risposta coerente, e se fai deploy su Kubernetes ti compra una somiglianza che nient'altro ti dà. Porta però anche il ciclo di build e deploy del cluster dentro il ciclo che ripeti duecento volte al giorno.
Aseptic è lo strano della lista di proposito: dà per scontato che non vuoi un cluster sulla tua macchina per provare un flusso. I tuoi servizi girano come processi, l'infrastruttura comune —Kafka, Redis, PostgreSQL— te la avviano in Docker, e ogni dipendenza si risolve verso un servizio locale, il vostro ambiente condiviso nel cloud o un mock, con gli URL riscritti all'avvio per non toccare nessun repository.
Dove Aseptic è la scelta sbagliata
Se quello che devi provare è la parte di Kubernetes —ingress, network policy, sidecar, probe, limiti di risorse—, Aseptic non esegue nulla di tutto ciò e non ti serve. Se i tuoi servizi si avviano solo dentro un namespace preparato, eseguirli fuori non è una scorciatoia: è una riscrittura. E se la verifica quotidiana che le tue immagini e i tuoi manifest funzionino ancora vale per te più di un ciclo rapido, quella te la danno Skaffold o Tilt e Aseptic no.
È inoltre in beta pubblica, esce per Windows, macOS e Linux e non è open source. Tutti gli altri strumenti di questa lista sono open source, e per alcuni team questo decide già tutto.
Come scegliere senza un foglio di calcolo
- I tuoi servizi si avviano con variabili d'ambiente e un database? Se sì, eseguirli nativi è il ciclo più economico che puoi avere.
- Stai provando il tuo codice o il tuo deploy? Il tuo codice non ha bisogno di un cluster. Il tuo deploy non ha bisogno d'altro.
- Il sistema ci sta in una macchina? Se davvero non ci sta, Telepresence o un cluster condiviso è la risposta onesta.
- Chi mantiene la configurazione? Uno strumento la cui configurazione non è di nessuno sarà abbandonato in due trimestri, qualunque cosa tu scelga.
Il confronto ampio mette Docker Compose, un Kubernetes locale e Aseptic fianco a fianco con più dettaglio.
Domande frequenti
Qual è l'alternativa a Docker Compose per i microservizi?
Dipende da cosa ti sta costando. Se è la somiglianza alla produzione, un Kubernetes locale con Tilt o Skaffold; se è il ciclo quotidiano di lavorare su più servizi, un orchestratore locale. La pagina confronta i cinque approcci e dove ciascuno fallisce.
Docker Compose resta corto per i microservizi?
Non per l'infrastruttura, che è ciò che fa meglio. Resta corto quando dentro il compose ci sono i tuoi servizi: costruire un'immagine per ogni modifica, senza un hot reload comodo, e con la comunicazione fra loro risolta a mano.
C'è un'opzione che non richieda Kubernetes?
Sì: restare con Compose, o un orchestratore locale come Aseptic, che usa Docker per l'infrastruttura ed esegue i tuoi servizi come processi della macchina.