- O Docker Compose permite definir e gerenciar vários contêineres a partir de um único arquivo YAML, facilitando o trabalho com aplicações baseadas em microsserviços.
- A seção de serviços é obrigatória e deve ser preenchida com redes, volumes, configurações e segredos para controlar a comunicação, a persistência e a configuração.
- Exemplos como Flask+Redis ou um aplicativo full stack com frontend, backend e banco de dados mostram como o Compose simplifica o desenvolvimento e a implantação.
- Os comandos docker compose up, down, ps e toras Eles constituem o fluxo de trabalho básico para iniciar, depurar e parar conjuntos de contêineres.

Se você já experimentou com contêineres e percebeu que um aplicativo "de verdade" requer mais de um serviço rodando simultaneamente (banco de dados, API, frontend, cache, etc.), você eventualmente se deparará com o Docker Compose. Essa ferramenta permite configurar toda essa infraestrutura com um único arquivo e alguns comandos, sem precisar ficar alternando entre terminais e scripts intermináveis.
Neste tutorial, você aprenderá o que é o Docker Compose, como funciona o arquivo compose.yaml e como orquestrar aplicações com múltiplos contêineres de forma organizada: desde exemplos simples com Flask e Redis até arquiteturas mais complexas com frontend, backend e banco de dados. Você também abordará redes, volumes, configurações, segredos e os principais comandos para trabalhar confortavelmente em ambientes de desenvolvimento e mais exigentes.
O que é o Docker Compose e por que vale a pena usá-lo?
O Docker Compose é uma extensão do Docker que permite definir e gerenciar vários contêineres como se fossem uma única aplicação. Em vez de iniciar cada serviço manualmente com o comando `docker run` e seus parâmetros, você descreve tudo em um arquivo YAML e inicia com um único comando.
A beleza do Compose reside no fato de que muitas aplicações modernas são construídas sobre microsserviços que rodam em contêineres individuais : um banco de dados, uma API, um frontend, um sistema de filas, um cache tipo Redis, e assim por diante. O Docker recomenda que cada contêiner execute um único serviço; portanto, se você tentar incluir tudo em uma única imagem, acabará com um monstro difícil de escalar e manter.
Você pode executar dois ou mais serviços no mesmo contêiner, mas isso anula boa parte das vantagens do Docker : se um falhar, os outros também param de funcionar; você não pode dimensionar apenas a parte que recebe mais carga; e o gerenciamento de logs, recursos e falhas se torna uma grande bagunça.
Com o Docker Compose, você define cada serviço separadamente, especificando como eles se conectam entre si, quais dados eles armazenam, quais portas eles expõem, quais variáveis de ambiente eles usam e assim por diante. Dessa forma, se um contêiner falhar, os demais podem continuar funcionando dependendo de como você os configurou, e escalar um componente específico é tão simples quanto modificar sua configuração ou o número de réplicas.
Além disso, o Compose se integra perfeitamente a fluxos de trabalho de CI/CD e implantações em produção . Você pode usá-lo diretamente com ferramentas como Portainer ou Docker Swarm e, se você trabalha com Kubernetes, projetos como o Kompose permitem traduzir um arquivo compose.yaml em manifestos do Kubernetes sem precisar reescrever tudo manualmente.
Pré-requisitos para seguir o tutorial do Docker Compose
Para acompanhar facilmente os exemplos deste tutorial, você precisa ter o Docker e o Docker Compose instalados . Atualmente, existem dois caminhos principais:
- Docker Engine + Docker Compose Instalados como binários independentes.
- Área de trabalho do Docker, que inclui o Docker Engine, o Docker Compose e uma interface gráfica.
É importante ter uma compreensão básica dos comandos do Docker (imagens, contêineres, portas, volumes) e não ter receio de usar a linha de comando. Os exemplos geralmente são baseados no Linux (por exemplo, Ubuntu 22.04), mas a lógica se aplica igualmente ao Windows e ao macOS com o Docker Desktop.
Verifique se tudo está em ordem executando comandos simples como "docker --version" e "docker compose version" no seu terminal . Se ambos os comandos forem respondidos sem erros, você está pronto para continuar com os exemplos.
Estrutura básica de um arquivo compose.yaml
O coração do Docker Compose é o arquivo compose.yaml (ou docker-compose.yml)Ali descrevemos quais serviços queremos configurar e como eles devem estar relacionados. Embora anteriormente o campo fosse usado version Para indicar a versão do formato, a documentação atual recomenda não defini-la, para que a versão mais recente do esquema seja sempre utilizada.
Dentro do arquivo Compose, você encontrará diversas seções possíveis, embora apenas uma seja obrigatória: `services` . A partir daí, você pode adicionar outras seções dependendo da complexidade do seu projeto:
- ServiçosDefinição de cada microsserviço (obrigatório).
- redesRedes personalizadas para controlar a comunicação entre contêineres.
- volumes: volumes para armazenar dados ou compartilhá-los entre serviços.
- configsConfiguração de serviço (por exemplo, arquivos de configuração do servidor web).
- segredos!: gerenciamento de informações sensíveis (senhas, chaves de API…).
Ao longo deste tutorial, você verá como combinar todas essas seções para um projeto típico que inclui um aplicativo, um banco de dados e uma API , além de um exemplo de aplicativo web em Python com Flask e Redis.
Serviços no Docker Compose: o núcleo da definição
A seção Os serviços são a peça essencial. a partir de qualquer arquivo Compose. Nele, você define cada um dos contêineres que comporão seu aplicativo, dando a eles o nome que desejar (por exemplo, web, database, api, redis, Etc.)
Para cada serviço, você pode definir diversos parâmetros , incluindo alguns que são comumente usados em projetos reais:
Parâmetro build indica onde o Dockerfile está localizado a partir da qual a imagem do serviço será construída. Normalmente, especifica-se um contexto (diretório) onde reside o Dockerfile da aplicação que você deseja empacotar.
Se você já tiver uma imagem criada ou quiser usar uma do registro, você usa image para fazer referência a issoO nome segue o formato [<registry>/][<project>/]<image>[:<tag>|@<digest>]E se você precisar controlar quando essa imagem é baixada ou atualizada, você pode usar pull_policy.
Campo ports É utilizado para mapear portas entre o host e o contêiner.A sintaxe é do tipo [HOST:]CONTAINER[/PROTOCOL]Por exemplo, se um banco de dados PostgreSQL estiver escutando na porta 5432 dentro do contêiner e você quiser expô-lo na porta 5555 do host, você faria algo assim: "5555:5432" na lista de portos.
A política de reinicialização é controlada por restartque indica o que fazer quando um contêiner termina com erro ou para. Os valores típicos são no, always, on-failure y unless-stoppedpermitindo que serviços críticos permaneçam operacionais mesmo que sofram interrupções ocasionais.
Se um serviço precisar que outro esteja disponível antes de começar, você pode usar depends_on definir dependências entre contêineresUm exemplo clássico é um aplicativo que exige que o banco de dados esteja em funcionamento para evitar falhas na conexão inicial.
Para configuração e credenciais, você tem duas abordagens comuns: env_file y environment. com env_file Você está apontando para um ou mais arquivos. .env com as variáveis de ambiente, enquanto em environment Você pode listá-los diretamente no YAML. A melhor prática é usar arquivos. .env Para evitar que senhas e dados sensíveis sejam incorporados no próprio arquivo compose.yaml.
Parâmetro volumes Permite montar caminhos ou volumes do host. Dentro do contêiner, você usará tanto a persistência de dados quanto o compartilhamento de pastas entre os serviços. Aqui, você fará referência apenas aos volumes que poderá definir posteriormente na seção acima. volumes Se você precisar que elas sejam compartilhadas ou gerenciadas de forma mais explícita.
Com esses campos, você já pode configurar serviços bastante completos. A especificação Compose inclui muitas opções mais avançadas (saúde, limites de recursos, comandos de inicialização , etc.), mas estas já abrangem a maioria dos usos comuns.
Exemplo 1: Aplicação web em Python com Flask e Redis
Um exemplo típico para entender o Docker Compose é a criação de uma aplicação web simples em Python , usando Flask para servir as páginas e Redis como um banco de dados em memória para o contador de visitantes. A ideia é que você não precisa instalar o Python nem o Redis na sua máquina: tudo roda dentro de contêineres.
O fluxo de trabalho seria algo assim: primeiro você cria um diretório para o projeto e dentro dele adiciona um arquivo. app.py com o código Flask. Nesse código você usa "redis" como nome do host e porta 6379, que é a porta padrão para o serviço Redis no seu contêiner.
A função que gerencia o contador de acessos tenta se conectar ao Redis várias vezes antes de desistir, pois o contêiner Redis pode levar alguns segundos para ficar disponível quando você inicia toda a pilha.
Mais app.py, você cria um arquivo requirements.txt com dependências do Python (por exemplo, Flask e redis-py), e um Dockerfile que especifica como construir a imagem da sua aplicação web: imagem base do Python (3.7, 3.10 ou qualquer outra), diretório de trabalho, variáveis de ambiente para o Flask, instalação do gcc e dependências do sistema, cópia do requirements.txt, instalação de pacotes e cópia do código.
No Dockerfile, você também marca o porta que exibirá o contêiner (por exemplo, 5000) e você define o comando padrão, normalmente flask run --debug ou algo semelhante, para que inicie automaticamente quando o contêiner for criado.
Com tudo isso pronto, o arquivo compose.yaml define dois serviços: um chamado, por exemplo, web, que é construído a partir do Dockerfile do projeto e expõe a porta 8000 externamente (mapeando a porta 8000 do host para a porta 5000 do contêiner), e outro chamado redis que Baixe a imagem oficial do Redis do Docker Hub..
Para iniciar a aplicação, basta navegar até o diretório do seu projeto e executar "docker compose up" . O Compose fará o download da imagem do Redis, criará a imagem da sua aplicação web e iniciará ambos os serviços na ordem correta.
Depois de estar instalado e funcionando, você acessa com seu navegador o seguinte: http://localhost:8000 (o http://127.0.0.1:8000) e você deverá ver uma mensagem do tipo "Olá Mundo" e um Contador de visitantes que aumenta a cada vez que você recarrega a página. a página. Se você inspecionar as imagens locais com docker image lsVocê verá algo como redis y web criado ou baixado.
Quando você quiser parar com tudo, você pode fazer Pressione CTRL+C no terminal onde você deixou "docker compose up" ou executar docker compose down a partir do diretório do projeto. Isso interromperá e removerá os contêineres criados pelo comando compose.
Melhorando o fluxo de trabalho: Vincule montagens e observe o Compose.
Trabalhar em desenvolvimento com Docker é mais conveniente se você não precisar reconstruir a imagem sempre que modificar o código. É aí que entram os Bind Mounts e, em versões mais recentes, o Docker Compose Watch.
Uma montagem de diretórios (bind mount) envolve a montagem de uma pasta do seu computador dentro do contêiner. No arquivo compose.yaml, você adiciona uma seção ao serviço web. volumes que mapeia o diretório do projeto para o diretório de trabalho do contêiner, por exemplo .:/codeDessa forma, quaisquer alterações feitas no editor serão refletidas instantaneamente no contêiner.
Se você também ativar o modo de depuração do Flask com a variável FLASK_DEBUG=1, o comando flask run O aplicativo será recarregado automaticamente ao detectar alterações nos arquivos, sem a necessidade de ser interrompido e reiniciado.
O Docker Compose Watch leva isso um passo adiante: você pode usar `docker compose watch` ou `docker compose up --watch` para que o Compose monitore os arquivos do seu projeto e sincronize as alterações com seus contêineres de forma mais inteligente. Quando você salva um arquivo, ele é copiado para o contêiner e o servidor de desenvolvimento atualiza o aplicativo sem precisar reiniciar todo o contêiner.
Experimente, por exemplo, alterar a mensagem de boas-vindas em app.py de "Olá, mundo!" para uma frase como "Olá do Docker"Salve o arquivo, atualize seu navegador e você verá a nova mensagem instantaneamente, enquanto o contador de visitas continua funcionando sem perder o estado.
E quando você terminar de trabalhar, como sempre, você pode puxar docker compose down prevenir Desligue e limpe os recipientes. que estavam em andamento com aquela pilha.
Exemplo 2: Aplicativo full stack com frontend, backend e banco de dados
Para visualizar o Docker Compose em uma arquitetura mais realista, imagine um aplicativo de lista de tarefas (Todo List) com um frontend em Vue.js, uma API em Node.js e um banco de dados MongoDB. Cada parte reside em seu próprio diretório e possui seu próprio Dockerfile.
No repositório, você pode encontrar uma pasta. frontend com o aplicativo Vue e outro backend com o servidor Node. O backend expõe endpoints para Criar, listar, atualizar e excluir tarefase se conecta ao MongoDB para armazená-las. O frontend consome esses endpoints para exibir e gerenciar a lista de tarefas no navegador.
O arquivo docker-compose.yml Localiza-se na raiz do projeto e define três serviços: frontend, backend y databaseO serviço de front-end é construído a partir do Dockerfile na pasta correspondente, geralmente expondo a porta interna 80 e mapeando-a para a porta 5173 do host (por exemplo, para usar a mesma URL do ambiente de desenvolvimento local).
O backend é construído a partir do Dockerfile no diretório backend, expõe a porta 3000 (tanto dentro quanto fora do contêiner, se você quiser simplificar) e declara uma dependência do banco de dados para garantir que o MongoDB esteja disponível na inicialização.
O serviço database use o diretamente Imagem oficial do MongoDB e construir um volume, digamos mongodb_dataem /data/db, que é onde o MongoDB armazena seus dados. Esse volume é declarado na seção superior. volumes a partir do modo de composição, para que os dados persistam mesmo se você excluir e recriar os contêineres.
Finalmente, todos esses serviços se conectam por meio de uma rede personalizada, por exemplo. my-network, definida na seção networksIsso permite que sejam resolvidos pelo nome do serviço (o backend pode se conectar ao Mongo usando o nome do host). database) e que o tráfego está encapsulado nessa rede isolada.
Com a configuração pronta, execute docker compose up No cerne do projeto, está a sua responsabilidade por Crie ou baixe as imagens e execute os três contêineres.Você pode verificar se tudo está em ordem com docker compose ps, e então acessando http://localhost:5173 Para visualizar o aplicativo Vue no seu navegador e criar suas primeiras tarefas.
Redes no Docker Compose: conectando serviços entre si.
A camada de rede permite que seus contêineres "enxerguem" e se comuniquem entre si de forma controlada . Por padrão, o Docker já cria redes para o Compose, mas defini-las explicitamente oferece mais clareza e controle sobre o que pode se comunicar com o quê.
Funciona de forma simples: cada serviço inclui um campo. networks onde você indica a quais redes ele pertence e, em seguida, na seção superior. networks Você define essas redes com suas configurações. A abordagem mais comum (e recomendada em muitos casos) é usar o driver. bridge.
Uma rede de pontes cria uma espaço privado rede para seus contêinerescom resolução automática de DNS baseada no nome do serviço. Isso significa que, por exemplo, se o seu serviço de banco de dados se chamar databaseQualquer outro serviço na mesma rede pode se conectar usando apenas database como nome de host.
Em um projeto com frontend, backend e banco de dados, você pode decidir, por exemplo, criar uma rede para o frontend e outra para o backend. O frontend se conectaria ao backend, e o backend ao banco de dados, mas o frontend e o banco de dados não precisariam necessariamente compartilhar a mesma rede , reduzindo assim a superfície de ataque interna.
Em código, isso se traduz em algo tão simples quanto atribuir a rede correspondente a cada serviço e, em seguida, definir essas redes com pontes de driver. No nível do aplicativo, a abordagem mais simples é usar o nome do serviço como host Ao configurar as conexões: de app a databasePor exemplo, simplesmente indicando que o host do banco de dados é "database".
Volumes no Docker Compose: Persistência de dados
Os volumes são a maneira recomendada de persistir informações geradas por contêineres , como bancos de dados , arquivos de usuário, backups etc. Eles também são usados para compartilhar dados entre serviços dentro da mesma pilha.
Na seção services Você pode montar volumes diretamente com volumesMas, quando você deseja que esse volume seja acessível por vários contêineres ou deseja gerenciá-lo de forma mais explícita, você também o define na seção superior. volumes do arquivo compose.yaml.
Imagine que você queira configurar um sistema de backup para seu banco de dados. Nesse caso, o serviço de banco de dados montaria um volume para armazenar seus dados, e outro serviço dedicado a backups montaria esse mesmo volume em modo somente leitura para realizar exportações ou sincronizações sem acessar o contêiner principal.
O Docker permite ajustar as configurações de volume com mais parâmetros (tipo de driver, opções específicas para drivers externos , etc.), mas na maioria dos casos é mais prático deixar o Docker gerenciar os volumes automaticamente, sem se preocupar com configurações complexas.
O importante é ter clareza sobre quais pastas em seus serviços precisam ser persistentes e declará-las como volumes no Compose para que você não perca dados ao recriar contêineres ou atualizar imagens.
Configurações: gerenciando arquivos de configuração
A seção configs Ele foi projetado para gerenciar arquivos de configuração. de serviços dentro da sua pilha, semelhante a volumes, mas especificamente focado em configuração.
Imagine um servidor Apache ou Nginx rodando em um contêiner Docker. Provavelmente, você precisará ajustar o arquivo de configuração dele com bastante frequência. Recriar a imagem toda vez que você modifica esses arquivos é ineficiente e irritante, especialmente em ambientes onde os parâmetros são alterados constantemente.
Com configs Você pode especificar no serviço o que deseja. aplicar uma configuração específica e depois descreva-o na seção configsExistem várias maneiras de defini-los, sendo a mais comum:
fileA configuração é gerada a partir de um arquivo local.external: se estiver marcado comotrueO Compose pressupõe que a configuração já existe e apenas a referencia.name: Nome interno da configuração no Docker, útil ao combinar comexternal: true.
Dessa forma, você pode atualizar o arquivo de configuração em sua máquina e reconstruir a pilha sem precisar reconstruir a imagem base , mantendo o código da imagem separado da configuração específica do ambiente.
Segredos: credenciais e dados confidenciais
A seção secrets resolve um problema clássicoOnde posso armazenar senhas, chaves de API e outras informações confidenciais sem deixá-las espalhadas pelo código ou pelo arquivo YAML?
Assim como as configurações, os segredos podem ser definidos de diferentes maneiras . A maneira usual é:
fileO segredo é gerado a partir do conteúdo de um arquivo (por exemplo, um arquivo de texto com uma chave).environmentO segredo é criado usando o valor de uma variável de ambiente do seu sistema.external: indica que o segredo já foi criado e só precisa ser referenciado, útil para evitar sobrescrever segredos que são gerenciados externamente.name: nome interno do segredo, especialmente relevante ao combinarexternal: truecom segredos criados por outra ferramenta.
Com os segredos, você pode permitir que os contêineres que precisam acessar essas credenciais as leiam de forma controlada, sem precisar deixá-las visíveis no repositório de código ou no próprio arquivo compose.yaml, fortalecendo significativamente a segurança de suas implantações.
Trabalhando com vários arquivos: Compor e incluir
Em projetos de grande porte, não é incomum que sua aplicação seja dividida em vários serviços, às vezes gerenciados por equipes diferentes. Nesses casos, é prático separar a configuração em vários arquivos Compose para melhor modularizar a arquitetura.
Uma abordagem típica é ter um compose.yaml arquivo principal para o aplicativo e outros arquivos para partes da infraestrutura. Por exemplo, você pode Mova a definição do Redis. ou outros serviços de suporte a arquivos infra.yaml e, no geral, escreva apenas o que diz respeito diretamente ao seu aplicativo.
Para fazer isso, você cria o arquivo. infra.yaml com sua própria seção services onde você deixa, por exemplo, o serviço Redis completo. Então, no seu compose.yaml principal, você adiciona uma seção include que aponta para o arquivo infra.yaml.
Quando você corre docker compose up A partir do diretório do projeto, Compose. Combine os dois arquivos E isso exibe todos os serviços como se estivessem em um único arquivo YAML, mas você ainda mantém a lógica separada e mais organizada.
Essa técnica facilita para diferentes equipes manterem seus próprios arquivos Compose e para que o aplicativo global seja montado usando includes, o que é muito útil em arquiteturas com dezenas de contêineres ou ambientes com muita infraestrutura compartilhada.
Comandos essenciais do Docker Compose
Embora o Compose tenha um bom catálogo de comandos, a maioria das pessoas usa apenas alguns deles regularmente no dia a dia . Vale a pena dominá-los, pois eles definem seu fluxo de trabalho.
O mais importante é docker compose upEste comando cria as imagens necessárias (caso ainda não existam), cria os contêineres, configura redes e volumes e inicia todos os serviços definidos no seu arquivo Compose. É o comando que você usa quando deseja iniciar seu conjunto de serviços.
Geralmente é combinado com a opção -d para executá-lo no modo "desanexado"Ou seja, em segundo plano. Dessa forma, você não sobrecarrega o terminal com registros e pode continuar usando essa sessão para outros comandos. Por exemplo: docker compose up -d.
Para parar e limpar o que você levantou, você usa docker compose downque interrompe e remove contêineres, redes e, opcionalmente, imagens e volumes associados. Duas flags muito comuns aqui são --rmi (para excluir imagens) e -v (para remover volumes definidos na seção volumes).
Se você quiser ver quais contêineres fazem parte do projeto e qual o status deles, você pode executar o seguinte comando: docker compose psEsta lista apresenta cada serviço, seu status (ativo, encerrado, etc.) e as portas expostas, o que é muito útil para verificar se tudo está funcionando corretamente após uma reinicialização. up.
Ao iniciar seu stack em modo detached, os logs não aparecem no terminal. Para visualizá-los, você precisa usar... docker compose logsglobalmente ou filtrando por serviço. A bandeira -f Permite rastrear registros em tempo real, o que é muito útil para depurar um serviço específico sem necessidade de acessar o interior do recipiente.
Fluxo de trabalho típico: definir compose.yaml, execute um docker compose up -d, verifique com docker compose ps, revise os registros com docker compose logs -f <servicio> Se algo der errado, e quando você terminar, use docker compose down Deixar tudo limpo.
Se você alguma vez se perder, docker compose --help Ele mostra a lista de subcomandos e opções disponíveis para ajudar você a lembrar o que cada item faz sem precisar consultar a documentação.
Considerando tudo o que foi dito acima, esta é uma ferramenta essencial para qualquer pessoa que trabalhe com contêineres além de projetos individuais. Ela permite desenvolver diretamente em um ambiente muito semelhante (ou idêntico) ao de produção, controlar serviços, redes e dados a partir de um simples arquivo YAML e evitar uma série de problemas de compatibilidade e implantação que inevitavelmente surgem ao trabalhar apenas "localmente", sem contêineres. Depois que você se acostuma a escrever um bom arquivo compose.yaml para seus projetos, é bem difícil voltar atrás.
Escritor apaixonado pelo mundo dos bytes e da tecnologia em geral. Adoro compartilhar meu conhecimento por meio da escrita, e é isso que farei neste blog, mostrar a vocês tudo o que há de mais interessante sobre gadgets, software, hardware, tendências tecnológicas e muito mais. Meu objetivo é ajudá-lo a navegar no mundo digital de uma forma simples e divertida.
