您的C++应用程序值得更好的打包:深入Docker
\n\n您编写了一个出色的C++解析器,能够闪电般地处理数GB的日志。或者,您可能创建了一个用于处理网络套接字的高负载微服务。代码经过调试,一切运行流畅。但关键时刻来临——部署。然后问题开始出现:“你用的是哪个版本的libcurl?”,“为什么在生产环境因GLIBC错误而崩溃?”,“你为什么要在服务器上安装整个GCC?”。
\n\n熟悉吗?欢迎来到使用Docker容器化C++应用程序的世界。如果您一直认为Docker只适用于JavaScript和Python,那您就错过了很多。实际上,正是对于C++,容器化赋予了超能力:您获得了一个不可变的环境,其中二进制文件一次构建,随处运行——从您的笔记本电脑到生产集群。
\n\n在本文中,我们不仅仅是讨论理论。我们将逐步创建一个C++应用程序,将其打包到Docker中,将镜像优化到尽可能小的尺寸,并让容器像时钟一样精准运行。我们开始吧!
\n\n为什么Docker是C++开发者的最佳朋友?
\n\n许多人认为C++应用程序以二进制形式分发很容易。从技术上讲——是的。实际上——这是一条通往地狱的道路。不同的Linux发行版、glibc版本、缺少库、损坏的ABI兼容性——这只是问题的一部分。
\n\nDocker解决了C++开发者的三大痛点:
\n\n- \n
- 构建的可重现性:您确切知道使用了哪个版本的编译器、CMake、Boost和其他依赖项。再也没有“在我机器上能运行”的问题。 \n
- 隔离性:容器不会污染主机系统。无需在生产服务器上安装GCC——只需最小的运行时环境即可。 \n
- 可扩展性:准备好的镜像可以轻松部署到Kubernetes或Docker Swarm中。C++擅长处理负载,而Docker有助于分配这些负载。 \n
最重要的是——C++应用程序的容器化不需要魔法。您所需要的只是Docker和一点耐心。
\n\n创建一个简单的C++应用程序用于容器化
\n\n作为示例,我们不使用“Hello, World!”,而是更贴近实际的东西。假设我们正在使用C++和Boost.Beast编写一个小型HTTP服务器。但为了避免文章过于复杂,我们做一个简化版本——基于纯POSIX API的套接字回显服务器。这将展示如何处理系统依赖项。
\n\n文件 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 // 创建套接字\n if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {\n perror("socket failed");\n exit(EXIT_FAILURE);\n }\n\n // 绑定到端口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