يعد نظام توصيات محتوى الفيديو عبر الإنترنت الذي نعمل عليه بمثابة تطوير تجاري مغلق تقنيًا ، وهو مجموعة متعددة المكونات من مكوناته الخاصة والمفتوحة المصدر. الغرض من هذه المقالة هو وصف إدخال نظام تجميع سرب عمال الرصيف لمنصة انطلاق ، دون تعطيل سير العمل الحالي لعملياتنا في وقت محدود. ينقسم السرد المقدم إلى انتباهكم إلى قسمين. يصف الجزء الأول CI / CD قبل استخدام سرب docker ، ويصف الجزء الثاني عملية التنفيذ. يمكن لأولئك غير المهتمين بقراءة الجزء الأول الانتقال بأمان إلى الجزء الثاني.
الجزء الأول
في السنة البعيدة ، كانت هناك حاجة لتكوين عملية CI / CD في أسرع وقت ممكن. أحد الشروط هو عدم استخدام Docker
لنشر المكونات المطورة لعدة أسباب:
- من أجل تشغيل أكثر موثوقية واستقرارًا للمكونات في الإنتاج (أي ، في الواقع ، شرط عدم استخدام المحاكاة الافتراضية)
- لا يريد المطورون البارزون العمل مع Docker (غريب ، لكن كان الأمر كذلك)
- لأسباب أيديولوجية إدارة البحث والتطوير
كانت البنية التحتية والمكدس وعينة المتطلبات الأولية لـ MVP كما يلي:
- 4 خوادم Intel® X5650 مع دبيان (آلة واحدة أكثر قوة بالكامل للتطوير)
- ويتم تطوير المكونات المخصصة في C ++ ، Python3
- الأدوات الرئيسية المستخدمة من قبل الطرف الثالث: كافكا ، كليك هاوس ، إيرفلو ، ريديس ، جرافانا ، بوستجرسل ، ميسكل ، ...
- تجميع خطوط الأنابيب واختبار المكونات بشكل منفصل لتصحيح الأخطاء والإفراج عنها
تتمثل إحدى المشكلات الأولى التي يتعين حلها في المرحلة الأولية في كيفية نشر مكونات مخصصة في أي بيئة (CI / CD).
قررت مكونات الجهة الخارجية تثبيت نظامي وتحديثها بشكل منتظم. يمكن نشر التطبيقات المخصصة المطورة في C ++ أو Python بعدة طرق. من بينها ، على سبيل المثال: إنشاء حزم النظام ، وإرسالها إلى مستودع الصور المجمعة وتثبيتها اللاحقة على الخوادم. لسبب غير معروف ، تم اختيار طريقة أخرى ، وهي استخدام CI ، يتم تجميع ملفات التطبيق القابلة للتنفيذ ، وإنشاء بيئة مشروع افتراضية ، وتثبيت وحدات نمطية من المتطلبات. يتم إرسال كل هذه الأعمال الفنية جنبًا إلى جنب مع التكوينات والبرامج النصية وبيئة التطبيق المصاحبة للخوادم. بعد ذلك ، يتم تشغيل التطبيقات من مستخدم افتراضي دون حقوق المسؤول.
تم اختيار Gitlab-CI كنظام CI / CD. بدا خط الأنابيب الناتج مثل هذا:

