Começando bem com Docker
Igor Pestana
Posted on February 21, 2023
Introdução
Arquitetura do Docker
Instalação do Docker
Imagem
Container
Port Bind
Networks
Volumes
Docker Registry
Docker compose
Boas práticas
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
Container = combinação de tecnologias do kernell do linux (cgroups + namespaces + overlayfilesystem)
2. Arquitetura do Docker
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.
Empresas possuem registry privado como o Nexus, Jay Frog ou um provedor de cloud.
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/
Erro de falta de permissão
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
Para adicionar o usuário ao grupo sudo usermod -a -G docker
Reiniciar o sistema
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
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.
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 -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 . --no-cache - cria uma imagem sem o cache. ex. docker build -t ubuntu-curl . --no-cache
Padrão do nome da imagem
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 .
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.
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
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 .
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.
docker - mostra as informações do container
docker container run - inicia o container. ex. docker container run ubuntu
docker container stop - interrompe um container em execução.
docker container start -i - inicia um container existente. -i parâmetro que executa o container de forma interativa. ex. docker container start f06a0ce6cd43 -i
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.
docker container run -e - 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 - 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 - mostra informações do container ex. docker inspect 60037a544848
6. Port Bind
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 : - 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.
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
2- Configurando o DBeaver
Para download do client de db: https://dbeaver.io/download/
Exemplo 3 — Criando um gerenciador de banco de dados MongoDB.
7. Networks
IP
Todo container tem um IP e pode ser visto usando o comando docker container inspect :
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
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 - cria uma network
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
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
8. Volumes
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 - 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
Docker Engine
Criar volume
docker volume create - para criar o volume. ex. docker volume create mongo-vol
docker volume ls - lista os volumes criados.
Backup do volume
9. Docker Registry
Enviar imagem para o docker registry
Comandos
docker login - login no docker
docker logout - logout no docker
docker push - sobe a imagem para o dockerhub ex. docker push igorspestana/rotten-potatoes:v1
docker tag - 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
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"]
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"]
Usar o .dockerignore para que ignorar diretórios específicos na hora de construir imagens.
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
Posted on February 21, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.