Docker Compose – Explicado

Docker Compose – Explicado

Como foi visto em nosso post anterior sobre o Docker [1], para realizar a execução de um contêiner basta executar o comando docker container run, porém, e se for necessário executar vários contêineres de uma única vez e cada um com um propósito distinto? Seria muito trabalhoso executar diversas vezes o mesmo comando e ainda incluir os parâmetros necessários, além da criação da rede e volumes, se necessário.

É aí que entra o Docker Compose, uma ferramenta instalada separadamente do Docker Engine mas que também faz parte oficialmente do projeto Docker. O Docker Compose é utilizado justamente para facilitar o provisionamento e gerenciamento de multi-contêineres principalmente em ambientes de desenvolvimento, testes automatizados ou cenários de execução em um único host, bastando apenas um arquivo YAML com as instruções e parâmetros desejados para os nossos contêineres e que com um único comando conseguimos realizar a execução e/ou atualização de todos eles. Ele ainda realiza o isolamento do ambiente de um conjunto de contêineres separando-os por nome de “projeto”: por padrão é dado ao projeto o nome do diretório onde este está sendo executado, porém é possível atribuir um outro utilizando o parâmetro -p.

Comandos do Docker Compose

O Docker Compose possui alguns comandos a serem utilizados para garantir toda essa facilidade no provisionamento e gerenciamento dos contêineres, vejamos então os principais:

  • docker-compose up: cria e inicia os contêineres;
  • docker-compose build: realiza apenas a etapa de build das imagens que serão utilizadas;
  • docker-compose logs: visualiza os logs dos contêineres;
  • docker-compose restart: reinicia os contêineres;
  • docker-compose ps: lista os contêineres;
  • docker-compose scale: permite aumentar o número de réplicas de um contêiner;
  • docker-compose start: inicia os contêineres;
  • docker-compose stop: paralisa os contêineres;
  • docker-compose down: paralisa e remove todos os contêineres e seus componentes como rede, imagem e volume.

Estrutura do arquivo

Como já dito, para executar nossos contêineres com o Compose é necessário possuir um arquivo YAML que contenha todas as informações e parâmetros que desejamos passar para a execução deles. Por padrão, os comandos docker-compose… procuram um arquivo no diretório corrente nomeado como docker-compose.yml, porém é possível indicar um outro nome de arquivo e também em um outro local passando o parâmetro -f. Abaixo podemos observar um exemplo de um arquivo que contém a estrutura dos principais parâmetros utilizados para a execução dos contêineres via Compose:


version: '3.8'

networks:
  web_network:
    driver: overlay

volumes:
  site_conf:

services:
  web-server:
    image: httpd:latest
    ports: "80:80"
    deploy:
      placement:
        constraints:
          - "node.role==worker"
      mode: replicated
      replicas: 2
      resource:
        cpus: "0.50"
        memory: 256M
    restart: always
    networks:
      - web_network
    volumes:
      - /dir/site/html:/var/www/html
      - site_conf:/etc/httpd

Parece um pouco confuso ao primeiro olhar, mas posso garantir que tudo isso faz muito sentido! Abaixo iremos detalhar esses principais parâmetros utilizados no exemplo para entender melhor sobre o que cada um deles é responsável. É muito importante ficar atento a endentação das configurações no documento, e caso você não esteja familiarizado com o tipo de arquivo YAML, uma endentação incorreta pode invalidar toda configuração passada no arquivo.

Conceito de serviços

Observando o conteúdo do arquivo YAML mostrado acima, podemos observar que há um bloco de configuração denominado services pois o Docker Compose trata todos os contêineres que desejamos executar como serviços e é dessa forma que devemos referenciá-los no arquivo de configuração. Tratando os contêineres dessa forma, o Compose consegue atribuir o mesmo conjunto de configurações para todos os contêineres que fizerem parte deste serviço, por exemplo:

  • No caso do arquivo acima temos um parâmetro (que iremos detalhar mais adiante) chamado “replicas”, que serve para informar a quantidade de contêineres “idênticos” que desejamos em nosso ambiente, assim, o Compose irá criar a quantidade de contêineres informada e todos com a mesma configuração, identificando-os de uma forma parecida com esta: nome-do-projeto_web-server.1, nome-do-projeto_web-server.2, nome-do-projeto_web-server.n.

O nome do serviço pode ser variado, geralmente se atribui um nome que corresponda a função daquele serviço, mas não há uma regra para a criação dos nomes.

build e image

Podemos ver no arquivo que também há uma opção image, essa opção deve ser utilizada para cada serviço que declararmos dentro do arquivo, pois é com o valor desta opção que o Docker entenderá qual imagem de contêiner deve ser utilizada para a construção do contêiner.

É possível ao invés de utilizar a opção image, utilizar a opção build onde informaremos o contexto e o arquivo (Dockerfile) que possui as instruções para realizar o build de uma imagem para ser utilizada.

