Бо истифода аз Docker Swarm барномаҳоро ҷойгир кунед

Системаи тавсияи мундариҷаи видеоии онлайн, ки мо дар он кор карда истодаем, як рушди пӯшидаи тиҷоратӣ буда, аз ҷиҳати техникӣ кластери бисёрҷонибаи ҷузъҳои хусусӣ ва кушодаасос мебошад. Мақсади навиштани ин мақола тавсифи татбиқи системаи кластеркунии тӯдаи докер барои платформаи статсионарӣ, бидуни халалдор кардани ҷараёни муқарраршудаи равандҳои мо дар шароити маҳдуди вақт мебошад. Ҳикояе, ки ба диққати шумо пешниҳод шудааст, ба ду қисм тақсим шудааст. Қисми якум CI/CD-ро пеш аз истифодаи тӯдаи докер тавсиф мекунад ва қисми дуюм раванди татбиқи онро тавсиф мекунад. Онҳое, ки ба хондани қисми аввал таваҷҷӯҳ надоранд, метавонанд бехатар ба дуюм гузаранд.

Қисми I

Як вактхо зарурати харчи зудтар ба рох мондани процесси CI/CD ба миён омад. Яке аз шартҳо истифода набурдани Docker буд барои ҷойгиркунӣ ҷузъҳо бо якчанд сабабҳо таҳия карда мешаванд:

  • барои кори боэътимод ва устувори ҷузъҳо дар истеҳсолот (яъне, аслан, талабот барои истифодаи виртуализатсия)
  • Таҳиягарони пешбар намехостанд бо Docker кор кунанд (аҷиб, аммо ҳамин тавр буд)
  • бо сабабхои идеологии идоракунии илмй-тадкикотй

Инфрасохтор, стек ва тақрибии талаботҳои ибтидоӣ барои MVP инҳо буданд:

  • 4 сервери Intel® X5650 бо Debian (як мошини пуриқтидори пурра барои рушд)
  • Таҳияи ҷузъҳои фармоишии шахсии шумо дар C++, Python3 амалӣ карда мешавад
  • Асбобҳои асосии тарафи сеюм истифода мешаванд: Кафка, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql,…
  • Қубурҳо барои сохтан ва санҷиши ҷузъҳои алоҳида барои ислоҳ ва озод кардан

Яке аз аввалин саволҳое, ки бояд дар марҳилаи аввал ҳал карда шаванд, ин аст, ки ҷузъҳои фармоишӣ дар ҳама гуна муҳит чӣ гуна ҷойгир карда мешаванд (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. Preparat_init.d.py — дар асоси шаблон скриптхоро барои ҷузъҳои start-stop омода мекунад
  4. deploy.py — ҷузъҳои навро ҷойгир ва аз нав оғоз мекунад

Вақт гузашт. Марҳилаи саҳнасозӣ бо пеш аз истеҳсол ва истеҳсол иваз карда шуд. Дастгирии маҳсулот дар як тақсимоти дигар (CentOS) илова карда шуд. Боз 5 сервери тавонои физикӣ ва даҳҳо серверҳои виртуалӣ илова карда шуданд. Ва барои таҳиягарон ва озмоишгарон санҷиши вазифаҳои худро дар муҳити кам ё камтар ба ҳолати корӣ наздиктар душвортар мешуд. Дар ин вакт маълум шуд, ки бе у кор кардан мумкин нест...

Қисми II

Бо истифода аз 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 илова кардем.

Ба .гитлаб-ци .ймл боз якчанд чои кор илова карда шуд

## 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

Аз порчаи рамзи дар боло овардашуда маълум аст, ки ду тугма ба қубурҳо (deploy_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 ба як тасвири бузурги танзимшудаи мо ба назар мерасад ва дар маҷмӯъ, амалан аз ҷойгиркунии дар Қисми I тавсифшуда фарқ надорад. Ман фарқиятҳоро таъкид хоҳам кард:

  • git clone... — мо файлҳои заруриро барои иҷрои ҷойгиркунӣ мегирем (createconfig.py, install_venv.sh ва ғайра)
  • curl... && кушодан... - Артефактҳои сохтмонро зеркашӣ ва кушоед (утилитҳои тартибдодашуда)

Танҳо як мушкили то ҳол тавсифнашуда вуҷуд дорад: ҷузъҳое, ки интерфейси веб доранд, аз браузерҳои таҳиягарон дастрас нестанд. Мо ин мушкилотро бо истифода аз прокси баръакс ҳал мекунем, ҳамин тавр:

Дар .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

Ҳамин тавр, ҷойгиркунии кластерҳои ҷудогонаи марҳилавӣ амалӣ карда шуд ва таҳиягарон ҳоло метавонанд онҳоро ба ҳар миқдоре, ки барои санҷиши вазифаҳои худ кофӣ ҳастанд, оғоз кунанд.

Нақшаҳои оянда:

  • Ҷузъҳои моро ҳамчун хидматҳо ҷудо кунед
  • Барои ҳар як файли Docker эҷод кунед
  • Гиреҳҳои камтар боршударо дар стек ба таври худкор муайян кунед
  • Гиреҳҳоро бо истифода аз қолаби ном муайян кунед (ба ҷои истифодаи id, ки дар мақола аст)
  • Иловаи чек, ки стек нобуд шудааст
  • ...

Ташаккури махсус барои мақола.

Манбаъ: will.com

Илова Эзоҳ