El sistema de recomanació de contingut de vídeo en línia en el qual estem treballant és un desenvolupament comercial tancat i tècnicament és un clúster multicomponent de components propietaris i de codi obert. L'objectiu d'escriure aquest article és descriure la implementació del sistema d'agrupament de docker swarm per a un lloc de prova sense interrompre el flux de treball establert dels nostres processos en un temps limitat. La narració presentada a la vostra atenció està dividida en dues parts. La primera part descriu CI / CD abans d'utilitzar Docker Swarm, i la segona descriu el procés de la seva implementació. Aquells que no estiguin interessats en llegir la primera part poden passar amb seguretat a la segona.
Primera part
A l'any llunyà i llunyà, va ser necessari configurar el procés CI / CD el més ràpidament possible. Una de les condicions era no utilitzar Docker per al desplegament components desenvolupats per diverses raons:
- per a un funcionament més fiable i estable dels components en Producció (és a dir, de fet, el requisit de no utilitzar la virtualització)
- els principals desenvolupadors no volien treballar amb Docker (estrany, però així era)
- segons les consideracions ideològiques de la gestió de l'R+D
La infraestructura, la pila i els requisits inicials aproximats per a MVP es van presentar de la següent manera:
- 4 servidors Intel® X5650 amb Debian (una màquina més potent està totalment desenvolupada)
- El desenvolupament de components personalitzats propis es realitza en C++, Python3
- Eines principals de tercers utilitzades: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
- Conduccions per construir i provar components per separat per a la depuració i el llançament
Una de les primeres preguntes que cal abordar en l'etapa inicial és com es desplegaran els components personalitzats en qualsevol entorn (CI/CD).
Vam decidir instal·lar components de tercers de manera sistèmica i actualitzar-los sistemàticament. Les aplicacions personalitzades desenvolupades en C++ o Python es poden desplegar de diverses maneres. Entre ells, per exemple: crear paquets del sistema, enviar-los al repositori d'imatges construïdes i després instal·lar-los en servidors. Per una raó desconeguda, es va triar un altre mètode, és a dir: mitjançant CI, es compilen els fitxers executables de l'aplicació, es crea un entorn de projecte virtual, s'instal·len mòduls py des de requirements.txt i tots aquests artefactes s'envien juntament amb configuracions, scripts i el entorn d'aplicació que l'acompanya als servidors. A continuació, s'inicien les aplicacions com a usuari virtual sense drets d'administrador.
Es va triar Gitlab-CI com a sistema CI/CD. El pipeline resultant semblava a això:
Estructuralment, gitlab-ci.yml tenia aquest aspecte
---
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
Val la pena assenyalar que el muntatge i les proves es realitzen a la seva pròpia imatge, on ja s'han instal·lat tots els paquets de sistema necessaris i s'han fet altres configuracions.
Tot i que cadascun d'aquests guions a les feines és interessant a la seva manera, però per descomptat no en parlaré.La descripció de cadascun d'ells trigarà molt de temps i aquest no és l'objectiu de l'article. Només cridaré la vostra atenció sobre el fet que l'etapa de desplegament consisteix en una seqüència d'scripts de trucada:
- createconfig.py - crea un fitxer settings.ini amb la configuració dels components en diversos entorns per al desplegament posterior (preproducció, producció, proves, ...)
- install_venv.sh - crea un entorn virtual per als components py en un directori específic i el copia a servidors remots
- prepare_init.d.py — prepara scripts d'inici i parada per al component basant-se en la plantilla
- deploy.py - descompon i reinicia nous components
El temps va passar. L'etapa de muntatge va ser substituïda per la preproducció i la producció. S'ha afegit suport per al producte en una distribució més (CentOS). S'han afegit 5 servidors físics més potents i una dotzena de virtuals. I cada vegada era més difícil per als desenvolupadors i provadors provar les seves tasques en un entorn més o menys proper a l'estat de treball. En aquest moment, va quedar clar que era impossible prescindir d'ell...
Part II
Per tant, el nostre clúster és un sistema espectacular d'un parell de dotzenes de components separats que Dockerfiles no descriu. Només podeu configurar-lo per al desplegament en un entorn específic en general. La nostra tasca és desplegar el clúster en un entorn de prova per provar-lo abans de la prova prèvia al llançament.
Teòricament, hi pot haver diversos clústers que s'executen simultàniament: tants com tasques hi hagi en estat completat o properes a la finalització. Les capacitats dels servidors de què disposem ens permeten executar diversos clústers a cada servidor. Cada clúster de prova ha d'estar aïllat (no hi ha d'haver intersecció en ports, directoris, etc.).
El nostre recurs més valuós és el nostre temps, i no en teníem gaire.
Per començar més ràpid, vam triar Docker Swarm per la seva senzillesa i flexibilitat arquitectònica. El primer que vam fer va ser crear un gestor i diversos nodes als servidors remots:
$ 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
A continuació, creeu una xarxa:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
A continuació, vam connectar els nodes Gitlab-CI i Swarm pel que fa al control remot dels nodes des de CI: instal·lació de certificats, configuració de variables secretes i configuració del servei Docker al servidor de control. Aquest
A continuació, hem afegit feines de creació i destrucció de piles a .gitlab-ci .yml.
S'han afegit algunes feines més a .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
Des del fragment de codi anterior, podeu veure que s'han afegit dos botons (deploy_staging, stop_staging) a Pipelines, que requereixen una acció manual.
El nom de la pila coincideix amb el nom de la branca i aquesta singularitat hauria de ser suficient. Els serveis de la pila reben adreces IP úniques i ports, directoris, etc. estarà aïllat, però igual de pila en pila (perquè el fitxer de configuració és el mateix per a totes les piles), el que volíem. Despleguem la pila (clúster) utilitzant docker-compose.yml, que descriu el nostre clúster.
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
Aquí podeu veure que els components estan connectats per una xarxa (nw_swarm) i estan disponibles entre ells.
Els components del sistema (basats en redis, mysql) es separen del conjunt general de components personalitzats (en els plans i els personalitzats es divideixen com a serveis). L'etapa de desplegament del nostre clúster sembla passar CMD a la nostra gran imatge configurada i, en general, pràcticament no difereix del desplegament descrit a la part I. Destacaré les diferències:
- git clon... - obtenir els fitxers necessaris per implementar (createconfig.py, install_venv.sh, etc.)
- arrissol... && descomprimir... - descarregar i descomprimir artefactes de construcció (utilitats compilades)
Només hi ha un problema encara no descrit: els components que tenen una interfície web no són accessibles des dels navegadors dels desenvolupadors. Resolem aquest problema utilitzant el proxy invers, així:
A .gitlab-ci.yml, després de desplegar la pila de clúster, afegim la línia de desplegament de l'equilibrador (que, quan es compromet, només actualitza la seva configuració (crea nous fitxers de configuració nginx segons la plantilla: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - vegeu el codi 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
Als ordinadors de desenvolupament, actualitzeu /etc/hosts; prescriu l'URL a nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Per tant, s'ha implementat el desplegament de clústers de prova aïllats i els desenvolupadors ara els poden executar en qualsevol nombre suficient per comprovar les seves tasques.
Plans futurs:
- Separeu els nostres components com a serveis
- Teniu per a cada Dockerfile
- Detecta automàticament els nodes menys carregats a la pila
- Especifiqueu els nodes pel patró de nom (en lloc d'utilitzar id com a l'article)
- Afegiu una comprovació que la pila està destruïda
- ...
Gràcies especials per
Font: www.habr.com