Systemet for anbefaling av online videoinnhold vi jobber med er en lukket kommersiell utvikling og er teknisk sett en multikomponentklynge av proprietære og åpen kildekodekomponenter. Formålet med å skrive denne artikkelen er å beskrive implementeringen av docker swarm clustering-systemet for et oppsamlingssted uten å forstyrre den etablerte arbeidsflyten til prosessene våre i løpet av en begrenset tid. Fortellingen som presenteres for din oppmerksomhet er delt inn i to deler. Den første delen beskriver CI / CD før du bruker docker swarm, og den andre beskriver prosessen med implementeringen. De som ikke er interessert i å lese den første delen kan trygt gå videre til den andre.
Del I
Tilbake i det fjerne, fjerne året var det nødvendig å sette opp CI/CD-prosessen så raskt som mulig. En av betingelsene var å ikke bruke Docker for utplassering utviklet komponenter av flere grunner:
- for mer pålitelig og stabil drift av komponenter i produksjon (det vil faktisk si kravet om å ikke bruke virtualisering)
- ledende utviklere ønsket ikke å jobbe med Docker (rart, men det var slik det var)
- etter FoU-ledelsens ideologiske betraktninger
Infrastruktur, stabel og omtrentlige startkrav for MVP ble presentert som følger:
- 4 Intel® X5650-servere med Debian (en kraftigere maskin er fullt utviklet)
- Utvikling av egne tilpassede komponenter utføres i C++, Python3
- De viktigste tredjepartsverktøyene som brukes: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
- Rørledninger for å bygge og teste komponenter separat for feilsøking og utgivelse
Et av de første spørsmålene som må tas opp i den innledende fasen er hvordan tilpassede komponenter vil bli distribuert i ethvert miljø (CI / CD).
Vi bestemte oss for å installere tredjepartskomponenter systemisk og oppdatere dem systemisk. Tilpassede applikasjoner utviklet i C++ eller Python kan distribueres på flere måter. Blant dem, for eksempel: å lage systempakker, sende dem til depotet for bygde bilder og deretter installere dem på servere. Av en ukjent grunn ble en annen metode valgt, nemlig: ved bruk av CI kompileres applikasjonskjørbare filer, et virtuelt prosjektmiljø opprettes, py-moduler installeres fra requirements.txt, og alle disse artefaktene sendes sammen med konfigurasjoner, skript og medfølgende applikasjonsmiljø til servere. Deretter lanseres applikasjoner som en virtuell bruker uten administratorrettigheter.
Gitlab-CI ble valgt som CI/CD-systemet. Den resulterende rørledningen så omtrent slik ut:
Strukturelt sett så gitlab-ci.yml slik ut
---
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
Det er verdt å merke seg at montering og testing utføres på sitt eget bilde, der alle nødvendige systempakker allerede er installert og andre innstillinger er gjort.
Selv om hvert av disse skriptene i jobber er interessant på sin egen måte, men jeg vil selvfølgelig ikke snakke om dem. Beskrivelsen av hver av dem vil ta mye tid, og dette er ikke formålet med artikkelen. Jeg vil bare trekke oppmerksomheten din til det faktum at distribusjonsstadiet består av en sekvens med kalleskript:
- createconfig.py - oppretter en settings.ini-fil med komponentinnstillinger i ulike miljøer for påfølgende distribusjon (forproduksjon, produksjon, testing, ...)
- install_venv.sh - oppretter et virtuelt miljø for py-komponenter i en bestemt katalog og kopierer det til eksterne servere
- prepare_init.d.py — forbereder start-stopp-skript for komponenten basert på malen
- deploy.py - bryter ned og starter nye komponenter på nytt
Tiden gikk. Iscenesettelsen ble erstattet av preproduksjon og produksjon. Lagt til støtte for produktet på en distribusjon til (CentOS). Lagt til 5 kraftigere fysiske servere og et dusin virtuelle. Og det ble stadig vanskeligere for utviklere og testere å teste oppgavene sine i et miljø mer eller mindre nær arbeidstilstanden. På dette tidspunktet ble det klart at det var umulig å klare seg uten ham ...
Del II
Så klyngen vår er et spektakulært system med et par dusin separate komponenter som ikke er beskrevet av Dockerfiles. Du kan bare konfigurere den for distribusjon til et spesifikt miljø generelt. Vår oppgave er å distribuere klyngen i et oppsamlingsmiljø for å teste det før testing før utgivelse.
Teoretisk sett kan det være flere klynger som kjører samtidig: så mange som det er oppgaver i fullført tilstand eller nær fullføring. Kapasiteten til serverne vi har til rådighet gjør at vi kan kjøre flere klynger på hver server. Hver iscenesettelsesklynge må være isolert (det må ikke være noe kryss i porter, kataloger osv.).
Vår mest verdifulle ressurs er vår tid, og vi hadde ikke mye av den.
For en raskere start valgte vi Docker Swarm på grunn av dens enkelhet og arkitekturfleksibilitet. 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 Ready Active 19.03.2
jilwe56pl2zvabupryuosdj78 nop-test-2 Ready Active 19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 * nop-test-3 Ready Active Leader 19.03.2
Deretter oppretter du et nettverk:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Deretter koblet vi sammen Gitlab-CI og Swarm-noder når det gjelder fjernkontroll av noder fra CI: installasjon av sertifikater, innstilling av hemmelige variabler og oppsett av Docker-tjenesten på kontrollserveren. Denne
Deretter la vi til stackopprettings- og destruksjonsjobber til .gitlab-ci .yml.
Noen flere jobber er lagt til .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
Fra kodebiten ovenfor kan du se at to knapper (deploy_staging, stop_staging) er lagt til Pipelines, som krever manuell handling.
Stabelnavnet samsvarer med filialnavnet, og denne unikheten bør være tilstrekkelig. Tjenester i stabelen mottar unike ip-adresser, og porter, kataloger osv. vil være isolert, men det samme fra stabel til stabel (fordi konfigurasjonsfilen er lik for alle stabler) - det vi ønsket. Vi distribuerer stabelen (klyngen) ved hjelp av Docker-compose.yml, som beskriver klyngen vår.
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 kan du se at komponentene er koblet sammen av ett nettverk (nw_swarm) og er tilgjengelige for hverandre.
Systemkomponenter (basert på redis, mysql) er atskilt fra den generelle utvalget av tilpassede komponenter (i planer og tilpassede er de delt inn som tjenester). Implementeringsstadiet av klyngen vår ser ut som å overføre CMD til vårt ene store konfigurerte bilde, og generelt sett skiller det seg praktisk talt ikke fra distribusjonen beskrevet i del I. Jeg vil fremheve forskjellene:
- git klone... - få filene som trengs for å distribuere (createconfig.py, install_venv.sh, etc.)
- krølle... && pakke ut... - last ned og pakk ut byggeartefakter (kompilerte verktøy)
Det er bare ett ennå ubeskrevet problem: komponenter som har et nettgrensesnitt er ikke tilgjengelige fra utviklerens nettlesere. Vi løser dette problemet ved å bruke omvendt proxy, slik:
I .gitlab-ci.yml, etter å ha distribuert klyngestakken, legger vi til linjen for å distribuere balanseren (som, når den er forpliktet, bare oppdaterer konfigurasjonen (oppretter nye nginx-konfigurasjonsfiler i henhold til malen: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) – se docker-compose-nginx.yml-kode)
- 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å utviklingsdatamaskiner, oppdater /etc/hosts; foreskriv url til nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Så utrullingen av isolerte staging-klynger er implementert, og utviklere kan nå kjøre dem i et hvilket som helst antall nok til å sjekke oppgavene deres.
Fremtidsplaner:
- Skill komponentene våre som tjenester
- Har for hver Dockerfile
- Registrer automatisk mindre belastede noder i stabelen
- Spesifiser noder etter navnemønster (i stedet for å bruke id som i artikkelen)
- Legg til en sjekk for at stabelen er ødelagt
- ...
Spesiell takk for
Kilde: www.habr.com