Het online videocontentaanbevelingssysteem waaraan we werken is een gesloten commerciële ontwikkeling en is technisch gezien een uit meerdere componenten bestaand cluster van eigen en open source componenten. Het doel van het schrijven van dit artikel is om de implementatie van het docker-zwermclustersysteem voor een stagingsite te beschrijven zonder de gevestigde workflow van onze processen in een beperkte tijd te verstoren. Het verhaal dat onder uw aandacht wordt gebracht, is verdeeld in twee delen. Het eerste deel beschrijft CI / CD voordat docker-zwerm wordt gebruikt, en het tweede beschrijft het implementatieproces ervan. Degenen die niet geïnteresseerd zijn in het lezen van het eerste deel, kunnen veilig doorgaan naar het tweede.
Deel I
Terug in het verre, verre jaar was het nodig om het CI/CD-proces zo snel mogelijk op te zetten. Eén van de voorwaarden was om Docker niet te gebruiken voor inzet ontwikkelde componenten om verschillende redenen:
- voor een betrouwbaardere en stabielere werking van componenten in productie (dat is in feite de vereiste om geen virtualisatie te gebruiken)
- toonaangevende ontwikkelaars wilden niet met Docker werken (raar, maar zo was het)
- volgens de ideologische overwegingen van het R&D-management
Infrastructuur, stack en geschatte initiële vereisten voor MVP werden als volgt gepresenteerd:
- 4 Intel® X5650-servers met Debian (een krachtigere machine is volledig ontwikkeld)
- Ontwikkeling van eigen maatwerkcomponenten wordt uitgevoerd in C++, Python3
- Belangrijkste gebruikte tools van derden: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, ...
- Pijplijnen voor het afzonderlijk bouwen en testen van componenten voor foutopsporing en release
Een van de eerste vragen die in de beginfase moet worden beantwoord, is hoe aangepaste componenten in elke omgeving (CI/CD) zullen worden ingezet.
We hebben besloten om componenten van derden systemisch te installeren en systematisch bij te werken. Maatwerkapplicaties ontwikkeld in C++ of Python kunnen op verschillende manieren worden ingezet. Onder hen bijvoorbeeld: systeempakketten maken, deze naar de repository met ingebouwde afbeeldingen sturen en ze vervolgens op servers installeren. Om onbekende reden is er voor een andere methode gekozen, namelijk: met behulp van CI worden uitvoerbare bestanden van applicaties gecompileerd, wordt een virtuele projectomgeving gecreëerd, worden py-modules geïnstalleerd vanuit eisen.txt, en al deze artefacten worden meegestuurd met configuraties, scripts en de begeleidende applicatieomgeving naar servers. Vervolgens worden applicaties gestart als virtuele gebruiker zonder beheerdersrechten.
Gitlab-CI werd gekozen als het CI/CD-systeem. De resulterende pijplijn zag er ongeveer zo uit:
Structureel zag gitlab-ci.yml er zo uit
---
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
Het is vermeldenswaard dat de assemblage en het testen worden uitgevoerd op een eigen image, waarbij alle benodigde systeempakketten al zijn geïnstalleerd en andere instellingen zijn gemaakt.
Hoewel elk van deze scripts in banen op zijn eigen manier interessant is, maar ik zal er natuurlijk niet over praten. De beschrijving van elk van hen zal veel tijd vergen en dit is niet het doel van het artikel. Ik vestig alleen uw aandacht op het feit dat de implementatiefase bestaat uit een reeks aanroepscripts:
- createconfig.py - creëert een settings.ini-bestand met componentinstellingen in verschillende omgevingen voor latere implementatie (preproductie, productie, testen, ...)
- install_venv.sh - creëert een virtuele omgeving voor py-componenten in een specifieke map en kopieert deze naar externe servers
- prepare_init.d.py — bereidt start-stop-scripts voor de component voor op basis van de sjabloon
- implementeren.py - ontleedt en start nieuwe componenten opnieuw
Verstreken tijd. De enscenering werd vervangen door preproductie en productie. Ondersteuning toegevoegd voor het product op nog een distributie (CentOS). Er zijn vijf krachtigere fysieke servers en een tiental virtuele servers toegevoegd. En het werd voor ontwikkelaars en testers steeds moeilijker om hun taken te testen in een omgeving die min of meer dicht bij de werkende staat lag. Op dit moment werd duidelijk dat het onmogelijk was om zonder hem te doen ...
Deel II
Ons cluster is dus een spectaculair systeem van een paar dozijn afzonderlijke componenten die niet door Dockerfiles worden beschreven. U kunt het alleen configureren voor implementatie in een specifieke omgeving in het algemeen. Onze taak is om het cluster in een faseringsomgeving te implementeren om het te testen voordat het pre-releasetesten uitvoert.
Theoretisch kunnen er meerdere clusters tegelijkertijd actief zijn: net zoveel als er taken zijn die voltooid zijn of bijna voltooid zijn. Dankzij de capaciteiten van de servers die wij tot onze beschikking hebben, kunnen wij op elke server meerdere clusters draaien. Elk faseringscluster moet geïsoleerd zijn (er mag geen kruispunt zijn in poorten, mappen, enz.).
Onze meest waardevolle hulpbron is onze tijd, en daar hadden we niet veel van.
Voor een snellere start hebben we voor Docker Swarm gekozen vanwege de eenvoud en architectuurflexibiliteit. Het eerste dat we deden was een manager en verschillende knooppunten op de externe servers maken:
$ 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
Maak vervolgens een netwerk:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Vervolgens hebben we Gitlab-CI- en Swarm-nodes met elkaar verbonden in termen van afstandsbediening van knooppunten vanuit CI: het installeren van certificaten, het instellen van geheime variabelen en het opzetten van de Docker-service op de controleserver. Deze
Vervolgens hebben we taken voor het maken en vernietigen van stapels toegevoegd aan .gitlab-ci .yml.
Er zijn nog een paar taken toegevoegd aan .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
Uit het bovenstaande codefragment kunt u zien dat er twee knoppen (deploy_staging, stop_staging) zijn toegevoegd aan Pipelines, waarvoor handmatige actie vereist is.
De stapelnaam komt overeen met de vertakkingsnaam en dit unieke karakter zou voldoende moeten zijn. Services in de stapel ontvangen unieke IP-adressen en poorten, mappen, enz. zal geïsoleerd zijn, maar hetzelfde van stapel tot stapel (omdat het configuratiebestand voor alle stapels hetzelfde is) - wat we wilden. We implementeren de stapel (cluster) met behulp van havenarbeider-compose.yml, die ons cluster beschrijft.
havenarbeider-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
Hier kun je zien dat de componenten via één netwerk (nw_swarm) met elkaar verbonden zijn en voor elkaar beschikbaar zijn.
Systeemcomponenten (gebaseerd op redis, mysql) zijn gescheiden van de algemene pool van aangepaste componenten (in plannen en aangepaste componenten zijn verdeeld als services). De implementatiefase van ons cluster lijkt op het doorgeven van CMD aan ons ene grote geconfigureerde image en verschilt in het algemeen praktisch niet van de implementatie die in Deel I wordt beschreven. Ik zal de verschillen benadrukken:
- git kloon... - haal de bestanden op die nodig zijn om te implementeren (createconfig.py, install_venv.sh, etc.)
- krullen... && uitpakken... - download en pak build-artefacten uit (gecompileerde hulpprogramma's)
Er is slechts één nog onbeschreven probleem: componenten met een webinterface zijn niet toegankelijk vanuit de browsers van ontwikkelaars. We lossen dit probleem op met behulp van reverse proxy, dus:
In .gitlab-ci.yml voegen we, na het implementeren van de clusterstack, de regel toe voor het implementeren van de balancer (die, wanneer hij een commit uitvoert, alleen de configuratie bijwerkt (nieuwe nginx-configuratiebestanden aanmaakt volgens de sjabloon: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - zie docker-compose-nginx.yml-code)
- 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
Op de ontwikkelcomputers update je /etc/hosts; schrijf url voor naar nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
De inzet van geïsoleerde staging-clusters is dus geïmplementeerd en ontwikkelaars kunnen ze nu in een groot aantal uitvoeren dat voldoende is om hun taken te controleren.
Plannen voor de toekomst:
- Scheid onze componenten als services
- Heb voor elk Dockerfile
- Detecteer automatisch minder geladen knooppunten in de stapel
- Specificeer knooppunten op naampatroon (in plaats van id te gebruiken zoals in het artikel)
- Voeg een controle toe dat de stapel is vernietigd
- ...
Speciale dank voor
Bron: www.habr.com