Skip to main content


Containers Docker


Um container Docker é uma unidade de software que empacota um aplicativo e todas as suas dependências, incluindo bibliotecas e configurações, em um ambiente isolado. Ele garante que o aplicativo funcione de maneira consistente, independentemente do sistema operacional subjacente. Os containers são leves porque compartilham o kernel do sistema operacional do host hospedeiro, ao contrário de máquinas virtuais que incluem um sistema operacional completo.


Os containers são gerenciados pelo Docker Engine, uma plataforma que fornece as ferramentas necessárias para criar, executar e gerenciar containers. O Docker Engine utiliza tecnologias de virtualização baseadas no kernel, como namespaces e cgroups, para garantir o isolamento e o uso eficiente de recursos. Em ambientes maiores, a orquestração de múltiplos containers pode ser feita por ferramentas como Kubernetes ou Docker Swarm, que automatizam tarefas como balanceamento de carga, escalabilidade e gerenciamento de falhas.


No Docker, o Docker Engine usa o containerd como o componente principal para gerenciar containers. O containerd é um runtime de containers de baixo nível e uma ferramenta essencial na arquitetura do Docker, responsável por realizar operações fundamentais relacionadas aos containers.



Relação entre Docker Engine e containerd


O Docker Engine é a camada de alto nível que fornece a interface completa do Docker, permitindo que você use comandos como docker run, docker build, etc. Ele abstrai a complexidade subjacente e oferece uma experiência de gerenciamento mais simples.


Já p containerd é o runtime utilizado internamente pelo Docker Engine para executar, pausar, parar e remover containers. Ele gerencia diretamente os ciclos de vida dos containers, redes, armazenamento e snapshots de imagens. Foi originalmente parte do Docker, mas agora é um projeto independente mantido pela CNCF (Cloud Native Computing Foundation).


Quando executamos um comando como docker run, o Docker Engine interpreta e processa a solicitação, delegando sua execução ao containerd. O containerd, por sua vez, interage com o runc, o runtime de baixo nível responsável por utilizar os recursos do kernel, como namespaces e cgroups, para configurar o ambiente isolado do container. Em essência, o containerd é o componente que gerencia todo o ciclo de vida dos containers, incluindo criação, execução, pausa, reinício e exclusão.



Ciclo de vida do container


O ciclo de vida de um container representa as etapas pelas quais ele passa desde a sua criação até a sua remoção. No Docker, esse ciclo é gerenciado principalmente pelo containerd e pelo Docker Engine, usando recursos do kernel para isolar e gerenciar o container. Durante o ciclo de vida, um container pode estar em diferentes estados:

  • Created: Configurado, mas ainda não em execução.
  • Running: O processo principal está ativo.
  • Paused: Processos suspensos.
  • Stopped: O processo foi encerrado.
  • Removed: O container não existe mais no sistema.

No ciclo de vida de um container, o comando principal que ele executa é definido como o processo principal do container. Este comando é configurado geralmente por meio das instruções CMD ou ENTRYPOINT no Dockerfile da imagem, ou diretamente ao executar o container com o comando docker run.


O container vive exclusivamente para executar esse comando principal. Quando o comando é concluído (ou encerrado), o container atinge seu estado de parado (exited). Ele não continua ativo ou executando tarefas adicionais. O container é projetado para ser efêmero e viver exclusivamente para o comando principal que foi definido. Ele não possui um "processo de fundo" ativo após a conclusão desse comando.


Para manter um container ativo, é necessário que o comando principal seja algo que não termine naturalmente, como executar um bash, ou ficar escutando continuamente por requisições.



Attach vs Exec


O comando docker attach conecta seu terminal diretamente ao processo principal de um container. Ele é útil para observar ou interagir com o terminal de um container cujo processo principal foi projetado para ser interativo ou gerar saída diretamente para o terminal. O docker attach se conecta ao terminal padrão (stdout, stderr, stdin) do processo principal do container.


