Primeiros passos no Docker
Tags: braziliandevs, docker, beginners, programming
Published: 2024-02-07 12:22:42

Desenvolvendo de forma simples e direta, é hoje que você vai começar a usar o **[Docker](https://www.docker.com)**. 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](https://www.redhat.com/pt-br/topics/cloud-native-apps/what-is-containerization) de uma aplicação.

<img src="https://media.giphy.com/media/FaKV1cVKlVRxC/giphy.gif"/>

# Table of contents

- [Objetivo](#objetivo)
- [Requisitos](#requisitos)
- [O que é](#o-que-é)
	- [Dockerfile](#dockerfile)
	- [Docker Image](#docker-image)
	- [Container](#container)
	- [Volume](#volume)
	- [Compose](#compose) 
- [CLI do Docker](#cli-do-docker)
- [Botando a mão na massa](#botando-a-mão-na-massa)
	- [Seu primeiro Dockerfile](#seu-primeiro-dockerfile)
	- [Configuração do Vite](#configuração-do-vite)
	- [Criando a imagem do front](#criando-a-imagem-do-front)
	- [Criando o container do front](#criando-o-container-do-front)
	- [Criando uma network](#criando-uma-network)
	- [Baixando a imagem do Postgres](#baixando-a-imagem-do-postgres)
	- [Criando um volume para persistência de dados](#criando-um-volume-para-persistência-de-dados)
	- [Executando container Postgres](#executando-container-postgres)
	- [Criando a imagem do back](#criando-a-imagem-do-back)
	- [Criando o container do back](#criando-o-container-do-back)
- [Compose](#compose)
- [Conteúdo complementar](#conteúdo-complementar)
- [Conclusão](#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:
- [Docker](https://docs.docker.com/desktop/)
	- [Windows](https://docs.docker.com/desktop/install/windows-install/)
	- [Mac](https://docs.docker.com/desktop/install/mac-install/)
	- [Linux](https://docs.docker.com/desktop/install/linux-install/)
- [Docker-compose](https://docs.docker.com/compose/install/)

# 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 mascote do docker e o mascote do compose](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vwat4tpctijhxj07va51.png)

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.

```bash
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:**
```bash
docker container run nome-da-imagem
```

**Listar containers em execução:**
```bash
docker container ls
```

**Listar todos os containers (Inclusive parados):**
```bash
docker container ls -a
```

**Parar um container:**
```bash
docker container stop nome-ou-ID-do-container
```

**Iniciar um container parado:**
```bash
docker container start nome-ou-ID-do-container
```

**Remover um container (deve estar parado):**
```bash
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:**
```bash
docker image ls
```

 **Baixar uma imagem do Docker Hub:**
```bash
docker pull nome-da-imagem
```

**Construir uma imagem a partir de um Dockerfile:**
```bash
docker build -t nome-da-imagem caminho-do-Dockerfile
```

**Remover uma imagem local:**
```bash
docker rm nome-da-imagem
```

## Para Gerenciar Redes:

**Listar redes Docker:**
```bash
docker network ls
```

 **Criar uma rede Docker:**
 ```bash
docker network create nome-da-rede
```

**Conectar um container a uma rede:**
```bash
docker network connect nome-da-rede nome-ou-ID-do-container
```

**Desconectar um container de uma rede:**
```bash
docker network disconnect nome-da-rede nome-ou-ID-do-container
```

## Outros comandos úteis:

 **Logs de um container:**
```bash
docker logs nome-ou-ID-do-container
```

**Informações Detalhadas de um container:**
```bash
docker inspect nome-ou-ID-do-container
```

# Botando a mão na massa


![imagem mostrando o caminho do dockerfile até o container](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7970rk04n6dvqf7k8dxs.png)

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](https://pt-br.legacy.reactjs.org), [Vite](https://vitejs.dev),[Express](https://expressjs.com) e [PostgreSQL](https://www.postgresql.org).

[Link do repositório da aplicação](https://github.com/Clintonrocha98/getting-started-with-docker)🐳

## 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:

```Dockerfile
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](https://docs.docker.com/engine/reference/builder/) 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** ](https://vitejs.dev/config/).

> [Artigo que me ajudou a entender a configuração usada nesse projeto.](https://thedkpatel.medium.com/dockerizing-react-application-built-with-vite-a-simple-guide-4c41eb09defa)

## 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: 

```bash
docker image build -t front .
```

- `-t` ou `-tag` é usado para dar nome e dar `tag` para a imagem, nesse exemplo estou dando o nome da imagem como `front` caso eu quisesse ter diferentes versões dessa imagem poderia ser usado o segundo argumento para uma `tag` como `front:nome-da-tag`, geralmente na `tag` é colocado a versão ou algo que facilite a identificação `front:1.0`. 
- No terminal eu estou no diretório `./front` que é onde está o `dockerfile` é 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`.

![gif monstrando os comandos que foram mostrados antes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mysqqy9qosvk4rhn2bvg.gif)
>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:

```bash
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.

![Image com os comandos acima citados](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4t5jy32mfggwi61nxy05.gif)

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. 

![Image do front no ar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8yo41aw7zaim74vnjccd.png)

## Criando uma network


![imagem mostrando um diagrama mostrando a conexão do entre os containers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ptffts98j3o0y14al6f8.png)

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.

```bash
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:

```bash
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:

```bash
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:

```bash
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 o `user` que você escolher. Por padrão, o `user` 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, o `POSTGRES_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. 

```Dockerfile
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: 

```bash
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:

```bash
docker container run -d --network=artigo -p 3000:3000 back
```

![gif com exemplo de uso do comando do exemplo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ogd16c4eqprv6ppyv0fk.gif)

Agora nossa aplicação está funcionando completamente, você pode acessar http://localhost:8080 e criar uma tarefa e deletar. 

# Docker Compose

![imagem mostrando a relação do compose com containers](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z8s776rui35mbzznp7pj.png)

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`.

```yaml
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`, e `POSTGRES_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:

```bash
docker compose up -d
```

![gif com o comando do exemplo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iom5n8izxhkmklr7nxy8.gif)
>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](https://twitter.com/badtux_). Ele disponibilizou **completamente de graça** o:
[Livro Descoplicando o Docker](https://livro.descomplicandodocker.com.br)

Treinamento completo no **Youtube**: 
[Descomplicando Docker](https://www.youtube.com/watch?v=qZevFPMtQho&list=PLf-O3X2-mxDn1VpyU2q3fuI6YYeIWp5rR&index=1&t=0s)

Para quem é muito agoniado, tem o resumo:
[Tudo o que você precisa saber sobre docker em 2h](https://youtu.be/MeFyp4VnNx0)

Para quem quer se especializar um pouco mais:
[Roadmap Docker (english)](https://roadmap.sh/docker)

# 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](https://twitter.com/ClintonRocha_) ou no Discord da [He4rt](https://discord.gg/he4rt).

Obrigado por ler ate aqui.