Se estiver se perguntando como criar contêineres Docker reproduzíveis com o Docker Compose, você veio ao lugar certo. Neste tutorial passo a passo do Docker Compose, você vai aprender como criar contêineres simples, mapear portas com o Docker Compose até cenários complexos com vários contêineres.
Está pronto? Vamos lá!
Pré-requisitos
Se você deseja seguir passo a passo neste tutorial, certifique-se de ter o seguinte:
- A fresh install of Ubuntu Server LTS with SSH Enabled. This guide will use Ubuntu Server LTS 20.04.1 as the Docker host machine.
- A computer with VS Code installed (optional). This guide will use Visual Studio Code 1.52.1 to SSH to the Docker host and run commands.
- O VS Code extensão SSH oficial instalada e conectada ao host Docker. (opcional)
O que é o Docker Compose?
Comandos únicos podem ficar longos, realmente longos no Docker. Veja o exemplo abaixo. Este exemplo cria um contêiner para uma aplicação de software chamada bookstack.
À medida que a complexidade de um ambiente Docker aumenta, também aumenta a quantidade de flags e condições necessárias para uma configuração de contêiner funcionando. A linha de comando do Docker começa a se tornar complicada e difícil de solucionar; especialmente quando configurações de múltiplos contêineres começam a entrar em jogo.
O Docker Compose é uma maneira de criar contêineres Docker reproduzíveis usando um arquivo de configuração em vez de comandos Docker extremamente longos. Ao usar um arquivo de configuração estruturado, os erros são mais fáceis de identificar e as interações entre contêineres são mais fáceis de definir.
O Docker Compose rapidamente se torna inestimável ao lidar com dependências de contêiner ou ambientes de múltiplos contêineres.
O Docker Compose é uma maneira fantástica de entrar no Infrastructure as Code sem a complexidade dos sistemas distribuídos como o Kubernetes.
O Docker Compose utiliza uma estrutura de arquivo de configuração chamada YAML. O YAML é semelhante ao JSON ou HTML no sentido de que é uma linguagem estruturada e legível por máquina. O YAML foca especificamente em ser o mais legível possível para os humanos, mantendo o poder da estrutura.
O YAML tem uma desvantagem em que as guias e outros espaços em branco são significativos e devem ser formatados corretamente. O VS Code faz muito desse trabalho árduo para você, e é por isso que você verá muitos dos exemplos sendo feitos no VS Code.
Instalando o Docker Compose
Agora vamos começar a colocar a mão na massa. Supondo que você esteja conectado ao seu host Docker, é hora de instalar o Docker Compose.
O Docker Compose é um pacote separado do tempo de execução do Docker. Mas a instalação do Docker Compose também instalará o tempo de execução do Docker, matando dois coelhos com uma cajadada só!
Para instalar o Docker Compose e o tempo de execução do Docker, execute os dois comandos a seguir.

