Deploy aplikacionet me Docker Swarm

Sistemi rekomandues online pĂ«r pĂ«rmbajtjen e videos, mbi tĂ« cilin po punojmĂ«, Ă«shtĂ« njĂ« zhvillim komercial i mbyllur dhe pĂ«rbĂ«n teknikisht njĂ« grumbull multikomponentesh nga komponentĂ«t tonĂ« dhe ato open source. QĂ«llimi i kĂ«tij artikulli Ă«shtĂ« tĂ« pĂ«rshkruajĂ« zbatimin e sistemit tĂ« grumbullimit docker swarm pĂ«r njĂ« platformĂ« staging, pa e prishur rrjedhĂ«n e procesit tonĂ« nĂ« kushte kohe tĂ« kufizuar. Narrativa e paraqitur pĂ«r ju Ă«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 lehtĂ«si nĂ« tĂ« dytĂ«n.

Pjesa I

Në vitin shumë të largët, nevojitej të konfigurohej sa më shpejt procesi CI/CD. Një nga kushtet ishte që të mos përdorej Docker për deployment komponentëve të zhvilluar për disa arsye:

  • pĂ«r njĂ« funksionim mĂ« tĂ« besueshĂ«m dhe tĂ« qĂ«ndrueshĂ«m tĂ« komponentĂ«ve nĂ« Production (dmth, nĂ« thelb kĂ«rkesa pĂ«r tĂ« mos pĂ«rdorur virtualizimin)
  • udhĂ«heqĂ«sit e zhvilluesve nuk donin tĂ« punonin me Docker (çuditĂ«risht, por ishte ashtu)
  • pĂ«r arsye ideologjike tĂ« drejtuesve tĂ« R&D

Infrastruktura, staku dhe kërkesat e para për MVP u paraqitën si të tillë:

  • 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 tĂ« pĂ«rdorura: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, 

  • Pipeline tĂ« ndara pĂ«r ndĂ«rtimin dhe testimin e komponentĂ«ve 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 vendosja e komponentëve të personalizuar në ndonjë ambient (CI/CD).

Komponentët e jashtëm u vendosën të instalohen dhe përditësohen në mënyrë sistematike. Aplikacionet e personalizuara, të zhvilluara në C++ ose Python, mund të implementohen në disa mënyra. Ndër to, për shembull: krijimi i paketave sistematike, dërgimi i tyre në një depo të imazheve të ndërtuara dhe instalimi i tyre të mëvonshëm në servera. Për një arsye të panjohur, u zgjodh një mënyrë tjetër, konkretisht: me ndihmën e CI, compile-ohen skedarët ekzekutivë të aplikacioneve, krijohet një ambient virtual për projektin, instalohet modulet py nga requirements.txt dhe të gjithë këto artefakte dërgohen së bashku me konfigurimet, skriptet dhe ambientin përkatës të aplikacioneve në servera. Më pas, aplikacionet fillojnë të punojnë nga një përdorues virtual pa të drejta administratori.

Si sistem CI/CD u zgjodh Gitlab-CI. Pipeline i krijuar dukej kështu:

Deploy aplikacionet me Docker Swarm
Strukturore, gitlab-ci.yml kishte pamjen e mëposhtme

---
variables:
  # versioni minimale të CPU-së në serverët ku zhvillohet klasteri
  CMAKE_CPUTYPE: "westmere"

  DEBIAN: "MYREGISTRY:5000/debian:latest"

before_script:
  - eval $(ssh-agent -s)
  - ssh-add  ~/.ssh/config

stages:
  - ndërtim
  - testim
  - shpërndarje

debug.debian:
  stage: ndërtim
  image: $DEBIAN
  script:
    - cd builds/release && ./build.sh
    paths:
      - bin/
      - builds/release/bin/
    when: always
release.debian:
  stage: ndërtim
  image: $DEBIAN
  script:
    - cd builds/release && ./build.sh
    paths:
      - bin/
      - builds/release/bin/
    when: always

