حاويات تطبيقات C++ باستخدام Docker: دليل خطوة بخطوة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

تطبيق C++ الخاص بك يستحق تغليفًا أفضل: نغوص في Docker

\n\n

لقد كتبت محللًا رائعًا بلغة C++ يعالج بسرعة فائقة غيغابايتات من السجلات. أو ربما أنشأت خدمة مصغرة عالية التحميل للعمل مع مآخذ الشبكة. الكود مُصحح، كل شيء يعمل بسلاسة. ولكن تأتي لحظة الحقيقة - النشر. وهنا تبدأ المشاكل: "ما إصدار libcurl لديك؟"، "لماذا تعطل في بيئة الإنتاج مع خطأ GLIBC؟"، "لماذا تقوم بتثبيت GCC بأكمله على الخادم؟".

\n\n

هل يبدو ذلك مألوفًا؟ مرحبًا بك في عالم حاويات تطبيقات C++ باستخدام Docker. إذا كنت لا تزال تعتقد أن Docker مخصص فقط لـ JavaScript و Python، فأنت تفوت الكثير. في الواقع، بالنسبة لـ C++، تمنحك الحاويات قوة خارقة: تحصل على بيئة غير قابلة للتغيير حيث يتم بناء الملف الثنائي مرة واحدة ويعمل في كل مكان - من حاسوبك المحمول إلى مجموعة الإنتاج.

\n\n

في هذه المقالة، لن نناقش النظرية فقط. سنقوم خطوة بخطوة بإنشاء تطبيق C++، وتعبئته في Docker، وتحسين الصورة إلى أصغر حجم ممكن، وجعل الحاوية تعمل كالساعة. هيا بنا!

\n\n

لماذا Docker هو أفضل صديق لمطور C++؟

\n\n

يعتقد الكثيرون أن تطبيقات C++ سهلة التوزيع كملفات ثنائية. تقنيًا - نعم. عمليًا - هذا طريق إلى الجحيم. توزيعات Linux المختلفة، إصدارات glibc، نقص المكتبات، عدم توافق ABI - هذه فقط بعض المشاكل.

\n\n

Docker يحل ثلاث مشاكل رئيسية لمطور C++:

\n\n
    \n
  • قابلية إعادة إنتاج البناء: أنت تعرف بالضبط أي إصدار من المترجم، CMake، Boost والاعتماديات الأخرى يُستخدم. لا مزيد من "يعمل على جهازي".
  • \n
  • العزل: الحاوية لا تلوث النظام المضيف. لا حاجة لتثبيت GCC على خادم الإنتاج - يكفي وقت تشغيل بسيط.
  • \n
  • التوسع: الصورة الجاهزة سهلة النشر في Kubernetes أو Docker Swarm. C++ يتعامل مع الحمل بشكل ممتاز، وDocker يساعد في توزيع هذا الحمل.
  • \n
\n\n

والأهم - حاويات تطبيقات C++ لا تتطلب سحرًا. كل ما تحتاجه هو Docker وقليل من الصبر.

\n\n

إنشاء تطبيق C++ بسيط للحاويات

\n\n

كمثال، لنأخذ ليس "Hello, World!"، بل شيئًا أكثر حيوية. لنفترض أننا نكتب خادم HTTP صغيرًا باستخدام C++ مع Boost.Beast. ولكن لتجنب إثقال المقالة، سنصنع نسخة مبسطة - خادم صدى مأخذ باستخدام POSIX API الخالص. سيظهر هذا كيفية العمل مع الاعتماديات النظامية.

\n\n

ملف server.cpp:

\n\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\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                

المدونات

توصيات الكتب