Uma vez instalado, agora você deve criar uma estrutura de pastas para armazenar containers.
Criando uma Estrutura de Pastas para o Docker Compose
Antes de criar um contêiner com o Docker Compose, você deve primeiro criar uma pasta para armazenar contêineres. Você não deve apenas criar uma estrutura de pastas para armazenar contêineres, mas você verá que vários comandos do Docker são sensíveis à localização de vários arquivos de configuração; o Docker Compose não é diferente.
O componente mais importante do Docker Compose é seu arquivo de configuração chamado docker-compose.yaml. Este arquivo de configuração, como explicado acima, dita como o tempo de execução do Docker deve construir um contêiner.
Ao executar o Docker Compose, o comando procurará seu arquivo de configuração na mesma pasta em que o comando é executado. Devido a esse requisito, é sempre melhor criar uma pasta separada ao executar o Docker Compose.
Só pode haver um arquivo de configuração do Docker Compose por pasta.
Para demonstrar a criação de um contêiner Docker com o Docker Compose, primeiro crie uma estrutura de pastas para armazenar o futuro contêiner e seu arquivo de configuração usando um pequeno servidor de arquivos chamado Caddy.
Caddy é um servidor de arquivos, semelhante ao apache httpd ou nginx, mas escrito na linguagem Go. O Caddy é especificamente projetado para facilidade de uso (e irá gerar ou servir automaticamente um arquivo index.html) sem configuração. Essa combinação torna o Caddy uma boa escolha para iniciantes.
Assumindo que você está logado no seu host Docker, crie a estrutura de pastas da seguinte forma:
- Em seu diretório pessoal, crie uma pasta chamada containers. Essa pasta será um bom espaço reservado para este e outros contêineres.
- Dentro da pasta containers, crie uma subpasta chamada caddy. Esta pasta conterá o arquivo de configuração do Docker Compose e o próprio contêiner Caddy.
- Finalmente, dentro da pasta do contêiner, caddy, crie um arquivo de texto em branco chamado docker-compose.yaml que se tornará o arquivo de configuração do Docker Compose.
Com a estrutura de pastas e o arquivo de configuração do Docker Compose criados, você pode começar a preencher esse arquivo com uma configuração do Docker Compose.
Criando um Arquivo de Configuração do Docker Compose
Em sua forma mais básica, um arquivo docker-compose.yaml para o contêiner caddy se parece com o seguinte. Em seu editor de texto Linux favorito ou com o VS Code, copie e cole o código abaixo no arquivo de configuração do Docker Compose criado anteriormente.
Vamos percorrer cada uma das opções mostradas:
version
especifica a versão do arquivo docker-compose. Cada nova definição do Docker Compose inclui mudanças que quebram a especificação. Portanto, a versão é importante para que o Docker Compose possa determinar quais recursos precisa usar. A versão 3.7 é a última versão suportada pelo Ubuntu 20.04.1 LTS.
A especificação completa para o Docker Compose 3.x pode ser encontrada aqui. A documentação vinculada menciona todas as opções que você pode usar dentro do Docker Compose
services
contém as especificações para os contêineres reais. Você pode definir vários contêineres nesta seção.caddy
é o nome do primeiro contêiner (isto é puramente para referência).container_name
define o nome real dado ao contêiner pelo Docker e deve ser único.image
é o nome da imagem. Neste caso, caddy do Docker Hub está definido. O nome ou número após a marca separado por dois pontos é a versão.
Mapeamento de Porta
Essa última opção em particular requer uma menção especial:
Em Docker Compose, a diretiva ports
permite configurar uma ou mais correspondências do host para o contêiner. Por exemplo, acima, você mapeou a porta 80
no host para a porta 80
no contêiner. No entanto, não é necessário que os números das portas correspondam. O exemplo abaixo mapeia a porta 8800
no host para a porta 80
no contêiner.
Você também pode definir várias portas como mostrado abaixo.
Ao fazer isso, você mapearia tanto a porta 80
quanto a 443
para o host (uma configuração comum para servidores web, para atender tanto HTTP quanto HTTPS).
O criador da imagem Docker define as portas disponíveis no momento da criação. Certifique-se de verificar a documentação da imagem com a qual está trabalhando no Docker Hub ou no site do mantenedor para portas mapeáveis. Não faz sentido mapear uma porta se ela não estiver em uso!
Com isso em mente, vamos ver como realmente executar o contêiner.
Executando o Contêiner
Neste ponto, você deve ter o arquivo docker-compose.yaml dentro da sua pasta ~\containers\caddy. Agora é a hora de criar e iniciar o contêiner Caddy.
No seu terminal, execute o seguinte comando, que iniciará os contêineres Docker definidos no arquivo docker-compose.yaml.
Você pode ter notado que não precisou especificar a localização do arquivo docker-compose.yaml ao executar
sudo docker-compose up -d
. O Docker Compose espera que você execute todos os comandos dentro da pasta que contém o arquivo docker-compose.yaml, pois muitos comandos são relativos a essa pasta.
Agora verifique se o contêiner está ativo e funcionando navegando para http://<seu ip>. Este guia está usando http://homelab-docker como referência.
Você pode ver esse processamento acontecendo no VS Code enquanto está conectado via SSH ao host do Docker na animação abaixo:

