La interreta videoenhava rekomendsistemo pri kiu ni laboras estas fermita komerca evoluo kaj estas teknike plurkomponenta aro de proprietaj kaj malfermfontaj komponentoj. La celo de skribi ĉi tiun artikolon estas priskribi la efektivigon de la docker-svarma clustering-sistemo por sursceniga retejo sen interrompi la establitan laborfluon de niaj procezoj en limigita tempo. La rakonto prezentita al via atento estas dividita en du partojn. La unua parto priskribas CI / KD antaŭ uzi docker-svarmon, kaj la dua priskribas la procezon de ĝia efektivigo. Tiuj, kiuj ne interesiĝas legi la unuan parton, povas sekure transiri al la dua.
Parto I
Reen en la malproksima, malproksima jaro, necesis starigi la CI/KD-procezon kiel eble plej rapide. Unu el la kondiĉoj estis ne uzi Docker por deplojo evoluigitaj komponantoj pro pluraj kialoj:
- por pli fidinda kaj stabila funkciado de komponantoj en Produktado (tio estas, fakte, la postulo ne uzi virtualigon)
- ĉefaj programistoj ne volis labori kun Docker (stranga, sed tiel estis)
- laŭ la ideologiaj konsideroj de la R&D-administrado
Infrastrukturo, stako kaj proksimumaj komencaj postuloj por MVP estis prezentitaj jene:
- 4 Intel® X5650-serviloj kun Debian (unu plia potenca maŝino estas plene evoluinta)
- Disvolviĝo de propraj kutimaj komponantoj estas farita en C++, Python3
- Ĉefaj triaj iloj uzataj: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, ...
- Duktoj por konstruado kaj testado de komponantoj aparte por sencimigo kaj liberigo
Unu el la unuaj demandoj, kiujn oni devas trakti en la komenca etapo, estas kiel kutimaj komponantoj estos deplojitaj en iu ajn medio (CI / KD).
Ni decidis sisteme instali triajn komponantojn kaj ĝisdatigi ilin sisteme. Propraj aplikoj evoluigitaj en C++ aŭ Python povas esti deplojitaj laŭ pluraj manieroj. Inter ili, ekzemple: krei sistemajn pakaĵojn, sendi ilin al la deponejo de konstruitaj bildoj kaj poste instali ilin sur serviloj. Pro nekonata kialo, alia metodo estis elektita, nome: uzante CI, aplikaĵaj ruleblaj dosieroj estas kompilitaj, virtuala projekta medio estas kreita, py-moduloj estas instalitaj de requirements.txt, kaj ĉiuj ĉi tiuj artefaktoj estas senditaj kune kun agordoj, skriptoj kaj la akompananta aplikaĵa medio al serviloj. Poste, aplikaĵoj estas lanĉitaj kiel virtuala uzanto sen administrantorajtoj.
Gitlab-CI estis elektita kiel la CI/CD-sistemo. La rezulta dukto aspektis kiel ĉi tio:
Strukture, gitlab-ci.yml aspektis tiel
---
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
Indas noti, ke la muntado kaj testado estas faritaj sur sia propra bildo, kie ĉiuj necesaj sistemaj pakoj jam estis instalitaj kaj aliaj agordoj estis faritaj.
Kvankam ĉiu el ĉi tiuj skriptoj en laboroj estas interesa siamaniere, sed kompreneble mi ne parolos pri ili.La priskribo de ĉiu el ili prenos multe da tempo kaj ĉi tio ne estas la celo de la artikolo. Mi nur atentigos vian atenton pri tio, ke la deploja stadio konsistas el sinsekvo de vokaj skriptoj:
- createconfig.py - kreas dosieron settings.ini kun komponaj agordoj en diversaj medioj por posta disfaldo (Preproduktado, Produktado, Testado, ...)
- instal_venv.sh - kreas virtualan medion por py-komponentoj en specifa dosierujo kaj kopias ĝin al foraj serviloj
- prepari_init.d.py — preparas start-haltajn skriptojn por la komponanto surbaze de la ŝablono
- deploy.py - malkomponiĝas kaj rekomencas novajn komponantojn
La tempo pasis. La ensceniga stadio estis anstataŭigita per antaŭproduktado kaj produktado. Aldonita subteno por la produkto sur unu plia distribuo (CentOS). Aldonitaj 5 pli potencaj fizikaj serviloj kaj dekduo da virtualaj. Kaj fariĝis pli kaj pli malfacile por programistoj kaj testantoj testi siajn taskojn en medio pli-malpli proksima al la laborstato. En ĉi tiu tempo, evidentiĝis, ke estis neeble fari sen li ...
Parto II
Do, nia areto estas sensacia sistemo de kelkaj dekduoj da apartaj komponantoj, kiuj ne estas priskribitaj de Dockerfiles. Vi povas nur agordi ĝin por deplojo al specifa medio ĝenerale. Nia tasko estas deploji la areton en aranĝan medion por testi ĝin antaŭ antaŭ-eldono.
Teorie, povas ekzisti pluraj aretoj kurantaj samtempe: tiom da kiom estas taskoj en la finita stato aŭ proksimaj al kompletigo. La kapabloj de la serviloj je nia dispono permesas al ni ruli plurajn aretojn sur ĉiu servilo. Ĉiu sursceniga areto devas esti izolita (ne devas esti intersekciĝo en havenoj, dosierujoj, ktp.).
Nia plej valora rimedo estas nia tempo, kaj ni ne havis multe da ĝi.
Por pli rapida komenco, ni elektis Docker Swarm pro ĝia simpleco kaj arkitekturo-fleksebleco. La unua afero, kiun ni faris, estis krei administranton kaj plurajn nodojn sur la foraj serviloj:
$ 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
Poste, kreu reton:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
Poste, ni konektis Gitlab-CI kaj Swarm-nodojn laŭ fora kontrolo de nodoj de CI: instali atestojn, agordi sekretajn variablojn kaj agordi la Docker-servon sur la kontrolservilo. Ĉi tiun
Poste, ni aldonis stakajn kreajn kaj detruajn laborojn al .gitlab-ci .yml.
Kelkaj pliaj laboroj estis aldonitaj al .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
El la ĉi-supra koda fragmento, vi povas vidi, ke du butonoj (deploy_staging, stop_staging) estis aldonitaj al Pipelines, postulante manan agadon.
La staknomo kongruas kun la branĉonomo kaj ĉi tiu unikeco devus esti sufiĉa. Servoj en la stako ricevas unikajn ip-adresojn, kaj havenojn, dosierujojn ktp. estos izolita, sed same de stako al stako (ĉar la agorda dosiero estas sama por ĉiuj stakoj) - kion ni volis. Ni deplojas la stakon (areto) uzante docker-compose.yml, kiu priskribas nian areton.
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
Ĉi tie vi povas vidi, ke la komponantoj estas konektitaj per unu reto (nw_swarm) kaj disponeblas unu al la alia.
Sistemkomponentoj (bazitaj sur redis, mysql) estas apartigitaj de la ĝenerala grupo de kutimaj komponantoj (en planoj kaj kutimaj estas dividitaj kiel servoj). La disfalda stadio de nia areto aspektas kiel pasi CMD en nian unu grandan agorditan bildon kaj, ĝenerale, praktike ne diferencas de la deplojo priskribita en Parto I. Mi reliefigos la diferencojn:
- git-klono... - akiru la dosierojn necesajn por deploji (createconfig.py, install_venv.sh, ktp.)
- buklo... && malfermu... - elŝutu kaj malzipu konstruajn artefaktojn (kompilitaj iloj)
Estas nur unu ankoraŭ nepriskribita problemo: komponantoj, kiuj havas retan interfacon, ne estas alireblaj de retumiloj de programistoj. Ni solvas ĉi tiun problemon uzante inversan prokurilon, tiel:
En .gitlab-ci.yml, post deplojado de la cluster-stako, ni aldonas la linion de deplojado de la balancilo (kiu, kiam ĝi estas kompromitata, nur ĝisdatigas sian agordon (kreas novajn agordajn dosierojn de nginx laŭ la ŝablono: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - vidu docker-compose-nginx.yml kodon)
- 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
Sur la evolukomputiloj, ĝisdatigu /etc/hosts; preskribi URL al nginx:
10.50.173.106 staging_BRANCH-1831_cluster.dev
Do, la disfaldiĝo de izolitaj sursceniga aretoj estis efektivigita kaj programistoj nun povas ruli ilin en ajna nombro sufiĉa por kontroli siajn taskojn.
Estontaj planoj:
- Apartigu niajn komponantojn kiel servojn
- Havu por ĉiu Dockerfile
- Aŭtomate detektu malpli ŝarĝitajn nodojn en la stako
- Specifu nodojn laŭ nomo ŝablono (prefere ol uzi id kiel en la artikolo)
- Aldonu kontrolon, ke la stako estas detruita
- ...
Specialan dankon pro
fonto: www.habr.com