DEV Community

Cadu Magalhães
Cadu Magalhães

Posted on • Updated on • Originally published at blog.cadumagalhaes.dev

Tudo que você precisa saber pra usar Docker

De forma muito simplificada, Docker (a plataforma*) é um software open source que foi feito com o objetivo de facilitar o processo de desenvolver, implantar e executar aplicações. Em resumo, é uma plataforma de virtualização através de contêineres.
Outra forma de resumir é dizer que é um dos softwares que tem o objetivo de evitar o clichê do

Na minha máquina funciona.

* Docker pode ser o projeto, a ferramenta/plataforma, a empresa. Aqui vamos falar sobre a ferramenta

Vou deixar alguns exemplos específicos de Node e Python, mas no geral esse post é feito para quem já sabe desenvolver ou já tem uma aplicação pronta, e quer colocar ela para rodar em contêineres.

Se você for iniciante, talvez alguns pedaços não façam muito sentido. Nesse caso pode deixar um comentário que eu tento explicar da melhor forma possível.

Tudo que eu preciso saber sobre?

Meu objetivo aqui é simples: prover toda informação disponível para você (desenvolvedor) começar e conseguir usar a ferramenta da vez. Não pretendo responder tudo que você pode ou deveria saber, apenas o necessário.

O que é Docker

Mascote do Docker, Moby

Docker é uma plataforma de virtualização por contêiner, que permite empacotar e executar aplicativos de forma isolada do sistema operacional.

Pra complementar, vou deixar um subtópico aqui:

O que são contêineres

Contêineres (ou containers) são uma forma de virtualização de computadores que usa recursos do sistema operacional (SO) hospedeiro, ao invés de precisar fornecer um SO inteiro - como seria uma máquina virtual (VM) - para executar o que precisar. O contêiner compartilha o kernel do SO, e por isso é muito rápido para iniciar e interromper, e altamente portátil. Sua principal vantagem é permitir a execução de aplicações em um ambiente isolado, o que garante que a sua execução em qualquer máquina será a mesma.

Quando eu deveria usar Docker

Desde que eu aprendi a usar, a minha resposta é sempre. Existem alguns aspectos que podem fazer a sua utilização parecer um empecilho, principalmente durante o desenvolvimento, mas vou tentar desmistificar tudo isso e te ajudar a usar da forma mais simples que conheço.

Mas respondendo de forma mais completa, os principais motivos para usar contêineres (na minha opinião) são:

1. Fazer uma aplicação com objetivo de publicar

Para resolver uma lista de exercícios de lógica de programação da faculdade usando contêineres, seria um pouco de exagero (mas é possível).

Mas toda vez que você estiver trabalhando em uma aplicação - um site, ou uma api por exemplo - que eventualmente vai ser publicada de alguma forma, encapsular em contêineres vai facilitar muito a sua vida na hora de fazer a implantação (ou deploy).

Isso porque o isolamento garante que todas as dependências utilizadas - e aqui podemos considerar pacotes (do npm ou do pip, por exemplo), programas disponíveis no sistema operacional ou até arquivos - estarão disponíveis quando chegar a hora da implantação.

2. Padronizar o ambiente de desenvolvimento

O motivo é o mesmo acima, basicamente. Caso você trabalhe em mais de um computador (eu uso o meu notebook pessoal com linux e o notebook da empresa com windows, por exemplo), ou simplesmente esteja desenvolvendo algo em equipe, conteinerizar a sua aplicação vai facilitar muito a configuração inicial do ambiente para começar a desenvolver sem gastar horas instalando programas e pacotes específicos.

Por onde começar

Instalação

No meu post sobre a configuração do windows para desenvolvimento eu já expliquei como se instala no Windows, por isso não vou repetir aqui (o link já está com a âncora do pedaço sobre Docker).

No caso de Linux, para instalar basta executar os comandos abaixo:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
Enter fullscreen mode Exit fullscreen mode

Isso vai baixar o script de instalação oficial do docker e em seguida executar. Esse script não funciona para instalação no WSL, pois o script identifica que estamos no Windows e recomenda que você instale usando o Docker Desktop.

Dica: caso queira instalar em algum servidor de forma automática (com terraform, por exemplo), você pode executar o segundo comando como mostrado abaixo. Isso vai garantir que não fique parado no prompt de confirmação

yes | sudo sh get-docker.sh
Enter fullscreen mode Exit fullscreen mode