Sucesso! Agora você usou o Docker Compose com sucesso para iniciar um contêiner a partir de um arquivo de configuração. Com esse primeiro passo importante dado, vamos ver como você gerencia o estado do seu contêiner.
Comandos para Gerenciar Contêineres Desanexados
Na seção anterior, você iniciou o contêiner caddy usando a bandeira -d
. Fazendo isso, um contêiner foi executado em um estado desanexado. Quando um contêiner está em um estado desanexado, significa que o contêiner continuará a rodar em segundo plano. Mas, isso apresenta um problema: como você gerencia esse contêiner se não tem mais controle direto?
Para resolver esse problema, o Docker Compose possui uma série de comandos que gerenciarão contêineres iniciados com um arquivo docker-compose.yaml:
- O comando
docker-compose restart
é usado para reiniciar um contêiner que está atualmente em execução. Fazê-lo é diferente de realmente executar novamente o comandodocker-compose up -d
. O comando de reinício simplesmente reiniciará um contêiner existente, executará novamente o comandodocker-compose up -d
e recriará o contêiner do zero (se o arquivo de configuração tiver sido alterado). - O
docker-compose stop
irá parar um contêiner em execução sem destruir o contêiner. Da mesma forma,docker-compose start
iniciará o contêiner novamente. - O
docker-compose down
irá parar os contêineres em execução e também os destruirá. Aqui é onde entram os bind mounts dos volumes (leia mais abaixo). - O
docker-compose pull
irá buscar a versão atual da imagem do docker (ou imagens) no repositório. Se estiver usando a taglatest
, você pode seguir comdocker-compose down && sudo docker-compose up -d
para substituir o contêiner pela versão mais recente. Usardocker-compose pull
é uma maneira conveniente de atualizar contêineres rapidamente com tempo de inatividade mínimo. - O
docker-compose logs
irá mostrar os logs do contêiner em execução (ou parado). Você também pode abordar contêineres individuais (se houver vários contêineres definidos no arquivo de composição) comdocker-compose logs <nome do contêiner>
.
A full list of docker-compose commands can be seen by running
docker-compose
with no additional arguments or referenced here in the documentation.
Agora que você tem um contêiner em execução, vamos ver como usar conteúdo salvo localmente em sua máquina.
Criando Bind Mounts no Docker Compose
Montagens de Vínculo são como o Docker mapeia dados importantes do usuário para armazenamento local no seu servidor. Para começar, gere algum conteúdo para o contêiner hospedar:
- No host do Docker, dentro da pasta ~/containers/caddy, crie uma nova pasta chamada files.
2. Crie um novo arquivo chamado index.html dentro da pasta ~/containers/caddy que se parece com abaixo. Esta será a página principal que o servidor web Caddy servirá.
3. Modifique seu arquivo de configuração do Docker Compose para se parecer com abaixo. O arquivo de exemplo abaixo está adicionando a seção volumes
e apontando uma montagem de vínculo para a pasta files recém-criada para torná-la disponível para o contêiner.
4. Execute docker-compose up -d
novamente. O Docker Compose agora reconhecerá que o arquivo foi alterado e recriará seu contêiner.
5. Navegue até a página do contêiner com um navegador e você deverá ver que está servindo a página “Hello World!”.
Você pode ver o seguinte na animação abaixo:

Você está agora hospedando conteúdo armazenado localmente na sua máquina! No entanto, e se o seu conteúdo estiver em uma fonte externa como um compartilhamento de rede?
Usando Docker Compose com Volumes do Docker
Depois de criar um contêiner simples com o Docker Compose, provavelmente você precisará que esse contêiner acesse arquivos em outro lugar, talvez em um compartilhamento de rede. Se for o caso, você pode configurar o contêiner para usar volumes do Docker diretamente no arquivo de configuração do Docker Compose.
Para fins de demonstração, este guia criará um servidor de compartilhamento de arquivos de rede (NFS) no host Docker. O uso de conteúdo local como um ponto de montagem NFS não tem objetivo prático além da demonstração. Normalmente, se você for montar um volume NFS, será de uma fonte externa, como um NAS ou servidor remoto.
Configurar um Compartilhamento NFS
Se você ainda não tiver um compartilhamento NFS configurado, configure um agora no host Docker para este tutorial. Para fazer isso:
- Instale o pacote do servidor NFS executando
apt install nfs-kernel-server -y
.
2. Adicione o contêiner como uma exportação NFS (semelhante a um compartilhamento CIFS do Windows) executando o seguinte.
3. Agora verifique se o host expõe o compartilhamento NFS executando showmount -e localhost
. Este comando mostrará qualquer compartilhamento NFS atualmente exposto e quem tem acesso.
Na captura de tela abaixo, você pode ver /home/homelab/containers está exposto, mas apenas para o computador localhost (que é o mesmo servidor que executa o host Docker).

