Introdução ao Docker: O Guia Completo para Iniciantes
O que é o Docker?
Docker é uma ferramente de containerização, ele cria containers para facilitar o processo de desenvolvimento de forma rápida e eficiente. Mas, o que é um container?
Containers são unidades executáveis de software que empacotam o código junto com suas bibliotecas e dependências, ou seja, eles virtualizam apenas os recursos que eles necessitam, sem emular um novo hardware como VMs fazem. Por isso, containers são tão leves, eles são apenas o suficiente para uma aplicação funcionar.
Agora, igual para criar VMs usamos ferramentas como Virtualbox, para criar containers usamos o Docker.
Como utilizar o Docker?
Primeiro, é preciso instalar o Docker no seu sistema. Os processos de instalação podem ser encontrados no site oficial. Para instalar no Ubuntu, escreva os comandos no terminal:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Estrutura do Dockerfile
Com o Docker instalado, agora precisamos criar um container. Para criar um container, precisamos de dizer pro Docker o que nos queremos que o container contenha e para isso usamos o Dockerfile
. Por exemplo:
FROM python:3.12.3-slim-bullseye
WORKDIR /usr/local/projeto_multitrilionario
COPY backend/requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY backend/src .
CMD [ "python3", "-m", "flask", "run", "--host=0.0.0.0"]
Esse pequeno Dockerfile, tem uma função bem simples: baixar o python em um sistema, baixar o código fonte de um programa e instalar as dependências e hospedar um servidor com Flask. A estrutura de um Dockerfile sempre é parecida com isso:
Síntaxe | Descrição |
---|---|
FROM <imagem> |
Baixa uma imagem base do sistema operacional, contendo apenas o essencial para funcionar, como Kernel e algumas ferramentas. |
WORKDIR /caminho/desejado |
Define um diretório de trabalho dentro do container, facilitando operações como cópia e movimentação de arquivos. Semelhante ao cd no Linux. |
COPY <origem> <destino> |
Copia arquivos ou diretórios do host para o container. A origem é relativa ao local onde o Dockerfile está armazenado. |
RUN <comando> |
Executa comandos dentro do container durante o processo de build. Geralmente utilizado para instalação de pacotes ou compilação de código. |
CMD ["comando", "arg1", "arg2", ...] |
Define o comando principal que será executado quando o container iniciar. Se este comando falhar, o container será encerrado. |
Construindo e Executando
Agora, com todos os comandos principais definidos, devemos construir o container:
sudo docker build -t "projeto_multitrilionario" .
Nessa fase, qualquer problema em relação a instalação do container, como uma imagem não localizada, um comando RUN falhando, um arquivo no host que não foi localizado, será relatado e, consequentemente, falhará. Isso pode demorar alguns minutos, mas é um tanto legal ver a velocidade que ele executa cada instrução e as barras progredindo.
Se este comando for executado com sucesso, você poderá ver que a imagem está instalada com o comando:
sudo docker image list | grep "projeto_multitrilionario"
Agora, só falta executar o container:
sudo docker run -d --name "container_multitrilionario" "projeto_multitrilionario"
Pode parecer um comando um pouco assustador, mas é simples. Primeiro, o comando run do docker tem a seguinte sintaxe:
docker run [ARG] <imagem>
Onde [ARG]
é opcional e guarda informações de como executar o container, especificar espaços no disco, quantidade de memória utilizar, configurar redes e guardar o nome da máquina. Agora, a <imagem>
é necessário e esta será a imagem que foi construída anteriormente pelo docker build, no caso: "projeto_multitrilionario"
.
O argumento -d
(ou --detach
) fala ao Docker executar o container em segundo plano, o que permitirá você fazer outras tarefas no host ou, até mesmo, entrar no container usando o shell. O argumento --name
diz que você quer nomear esse container. É bom dar nomes significativos aos containers, como "db-mongo"
para o banco de dados do mongo.
Docker Compose - Aumentando o horizonte
Agora que você aprendeu a criar um container com o Dockerfile
e executar ele com o docker run
, é hora de você aprender a usar o Docker Compose.
O Docker Compose é uma ferramenta que permite você trabalhar com mais de um container de forma extremamente rápida e escalável. Ele usa de uma estrutura simples de se aprender e utiliza os conceitos do Dockerfile
anteriormente.
Para utilizar o Compose precisamos antes dizer como queremos utilizar os Containers. Para isso, usamos o arquivo compose.yaml
:
# compose.yaml
services:
backend:
build:
dockerfile: .
environment:
- DB_HOST=db
- DB_PORT=27017
- UPLOAD_FOLDER_FLASK=/usr/local/projeto_multitrilionario/uploads
ports:
- "5000:5000"
links:
- "db:db"
develop:
watch:
- action: sync
path: ./backend/src
target: /usr/local/projeto_multitrilionario/
db:
image: "mongo:latest"
ports:
- "27017:27017"
restart: unless-stopped
networks:
- net
volumes:
- mongo_data:/data/db
networks:
net:
driver: bridge
volumes:
mongo_data:
Ok, eu sei que é muita coisa. Mas vai tudo fazer sentido, eu espero… Vamos dar nome aos bois antes:
services:
backend:
...
db:
...
O services
especifica quais serviços (containers) vamos utilizar. Você pode perceber que tem dois nomes (tags
) que estão abaixo do services
, estes são os containers, ou melhor, o nomes deles, ao menos. O Compose irá ler isso e entender que você quer criar dois containers com os nomes backend
e db
. Mas, ele só sabe disso ainda… Agora, para entender de fato o que o container será vamos olhar em baixo das tags dos containers:
...
db:
image: "mongo:latest"
ports:
- "27017:27017"
restart: unless-stopped
networks:
- net
volumes:
- mongo_data:/data/db
Cada tag embaixo de db
fala ao Compose como queremos que funcione o container db
. Igual no Dockerfile
, que baixamos uma imagem, o Compose define que o container db
terá a imagem mongo
na versão mais recente (mongo:latest
). A tag ports
modifica algo que não tinhamos visto anteriormente. O Compose identifica que nos queremos que o container tenha portas expostas para o host e cada linha com hifén em baixo de ports
diz um par de portas. Neste caso, o Compose irá exportar a porta 27017
no container e deixará acessível ao host como 27017
(tem a sintaxe <container>:<porta para host>
).
A tag restart
indica ao Compose quando que o container deve ser reiniciado. Caso um container morra por conta de um erro (que pode ser causado no CMD
da imagem) o Compose pode querer reiniciá-lo. O valor "unless-stopped"
diz ao Compose manter ligado o container db mesmo que o CMD
falhe. Isso pode ser útil para fazer testes.
O networks
indica ao Compose quais redes usar. Aqui rede é como se fosse sua rede local (LAN) onde você pode acessar outros dispositivos usando o IP local. Nesse caso, o Compose irá utilizar uma rede já definida com o nome net
(como se fosse uma outra rede local). Isso se torna útil caso você queira que um container possa conversar com outro.
E por último, volumes são pontes entre o container e um diretório no host. Mais informações podem ser consultadas no documentação.
Agora, se torna mais fácil entender que o container backend é um container com alguma imagem, que expõe a porta 5000
no container e 5000
no host. Mas ainda nos resta enteder o build
, develop
, environment
e links
.
...
build:
dockerfile: .
A tag build
diz ao Compose que devemos construir uma imagem. Para construir a imagem, ele deve achar um Dockerfile
que está no diretório .
(mesmo diretório da onde ele for lançado). Então, se você executar o Compose no diretório Downloads/projeto_multitrilionario
ele irá procurar o Dockerfile ai dentro. Simples, não?
...
environment:
- DB_HOST=db
- DB_PORT=27017
- UPLOAD_FOLDER_FLASK=/usr/local/projeto_multitrilionario/uploads
A tag environment
é um pouco diferente. Se você já definiu uma varíavel de ambiente no Linux com export VAR=VALUE
você já deve ter sacado. O Compose, quando lê a tag environment
define uma série de variáveis do ambiente, que podem ser utilizadas no programa para esconder segredos ou configurar a execução do projeto.
...
links:
- "db:db"
Essa você já devia estar esperando. A tag links
serve para linkar o atual container (backend
no caso) com outro container, na mesma rede. Nesse caso, ele está deixando disponível o container db
para o container backend
com o nome db
(a sintaxe é <nome do container>:<hostname para o container>
). Desse jeito, é possível acessar o container db
igual acessamos o google.com
(só digitar esse endereço e o sistema o achará). Isso facilita muito o desenvolvimento, uma vez que podemos separar a lógica do programa de um banco de dados e um front-end e permitir acessos com hostnames invés de IPs.
...
develop:
watch:
- action: sync
path: ./backend/src
target: /usr/local/projeto_multitrilionario/
Essa é apenas uma utilidade. A tag develop.watch
monitora mudanças em diretórios e, se houver alguma mudança desde a última vez que o container foi lançado, ela pode sincronizar a mudança com o container novamente. Nesse caso, quando a pasta ./backend/src
no host mudar, o Compose copiará as mudanças necessárias para a pasta /usr/local/projeto_multitrilionario/
no container.
networks:
net:
driver: bridge
Agora, saímos das definições do container e estamos configurando a rede como um todo. O Compose irá criar, assim que ver essa definição, uma nova rede com o nome net
e deixará disponível para ser utilizada pelos containers. Desse jeito, você pode linkar vários containeres juntos, como vimos anteriormente, de uma forma simples e rápida.
Como executar o Docker Compose
Agora que você já tem um Dockerfile pronto, um compose.yaml pronto só falta executar isso tudo. Para isso, só execute:
sudo docker compose up -d
Simples, não? O Docker Compose irá ler o compose.yaml
, ver que precisa criar os containers, buildar um Dockerfile
que está na mesma pasta de lançamento, configurar os volumes e redes para usar, configurar o ambiente de desenvolvimento, baixar umas imagens e pronto. Agora, seus containers estão todos ligados e em segundo plano (-d
). Para ver os registros deles você pode escrever:
sudo docker compose logs
Para ver todos os logs. Agora, para ver de um container apenas:
sudo docker compose logs db
Caso você queira parar todos eles:
sudo docker compose stop
E é isso, espero que esse artigo seja útil para você. Tente adicionar o Docker em um projeto seu, talvez você goste do que .devcontainers pode te oferecer :)