Começando bem com Docker
Published: 2023-02-21 22:24:12


 1. Introdução

 2. Arquitetura do Docker

 3. Instalação do Docker

 4. Imagem

 5. Container

 6. Port Bind

 7. Networks

 8. Volumes

 9. Docker Registry

 10. Docker compose

 11. Boas práticas

 12. Desinstalar o Docker

# 1. Introdução

## 1.1. Stacks para fazer deploy da aplicação

**Stack tradicional com vm**

hardware → hypervisor → virtual machine → operating system → application

* Na criação de um hardware virtual o kernel é isolado e cada vm tem o seu próprio kernel.

Desvantagem:

* Desperdício de hardware

* Difícil gerenciamento de libs — Várias aplicações usando a mesma lib pode gerar problema. Atualizações de lib feita pelo próprio OS podem quebrar a aplicação

* Não é imutável

**Stack com container**

hardware → operating system → container engine (docker) → container 1 / container 2 / containter 3

* Docker foi feito para ser usado no linux porque usa primitivas do kernel linux para construir um container (chroot, cgroups)

* O Docker é instalado como um pacote do linux

* O container utiliza o mesmo kernel do OS (docker host) e isola as aplicações usando o conceito de namespaces.

* Os containers são pedaços lógicos isolados compartilhando o mesmo kernel.

Vantagens:

* Otimização de recursos hardware

* Empacotamento da aplicação — Acaba com o problema de “funciona na minha máquina mas não funciona na sua”

* Imutabilidade — Por conta do empacotamento a mesma aplicação pode rodar na minha máquina, no servidor, em cloud e onde mais quiser porque a aplicação não muda, é um objeto.

* Facilidade no deploy

### 1.2. Containers

* Abstração a nível de sistema operacional

* Agrupa códigos, bibliotecas e dependências para garantir a execução de um aplicativo em um ambiente isolado

* Parece com as máquinas virtuais mas as máquinas virtuais são uma abstração a nível de hardware com a simulação de servidores dentro de um servidor com um sistema operacional completo.

* Containers (MB) são mais leves que VMs (GB).

* Os containers não precisam de um SO completo.

* Os containers compartilham o mesmo kernel do SO onde estão sendo executados. São como um processo do SO.

Vantagens do uso:

* Ambiente isolado

* Idempotência

* Distribuição facilitada

Desvantagens:

* Escalabilidade

* Resiliência

Ferramentas container:

* Docker

* Podman

* ContainerD

## 1.3. Docker

* Tecnologia e plataforma open source

* Para VMs temos o Virtualbox e para containers temos o Docker

* Otimização muito mais eficiente dos recursos

* Inicialização muito mais rápida que as VMs

* Importante para trabalhar numa arquitetura de sistemas distribuídos com microsserviços.

* Microsserviços são serviços independentes que se comunicam entre si. Uma vantagem de utilizar o microsserviços é utilizar a multilinguagens (um java e outro em node).

* Usando o Docker para microsserviços a gente garante um ambiente isolado com as dependências específicas para aquele serviço para serem executados em qualquer ambiente ou em qualquer servidor.

* Facilidade de escalar os containers, os serviços de acordo com a demanda. Facilidade de expandir a aplicação.

* Orquestração de containers: Docker Swarm e Kubernetes

## 1.4. Virtualização X Containerização

