Meie töötatav online soovituslik videosisustussüsteem on suletud kommertstoode, mis tehniliselt esindab mitmekomponendilist klastri struktuuri, koosnedes nii meie enda kui ka avatud lähtekoodiga komponentidest. Käesoleva artikli eesmärk on kirjeldada docker swarm klastri süsteemi rakendamist staging-keskkonnas, rikkumata meie protsesside kehtestatud töövoogu piiratud aja tingimustes. Esitatud narratiiv on jagatud kaheks osaks. Esimene osa kirjeldab CI/CD-d enne docker swarm kasutuselevõttu ning teine — selle rakendamise protsessi. Need, kes ei soovi lugeda esimest osa, võivad julgelt liikuda teise juurde.
Osa I
Kauges kauges minevikus oli vajalik võimalikult kiiresti seadistada CI/CD protsess. Üheks tingimuseks oli Dockerit mitte kasutada komponentide juurutamiseks mitmel põhjusel:
- komponentide usaldusväärse ja stabiilse töö tagamiseks tootmises (ehk siis, sisuliselt nõue mitte kasutada virtualiseerimist)
- peamised arendajad ei soovinud Dockeriga töötada (imelik, aga nii see oli)
- R&D juhtkonna ideoloogilistel kaalutlustel
MVP infrastruktuuri, tehnoloogiate ja umbkaudsete algsetest nõuetest andmed olid järgmised:
- 4 serverit Intel® X5650 Debianiga (üks võimsam masin ainult arenduseks)
- Oma kohandatud komponentide arendus toimub C++ ja Python3 keeles
- Peamised kolmandate osapoolte tööriistad: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
- Komponentide ehitamise ja testimise pipingud eraldi debug ja release jaoks
Üks esimesi küsimusi, mida algfaasis lahendada tuleb, on, kuidas juurutatakse kohandatud komponente mingis keskkonnas (CI/CD).
Kolmandate osapoolte komponente otsustati hallata süsteemselt ja neid ka süsteemselt uuendada. Kohandatud rakendusi, mis on arendatud C++ või Pythonis, saab aga kasutada mitmel viisil. Näiteks: süsteemipakettide loomine, nende saatmine kogutud piltide hoidlasse ja hilisem installimine serveritesse. Teataval määral tundmatu põhjusel valiti aga teine meetod, nimelt: CI abil kompileeritakse rakenduste käivitatavad failid, luuakse projekti virtuaalne keskkond, installitakse py-moodulid requirements.txt failist ning kõik need artefaktid saadetakse koos konfigureerimisfailide, skriptide ja rakenduste lisakeskkonnaga serveritesse. Edasi käivitatakse rakendused virtuaalse kasutajana, kellel pole administraatori õigusi.
CI/CD süsteemina valiti Gitlab-CI. Valminud pipeline nägi välja umbes nii:

Struktuurselt nägi gitlab-ci.yml välja järgmiselt
---
variables:
# minimaalne CPU versioon serverites, kus kluster välja arendatakse
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
## 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
Oluline on märkida, et koostamine ja testimine toimub omaenda pildil, kus on juba installitud kõik vajalikud süsteemipakettid ja tehtud muud seadistused.
Kuigi iga skript job-ides on huvitav omamoodi, ei hakka ma neist rääkima, kuna nende kohta kirjeldamine võtaks märkimisväärselt aega ja see ei ole artikli eesmärk. Toodan tähelepanu sellele, et deployment'i etapp koosneb skriptide järjestikustest kutsetest:
- createconfig.py — loob settings.ini faili, mis sisaldab komponentide seadeid erinevates keskkondades edasiseks deploiimiseks (Eelkvaliteet, Tootmine, Testimine, …)
- install_venv.sh — loob virtuaalse keskkonna py-komponentide jaoks kindlas kaustas ja kopeerib selle kaugserveritesse
- prepare_init.d.py — valmistab ette komponentide käivitus- ja peatamis skripte mallide põhjal
- deploy.py — paigutab ja taaskäivitab uusi komponente
Aeg möödus. Staging' etapp asendati eelkvaliteediga ja tootmisega. Toote tugi lisandus veel ühes distributsioonis (CentOS). Lisandusid veel 5 võimsat füüsilist serverit ja kümme virtuaalset. Arendajatel ja testijatel muutus järjest keerulisem oma ülesandeid katsetada keskkonnas, mis oleks veidi rohkem tööolekule lähedane. Sel ajal sai selgeks, et sellest ei saa ilma jääda...
Osa II

