Mūsu izstrādātā video satura tiešsaistes ieteikumu sistēma ir slēgta komerciāla izstrāde, kas tehniski ir daudzkomponentu klasteris no privātiem un atvērtā koda komponentiem. Šī raksta mērķis ir aprakstīt docker swarm klasterizācijas sistēmas ieviešanu stāvvietā, nepārkāpjot mūsu procesu darba plūsmu ierobežota laika apstākļos. Jūsu uzmanībai piedāvātais stāstījums ir sadalīts divās daļās. Pirmajā daļā tiek aprakstīts CI/CD pirms docker swarm izmantošanas, bet otrajā — tā ieviešanas process. Tie, kas nav ieinteresēti lasīt pirmo daļu, var droši pāriet uz otro.
1. daļa
Tālā, tālā gadā bija nepieciešams pēc iespējas ātrāk uzstādīt CI/CD procesu. Viens no nosacījumiem bija neizmantot Docker izvietošanai izstrādātajiem komponentiem vairākos iemeslu dēļ:
- lai nodrošinātu uzticamāku un stabilāku komponentu darbību ražošanā (t.i., būtībā prasība neizmantot virtualizāciju)
- galvenie izstrādātāji negribēja strādāt ar Docker (dīvaini, bet tā tiešām bija)
- izpētes un attīstības vadības idejisku iemeslu dēļ
Infrastruktūra, steks un aptuvenās prasības MVP bija šādas:
- 4 serveri Intel® X5650 ar Debian (viena spēcīgāka mašīna pilnībā izstrādei)
- Pats šo pasūtījuma komponentu izstrāde notiek C++, Python3
- Pamatā izmantotie trešo pušu rīki: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
- Sadaļu izveide un testēšana atsevišķi debug un release
Viens no pirmajiem jautājumiem, kas jāatrisina sākumposmā, ir kā tiks veikta pasūtījuma komponentu izvietošana kādā vidē (CI/CD).
Trešo pušu komponentes nolēma instalēt un atjaunināt sistēmiski. Tomēr pielāgotās lietojumprogrammas, kas izstrādātas C++ vai Python valodā, var izvietot vairākos veidos. Starp tiem ir, piemēram: sistēmas pakotņu izveide, to nosūtīšana uz sagatavoto attēlu krātuvi un to turpmāka instalācija serveros. Nezināmu iemeslu dēļ tika izvēlēts cits paņēmiens, proti: izmantojot CI, tiek kompilēti izpildāmi lietojumprogrammu faili, tiek izveidota projekta virtuālā vide, tiek instalēti py moduļi no requirements.txt, un visi šie artefakti tiek nosūtīti kopā ar konfigurācijām, skriptiem un saistīgo pielietojuma vidi uz serveriem. Pēc tam tiek palaistas lietojumprogrammas no virtuālā lietotāja bez administratora tiesībām.
CI/CD sistēmas izvēlēti Gitlab-CI. Iegūtais pipeline izskatījās aptuveni šādi:

Strukturāli gitlab-ci.yml izskatījās šādi
---
variables:
# minimālā CPU versija serveros, uz kuriem tiek izvietots klasteris
CMAKE_CPUTYPE: "westmere"
DEBIAN: "MYREGISTRY:5000/debian:latest"
before_script:
- eval $(ssh-agent -s)
- ssh-add ~/.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
## testēšanas posms
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
## iepriekšējā posms
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
Jāatzīmē, ka izstrāde un testēšana tiek veikta uz sava attēla, kur jau ir instalēti visi nepieciešamie sistēmas pakotnes un veikti citi iestatījumi.
Lai gan katrs no šiem skriptiem darba uzdevumos ir interesants savā ziņā, es protams nestāstīšu par tiem, jo katra aprakstīšana prasītu ievērojamu laiku, un raksta mērķis nav šis. Vēršu uzmanību uz to, ka izvietošanas posms sastāv no secīgas skriptu izsaukšanas:
- createconfig.py — izveido settings.ini failu ar komponentu konfigurācijām dažādās vidēs tālākai izvietošanai (Priekšprodukcija, Ražošana, Testēšana, …)
- install_venv.sh — izveido virtuālo vidēšanu py komponentēm noteiktā direktorijā un kopē to uz attālinātiem serveriem
- prepare_init.d.py — sagatavo komponentu palaišanas un apstāšanās skriptus, pamatojoties uz šablonu
- deploy.py — izvieto un restartē jaunas komponentes
Laiks gāja. Staging posmu nomainīja priekšprodukcija un ražošana. Atbalstīts vēl viens produkta izplatījums (CentOS). Tika pievienoti vēl 5 jaudīgi fiziskie serveri un desmit virtuālie. Un izstrādātājiem un testētājiem kļuva arvien grūtāk pārbaudīt savus uzdevumus vidē, kas vairāk vai mazāk atbilst darba stāvoklim. Šajā laikā kļuva skaidrs, ka bez tā neiztikt…
Otrā daļa

