Deploy aplikacionesh me ndihmën e Docker Swarm

Sistemi online i rekomandimeve pĂ«r pĂ«rmbajtjen video, me tĂ« cilin po punojmĂ«, Ă«shtĂ« njĂ« zhvillim komercial i mbyllur dhe nĂ« mĂ«nyrĂ« teknike paraqet njĂ« klaster shumĂ«komponentĂ«sh nga komponentĂ« tĂ« vetĂ«caktuar dhe open source. QĂ«llimi i shkrimit tĂ« kĂ«tij artikulli Ă«shtĂ« pĂ«rshkrimi i zbatimit tĂ« sistemit tĂ« klasterizimit docker swarm pĂ«r njĂ« ambient staging, pa e dĂ«mtuar workflow-n tonĂ« nĂ« kushte kohore tĂ« kufizuara. Narrativa qĂ« ju prezantohet Ă«shtĂ« e ndarĂ« nĂ« dy pjesĂ«. Pjesa e parĂ« pĂ«rshkruan CI/CD para pĂ«rdorimit tĂ« docker swarm, ndĂ«rsa e dyta – procesin e implementimit tĂ« tij. Ata qĂ« nuk janĂ« tĂ« interesuar tĂ« lexojnĂ« pjesĂ«n e parĂ«, mund tĂ« kalojnĂ« me besim nĂ« tĂ« dytĂ«n.

Pjesa I

Në një vit të largët, kërkohej të vendosej sa më shpejt procesi CI/CD. Një nga kushtet ishte që të mos përdorej Docker për deploy të komponenteve që po zhvilloheshin për disa arsye:

  • pĂ«r njĂ« funksionim mĂ« tĂ« besueshĂ«m dhe stabil tĂ« komponentĂ«ve nĂ« Production (pra nĂ« thelb njĂ« kĂ«rkesĂ« pĂ«r tĂ« mos pĂ«rdorur virtualizimin)
  • programuesit kryesorĂ« nuk donin tĂ« punonin me Docker (çuditĂ«risht, por ishte pikĂ«risht kĂ«shtu)
  • pĂ«r arsye ideologjike tĂ« drejtuesve tĂ« R&D

Infrastruktura, steku dhe kërkesat përfundimtare për MVP paraqiteshin si më poshtë:

  • 4 serverĂ« IntelÂź X5650 me Debian (njĂ« makinĂ« mĂ« e fuqishme plotĂ«sisht pĂ«r zhvillim)
  • Zhvillimi i komponentĂ«ve tĂ« personalizuar bĂ«het nĂ« C++, Python3
  • Mjetet kryesore tĂ« 3rdparty qĂ« pĂ«rdoren: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, ...
  • Pipeline pĂ«r ndĂ«rtimin dhe testimin e komponentĂ«ve veçmas pĂ«r debug dhe release

Një nga pyetjet e para që duhet të zgjidhet në fazën fillestare është se si do të bëhet zhvillimi i komponentëve të personalizuar në ndonjë ambient (CI/CD).

Komponentët e jashtëm u vendos të instalohen dhe azhurnohen sistematikisht. Aplikacionet e personalizuara, të zhvilluara në C++ ose Python, mund të implementohen në disa mënyra. Mes tyre, për shembull: krijimi i paketave sistemore, dërgimi i tyre në një repository të imazheve të ndërtuara dhe instalimi i tyre në servera. Për një arsye tashmë të panjohur, u zgjodh një mënyrë tjetër, konkretisht: me ndihmën e CI, kompilohen skedarët ekzekutivë të aplikacioneve, krijohet një ambient virtual të projektit, instalohen modulët py nga requirements.txt dhe të gjithë këto artefakte dërgohen së bashku me konfigurimet, skriptet dhe ambientin e shoqërueshëm të aplikacioneve në servera. Më pas, aplikacionet fillojnë nga një përdorues virtual pa të drejta administratori.

Si sistem CI/CD u zgjodh Gitlab-CI. Pipeline i arritur dukej përafërsisht kështu:

