Docker: "funciona na minha máquina"

Docker: "funciona na minha máquina"

Containers são um dos maiores responsáveis por como servimos as aplicações hoje.


O que é o Docker?

Docker é um conjunto de soluções PaaS (Plataforma Como Um Serviço), de código aberto, para servir aplicações através da construção, deploy e gerência de containers.

O que é um container?

Segundo o site oficial do Docker

Containers são unidades padrão de software, que empacotam códigos e todas as suas dependências, para que a aplicação rode de maneira rápida e confiável de um ambiente para outro.

Vamos entender essa definição. Eles são unidades padrão de software, porque sua execução é isolada do sistema operacional, isso permite que ela funcione sem interferências de outros programas, o que a torna mais estável e reprodutível.

Container vs Máquina virtual

Após a definição acima, você talvez esteja pensando: “Não é isso que uma máquina virtual faz?”. Bom, realmente um máquina virtual cria um isolamento do sistema operacional principal, mas ela faz isso virtualizando outros sistemas operacionais, consumindo muito mais recursos em relação aos containers, já que eles funcionam como processos do sistema operacional.

Docker
App 1
nodejs
Motor de container
Sistema Operacional
Kernel
Hardware
App 2
python

Como funciona um container?

O container funciona através da combinação de namespaces, que é uma funcionalidade que permite o isolamento entre grupos de processos em seu nível lógico, com cgroups, que uma funcionalidade de definição de limite e compartilhamento de consumo de recursos por um processo.

Namespaces

  • PID - Isolamento de processos
  • NET - Isolamento de interfaces de rede
  • IPC - Isolamento de comunicação entre processos e memória compartilhada
  • MNT - Isolamento do Sistema de Arquivos / ponto de montagem
  • UTS - Isolamento do kernel (simula outro host)

Cgroups

  • blkio - Implementa bloqueio de entrada e saída
  • acct - Agrupa tarefas usando cgroups e declara o uso de CPU desses grupos
  • sets - Provém um mecanismo de atribuição de CPUs e memória para um conjunto de tarefas
  • devices - Implementa um cgroup para impor restrições a arquivos de dispositivos
  • freezer - Gerencia o estado de execução de uma tarefa, pausando e executando de acordo com a vontade do administrador do sistema
  • hugetlb - Controlador que permite ajustar o uso de páginas de memória virtual maiores, por grupo de controle
  • memory - Controlador que isola o comportamento da memória, de um grupo de tarefas, do resto da sistema
  • net_cls - Provém um interface para marcar pacotes de rede com um identificador de classe
  • net_prio - Provém um interface que permite a um administrador configurar dinamicamente a prioridade de um tráfico rede
  • resource_counter - Deve facilitar o gerenciamento de recursos pelos controladores, provendo ferramentas em comum para contabilização

Container vs Imagem

Container é uma imagem com uma camada temporária de leitura e escrita. Isso permite que a mesma imagem seja reutilizada, alterando somente a camada e leitura e escrita, o que torna o processo do container ainda mais leve.

Container
Camada de Leitura e Escrita
Imagem

Tipos de persistência

Bind mounts

Gerenciados pelo file system

Terminal window
docker run -it -v /volume-dk:/app ubuntu
Terminal window
docker run -it --mount type=bind,source=/home/viniciusflv/Documents/codes/app-exemplo/volume-dk,target=/app ubuntu

Volumes

Gerenciados pelo docker

Terminal window
docker volume create [name]
Terminal window
docker run -it -v meu-volume:/app ubuntu
Terminal window
docker run -it --mount source=meu-volume,target=/app ubuntu

tmpfs

Não é escrito na camada de leitura e escrita

Terminal window
docker run -it --tmpfs=/app ubuntu
Terminal window
docker run -it --mount type=tmpfs,destination=/app ubuntu

Comunicação através de redes

Lista redes

Terminal window
docker network ls

Bridge

Cria um canal de redes

Cria redes

Terminal window
docker network create --driver bridge [netname]

Nomes de redes

Terminal window
docker run -it --name [name] --network [netname] [image] [cmd]

None

Remove a interface de rede

Host

Remove o isolamento da rede do container e sistema

Instalando o docker no linux

Atualizando programas

Terminal window
sudo apt update

Instalando dependências

Terminal window
sudo apt install \
ca-certificates \
curl \
gnupg \
lsb-release

Fazendo o download

Terminal window
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Adicionando a chave de assinatura

Terminal window
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Instalando o Docker CE

