انشر التطبيقات باستخدام Docker Swarm

نظام توصية محتوى الفيديو عبر الإنترنت الذي نعمل عليه هو عبارة عن تطوير تجاري مغلق وهو من الناحية الفنية مجموعة متعددة المكونات من المكونات الخاصة والمفتوحة المصدر. الغرض من كتابة هذه المقالة هو وصف تنفيذ نظام تجميع سرب الرصيف لموقع التدريج دون تعطيل سير العمل المحدد لعملياتنا في وقت محدود. القصة المعروضة على انتباهك مقسمة إلى جزأين. يصف الجزء الأول CI / CD قبل استخدام سرب عامل الإرساء ، بينما يصف الجزء الثاني عملية تنفيذه. يمكن لأولئك الذين لا يهتمون بقراءة الجزء الأول الانتقال بأمان إلى الجزء الثاني.

الجزء الأول

وبالعودة إلى العام البعيد البعيد ، كان من الضروري إعداد عملية CI / CD بأسرع ما يمكن. كان أحد الشروط عدم استخدام Docker للنشر المكونات المتقدمة لعدة أسباب:

  • من أجل تشغيل أكثر موثوقية واستقرارًا للمكونات في الإنتاج (أي ، في الواقع ، شرط عدم استخدام المحاكاة الافتراضية)
  • لم يرغب المطورون الرائدون في العمل مع Docker (غريب ، لكن هكذا كان الأمر)
  • وفقًا للاعتبارات الأيديولوجية لإدارة البحث والتطوير

تم عرض البنية التحتية والمكدس والمتطلبات الأولية التقريبية لـ MVP على النحو التالي:

  • 4 خوادم Intel® X5650 مع Debian (تم تطوير جهاز واحد أكثر قوة بالكامل)
  • يتم تطوير المكونات المخصصة في C ++ و Python3
  • أدوات الطرف الثالث الرئيسية المستخدمة: كافكا ، كليك هاوس ، إيرفلو ، ريدس ، غرافانا ، بوستجرسكل ، ميسكل ، ...
  • خطوط الأنابيب لبناء المكونات واختبارها بشكل منفصل للتصحيح والإصدار

أحد الأسئلة الأولى التي يجب معالجتها في المرحلة الأولية هو كيفية نشر المكونات المخصصة في أي بيئة (CI / CD).

قررنا تثبيت مكونات الجهات الخارجية بشكل منهجي وتحديثها بشكل منهجي. يمكن نشر التطبيقات المخصصة المطورة بلغة C ++ أو Python بعدة طرق. من بينها ، على سبيل المثال: إنشاء حزم النظام ، وإرسالها إلى مستودع الصور المبنية ثم تثبيتها على الخوادم. لسبب غير معروف ، تم اختيار طريقة أخرى ، وهي: استخدام CI ، يتم تجميع الملفات التنفيذية للتطبيق ، وإنشاء بيئة مشروع افتراضية ، ويتم تثبيت وحدات py من requirements.txt ، ويتم إرسال جميع هذه العناصر مع التكوينات والبرامج النصية و بيئة التطبيق المصاحبة للخوادم. بعد ذلك ، يتم تشغيل التطبيقات كمستخدم افتراضي بدون حقوق المسؤول.

تم اختيار Gitlab-CI كنظام CI / CD. بدا خط الأنابيب الناتج مثل هذا:

انشر التطبيقات باستخدام Docker Swarm
من الناحية الهيكلية ، بدا 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 *ntStrictHostKeyChecking nonn" > ~/.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

تجدر الإشارة إلى أن التجميع والاختبار يتم على صورته الخاصة ، حيث تم بالفعل تثبيت جميع حزم النظام الضرورية وتم إجراء إعدادات أخرى.