Se o comando principal do container não for interativo, o docker attach não terá nada para mostrar no terminal e nesses casos você verá um terminal "preto" sem informações visíveis. Portanto, o docker attach deve ser usado apenas quando o processo principal é projetado para ser interativo ou gerar logs contínuos.


Quando o comando principal do container não for interativo podemos usar o comando docker container exec para executar um shell dentro do container. Algumas imagens Docker, como o scratch, não possuem um shell porque são intencionalmente reduzidas para incluir apenas o mínimo necessário. Essa imagem é um caso especial, é completamente vazia e usada como ponto de partida para criar imagens extremamente leves e sem shell, você não consegue interagir diretamente com o container de maneira tradicional (via sh ou bash).


Se o container não tiver um shell e você precisar inspecioná-lo, pode usar ferramentas como containers de depuração, adicionar um shell à imagem ou rodar comandos diretamente com docker exec.



Docker container


O comando docker container é um comando do Docker usado para interagir com contêineres Docker. Ele oferece uma variedade de subcomandos que permitem criar, gerenciar, listar e interagir com contêineres Docker. Aqui estão alguns dos principais subcomandos e suas funções:


ComandoDescrição
docker container run <imagem>Cria e executa um novo contêiner Docker a partir de uma imagem.
docker container start <contêiner>Inicia um ou mais contêineres que foram previamente criados, mas estão atualmente parados.
docker container stop <contêiner>Para um ou mais contêineres em execução de forma segura.
docker container restart <contêiner>Reinicia um ou mais contêineres Docker.
docker container pause <contêiner>Pausa a execução de um ou mais contêineres em execução.
docker container unpause <contêiner>Retoma a execução de um ou mais contêineres pausados.
docker container lsLista todos os contêineres em execução no host Docker.
docker container rm <contêiner>Remove um ou mais contêineres Docker.
docker container inspect <contêiner>Exibe informações detalhadas sobre um contêiner específico.
docker container logs <contêiner>Exibe os logs de saída de um contêiner específico.
docker container exec <contêiner> <comando>Executa um comando dentro de um contêiner em execução.
docker container cp <origem> <destino>Copia arquivos ou diretórios entre o host Docker e um contêiner.
docker stop $(docker ps -q)Para todos os containers no host Docker.
docker rm $(docker ps -a -q)Apaga todos os containers no host Docker.
sudo docker exec -it <contêiner> shConecta no shel SH de um container que já está em execução (Para deslogar, pode usar o comando exit).
docker container run hello-worldCriando um container a partir de uma imagem chamada 'hello-world'.
docker container -d run hello-worldCriando um container com processo em background a partir de uma imagem chamada 'hello-world'.
docker container stats ID_CONTAINERVerificando os recursos usados por um container especifico.

Agora veremos alguns comandos:

Terminal
# Criando um container a partir de uma imagem chamada 'hello-world':
╼ $ docker container run hello-world

# Exibindo os containers parados ou finalizados:
╼ $ docker container ls -a

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a4ad9aa898f8 hello-world "/hello" 40 seconds ago Exited (0) 38 seconds ago youthful_easley
8df845ee485e hello-world "/hello" 44 seconds ago Exited (0) 42 seconds ago charming_hamilton
a4e871fba758 hello-world "/hello" 12 minutes ago Exited (0) 12 minutes ago

# Criando um container e logando nele:
╼ $ docker container run -ti centos:7

# Logar num container que esteja rodando:
╼ $ docker attach ID_CONTAINER

# Executa o Shell SH de um container que esteja rodando.
# Similar a logar no container:
╼ $ docker exec -it ID_CONTAINER sh

# Criando um container e não inicializando:
╼ $ docker container create -ti ubuntu

# Para inicializar nosso container:
╼ $ docker container start ID_CONTAINER

# Desligar o container:
╼ $ docker container stop ID_CONTAINER

# Reiniciar o container:
╼ $ docker container restart ID_CONTAINER

# Pausar o container:
╼ $ docker container pause ID_CONTAINER

# Despausar:
╼ $ docker container unpause ID_CONTAINER

# Verificando os recursos usados por um container especifico:
╼ $ docker container stats ID_CONTAINER

