Sustav za preporuku online video sadržaja na kojem radimo je zatvoreni komercijalni razvoj i tehnički je višekomponentni klaster vlasničkih komponenti i komponenti otvorenog koda. Svrha pisanja ovog članka je opisati implementaciju sustava grupiranja docker swarm za početno mjesto bez ometanja uspostavljenog tijeka rada naših procesa u ograničenom vremenu. Pripovijest koja je predstavljena vašoj pozornosti podijeljena je u dva dijela. Prvi dio opisuje CI/CD prije korištenja docker swarma, a drugi opisuje proces njegove implementacije. Oni koji nisu zainteresirani za čitanje prvog dijela mogu sigurno prijeći na drugi.
I dio
Daleke, daleke godine bilo je potrebno što brže postaviti CI/CD proces. Jedan od uvjeta bio je ne koristiti Docker za raspoređivanje razvijene komponente iz nekoliko razloga:
- za pouzdaniji i stabilniji rad komponenti u Produkciji (to je, zapravo, zahtjev da se ne koristi virtualizacija)
- vodeći programeri nisu htjeli raditi s Dockerom (čudno, ali tako je bilo)
- prema ideološkim promišljanjima menadžmenta istraživanja i razvoja
Infrastruktura, stog i približni početni zahtjevi za MVP predstavljeni su kako slijedi:
- 4 Intel® X5650 poslužitelja s Debianom (jedan moćniji stroj je potpuno razvijen)
- Razvoj vlastitih prilagođenih komponenti provodi se u C++, Python3
- Glavni korišteni alati treće strane: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
- Cjevovodi za izgradnju i testiranje komponenti zasebno za otklanjanje pogrešaka i izdavanje
Jedno od prvih pitanja koje treba riješiti u početnoj fazi je kako će se prilagođene komponente postaviti u bilo koje okruženje (CI / CD).
Odlučili smo sistemski instalirati komponente trećih strana i sistemski ih ažurirati. Prilagođene aplikacije razvijene u C++ ili Pythonu mogu se implementirati na nekoliko načina. Među njima, na primjer: kreiranje sistemskih paketa, njihovo slanje u repozitorij izgrađenih slika i zatim njihovo instaliranje na poslužitelje. Iz nepoznatog razloga odabrana je druga metoda, naime: pomoću CI-ja kompajliraju se izvršne datoteke aplikacije, stvara se virtualno projektno okruženje, py moduli se instaliraju iz requirements.txt, a svi ti artefakti šalju se zajedno s konfiguracijama, skriptama i prateće aplikacijsko okruženje poslužiteljima. Zatim se aplikacije pokreću kao virtualni korisnik bez administratorskih prava.
Kao CI/CD sustav odabran je Gitlab-CI. Rezultirajući cjevovod izgledao je 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
Vrijedno je napomenuti da se montaža i testiranje provodi na vlastitoj slici, gdje su već instalirani svi potrebni sistemski paketi i napravljene druge postavke.
Iako je svaka od ovih skripti u poslovima zanimljiva na svoj način, ali naravno neću govoriti o njima, opis svake od njih će oduzeti dosta vremena i to nije svrha članka. Samo ću vam skrenuti pozornost na činjenicu da se faza implementacije sastoji od slijeda poziva skripti:
- createconfig.py - stvara datoteku settings.ini s postavkama komponenti u različitim okruženjima za naknadnu implementaciju (Pretprodukcija, Proizvodnja, Testiranje, ...)
- install_venv.sh - stvara virtualno okruženje za py komponente u određenom direktoriju i kopira ga na udaljene poslužitelje
- pripremiti_init.d.py — priprema start-stop skripte za komponentu na temelju predloška
- implementirati.py - razgrađuje i ponovno pokreće nove komponente
Vrijeme je prolazilo. Uprizorenje je zamijenjeno pretprodukcijom i produkcijom. Dodana podrška za proizvod na još jednoj distribuciji (CentOS). Dodano 5 snažnijih fizičkih poslužitelja i desetak virtualnih. A razvojnim programerima i testerima postajalo je sve teže testirati svoje zadatke u okruženju manje-više bliskom radnom stanju. U to vrijeme postalo je jasno da je nemoguće bez njega ...
II dio
Dakle, naš klaster je spektakularan sustav od nekoliko desetaka zasebnih komponenti koje Dockerfiles ne opisuje. Možete ga općenito konfigurirati samo za implementaciju u određeno okruženje. Naš je zadatak implementirati klaster u probno okruženje kako bismo ga testirali prije testiranja prije izdavanja.
Teoretski, može postojati nekoliko klastera koji rade istovremeno: onoliko koliko ima zadataka u dovršenom stanju ili blizu dovršetka. Kapaciteti poslužitelja kojima raspolažemo omogućuju nam pokretanje nekoliko klastera na svakom poslužitelju. Svaki pripremni klaster mora biti izoliran (ne smije postojati križanje u portovima, direktorijima itd.).
Naš najvrjedniji 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. Prvo što smo napravili bilo je stvaranje upravitelja i nekoliko čvorova na udaljenim poslužiteljima:
$ 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 izradite 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-ja: instaliranje certifikata, postavljanje tajnih varijabli i postavljanje Docker servisa na kontrolnom poslužitelju. Ovaj
Zatim smo dodali poslove stvaranja i uništavanja hrpa u .gitlab-ci .yml.
Dodano je još nekoliko poslova 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 gumba (deploy_staging, stop_staging) dodana u Pipelines, što zahtijeva ručnu radnju.
Naziv steka odgovara nazivu grane i ta bi jedinstvenost trebala biti dovoljna. Usluge u stogu primaju jedinstvene IP adrese, portove, direktorije itd. će biti izoliran, ali isti od stog do stog (jer je konfiguracijska datoteka ista za sve hrpe) - ono što smo htjeli. Raspoređujemo stog (klaster) pomoću doker-compose.yml, koji opisuje naš klaster.
doker-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 da su jedna drugoj dostupne.
Komponente sustava (temeljene na redis, mysql) odvojene su od općeg skupa prilagođenih komponenti (u planovima i prilagođene su podijeljene kao usluge). Faza implementacije našeg klastera izgleda kao prosljeđivanje CMD-a u našu jednu veliku konfiguriranu sliku i općenito se praktički ne razlikuje od implementacije opisane u I. dijelu. Naglasit ću razlike:
- git klon... - dobiti datoteke potrebne za implementaciju (createconfig.py, install_venv.sh, itd.)
- uviti... && raspakirati... - preuzimanje i raspakiranje artefakata izrade (kompilirani uslužni programi)
Postoji samo jedan još neopisani problem: komponente koje imaju web sučelje nisu dostupne iz preglednika programera. Ovaj problem rješavamo pomoću obrnutog proxyja, dakle:
U .gitlab-ci.yml, nakon postavljanja hrpe klastera, dodajemo redak postavljanja balansera (koji, kada se predaje, samo ažurira svoju konfiguraciju (stvara nove nginx konfiguracijske datoteke prema predlošku: /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čunalima ažurirajte /etc/hosts; propiši url za nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Dakle, implementacija izoliranih probnih klastera je implementirana i programeri ih sada mogu pokrenuti u bilo kojem broju dovoljnom za provjeru njihovih zadataka.
Planovi za buducnost:
- Odvojite naše komponente kao usluge
- Imati za svaku Dockerfile
- Automatski otkrij manje opterećene čvorove u stogu
- Navedite čvorove uzorkom naziva (umjesto korištenja ID-a kao u članku)
- Dodajte provjeru da je stog uništen
- ...
Posebna zahvala za
Izvor: www.habr.com