Nii et meie klaster on päris eksootiline süsteem, mis koosneb paarikümnest eraldi komponendist, mis ei ole kirjeldatud Dockerfile'ide kaudu. Selle konfigureerimine kindlasse keskkonda deploiimiseks on võimalik ainult üldiselt. Meie ülesanne on deploida klaster staging-keskkonda selle katsetamiseks enne eelravimiste testimist.
Teoreetiliselt võib korraga olla mitu töötavat klastrit: nii palju kui on ülesandeid lõpetamisprotsessis või sellele lähedal. Meie käsutuses olev serverite võimsus võimaldab käivitada mitu klastrit igas serveris. Iga staging-klaster peab olema isoleeritud (portide, kaustade jne ülekatte vältimiseks).
Kõige väärtuslikum ressurs on meie aeg, ja seda oli meil vähe.
Kiirema käivituse jaoks valisime Docker Swarmi selle lihtsuse ja arhitektuuri paindlikkuse tõttu. Esimene asi, mida tegime, oli luua kaugserverites haldur ja mitu nodi:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kilqc94pi2upzvabttikrfr5d nop-test-1 Valmis Aktivne 19.03.2
jilwe56pl2zvabupryuosdj78 nop-test-2 Valmis Aktivne 19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 * nop-test-3 Valmis Aktivne Juht 19.03.2
Seejärel loodi võrk:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Seejärel sidusime Gitlab-CI ja Swarmi sõlmed CI kaugjuhtimise osas: sertifikaatide seadistamine, saladusmuutujate seadistamine, samuti Docker teenuse seadistamine haldusteenuses. See säästis meelt lähedane aega.
Seejärel lisasime .gitlab-ci .yml-infose stäki loomise ja hävitamise tööülesandeid.
.gitlab-ci .yml lisandus veel mõned tööülesanded
## 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
Ülaltoodud koodilõigust on näha, et Pipelines'i on lisatud kaks nuppu (deploy_staging, stop_staging), mis nõuavad käsitsi sekkumist.

Stäki nimi vastab haru nimele ja see ainulaadsus peaks olema piisav. Teenused stäkis saavad unikaalsed ip-aadressid ja sadamad, kataloogid jne on isolatsioonis, kuid igas stäkis ühesugused (kuna konfiguratsioonifail on kõikide stäkide jaoks ühesugune) — seda, mida me soovisime. Stäkki (kluster) käivitame docker-compose.yml, kus on kirjeldatud meie kloster.
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
Siit on näha, et komponendid on ühendatud ühe võrguga (nw_swarm) ja saavad üksteisele ligi.
Süsteemikomponendid (baseeruvad redis'il, mysql'il) on eraldatud kohandatud komponentide üldisest basseinist (plaanides on ka kohandatud eraldada teenustena). Meie klastri juurutamise etapp näeb välja nagu CMD edastamine meie ühte suuresse konfigureeritud image'i ning ei erine üldiselt juurutamisest, mis on kirjeldatud I osas. Toodud ajakohasused:
- git clone … — saame failid, mis on vajalikud juurutamiseks (createconfig.py, install_venv.sh jne.)
- curl… && unzip … — laadime alla ja dekomprimeerime kogumise artefaktid (kompileeritud utiliidid)
Alles, mis on veel lahtine probleem: komponentide veebiliidesed ei ole arendajate brauseritest ligipääsetavad. Me lahendame selle probleemi reverse proxy abil järgmisel viisil:
Failis .gitlab-ci.yml, pärast klastrite kasitluse depoolimist, lisame tasakaalustaja depoolimise rea (mis commitide ajal värskendab ainult oma konfiguratsiooni (loon uusi nginx konfiguratsioonifaile mallist: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) — vt koodi 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
Arendajate arvutites uuendame /etc/hosts; kirjutame url nginx'i juurde:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Nii, isoleeritud staging-klastrite depoolimine on ellu viidud ning arendajad saavad nüüd neid käivitada piisavas koguses oma ülesannete kontrollimiseks.
Edasised plaanid:
- Meie komponentide jagamine teenusteks
- Iga Dockerfile'i loomine
- Automaatne vähem koormatud sõlmedes tuvastamine klastris
- Noodide määramine nime mallide järgi (mitte kasutada id-d nagu artiklis)
- Kontrollida, et klaster on hävitatud
- …
Eriline tänu .
Allikas: habr.com
