Desenvolvendo de forma simples e direta, é hoje que você vai começar a usar o Docker. Nada de termos complicados, apenas uma abordagem fácil para que você possa usar containers sem stress. Você provavelmente já deve ter ouvido falar de Docker em requisitos de vagas ou em projetos de algum outro desenvolvedor e com isso tentou usá-lo e não conseguiu ou então não entendeu como usar. Nesse artigo vou te mostrar de maneira simples como é o processo de conteinerização de uma aplicação.
Table of contents
- Objetivo
- Requisitos
- O que é
- CLI do Docker
- Botando a mão na massa
- Compose
- Conteúdo complementar
- Conclusão
Objetivo
Meu principal objetivo é ajudar você leitor com os primeiros passos com Docker no desenvolvimento de seus projetos. Pretendo ter uma abordagem pautada no contexto da aplicação que vou usar, vamos começar explicando as ferramentas de uma forma diferente das que encontrei pela internet, em seguida vou mostrá-las mais a prática, usando uma pequena aplicação como exemplo. Esse artigo conciliado ao conteúdo teórico que vou disponibilizar por links durante e ao final vão te dar uma boa base do que é essa ferramenta e como ela funciona por baixo dos panos, ao fim de tudo eu espero que você seja capaz de dar seus primeiros passos criando container de tudo 🐳.
Requisitos
Ter instalado:
O que é
Dockerfile
É um arquivo de configuração, onde está o passo inicial para a criação de uma Docker Image. Nele contem as instruções para configurar sua aplicação no container. Veja o dockerfile como um manual que lista o passo a passo de como montar algo (sua aplicação no caso) e as instruções de como ligar esse algo na tomada.
Docker Image
A Docker Image é o aglomerado de tudo o que é necessário para sua aplicação funcionar, é onde se encontra o código-fonte, bibliotecas, dependências e mais o que for necessário para que ela funcione. Seu app em uma foto.
Container
Imagine que você queira fazer uma festa independente do local que esteja, e para isso você carrega uma caixa com balões, caixinha de som e luzes coloridas, coisas leves. Pronto sua festa pode ser feita em qualquer lugar, essa caixa é o nosso container, ele reúne tudo o que é necessário para uma aplicação funcionar. Ele resolve aquele famoso problema de "Na minha máquina funciona".
Em outras palavras, os containers são ambientes de software leves, portáteis e isolados que permitem aos desenvolvedores executar e empacotar aplicativos com suas dependências, de forma consistente em diferentes plataformas. Eles ajudam a agilizar os processos de desenvolvimento, implantação e gerenciamento de aplicativos, garantindo ao mesmo tempo que os aplicativos sejam executados de forma consistente, independentemente da infraestrutura.
Volume
Em termos simples, um volume no contexto do Docker é um mecanismo que permite que dados sejam compartilhados e persistam entre o host e os containers. Ele oferece uma maneira de armazenar e compartilhar informações de forma independente do ciclo de vida dos containers.
Por exemplo, se você tiver um container de banco de dados, pode usar um volume para armazenar os dados do banco de forma que esses dados persistam mesmo se o container for removido. Isso proporciona flexibilidade e consistência na gestão de dados em ambientes Docker.
Compose
O Docker Compose é uma ferramenta que simplifica a gestão de aplicações Docker compostas por vários containers. Ele permite definir, configurar e executar vários serviços, redes e volumes em um arquivo YAML
, facilitando a criação e a execução de ambientes complexos com apenas um comando.
CLI do Docker
Existe o Docker Desktop onde você pode fazer a maioria das configurações, ele é até amigável, porem o mais indicado é você se acostumar com os comandos pelo terminal, até por que em caso de qualquer dúvida as respostas que você vai encontrar pela internet na sua grande maioria vão ser em formato de comandos, caso você queria configurar através do aplicativo desktop recomendo você olhar a documentação. Nesse tópico eu pretendo mostrar alguns comandos básicos e fala o que cada um faz.
docker --help
Começando pelo mais simples, mas também muito importante, o --help
mostra todos os comandos do Docker junto a uma pequena explicação, ele também pode ser usado em conjunto com os próximos comandos que vou mostrar no caso de você não se lembrar das opções que aquele comando tem.
Todos os comandos que vou usar são da nova versão feita para fornecer uma experiência de usuário mais estruturada ao usar a linha de comando. Que eu saiba, ainda não houve nenhum anúncio oficial para abandonar o suporte aos comandos antigos (como docker ps
e outros), embora possa ser razoável supor que isso possa acontecer em algum momento no futuro.
Para gerenciar containers:
Criar e executar container:
docker container run nome-da-imagem
Listar containers em execução:
docker container ls
Listar todos os containers (Inclusive parados):
docker container ls -a
Parar um container:
docker container stop nome-ou-ID-do-container
Iniciar um container parado:
docker container start nome-ou-ID-do-container
Remover um container (deve estar parado):
docker container rm nome-ou-ID-do-container
## com a opção '-f' ele força a parada do container e em seguida o remove
Para Gerenciar Imagens:
Listar imagens locais:
docker image ls
Baixar uma imagem do Docker Hub:
docker pull nome-da-imagem
Construir uma imagem a partir de um Dockerfile:
docker build -t nome-da-imagem caminho-do-Dockerfile
Remover uma imagem local:
docker rm nome-da-imagem
Para Gerenciar Redes:
Listar redes Docker:
docker network ls
Criar uma rede Docker:
docker network create nome-da-rede
Conectar um container a uma rede:
docker network connect nome-da-rede nome-ou-ID-do-container
Desconectar um container de uma rede:
docker network disconnect nome-da-rede nome-ou-ID-do-container
Outros comandos úteis:
Logs de um container:
docker logs nome-ou-ID-do-container
Informações Detalhadas de um container:
docker inspect nome-ou-ID-do-container
Botando a mão na massa
Nesse tópico vamos abordar as ferramentas acima citadas um pouco mais prática, e para isso vamos usar uma pequena aplicação com as tecnologias React, Vite,Express e PostgreSQL.
Link do repositório da aplicação🐳
Seu primeiro Dockerfile
No diretório ./front
podemos ver o arquivo .dockerignore
que funcionar parecido com o .gitignore
, nele você define arquivos ou pastas que não são necessárias na imagem da sua aplicação. O arquivo Dockerfile
tem a seguinte estrutura:
FROM node:18.17.0-alpine
WORKDIR /app
COPY package*.json ./
COPY . .
RUN npm install
RUN npm run build
EXPOSE 8080
CMD [ "npm", "run", "preview" ]
FROM
Aqui é o ponto de partida da sua imagem, é onde você define a base do seu container, e para essa aplicação eu estou baixando a imagem do node
na versão 18.17.0-alpine
e quando você ver o alpine
em alguma imagem significa que a imagem foi construída usando Alpine Linux como base. Essas imagens são populares porque são eficientes, rápidas de baixar e fornecem um ambiente leve para executar aplicativos em containers.
WORKDIR
Define o diretório de trabalho dentro do container como /app
. Isso significa que os próximos comandos serão executados dentro deste diretório.
COPY
Copia o arquivo package.json
(e qualquer arquivo que comece com "package") do diretório local para o diretório /app
no container.
O segundo COPY
copia todos os outros arquivos do diretório local para o diretório /app
no container.
RUN
Executa o comando npm install
dentro do container, que instala as dependências especificadas no arquivo package.json
.
O segundo RUN
executa o comando npm run build
para construir a aplicação. Esse comando é geralmente usado para compilar e preparar a aplicação para execução.
EXPOSE
Informa que a aplicação dentro do container estará escutando na porta 8080
. Isso não faz com que a porta seja automaticamente aberta, mas é uma informação útil para quem está usando a imagem para entender qual porta a aplicação espera ser exposta.
CMD
Define o comando padrão a ser executado quando um container baseado nesta imagem for iniciado. Neste caso, o comando é npm run preview
, que inicia o servidor da aplicação para que ela possa ser acessada.
Essas vão ser as instruções mais básicas que vamos usar, existem algumas outras e recomendo você dá uma olhada na documentação para ter uma base melhor.
Configuração do Vite
No Vite para que nossa aplicação rode na porta 8080
é necessário configurar o arquivo vite.config.ts
. Caso você queira abordar outras configurações recomendo ler a documentação do Vite .
Artigo que me ajudou a entender a configuração usada nesse projeto.
Criando a imagem do front
Pronto, com o Dockerfile
preenchido corretamente podemos criar nossa primeira imagem. Se você prestou atenção no tópico CLI do Docker
já deve ter uma ideia de como vamos fazer isso, vá no terminal e acesse o diretório ./front
e execute o seguinte comando:
docker image build -t front .
-
-t
ou-tag
é usado para dar nome e dartag
para a imagem, nesse exemplo estou dando o nome da imagem comofront
caso eu quisesse ter diferentes versões dessa imagem poderia ser usado o segundo argumento para umatag
comofront:nome-da-tag
, geralmente natag
é colocado a versão ou algo que facilite a identificaçãofront:1.0
. - No terminal eu estou no diretório
./front
que é onde está odockerfile
é por isso que só uso o.
(ponto).
Se você presta atenção no seu terminal, vai notar que o Docker esta seguindo as instruções feitas no Dockerfile
.
No gif a build da imagem foi rápido porque boa parte dessa imagem no meu pc já estava em cache, no seu deve demorar mais.
Criando o container do front
Já temos a imagem do front e com ela podemos criar o tão sonhado container, novamente vá no seu terminal e execute o comando:
docker container run -d -p 8080:8080 front
-
-d
: É usado para executar um container em segundo plano, ou seja, em modo "detached". Quando você executa um container Docker, por padrão, ele é iniciado em primeiro plano. -
-p 8080:8080
: essa opção indica que estamos mapeando a porta 8080 do container para a porta 8080 do host. -
front
: é o nome da imagem o container vai usar.
Acessando o http://localhost:8080 em seu navegador já vamos conseguir acessar o front da nossa aplicação, mas ainda falta o nosso back e banco esta no ar.
Criando uma network
O docker network
é um recurso do Docker que permite criar redes isoladas para containers. As redes do Docker desempenham um papel fundamental ao fornecer comunicação entre containers, garantindo isolamento, segurança e flexibilidade nas interações entre os diferentes componentes de um sistema distribuído.
docker network artigo
O nome da rede vai ser
artigo
😁.
Baixando a imagem do Postgres
O próximo passo é baixar a imagem do Postgres a partir do Docker Hub. Para fazer isso, execute o seguinte comando em seu terminal:
docker pull postgres
Criando um volume para persistência de dados
Antes de executar o containers do banco e do back, é importante criar um volume para persistência de dados. Isso garantirá que os dados do Postgres não sejam perdidos quando o container for interrompido ou excluído. Para criar um volume, execute o seguinte comando em seu terminal:
docker volume create pgdata
O nome do volume vai ser
pgdata
.
Executando container Postgres
Com o volume criado, agora você pode executar o container Postgres. Para fazer isso, execute o seguinte comando em seu terminal:
docker container run -d --name postgres --network=artigo -p 5432:5432 -e POSTGRES_USER=postgres -e POSTGRES_HOST=postgres -e POSTGRES_DATABASE=postgres -e POSTGRES_PASSWORD=password -v pgdata:/var/lib/postgresql/data postgres
-
-d
: essa opção indica que o container será executado em segundo plano. -
--name postgres
: com essa opção, você pode dar um nome ao seu container. Neste caso, estamos nomeando o container como "postgres". -
--network
: essa opção é utilizada para especificar a rede à qual um container deve ser conectado durante sua execução. -
-p 5432:5432
: essa opção indica que estamos mapeando a porta 5432 do container para a porta 5432 do host. A porta 5432 é a porta padrão do Postgres. -
-e POSTGRES_USER=postgres
: essa opção define a variável de ambiente POSTGRES_USER com ouser
que você escolher. Por padrão, ouser
do PostgreSQL épostgres
. -
-e POSTGRES_HOST=postgres
: normalmente se refere ao endereço do host onde o banco de dados PostgreSQL está em execução. Se você está executando o PostgreSQL em um container Docker, oPOSTGRES_HOST
pode ser o nome do container ou o endereço IP do container, dependendo de como você está configurando a comunicação. -
-e POSTGRES_DATABASE=postgres
: essa opção é usada para especificar o nome do banco de dados ao qual você deseja se conectar. -
-e POSTGRES_PASSWORD=sua-senha
: essa opção define a variável de ambiente POSTGRES_PASSWORD com a senha que você escolher. -
-v pgdata:/var/lib/postgresql/data
: essa opção define o volume pgdata criado anteriormente como o local onde os dados do Postgres serão armazenados. postgres
: esse é o nome da imagem que estamos utilizando para criar o container.-d
(ou--detach
)-p
(ou--publish
)-e
(ou--env
)-v
(ou--volume
)
Criando a imagem do back
O Dockerfile
que esta no diretório /back
não muda muita coisa do que fizemos no front, apenas o EXPOSE
e o CMD
para fazer o start na aplicação.
FROM node:18.17.0-alpine
WORKDIR /app
COPY package*.json ./
COPY . .
RUN npm install
EXPOSE 3000
CMD ["npm", "run", "start"]
No terminal e acesse o diretório ./back
e execute o seguinte comando:
docker image build -t back .
Criando o container do back
Sem segredo algum, vamos fazer o mesmo processo que fizemos com o container do front
. Já temos a imagem do back e com ela podemos criar o tão sonhado container, novamente vá no seu terminal e execute o comando:
docker container run -d --network=artigo -p 3000:3000 back
Agora nossa aplicação está funcionando completamente, você pode acessar http://localhost:8080 e criar uma tarefa e deletar.
Docker Compose
Podemos notar a quantidade de comandos no terminal que foi necessário para que essa pequena aplicação fosse ao ar, com o compose vamos encurtar todo esse processo colocando tudo em um só lugar, facilitando o nosso desenvolvimento. A estrutura do compose.yaml
lembra um pouco o Dockerfile
.
version: '3'
services:
back:
build: ./back/.
ports:
- 3000:3000
depends_on:
- postgres
front:
build: ./front/.
ports:
- 8080:8080
postgres:
image: postgres:latest
environment:
POSTGRES_USER: postgres
POSTGRES_HOST: postgres
POSTGRES_PASSWORD: password
POSTGRES_DATABASE: postgres
volumes:
- api-postgres-data:/var/lib/postgresql/data
ports:
- 5432:5432
volumes:
api-postgres-data:
-
version: '3'
: Especifica a versão da sintaxe do Docker Compose que está sendo usada. Neste caso, é a versão 3. -
services
: Define os serviços que serão executados.-
back
: Configurações para o serviço chamado "back".-
build: ./back/.
: Especifica que a imagem para o serviço "back" deve ser construída a partir do diretório./back/
. -
ports: - 3000:3000
: Mapeia a porta 3000 do contêiner para a porta 3000 do host. -
depends_on: - postgres
: Indica que o serviço "back" depende do serviço "postgres", garantindo que o serviço "postgres" seja iniciado antes do serviço "back".
-
-
front
: Configurações para o serviço chamado "front".-
build: ./front/.
: Especifica que a imagem para o serviço "front" deve ser construída a partir do diretório./front/
. -
ports: - 8080:8080
: Mapeia a porta 8080 do contêiner para a porta 8080 do host.
-
-
postgres
: Configurações para o serviço chamado "postgres".-
image: postgres:latest
: Usa a imagem oficial mais recente do PostgreSQL disponível no Docker Hub. -
environment:
: Define variáveis de ambiente para configurar o PostgreSQL.-
POSTGRES_USER
,POSTGRES_HOST
,POSTGRES_PASSWORD
, ePOSTGRES_DATABASE
são configurados para criar um usuário, host, senha e banco de dados no PostgreSQL.
-
-
volumes: - api-postgres-data:/var/lib/postgresql/data
: Mapeia um volume chamado "api-postgres-data" para o diretório onde os dados do PostgreSQL são armazenados dentro do contêiner. -
ports: - 5432:5432
: Mapeia a porta 5432 do contêiner para a porta 5432 do host.volumes:
:
-
-
- Define um volume chamado "api-postgres-data". Este volume é usado para persistir os dados do PostgreSQL, permitindo que os dados persistam mesmo se o contêiner for removido.
Antes de executar o comando do Compose finalize os dois containers feitos anteriormente para que não de conflito nas portas. Após isso no seu terminal acesse o diretório raiz do projeto, onde está o compose.yaml
e use o seguinte comando:
docker compose up -d
O processo de build demora um pouco, lembre-se que esse comando faz o build de 3 containers, no gif é nessa velocidade porque o build esta em cash.
Espero que você tenha percebido o quão maravilhoso é o Compose, a quantidade de trabalho que ele te poupa é absurdo. Apenas com um comando ele cria a imagem do front e back, cria um container a partir da imagem criada para o front, back e para o postgres e ainda cria uma network para que os container possam se comunicar entre eles. Existem muitas outras funcionalidades e opções para se fazer no compose, espero que você tenha ficado curioso com o que possa ser feito.
Conteúdo complementar
Tenha a documentação oficial como sua melhor amiga no processo de estudo, mas além dela existe o treinamento da LINUXtips, feito pelo Jeferson Fernando. Ele disponibilizou completamente de graça o:
Livro Descoplicando o Docker
Treinamento completo no Youtube:
Descomplicando Docker
Para quem é muito agoniado, tem o resumo:
Tudo o que você precisa saber sobre docker em 2h
Para quem quer se especializar um pouco mais:
Roadmap Docker (english)
Conclusão
Esse conteúdo foi o resultado de um aglomerado de anotações e abstrações que tive durante meu processo de estudo sobre docker, a parte que mais bati cabeça foi com o network, durante os meus testes eu ficava tentando fazer os container se comunicarem entre eles, mas um não encontrava o outro, foi uma grande dor de cabeça, mas no fim deu tudo certo. Algumas explicações acabei sendo bem raso, mas foi no intuito de tentar facilitar seu entendimento sobre alguns tópicos.
Caso eu tenha feito alguma explicação errada, ficarei agradecido com seu feedback, você pode me encontrar no Twitter ou no Discord da He4rt.
Obrigado por ler ate aqui.