Sistem za preporuku video sadržaja na mreži na kojem radimo je zatvorena komercijalna izrada i tehnički je višekomponentni klaster vlasničkih i komponenti otvorenog koda. Svrha pisanja ovog članka je da opiše implementaciju sistema docker swarm klasteriranja za mjesto za postavljanje bez narušavanja uspostavljenog toka rada naših procesa u ograničenom vremenu. Narativ koji vam je predstavljen podijeljen je na dva dijela. Prvi dio opisuje CI/CD prije korištenja docker roja, a drugi opisuje proces njegove implementacije. Oni koji nisu zainteresovani za čitanje prvog dela mogu sa sigurnošću da pređu na drugi.
I deo
Još daleke, daleke godine, bilo je potrebno što brže postaviti CI/CD proces. Jedan od uslova je bio da se ne koristi Docker za raspoređivanje razvijene komponente iz nekoliko razloga:
- za pouzdaniji i stabilniji rad komponenti u proizvodnji (odnosno, u stvari, uslov da se ne koristi virtuelizacija)
- vodeći programeri nisu hteli da rade sa Dockerom (čudno, ali tako je bilo)
- prema ideološkim promišljanjima menadžmenta istraživanja i razvoja
Infrastruktura, stek i približni početni zahtjevi za MVP predstavljeni su na sljedeći način:
- 4 Intel® X5650 servera s Debianom (jedan snažniji stroj je u potpunosti razvijen)
- Razvoj sopstvenih prilagođenih komponenti se vrši u C++, Python3
- Glavni korišteni alati treće strane: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql,…
- Cjevovodi za izgradnju i testiranje komponenti odvojeno za otklanjanje grešaka i oslobađanje
Jedno od prvih pitanja koje treba riješiti u početnoj fazi je kako će prilagođene komponente biti raspoređene u bilo kojem okruženju (CI/CD).
Odlučili smo da sistemski instaliramo komponente treće strane i da ih sistemski ažuriramo. Prilagođene aplikacije razvijene u C++ ili Python-u mogu se implementirati na nekoliko načina. Među njima, na primjer: kreiranje sistemskih paketa, njihovo slanje u spremište izgrađenih slika i zatim njihovo instaliranje na servere. Iz nepoznatog razloga odabran je drugi metod, a to je: pomoću CI-a se kompajliraju izvršni fajlovi aplikacije, kreira se virtuelno projektno okruženje, instaliraju se py moduli iz requirements.txt, a svi ovi artefakti se šalju zajedno sa konfiguracijama, skriptama i prateće okruženje aplikacije na servere. Zatim se aplikacije pokreću kao virtuelni korisnik bez administratorskih prava.
Gitlab-CI je izabran kao CI/CD sistem. Rezultirajući cevovod je izgledao otprilike ovako:
Strukturno, gitlab-ci.yml je izgledao ovako
---
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
Vrijedi napomenuti da se sklapanje i testiranje vrši na vlastitoj slici, gdje su svi potrebni sistemski paketi već instalirani i druga podešavanja su napravljena.
Iako je svaka od ovih skripti u poslovima zanimljiva na svoj način, naravno da o njima neću govoriti, opis svakog od njih će oduzeti dosta vremena i to nije svrha članka. Samo ću vam skrenuti pažnju na činjenicu da se faza implementacije sastoji od niza skripti za pozivanje:
- createconfig.py - kreira datoteku settings.ini sa postavkama komponenti u različitim okruženjima za naknadnu implementaciju (preprodukcija, proizvodnja, testiranje,...)
- install_venv.sh - kreira virtuelno okruženje za py komponente u određenom direktorijumu i kopira ga na udaljene servere
- pripremi_init.d.py — priprema start-stop skripte za komponentu na osnovu šablona
- deploy.py - razgrađuje i ponovo pokreće nove komponente
Vrijeme je prolazilo. Scenska faza je zamijenjena predprodukcijom i produkcijom. Dodata podrška za proizvod na još jednoj distribuciji (CentOS). Dodano 5 moćnijih fizičkih servera i desetak virtualnih. A programerima i testerima je postajalo sve teže da testiraju svoje zadatke u okruženju manje ili više bliskom radnom stanju. Tada je postalo jasno da je nemoguće bez njega ...
II deo
Dakle, naš klaster je spektakularan sistem od nekoliko desetina odvojenih komponenti koje nisu opisane u Dockerfiles-u. Možete ga konfigurirati samo za primenu u određenom okruženju općenito. Naš zadatak je da postavimo klaster u scensko okruženje kako bismo ga testirali prije testiranja prije izdanja.
Teoretski, može postojati nekoliko klastera koji rade istovremeno: onoliko koliko ima zadataka u završenom stanju ili blizu završetka. Kapaciteti servera kojima raspolažemo omogućavaju nam da pokrenemo nekoliko klastera na svakom serveru. Svaki staging klaster mora biti izoliran (ne smije biti ukrštanja portova, direktorija, itd.).
Naš najvredniji resurs je naše vrijeme, a nismo ga imali puno.
Za brži početak, odabrali smo Docker Swarm zbog njegove jednostavnosti i fleksibilnosti arhitekture. Prva stvar koju smo uradili je kreiranje menadžera i nekoliko čvorova na udaljenim serverima:
$ 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
Zatim kreirajte mrežu:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Zatim smo povezali Gitlab-CI i Swarm čvorove u smislu daljinske kontrole čvorova iz CI: instaliranje certifikata, postavljanje tajnih varijabli i postavljanje Docker servisa na kontrolnom serveru. Ovaj
Zatim smo dodali poslove kreiranja i uništavanja stekova u .gitlab-ci .yml.
Još nekoliko poslova je dodano u .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
Iz gornjeg isječka koda, možete vidjeti da su dva dugmeta (deploy_staging, stop_staging) dodana u cjevovode, što zahtijeva ručnu akciju.
Ime steka odgovara imenu grane i ova jedinstvenost bi trebala biti dovoljna. Usluge u steku dobijaju jedinstvene IP adrese, portove, direktorijume itd. biće izolovano, ali isto od steka do steka (jer je konfiguracioni fajl isti za sve stekove) - ono što smo želeli. Mi postavljamo stek (klaster) koristeći docker-compose.yml, koji opisuje naš 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
Ovdje možete vidjeti da su komponente povezane jednom mrežom (nw_swarm) i dostupne su jedna drugoj.
Sistemske komponente (bazirane na redis, mysql) su odvojene od opšteg skupa prilagođenih komponenti (u planovima i prilagođene su podeljene kao usluge). Faza implementacije našeg klastera izgleda kao prenošenje CMD-a u našu jednu veliku konfiguriranu sliku i, općenito gledano, praktički se ne razlikuje od implementacije opisane u dijelu I. Istaknut ću razlike:
- git klon... - nabavite datoteke potrebne za implementaciju (createconfig.py, install_venv.sh, itd.)
- uviti... && otkopčati... - preuzmite i raspakujte artefakte izgradnje (kompajlirane uslužne programe)
Postoji samo jedan još neopisan problem: komponente koje imaju web interfejs nisu dostupne iz pretraživača programera. Ovaj problem rješavamo korištenjem obrnutog proxyja, na sljedeći način:
U .gitlab-ci.yml, nakon postavljanja steka klastera, dodajemo liniju za postavljanje balansera (koji, kada se urezuje, samo ažurira svoju konfiguraciju (kreira nove nginx konfiguracijske datoteke prema šablonu: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - pogledajte kod 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
Na razvojnim računarima, ažurirajte /etc/hosts; prepisati url za nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Dakle, implementacija izolovanih scenskih klastera je implementirana i programeri ih sada mogu pokrenuti u bilo kojem broju dovoljnom da provjere svoje zadatke.
Budući planovi:
- Odvojite naše komponente kao usluge
- Imati za svaki Dockerfile
- Automatski otkrijte manje opterećene čvorove u stogu
- Odredite čvorove prema uzorku imena (umjesto da koristite id kao u članku)
- Dodajte provjeru da je stek uništen
- ...
Posebno hvala za
izvor: www.habr.com