Se você vir a pasta /home/<username>/containers na saída, o compartilhamento NFS está configurado.
Definindo um Volume Nomeado Docker
Depois de criar o compartilhamento NFS, agora você precisa informar ao Docker como acessar esse compartilhamento. Usando o Docker Compose, você pode fazer isso definindo um volume nomeado no arquivo de configuração do Docker Compose.
A named volume is a way for Docker to abstract network-based file shares. Network file sharing comes in all sorts of shapes and sizes these days: CIFS (windows) shares, NFS (Linux) shares, AWS S3 Buckets, and more. By creating a Named Volume, Docker does the hard part of figuring out how to talk to the network share and lets the container just treat the share as if it is local storage.
Para criar um volume nomeado:
- Abra o arquivo de configuração do Docker Compose (docker-compose.yaml). Se você estiver seguindo junto, o arquivo deve estar localizado na pasta ~/containers/caddy.
2. Dentro do arquivo de configuração do Docker Compose, adicione uma seção volumes
após a seção services
. Seu arquivo de configuração deve ser semelhante ao abaixo. A seção volumes
cria um volume nomeado chamado MyWebsite. Dentro desse volume nomeado, os parâmetros necessários (como IP, configurações NFS e caminho) são especificados. O parâmetro volumes
dentro da seção services
também é modificado para apontar para o volume nomeado em vez de uma pasta local.
3. Depois de definir o volume nomeado apontando para o compartilhamento NFS no arquivo de configuração do Docker Compose, execute docker-compose up -d
para criar e iniciar o contêiner. Se tudo correr bem, o contêiner e o site deverão ser iniciados novamente.

4. Navegue novamente para a página do contêiner. O conteúdo do arquivo index.html deve aparecer como se o arquivo estivesse sendo montado localmente. No entanto, esse arquivo está sendo montado através do servidor NFS configurado na rede.

Agora que você pode montar volumes externos do Docker no Docker Compose, pode trazer todos os tipos de armazenamento em rede para seus contêineres. No entanto, o Docker Compose pode fazer mais do que apenas definir contêineres ou volumes únicos. Vamos mergulhar em cenários mais complexos e multi-contêineres.
Este tutorial não usará mais o contêiner caddy, então você pode removê-lo usando
docker-compose down
.
Definindo Múltiplos Contêineres no Docker Compose
A maioria dos contêineres Docker não funciona isoladamente. Os contêineres Docker geralmente têm dependências de serviço como bancos de dados ou serviços web separados que se comunicam por meio de uma API.
Usando o Docker Compose, você pode agrupar contêineres definidos dentro de um único arquivo. Ao definir múltiplos contêineres em um único arquivo, os contêineres podem se comunicar entre serviços dependentes e simplificar a organização de layouts de contêineres complexos.
Para demonstrar tal cenário, vamos configurar uma aplicação wiki popular chamada BookStack.
O BookStack é um software wiki popular conhecido por sua facilidade de uso e layout hierárquico (ao contrário de um layout plano, como o mediawiki).
O BookStack, assim como muitas aplicações web, requer um banco de dados separado para funcionar corretamente, bem como as informações necessárias para se comunicar com o banco de dados. Configurar tal situação é onde o Docker Compose se destaca.
Crie o Arquivo de Configuração do Docker Compose
O BookStack não possui uma imagem Docker mantida internamente; no entanto, linuxserver.io mantém uma respeitável imagem Docker em nome do BookStack. Embora a documentação no site do Docker Hub tenha um arquivo de configuração recomendado para o Docker Compose, este tutorial criará um novo arquivo de configuração, explicando os conceitos.
No host Docker:
- Primeiro, crie uma pasta para o BookStack. Se você seguiu os tutoriais da seção anterior, deverá ter uma pasta ~/containers. Crie uma pasta chamada bookstack lá.
2. Em seguida, crie um arquivo de configuração em branco para o Docker Compose chamado docker-compose.yaml dentro da pasta bookstack.

3. Agora abra o arquivo de configuração do Docker Compose e defina dois containers: o container bookstack
e o container bookstack_db
(mariadb).
Até agora, este arquivo docker-compose.yaml está principalmente utilizando conceitos já introduzidos: Você tem dois serviços (bookstack
e bookstack_db
), ambos com imagens e vinculações de montagem. O contêiner do bookstack possui um mapeamento de porta do host 8080 para a porta interna 80.
Dada a sobrecarga extremamente baixa dos contêineres do Docker, é prática comum definir um contêiner de banco de dados separado para cada aplicativo da web. Fazê-lo permite uma maior separação de responsabilidades. Isso é nitidamente diferente das configurações tradicionais de banco de dados, onde uma única instalação de banco de dados pode atender a centenas de aplicativos da web.
Uma nova opção que você pode ver no arquivo acima é o comando depends_on
. Este comando informa ao Docker a ordem em que os contêineres devem iniciar. Definir o comando depends_on
diz ao Docker que o contêiner bookstack_db
deve iniciar primeiro.
Configurando a Comunicação entre Contêineres com Variáveis de Ambiente
Este arquivo de configuração construído na última seção ainda não está completo. Embora você tenha definido dois serviços (contêineres), eles não estão se comunicando entre si! O contêiner bookstack
não tem ideia de como se comunicar com o contêiner bookstack_db
. Vamos resolver isso usando variáveis de ambiente.
As variáveis de ambiente são a maneira mais comum de fornecer variáveis para contêineres do Docker. Essas são variáveis fornecidas durante a execução (ou definidas no arquivo de configuração docker-compose.yaml) para fornecer informações sobre o que o contêiner precisa fazer.
As variáveis de ambiente são definidas pela pessoa que cria a imagem Docker. Elas serão diferentes dependendo da imagem Docker que você está utilizando, e é necessário consultar a documentação do criador sobre quais variáveis de ambiente usar.
Existem dois métodos para definir variáveis de ambiente; diretamente no arquivo docker-compose.yaml ou como um arquivo separado.
A separate file is, typically, the recommended method, especially if variables contain sensitive data such as passwords. A docker-compose.yaml file is designed to be shared or even uploaded to a public-facing GitHub repo. Having a separate file for sensitive data reduces the chance of an accidental security breach.
No host Docker, crie agora duas variáveis de ambiente; uma para o contêiner bookstack e outra para o contêiner bookstack_db.
- Crie um novo arquivo na pasta ~/containers/bookstack chamado bookstack.env com o seguinte conteúdo:
2. Crie um novo arquivo na pasta ~/containers/bookstack chamado bookstack_db.env e inclua o seguinte conteúdo:
3. Como prática recomendada, certifique-se agora de que ambos os arquivos env não sejam legíveis por outros usuários.
Você deve alterar o acesso de leitura porque ambos os arquivos bookstack.env e bookstack_db.env contêm dados sensíveis.
4. Atualize o arquivo Docker Compose ~/containers/bookstack/docker-compose.yaml para fazer referência a esses dois arquivos de ambiente conforme mostrado abaixo.
5. Agora, inicie os contêineres bookstack e bookstack_db usando o Docker Compose.
Você pode ver cada um dos passos mencionados nesta seção realizados no VS Code abaixo.

Monitorando os Logs do Docker Compose
O mecanismo do Docker trabalha em conjunto com o Docker Compose para realizar diversas tarefas em segundo plano. Poder monitorar o que está acontecendo, especialmente ao trabalhar com vários containers ao mesmo tempo, é útil.
Para monitorar o container do bookstack, por exemplo, utilize o comando logs
. Neste tutorial, assim que você visualizar os logs exibindo [services.d] done
, você pode acessar a URL do bookstack.


Nesta etapa, você deve ter um wiki totalmente funcional em execução dentro do seu próprio container, com seu próprio banco de dados, completamente dentro do Docker!
Enquanto você tiver as pastas bookstack e bookstack_db, poderá recriar seu ambiente do bookstack do zero.
Docker Compose e Networking
Até este ponto, você não aprendeu muito sobre a comunicação e aspectos de rede de como os containers trabalham juntos. Vamos mudar isso.
Ao criar vários containers dentro de um único arquivo docker-compose.yaml, como feito nas seções anteriores, todos eles são atribuídos à mesma rede (geralmente chamada de nome-da-pasta-pai_default).
Você pode visualizar a rede criada para os containers quando executa o comando docker-compose up -d
, conforme mostrado abaixo.

