ڈوکر سوارم کا استعمال کرتے ہوئے ایپلی کیشنز کو تعینات کریں۔

آن لائن ویڈیو مواد کی سفارش کا نظام جس پر ہم کام کر رہے ہیں ایک بند تجارتی ترقی ہے اور تکنیکی طور پر ملکیتی اور اوپن سورس اجزاء کا ایک کثیر اجزاء کا کلسٹر ہے۔ اس مضمون کو لکھنے کا مقصد محدود وقت کے حالات میں ہمارے عمل کے قائم شدہ ورک فلو میں خلل ڈالے بغیر، سٹیجنگ پلیٹ فارم کے لیے ڈوکر سوارم کلسٹرنگ سسٹم کے نفاذ کو بیان کرنا ہے۔ آپ کی توجہ کے لیے پیش کی گئی داستان کو دو حصوں میں تقسیم کیا گیا ہے۔ پہلا حصہ docker swarm استعمال کرنے سے پہلے CI/CD کی وضاحت کرتا ہے، اور دوسرا حصہ اس پر عمل درآمد کے عمل کو بیان کرتا ہے۔ جو لوگ پہلے حصے کو پڑھنے میں دلچسپی نہیں رکھتے وہ محفوظ طریقے سے دوسرے پر جا سکتے ہیں۔

حصہ اول

ایک زمانے میں، CI/CD کے عمل کو جلد از جلد ترتیب دینے کی ضرورت تھی۔ ایک شرط یہ تھی کہ ڈوکر کا استعمال نہ کیا جائے۔ تعیناتی کے لیے اجزاء کئی وجوہات کے لئے تیار کیے جا رہے ہیں:

  • پروڈکشن میں اجزاء کے زیادہ قابل اعتماد اور مستحکم آپریشن کے لیے (یعنی، جوہر میں، ورچوئلائزیشن کو استعمال نہ کرنے کی ضرورت)
  • سرکردہ ڈویلپرز ڈوکر کے ساتھ کام نہیں کرنا چاہتے تھے (عجیب، لیکن ایسا ہی تھا)
  • آر اینڈ ڈی مینجمنٹ کی نظریاتی وجوہات کی بناء پر

MVP کے لیے بنیادی ڈھانچہ، اسٹیک اور تخمینی ابتدائی ضروریات حسب ذیل تھیں:

  • Debian کے ساتھ 4 Intel® X5650 سرورز (ایک اور طاقتور مشین مکمل طور پر ترقی کے لیے)
  • آپ کے اپنی مرضی کے اجزاء کی ترقی C++، Python3 میں کی جاتی ہے۔
  • تیسری پارٹی کے اہم ٹولز استعمال کیے گئے: کافکا، کلک ہاؤس، ایئر فلو، ریڈیس، گرافانا، پوسٹگریس کی ایل، ایس کیو ایل، …
  • ڈیبگ اور ریلیز کے لیے الگ الگ اجزاء کی تعمیر اور جانچ کے لیے پائپ لائنز

پہلے سوالوں میں سے ایک جسے ابتدائی مرحلے میں حل کرنے کی ضرورت ہے وہ یہ ہے کہ کس طرح کسٹم اجزاء کو کسی بھی ماحول (CI/CD) میں تعینات کیا جائے گا۔

ہم نے تیسرے فریق کے اجزاء کو نظامی طور پر انسٹال کرنے اور انہیں نظامی طور پر اپ ڈیٹ کرنے کا فیصلہ کیا۔ C++ یا Python میں تیار کردہ اپنی مرضی کے مطابق ایپلی کیشنز کو کئی طریقوں سے تعینات کیا جا سکتا ہے۔ ان میں، مثال کے طور پر: سسٹم پیکجز بنانا، انہیں جمع شدہ امیجز کے ذخیرے میں بھیجنا اور ان کے بعد سرور پر انسٹال کرنا۔ پہلے سے ہی نامعلوم وجہ سے، ایک اور طریقہ کا انتخاب کیا گیا، یعنی: CI کا استعمال کرتے ہوئے، ایپلیکیشن ایگزیکیوٹیبل فائلیں مرتب کی جاتی ہیں، ایک ورچوئل پروجیکٹ ماحول بنایا جاتا ہے، requirements.txt سے py ماڈیولز انسٹال کیے جاتے ہیں، اور یہ تمام نمونے کنفیگرز، اسکرپٹس کے ساتھ بھیجے جاتے ہیں۔ سرورز کے ساتھ درخواست کا ماحول۔ اس کے بعد، ایڈمنسٹریٹر کے حقوق کے بغیر ایک ورچوئل صارف سے ایپلیکیشنز لانچ کی جاتی ہیں۔

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 *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. تیاری_init.d.py - ٹیمپلیٹ کی بنیاد پر اسٹارٹ اسٹاپ اجزاء کے لیے اسکرپٹ تیار کرتا ہے۔
  4. deploy.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