من الناحية الهيكلية ، بدا gitlab-ci.yml هكذا--- variables: # , CMAKE_CPUTYPE: "westmere" DEBIAN: "MYREGISTRY:5000/debian:latest" before_script: - eval $(ssh-agent -s) - ssh-add <(echo "$SSH_PRIVATE_KEY") - mkdir -p ~/.ssh && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config stages: - build - testing - deploy debug.debian: stage: build image: $DEBIAN script: - cd builds/release && ./build.sh paths: - bin/ - builds/release/bin/ when: always release.debian: stage: build image: $DEBIAN script: - cd builds/release && ./build.sh paths: - bin/ - builds/release/bin/ when: always ## testing stage tests.codestyle: stage: testing image: $DEBIAN dependencies: - release.debian script: - /bin/bash run_tests.sh -t codestyle -b "${CI_COMMIT_REF_NAME}_codestyle" tests.debug.debian: stage: testing image: $DEBIAN dependencies: - debug.debian script: - /bin/bash run_tests.sh -e codestyle/test_pylint.py -b "${CI_COMMIT_REF_NAME}_debian_debug" artifacts: paths: - run_tests/username/ when: always expire_in: 1 week tests.release.debian: stage: testing image: $DEBIAN dependencies: - release.debian script: - /bin/bash run_tests.sh -e codestyle/test_pylint.py -b "${CI_COMMIT_REF_NAME}_debian_release" artifacts: paths: - run_tests/username/ when: always expire_in: 1 week ## staging stage deploy_staging: stage: deploy environment: staging image: $DEBIAN dependencies: - release.debian script: - cd scripts/deploy/ && python3 createconfig.py -s $CI_ENVIRONMENT_NAME && /bin/bash install_venv.sh -d -r ../../requirements.txt && python3 prepare_init.d.py && python3 deploy.py -s $CI_ENVIRONMENT_NAME when: manual
تجدر الإشارة إلى أن التجميع والاختبار يتمان على صورته الخاصة ، حيث تم بالفعل تثبيت جميع حزم النظام الضرورية وإعدادات أخرى.
على الرغم من أن كل هذه النصوص في الوظيفة مثيرة للاهتمام بطريقتها الخاصة ، إلا
أنني بالتأكيد لن أتحدث عنها ، لكن وصف كل منها سيستغرق وقتًا كبيرًا وهذا ليس الغرض من المقالة. سأولي فقط الانتباه إلى أن مرحلة النشر تتكون من سلسلة من مكالمات البرنامج النصي:
- createconfig.py - يقوم بإنشاء ملف settings.ini بإعدادات المكونات في بيئة مختلفة للنشر اللاحق (ما قبل الإنتاج والإنتاج والاختبار ...)
- install_venv.sh - ينشئ بيئة افتراضية لمكونات py في دليل محدد ونسخها إلى خوادم بعيدة
- prepar_init.d.py - تحضير البرامج النصية لإيقاف المكون بناءً على قالب
- publish.py - إلغاء ضغط وإعادة تشغيل مكونات جديدة
مر الوقت. تم استبدال مرحلة التدريج قبل الإنتاج والإنتاج. تمت إضافة دعم المنتج على مجموعة توزيع أخرى (CentOS). تمت إضافة 5 خوادم مادية قوية وعشرات الخوادم الافتراضية. وأصبح من الصعب على المطورين والمختبرين إدارة مهامهم في بيئة أقرب إلى حالة العمل بشكل أو بآخر. في هذا الوقت ، أصبح من الواضح أنه من المستحيل الاستغناء عنه ...
الجزء الثاني

لذلك ، فإن مجموعتنا
لا تزال مشهدًا لنظام مكون من بضع عشرات مكونات منفصلة لم يتم وصفها بواسطة Dockerfiles. يمكنك تهيئتها للنشر في بيئة معينة فقط ككل. مهمتنا هي نشر الكتلة في بيئة مرحلية لتشغيلها قبل الاختبار التجريبي.
من الناحية النظرية ، يمكن أن يكون هناك العديد من مجموعات العمل في وقت واحد: العديد من المهام في حالة الانتهاء أو على وشك الانتهاء. تتيح لنا الإمكانيات المتاحة لخوادمنا تشغيل العديد من المجموعات على كل خادم. يجب عزل كل مجموعة مرحلية (يجب ألا يكون هناك تقاطع في المنافذ أو الأدلة أو ما إلى ذلك).
المورد الأكثر قيمة هو وقتنا ، ولم يكن لدينا الكثير.
لبداية أسرع ، اختاروا Docker Swarm بسبب بساطته ومرونته المعمارية. أول شيء فعلناه هو إنشاء خوادم المدير البعيد وعقد متعددة:
$ docker node ls ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION kilqc94pi2upzvabttikrfr5d nop-test-1 Ready Active 19.03.2 jilwe56pl2zvabupryuosdj78 nop-test-2 Ready Active 19.03.2 j5a4yz1kr2xke6b1ohoqlnbq5 * nop-test-3 Ready Active Leader 19.03.2
بعد ذلك ، أنشأنا شبكة:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
بعد ذلك ، قاموا بتوصيل العقد Gitlab-CI و Swarm من حيث الإدارة عن بعد لعقد CI: تثبيت الشهادات ، وإعداد المتغيرات السرية ، وإعداد خدمة Docker على خادم الإدارة. هذا
المقال أنقذنا كثيرًا من الوقت.
بعد ذلك ، أضفنا وظائف لإنشاء وتدمير المكدس في .gitlab-ci .yml.
تمت إضافة المزيد من الوظائف إلى .gitlab-ci .yml ## staging stage deploy_staging: stage: testing before_script: - echo "override global 'before_script'" image: "REGISTRY:5000/docker:latest" environment: staging dependencies: [] variables: DOCKER_CERT_PATH: "/certs" DOCKER_HOST: tcp://10.50.173.107:2376 DOCKER_TLS_VERIFY: 1 CI_BIN_DEPENDENCIES_JOB: "release.centos.7" script: - mkdir -p $DOCKER_CERT_PATH - echo "$TLSCACERT" > $DOCKER_CERT_PATH/ca.pem - echo "$TLSCERT" > $DOCKER_CERT_PATH/cert.pem - echo "$TLSKEY" > $DOCKER_CERT_PATH/key.pem - docker stack deploy -c docker-compose.yml ${CI_ENVIRONMENT_NAME}_${CI_COMMIT_REF_NAME} --with-registry-auth - rm -rf $DOCKER_CERT_PATH when: manual ## stop staging stage stop_staging: stage: testing before_script: - echo "override global 'before_script'" image: "REGISTRY:5000/docker:latest" environment: staging dependencies: [] variables: DOCKER_CERT_PATH: "/certs" DOCKER_HOST: tcp://10.50.173.107:2376 DOCKER_TLS_VERIFY: 1 script: - mkdir -p $DOCKER_CERT_PATH - echo "$TLSCACERT" > $DOCKER_CERT_PATH/ca.pem - echo "$TLSCERT" > $DOCKER_CERT_PATH/cert.pem - echo "$TLSKEY" > $DOCKER_CERT_PATH/key.pem - docker stack rm ${CI_ENVIRONMENT_NAME}_${CI_COMMIT_REF_NAME} # TODO: need check that stopped when: manual
من مقتطف الشفرة أعلاه ، من الواضح أنه تمت إضافة زرين (publish_staging ، stop_staging) إلى خطوط الأنابيب التي تتطلب تدخل يدوي.