Dockerfile

A base de conteinerizar aplicações está na construção do Dockerfile. Esse arquivo pode ser bem complexo, mas o que você precisa saber é:

FROM

Logo no inicio do seu arquivo, o primeiro comando precisa ser o FROM. Ele é o ponto de partida da sua imagem, e define qual é a base do seu container.

Uso:

  • FROM <image>
  • FROM <image>:<tag>

Por exemplo, se eu usar

FROM python:3.11
Enter fullscreen mode Exit fullscreen mode

Vou ter uma imagem que já vai vir com python instalado, além de uma série de dependências comuns de aplicações em python.

De forma semelhante, poderia usar FROM node:20.3-slim para inicializar uma aplicação em Node JS.

O seu Dockerfile pode ter vários comandos FROM, juntando pedaços de imagens diferentes para montar a sua aplicação.

Uma coisa que você precisa se atentar, é que as imagens* utilizadas vão ter tamanhos diferentes, dependendo da quantidade de coisas que tem instaladas. Imagens mais completas são mais pesadas, e a maioria das imagens comuns tem uma versão -slim , que geralmente vai ter menos dependências.

De forma geral, se você estiver preocupado com o tamanho da imagem do seu container, o ideal seria começar com uma imagem mais “limpa” e instalar manualmente todas as ferramentas que vai utilizar. Mas normalmente não precisamos nos preocupar com isso, então usar a imagem -slim já é um bom começo.

COPY

O próximo passo - considerando que a imagem que escolhemos já tem os programas que precisamos - é passar os arquivos com o seu código para ela, para que possam ser executados. Podemos fazer isso usando o comando ADD, ou o comando COPY. O primeiro é basicamente o segundo com esteróides, pois além de permitir a cópia, também permite o download de arquivos remotos (através de uma URL) e a descompressão de arquivos comprimidos.