Tātad, mūsu klasteris ir diezgan iespaidīga sistēma no pāris desmitiem atsevišķu komponentu, kas nav aprakstīti ar Dockerfile. To konfigurēt izvietošanai noteiktā vidē var tikai kopumā. Mūsu uzdevums ir izvietot klasteri staging vidē, lai to iztestētu pirms priekšizlaides testēšanas.
Teorētiski, vienlaikus var būt vairāki klasteri: tik, cik uzdevumu ir pabeigtā vai tuvu pabeigtai stāvoklī. Resursi, kas ir mūsu rīcībā esošajos servidores, ļauj palaist vairākus klasterus katrā serverī. Katram staging klasterim jābūt izolētam (nedrīkst būt portu, direktoriju utt. pārklāšanās).
Visvērtīgākais resurss ir mūsu laiks, un tā mums bija maz.
Lai ātrāk uzsāktu, izvēlējāmies Docker Swarm tā vienkāršības un elastīgās arhitektūras dēļ. Pirmā lieta, ko mēs izdarījām, bija izveidot attālinātajos servidores menedžeri un pāris mezglus:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kilqc94pi2upzvabttikrfr5d nop-test-1 Gatavs Aktīvs 19.03.2
jilwe56pl2zvabupryuosdj78 nop-test-2 Gatavs Aktīvs 19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 * nop-test-3 Gatavs Aktīvs Līderis 19.03.2
Tālāk izveidojām tīklu:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Tālāk saistījām Gitlab-CI un Swarm mezglus attiecībā uz tālvadību no CI: sertifikātu uzstādīšana, slepeno mainīgo konfigurēšana, kā arī Docker pakalpojuma konfigurēšana vadības serverī. Šis mums ļoti ietaupīja laiku.
Tālāk pievienojām jobus staka izveidošanai un iznīcināšanai .gitlab-ci.yml.
Tādējādi .gitlab-ci.yml pievienojās vēl dažas job
## 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
No iepriekš minētā koda fragmenta ir redzams, ka Pipelines pievienojās divas pogas (deploy_staging, stop_staging), kas prasa roku iejaukšanos.

Staka nosaukums atbilst zarojuma nosaukumam, un šai unikālitātei vajadzētu būt pietiekamai. Pakalpojumi stakā saņem unikālas IP adreses, bet porti, direktorijas u.t.t. būs izolēti, bet vienādi no staka uz staku (jo konfigurācijas fails ir identisks visiem stakiem) — tieši to mēs arī vēlējāmies. Staku (klasteri) mēs izvēršam, izmantojot docker-compose.yml, kurā ir aprakstīts mūsu klasteris.
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
Šeit redzams, ka komponenti ir apvienoti vienotā tīklā (nw_swarm) un ir pieejami viens otram.
Sistēmas komponenti (balstīti uz redis, mysql) ir atdalīti no kopējā pielāgoto komponentu baseina (plānos ir pielāgoto komponentu sadalīšana kā pakalpojumi). Mūsu klastera izvietošanas posms izskatās kā CMD nodošana mūsu lielajam konfigurētajam attēlam un būtībā praktiski neatšķiras no izvietošanas, kas aprakstīta I daļā. Uzsvēršu atšķirības:
- git clone … — iegūstam failus, kas nepieciešami izvietošanai (createconfig.py, install_venv.sh u.c.)
- curl… && unzip … — lejupielādējam un izrakstām izstrādes artefaktus (kompilētās utilītas)
Ir palikusi tikai viena līdz šim neaprakstīta problēma: komponenti, kuriem ir tīmekļa saskarne, nav pieejami izstrādātāju pārlūkprogrammās. Mēs šo problēmu risinām ar reverso proksi, šādā veidā:
Failā .gitlab-ci.yml pēc klastera steka izvietošanas pievienojam rindu slodzes izlīdzinātāja izvietošanai (kas, pievienojot izmaiņas, tikai atjauno savu konfigurāciju (izveido jaunus konfigurācijas failus nginx pēc parauga: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) — skat. kods 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
Izstrādātāju datoros atjaunojam /etc/hosts; ierakstām url līdz nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Tātad, izolētu staging-klasteru izvietošana ir īstenota, un izstrādātāji tagad var tos palaist vajadzīgā skaitā, lai pārbaudītu savus uzdevumus.
Nākamie plāni:
- Sadalīt mūsu komponentus kā pakalpojumus
- Pievienot katram Dockerfile
- Automātiski noteikt mazāk noslogotas mezglus stekā
- Noteikt mezglus pēc nosaukuma parauga (nevis izmantot id kā rakstā)
- Pievienot pārbaudi, ka steks ir iznīcināts
- …
Atsevišķa pateicība par .
Avots: habr.com
