سیستم توصیه محتوای ویدیویی آنلاین که ما روی آن کار می کنیم، یک توسعه تجاری بسته است و از نظر فنی یک خوشه چند جزئی از اجزای اختصاصی و منبع باز است. هدف از نوشتن این مقاله توصیف پیادهسازی سیستم خوشهبندی ازدحام docker برای یک سایت مرحلهبندی بدون ایجاد اختلال در گردش کار ایجاد شده فرآیندهای ما در مدت زمان محدود است. روایت ارائه شده به دو بخش تقسیم می شود. بخش اول CI / CD را قبل از استفاده از Docker Swarm توصیف می کند و قسمت دوم روند اجرای آن را شرح می دهد. کسانی که علاقه ای به خواندن قسمت اول ندارند می توانند با خیال راحت به قسمت دوم بروند.
قسمت اول
در سالهای دور و دور، لازم بود که فرآیند CI / CD در اسرع وقت راه اندازی شود. یکی از شرایط استفاده نکردن از داکر بود برای استقرار اجزای توسعه یافته به چند دلیل:
- برای عملکرد مطمئن تر و پایدارتر کامپوننت ها در تولید (یعنی در واقع نیاز به استفاده نکردن از مجازی سازی)
- توسعه دهندگان پیشرو نمی خواستند با Docker کار کنند (عجیب است، اما اینطور بود)
- با توجه به ملاحظات ایدئولوژیک مدیریت تحقیق و توسعه
زیرساخت، پشته و الزامات اولیه تقریبی برای MVP به شرح زیر ارائه شد:
- 4 سرور Intel® X5650 با Debian (یک دستگاه قدرتمند دیگر کاملاً توسعه یافته است)
- توسعه مولفه های سفارشی خود در C ++، Python3 انجام می شود
- ابزارهای اصلی شخص ثالث مورد استفاده: Kafka، Clickhouse، Airflow، Redis، Grafana، Postgresql، Mysql، …
- خطوط لوله برای ساخت و آزمایش اجزا به طور جداگانه برای رفع اشکال و انتشار
یکی از اولین سوالاتی که در مرحله اولیه باید به آن پرداخته شود این است که چگونه اجزای سفارشی در هر محیطی (CI / CD) مستقر خواهند شد.
ما تصمیم گرفتیم اجزای شخص ثالث را به صورت سیستمی نصب کنیم و آنها را به صورت سیستمی به روز کنیم. برنامه های سفارشی توسعه یافته در C++ یا Python می توانند به روش های مختلفی مستقر شوند. از جمله: ایجاد بسته های سیستمی، ارسال آنها به مخزن تصاویر ساخته شده و سپس نصب آنها بر روی سرورها. به دلیل نامعلومی، روش دیگری انتخاب شد، یعنی: با استفاده از CI، فایلهای اجرایی برنامه کامپایل میشوند، یک محیط پروژه مجازی ایجاد میشود، ماژولهای py از request.txt نصب میشوند و همه این آرتیفکتها به همراه تنظیمات، اسکریپتها و محیط برنامه همراه با سرورها در مرحله بعد، برنامه ها به عنوان یک کاربر مجازی بدون حقوق سرپرست راه اندازی می شوند.
Gitlab-CI به عنوان سیستم CI/CD انتخاب شد. خط لوله حاصل چیزی شبیه به این بود:
از نظر ساختاری، gitlab-ci.yml شبیه این بود
---
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
شایان ذکر است که مونتاژ و آزمایش بر روی تصویر خود انجام می شود ، جایی که تمام بسته های سیستمی لازم قبلاً نصب شده و سایر تنظیمات نیز انجام شده است.
اگرچه هر یک از این اسکریپت ها در مشاغل به نوع خود جالب هستند، اما البته من در مورد آنها صحبت نمی کنم. شرح هر یک از آنها زمان زیادی می برد و این هدف مقاله نیست. من فقط توجه شما را به این واقعیت جلب می کنم که مرحله استقرار شامل دنباله ای از اسکریپت های فراخوانی است:
- createconfig.py - یک فایل settings.ini با تنظیمات کامپوننت در محیط های مختلف برای استقرار بعدی (پیش تولید، تولید، تست، ...) ایجاد می کند.
- install_venv.sh - یک محیط مجازی برای اجزای py در یک دایرکتوری خاص ایجاد می کند و آن را در سرورهای راه دور کپی می کند
- përgatit_init.d.py - اسکریپت های start-stop را برای کامپوننت بر اساس الگو آماده می کند
- deploy.py - اجزای جدید را تجزیه و راه اندازی مجدد می کند
زمان گذشت. پیش تولید و تولید جایگزین مرحله صحنه سازی شد. پشتیبانی از محصول در یک توزیع دیگر (CentOS) اضافه شده است. اضافه شدن 5 سرور فیزیکی قوی تر و ده ها سرور مجازی. و برای توسعهدهندگان و آزمایشکنندگان سختتر میشد که وظایف خود را در محیطی کم و بیش نزدیک به حالت کار آزمایش کنند. در این زمان مشخص شد که بدون او غیرممکن است ...
قسمت دوم
بنابراین، خوشه ما یک سیستم دیدنی از چند دوجین مؤلفه جداگانه است که توسط Dockerfiles توضیح داده نشده است. شما فقط می توانید آن را برای استقرار در یک محیط خاص به طور کلی پیکربندی کنید. وظیفه ما این است که خوشه را در یک محیط مرحله بندی مستقر کنیم تا آن را قبل از آزمایش قبل از انتشار آزمایش کنیم.
از نظر تئوری، میتواند چندین خوشه به طور همزمان در حال اجرا باشد: به تعداد وظایفی که در حالت تکمیل شده یا نزدیک به اتمام وجود دارد. ظرفیت سرورهای در اختیار ما به ما این امکان را می دهد که چندین کلاستر را روی هر سرور اجرا کنیم. هر خوشه مرحله بندی باید ایزوله باشد (هیچ تقاطعی در پورت ها، دایرکتوری ها و غیره نباید وجود داشته باشد).
با ارزش ترین منبع ما زمان ما است و ما مقدار زیادی از آن را نداشتیم.
برای شروع سریعتر، Docker Swarm را به دلیل سادگی و انعطافپذیری معماری آن انتخاب کردیم. اولین کاری که انجام دادیم ایجاد یک مدیر و چندین گره در سرورهای راه دور بود:
$ 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
بعد، یک شبکه ایجاد کنید:
$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm
بعد، گرههای Gitlab-CI و Swarm را از نظر کنترل از راه دور گرهها از CI به هم وصل کردیم: نصب گواهیها، تنظیم متغیرهای مخفی و راهاندازی سرویس Docker در سرور کنترل. این یکی
در مرحله بعد، کارهای ایجاد پشته و تخریب را به .gitlab-ci .yml اضافه کردیم.
چند شغل دیگر به .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
از قطعه کد بالا، می بینید که دو دکمه (deploy_staging، stop_staging) به Pipelines اضافه شده است که نیاز به اقدام دستی دارد.
نام پشته با نام شاخه مطابقت دارد و این منحصر به فرد بودن باید کافی باشد. سرویسهای موجود در پشته آدرسهای IP منحصربهفرد و پورتها، دایرکتوریها و غیره را دریافت میکنند. جدا می شود، اما از پشته به پشته یکسان است (زیرا فایل پیکربندی برای همه پشته ها یکسان است) - آنچه ما می خواستیم. ما پشته (خوشه) را با استفاده از docker-compose.yml، که خوشه ما را توصیف می کند.
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
در اینجا می بینید که کامپوننت ها توسط یک شبکه (nw_swarm) به هم متصل شده اند و در دسترس یکدیگر هستند.
اجزای سیستم (بر اساس redis، mysql) از مجموعه کلی اجزای سفارشی جدا می شوند (در پلن ها و موارد سفارشی به عنوان خدمات تقسیم می شوند). مرحله استقرار خوشه ما به نظر می رسد که CMD را به یک تصویر پیکربندی شده بزرگ منتقل می کنیم و به طور کلی، عملاً با استقرار شرح داده شده در قسمت اول تفاوتی ندارد. من تفاوت ها را برجسته می کنم:
- git clone... - دریافت فایل های مورد نیاز برای استقرار (createconfig.py، install_venv.sh، و غیره)
- حلقه کردن... && باز کردن زیپ... - آرتیفکت های ساخت را دانلود و از حالت فشرده خارج کنید (ابزارهای کامپایل شده)
تنها یک مشکل هنوز توصیف نشده وجود دارد: اجزایی که دارای رابط وب هستند از مرورگرهای توسعه دهندگان قابل دسترسی نیستند. ما این مشکل را با استفاده از پروکسی معکوس حل می کنیم، به این ترتیب:
در gitlab-ci.yml، پس از استقرار پشته کلاستر، خط استقرار متعادل کننده را اضافه می کنیم (که در هنگام commit، فقط پیکربندی آن را به روز می کند (فایل های پیکربندی جدید nginx را مطابق الگو ایجاد می کند: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - کد 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
در رایانه های توسعه دهنده، /etc/hosts را به روز کنید. url را برای nginx تجویز کنید:
10.50.173.106 staging_BRANCH-1831_cluster.dev
بنابراین، استقرار خوشه های مرحله بندی ایزوله اجرا شده است و توسعه دهندگان اکنون می توانند آنها را به هر تعداد کافی برای بررسی وظایف خود اجرا کنند.
برنامه های آینده:
- اجزای ما را به عنوان خدمات جدا کنید
- برای هر Dockerfile داشته باشید
- به طور خودکار گره های کمتر بارگذاری شده در پشته را شناسایی کنید
- گره ها را با الگوی نام مشخص کنید (به جای استفاده از شناسه در مقاله)
- چکی اضافه کنید که پشته از بین رفته است
- ...
تشکر ویژه برای
منبع: www.habr.com