# Rodando um comando no container e saindo:
╼ $ docker container top ID_CONTAINER

# Pegando os "logs" de um container:
╼ $ docker container logs ID_CONTAINER

# Pegando os "logs" de um container (igual 'tail -f'):
╼ $ docker container logs -f ID_CONTAINER

# Executando um container em modo backgound (-d) e espelhando a porta local (80) para
# a porta 80 do container:
╼ $ docker run -d -p 80:80 --name webserver nginx

# Não é possivel remover um container que esteja em execução,
# mas caso queira remover o container mesmo assim, use o parametro
# '-f' no RM.
╼ $ docker container rm -f ID_CONTAINER


Configurando recursos


Ao subir um container sem especificar um limite de recursos, ele irá subir sem controle, podendo consumir todo o recurso da máquina, impactando até mesmo o host hospedeiro.



Limitando a memória


Vamos definir um limite de memória RAM para o container no momento da criação dele. Primeiro inicie o container:

Terminal
╼ $ docker container run -ti --name teste debian

Agora vamos exibir algumas informações do container:

Terminal
# Exibe informações variadas do container:
╼ $ docker container inspect teste

# Consultando o limite de memoria:
╼ $ docker container inspect teste | grep -i memo
"Memory": 0,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
# Nesse caso, está zerado, indicando que nenhum limite foi configurado.

Iniciando um novo container com limite de memória:

Terminal
╼ $ docker container run -ti --name teste_memoria -m 512M debian

# Consultando o limite de memoria:
╼ $ docker container inspect teste_memoria | grep -i memo
"Memory": 536870912,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": -1,
"MemorySwappiness": null,
# O valor da memória exibido está em bytes.


Limitando a CPU


Para limitar o uso da CPU, utilizemos o parametro --cpus=X, onde X é o valor que será passado, lembrando que isso são núcleos da CPU.

Terminal
# Criando um novo container e limitando o uso da CPU:
╼ $ docker container run -ti --name teste_cpu -m 512M --cpus=1 debian

# Verificando se deu certo:
╼ $ docker container inspect teste_cpu | grep -i cpu
"CpuShares": 0,
"NanoCpus": 1000000000,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"CpuCount": 0,
"CpuPercent": 0,


Mudando recursos de Container em produção


Para alterar os limites de recursos consumidos pelo container como memória RAM e CPU para o container em produção, podemos usar o comando docker update. Dessa forma, o container não precisa morrer para que os recursos aumentem.

Terminal
# Alterando a memória e CPU de um container
╼ $ docker container update -m 256m --cpus=1 container1


Restart Policies


As restart policies (políticas de reinício) no Docker determinam como os contêineres devem se comportar em caso de falhas ou reinicializações do Docker ou do sistema host. Elas são úteis para manter serviços sempre disponíveis ou para evitar reinicializações desnecessárias.


As restart policies são configuradas com a opção --restart no comando docker run ou no arquivo docker-compose.yml. Existem quatro valores principais para --restart:

  1. no (padrão)
    O contêiner não será reiniciado automaticamente, independentemente do motivo de sua parada.

  2. always
    O contêiner será reiniciado sempre, independentemente do código de saída ou do motivo da parada. Útil para manter serviços críticos sempre em execução.

  3. unless-stopped
    O contêiner será reiniciado automaticamente, a menos que seja explicitamente parado pelo usuário. Parecido com always, mas não reinicia o contêiner após um docker stop.

  4. on-failure (com opção de limite)
    O contêiner será reiniciado somente se ele falhar, ou seja, retornar um código de saída diferente de 0. Opcionalmente, você pode especificar um limite de tentativas de reinício, como on-failure:5.


Ao executar um contêiner com docker run, você pode definir a política com --restart:

Terminal
# Cria um container e já aplica a política:
docker run --restart unless-stopped -d my-container

# Aplica a política para um container que já esteja em execução:
docker update --restart unless-stopped <container_id>

Para ver se um container tem uma politica dessa, basta usar o comando abaixo:

Terminal
docker inspect my-container | grep -A3 RestartPolicy
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},