Dockerfiles enxutos e pontos de entrada do Shell
Crie scripts de compilação em várias etapas e adaptadores robustos de ponto de entrada com tratamento de sinais e criação de modelos de configuração.
Dockerfiles enxutos e pontos de entrada do Shell é uma aula grátis de Linux Command Line & Bash Scripting Mastery no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Linux Command Line & Bash Scripting Mastery, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.
Por que Dockerfiles enxutos são importantes para DevOps
Em ambientes de produção, cada megabyte em uma imagem Docker tem um custo: downloads mais lentos, superfícies de ataque maiores e espaço desperdiçado no registro. Dockerfiles enxutos, combinados com scripts robustos de ponto de entrada no interpretador de comandos, são uma característica de uma prática madura de DevOps.
- Compilações em várias etapas separam as ferramentas usadas durante a compilação da imagem final de execução, reduzindo drasticamente o tamanho.
- Adaptadores de ponto de entrada são pequenos scripts de interpretador de comandos que inicializam contêineres: eles geram arquivos de configuração a partir de modelos, validam variáveis de ambiente, tratam sinais e, por fim, executam o processo principal.
- Juntos, eles formam a base de cargas de trabalho de contêineres confiáveis e portáveis no Kubernetes, no ECS e em ambientes de servidores físicos.
Esta lição aborda ambas as práticas de ponta a ponta, com padrões prontos para produção que você pode inserir diretamente em seus pipelines.
Anatomia de um Dockerfile com várias etapas
Um Dockerfile com várias etapas usa várias instruções FROM. Cada etapa é um conjunto isolado de camadas; você copia apenas os artefatos necessários para a etapa seguinte.
- Etapa 0 (compilador): instala compiladores, executores de testes e dependências de compilação.
- Etapa 1 (execução): começa com uma base mínima (por exemplo,
alpine,distroless) e copia apenas binários compilados ou pacotes da aplicação. - A imagem final nunca contém
gcc,makeou código-fonte, a menos que você os copie explicitamente.
Use --from=<stage> em COPY para copiar arquivos entre os limites das etapas. Dê nomes às etapas com AS <name> para facilitar a leitura e permitir a seleção específica com docker build --target.
# ---- Stage 0: builder ----
FROM golang:1.22-alpine AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags='-s -w' -o /app/server ./cmd/server
# ---- Stage 1: runtime ----
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
ENTRYPOINT ["/server"]Minimizando camadas e invalidando o cache
Cada instrução RUN, COPY e ADD cria uma nova camada. Instruções mal ordenadas invalidam o cache da compilação sem necessidade, tornando lentos os pipelines de CI.
- Copie os manifestos de dependências (
package.json,go.mod,requirements.txt) antes de copiar o código-fonte, para que a instalação das dependências seja armazenada em cache de forma independente. - Encadeie comandos relacionados com
&&e faça a limpeza na mesma camadaRUNpara evitar deixar o cache de pacotes em camadas intermediárias. - Use
--no-cachenos gerenciadores de pacotes e remova os arquivos de listas após a instalação.
FROM python:3.12-slim AS builder
WORKDIR /app
# 1. Install deps first (cached until requirements change)
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt
# 2. Copy source (cache busted only when code changes)
COPY src/ ./src/
FROM python:3.12-slim
COPY --from=builder /install /usr/local
COPY --from=builder /app/src /app/src
WORKDIR /app
CMD ["python", "-m", "src.main"]Segredos do BuildKit e encaminhamento de SSH
Registros privados, chaves SSH e tokens de API nunca devem aparecer nas camadas da imagem. O Docker BuildKit oferece dois mecanismos seguros:
--secret: monta um arquivo secreto dentro de uma única etapaRUN, sem incorporá-lo a uma camada. Acesse-o por meio de/run/secrets/<id>.--ssh: encaminha o soquete do agente SSH do host para a compilação, para quegit clonepossa se autenticar sem incorporar chaves privadas.
Ative o BuildKit com DOCKER_BUILDKIT=1 ou por meio de docker buildx build. A diretiva # syntax=docker/dockerfile:1 habilita esses recursos.
# syntax=docker/dockerfile:1
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
# Mount NPM token as a secret — never stored in the image
RUN --mount=type=secret,id=npm_token \
NPM_TOKEN=$(cat /run/secrets/npm_token) \
npm ci --prefer-offline
# Usage at build time:
# DOCKER_BUILDKIT=1 docker build \
# --secret id=npm_token,src=~/.npmrc-token \
# -t myapp:latest .Escrevendo um adaptador robusto de ponto de entrada
O adaptador de ponto de entrada é um script de interpretador de comandos definido como ENTRYPOINT no Dockerfile. Sua função é preparar o ambiente de execução antes de transferir o controle ao processo principal.
Um adaptador bem estruturado segue esta ordem:
- Etapa 1: Defina
set -euo pipefailpara que qualquer falha interrompa a execução imediatamente. - Etapa 2: Valide as variáveis de ambiente obrigatórias e falhe rapidamente com uma mensagem útil.
- Etapa 3: Gere arquivos de configuração a partir de modelos com base nas variáveis de ambiente.
- Etapa 4: Registre manipuladores de sinais para um encerramento normal.
- Etapa 5:
exec "$@"— substitua o interpretador de comandos pelo processo principal, para que o PID 1 seja a aplicação, e não o adaptador.
O exec final é fundamental: sem ele, os sinais enviados pelo Kubernetes ou pelo ambiente de execução do Docker não são encaminhados ao processo filho.
#!/usr/bin/env bash
set -euo pipefail
# Step 2: validate required env vars
REQUIRED_VARS=(DATABASE_URL APP_SECRET PORT)
for var in "${REQUIRED_VARS[@]}"; do
if [[ -z "${!var:-}" ]]; then
echo "[entrypoint] ERROR: required env var '$var' is not set" >&2
exit 1
fi
done
# Step 3: template config (see next scene)
# Step 4: signal handling (see scene after that)
# Step 5: hand off to CMD
exec "$@"Geração de configurações com envsubst
envsubst (do pacote GNU gettext, presente no Alpine como gettext) substitui os marcadores ${VAR} em um arquivo de modelo pelos valores atuais das respectivas variáveis de ambiente.
- Inclua um modelo de configuração
*.tmplna imagem; o ponto de entrada o renderiza na inicialização. - Informe explicitamente a lista de variáveis a
envsubst, para que ele não expanda acidentalmente cifrões não relacionados (por exemplo, em uma expressão regular do nginx). - Grave o arquivo renderizado em um caminho gravável, como
/tmp, ou em um volume de configuração dedicado.
#!/usr/bin/env bash
# Template: /etc/nginx/conf.d/app.conf.tmpl contains:
# server { listen ${NGINX_PORT}; server_name ${SERVER_NAME}; ... }
export NGINX_PORT=${NGINX_PORT:-8080}
export SERVER_NAME=${SERVER_NAME:-localhost}
envsubst '${NGINX_PORT} ${SERVER_NAME}' \
< /etc/nginx/conf.d/app.conf.tmpl \
> /etc/nginx/conf.d/app.conf
echo "[entrypoint] nginx config rendered:"
grep -E 'listen|server_name' /etc/nginx/conf.d/app.conf
exec "$@"Tratamento de sinais e encerramento normal
Os contêineres recebem SIGTERM quando são interrompidos pelo Kubernetes, pelo ECS ou por docker stop. Se o adaptador de ponto de entrada for o PID 1 e não encaminhar os sinais, o processo principal será encerrado com SIGKILL após o período de tolerância — causando perda de solicitações ou corrupção de dados.
- Use
trappara capturarSIGTERMeSIGINTno adaptador. - Encaminhe o sinal ao PID filho usando
kill -TERM "$child". - Use
wait "$child"para bloquear até o encerramento do filho e, em seguida, repasse o código de saída dele. - Como alternativa, use
execpara substituir completamente o interpretador de comandos — assim, o sistema operacional entrega os sinais diretamente ao processo filho, sem necessidade detrap. Esse é o padrão preferido para casos simples.
#!/usr/bin/env bash
set -euo pipefail
# Start main process in background
"$@" &
child=$!
# Forward SIGTERM and SIGINT to the child
trap 'echo "[entrypoint] caught SIGTERM, forwarding..."; kill -TERM "$child"' TERM
trap 'echo "[entrypoint] caught SIGINT, forwarding..."; kill -INT "$child"' INT
# Wait for child to exit and capture its exit code
wait "$child"
exit $?Usando tini como um processo de inicialização mínimo
Quando o contêiner cria processos filhos (por exemplo, um interpretador de comandos que cria trabalhadores), você precisa de uma inicialização real para recolher processos zumbis. tini é um pequeno binário de inicialização desenvolvido especificamente para contêineres.
- Adicione
tinià imagem e defina-o como o adaptador do ponto de entrada. - Ele recolhe processos filhos zumbis, encaminha sinais corretamente e encerra com o código de status do filho.
- O Docker inclui um tini integrado, ativado com
docker run --init, mas incorporá-lo à imagem garante um comportamento consistente entre os ambientes de execução (Kubernetes, ECS etc.).
FROM node:20-alpine
# Install tini for proper signal handling and zombie reaping
RUN apk add --no-cache tini
WORKDIR /app
COPY --chown=node:node . .
RUN npm ci --omit=dev
USER node
# tini wraps CMD; forwards SIGTERM and reaps zombies
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "server.js"]Executando como um usuário não root
Contêineres executados como root (UID 0) representam um grande risco de segurança: uma fuga do contêiner concede acesso total ao host. Sempre mude para um usuário sem privilégios antes de executar o processo principal.
- Crie um usuário e um grupo de sistema dedicados no Dockerfile com
addgroup/adduser(Alpine) ougroupadd/useradd(Debian). - Altere o proprietário dos arquivos da aplicação com
COPY --chown=appuser:appgroup— isso é mais eficiente do que uma camadaRUN chownseparada. - Troque para o usuário com a instrução
USER. O ponto de entrada e o CMD herdam esse usuário. securityContext.runAsNonRoot: truedo Kubernetes se recusará a iniciar uma imagem que ainda seja executada como root.
FROM python:3.12-slim
# Create non-root user
RUN groupadd --gid 1001 appgroup && \
useradd --uid 1001 --gid appgroup --shell /bin/bash --create-home appuser
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy app files with correct ownership in a single layer
COPY --chown=appuser:appgroup src/ ./src/
COPY --chown=appuser:appgroup entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
USER appuser
ENTRYPOINT ["/entrypoint.sh"]
CMD ["python", "-m", "src.main"]Verificações de integridade e sondas de prontidão na imagem
As sondas de atividade e prontidão do Kubernetes são definidas nos manifestos, mas você também pode incorporar um HEALTHCHECK ao Dockerfile para ambientes autônomos de docker run e Docker Compose.
HEALTHCHECK --interval=10s --timeout=3s --start-period=15s --retries=3 CMD ...- Use
curl --failouwget -qO-para serviços HTTP; para processos do sistema que não usam HTTP, verifique um soquete com/dev/tcp/localhost/PORT. - Instale apenas o necessário: em imagens distroless, evite adicionar
curlapenas para verificações de integridade — use um binário de sondagem específico ou o próprio binário de integridade da aplicação.
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY dist/ /usr/share/nginx/html/
# Lightweight health check using bash TCP pseudo-device
# (no curl needed — works on any image with bash)
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
CMD bash -c 'exec 3<>/dev/tcp/localhost/80 && echo -e "GET /health HTTP/1.0\r\n" >&3 && cat <&3 | grep -q "200 OK"' || exit 1
EXPOSE 80Reunindo tudo: um ponto de entrada para produção
A seguir, há um adaptador de ponto de entrada completo e pronto para produção, que combina todos os padrões desta lição: validação do ambiente, geração de configurações a partir de modelos, encaminhamento de sinais e transferência com exec. Esse padrão é usado em microsserviços reais de Node.js, Python e Go implantados no Kubernetes.
- Cada seção é comentada para servir como um modelo autoexplicativo.
- O adaptador tem menos de 50 linhas — os pontos de entrada devem ser simples e fáceis de auditar.
- Observe o
exec "$@"final: depois que toda a configuração estiver concluída, o interpretador de comandos é substituído pelo processo da aplicação, que se torna o PID 1 e recebe diretamente todos os sinais do sistema operacional.
#!/usr/bin/env bash
# entrypoint.sh — production-grade container entrypoint shim
set -euo pipefail
# ── 1. Validate required environment variables ──────────────────
REQUIRED=(DATABASE_URL APP_SECRET PORT LOG_LEVEL)
for var in "${REQUIRED[@]}"; do
[[ -n "${!var:-}" ]] || { echo "[entrypoint] FATAL: $var is not set" >&2; exit 1; }
done
# ── 2. Set safe defaults for optional variables ─────────────────
export HOST=${HOST:-0.0.0.0}
export WORKERS=${WORKERS:-2}
# ── 3. Render config template ───────────────────────────────────
if [[ -f /etc/app/app.conf.tmpl ]]; then
envsubst '${DATABASE_URL} ${PORT} ${LOG_LEVEL} ${HOST}' \
< /etc/app/app.conf.tmpl \
> /etc/app/app.conf
echo "[entrypoint] config rendered at /etc/app/app.conf"
fi
# ── 4. Wait for dependent services (optional, fast) ─────────────
if [[ -n "${WAIT_FOR_HOST:-}" ]]; then
echo "[entrypoint] waiting for ${WAIT_FOR_HOST}:${WAIT_FOR_PORT:-5432}..."
until bash -c "exec 3<>/dev/tcp/${WAIT_FOR_HOST}/${WAIT_FOR_PORT:-5432}" 2>/dev/null; do
sleep 1
done
echo "[entrypoint] dependency ready"
fi
# ── 5. Hand off to CMD (PID 1 becomes the application) ──────────
echo "[entrypoint] starting: $*"
exec "$@"Verificação de conhecimentos: tratamento de sinais nos pontos de entrada
Teste sua compreensão sobre o tratamento de sinais em scripts de ponto de entrada de contêineres.
Recapitulação: Dockerfiles enxutos e pontos de entrada no interpretador de comandos
Você abordou toda a estrutura da criação de contêineres para produção:
- Compilações em várias etapas usam várias instruções
FROMpara manter compiladores e ferramentas de compilação fora da imagem final, produzindo camadas de execução enxutas e mínimas. - Ordenação das camadas — copie os manifestos de dependências antes do código-fonte — maximiza os acertos do cache e acelera os pipelines de CI.
- Segredos e montagens SSH do BuildKit mantêm as credenciais fora do histórico da imagem sem interromper compilações autenticadas.
- Adaptadores de ponto de entrada validam variáveis de ambiente, geram arquivos de configuração a partir de modelos com
envsubste transferem o controle para a aplicação por meio deexec "$@". - O tratamento de sinais exige
exec(para que a aplicação seja o PID 1) ou um padrão explícito detrap+kill+waitquando são usados trabalhos em segundo plano. - tini adiciona o recolhimento de processos zumbis e o encaminhamento correto de sinais quando o contêiner cria vários processos.
- Usuários não root e instruções
HEALTHCHECKcompletam uma imagem segura e observável, pronta para cargas de trabalho de produção no Kubernetes.
Combine esses padrões de forma consistente e suas imagens serão menores, mais rápidas de implantar e significativamente mais robustas em condições de produção.
Perguntas Frequentes
A aula “Dockerfiles enxutos e pontos de entrada do Shell” é grátis?
Sim — o texto completo de “Dockerfiles enxutos e pontos de entrada do Shell” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Linux Command Line & Bash Scripting Mastery, atualize para CoddyKit PRO. O curso de Linux Command Line & Bash Scripting Mastery inclui 4 aulas no total.
O que vou aprender em “Dockerfiles enxutos e pontos de entrada do Shell”?
Crie scripts de compilação em várias etapas e adaptadores robustos de ponto de entrada com tratamento de sinais e criação de modelos de configuração. Você pratica Linux Command Line & Bash Scripting Mastery com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Linux Command Line & Bash Scripting Mastery?
Nenhuma experiência prévia é necessária. Linux Command Line & Bash Scripting Mastery no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “Dockerfiles enxutos e pontos de entrada do Shell”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Linux Command Line & Bash Scripting Mastery?
Sim. Cada aula de Linux Command Line & Bash Scripting Mastery inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Dockerfiles enxutos e pontos de entrada do Shell
- Criação de modelos de configurações com envsubst e heredocs
- Criação de scripts para recursos de nuvem com CLI e jq
- Sondagens de integridade, barreiras de prontidão e loops de espera