Terminal window
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Dando permissão de execução da CLI para o usuário

Terminal window
sudo usermod -aG docker $USER

Executando um container

Para executar um container podemos acessar o Docker Hub, que é um repositório de imagens de código aberto publicadas, e procurar uma imagem. Vou começar escolhendo a imagem oficial do Ubuntu.

Terminal window
docker run ubuntu

Sempre que executar um container o seguinte fluxo irá acontecer:

não
sim
Tem imagem local?
Executa o container
Baixa imagem
Valida o hash

Como foi a primeira execução você vai notar que os seguintes logs aparecerão no seu terminal:

Terminal window
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
e96e057aae67: Already exists
Digest: sha256:4b1d0c4a2d2aaf63b37111f34eb9fa89fa1bf53dd6e4ca954d47caebca4005c2
Status: Downloaded newer image for ubuntu:latest

Também vai notar que nada acontece, mesmo rodando o comando outra vez. Isso é porque o padrão do docker é encerar a execução do container caso não tenha nenhum processo rodando.

Podemos executar um comando do container declarando ele na frente do id da imagem:

Terminal window
docker run ubuntu echo Olá mundo

Listando imagens e containers em execução

Agora que já fizemos o download da nossa primeira imagem podemos listá-las com o comando:

Terminal window
docker images

Para listar os containers em execução vamos primeiro executar o bash em modo interativo:

Terminal window
docker run -it ubuntu bash

E em outro terminal execute o comando:

Terminal window
docker ps

Deletando imagens e containers

Deleta um container

Terminal window
docker container rm -f [container]

Deleta todos os containers

Terminal window
docker container rm -f $(docker container list -q)

Deleta uma imagem

Terminal window
docker image rm -f [image]

Construindo uma imagem

Para construir uma imagem é necessária a criação de um arquivo Dockerfile. Nesse arquivo será declarado uma série de instruções que irão configurar a imagem.

FROM

Todo Dockerfile deve ser precedido pela instrução FROM seguido do id da imagem pai. Ela serve de base para podermos construir nossa camada customizada da imagem.

FROM ubuntu

RUN

Podemos rodar comandos dentro da imagem através da instrução RUN. O resultado desses comandos serão atribuídos a imagem de maneira sequencial.

RUN echo Hello World

LABEL

Adicionam metadados a uma imagem.

LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"

CMD

O propósito principal da instrução CMD é prover argumentos padrões para a execução de um ENTRYPOINT.

CMD ["cd", "./app"]

EXPOSE

Expõe uma porta de execução de uma aplicação.

EXPOSE 3000

ENV

Declara uma variável de ambiente.

ENV PORT=3000

ARG

Define variáveis que usuários podem passar em build-time com o comando docker build através da flag --build-arg <varname>=<value>.

ENV PORT=3000

ADD

Copia arquivos remotos ou compactados e adiciona ao sistema de arquivos da imagem.

ADD hom* /mydir/

COPY

Copia arquivos locais e adiciona ao sistema de arquivos da imagem.

COPY hom* /mydir/

VOLUME

Cria um ponto de montagem de disco, com um nome especifico, e marca ele como montado externamente para outros volumes ou containers.

VOLUME ["/data"]

USER

Instrução que cria um nome de usuário (ou UID), e opcionalmente o grupo do usuário (ou GID), para ser usado como padrão do restante do estágio atual.

USER node

WORKDIR

Instrução que declara o diretório de trabalho para as instruções RUN, CMD, ENTRYPOINT, COPY e ADD.

WORKDIR /path/to/workdir

ENTRYPOINT

Define o comando de entrada do container executável.

ENTRYPOINT ["npm", "run", "dev"]

ONBUILD

Instruções a serem executadas quando a imagem é usada de base para outro build.

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src

STOPSIGNAL

Altera o sinal de parada padrão do docker.

STOPSIGNAL signal

HEALTHCHECK

Instrução que diz ao docker como testar se o container ainda está funcional.

HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1

SHELL

Instrução permite alterar o shell padrão usado para rodar os comandos.

SHELL ["powershell", "-command"]

Subindo uma imagem no docker hub?

  1. Crie sua conta no Docker Hub
npm login
  1. Use o comando abaixo para logar
Terminal window
docker login -u [username]
  1. Crie uma imagem com o nome concatenando o username e o nome da imagem separados por ”/” [username]/[imagename]
Terminal window
docker build username/imagename:1.0.0
  1. Execute o comando de publicação
Terminal window
docker push username/imagename:1.0.0