Docker & Kubernetes

O que Todo Dev Deve Saber sobre Docker Engine: Arquitetura, Daemon, CLI e o Ciclo de Vida de Containers

13 min de leitura

O que Todo Dev Deve Saber sobre Docker Engine: Arquitetura, Daemon, CLI e o Ciclo de Vida de Containers

Docker Engine: Arquitetura Fundamental O Docker Engine é o coração de toda a plataforma Docker. Trata-se de uma aplicação cliente-servidor que funciona como um gerenciador de containers, permitindo que você crie, execute, distribua e gerencie aplicações em ambientes isolados. Diferentemente de máquinas virtuais tradicionais, containers compartilham o kernel do sistema operacional host, tornando-os leves e eficientes. A arquitetura do Docker Engine é construída sobre três pilares fundamentais: o daemon Docker (dockerd), a CLI (Command Line Interface) e a API REST. O daemon é um processo de longa duração que executa no sistema operacional, gerenciando objetos Docker como images, containers, volumes e networks. A CLI é a interface pela qual você interage com o daemon, enquanto a API REST permite que programas terceirizados se comuniquem com o Docker programaticamente. Componentes Principais da Arquitetura O Docker Engine funciona através de uma arquitetura modular bem definida. O containerd é o runtime de container que atua como intermediário entre o daemon e o sistema

<h2>Docker Engine: Arquitetura Fundamental</h2>

<p>O Docker Engine é o coração de toda a plataforma Docker. Trata-se de uma aplicação cliente-servidor que funciona como um gerenciador de containers, permitindo que você crie, execute, distribua e gerencie aplicações em ambientes isolados. Diferentemente de máquinas virtuais tradicionais, containers compartilham o kernel do sistema operacional host, tornando-os leves e eficientes.</p>

<p>A arquitetura do Docker Engine é construída sobre três pilares fundamentais: o daemon Docker (dockerd), a CLI (Command Line Interface) e a API REST. O daemon é um processo de longa duração que executa no sistema operacional, gerenciando objetos Docker como images, containers, volumes e networks. A CLI é a interface pela qual você interage com o daemon, enquanto a API REST permite que programas terceirizados se comuniquem com o Docker programaticamente.</p>

<h3>Componentes Principais da Arquitetura</h3>

<p>O Docker Engine funciona através de uma arquitetura modular bem definida. O <strong>containerd</strong> é o runtime de container que atua como intermediário entre o daemon e o sistema operacional, gerenciando o ciclo de vida dos containers de forma isolada. O <strong>runc</strong> é o runtime de container de baixo nível que efetivamente executa os containers usando features nativas do Linux como namespaces e cgroups.</p>

<pre><code>┌─────────────────────────────────────────────────┐

│ Aplicação do Usuário │

├─────────────────────────────────────────────────┤

│ Docker CLI (docker command) │

├─────────────────────────────────────────────────┤

│ Docker Daemon (dockerd) - API Server │

├─────────────────────────────────────────────────┤

│ containerd - Container Runtime Manager │

├─────────────────────────────────────────────────┤

│ runc - Container Runtime (executa) │

├─────────────────────────────────────────────────┤

│ Kernel Linux (namespaces, cgroups, etc) │

└─────────────────────────────────────────────────┘</code></pre>

<p>Quando você executa um comando Docker, a CLI se conecta ao daemon através de um socket Unix (<code>/var/run/docker.sock</code> por padrão). O daemon processa a solicitação, interage com o containerd, que por sua vez utiliza o runc para executar os containers. Esse design em camadas permite que o Docker seja modular e que cada componente possa ser atualizado independentemente.</p>

<h2>Docker Daemon e Sua Operação</h2>

<p>O Docker Daemon (dockerd) é um serviço que roda em background no seu sistema operacional. Ele é responsável por escutar requisições da CLI ou da API, gerenciar o ciclo de vida completo dos containers e manter o estado de todos os objetos Docker. Em sistemas Linux, o daemon geralmente roda como um serviço systemd, enquanto em Windows e macOS roda através do Docker Desktop.</p>

