U sistema di ricunniscenza di cuntenutu video in linea chì avemu travagliatu hè un sviluppu cummerciale chjusu è tecnicamente hè un cluster multi-cumpunente di cumpunenti proprietarii è open source. U scopu di scrive stu articulu hè di discrive l'implementazione di u sistema di clustering docker swarm per un situ di staging senza disturbà u flussu di travagliu stabilitu di i nostri prucessi in un tempu limitatu. A narrativa presentata à a vostra attenzione hè divisa in dui parti. A prima parte descrive CI / CD prima di utilizà docker swarm, è a seconda descrive u prucessu di a so implementazione. Quelli chì ùn anu micca interessatu à leghje a prima parte pò passà in modu sicuru à a seconda.
Parte I
Torna in l'annu distante, distante, era necessariu di stallà u prucessu CI / CD u più prestu pussibule. Una di e cundizioni ùn era micca aduprà Docker per l'implementazione cumpunenti sviluppati per parechje ragioni:
- per un funziunamentu più affidabile è stabile di cumpunenti in Produzione (vale à dì, in fatti, u requisitu di ùn aduprà a virtualizazione)
- I sviluppatori principali ùn vulianu micca travaglià cù Docker (stranu, ma hè cusì)
- secondu e considerazioni ideologiche di a gestione di R&D
L'infrastruttura, a pila è i requisiti iniziali apprussimati per MVP sò stati presentati cusì:
- 4 servitori Intel® X5650 cù Debian (una macchina più putente hè sviluppata cumplettamente)
- U sviluppu di i propri cumpunenti persunalizati hè realizatu in C ++, Python3
- Principali strumenti di terze parti utilizati: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, ...
- Pipelines per custruisce è teste cumpunenti separatamente per debug è liberazione
Una di e prime dumande chì deve esse trattatu in a fase iniziale hè cumu si metteranu cumpunenti persunalizati in ogni ambiente (CI / CD).
Avemu decisu di stallà cumpunenti di terzu in modu sistematicu è aghjurnà in modu sistemicu. L'applicazioni persunalizate sviluppate in C++ o Python ponu esse implementate in parechje manere. Frà elli, per esempiu: creazione di pacchetti di sistema, inviendu à u repository di l'imaghjini custruiti è poi installendu in i servitori. Per una ragione scunnisciuta, hè statu sceltu un altru metudu, vale à dì: usendu CI, i schedarii eseguibili di l'applicazioni sò compilati, un ambiente di prughjettu virtuale hè creatu, i moduli py sò stallati da requirements.txt, è tutti questi artefatti sò mandati cù configs, scripts è u accumpagna l'ambiente di l'applicazione à i servitori. Dopu, l'applicazioni sò lanciate cum'è un utilizatore virtuale senza diritti di amministratore.
Gitlab-CI hè statu sceltu cum'è u sistema CI / CD. U pipeline risultante pareva cusì cusì:
Strutturalmente, gitlab-ci.yml pareva cusì
---
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
Hè da nutà chì l'assemblea è a prova sò realizati nantu à a so propria maghjina, induve tutti i pacchetti di sistema necessarii sò digià stallati è altri paràmetri sò stati fatti.
Ancu ognuna di sti scripts in impieghi hè interessante in u so modu, ma di sicuru ùn ne parleraghju micca.. A descrizzione di ognuna di elli hà da piglià assai tempu è questu ùn hè micca u scopu di l'articulu. Aghju attiratu solu a vostra attenzione à u fattu chì a tappa di implementazione hè custituita da una sequenza di script di chjama:
- createconfig.py - crea un schedariu settings.ini cù paràmetri di cumpunenti in diversi ambienti per a implementazione successiva (Preproduzione, Produzione, Test, ...)
- install_venv.sh - crea un ambiente virtuale per i cumpunenti py in un repertoriu specificu è u copia à i servitori remoti
- prepare_init.d.py - prepara script start-stop per u cumpunente basatu annantu à u mudellu
- deploy.py - decompose è riavvia novi cumpunenti
U tempu passava. A tappa di staging hè stata rimpiazzata da a preproduzione è a pruduzzione. Aghjunghje supportu per u pruduttu nantu à una distribuzione più (CentOS). Aghjunghjite 5 servitori fisichi più putenti è una decina di virtuale. È hè diventatu di più in più difficiuli per i sviluppatori è i testatori di pruvà i so compiti in un ambiente più o menu vicinu à u statu di travagliu. À questu tempu, hè diventatu chjaru chì era impussibile di fà senza ellu ...
Parte II
Dunque, u nostru cluster hè un sistema spettaculare di un coppiu di decine di cumpunenti separati chì ùn sò micca descritti da Dockerfiles. Pudete solu cunfigurà per a implementazione in un ambiente specificu in generale. U nostru compitu hè di implementà u cluster in un ambiente di staging per pruvà prima di teste di pre-liberazione.
Teoricamente, ci ponu esse parechje clusters chì currenu simultaneamente: quant'è ci sò compiti in u statu finitu o vicinu à a fine. E capacità di i servitori à a nostra dispusizione ci permettenu di eseguisce parechji clusters in ogni servitore. Ogni cluster di staging deve esse isolatu (ùn deve esse micca intersezzione in porti, cartulari, etc.).
A nostra risorsa più preziosa hè u nostru tempu, è ùn avemu micca assai di questu.
Per un principiu più veloce, avemu sceltu Docker Swarm per a so simplicità è a flessibilità di l'architettura. A prima cosa chì avemu fattu hè di creà un manager è parechji nodi nantu à i servitori remoti:
$ 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
Dopu, crea una reta:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
In seguitu, avemu cunnessu i nodi Gitlab-CI è Swarm in quantu à u cuntrollu remotu di i nodi da CI: installà certificati, stabilisce variàbili secreti, è stabilisce u serviziu Docker in u servitore di cuntrollu. Questu
In seguitu, aghjunghjemu i travaglii di creazione è distruzzione di stack à .gitlab-ci .yml.
Uni pochi di più impieghi sò stati aghjuntu à .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
Da u snippet di codice sopra, pudete vede chì dui buttoni (deploy_staging, stop_staging) sò stati aghjuntu à Pipelines, chì necessitanu l'azzione manuale.
U nome di stack currisponde à u nome di a filiera è questa unicità deve esse abbastanza. I servizii in a pila ricevenu indirizzi IP unichi, è porti, cartulari, etc. serà isolatu, ma u listessu da stack à stack (perchè u schedariu di cunfigurazione hè u listessu per tutti i stacks) - ciò chì vulemu. Implementemu a pila (cluster) usendu docker-compose.yml, chì descrive u nostru cluster.
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
Quì pudete vede chì i cumpunenti sò cunnessi da una reta (nw_swarm) è sò dispunibuli per l'altri.
I cumpunenti di u sistema (basatu nantu à redis, mysql) sò siparati da u gruppu generale di cumpunenti persunalizati (in i piani è quelli persunalizati sò spartuti cum'è servizii). A tappa di implementazione di u nostru cluster s'assumiglia à passà CMD in a nostra una grande maghjina cunfigurata è, in generale, praticamenti ùn difiere micca da a implementazione descritta in a Parte I. Evideraghju e differenze:
- git clone... - uttene i fugliali necessarii per implementà (createconfig.py, install_venv.sh, etc.)
- arriccia... && unzip... - scaricate è scomprime l'artefatti di custruzzione (utilità cumpilate)
Ci hè solu un prublema ancu micca descrittu: i cumpunenti chì anu una interfaccia web ùn sò micca accessibili da i navigatori di sviluppatori. Risolvemu stu prublema usendu proxy inversu, cusì:
In .gitlab-ci.yml, dopu avè implementatu u cluster stack, aghjunghjemu a linea di implementazione di u balancer (chì, quandu si cummette, aghjurnà solu a so cunfigurazione (crea novi schedarii di cunfigurazione nginx secondu u mudellu: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - vede u codice 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
Nant'à i computer di sviluppu, aghjurnà /etc/hosts; prescrive l'url à nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Dunque, a implementazione di clusters di staging isolati hè stata implementata è i sviluppatori ponu avà eseguisce in ogni numeru abbastanza per verificà e so attività.
Piani futuri:
- Separate i nostri cumpunenti cum'è servizii
- Avete per ogni Dockerfile
- Detecta automaticamente i nodi menu caricati in a pila
- Specificà i nodi per u mudellu di nome (piuttostu cà aduprà l'id cum'è in l'articulu)
- Aghjunghjite un verificatu chì a pila hè distrutta
- ...
Grazie speciale per
Source: www.habr.com