Quando todos os containers são atribuídos à mesma rede, o Docker cria entradas DNS para eles internamente. Por isso, no exemplo anterior, você se referiu ao seu banco de dados como bookstack_db
nas variáveis de ambiente. Esse nome bookstack_db
é, na verdade, uma entrada DNS que aponta para o endereço IP do container do banco de dados.
Você também não precisa depender do Docker Compose para autogerar redes para você. Você pode definir manualmente redes internas ou externas. Definir manualmente redes é ótimo quando você tem um contêiner que precisa se comunicar com outro contêiner em um arquivo docker-compose.yaml separado. Você pode expor as portas, ou pode criar uma rede à qual ambos podem se juntar!
Observe que quando você começa a definir redes explicitamente, também precisa definir explicitamente a rede padrão. O Docker Compose deixará de criar essa rede automaticamente assim que você começar a definir as redes
Agora modifique o docker-compose.yaml do bookstack para incluir uma rede criada externamente.
- Crie a rede externa com
docker network create my_external_network
.
2. Defina a rede externa em docker-compose.yaml:
3. Execute docker-compose up -d
para recriar os contêineres. Seus dois contêineres agora estão conectados a duas redes, como mostrado abaixo.

O contêiner do bookstack agora está também conectado a uma rede definida externamente. Isso permite que você crie outro contêiner que transforme o tráfego HTTP do bookstack em HTTPS antes de sair do Docker (referido como um reverse-proxy).
Configurando um Usuário Específico para Executar um Contêiner
Por padrão, todos os contêineres Docker são executados como um usuário root em um ambiente isolado. Isso é equivalente a rodar uma máquina virtual logada como o usuário Administrador padrão. Embora isso geralmente não seja um problema, existem preocupações de segurança se o isolamento for comprometido.
Outro problema de rodar como root são as permissões de arquivo. Você pode notar que se tentar deletar a pasta db dentro da pasta bookstack, você na verdade não conseguirá; o conteúdo é de propriedade do root.
Embora a maioria das imagens não aprecie ser executada como um usuário não-root, as imagens do linuxserver.io em particular oferecem uma variável de ambiente para definir o usuário que roda dentro do contêiner. Você pode fazer isso adicionando UID=1000
e GID=1000
na configuração bookstack.env.
1000:1000 é o ID de usuário e grupo padrão para o primeiro usuário no ubuntu (o que você pode não ser). Você pode ler mais sobre IDs de Usuários e Grupos em Relacionado: Um Cara do Windows em um Mundo Linux: Usuários e Permissões de Arquivos)
Você também pode forçar um UID e GID usando o parâmetro
user
no docker-compose, mas isso não é recomendado, pois a maioria dos contêineres não se comporta bem quando forçado a um usuário diferente
Configurando a Política de Reinício
Se você deseja que os contêineres construídos com o Docker Compose reiniciem em caso de falha, utilize a política de reinício adicionando a restart
policy e incluindo o parâmetro restart: <option>
nas configurações do contêiner no arquivo docker-compose.yaml.
Ao adicionar esse parâmetro, os contêineres serão reiniciados automaticamente em caso de falha, ajudando a manter a disponibilidade em caso de problemas inesperados de energia.
Configurando manualmente entradas DNS para contêineres
Assim como no Windows e Linux, o Docker também possui um “arquivo hosts”. Usando o parâmetro extra_hosts em um arquivo de configuração, você pode forçar um host a resolver para um IP específico. Isso pode ser útil quando há restrições de DNS, como DNS dividido ou um servidor de teste com o qual você deseja interagir temporariamente.
Executando Comandos
Depois que o contêiner é iniciado, você pode executar comandos dentro dele usando o docker-compose run
. Por exemplo, talvez você queira iniciar um terminal Bash dentro do seu contêiner bookstack. Para fazer isso, execute o comando abaixo.
Conclusão
Neste ponto, você deve ter informações suficientes para acompanhar a maioria dos tutoriais de docker-compose disponíveis na web. Ter esse conhecimento pode ampliar significativamente sua capacidade de ingressar no mundo do Docker e na construção de aplicativos web com Infrastructure as Code.
Source:
https://adamtheautomator.com/docker-compose-tutorial/