<p>O daemon armazena suas configurações em <code>/etc/docker/daemon.json</code> (no Linux), permitindo customizações como limites de recursos, drivers de storage, logging e outras opções avançadas. Ele mantém um estado persistente que sobrevive a reinicializações, garantindo que containers e volumes configurados anteriormente sejam recuperados quando o daemon reinicia.</p>

<h3>Iniciando e Gerenciando o Daemon</h3>

<p>No Linux, você pode controlar o daemon usando systemctl. Para verificar se o daemon está rodando:</p>

<pre><code class="language-bash"># Verificar status do daemon

sudo systemctl status docker

Iniciar o daemon

sudo systemctl start docker

Parar o daemon

sudo systemctl stop docker

Reiniciar o daemon

sudo systemctl restart docker

Habilitar para iniciar automaticamente no boot

sudo systemctl enable docker</code></pre>

<p>Para visualizar informações detalhadas sobre o daemon e seu ambiente:</p>

<pre><code class="language-bash">docker info</code></pre>

<p>Esse comando retorna informações como versão do Docker, número de containers em execução, drivers de storage, logging, e configurações de rede. É uma ferramenta valiosa para diagnosticar problemas e entender o estado atual do seu ambiente Docker.</p>

<h3>Configuração do Daemon</h3>

<p>Um arquivo <code>daemon.json</code> típico pode parecer assim:</p>

<pre><code class="language-json">{

&quot;debug&quot;: false,

&quot;log-level&quot;: &quot;info&quot;,

&quot;storage-driver&quot;: &quot;overlay2&quot;,

&quot;storage-opts&quot;: [

&quot;overlay2.override_kernel_check=true&quot;

],

&quot;insecure-registries&quot;: [

&quot;registry.interno.local:5000&quot;

],

&quot;registry-mirrors&quot;: [

&quot;https://mirror.docker.io&quot;

],

&quot;live-restore&quot;: true,

&quot;max-concurrent-downloads&quot;: 5

}</code></pre>

<p>Cada propriedade controla um aspecto diferente da operação do daemon. A opção <code>live-restore</code> é particularmente importante em ambientes de produção, pois permite que containers continuem rodando mesmo se o daemon parar e reiniciar. A configuração <code>storage-driver</code> define qual mecanismo de armazenamento o Docker usará para as camadas das imagens.</p>

<h2>Docker CLI: Interagindo com Containers</h2>

<p>A Docker CLI é a ferramenta de linha de comando que você utiliza diariamente para interagir com o Docker. Ela funciona como um cliente que se conecta ao daemon através de uma chamada de API REST. Cada comando que você executa é traduzido em uma requisição HTTP que o daemon processa e responde.</p>

<p>A CLI segue um padrão de comando bem estruturado: <code>docker [COMANDO] [OPÇÕES] [ARGUMENTOS]</code>. Os comandos principais são divididos em categorias que trabalham com diferentes objetos Docker: imagens (<code>docker image</code>), containers (<code>docker container</code>), volumes (<code>docker volume</code>), networks (<code>docker network</code>), e muitos outros.</p>

<h3>Comandos Essenciais para Containers</h3>

<p>Os comandos mais frequentemente utilizados são aqueles que lidam diretamente com o ciclo de vida dos containers. Vamos explorar os principais:</p>

<pre><code class="language-bash"># Criar e executar um container

docker run --name meu-app -d -p 8080:80 nginx:latest

Listar containers em execução

docker ps

Listar todos os containers (incluindo parados)

docker ps -a

Executar um comando dentro de um container rodando

docker exec -it meu-app /bin/bash

Visualizar logs de um container

docker logs meu-app

Ver logs em tempo real

docker logs -f meu-app

Parar um container

docker stop meu-app

Iniciar um container parado

docker start meu-app

Remover um container

docker rm meu-app

Inspecionar detalhes de um container

docker inspect meu-app</code></pre>

