Internetinė vaizdo įrašų turinio rekomendacijų sistema, su kuria dirbame, yra uždara komercinė plėtra ir techniškai yra kelių komponentų patentuotų ir atvirojo kodo komponentų grupė. Šio straipsnio tikslas – apibūdinti dokerių spiečiaus klasterizacijos sistemos diegimą sustojimo vietoje, nesutrikdant nustatytos mūsų procesų darbo eigos per ribotą laiką. Jūsų dėmesiui pateiktas pasakojimas yra padalintas į dvi dalis. Pirmoje dalyje aprašomas CI / CD prieš naudojant docker spiečius, o antroje – jo įgyvendinimo procesas. Tie, kurie nesidomi skaityti pirmąja dalimi, gali drąsiai pereiti prie antrosios.
I dalis
Dar tolimais, tolimais metais reikėjo kuo greičiau nustatyti CI / CD procesą. Viena iš sąlygų buvo nenaudoti „Docker“. dislokavimui sukurti komponentai dėl kelių priežasčių:
- patikimesniam ir stabilesniam komponentų veikimui gamyboje (tai iš tikrųjų yra reikalavimas nenaudoti virtualizacijos)
- pagrindiniai kūrėjai nenorėjo dirbti su „Docker“ (keista, bet taip buvo)
- pagal ideologinius MTEP valdymo sumetimus
Infrastruktūra, dėklas ir apytiksliai pradiniai MVP reikalavimai buvo pateikti taip:
- 4 „Intel® X5650“ serveriai su „Debian“ (visiškai sukurtas dar vienas galingas įrenginys)
- Savo pasirinktinių komponentų kūrimas atliekamas C ++, Python3
- Pagrindiniai naudojami trečiųjų šalių įrankiai: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql,…
- Vamzdynai, skirti komponentams kurti ir testuoti atskirai derinant ir išleidžiant
Vienas iš pirmųjų klausimų, kurį reikia išspręsti pradiniame etape, yra tai, kaip pasirinktiniai komponentai bus įdiegti bet kurioje aplinkoje (CI / CD).
Nusprendėme sistemingai įdiegti trečiųjų šalių komponentus ir sistemingai juos atnaujinti. Pasirinktinės programos, sukurtos C++ arba Python kalbomis, gali būti įdiegtos keliais būdais. Tarp jų, pavyzdžiui: sistemos paketų kūrimas, jų siuntimas į sukurtų vaizdų saugyklą ir įdiegimas serveriuose. Dėl neaiškios priežasties buvo pasirinktas kitas būdas, būtent: naudojant CI, sukompiliuojami vykdomieji programos failai, sukuriama virtuali projekto aplinka, diegiami py moduliai iš reikalavimų.txt ir visi šie artefaktai siunčiami kartu su konfigūracijomis, scenarijais ir serveriams pridedama taikomųjų programų aplinka. Tada programos paleidžiamos kaip virtualus vartotojas be administratoriaus teisių.
Gitlab-CI buvo pasirinkta kaip CI / CD sistema. Gautas dujotiekis atrodė maždaug taip:
Struktūriškai gitlab-ci.yml atrodė taip
---
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
Verta paminėti, kad surinkimas ir bandymai atliekami pagal savo atvaizdą, kur jau yra įdiegti visi reikalingi sistemos paketai ir atlikti kiti nustatymai.
Nors kiekvienas iš šių scenarijų darbuose yra savaip įdomus, bet, žinoma, apie juos nekalbėsiu.Kiekvieno iš jų aprašymas užtruks daug laiko ir tai nėra straipsnio tikslas. Aš tik atkreipsiu jūsų dėmesį į tai, kad diegimo etapą sudaro scenarijų iškvietimo seka:
- createconfig.py - sukuria failą settings.ini su komponentų nustatymais įvairiose aplinkose, kad būtų galima vėliau diegti (išankstinė gamyba, gamyba, testavimas ir kt.)
- install_venv.sh - sukuria virtualią aplinką py komponentams konkrečiame kataloge ir nukopijuoja ją į nuotolinius serverius
- ready_init.d.py — pagal šabloną parengia komponento start-stop scenarijus
- deploy.py - suskaido ir iš naujo paleidžia naujus komponentus
Praėjęs laikas. Pastatymo etapą pakeitė parengimas ir gamyba. Pridėtas produkto palaikymas dar viename paskirstyme (CentOS). Pridėta 5 galingesni fiziniai serveriai ir keliolika virtualių. O kūrėjams ir bandytojams darėsi vis sunkiau išbandyti savo užduotis aplinkoje, daugmaž artimoje darbo būsenai. Tuo metu tapo aišku, kad be jo neįmanoma išsiversti ...
II dalis
Taigi, mūsų klasteris yra įspūdinga sistema, susidedanti iš kelių dešimčių atskirų komponentų, kurių Dockerfiles neaprašė. Jį galite konfigūruoti tik tam, kad jis būtų įdiegtas konkrečioje aplinkoje. Mūsų užduotis yra įdiegti klasterį į sustojimo aplinką, kad ją išbandytume prieš atliekant išankstinį išleidimą.
Teoriškai vienu metu gali veikti keli klasteriai: tiek, kiek yra užduočių baigtos būsenos arba beveik baigtos. Mūsų turimų serverių pajėgumai leidžia kiekviename serveryje paleisti keletą grupių. Kiekvienas sustojimo klasteris turi būti izoliuotas (prievaduose, kataloguose ir pan. neturi būti sankirtos).
Mūsų vertingiausias išteklius yra mūsų laikas, ir mes jo neturėjome daug.
Norėdami pradėti greičiau, pasirinkome Docker Swarm dėl jo paprastumo ir architektūros lankstumo. Pirmas dalykas, kurį padarėme, buvo sukurti tvarkyklę ir kelis mazgus nuotoliniuose serveriuose:
$ 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
Tada sukurkite tinklą:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Toliau sujungėme „Gitlab-CI“ ir „Swarm“ mazgus nuotoliniu būdu valdydami mazgus iš CI: įdiegėme sertifikatus, nustatėme slaptus kintamuosius ir „Docker“ paslaugą valdymo serveryje. Šitas
Tada į .gitlab-ci .yml pridėjome dėklo kūrimo ir naikinimo užduotis.
Dar keli darbai buvo įtraukti į .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
Iš anksčiau pateikto kodo fragmento matote, kad du mygtukai (deploy_staging, stop_staging) buvo pridėti prie konvejerių, todėl reikia atlikti rankinį veiksmą.
Krūvos pavadinimas sutampa su filialo pavadinimu ir šio unikalumo turėtų pakakti. Stekuose esančios paslaugos gauna unikalius IP adresus ir prievadus, katalogus ir kt. bus izoliuotas, bet tas pats nuo stack iki stack (nes konfigūracijos failas yra vienodas visiems stackams) - ko mes norėjome. Mes diegiame steką (klasterį) naudodami docker-compose.yml, kuris apibūdina mūsų klasterį.
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
Čia matote, kad komponentai yra sujungti vienu tinklu (nw_swarm) ir yra prieinami vienas kitam.
Sistemos komponentai (pagrįsti redis, mysql) yra atskirti nuo bendro pasirinktinių komponentų telkinio (planuose ir pasirinktiniai skirstomi kaip paslaugos). Mūsų klasterio diegimo etapas atrodo kaip CMD perdavimas į vieną didelį sukonfigūruotą vaizdą ir apskritai praktiškai nesiskiria nuo I dalyje aprašyto diegimo. Pabrėžsiu skirtumus:
- git klonas... - Gaukite diegti reikalingus failus (createconfig.py, install_venv.sh ir kt.)
- susisukti... && atsegti... - Atsisiųskite ir išpakuokite kūrimo artefaktus (sudarytas priemones)
Yra tik viena dar neaprašyta problema: komponentai, turintys žiniatinklio sąsają, nepasiekiami kūrėjų naršyklėse. Šią problemą išsprendžiame naudodami atvirkštinį tarpinį serverį, taigi:
Į .gitlab-ci.yml, įdiegę klasterio krūvą, pridedame balansavimo priemonės diegimo eilutę (kuris, kai įsipareigojamas, tik atnaujina konfigūraciją (sukuria naujus nginx konfigūracijos failus pagal šabloną: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) – žr. docker-compose-nginx.yml kodą)
- 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
Kūrimo kompiuteriuose atnaujinkite /etc/hosts; nurodyti nginx URL:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Taigi, buvo įdiegtas izoliuotų sustojimo grupių diegimas ir kūrėjai dabar gali juos paleisti bet kokiu kiekiu, kurio pakanka savo užduotims patikrinti.
Ateities planai:
- Atskirkite mūsų komponentus kaip paslaugas
- Turėkite kiekvienam Dockerfile
- Automatiškai aptikti mažiau įkeltus mazgus krūvoje
- Nurodykite mazgus pagal pavadinimo šabloną (o ne naudokite ID, kaip nurodyta straipsnyje)
- Pridėkite čekį, kad kaminas sunaikintas
- ...
Ypatingas ačiū už
Šaltinis: www.habr.com