على الرغم من أن كل نص من هذه النصوص في الوظائف مثير للاهتمام بطريقته الخاصة ، إلا أنني بالطبع لن أتحدث عنها. سيستغرق وصف كل منها الكثير من الوقت وهذا ليس الغرض من المقالة. سألفت انتباهك فقط إلى حقيقة أن مرحلة النشر تتكون من سلسلة من البرامج النصية للاتصال:

  1. createconfig.py - ينشئ ملف settings.ini بإعدادات مكونة في بيئات مختلفة للنشر اللاحق (ما قبل الإنتاج ، والإنتاج ، والاختبار ، ...)
  2. install_venv.sh - يُنشئ بيئة افتراضية لمكونات py في دليل معين ونسخها إلى خوادم بعيدة
  3. Prepar_init.d.py - يقوم بإعداد سكربتات بدء وإيقاف للمكون بناءً على القالب
  4. نشر - يتحلل ويعيد تشغيل المكونات الجديدة

مر الوقت. تم استبدال مرحلة التدريج بمرحلة ما قبل الإنتاج والإنتاج. تمت إضافة دعم للمنتج على توزيع آخر (CentOS). تمت إضافة 5 خوادم فعلية أكثر قوة وعشرات من الخوادم الافتراضية. وأصبح من الصعب أكثر فأكثر على المطورين والمختبرين اختبار مهامهم في بيئة قريبة إلى حد ما من حالة العمل. في هذا الوقت اتضح أنه من المستحيل الاستغناء عنه ...

الجزء الثاني

انشر التطبيقات باستخدام Docker Swarm

لذا ، فإن مجموعتنا عبارة عن نظام مذهل يتكون من بضع عشرات من المكونات المنفصلة التي لم يتم وصفها بواسطة 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) إلى خطوط الأنابيب ، مما يتطلب إجراء يدويًا.

انشر التطبيقات باستخدام Docker Swarm
يتطابق اسم المكدس مع اسم الفرع ويجب أن يكون هذا التفرد كافيًا. تتلقى الخدمات في المكدس عناوين IP فريدة ومنافذ وأدلة وما إلى ذلك. سيتم عزله ، ولكن الشيء نفسه من المكدس إلى المكدس (لأن ملف التكوين هو نفسه لجميع الأكوام) - ما أردناه. ننشر المكدس (الكتلة) باستخدام عامل ميناء-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 إلى صورة واحدة كبيرة مكونة ، وبشكل عام ، لا تختلف عمليًا عن النشر الموضح في الجزء الأول. سأقوم بتسليط الضوء على الاختلافات:

  • استنساخ بوابة ... - احصل على الملفات المطلوبة للنشر (createconfig.py ، install_venv.sh ، إلخ.)
  • تجعيد ... && فك الضغط ... - تنزيل وفك ضغط عناصر البناء (الأدوات المترجمة)

هناك مشكلة واحدة فقط لم يتم وصفها حتى الآن: المكونات التي لها واجهة ويب لا يمكن الوصول إليها من مستعرضات المطورين. نحل هذه المشكلة باستخدام الوكيل العكسي ، وبالتالي:

في .gitlab-ci.yml ، بعد نشر مكدس الكتلة ، نضيف سطر نشر الموازن (والذي ، عند الالتزام ، يقوم فقط بتحديث تكوينه (ينشئ ملفات تكوين nginx جديدة وفقًا للقالب: / etc / nginx / conf. د / $ {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

على أجهزة الكمبيوتر الخاصة بالتطوير ، قم بتحديث / etc / hosts ؛ يصف عنوان url لـ nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

لذلك ، تم تنفيذ نشر مجموعات التدريج المعزول ويمكن للمطورين الآن تشغيلها بأي عدد كافٍ للتحقق من مهامهم.

خطط مستقبلية:

  • افصل بين مكوناتنا كخدمات
  • احصل على كل ملف Dockerfile
  • اكتشاف العقد الأقل تحميلًا في المكدس تلقائيًا
  • حدد العقد حسب نمط الاسم (بدلاً من استخدام المعرف كما في المقالة)
  • أضف تحققًا من إتلاف المكدس
  • ...

شكر خاص ل статью.

المصدر: www.habr.com

إضافة تعليق