اسم المكدس يتوافق مع اسم الفرع وهذا التفرد يجب أن يكون كافيا. تتلقى الخدمات الموجودة في مجموعة البيانات عناوين IP فريدة من نوعها والمنافذ والدلائل وما إلى ذلك. سيتم عزله ، ولكن الشيء نفسه من المكدس إلى المكدس (لأن ملف التكوين هو نفسه لجميع الكدسات) - هذا ما حققناه. ننشر
المكدس (الكتلة) باستخدام
docker-compose.yml ، الذي يصف نظامنا العنقودي.
عامل ميناء-compose.yml --- version: '3' services: userprop: image: redis:alpine deploy: replicas: 1 placement: constraints: [node.id == kilqc94pi2upzvabttikrfr5d] restart_policy: condition: none networks: nw_swarm: celery_bcd: image: redis:alpine deploy: replicas: 1 placement: constraints: [node.id == kilqc94pi2upzvabttikrfr5d] restart_policy: condition: none networks: nw_swarm: schedulerdb: image: mariadb:latest environment: MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' MYSQL_DATABASE: schedulerdb MYSQL_USER: **** MYSQL_PASSWORD: **** command: ['--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci', '--explicit_defaults_for_timestamp=1'] deploy: replicas: 1 placement: constraints: [node.id == kilqc94pi2upzvabttikrfr5d] restart_policy: condition: none networks: nw_swarm: celerydb: image: mariadb:latest environment: MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' MYSQL_DATABASE: celerydb MYSQL_USER: **** MYSQL_PASSWORD: **** deploy: replicas: 1 placement: constraints: [node.id == kilqc94pi2upzvabttikrfr5d] restart_policy: condition: none networks: nw_swarm: cluster: image: $CENTOS7 environment: - CENTOS - CI_ENVIRONMENT_NAME - CI_API_V4_URL - CI_REPOSITORY_URL - CI_PROJECT_ID - CI_PROJECT_URL - CI_PROJECT_PATH - CI_PROJECT_NAME - CI_COMMIT_REF_NAME - CI_BIN_DEPENDENCIES_JOB command: > sudo -u myusername -H /bin/bash -c ". /etc/profile && mkdir -p /storage1/$CI_COMMIT_REF_NAME/$CI_PROJECT_NAME && cd /storage1/$CI_COMMIT_REF_NAME/$CI_PROJECT_NAME && git clone -b $CI_COMMIT_REF_NAME $CI_REPOSITORY_URL . && curl $CI_API_V4_URL/projects/$CI_PROJECT_ID/jobs/artifacts/$CI_COMMIT_REF_NAME/download?job=$CI_BIN_DEPENDENCIES_JOB -o artifacts.zip && unzip artifacts.zip ; cd /storage1/$CI_COMMIT_REF_NAME/$CI_PROJECT_NAME/scripts/deploy/ && python3 createconfig.py -s $CI_ENVIRONMENT_NAME && /bin/bash install_venv.sh -d -r ../../requirements.txt && python3 prepare_init.d.py && python3 deploy.py -s $CI_ENVIRONMENT_NAME" deploy: replicas: 1 placement: constraints: [node.id == kilqc94pi2upzvabttikrfr5d] restart_policy: condition: none tty: true stdin_open: true networks: nw_swarm: networks: nw_swarm: external: true
هنا يمكنك أن ترى أن المكونات متصلة بشبكة واحدة (nw_swarm) ويمكن الوصول إليها من بعضها البعض.
يتم فصل مكونات النظام (استنادًا إلى redis ، mysql) من المجموعة المشتركة للمكونات المخصصة (يتم تقسيم الخطط والعرف كخدمات). تبدو مرحلة النشر الخاصة بمجموعتنا مثل نقل CMD إلى صورة واحدة كبيرة التكوين ، وبشكل عام لا يختلف عملياً عن النشر الموضح في الجزء الأول. أؤكد الاختلافات:
- git clone ... - نحصل على الملفات اللازمة لإنشاء عملية نشر (createconfig.py ، install_venv.sh ، وما إلى ذلك)
- curl ... && unzip ... - قم بتنزيل وفك ضغط أدوات التجميع (الأدوات المساعدة المترجمة)
هناك مشكلة واحدة لم يتم وصفها بعد: لا يمكن الوصول إلى المكونات التي تحتوي على واجهة ويب من متصفحات المطورين. نحل هذه المشكلة باستخدام الوكيل العكسي ، وبالتالي:
في .gitlab-ci.yml ، بعد نشر مكدس نظام المجموعة ، أضف خط نشر الموازن (الذي ، عند الالتزام ، يقوم فقط بتحديث التكوين الخاص به (ينشئ ملفات تكوين nginx جديدة باستخدام القالب: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME►.conf) - راجع رمز docker-compose-nginx.yml)
- docker stack deploy -c docker-compose-nginx.yml ${CI_ENVIRONMENT_NAME} --with-registry-auth
عامل ميناء-يؤلف-nginx.yml --- version: '3' services: nginx: image: nginx:latest environment: CI_COMMIT_REF_NAME: ${CI_COMMIT_REF_NAME} NGINX_CONFIG: |- server { listen 8080; server_name staging_${CI_COMMIT_REF_NAME}_cluster.dev; location / { proxy_pass http://staging_${CI_COMMIT_REF_NAME}_cluster:8080; } } server { listen 5555; server_name staging_${CI_COMMIT_REF_NAME}_cluster.dev; location / { proxy_pass http://staging_${CI_COMMIT_REF_NAME}_cluster:5555; } } volumes: - /tmp/staging/nginx:/etc/nginx/conf.d command: /bin/bash -c "echo -e \"$$NGINX_CONFIG\" > /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf; nginx -g \"daemon off;\"; /etc/init.d/nginx reload" ports: - 8080:8080 - 5555:5555 - 3000:3000 - 443:443 - 80:80 deploy: replicas: 1 placement: constraints: [node.id == kilqc94pi2upzvabttikrfr5d] restart_policy: condition: none networks: nw_swarm: networks: nw_swarm: external: true
على تطوير أجهزة الكمبيوتر ، التحديث / الخ / المضيفين ؛ تسجيل عنوان url إلى nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
لذلك ، تم تنفيذ نشر مجموعات التدريج معزولة ويمكن للمطورين الآن إطلاقها في
أي كمية كافية لاختبار مهامهم.
خطط أخرى:
- افصل مكوناتنا عن الخدمات
- جعل لكل Dockerfile
- الكشف تلقائيًا عن العقد الأقل تحميلًا في الحزمة
- تعيين العقد حسب نمط الاسم (بدلاً من استخدام المعرّف كما في المقالة)
- إضافة التحقق من أن مكدس دمرت
- ...
شكر خاص لهذه
المادة .