<p>A flag <code>-d</code> (detached) inicia o container em background, retornando o controle do terminal imediatamente. A flag <code>-it</code> (interactive + tty) permite interação direta com o container, abrindo um shell quando combinada com um comando como <code>/bin/bash</code>. A flag <code>-p</code> mapeia portas entre o host e o container, no formato <code>porta_host:porta_container</code>.</p>

<h3>Buildando Imagens com a CLI</h3>

<p>Criar imagens Docker também é feito através da CLI. Um Dockerfile é um arquivo de texto que contém instruções para construir uma imagem. Aqui está um exemplo prático:</p>

<pre><code class="language-dockerfile">FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV FLASK_APP=app.py

ENV FLASK_ENV=production

EXPOSE 5000

CMD [&quot;python&quot;, &quot;-m&quot;, &quot;flask&quot;, &quot;run&quot;, &quot;--host=0.0.0.0&quot;]</code></pre>

<p>Para construir a imagem e executar o container:</p>

<pre><code class="language-bash"># Construir a imagem

docker build -t minha-app-flask:1.0 .

Executar um container baseado na imagem

docker run -d -p 5000:5000 --name app-flask minha-app-flask:1.0

Verificar se está rodando

docker ps</code></pre>

<p>A flag <code>-t</code> especifica uma tag para a imagem no formato <code>nome:versao</code>. O ponto final (<code>.</code>) indica que o Dockerfile está no diretório atual. O Docker irá executar cada instrução do Dockerfile sequencialmente, criando camadas (layers) que são cacheadas para builds futuros.</p>

<h2>Ciclo de Vida de Containers</h2>

<p>O ciclo de vida de um container é uma sequência bem definida de estados pelos quais passa desde sua criação até sua remoção. Compreender esse ciclo é essencial para gerenciar containers efetivamente em produção. Um container passa por vários estados: Created, Running, Paused, Stopped e Deleted.</p>

<p>Quando você executa <code>docker run</code>, ocorre uma sequência de operações: primeiro a imagem é baixada (se não existir localmente), depois um container é criado a partir dessa imagem, e finalmente o container é iniciado executando o comando especificado. Se nenhum comando for fornecido, o container usará o comando padrão definido na imagem (ENTRYPOINT ou CMD).</p>

<h3>Estados e Transições</h3>

<p>Um container começa no estado <strong>Created</strong> quando é criado mas não iniciado. Quando você executa <code>docker start</code>, ele transita para <strong>Running</strong>, onde o processo principal está executando. Do estado Running, você pode pausar o container com <code>docker pause</code>, movendo-o para <strong>Paused</strong>. Um container pausado pode ser retomado com <code>docker unpause</code>.</p>

<pre><code class="language-bash"># Demonstração do ciclo de vida

docker create --name meu-container nginx:latest

Container agora está em Created

docker start meu-container

Container agora está em Running

docker pause meu-container

Container agora está em Paused

docker unpause meu-container

Container volta para Running

docker stop meu-container

Container agora está em Stopped

docker rm meu-container

Container é removido (estado Deleted)</code></pre>

<p>Quando você executa <code>docker stop</code>, o Docker envia um sinal SIGTERM para o processo principal do container, dando-lhe tempo para encerrar gracefully. Se o container não parar dentro do timeout padrão (10 segundos), o Docker envia SIGKILL para forçar o término. Você pode customizar esse timeout com a flag <code>--time</code>.</p>

<h3>Gerenciamento do Ciclo de Vida em Aplicações Reais</h3>

<p>Em aplicações reais, você frequentemente precisa garantir que containers reiniciem automaticamente em caso de falha. O Docker oferece políticas de restart para isso:</p>

<pre><code class="language-bash"># Restart sempre que o container parar

docker run -d --restart always --name api-service nginx

Restart apenas se saiu com código de erro (não-zero)

docker run -d --restart on-failure --name worker nginx

Restart com máximo de tentativas

docker run -d --restart on-failure:5 --name worker nginx

Restart com delay entre tentativas

docker run -d --restart on-failure:5 --restart-delay 5s --name worker nginx</code></pre>