اگلا، ہم نے CI سے نوڈس کے ریموٹ مینجمنٹ کے لحاظ سے Gitlab-CI اور Swarm نوڈس کو جوڑ دیا: سرٹیفکیٹ انسٹال کرنا، خفیہ متغیرات کو ترتیب دینا، اور کنٹرول سرور پر 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

مذکورہ کوڈ کے ٹکڑے سے یہ واضح ہے کہ پائپ لائنز (تعیناتی_اسٹیجنگ، اسٹاپ_اسٹیجنگ) میں دو بٹن شامل کیے گئے ہیں جن کے لیے دستی کارروائی کی ضرورت ہے۔

ڈوکر سوارم کا استعمال کرتے ہوئے ایپلی کیشنز کو تعینات کریں۔
اسٹیک کا نام برانچ کے نام سے میل کھاتا ہے اور یہ انفرادیت کافی ہونی چاہیے۔ اسٹیک میں خدمات منفرد IP پتے، اور بندرگاہیں، ڈائریکٹریز وغیرہ وصول کرتی ہیں۔ الگ تھلگ کیا جائے گا، لیکن اسٹیک سے اسٹیک تک ایک جیسا (چونکہ کنفیگریشن فائل تمام اسٹیکوں کے لیے ایک جیسی ہے) - یہی ہم چاہتے تھے۔ ہم اسٹیک (کلسٹر) کا استعمال کرتے ہوئے تعینات کرتے ہیں۔ docker compose.yml، جو ہمارے کلسٹر کی وضاحت کرتا ہے۔

docker 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 پر مبنی) کو حسب ضرورت اجزاء کے عمومی پول سے الگ کیا جاتا ہے (منصوبوں میں، حسب ضرورت اجزاء کو خدمات کے طور پر بھی تقسیم کیا جاتا ہے)۔ ہمارے کلسٹر کی تعیناتی کا مرحلہ سی ایم ڈی کو ہماری ایک بڑی ترتیب شدہ تصویر میں منتقل کرنے جیسا لگتا ہے اور عام طور پر، حصہ I میں بیان کردہ تعیناتی سے عملی طور پر مختلف نہیں ہے۔ میں فرق پر زور دوں گا:

  • گٹ کلون... - ہمیں تعیناتی انجام دینے کے لیے ضروری فائلیں ملتی ہیں (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

docker-compose-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

لہٰذا، الگ تھلگ سٹیجنگ کلسٹرز کی تعیناتی عمل میں لائی گئی ہے اور ڈویلپرز اب انہیں اپنے کاموں کی جانچ کے لیے کسی بھی مقدار میں کافی مقدار میں لانچ کر سکتے ہیں۔

مستقبل کے منصوبے:

  • ہمارے اجزاء کو خدمات کے طور پر الگ کریں۔
  • ہر ایک کے لیے ایک ڈاکر فائل بنائیں
  • اسٹیک میں خودکار طور پر کم بھری ہوئی نوڈس کا پتہ لگائیں۔
  • نام کے سانچے کا استعمال کرتے ہوئے نوڈس کی وضاحت کریں (مضمون میں آئی ڈی استعمال کرنے کے بجائے)
  • ایک چیک شامل کریں کہ اسٹیک تباہ ہو گیا ہے۔
  • ...

کے لیے خصوصی شکریہ مضمون.

ماخذ: www.habr.com

نیا تبصرہ شامل کریں