Deploy aplikacionesh me ndihmën e Docker Swarm
Struktura e gitlab-ci.yml dukej si më poshtë

---
variables:
  # versioni minimal i CPU në serverat ku implementohet klasteri
  CMAKE_CPUTYPE: "westmere"

  DEBIAN: "MYREGISTRY:5000/debian:latest"

before_script:
  - eval $(ssh-agent -s)
  - ssh-add  ~/.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

ËshtĂ« e rĂ«ndĂ«sishme tĂ« theksohet se ndĂ«rtimi dhe testimi bĂ«het nĂ« imazhin e tij, ku janĂ« instaluar tĂ« gjitha paketat sistemore tĂ« nevojshme dhe janĂ« kryer konfigurime tĂ« tjera.

Megjithëse secili nga këto skripte në punët është interesant në mënyrën e vet, nuk do të flas për to; përshkrimi i secilit do të zinte një kohë të konsiderueshme dhe kjo nuk është qëllimi i artikullit. Do të vë në dukje vetëm se faza e depoy-t përbëhet nga një renditje e thirrjeve të skripteve:

  1. createconfig.py — krijon skedarin settings.ini me konfigurimet e komponenteve nĂ« ambjente tĂ« ndryshme pĂ«r depoy-imin e mĂ«vonshĂ«m (Preproduction, Production, Testing, ...)
  2. install_venv.sh — krijon njĂ« ambjent virtual pĂ«r komponentet py nĂ« njĂ« drejtor tĂ« caktuar dhe e kopjon atĂ« nĂ« serverĂ«t e largĂ«t
  3. prepare_init.d.py — pĂ«rgatit skriptet pĂ«r nisje-ndalje tĂ« komponenteve bazuar nĂ« njĂ« model
  4. deploy.py — vendos dhe rilanson komponentet e reja

Kaloi koha. Faza e staging u zëvendësua nga preproduction dhe production. U shtua mbështetje për produktin në një distribucion tjetër (CentOS). U shtuan edhe 5 servera fizikë të fuqishëm dhe një dhjetë virtualë. Dhe zhvilluesve dhe testuesve po u bëhej gjithnjë e më e vështirë të testonin detyrat e tyre në një ambient që ishte më afër gjendjes operuese. Në atë kohë u bë e qartë se nuk mund të shpëtohej pa të...

Pjesa II

Deploy aplikacionesh me ndihmën e Docker Swarm

Pra, klusteri ynë është një sistem mjaft interesante me disa dhjetra komponente të veçanta, të papërshkruara nga Dockerfile. Të konfigurosh atë për depoy në një ambjent të caktuar mund të bëhet vetëm në përmbledhje. Detyra jonë është të depoy-ojmë klusterin në ambientin staging për të testuar atë para testimit para-lëshimit.

Teoretikisht, ka mundĂ«si tĂ« kenĂ« disa klustera qĂ« punojnĂ« njĂ«kohĂ«sisht: aq sa janĂ« detyrat nĂ« gjendje tĂ« pĂ«rfunduar ose tĂ« afĂ«rta me pĂ«rfundimin. Kapacitetet qĂ« kemi nĂ« dispozicionin e serverĂ«ve tanĂ« lejojnĂ« tĂ« startohet disa klustera nĂ« çdo server. Çdo kluster staging duhet tĂ« jetĂ« i izoluar (nuk duhet tĂ« ketĂ« ndĂ«rtime nĂ« porte, drejtorira etj.).

Burimi më i çmuar është koha jonë, dhe ajo ishte e kufizuar.

Për të filluar më shpejt, zgjodhëm Docker Swarm për shkak të thjeshtësisë dhe fleksibilitetit të arkitekturës. E para që bëmë ishte krijimi i një menaxheri dhe disa nodesh në serverat e largët:

$ 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

MĂ« pas, krijuam rrjetin:


$ docker network create --driver overlay --subnet 10.10.10.0/24 nw_swarm

