Den online anbefalingssystemet for videoinnhold som vi jobber med, er en lukket kommersiell utvikling og utgjĂžr teknisk sett en multi-komponentklynge av egne og open source-komponenter. MĂ„let med denne artikkelen er Ă„ beskrive implementeringen av docker swarm-klyngesystemet for staging-miljĂžet, uten Ă„ forstyrre den etablerte arbeidsflyten vĂ„r innenfor begrenset tid. Den fortellingen som presenteres for deg, er delt inn i to deler. Den fĂžrste delen beskriver CI/CD fĂžr bruken av docker swarm, og den andre â prosessen med dens implementering. De som ikke er interessert i Ă„ lese den fĂžrste delen, kan trygt gĂ„ videre til den andre.
Del I
I et fjernt Är var det nÞdvendig Ä sette opp CI/CD-prosessen sÄ raskt som mulig. En av betingelsene var Ä ikke bruke Docker til distribusjon av utviklede komponenter av flere grunner:
- for mer pÄlitelig og stabil drift av komponentene i produksjon (det vil si kravet om ikke Ä bruke virtualisering)
- de ledende utviklerne Ăžnsket ikke Ă„ jobbe med Docker (merkelig, men det var faktisk slik)
- av ideologiske grunner fra ledelsen i R&D
Infrastrukturen, staken og de omtrentlige spesifikasjonene for MVP ble presentert som fĂžlger:
- 4 servere IntelÂź X5650 med Debian (en kraftigere maskin fullt ut til utvikling)
- Utviklingen av egne tilpassede komponenter foregÄr i C++, Python3
- De viktigste 3rdparty-brukte verktĂžyene: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, âŠ
- Bygg- og testpipelines for komponenter separat for debug og release
Ett av de fÞrste spÞrsmÄlene som mÄ lÞses i den tidlige fasen, er hvordan distribusjonen av tilpassede komponenter vil finne sted i et hvilket som helst miljÞ (CI/CD).
Tredjeparter har besluttet Ä installere og oppdatere systemisk. Tilpassede applikasjoner, utviklet i C++ eller Python, kan implementeres pÄ flere mÄter. Blant dem er: opprettelse av systempakker, sending av disse til et repository av kompilte bilder og deres pÄfÞlgende installasjon pÄ serverne. Av en ukjent grunn ble en annen metode valgt, nemlig: med CI kompilert kjÞrbare filer for applikasjoner, opprettet et virtuelt miljÞ for prosjektet, installert py-moduler fra requirements.txt, og alle disse artefaktene blir sendt sammen med konfigurasjoner, skript og tilhÞrende applikasjonsmiljÞer til serverne. Deretter startes applikasjonene av en virtuell bruker uten administratorrettigheter.
Gitlab-CI ble valgt som CI/CD-system. Den resulterende pipeline sÄ omtrent slik ut:

Strukturelt sÄ gitlab-ci.yml slik ut
---
variables:
# minimum CPU-versjon pÄ serverne der klyngen deployeres
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
Det er viktig Ä merke seg at byggingen og testing skjer pÄ sitt eget bilde, hvor alle nÞdvendige systempakker allerede er installert, og andre konfigurasjoner er gjort.
Selv om hver av disse skriptene i jobber har sin egen interesse, kommer jeg selvfÞlgelig ikke til Ä forklare dem da beskrivelsen av hver av dem ville ta betydelig tid, og det er ikke mÄlet med artikkelen. Jeg vil bare pÄpeke at distribusjonsfasen bestÄr av en sekvens av skriptoppringninger:
- createconfig.py â oppretter filen settings.ini med komponentinnstillinger i forskjellige miljĂžer for videre distribusjon (Preproduction, Production, Testing, âŠ)
- install_venv.sh â oppretter et virtuelt miljĂž for py-komponentene i en spesifikk katalog og kopierer det til eksterne servere
- prepare_init.d.py â forbereder start- og stopp-skriptene for komponentene basert pĂ„ en mal
- deploy.py â distribuerer og gjenstarter nye komponenter
Tiden gikk. Staging-fasen ble erstattet av preproduksjon og produksjon. StĂžtte for produktet ble lagt til i enda en distribusjon (CentOS). Fem kraftige fysiske servere og et dusin virtuelle ble lagt til. Utviklerne og testerne fikk det stadig vanskeligere Ă„ teste oppgavene sine i et miljĂž som var mer eller mindre likt det operative. Det ble klart i denne perioden at vi ikke kunne klare oss uten detâŠ
Del II

SĂ„, klustret vĂ„rt fremstĂ„r som et imponerende syn, et system bestĂ„ende av et par dusin separate komponenter som ikke er beskrevet av Dockerfile-filer. Ă
konfigurere det for distribusjon til et spesifikt miljÞ kan kun gjÞres som helhet. VÄrt mÄl er Ä distribuere klustret til staging-miljÞet for testing fÞr pre-release.
Teoretisk kan det vÊre flere klustre som kjÞrer samtidig: sÄ mange som oppgavene som er i fullfÞrt eller nÊr fullfÞrt tilstand. Kraften som er tilgjengelig pÄ serverne vÄre tillater oss Ä kjÞre flere klustre pÄ hver server. Hvert staging-kluster mÄ vÊre isolert (det mÄ ikke vÊre overlapping nÄr det gjelder porter, kataloger osv.).
Den mest verdifulle ressursen er tiden vÄr, og vi hadde lite av den.
For Ä starte raskere valgte vi Docker Swarm pÄ grunn av dens enkelhet og fleksibilitet. Det fÞrste vi gjorde, var Ä opprette en manager og flere noder pÄ de eksterne serverne:
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kilqc94pi2upzvabttikrfr5d nop-test-1 Klar Aktiv 19.03.2
jilwe56pl2zvabupryuosdj78 nop-test-2 Klar Aktiv 19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 * nop-test-3 Klar Aktiv Leiar 19.03.2
Vidare, oppretta vi eit nettverk:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Vidare, knytte vi Gitlab-CI og Swarm-nodene for fjernadministrasjon av nodene frÄ CI: installasjon av sertifikat, konfigurering av hemmelige variablar, samt konfigurasjon av Docker-tjenesten pÄ styringsserveren. Dette sparte oss mykje tid.
Vidare la vi til jobbar for oppretting og sletting av stakken i .gitlab-ci.yml.
I .gitlab-ci.yml kom det til fleire jobbar
## 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
FrÄ den ovanfor viste kodebiten ser vi at det er lagt til to knappar i Pipelines (deploy_staging, stop_staging) som krev manuell inngriping.

Namn pĂ„ stakken samsvarar med namnet pĂ„ greina, og denne unikheit bĂžr vere tilstrekkeleg. Tjenestene i stakken fĂ„r unike IP-adresser, medan portar, katalogar og sĂ„ vidare vil vere isolerte, men like for kvar stakk (sidan konfigurasjonsfilen er den same for alle stakkar) â noko vi har oppnĂ„dd. Stakken (klynga) rullast ut med hjelp av docker-compose.yml, der klustret vĂ„rt er beskrevet.
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
Her ser ein at komponentane er kopla saman med eit nett (nw_swarm) og er tilgjengelege for kvarandre.
Systemkomponentar (basert pÄ redis, mysql) er separert frÄ den generelle bassenget av tilpassa komponentar (i planane er det ogsÄ tilpassa som skal delast som tenester). Steget for distribusjon av klynga vÄr ser ut som Ä overfÞre CMD til vÄrt eine store konfigurerte bilete, og skiller seg i det heile tatt ikkje frÄ distribusjonen som er beskriven i Del I. Eg vil understreke skillene:
- git clone ⊠â vi hentar fila som er nĂždvendige for Ă„ gjennomfĂžre distribusjonen (createconfig.py, install_venv.sh osv.)
- curl⊠&& unzip ⊠â vi lastar ned og pakkar ut byggartefaktane (kompilerte verktĂžy)
Det er bare ett ubeskrevet problem igjen: komponentene med webgrensesnitt er ikke tilgjengelige fra utviklernes nettlesere. Vi lÞser dette problemet med en reverse proxy pÄ fÞlgende mÄte:
I .gitlab-ci.yml etter distribusjonen av klyngen, legger vi til linjen for distribusjon av lastbalanserer (som kun oppdaterer sin konfigurasjon ved forpliktelse (lager nye konfigurasjonsfiler for nginx etter mal: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) â se koden 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
PĂ„ utviklernes datamaskiner oppdaterer vi /etc/hosts; vi skriver inn URL-en til nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
SÄ, distribusjonen av isolerte staging-klynger er gjennomfÞrt, og utviklerne kan nÄ kjÞre dem i et hvilket som helst tilstrekkelig antall for Ä teste oppgavene sine.
Fremtidige planer:
- Dele komponentene vÄre som tjenester
- Opprette hver Dockerfile
- Automatisk bestemme mindre belastede noder i klyngen
- Sette noder etter navnemal (og ikke bruke id som i artikkelen)
- Legge til sjekk for at klyngen er Ăždelagt
- âŠ
Spesiell takk til .
Kilde: habr.com
