Your C++ Application Deserves Better Packaging: Diving into Docker
\n\nYou wrote a cool parser in C++ that processes gigabytes of logs in a flash. Or maybe you created a high-load microservice for working with network sockets. The code is debugged, everything runs smoothly. But then comes the moment of truth — deployment. And here it begins: "What version of libcurl do you have?", "Why did it crash in production with a GLIBC error?", "Why are you installing the entire GCC on the server?"
\n\nSound familiar? Welcome to the world of containerizing C++ applications with Docker. If you thought Docker was only about JavaScript and Python, you are missing out on a lot. In fact, it is for C++ that containerization provides a superpower: you get an immutable environment where the binary is built once and runs everywhere — from your laptop to a production cluster.
\n\nIn this article, we will not just discuss theory. We will step by step create a C++ application, package it into Docker, optimize the image to the smallest possible size, and make the container run like clockwork. Let's go!
\n\nWhy Docker is a C++ Developer's Best Friend?
\n\nMany believe that C++ applications are easy to distribute as binaries. Technically — yes. Practically — it's a path to hell. Different Linux distributions, glibc versions, missing libraries, broken ABI compatibility — these are just some of the problems.
\n\nDocker solves three main pain points for a C++ developer:
\n\n- \n
- Build Reproducibility: you know exactly which version of the compiler, CMake, Boost, and other dependencies is used. No more "it works on my machine". \n
- Isolation: the container does not clutter the host system. You don't need to install GCC on the production server — a minimal runtime is sufficient. \n
- Scalability: a ready-made image can be easily deployed in Kubernetes or Docker Swarm. C++ handles load excellently, and Docker helps distribute that load. \n
And most importantly — containerizing C++ applications does not require magic. All you need is Docker and a little patience.
\n\nCreating a Simple C++ Application for Containerization
\n\nFor an example, let's take not "Hello, World!", but something more practical. Let's say we are writing a small HTTP server in C++ using Boost.Beast. But to avoid overloading the article, we'll make a simplified version — a socket echo server using the pure POSIX API. This will show how to work with system dependencies.
\n\nFile server.cpp:
#include <iostream>\n#include <string>\n#include <cstring>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <unistd.h>\n\nint main() {\n int server_fd, new_socket;\n struct sockaddr_in address;\n int opt = 1;\n int addrlen = sizeof(address);\n char buffer[1024] = {0};\n\n // Create a socket\n if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {\n perror("socket failed");\n exit(EXIT_FAILURE);\n }\n\n // Bind to port 8080\n if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {\n perror("setsockopt");\n exit(EXIT_FAILURE);\n }\n address.sin_family = AF_INET;\n address.sin_addr.s_addr = INADDR_ANY;\n address.sin_port = htons(8080);\n\n if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {\n perror("bind failed");\n exit(EXIT_FAILURE);\n }\n\n if (listen(server_fd, 3) < 0) {\n perror("listen");\n exit(EXIT_FAILURE);\n }\n\n std::cout << "Server listening on port 8080" << std::endl;\n\n while (true) {\n if ((new_socket = accept(server_fd, (struct sockaddr *)&address,\n (socklen_t*)&addrlen)) < 0) {\n perror("accept");\n exit(EXIT_FAILURE);\n }\n\n read(new_socket, buffer, 1024);\n