Системаи тавсияи мундариҷаи видеоии онлайн, ки мо дар он кор карда истодаем, як рушди пӯшидаи тиҷоратӣ буда, аз ҷиҳати техникӣ кластери бисёрҷонибаи ҷузъҳои хусусӣ ва кушодаасос мебошад. Мақсади навиштани ин мақола тавсифи татбиқи системаи кластеркунии тӯдаи докер барои платформаи статсионарӣ, бидуни халалдор кардани ҷараёни муқарраршудаи равандҳои мо дар шароити маҳдуди вақт мебошад. Ҳикояе, ки ба диққати шумо пешниҳод шудааст, ба ду қисм тақсим шудааст. Қисми якум 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 интихоб карда шуд. Қубури натиҷавӣ чунин менамуд:
Сохтор, 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
Қобили зикр аст, ки васлкунӣ ва озмоиш аз рӯи тасвири худ анҷом дода мешавад, ки дар он ҳама бастаҳои зарурии система аллакай насб карда шудаанд ва танзимоти дигар сохта шудаанд.
Гарчанде ки ҳар яке аз ин скриптҳо дар ҷойҳои корӣ ба таври худ ҷолибанд, ман албатта дар бораи онҳо сӯҳбат намекунам; тавсифи ҳар яки онҳо вақти зиёдеро талаб мекунад ва ин ҳадафи мақола нест. Иҷозат диҳед таваҷҷӯҳи шуморо ба он ҷалб намоям, ки марҳилаи ҷойгиркунӣ аз пайдарпаии скриптҳои занг иборат аст:
- createconfig.py — файли settings.ini бо танзимоти ҷузъҳо дар муҳитҳои гуногун барои ҷойгиркунии минбаъда эҷод мекунад (Пешистеҳсол, Истеҳсол, Санҷиш, ...)
- install_venv.sh — барои ҷузъҳои py дар директорияи мушаххас муҳити виртуалӣ эҷод мекунад ва онро ба серверҳои дурдаст нусхабардорӣ мекунад
- Preparat_init.d.py — дар асоси шаблон скриптхоро барои ҷузъҳои start-stop омода мекунад
- deploy.py — ҷузъҳои навро ҷойгир ва аз нав оғоз мекунад
Вақт гузашт. Марҳилаи саҳнасозӣ бо пеш аз истеҳсол ва истеҳсол иваз карда шуд. Дастгирии маҳсулот дар як тақсимоти дигар (CentOS) илова карда шуд. Боз 5 сервери тавонои физикӣ ва даҳҳо серверҳои виртуалӣ илова карда шуданд. Ва барои таҳиягарон ва озмоишгарон санҷиши вазифаҳои худро дар муҳити кам ё камтар ба ҳолати корӣ наздиктар душвортар мешуд. Дар ин вакт маълум шуд, ки бе у кор кардан мумкин нест...
Қисми II
Ҳамин тавр, кластери мо як системаи аҷибест аз якчанд даҳҳо ҷузъҳои инфиродӣ, ки аз ҷониби 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) илова карда шудаанд, ки амали дастӣ талаб мекунанд.
Номи стек ба номи филиал мувофиқат мекунад ва ин беназирӣ бояд кофӣ бошад. Хидматҳо дар стек суроғаҳои беназири 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, ки дар мақола аст)
- Иловаи чек, ки стек нобуд шудааст
- ...