## faza e testimit
tests.codestyle:
  stage: testim
  image: $DEBIAN
  dependencies:
    - release.debian
  script:
    - /bin/bash run_tests.sh -t codestyle -b "${CI_COMMIT_REF_NAME}_codestyle"
tests.debug.debian:
  stage: testim
  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: testim
  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

## faza e angazhimit
deploy_staging:
  stage: shpërndarje
  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

Vlen të theksohet se ndërtimi dhe testimi realizohet në imazhin e tij, ku janë instaluar tashmë të gjitha paketat sistemore të nevojshme dhe janë përfunduar konfigurimet e tjera.

Megjithëse çdo njëri nga këto skripte në punët është interesant në mënyrën e vet, unë nuk do të flas për ta, sepse përshkrimi i secilit prej tyre do të zinte një kohë të konsiderueshme dhe ajo nuk është qëllimi i këtij artikulli. Do të theksoj vetëm se faza e deploimit përbëhet nga një renditje e thirrjeve të skripteve:

  1. createconfig.py — krijon skedarin settings.ini me konfigurimet e komponenteve nĂ« mjedise tĂ« ndryshme pĂ«r deploimin e mĂ«vonshĂ«m (Preproduction, Production, Testing, 
)
  2. install_venv.sh — krijon njĂ« mjedis virtual pĂ«r komponentĂ«t py nĂ« njĂ« direktorium tĂ« caktuar dhe e kopjon atĂ« nĂ« serverat e largĂ«t
  3. prepare_init.d.py — pĂ«rgatit skriptet e fillimit ndalimit tĂ« komponenteve mbi bazĂ«n e njĂ« shablloni
  4. deploy.py — shpĂ«rndan dhe ristarton komponentĂ«t e rinj

Kalimi kalon. Faza staging u zëvendësua me preproduction dhe production. U shtua mbështetje për produktin në një shpërndarje tjetër (CentOS). U shtuan edhe 5 serverë fizikë të fuqishëm dhe një duzinë virtualë. Dhe për zhvilluesit dhe testuesit bëhej gjithnjë e më e vështirë të praktikoheshin detyrat e tyre në një mjedis më afër gjendjes së punës. Në këtë kohë u bë e qartë se ishte e pamundur të largohem pa të...

Pjesa II

Deploy aplikacionet me Docker Swarm

Pra, klasteri ynë përbën një sistem spektakolar prej disa dhjetëra komponenesh, të pa përshkruara në Dockerfile. Konfigurimi i tij për deploy në një mjedis të caktuar mund të bëhet vetëm në përgjithësi. Detyra jonë është të deploy-nim klasterin në mjedisin staging për testim para lëshimit.

Teorikisht, mund tĂ« ketĂ« disa klasterĂ« qĂ« punojnĂ« njĂ«herĂ«sh: aq sa ka detyra nĂ« gjendje tĂ« pĂ«rfunduar ose tĂ« afĂ«rt me pĂ«rfundimin. Kapacitetet e serverĂ«ve qĂ« kemi nĂ« dispozicion lejojnĂ« tĂ« nisim disa klasterĂ« nĂ« çdo server. Çdo klaster staging duhet tĂ« jetĂ« i izoluar (nuk duhet tĂ« ketĂ« pĂ«rputhje nĂ« porta, direktori etj.).

Burimi më i çmuar është koha jonë dhe e kemi pasur pak.

Për një fillim më të shpejtë zgjodhëm Docker Swarm për shkak të thjeshtësisë dhe fleksibilitetit të arkitekturës së tij. E para që bëmë ishte krijimi i menaxherit dhe disa nodash në serverët e largët:

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
kilqc94pi2upzvabttikrfr5d     nop-test-1     Gatshëm               Aktiv                                  19.03.2
jilwe56pl2zvabupryuosdj78     nop-test-2     Gatshëm               Aktiv                                  19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 *   nop-test-3     Gatshëm               Aktiv              Lider              19.03.2

