Lietojumprogrammu izvietošana, izmantojot Docker Swarm

Mūsu izstrādātā video satura tiešsaistes ieteikumu sistēma ir slēgta komerciāla izstrāde, kas tehniski ir daudzkomponentu klasteris no privātiem un atvērtā koda komponentiem. Šī raksta mērķis ir aprakstīt docker swarm klasterizācijas sistēmas ieviešanu stāvvietā, nepārkāpjot mūsu procesu darba plūsmu ierobežota laika apstākļos. Jūsu uzmanībai piedāvātais stāstījums ir sadalīts divās daļās. Pirmajā daļā tiek aprakstīts CI/CD pirms docker swarm izmantošanas, bet otrajā — tā ieviešanas process. Tie, kas nav ieinteresēti lasīt pirmo daļu, var droši pāriet uz otro.

1. daļa

Tālā, tālā gadā bija nepieciešams pēc iespējas ātrāk uzstādīt CI/CD procesu. Viens no nosacījumiem bija neizmantot Docker izvietošanai izstrādātajiem komponentiem vairākos iemeslu dēļ:

  • lai nodrošinātu uzticamāku un stabilāku komponentu darbību ražošanā (t.i., būtībā prasība neizmantot virtualizāciju)
  • galvenie izstrādātāji negribēja strādāt ar Docker (dīvaini, bet tā tiešām bija)
  • izpētes un attīstības vadības idejisku iemeslu dēļ

Infrastruktūra, steks un aptuvenās prasības MVP bija šādas:

  • 4 serveri Intel® X5650 ar Debian (viena spēcīgāka mašīna pilnībā izstrādei)
  • Pats šo pasūtījuma komponentu izstrāde notiek C++, Python3
  • Pamatā izmantotie trešo pušu rīki: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • Sadaļu izveide un testēšana atsevišķi debug un release

Viens no pirmajiem jautājumiem, kas jāatrisina sākumposmā, ir kā tiks veikta pasūtījuma komponentu izvietošana kādā vidē (CI/CD).

Trešo pušu komponentes nolēma instalēt un atjaunināt sistēmiski. Tomēr pielāgotās lietojumprogrammas, kas izstrādātas C++ vai Python valodā, var izvietot vairākos veidos. Starp tiem ir, piemēram: sistēmas pakotņu izveide, to nosūtīšana uz sagatavoto attēlu krātuvi un to turpmāka instalācija serveros. Nezināmu iemeslu dēļ tika izvēlēts cits paņēmiens, proti: izmantojot CI, tiek kompilēti izpildāmi lietojumprogrammu faili, tiek izveidota projekta virtuālā vide, tiek instalēti py moduļi no requirements.txt, un visi šie artefakti tiek nosūtīti kopā ar konfigurācijām, skriptiem un saistīgo pielietojuma vidi uz serveriem. Pēc tam tiek palaistas lietojumprogrammas no virtuālā lietotāja bez administratora tiesībām.

CI/CD sistēmas izvēlēti Gitlab-CI. Iegūtais pipeline izskatījās aptuveni šādi:

Lietojumprogrammu izvietošana, izmantojot Docker Swarm
Strukturāli gitlab-ci.yml izskatījās šādi

---
variables:
  # minimālā CPU versija serveros, uz kuriem tiek izvietots klasteris
  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

## testēšanas posms
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

## iepriekšējā posms
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

Jāatzīmē, ka izstrāde un testēšana tiek veikta uz sava attēla, kur jau ir instalēti visi nepieciešamie sistēmas pakotnes un veikti citi iestatījumi.

Lai gan katrs no šiem skriptiem darba uzdevumos ir interesants savā ziņā, es protams nestāstīšu par tiem, jo katra aprakstīšana prasītu ievērojamu laiku, un raksta mērķis nav šis. Vēršu uzmanību uz to, ka izvietošanas posms sastāv no secīgas skriptu izsaukšanas:

  1. createconfig.py — izveido settings.ini failu ar komponentu konfigurācijām dažādās vidēs tālākai izvietošanai (Priekšprodukcija, Ražošana, Testēšana, …)
  2. install_venv.sh — izveido virtuālo vidēšanu py komponentēm noteiktā direktorijā un kopē to uz attālinātiem serveriem
  3. prepare_init.d.py — sagatavo komponentu palaišanas un apstāšanās skriptus, pamatojoties uz šablonu
  4. deploy.py — izvieto un restartē jaunas komponentes

Laiks gāja. Staging posmu nomainīja priekšprodukcija un ražošana. Atbalstīts vēl viens produkta izplatījums (CentOS). Tika pievienoti vēl 5 jaudīgi fiziskie serveri un desmit virtuālie. Un izstrādātājiem un testētājiem kļuva arvien grūtāk pārbaudīt savus uzdevumus vidē, kas vairāk vai mazāk atbilst darba stāvoklim. Šajā laikā kļuva skaidrs, ka bez tā neiztikt…

Otrā daļa

Lietojumprogrammu izvietošana, izmantojot Docker Swarm