![](https://cdn-images-1.medium.com/max/2000/0*gmyG9wQo4UYBWq5T)

![](https://cdn-images-1.medium.com/max/2000/0*WKklXXACETlp1x4Z)

![](https://cdn-images-1.medium.com/max/2000/0*773f_6xu3AiRtI0K)
>  Container = combinação de tecnologias do kernell do linux (cgroups + namespaces + overlayfilesystem)

# 2. Arquitetura do Docker

![](https://cdn-images-1.medium.com/max/2000/0*jD9N5y8oYZ8cOSCi)

## **2.1. Elementos da arquitetura**

**Docker Daemon** — responsável por executar os containers e gerenciar todos os objetos do docker

**Docker Client** — ferramenta de interação com Docker Daemon

**Docker Registry** — repositório de imagens docker. dockerhub, azure container registry, elastic container registry

**Client**

* O client é a linha de comando

* $ docker ps $ docker pull $ docker run

* O client se comunica diretamente com o docker daemon do docker host. É possível configurar a linha de comando da minha máquina para se comunicar com outro docker host como o docker host que está na aws, azure, na empresa via vpn, etc.

**Docker Host**

* No docker host existe um processo chamado docker daemon que faz a lógica de gerenciar as imagens.

* Quando baixamos uma imagem o docker daemon coloca em /var/livre/docker

**Registry**

* Como uma store das imagens docker

* Servidor remoto que tem imagens para o docker daemon baixar.

* [https://hub.docker.com/](https://hub.docker.com/)

* Empresas possuem registry privado como o Nexus, Jay Frog ou um provedor de cloud.

![](https://cdn-images-1.medium.com/max/2000/0*Dod-kDy5g5wz8Qwc)

# 3. Instalação do Docker

Aqui apresento duas possibilidades para instação do Docker.

Opção 1- Seguir a documentação para instalação no Ubuntu [https://docs.docker.com/engine/install/ubuntu/](https://docs.docker.com/engine/install/ubuntu/)

**Erro de falta de permissão**

![](https://cdn-images-1.medium.com/max/2000/0*duWApQMtZHhVgaJZ)

**Para corrigir**

Depois que o Docker é instalado ele cria um grupo. É necessário adicionar o usuário ao grupo Docker.

Para acessar todos os grupos do linux cat /etc/group

![](https://cdn-images-1.medium.com/max/2000/0*W-9nBAKUUkukqIP7)

Para adicionar o usuário ao grupo sudo usermod -a -G docker <usuário>

![](https://cdn-images-1.medium.com/max/2000/0*yg0N3X2LkqgRVN6K)

Reiniciar o sistema

![](https://cdn-images-1.medium.com/max/2000/0*-Gz2oXGUPdgfFIlg)

Opção 2- Seguir para instalação do docker engine diretamente no wsl2 [https://github.com/codeedu/wsl2-docker-quickstart#docker-engine-docker-nativo-diretamente-instalado-no-wsl2](https://github.com/codeedu/wsl2-docker-quickstart#docker-engine-docker-nativo-diretamente-instalado-no-wsl2)

# 4. Imagem

**Imagem:**

* Base para executar o processo com o container

* Template do que vai ser executado em um container

* Filesytem necessário para executar o container: Executável da aplicação, Instalação da aplicação, Pacotes necessários para executar a aplicação.

![](https://cdn-images-1.medium.com/max/2000/0*Bzc8h2XLcvF5s52F)

## Comandos

docker image ls - lista as imagens

docker image rm $(docker image ls -aq) - remove todas as imagens

docker image prune - remove as imagens que não estão sendo mais utilizadas

docker build -t <nome da imagem> -f Dockerfile . - cria uma imagem baseada no Dockerfile. Se o Dockerfile estiver no diretório atual não é necessário usar o parâmetro *-f.* ex. *docker build -t ubuntu-curl -f Dockerfile .* ou *docker build -t ubuntu-curl .*

docker build -t <nome da imagem> . --no-cache - cria uma imagem sem o cache. ex. *docker build -t ubuntu-curl . --no-cache*

## Padrão do nome da imagem

![](https://cdn-images-1.medium.com/max/2000/0*GiHgxBBCepCyO2jz)

* Na criação da imagem é necessário informar a tag.

* Durante o uso do Docker quando a tag não é inserida é utilizado a tag latest da imagem.

    docker build -t igorspestana/rotten-potatoes:v1 -f Dockerfile .

![](https://cdn-images-1.medium.com/max/2000/0*r8gzV2oQpc41Xdej)

![](https://cdn-images-1.medium.com/max/2000/0*u-_64jly0OHwKEeT)

## Cache

**Imagem**

* Sistema de arquivo em camadas

* A cada execução de um parâmetro é criada uma nova camada. Criando uma pilha de camadas.

* As camadas criadas na imagem são apenas de leitura (read).

**Container**

* O container cria uma camada de leitura e escrita (read and write) sobre a camada de leitura (read) de uma imagem pronta.

* O container é rápido de ser criado e jogado fora porque é apenas uma camada.
>  Pesquisar sobre o overlay file system para entender o modelo de camadas.
>  Quando o docker cria uma container ele usa uma imagem como referência. Dessa forma não é criado uma imagem toda vez que um container novo é criado e isso gera uma economia de recursos, otimiza a construção da imagem e o upload da imagem. O Docker cria um cache para armazenar os dados das imagens de referencia e é reutilizado para cada container.

![](https://cdn-images-1.medium.com/max/2000/0*WB9yfFaOTz1XU_My)

![](https://cdn-images-1.medium.com/max/2000/0*A-R7m221N4ByXSwp)

## Dockerfile

Receita para criar imagem.

Processo: Dockerfile → Imagem → Container

* Arquivo de texto enviado para o Docker com as instruções para criar uma imagem.

* Alguns statements:

* From: a partir de qual imagem será criado. imagem base com binário e pip. Workdir: Run: comandos que vou executar dentro da imagem. instalar um pacote, permissão de execução para um arquivo, mover um arquivo, etc. comandos executados antes de subir a aplicação. Expose: porta que está exposta. onde a aplicação está recebendo dados. Copy: copiar arquivos de onde esta rodando o docker e vai jogar para dentro da imagem. Env: declarar variáveis de ambiente Cmd: qual comando será executado quando eu der um docker run, quando iniciar o container a partir da imagem. comando para subir a aplicação.

**Statements**
>  *FROM -> Inicializa o build de uma imagem a partir de uma imagem base.*
>  *RUN -> Executa um comando.*
>  *LABEL -> Adiciona metadados a imagem.*
>  *CMD -> Define o comando e/ou os parâmetros padrão.*
>  *EXPOSE -> Define que o container precisa expor a porta em questão.*
>  *ARG -> Define um argumento para ser usado no processo de construção.*
>  *ENV -> Define variáveis de ambiente.*
>  *ADD -> Copia arquivos ou diretórios ou arquivos remotos e adiciona ao sistema de arquivos da imagem.*
>  *COPY -> Copia arquivos ou diretórios e adiciona ao sistema de arquivos da imagem.*
>  *ENTRYPOINT -> Ajuda a configurar um container que pode ser executado como executável.*
>  *VOLUME -> Define volumes que devem ser definidos.*
>  *WORKDIR -> Define o seu diretório corrente.*

## Exemplo 1 — Criando uma imagem Ubuntu a partir de um Dockerfile

**1- Criando o Dockerfile**

Dockerfile

    FROM ubuntu
    RUN apt update
    RUN apt install curl --yes

![](https://cdn-images-1.medium.com/max/2000/0*lV6-69yaMb0QWsLU)

    FROM ubuntu
    RUN apt update && \\
    		apt install curl --yes
    		apt install vim --yes

    FROM ubuntu
    RUN apt update && \\
    		apt install curl vim --yes
>  Importante manter o apt install junto do apt update no mesmo comando.

**2- Criando a imagem**

    docker build -t ubuntu-curl .

![](https://cdn-images-1.medium.com/max/2000/0*nA03-kCZYPMa22Vy)

# 5. Container

docker container ls -a - mostra os containers criados. *-a* parâmetro que mostra também os containers que não estão em execução.

![](https://cdn-images-1.medium.com/max/2000/0*ePyW6wMmELyDlot7)

docker <container id> - mostra as informações do container

docker container run <imagem> - inicia o container. ex. *docker container run ubuntu*

docker container stop <container id> - interrompe um container em execução.

docker container start <container id> -i - inicia um container existente. *-i* parâmetro que executa o container de forma interativa. ex. *docker container start f06a0ce6cd43 -i*

![](https://cdn-images-1.medium.com/max/2000/0*pNIUypU-Klu-K_oT)

docker container run -it ubuntu /bin/bash - inicia o container. *-i* parâmetro que executa o container de forma interativa. *-t* parâmetro que habilita o tty.

![](https://cdn-images-1.medium.com/max/2000/0*Dlc37OMr2oBCFIFZ)

docker container run -e <variável de ambiente> <imagem> - inicia o container. *-e* parâmetro de variável de ambiente. ex. *docker container run -e MONGO_INITDB_ROOT_USERNAME=mongouser -e MONGO_INITDB_ROOT_PASSWORD=mongopwd mongo*

docker container rm <container id> - remove um container.

docker container rm $(docker container ls -aq) - remove todos os containers.

docker container rm -f $(docker container ls -aq) - remove todos os containers. -f remove mesmo se estiverem em execução

docker inspect <container id> - mostra informações do container ex. *docker inspect 60037a544848*

# 6. Port Bind

![](https://cdn-images-1.medium.com/max/2000/0*lwyLULLA4MUmZAv3)

O port bind vincula uma porta local a uma porta do container. Dessa forma não é necessário acessar o container para só então acessar a aplicação. Na imagem ao acessar a porta local 8080 é feito um vínculo com a porta 27017 onde é possível acessar o processo que está rodando nessa porta.

docker container run -p <porta local>:<porta do container> - inicia o container. *-p* parâmetro que define o vínculo da porta local com a porta do container.

## Exemplo 1 — Criando um servidor http Nginx.

![](https://cdn-images-1.medium.com/max/2000/0*-SZf8e7awSQIPsol)

![](https://cdn-images-1.medium.com/max/2000/0*2QJtnQa7xXW0d949)

## Exemplo 2 — Criando um gerenciador de banco de dados PostgreSQL.

**1- Criando o container**

Para acessar um banco de dados é necessário definir usuário, senha e definir um banco de dados. Nesse caso a criação do container precisa especificar variáveis de ambiente.

Para ler a documentação: [https://hub.docker.com/_/postgres](https://hub.docker.com/_/postgres)

![](https://cdn-images-1.medium.com/max/2000/0*_5VKhzhAF5KL3ESG)

![](https://cdn-images-1.medium.com/max/2000/0*lQ38wRW_-s5LN966)

**2- Configurando o DBeaver**

Para download do client de db: [https://dbeaver.io/download/](https://dbeaver.io/download/)

![](https://cdn-images-1.medium.com/max/2000/0*-gWHrmcappHoclpU)

Exemplo 3 — Criando um gerenciador de banco de dados MongoDB.

![](https://cdn-images-1.medium.com/max/2000/0*hQ-1Rj9l6C_CgsEM)

# 7. Networks

## IP

Todo container tem um IP e pode ser visto usando o comando *docker container inspect <container id>:*

![](https://cdn-images-1.medium.com/max/2000/0*9Kl24y1o4ubEp4y8)

## Exemplo

Criação do container usando as variáveis do mongo e referenciando o ip 172.17.0.2

docker container run -d -p 5000:5000 -e MONGODB_HOST=172.17.0.2 -e MONGODB_USERNAME=mongouser -e MONGODB_PASSWORD=mongopwd igorspestana/rotten-potatoes:v1

![](https://cdn-images-1.medium.com/max/2000/0*akCuMvUM5Br-fFZn)
>  Container não deve ser referenciado por IP porque ele é efêmero e está o tempo todo caindo e subindo um novo com novo IP. A forma correta é usando o Docker Network

## Docker Network

docker network create <nome da network> - cria uma network

![](https://cdn-images-1.medium.com/max/2000/0*qMVeI-z8oW3rQBBj)

## Exemplo

Criação do container usando as variáveis do mongo e referenciando a network

Criando container com network especificada

docker container run -d -p "27017:27017" -e MONGO_INITDB_ROOT_USERNAME=mongouser -e MONGO_INITDB_ROOT_PASSWORD=mongopwd --name mongodb --network=rotten-network mongo

![](https://cdn-images-1.medium.com/max/2000/0*x8fVQDrSTWMGWLhg)

Criação do container usando as variáveis do mongo e referenciando o host mongodb

Criando container com network especificada

docker container run -d -p 5000:5000 -e MONGODB_HOST=mongodb -e MONGODB_USERNAME=mongouser -e MONGODB_PASSWORD=mongopwd --name rotten-potatoes --network=rotten-network igorspestana/rotten-potatoes:v1

![](https://cdn-images-1.medium.com/max/2000/0*wYTGG-MR3R3x5N2A)

# 8. Volumes

![](https://cdn-images-1.medium.com/max/2000/0*7mZAOnbhq1i31uEx)

Existem duas possibilidades para que os dados sejam mapeados: fazendo um bind de diretório ou usando o Docker Engine
>  O volume deve ser especificado no container para que na situação do container com o banco de dados cair e subir um novo os dados continuem populando a aplicação.

## Comandos

docker inspect <volume> - mostra informações do volume como o diretório local de armazenamento

docker volume ls - lista os volumes

docker volume rm $(docker volume ls) - remove todos os volumes

## Bind de diretório

Cuidado quando for trabalhar com volume gerenciado por você mesmo utilizando o bind, fazendo um vínculo de diretório. A melhor forma de trabalhar com o volume é o docker gerenciando o volume com o Docker Engine.

Exemplo:

docker container run -d -p "27017:27017" -e MONGO_INITDB_ROOT_USERNAME=mongouser -e MONGO_INITDB_ROOT_PASSWORD=mongopwd --name mongodb --network=rotten-network -v "$(pwd)/mongodb:/data/db" mongo

![](https://cdn-images-1.medium.com/max/2000/0*Oip2abZPyTbCVfZi)

## Docker Engine

**Criar volume**

docker volume create <nome do volume> - para criar o volume. ex. *docker volume create mongo-vol*

docker volume ls - lista os volumes criados.

![](https://cdn-images-1.medium.com/max/2000/0*_yL5YD08spyykdn4)

**Backup do volume**

![](https://cdn-images-1.medium.com/max/2000/0*eq2yid3Qn1JMUp57)

## 9. Docker Registry

Enviar imagem para o docker registry

### Comandos

docker login - login no docker

docker logout - logout no docker

docker push <imagem> - sobe a imagem para o dockerhub ex. *docker push igorspestana/rotten-potatoes:v1*

docker tag <imagem> <imagem latest> - criar uma imagem com a tag latest. ex. *docker tag igorspestana/rotten-potatoes:v1 igorspestana/rotten-potatoes:latest*
>  Boa prática: enviar a imagem latest

![](https://cdn-images-1.medium.com/max/2000/0*VfqrzNVPfwXdtsZC)

# 10. Docker compose

Receita para criar container

docker network create

docker volume create

docker container run mongo

docker container run app

docker-componse up -d - subir container a partir do compose

docker-compose down - derrubar container a partir do compose

# 11. Boas práticas

Para garantir a idempotência é necessário informar a tag da imagem base. *ex. node:16.15.0 .* Especificando apenas *node* teremos a imagem latest, essa imagem mais atualizada pode ter algum breaking change e comprometer a aplicação. É possível conferir as versões disponíveis no dockerhub.

Nunca use imagens desconhecidas. Use imagens oficiais ou imagens confiáveis.

    FROM node:16.15.0
    WORKDIR /app
    COPY ./package*.json
    RUN npm install
    COPY . .
    EXPOSE 8080
    CMD ["node", "server.js"]

![](https://cdn-images-1.medium.com/max/2000/0*PgealbG4LYIkOEwG)

Usar COPY separado para arquivo das dependências e arquivo da aplicação. A aplicação constantemente sofre alteração e as dependências não, dessa forma aproveitamos o cache das camadas para otimizar a construção da imagem.

    FROM node:16.15.0
    WORKDIR /app
    COPY ./package*.json
    RUN npm install
    COPY . .
    EXPOSE 8080
    CMD ["node", "server.js"]

![](https://cdn-images-1.medium.com/max/2000/0*hahMZD7TSjm8LdFX)

Usar o .dockerignore para que ignorar diretórios específicos na hora de construir imagens.

![](https://cdn-images-1.medium.com/max/2000/0*8Jc2H_h7PcDCArOS)

Quando o comando *npm install roda o diretório node_modules é criado com as dependências especificadas em package-lock.json* e *package.json.*

Usando o *.dockerignore* com esse diretório especificado a criação desse diretório será ignorada no momento que a imagem for criada. **

# 12. Desinstalar o Docker

Para desinstalar completamente o Docker:

Passo 1

    dpkg -l | grep -i docker

Para identificar qual pacote instalado você tem:

Passo 2

    sudo apt-get purge -y docker-engine docker docker.io docker-ce docker-ce-cli docker-compose-plugin
    sudo apt-get autoremove -y --purge docker-engine docker docker.io docker-ce docker-compose-plugin

Os comandos acima não removerão imagens, conteineres, volumes ou arquivos de configuração criados pelo usuário em seu host. Se você deseja excluir todas as imagens, contêineres e volumes, execute os seguintes comandos:

    sudo rm -rf /var/lib/docker /etc/docker
    sudo rm /etc/apparmor.d/docker
    sudo groupdel docker
    sudo rm -rf /var/run/docker.sock