Uso:

  • COPY arquivo
  • COPY [arquivo_fonte.py](http://arquivo.py) arquivo_destino.py
  • COPY pasta_fonte/ pasta_destino/

Isso vai definir onde os seus arquivos vão ser copiados dentro da imagem do contêiner, e é necessário para que possamos executar. Um exemplo comum, no caso de uma aplicação em python por exemplo, é copiar o requirements.txt além da pasta com o seu código (no meu exemplo, app/

COPY requirements.txt
COPY app
Enter fullscreen mode Exit fullscreen mode

Voltando para o Node, vou aproveitar e apresentar o .dockerignore. Semelhante ao .gitignore, a função dele é definir no seu projeto (ou pasta) quais arquivos ou diretórios não devem ser acrescentados na imagem. Node é um bom exemplo porque quando instalamos as dependências do nosso projeto (de um package.json, por exemplo), elas são salvas na pasta node_modules/, dentro do diretório em que estivermos. Essa pasta pode ser muito grande, por isso podemos criar o arquivo .dockerignore contendo ela:

node_modules/
Enter fullscreen mode Exit fullscreen mode

E depois, quando fizermos a cópia, saberemos que essa pasta não estará na nossa imagem

COPY meu_projeto_node
Enter fullscreen mode Exit fullscreen mode

RUN

Em seguida, dependendo da sua aplicação vai ser necessário instalar as dependências do seu projeto para conseguir executar (por isso copiamos o requirements.txt, por exemplo). Para isso usamos o comando RUN, que serve para executar qualquer comando de script dentro da sua imagem. Você pode ter múltiplos comandos RUN, e eles vão ser executados no momento da criação da sua imagem.

Uso:

  • RUN comando
  • RUN comando complemento
  • RUN ["comando", "complemento"]

Para instalar as dependências de um projeto de python por exemplo, podemos colocar o seguinte após o nosso COPY

RUN pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

Esse comando também pode ser utilizado para instalar pacotes no seu sistema (com apt install por exemplo) ou executar qualquer comando de shell.

CMD

Por fim, também podemos configurar comandos que devem ser executados quando o container for executado. É aqui que dizemos como a nossa aplicação deve ser executada, por exemplo.

Uso:

  • CMD comando
  • CMD comando complemento
  • CMD ["comando", "complemento"]

Numa aplicação python simples por exemplo, podemos executar algo como

CMD python app.py
Enter fullscreen mode Exit fullscreen mode

Ou numa aplicação em Node, podemos executar

CMD npm start
Enter fullscreen mode Exit fullscreen mode

Um ponto importante sobre o CMD, é que somente o último comando inserido no seu Dockerfile vai ser executado. Por exemplo, se tivermos

CMD python main.py
CMD python app.py
Enter fullscreen mode Exit fullscreen mode

Somente o segundo será executado.

Caso sua aplicação precise executar mais de um comando para ser inicializada, podemos usar o comando ENTRYPOINT, que funciona de forma semelhante.

Comandos docker

build

Dentro da pasta da sua aplicação onde você criar o seu arquivo Dockerfile (normalmente são colocados na raiz do projeto, mas vai depender da sua organização), você deve executar o comando

docker build [PATH]
Enter fullscreen mode Exit fullscreen mode

Caso não passe nenhum PATH, ele procurará o Dockerfile no diretório atual. Se estiver em outra pasta, basta passar o seu caminho (algo como docker build container/

run

Esse é o comando principal para utilização de docker, e não vou entrar em muitos detalhes, mas a sintaxe básica é:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
Enter fullscreen mode Exit fullscreen mode

As opções mais comuns que eu costumo utilizar são:

  • —name: vai definir o nome do seu container. Se não for passado, vai criar com um nome aleatório
  • --volume, ou -v: permite conectar um diretório local com um diretório do container
  • --expose, ou -p: permite expor e mapear portas do seu container para o seu sistema local. Isso é muito importante caso esteja desenvolvendo serviços web
  • -e: permite passar variáveis de ambiente para o contexto do seu container

Acessando o seu container

Apesar de ser mais um comando, gosto de separar este pois permite que tenhamos acesso ao exato contexto do nosso container.

docker exec -it [container] bash
Enter fullscreen mode Exit fullscreen mode

Executando esse comando, entraremos num shell interativo do nosso container e que nos permite acessar como qualquer terminal Linux. Dentro deles podemos navegar entre os arquivos, editar e executar comandos. Isso pode ser muito útil caso precise debugar algum problema, mas o caso de uso mais comum para mim foi fazer a manutenção de containeres baseados em imagens prontas, para criar um usuário de uma aplicação por exemplo (dentro do container de Airflow).

Publicando a sua imagem

Esse tópico sozinho poderia ser um post a parte, por isso vou tentar resumir.

Depois de construir a nossa imagem, nós podemos disponibilizar ela em algum repositório de imagens. Isso facilita a implantação, pois a partir do momento em que a imagem está construida, você não precisa mais dos arquivos do código fonte para executar. Existem vários repositórios, mas minhas recomendações são:

Registros públicos

  • Docker Registry: o repositório oficial permite que você poste suas imagens e deixe acessíveis para qualquer pessoa
  • Github Container registry: também é possível armazenar suas imagens no Github

Registros privados

  • Docker hub: A Docker Inc (a empresa, não o produto) também oferece um serviço de hospedagem privado para salvar suas imagens
  • Qualquer nuvem, mas como exemplo vou destacar o Artifact Registry do Google Cloud

Dessa forma, quando for executar o seu serviço, basta referenciar o endereço remoto da imagem na hora da execução. Por exemplo:

docker run ghcr.io/1cadumagalhaes/TESTE_DOCKER:latest
Enter fullscreen mode Exit fullscreen mode

Isso significa que, caso eu queira fazer o deploy da minha aplicação numa VM hospedada em alguma nuvem por exemplo, tudo que eu preciso fazer é:

  1. Instalar o Docker
  2. Executar esse comando referenciando a minha imagem já existente

Sem se preocupar em clonar o código e sem nenhum setup.

Docker Compose

O Compose é uma ferramenta desenvolvida pra definir e executar aplicações que dependem de múltiplos contêineres docker. Com ele, você usa um arquivo de configuração YAML para definir os serviços da sua aplicação, e depois executa tudo com um único comando.

Apesar de ser focado em aplicações com múltiplos contêineres, eu considero muito prático usar para definir aplicações de um único contêiner também. Isso porque ele permite que nós façamos as definições do nosso serviço de forma declarativa. Por exemplo, digamos que eu esteja trabalhando no desenvolvimento de uma API feita em Python e que vai criar arquivos dentro do sistema, e que usa algumas configurações passadas via variável de ambiente. Para executar o meu container, eu precisaria fazer o seguinte comando:

docker run \
-v ./output:/app/output \
-p 8080:8080 \
-e NAMESPACE='staging' \
-e LOG_LEVEL='debug' \
-e API_KEY='abc123' \
--name meu_python \
.
Enter fullscreen mode Exit fullscreen mode

Claro que podemos salvar tudo isso num shell script (ou simplesmente decorar o comando), mas eu considero inconveniente. Ao invés disso, podemos definir um docker-compose.yaml assim:

services:
  meu_app:
    buid: .
    ports:
      - "8080:8080"
    environment:
      - NAMESPACE: 'staging'
      - LOG_LEVEL: 'debug'
      - API_KEY: 'abc123'
    volumes:
      - ./output:/app/output
Enter fullscreen mode Exit fullscreen mode

E depois de configurar esse arquivo, podemos executar nossa configuração com

docker compose up
Enter fullscreen mode Exit fullscreen mode

Também podemos usar os comandos down e restart além do up, e podemos passar várias opções, como:

  • -f : Caso o nome do seu arquivo seja diferente de docker-compose.yaml , podemos passar através dessa opção
  • -d : para executar os contêineres “desacoplados”, ou seja sem depender da sua sessão do terminal e ficar em background.

Usando Hot Reload com Docker

Muitas vezes, quando estamos desenvolvendo precisamos da agilidade de reiniciar o serviço assim que salvamos nossas alterações.

No ambiente local existem ferramentas próprias para isso, como o nodemon de Node JS, e elas podem auxiliar muito para que vejamos os resultados das nossas alterações.

Por padrão, para alterar códigos dentro do seu contêiner, você precisa alterar a sua imagem. E para isso, é necessário compilar a imagem (docker build) e executar novamente (docker run).

Caso você queira usar uma ferramenta de hot/live reload, você pode definir um volume conectando o seu diretório local com o diretório do contêiner, e dessa forma as alterações estarão conectadas. Por exemplo, caso esteja construindo uma API em Python (com FastAPI por ex) e executando com Gunicorn, podemos fazer o seguinte:

No Dockerfile

FROM tiangolo/uvicorn-gunicorn-fastapi:python3.11
COPY requirements.txt /usr/src/requirements.txt
RUN pip install -r /usr/src/requirements.txt
COPY ./app /app/app
Enter fullscreen mode Exit fullscreen mode

No docker-compose.yaml

services:
    api:
        build: .
        ports:
           - '8080:80'
        volumes:
           - ./app/:/app/app
        command:
           - /start-reload.sh
Enter fullscreen mode Exit fullscreen mode

Dessa forma, quando fizermos qualquer alteração no nosso código, o serviço será reiniciado dentro do contêiner. Caso queira fazer o mesmo com Node, esse post do Erick Wendel no freeCodeCamp explica como fazer essa configuração.

Onde encontrar mais informações

Além das documentações oficiais, a principal referência que eu tenho é o treinamento da LINUXtips, feito pelo Jeferson Fernando. Ele disponibilizou completamente de graça o livro Descomplicando o Docker depois de dar o treinamento de graça pra 5 mil pessoas (incluindo eu, na época um jovem padawan estagiário):

E depois ele resolveu liberar o treinamento todo completamente de graça no youtube.

Esse treinamento é MUITO completo e tem toda informação que você vai querer saber, então vamos combinar de ir lá no canal dele e deixar a Inscrição e deixar o like nos videos?

Conclusão

Coloquei aqui tudo que eu precisei pra conseguir trabalhar com docker nos últimos meses, do desenvolvimento do 0 até o deploy do serviço em produção. Essa é uma ferramenta muito poderosa e que tem muitos detalhes que eu não cobri aqui, por isso reforço que de uma moral para os materiais da LINUXtips.

Se você não sabia nada sobre docker e não conseguiu usar depois desse guia, me procura no twitter pra gente tentar deixar esse post mais completo.

E caso esse post tenha te ajudado de alguma forma, compartilha com alguém que você acha que também pode aproveitar.

Me diz o que você achou desse tipo de post, e se você gostaria que isso virasse um vídeo no Youtube! Independente do resultado, o post da semana que vem dessa semana será sobre Terraform. Me acompanha no twitter pra saber mais!

Espero que isso tenha sido útil para alguém, até a próxima!

Top comments (3)

Collapse
 
vcwild profile image
Victor Wildner

conteúdo completo da base, parabens!

Collapse
 
brunobarross profile image
Altamiro Bruno

Excelente!

Collapse
 
simonardelima profile image
Simonarde Lima

obrigado por compartilhar!