Contenedorización de aplicaciones C++ con Docker: guía completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué contenerizar aplicaciones C++?



El desarrollo en C++ tradicionalmente se asocia con el problema de "funciona en mi máquina". Diferentes versiones de compiladores, bibliotecas y dependencias del sistema convierten el despliegue en un dolor de cabeza. Docker resuelve este problema: empaqueta la aplicación con todo su entorno en un contenedor ligero que se ejecuta de la misma manera en cualquier sistema.



En este artículo, analizaremos el proceso completo de contenerización de una aplicación C++, desde un Dockerfile simple hasta una compilación multi-etapa con optimización del tamaño de la imagen.



1. Fundamentos: Dockerfile para una aplicación C++



Comencemos con el ejemplo más simple. Supongamos que tenemos un programa en C++ que imprime "Hello, Docker!".



Código fuente (hello.cpp)



#include <iostream>

int main() { std::cout << "Hello, Docker!" << std::endl; return 0;}


Primer Dockerfile



# Usamos la imagen con GCCFROM gcc:13.2

# Establecemos el directorio de trabajoWORKDIR /app

# Copiamos los fuentesCOPY hello.cpp .

# CompilamosRUN g++ -o hello hello.cpp

# EjecutamosCMD ["./hello"]


Construimos y ejecutamos:



docker build -t cpp-hello .docker run cpp-hello


La desventaja de este enfoque es que la imagen pesa alrededor de 1.2 GB debido al conjunto completo de herramientas GCC. Para producción, esto es inaceptable.



2. Compilación multi-etapa (Multi-stage build)



La mejor práctica es separar la compilación y la ejecución. En la primera etapa, compilamos la aplicación con el conjunto completo de herramientas; en la segunda, copiamos solo el binario resultante a una imagen mínima y limpia.



Dockerfile optimizado



# Etapa 1: compilaciónFROM gcc:13.2 AS builder

WORKDIR /app

COPY hello.cpp .

RUN g++ -static -o hello hello.cpp

# Etapa 2: ejecuciónFROM alpine:3.19

WORKDIR /app

# Copiamos el binario compilado estáticamenteCOPY --from=builder /app/hello .

CMD ["./hello"]


El punto clave es la bandera -static. Esta incluye todas las dependencias en el binario, permitiendo ejecutarlo en Alpine puro sin bibliotecas adicionales. ¡El tamaño de la imagen se reduce de 1.2 GB a ~6 MB!



3. Proyecto real: CMake + dependencias



Consideremos un caso más complejo: una aplicación que utiliza la biblioteca Boost y se compila con CMake.



Estructura del proyecto



myapp/├── CMakeLists.txt├── src/│   └── main.cpp└── Dockerfile


CMakeLists.txt



cmake_minimum_required(VERSION 3.20)project(MyApp LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Boost REQUIRED COMPONENTS system)

add_executable(myapp src/main.cpp)target_link_libraries(myapp Boost::system)


main.cpp



#include <iostream>#include <boost/asio.hpp>

int main() { boost::asio::io_context io; std::cout << "Boost.Asio funciona en el contenedor!" << std::endl; return 0;}


Dockerfile con dependencias



# Etapa 1: compilaciónFROM ubuntu:22.04 AS builder

# Instalamos dependencias de compilaciónRUN apt-get update && apt-get install -y \ build-essential \ cmake \ libboost-system-dev \ && rm -rf /var/lib/apt/lists/*

WORKDIR /app

# Copiamos los fuentesCOPY CMakeLists.txt .COPY src/ src/

# CompilamosRUN mkdir build && cd build && \ cmake .. -DCMAKE_BUILD_TYPE=Release && \ make -j$(nproc)

# Etapa 2: ejecuciónFROM ubuntu:22.04

# Instalamos solo dependencias de runtimeRUN apt-get update && apt-get install -y \ libboost-system1.74.0 \ && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY --from=builder /app/build/myapp .

CMD ["./myapp"]


Observe: en la segunda etapa, instalamos solo la versión de runtime de Boost (libboost-system1.74.0).

Blogs

Book Recommendations