Më pas, lidhëm Gitlab-CI dhe nodet Swarm për menaxhimin e largët të nodave nga CI: instalimi i certifikatave, konfigurimi i variablave të fshehta dhe konfigurimi i shërbimit Docker në serverin menaxhues. Ky artikulli na kurseu shumë kohë.

Më pas, shtuam job-at për krijimin dhe shkatërrimin e stekëve në .gitlab-ci.yml.

Në .gitlab-ci.yml janë shtuar edhe disa job

## 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

Nga fragmenti më sipër i kodit, shihet se në Pipelines janë shtuar dy butona (deploy_staging, stop_staging), që kërkojnë ndërhyrje manuale.

Deploy aplikacionesh me ndihmën e Docker Swarm
Emri i stek-ut pĂ«rkon me emrin e degĂ«s dhe kjo unikĂ«si duhet tĂ« jetĂ« e mjaftueshme. ShĂ«rbimet nĂ« stek marrin adresa unike IP, dhe portet, direktoret etj., do tĂ« jenĂ« tĂ« izoluara, por tĂ« njĂ«jta nga steku nĂ« stek (pasi skedari i konfigurimit Ă«shtĂ« identik pĂ«r tĂ« gjitha stekĂ«t) — kjo Ă«shtĂ« pikĂ«risht ajo qĂ« ne kĂ«rkonim. Ne e zhvillojmĂ« stekun (klusterin) me ndihmĂ«n e docker-compose.yml, nĂ« tĂ« cilin pĂ«rshkruhet klusteri ynĂ«.

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

Këtu shihet se komponentët janë të bashkuar në një rrjet të vetëm (nw_swarm) dhe janë të arritshëm nga njëri-tjetri.

Komponentët sistemorë (të bazuar në redis, mysql) janë të ndarë nga grupi i përgjithshëm i komponentëve personalë (në planet, po ashtu janë planifikuar të ndahen si shërbime). Faza e dërgimit të klasterit tonë duket si transferimi i CMD në imazhin tonë të madh të konfiguruar dhe në përgjithësi nuk ndryshon pothuajse nga dërgimi i përshkruar në Pjesën I. theksoj dallimet:

  • git clone 
 — merrni skedarĂ«t e nevojshĂ«m pĂ«r tĂ« kryer dĂ«rgimin (createconfig.py, install_venv.sh etj.)
  • curl
 && unzip 
 — shkarkoni dhe shkarkoni artifaktet e ndĂ«rtimit (njĂ«sitĂ« e kompilura)

Tani gjendjet vetëm një problem i pa përshkruar deri tani: komponentët me ndërfaqe web nuk janë të arritshëm nga shfletuesit e zhvilluesve. Ne po e zgjidhim këtë problem me anë të reverse proxy, në këtë mënyrë:

NĂ« .gitlab-ci.yml pas deploy-it tĂ« stack-ut tĂ« klasterit, shtojmĂ« linjĂ«n e deploy-it tĂ« balancuesit (i cili nĂ« commit-e, thjesht pĂ«rditĂ«son konfiguarcioni i tij (krijon skedarĂ« konfiguacioni tĂ« rinj nginx sipas modelit: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) — shih kodin 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

Në kompjuterët e zhvilluesve përditësojmë /etc/hosts; shkruajmë url-në deri te nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Pra, deploy-i i klasterëve staging të izoluara është realizuar dhe zhvilluesit tani mund t'i operojnë ata në çdo numër të mjaftueshëm për të verifikuar detyrat e tyre.

Planet e mëtejshme:

  • TĂ« ndahen komponentĂ«t tanĂ« si shĂ«rbime
  • TĂ« krijohet njĂ« Dockerfile pĂ«r secilin
  • TĂ« zbulohen automatikisht nodet mĂ« pak tĂ« ngarkuara nĂ« stack
  • TĂ« caktohen nodet sipas modelit tĂ« emrit (dhe jo tĂ« pĂ«rdoren id si nĂ« artikull)
  • TĂ« shtohet kontrolli qĂ« stack-u tĂ« ndodhet shkatĂ«rruar
  • 


Një falënderim i veçantë për artikull.

Burimi: habr.com

Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster