Docker e C++: conteinerização de aplicações: nível avançado
\n\nA conteinerização de aplicações C++ com Docker não é apenas transferir um binário para um ambiente isolado. Em nível avançado, é necessário considerar as particularidades da compilação, gerenciamento de dependências, otimização do tamanho da imagem e depuração. Neste artigo, abordaremos as melhores práticas para criar imagens Docker eficientes para projetos C++, utilizando compilação em múltiplos estágios, vinculação estática e dinâmica, além de ferramentas modernas como CMake e vcpkg.
\n\n1. Compilação em múltiplos estágios (Multi-stage build)
\nO problema clássico das imagens Docker para C++ é o tamanho enorme devido à inclusão do compilador, arquivos de cabeçalho e bibliotecas na imagem final. A compilação em múltiplos estágios resolve esse problema: o primeiro estágio (build stage) contém todas as ferramentas para compilação, enquanto o segundo (runtime stage) contém apenas o conjunto mínimo de dependências de tempo de execução.
\n\n# Primeiro estágio: compilação\nFROM gcc:13 AS builder\nWORKDIR /app\nCOPY . .\nRUN apt-get update && apt-get install -y cmake libboost-all-dev && \\\n cmake -B build -DCMAKE_BUILD_TYPE=Release && \\\n cmake --build build --parallel $(nproc)\n\n# Segundo estágio: tempo de execução\nFROM alpine:3.19\nRUN apk add --no-cache libstdc++ libgcc\nCOPY --from=builder /app/build/my_app /usr/local/bin/my_app\nCMD ["my_app"]\n\nEssa abordagem reduz a imagem final de 1,5 GB (imagem com GCC) para aproximadamente 30 MB. Para executar uma aplicação C++ no Alpine, apenas libstdc++ e libgcc são suficientes.
2. Gerenciamento de dependências: vcpkg e Conan
\nPara projetos C++ modernos com dezenas de bibliotecas, a instalação manual de dependências via apt-get torna-se incontrolável. A melhor solução é usar gerenciadores de pacotes, como vcpkg ou Conan, diretamente no Dockerfile.
FROM ubuntu:22.04 AS builder\nRUN apt-get update && apt-get install -y curl git cmake build-essential\nRUN git clone https://github.com/Microsoft/vcpkg.git /opt/vcpkg && \\\n /opt/vcpkg/bootstrap-vcpkg.sh && \\\n /opt/vcpkg/vcpkg install fmt spdlog nlohmann-json\n\nWORKDIR /app\nCOPY . .\nRUN cmake -B build -DCMAKE_TOOLCHAIN_FILE=/opt/vcpkg/scripts/buildsystems/vcpkg.cmake && \\\n cmake --build build\n\nImportante: para evitar recompilar o vcpkg e as bibliotecas a cada alteração no código-fonte, copie vcpkg.json e CMakeLists.txt antes de copiar os demais arquivos. O Docker armazena em cache as camadas, e as dependências serão reinstaladas apenas quando o manifesto for alterado.
3. Otimização da imagem: vinculação estática e Alpine
\nPara reduzir ao máximo o tamanho da imagem, pode-se usar vinculação estática. Isso é especialmente relevante para microsserviços e utilitários de linha de comando. No entanto, lembre-se: a vinculação estática pode aumentar o binário, mas elimina a necessidade de incluir bibliotecas dinâmicas no contêiner.
\n\n# CMakeLists.txt\nset(CMAKE_EXE_LINKER_FLAGS "-static -static-libgcc -static-libstdc++")\ntarget_link_libraries(my_app PRIVATE fmt::fmt spdlog::spdlog)\n\nA imagem final pode ser construída sobre scratch (imagem vazia):
FROM scratch\nCOPY --from=builder /app/build/my_app /\nENTRYPOINT ["/my_app"]\n\nEssa imagem pesa apenas alguns megabytes. No entanto, observe que a vinculação estática nem sempre é possível (por exemplo, ao usar OpenSSL ou CUDA) e, nesses casos, é melhor usar Alpine com bibliotecas dinâmicas.
\n\n4. Depuração de aplicações C++ no contêiner
\nA depuração em Docker é uma dificuldade adicional. Métodos avançados incluem:
\n- \n
- Uso de imagens de depuração: compile uma imagem separada com símbolos de depuração e
gdbserver. Conecte-se a ela a partir da IDE (VS Code, CLion) por meio de depuração remota. \n - Core dumps: permita a gravação de arquivos core no contêiner e analise-os após uma falha: \n
# Dockerfile para