مقدمة: لماذا ننشر C++ في Kubernetes؟
\n\nتظل لغة C++ واحدة من أكثر لغات البرمجة أداءً، خاصة في مجالات الأنظمة عالية التحميل، وخوادم الألعاب، والتطبيقات المالية، وإنترنت الأشياء. ومع ذلك، في عالم الحوسبة السحابية الحديث، لا يكفي مجرد كتابة كود سريع - بل يجب أن تكون قادرًا على نشره وتوسيعه وتحديثه بشكل موثوق دون توقف. وهنا يأتي دور Kubernetes (K8s) - نظام تنسيق الحاويات الذي يعمل على أتمتة نشر التطبيقات وتوسيع نطاقها وإدارتها.
\n\nفي هذا الدليل، سنشرح خطوة بخطوة عملية نشر تطبيق C++ بسيط في مجموعة Kubernetes. ستتعلم كيفية حزم كود C++ في صورة Docker، وكتابة المانيفستات اللازمة لـ Kubernetes، وتشغيل تطبيقك في السحابة. المقال موجه للمبتدئين، لذلك لن نتعمق في التكوينات المعقدة، بل سنركز على خط أنابيب أساسي ولكنه عملي.
\n\n1. التحضير: تطبيق C++ وحاوية Docker
\n\nقبل الحديث عن Kubernetes، نحتاج إلى تجهيز تطبيق C++ الخاص بنا للعمل داخل حاوية. الحاوية هي تغليف التطبيق مع جميع تبعياته في بيئة معزولة. بالنسبة لـ C++، هذا أمر بالغ الأهمية، لأن الملف الثنائي المترجم قد يعتمد على إصدارات محددة من المكتبات (libc، libstdc++، إلخ) على الجهاز الهدف.
\n\n1.1. تطبيق C++ بسيط للنشر
\n\nلننشئ ملف main.cpp - خادم HTTP بسيط يستجيب للطلبات. للتبسيط، لن نستخدم مكتبات خارجية، بل سنكتب خادم TCP بدائي.
// main.cpp\n#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 const char* hello = "HTTP/1.1 200 OK\\nContent-Type: text/plain\\nContent-Length: 12\\n\\nHello, K8s!\\n";\n\n // إنشاء socket\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 | SO_REUSEPORT, &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, (socklen_t*)&addrlen))<0) {\n perror("accept");\n exit(EXIT_FAILURE);\n }\n read(new_socket, buffer, 1024);\n send(new_socket, hello, strlen(hello), 0);\n close(new_socket);\n }\n return 0;\n}\n\n\n1.2. بناء ملف ثنائي ثابت
\n\nلجعل حاويتنا صغيرة قدر الإمكان وغير معتمدة على مكتبات النظام المضيف، من الأفضل بناء ملف ثنائي مرتبط بشكل ثابت. نفذ الأمر التالي:
\n\ng++ -static -o server main.cpp -lpthread\n\nالعلامة -static تضم جميع المكتبات الضرورية مباشرة في الملف التنفيذي. العلامة -lpthread مطلوبة للعمل مع الخيوط (على الرغم من أنها ليست إلزامية في مثالنا البسيط، إلا أنها ستكون مفيدة للمشاريع الحقيقية).
1.3. ملف Dockerfile لتطبيق C++
\n\nالآن لننشئ Dockerfile. سنستخدم البناء متعدد المراحل (multi-stage build): أولاً نقوم بترجمة الكود في صورة كبيرة تحتوي على المترجم، ثم