Docker e C++: conteinerização de aplicações do A ao Z
A conteinerização tornou-se uma parte essencial do desenvolvimento moderno de software. O Docker permite empacotar aplicações juntamente com suas dependências, garantindo reprodutibilidade e portabilidade. Neste artigo, vamos detalhar como conteinerizar aplicações C++ com Docker — desde conceitos básicos até técnicas avançadas de otimização.
1. Por que conteinerizar aplicações C++?
Muitos desenvolvedores acreditam erroneamente que o Docker é relevante apenas para linguagens interpretadas (Python, Node.js). No entanto, aplicações C++ também se beneficiam da conteinerização:
- Isolamento de dependências: bibliotecas, compiladores e utilitários do sistema não entram em conflito com outros projetos
- Reprodutibilidade de builds: ambiente idêntico em todas as etapas (dev, CI/CD, production)
- Simplificação do deploy: a imagem contém tudo o que é necessário para a execução
- Escalabilidade: fácil executar múltiplas instâncias via Docker Compose ou Kubernetes
Vamos analisar o processo com um exemplo prático.
2. Dockerfile básico para C++
Vamos começar com uma aplicação simples em C++. Crie o arquivo main.cpp:
#include <iostream>
int main() { std::cout << "Hello, Dockerized C++!" << std::endl; return 0;}Agora escreva o Dockerfile para compilar e executar:
# Imagem base com compiladorFROM gcc:latest AS builder
WORKDIR /appCOPY main.cpp .
# CompilaçãoRUN g++ -o hello main.cpp
# Imagem final mínimaFROM alpine:latest
WORKDIR /appCOPY --from=builder /app/hello .
CMD ["./hello"]Compile e execute:
docker build -t cpp-hello .docker run cpp-helloResultado: Hello, Dockerized C++!
Explicação:
- O primeiro estágio (builder) usa a imagem completa do gcc para compilar
- O segundo estágio usa o Alpine Linux mínimo para a imagem final
- A imagem final contém apenas o binário compilado — isso economiza espaço
3. Compilação com CMake e dependências
Projetos reais frequentemente usam CMake e bibliotecas externas. Vamos considerar um exemplo com a biblioteca fmt.
Estrutura do projeto:
myapp/├── CMakeLists.txt├── main.cpp└── DockerfileCMakeLists.txt:
cmake_minimum_required(VERSION 3.14)project(MyApp)
find_package(fmt REQUIRED)
add_executable(myapp main.cpp)target_link_libraries(myapp fmt::fmt)main.cpp:
#include <fmt/core.h>
int main() { fmt::println("Hello from {}!", "C++ with Docker"); return 0;}Dockerfile com build multiestágio:
# Estágio 1: Compilação de dependências e aplicaçãoFROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y \ build-essential cmake libfmt-dev \ && rm -rf /var/lib/apt/lists/*
WORKDIR /appCOPY CMakeLists.txt main.cpp ./RUN cmake -B build && cmake --build build
# Estágio 2: Imagem finalFROM ubuntu:22.04
RUN apt-get update && apt-get install -y libfmt9 \ && rm -rf /var/lib/apt/lists/*
WORKDIR /appCOPY --from=builder /app/build/myapp .
CMD ["./myapp"]Pontos importantes:
- As dependências de compilação (build-essential, cmake) não entram na imagem final
- As dependências de runtime (libfmt9) são instaladas separadamente
- O tamanho da imagem final é significativamente menor
4. Otimização de imagens Docker para C++
A otimização é especialmente importante para aplicações C++, pois as imagens com compiladores po