Më pas, krijuam një rrjet:


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

Më pas, lidhëm Gitlab-CI dhe nodat Swarm për menaxhimin e largët të nodave nga CI: instalimi i certifikatat, konfigurimi i variablave të fshehta, si dhe konfigurimi i shërbimit Docker në serverin menaxher. Kjo artikull na kursente shumë kohë.

Më pas, shtuam job'ët për krijimin dhe shkatërrimin e stack-ut në .gitlab-ci.yml.

NĂ« .gitlab-ci.yml u shtuan 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 i mësipërm të kodit, shihet se në Pipelines janë shtuar dy butona (deploy_staging, stop_staging), që kërkojnë ndërhyrje manuale.

Deploy aplikacionet me Docker Swarm
Emri i stekĂ«s i korrespondon emrit tĂ« degĂ«s dhe kjo unikati duhet tĂ« jetĂ« e mjaftueshme. ShĂ«rbimet nĂ« stekĂ« marrin adresa IP unike, ndĂ«rsa portet, direktorĂ«t etj. do tĂ« jenĂ« tĂ« izoluar, por tĂ« njĂ«jta nga steka nĂ« stek (pasi skedari i konfigurimit Ă«shtĂ« i njĂ«jtĂ« pĂ«r tĂ« gjitha stekĂ«t) — ajo qĂ« kemi kĂ«rkuar. Ne zhvillojmĂ« stek (klasterin) me ndihmĂ«n e docker-compose.yml, ku Ă«shtĂ« pĂ«rshkruar klasteri 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ë bashkuar në një rrjet të vetëm (nw_swarm) dhe janë të aksesueshëm për njëri-tjetrin.

Komponentët sistemikë (të bazuar në redis, mysql) janë të ndarë nga kurore e përbashkët e komponentëve të personalizuar (planifikojmë të ndajmë dhe komponentët e personalizuar si shërbime). Faza e implementimit të klasterit tonë duket si transmetimi i CMD në imazhin tonë të madh të konfiguruar dhe praktikisht nuk dallon nga implementimi përshkruar në Pjesën I. Do të theksoj dallimet:

  • git clone 
 — marrim skedarĂ«t e nevojshĂ«m pĂ«r tĂ« prodhuar implementimin (createconfig.py, install_venv.sh etj.)
  • curl
 && unzip 
 — shkarkojmĂ« dhe nxjerrim artefaktet e ndĂ«rtimit (utilitete tĂ« kompiluar)

Ka mbetur vetëm një problem tjetër që nuk është përshkruar akoma: komponentët që kanë ndërfaqe web nuk janë të aksesueshë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 implementimit tĂ« grumbullit tĂ« klasterit, shtojmĂ« rreshtin e implementimit tĂ« balancuesit (i cili pas komitimi, pĂ«rditĂ«son vetĂ«m konfigurimin e tij (krijon skedarĂ« tĂ« rinj konfigurimi 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, shpërndarja e klastereve të izoluara staging është implementuar dhe zhvilluesit tani mund t'i provojnë ato në një numër të mjaftueshëm për të verifikuar detyrat e tyre.

Planet e mëtejshme:

  • TĂ« ndajmĂ« komponentĂ«t tanĂ« si shĂ«rbime
  • TĂ« krijojmĂ« nga njĂ« Dockerfile pĂ«r secilĂ«n
  • TĂ« identifikojmĂ« automatikisht nodet me ngarkesĂ« mĂ« tĂ« ulĂ«t nĂ« njĂ«sinĂ« e sistemit
  • Cakto nodet sipas emrit (nuk pĂ«rdor id si nĂ« artikull)
  • Shto kontroll qĂ« steku Ă«shtĂ« shkatĂ«rruar
  • 


Faleminderit të veçantë për artikullin.

Burimi: habr.com

Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« đŸ”„ Bli njĂ« hosting tĂ« besueshĂ«m pĂ«r faqet me mbrojtje DDoS, VPS VDS serverĂ« | ProHoster