Também podemos combinar as duas opções, onde a opção build continuará exercendo seu papel de fornecer os argumentos necessários para o build de uma nova imagem e a opção image será utilizada apenas para informar qual será o nome dado à imagem que será construída bem como a sua respectiva tag.

ports e expose

Estas opções fazem referência às portas que serão utilizadas para acessar os serviços providos dentro do contêiner, onde a opção ports é utilizada para informar a porta do sistema hospedeiro que receberá as requisições e para qual porta deve encaminhar estas requisições para dentro do contêiner, respectivamente como visto no arquivo modelo acima.

Já a opção expose pode ser utilizada para informar quais portas estarão abertas para receber requisições naquele contêiner, porém, trata apenas das redes a qual este contêiner faz parte: é muito utilizada quando queremos que apenas os serviços se comuniquem entre si para consumir ou encaminhar requisições.

deploy

Há diversos parâmetros que podem ser utilizados neste setor. As configurações da opção deploy determinam as regras para a execução do serviço e como mostrado no exemplo, temos os principais argumentos utilizados, onde podemos informar em quais nodes de um cluster este serviço poderá ser executado, o método de execução, quantidade de réplicas e limite de recursos computacionais que serão utilizados pelo serviço.

restart e restart_policy

Com estas opções conseguimos definir o comportamento desejado em caso de queda do contêiner, a ação que deve ser tomada caso o processo responsável pelo contêiner morra. É possível utilizar apenas uma das duas opções por serviço. Na opção restart temos os seguintes argumentos que podem ser utilizados para definir o comportamento:

  • no: caso o contêiner saia do estado de execução, o compose não tentará executá-lo novamente;
  • always: utilizada para que sempre que o contêiner sair do estado de execução, independentemente da causa, o Compose execute-o novamente;
  • on-failure: irá tentar executar o contêiner novamente somente caso o código de retorno resulte em uma falha;
  • unless-stopped: sempre irá garantir que o estado do contêiner seja em execução, ao menos que este seja colocado em estado stopped manualmente ou por algum outro motivo;

Já a opção restart_policy é utilizada dentro do setor deploy, onde conseguimos passar algumas informações adicionais como quantidade de tentativas para iniciar o contêiner, tempo de espera para tentar iniciá-lo novamente, tempo máximo decorrido para executar as tentativas de iniciar o contêiner e a condição que o Compose irá utilizar para tentar executar o contêiner novamente podendo ser escolhidas uma das opções none, on-failure e any.

networks

Nesta opção é onde podemos definir as redes que deverão ser criadas para que os serviços dela façam parte. Para que uma rede seja criada é necessário realizar sua declaração como mostrado no arquivo exemplo, onde passamos o nome da rede a ser criada, o tipo da rede e driver e até mesmo a subnet.
Feita a declaração é necessário referenciar a rede na configuração do serviço para que este faço uso da rede criada.

volumes

Assim como na opção networks, aqui nós devemos fazer a declaração dos volumes que desejamos criar e referenciar estes depois dentro de cada serviço que irá utilizá-lo. Em caso de volumes do tipo bind, onde nós mapeamos diretórios existentes em nosso host hospedeiro, a declaração fora da configuração do serviço não é necessária, basta informar o caminho absoluto ou relativo (de acordo com o contexto passado) do diretório que deve ser refletido e o seu destino dentro do contêiner.

environment e env_file

Com essas opções conseguimos definir variáveis de ambientes para utilizar dentro de nossos contêineres, algumas imagens já possuem variáveis bem úteis que podem ser utilizadas e é importante ler a documentação oficial dela caso esteja baixando do Docker Hub por exemplo.

  • environment: com essa opção é possível passar em formato de lista todas as variáveis de ambiente e seus valores que serão utilizadas no serviço;
  • env_file: nesta opção é possível informar um arquivo que será utilizado como fonte de consulta para configurar as variáveis e este arquivo deve conter uma variável por linha e seu respectivo valor.

depends_on

Podemos informar com esta opção que, para que um serviço seja iniciado, ele depende que outro seja iniciado primeiro, criando uma dependência. Assim o Compose se encarrega de fazer com que o serviço dependente só seja executado após todos os outros serviços declarados aqui estiverem em execução. É um caso de uso realizar a utilização desta opção quando se precisa que um serviço de banco de dados por exemplo, seja provisionado e esteja ‘up’ para que outro serviço possa consumi-lo assim que for executado, caso contrário este não ficará em estado de execução.

command e entrypoint

Utilizando estas opções é possível alterar o processo principal responsável pela execução e função daquele contêiner. Ambos substituem o parâmetro CMD e ENTRYPOINT padrão da imagem utilizada. É preciso cuidado com estas opções e utilizar somente em caso real de necessidade de alteração do processo principal do contêiner, pois caso este processo por algum motivo seja terminado, o contêiner também entrará em estado de ‘exited’.

Demonstração

