Docker e C++: conteinerização de aplicações
No desenvolvimento moderno de software, a conteinerização tornou-se o padrão de facto para implantação e escalonamento de aplicações. O Docker permite empacotar uma aplicação junto com todas as suas dependências, bibliotecas e configurações em um contêiner isolado. Para desenvolvedores C++, isso é especialmente relevante, pois aplicações em C++ frequentemente exigem versões específicas de compiladores, bibliotecas do sistema e processos de compilação complexos. Neste artigo, vamos analisar como conteinerizar eficientemente aplicações C++ com Docker.
Por que Docker para C++?
Aplicações C++ são tradicionalmente consideradas complexas de implantar devido a problemas de portabilidade de binários entre diferentes distribuições Linux. O Docker resolve esse problema fornecendo um ambiente de execução unificado. As principais vantagens de usar Docker para projetos C++:
- Reprodutibilidade de compilações – versões fixas de compiladores e bibliotecas garantem que o código será compilado da mesma forma em qualquer máquina.
- Isolamento de dependências – diferentes projetos podem usar diferentes versões da mesma biblioteca sem conflitos.
- Simplificação de CI/CD – o contêiner pode ser usado como um artefato único para teste e implantação.
- Redução de tamanho – compilações multi-stage permitem criar imagens mínimas contendo apenas o arquivo executável.
Criando um Dockerfile para aplicação C++
Vamos considerar um exemplo básico. Temos uma aplicação C++ simples que imprime "Hello, Docker!".
Código fonte
// main.cpp#include <iostream>
int main() { std::cout << "Hello, Docker!" << std::endl; return 0;}Dockerfile básico
Vamos criar um Dockerfile simples que usa a imagem gcc para compilar e executar:
# Usamos a imagem oficial do GCCFROM gcc:latest
# Definimos o diretório de trabalhoWORKDIR /app
# Copiamos o código fonteCOPY main.cpp .
# Compilamos a aplicaçãoRUN g++ -o hello main.cpp
# Executamos a aplicaçãoCMD ["./hello"]Vamos compilar e executar o contêiner:
docker build -t cpp-hello .docker run cpp-helloSaída: Hello, Docker!
Compilação multi-stage: otimizando o tamanho da imagem
O problema da abordagem básica é que a imagem final contém o compilador, arquivos de cabeçalho e outras ferramentas que não são necessárias durante a execução. Para ambientes de produção, isso é redundante. A solução é a compilação multi-stage, que permite separar a etapa de compilação da etapa de execução.
Dockerfile otimizado
# Etapa de compilaçãoFROM gcc:latest AS builder
WORKDIR /appCOPY main.cpp .RUN g++ -static -o hello main.cpp
# Etapa finalFROM alpine:latest
WORKDIR /appCOPY --from=builder /app/hello .
CMD ["./hello"]Neste exemplo, usamos ligação estática (-static) para que o arquivo executável não dependa de bibliotecas dinâmicas. A imagem final ocupa apenas cerca de 5 MB em vez de 1.2 GB da imagem com GCC.
Conteinerização de projeto com CMake e dependências
Projetos reais geralmente usam sistemas de compilação (CMake) e dependências externas. Vamos considerar um exemplo com a biblioteca nlohmann/json.
Estrutura do projeto
project/├── CMakeLists.txt├── main.cpp├── external/│ └── json.hpp└── DockerfileCMakeLists.txt
cmake_minimum_required(VERSION 3.10)project(JsonApp)
set(CMAKE_CXX_STANDARD 17)
add_executable(app main.cpp)target_include_directories(app PRIVATE external)main.cpp
#include <iostream>#include "json.hpp"
using json = nlohmann::json;
int main() { json data = {{"name"
```