<p>A política <code>always</code> é útil para serviços que devem estar sempre disponíveis, enquanto <code>on-failure</code> é melhor para jobs que podem completar com sucesso e não devem ser reiniciados continuamente. Você também pode combinar essas opções com <code>--health-cmd</code> para realizar health checks e permitir que o Docker tome decisões mais inteligentes sobre quando reiniciar.</p>

<h3>Exemplo Completo: Ciclo de Vida com Health Checks</h3>

<p>Um padrão robusto em produção é combinar restart policies com health checks:</p>

<pre><code class="language-bash">docker run -d \

--name banco-dados \

--restart on-failure:3 \

--health-cmd=&quot;curl -f http://localhost:8000/health || exit 1&quot; \

--health-interval=30s \

--health-timeout=5s \

--health-retries=3 \

-p 8000:8000 \

minha-api:1.0</code></pre>

<p>O health check executa o comando especificado a cada 30 segundos. Se o comando retornar sucesso (código 0), o container é considerado saudável. Se falhar 3 vezes consecutivas, o container é marcado como unhealthy. Combinado com a política restart, isso garante que containers problemáticos sejam automaticamente reiniciados.</p>

<p>Para verificar o status de saúde:</p>

<pre><code class="language-bash"># Ver status do health check

docker inspect --format=&#039;{{json .State.Health}}&#039; banco-dados | jq .

Exemplo de saída:

{

&quot;Status&quot;: &quot;healthy&quot;,

&quot;FailingStreak&quot;: 0,

&quot;Log&quot;: [...]

}</code></pre>

<h2>Conclusão</h2>

<p>Dominar Docker Engine requer compreensão de três elementos interdependentes: a <strong>arquitetura em camadas</strong> que separa responsabilidades entre CLI, daemon, containerd e runc; o <strong>daemon como orquestrador central</strong> que gerencia estado e comunica com o sistema operacional; e finalmente o <strong>ciclo de vida de containers</strong> como mecanismo essencial para construir aplicações resilientes. Esses conceitos formam a base sólida necessária para avançar em tópicos como orquestração com Kubernetes, deploy em produção e otimização de performance. A prática constante com a CLI e a compreensão profunda de como o daemon funciona transformarão você de um usuário casual para um profissional capaz de diagnosticar problemas complexos e arquitetar soluções robustas.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.docker.com/engine/" target="_blank" rel="noopener noreferrer">Documentação Oficial do Docker Engine</a></li>

<li><a href="https://docs.docker.com/config/daemon/" target="_blank" rel="noopener noreferrer">Docker Daemon Configuration Reference</a></li>

<li><a href="https://github.com/opencontainers/runtime-spec" target="_blank" rel="noopener noreferrer">Docker Container Runtime Specification</a></li>

<li><a href="https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html" target="_blank" rel="noopener noreferrer">The Linux Container Internals: cgroups, namespaces and unshare</a></li>

<li><a href="https://docs.docker.com/develop/dev-best-practices/" target="_blank" rel="noopener noreferrer">Docker Best Practices and Production Guidelines</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em Docker & Kubernetes

Guia Completo de Probes em Kubernetes: Liveness, Readiness e Startup na Prática
Guia Completo de Probes em Kubernetes: Liveness, Readiness e Startup na Prática

Introdução aos Probes no Kubernetes Quando você deploya uma aplicação no Kube...

Como Usar FinOps em Kubernetes: Kubecost, Right-sizing e Spot Instances em Produção
Como Usar FinOps em Kubernetes: Kubecost, Right-sizing e Spot Instances em Produção

FinOps em Kubernetes: Uma Introdução Prática FinOps é a disciplina que une fi...

OPA Gatekeeper em Kubernetes: Políticas como Código no Cluster: Do Básico ao Avançado
OPA Gatekeeper em Kubernetes: Políticas como Código no Cluster: Do Básico ao Avançado

O que é OPA Gatekeeper? OPA (Open Policy Agent) Gatekeeper é um validador de...