Tātad, mūsu klasteris ir diezgan iespaidīga sistēma no pāris desmitiem atsevišķu komponentu, kas nav aprakstīti ar Dockerfile. To konfigurēt izvietošanai noteiktā vidē var tikai kopumā. Mūsu uzdevums ir izvietot klasteri staging vidē, lai to iztestētu pirms priekšizlaides testēšanas.

Teorētiski, vienlaikus var būt vairāki klasteri: tik, cik uzdevumu ir pabeigtā vai tuvu pabeigtai stāvoklī. Resursi, kas ir mūsu rīcībā esošajos servidores, ļauj palaist vairākus klasterus katrā serverī. Katram staging klasterim jābūt izolētam (nedrīkst būt portu, direktoriju utt. pārklāšanās).

Visvērtīgākais resurss ir mūsu laiks, un tā mums bija maz.

Lai ātrāk uzsāktu, izvēlējāmies Docker Swarm tā vienkāršības un elastīgās arhitektūras dēļ. Pirmā lieta, ko mēs izdarījām, bija izveidot attālinātajos servidores menedžeri un pāris mezglus:

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
kilqc94pi2upzvabttikrfr5d     nop-test-1     Gatavs               Aktīvs                                 19.03.2
jilwe56pl2zvabupryuosdj78     nop-test-2     Gatavs               Aktīvs                                 19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 *   nop-test-3     Gatavs               Aktīvs              Līderis              19.03.2

Tālāk izveidojām tīklu:


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

Tālāk saistījām Gitlab-CI un Swarm mezglus attiecībā uz tālvadību no CI: sertifikātu uzstādīšana, slepeno mainīgo konfigurēšana, kā arī Docker pakalpojuma konfigurēšana vadības serverī. Šis raksts mums ļoti ietaupīja laiku.

Tālāk pievienojām jobus staka izveidošanai un iznīcināšanai .gitlab-ci.yml.

Tādējādi .gitlab-ci.yml pievienojās vēl dažas 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

No iepriekš minētā koda fragmenta ir redzams, ka Pipelines pievienojās divas pogas (deploy_staging, stop_staging), kas prasa roku iejaukšanos.

Lietojumprogrammu izvietošana, izmantojot Docker Swarm
Staka nosaukums atbilst zarojuma nosaukumam, un šai unikālitātei vajadzētu būt pietiekamai. Pakalpojumi stakā saņem unikālas IP adreses, bet porti, direktorijas u.t.t. būs izolēti, bet vienādi no staka uz staku (jo konfigurācijas fails ir identisks visiem stakiem) — tieši to mēs arī vēlējāmies. Staku (klasteri) mēs izvēršam, izmantojot docker-compose.yml, kurā ir aprakstīts mūsu klasteris.

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

Šeit redzams, ka komponenti ir apvienoti vienotā tīklā (nw_swarm) un ir pieejami viens otram.

Sistēmas komponenti (balstīti uz redis, mysql) ir atdalīti no kopējā pielāgoto komponentu baseina (plānos ir pielāgoto komponentu sadalīšana kā pakalpojumi). Mūsu klastera izvietošanas posms izskatās kā CMD nodošana mūsu lielajam konfigurētajam attēlam un būtībā praktiski neatšķiras no izvietošanas, kas aprakstīta I daļā. Uzsvēršu atšķirības:

  • git clone … — iegūstam failus, kas nepieciešami izvietošanai (createconfig.py, install_venv.sh u.c.)
  • curl… && unzip … — lejupielādējam un izrakstām izstrādes artefaktus (kompilētās utilītas)

Ir palikusi tikai viena līdz šim neaprakstīta problēma: komponenti, kuriem ir tīmekļa saskarne, nav pieejami izstrādātāju pārlūkprogrammās. Mēs šo problēmu risinām ar reverso proksi, šādā veidā:

Failā .gitlab-ci.yml pēc klastera steka izvietošanas pievienojam rindu slodzes izlīdzinātāja izvietošanai (kas, pievienojot izmaiņas, tikai atjauno savu konfigurāciju (izveido jaunus konfigurācijas failus nginx pēc parauga: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) — skat. kods 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

Izstrādātāju datoros atjaunojam /etc/hosts; ierakstām url līdz nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Tātad, izolētu staging-klasteru izvietošana ir īstenota, un izstrādātāji tagad var tos palaist vajadzīgā skaitā, lai pārbaudītu savus uzdevumus.

Nākamie plāni:

  • Sadalīt mūsu komponentus kā pakalpojumus
  • Pievienot katram Dockerfile
  • Automātiski noteikt mazāk noslogotas mezglus stekā
  • Noteikt mezglus pēc nosaukuma parauga (nevis izmantot id kā rakstā)
  • Pievienot pārbaudi, ka steks ir iznīcināts
  • …

Atsevišķa pateicība par rakstu.

Avots: habr.com

Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem 🔥 Iegādājieties uzticamu tīmekļa hostingu ar DDoS aizsardzību, VPS VDS serveriem | ProHoster