Docker e C++: conteinerização de aplicações em nível avançado
A conteinerização de aplicações C++ com Docker não é apenas transferir código para um ambiente isolado. Em nível avançado, é a arte de equilibrar o tamanho da imagem, a velocidade de construção, a segurança e a compatibilidade com bibliotecas do sistema. Neste artigo, abordaremos técnicas que ajudarão você a criar imagens Docker eficientes, leves e sustentáveis para projetos C++.
Presumimos que você já conhece os fundamentos do Docker (comandos docker build, docker run) e a sintaxe básica do Dockerfile. Agora vamos para tarefas reais: construção em múltiplos estágios, trabalho com dependências do sistema, depuração dentro do contêiner e integração com CI/CD.
1. Construção em múltiplos estágios: separando compilação e execução
O principal problema do C++ no Docker é o tamanho da imagem. Um conjunto completo de ferramentas (g++, cmake, make, pacotes dev) pode pesar 1–2 GB. A solução é a construção em múltiplos estágios (multi-stage build).
Vamos considerar um exemplo clássico: uma aplicação C++ usando a biblioteca libcurl.
# Primeiro estágio: construçãoFROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y \ build-essential \ cmake \ libcurl4-openssl-dev \ && rm -rf /var/lib/apt/lists/*
WORKDIR /appCOPY . .
RUN cmake -B build -DCMAKE_BUILD_TYPE=Release \ && cmake --build build --parallel $(nproc)
# Segundo estágio: imagem executávelFROM ubuntu:22.04-slim
RUN apt-get update && apt-get install -y \ libcurl4 \ && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/build/myapp /usr/local/bin/myapp
CMD ["myapp"]Pontos-chave:
- No primeiro estágio (
builder), instalamos todas as ferramentas e dependências para a construção. - No segundo estágio — apenas dependências de runtime (
libcurl4em vez delibcurl4-openssl-dev). - A imagem final contém apenas o binário compilado e as bibliotecas necessárias.
- Usamos
--parallel $(nproc)para acelerar a construção.
O tamanho da imagem diminui de ~1.2 GB para ~80 MB. Isso é crítico para implantação e download de imagens em CI/CD.
2. Otimização de cache e camadas do Docker
Cada instrução no Dockerfile cria uma camada. A ordem incorreta das instruções faz com que, com a menor alteração no código, todo o cache seja invalidado.
Regra: primeiro copie o que muda raramente (CMakeLists.txt, dependências), depois o código.
FROM ubuntu:22.04 AS builder
WORKDIR /app
# 1. Dependências do sistema (mudam raramente)RUN apt-get update && apt-get install -y \ build-essential cmake libboost-all-dev \ && rm -rf /var/lib/apt/lists/*
# 2. Arquivos de configuração da construção (mudam com pouca frequência)COPY CMakeLists.txt .
# 3. Código fonte (muda constantemente)COPY src/ ./src/
# 4. ConstruçãoRUN cmake -B build && cmake --build buildDicas adicionais:
- Use
.dockerignorepara excluirbuild/,.git/,*.o. - Agrupe
RUN apt-get installem um único comando — isso reduz o número de camadas. - Para projetos C++ com muitos arquivos de cabeçalho, considere usar
ccachedentro do contêiner.
3. Trabalho com bibliotecas do sistema e vinculação estática
Às vezes, bibliotecas dinâmicas no contêiner estão ausentes ou são incompatíveis. Uma abordagem avançada é a vinculação estática ou o uso do Alpine Linux.
Opção A: Construção estática (g++)
FROM alpine:3.18 AS builder
RUN apk add --no-cache g++ gcc musl-dev make cmake
WORKDIR /appCOPY . .
# Vinculação estáticaRUN g++ -static -O2 -o myapp main.cpp -lpthread
FROM scratchCO