Algumas distribuições podem possuir o pacote docker-compose em seus repositórios facilitando a instalação através do gerenciador de pacotes, porém a versão quase sempre não estará atualizada de acordo com as releases liberadas mas a vantagem é que, em caso de atualização de versão, o gerenciador de pacotes irá se encarregar de realizar o processo facilmente para você.

Contudo não é nada complicado realizar a instalação e atualização de forma manual, garantindo assim que temos a última release ou a release desejada diretamente do repositório do docker-compose. Aqui veremos então como realizar a instalação de forma manual.

Lembrando que para realizar este laboratório você já deve possuir o Docker instalado e permissão para executá-lo mas caso ainda não tenha verifique o post anterior onde falamos sobre isso.

Instalação

Execute o comando abaixo para realizar o download do binário do docker-compose diretamente do repositório ofical do Github:

sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

Feito isso, vamos garantir que teremos permissão de execução deste binário, o que por padrão no Linux não é dado quando se cria um arquivo novo devido à umask padrão do sistema operacional, assim, execute o comando abaixo:

sudo chmod +x /usr/local/bin/docker-compose

Agora, para que nosso usuário para utilizar este binário de forma simples, sem que seja necessário utilizar o caminho absoluto do arquivo para executar o Compose, iremos criar um link simbólico para um diretório já conhecido na variável $PATH de todos os usuários:

sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Arquivo Compose

Daremos inicio então a criação de nosso arquivo docker-compose, para isso vamos primeiro realizar a criação de um diretório para assim lá dentro realizarmos a construção de nosso ambiente:

mkdir lab-compose ; cd lab-compose

Feito isso vamos dar início aqui à criação do nosso compose file, onde o propósito será iniciar um serviço WordPress e um serviço de banco de dados MySQL, para ter um site acessível. Assim você pode copiar e colar em seu terminal todo o comando a seguir que irá criar o compose file já com o conteúdo necessário para a execução:

cat <<EOF > docker-compose.yml
version: '3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "80:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: 
EOF

Agora com o arquivo criado, basta executar o comando do docker compose para iniciar todos esses serviços com as configurações passadas no arquivo, assim execute em terminal o comando a seguir:

docker-compose up -d

Agora você pode testar em seu navegador se o WordPress já está acessível no endereço local http://127.0.0.1/”, que deverá abrir a tela de configuração do WordPress.
Você também pode realizar atualizações no seu arquivo Compose para modificar as configurações de um serviço e sem a necessidade de ‘derrubar’ todos os serviços para atualizar o ambiente: basta executar o comando up novamente que somente o serviço alterado será atualizado.

Caso queira remover os contêineres e os recursos criados basta executar o comando:

docker-compose down

Onde utilizar?

Os cenários onde podemos realizar a execução do Compose irão depender bastante das necessidades e políticas implantando em seu local de trabalho, porém a maior utilização dele e seu propósito se dá em ambientes de desenvolvimento ou em ambientes onde se tem apenas um ‘node’ que irá executar os contêineres.

Próximos passos

Agora que já entendemos o que é o Compose e como utilizá-lo, o próximo passo é aprender sobre o Docker Swarm, orquestrador de contêineres da Docker e que também faz uso de arquivos de configuração como o Compose para executar todos os serviços de seu cluster.

Então nos vemos no próximo post!

 

Mais informações

[1] Aprenda a criar seu primeiro contêiner Docker: https://blog.4linux.com.br/docker-beginners/

Aprenda criar primeiro container Docker

 

Anterior Conhecendo o kernel Linux pelo /proc (parte 4) – comportamentos da memória virtual
Próxima Gitea básico

About author

Ricardo Caeiro
Ricardo Caeiro 6 posts

Ricardo Caeiro é formado em Redes de Computadores pela BandTec, MBA DevOps Engineering pela FIAP, possui mais de 10 anos de experiência na área da tecnologia da informação, já atuou em diversas frentes da área e hoje realiza consultoria e treinamento de tecnologias de software livre com ênfase em middleware e cultura DevOps.

View all posts by this author →

Você pode gostar também

DevOps

Terraform #parte2 – Alterando sua infraestrutura de forma incremental

Vimos no post anterior uma introdução ao Terraform e como criar de forma prática e simples uma máquina virtual na cloud da Google – GCP, porém não vimos como realizar

DevOps

4Linux faz parceria com exin e cria curso preparatório para certificação Devops master.

Material didático desenvolvido pela 4Linux foi homologado pelo EXIN A 4linux acaba de se tornar um A.T.O. (Authorized Training Organization) do EXIN e simultaneamente teve o material de seu curso

DevOps

Provas “beta-test” acontecerão na sede da 4Linux no dia 3 de setembro de 2017 em São Paulo na Vila Mariana.

O Linux e o mundo open source estão em constante evolução e o LPI trabalha arduamente para garantir que os seus exames de certificação reflitam os mais recentes avanços na