Implementa Applicazioni cù Docker Swarm

U sistema di ricunniscenza di cuntenutu video in linea chì avemu travagliatu hè un sviluppu cummerciale chjusu è tecnicamente hè un cluster multi-cumpunente di cumpunenti proprietarii è open source. U scopu di scrive stu articulu hè di discrive l'implementazione di u sistema di clustering docker swarm per un situ di staging senza disturbà u flussu di travagliu stabilitu di i nostri prucessi in un tempu limitatu. A narrativa presentata à a vostra attenzione hè divisa in dui parti. A prima parte descrive CI / CD prima di utilizà docker swarm, è a seconda descrive u prucessu di a so implementazione. Quelli chì ùn anu micca interessatu à leghje a prima parte pò passà in modu sicuru à a seconda.

Parte I

Torna in l'annu distante, distante, era necessariu di stallà u prucessu CI / CD u più prestu pussibule. Una di e cundizioni ùn era micca aduprà Docker per l'implementazione cumpunenti sviluppati per parechje ragioni:

  • per un funziunamentu più affidabile è stabile di cumpunenti in Produzione (vale à dì, in fatti, u requisitu di ùn aduprà a virtualizazione)
  • I sviluppatori principali ùn vulianu micca travaglià cù Docker (stranu, ma hè cusì)
  • secondu e considerazioni ideologiche di a gestione di R&D

L'infrastruttura, a pila è i requisiti iniziali apprussimati per MVP sò stati presentati cusì:

  • 4 servitori Intel® X5650 cù Debian (una macchina più putente hè sviluppata cumplettamente)
  • U sviluppu di i propri cumpunenti persunalizati hè realizatu in C ++, Python3
  • Principali strumenti di terze parti utilizati: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, ...
  • Pipelines per custruisce è teste cumpunenti separatamente per debug è liberazione

Una di e prime dumande chì deve esse trattatu in a fase iniziale hè cumu si metteranu cumpunenti persunalizati in ogni ambiente (CI / CD).

Avemu decisu di stallà cumpunenti di terzu in modu sistematicu è aghjurnà in modu sistemicu. L'applicazioni persunalizate sviluppate in C++ o Python ponu esse implementate in parechje manere. Frà elli, per esempiu: creazione di pacchetti di sistema, inviendu à u repository di l'imaghjini custruiti è poi installendu in i servitori. Per una ragione scunnisciuta, hè statu sceltu un altru metudu, vale à dì: usendu CI, i schedarii eseguibili di l'applicazioni sò compilati, un ambiente di prughjettu virtuale hè creatu, i moduli py sò stallati da requirements.txt, è tutti questi artefatti sò mandati cù configs, scripts è u accumpagna l'ambiente di l'applicazione à i servitori. Dopu, l'applicazioni sò lanciate cum'è un utilizatore virtuale senza diritti di amministratore.

Gitlab-CI hè statu sceltu cum'è u sistema CI / CD. U pipeline risultante pareva cusì cusì:

Implementa Applicazioni cù Docker Swarm
Strutturalmente, gitlab-ci.yml pareva cusì

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

Hè da nutà chì l'assemblea è a prova sò realizati nantu à a so propria maghjina, induve tutti i pacchetti di sistema necessarii sò digià stallati è altri paràmetri sò stati fatti.

Ancu ognuna di sti scripts in impieghi hè interessante in u so modu, ma di sicuru ùn ne parleraghju micca.. A descrizzione di ognuna di elli hà da piglià assai tempu è questu ùn hè micca u scopu di l'articulu. Aghju attiratu solu a vostra attenzione à u fattu chì a tappa di implementazione hè custituita da una sequenza di script di chjama:

  1. createconfig.py - crea un schedariu settings.ini cù paràmetri di cumpunenti in diversi ambienti per a implementazione successiva (Preproduzione, Produzione, Test, ...)
  2. install_venv.sh - crea un ambiente virtuale per i cumpunenti py in un repertoriu specificu è u copia à i servitori remoti
  3. prepare_init.d.py - prepara script start-stop per u cumpunente basatu annantu à u mudellu
  4. deploy.py - decompose è riavvia novi cumpunenti

U tempu passava. A tappa di staging hè stata rimpiazzata da a preproduzione è a pruduzzione. Aghjunghje supportu per u pruduttu nantu à una distribuzione più (CentOS). Aghjunghjite 5 servitori fisichi più putenti è una decina di virtuale. È hè diventatu di più in più difficiuli per i sviluppatori è i testatori di pruvà i so compiti in un ambiente più o menu vicinu à u statu di travagliu. À questu tempu, hè diventatu chjaru chì era impussibile di fà senza ellu ...

Parte II

Implementa Applicazioni cù Docker Swarm

Dunque, u nostru cluster hè un sistema spettaculare di un coppiu di decine di cumpunenti separati chì ùn sò micca descritti da Dockerfiles. Pudete solu cunfigurà per a implementazione in un ambiente specificu in generale. U nostru compitu hè di implementà u cluster in un ambiente di staging per pruvà prima di teste di pre-liberazione.

Teoricamente, ci ponu esse parechje clusters chì currenu simultaneamente: quant'è ci sò compiti in u statu finitu o vicinu à a fine. E capacità di i servitori à a nostra dispusizione ci permettenu di eseguisce parechji clusters in ogni servitore. Ogni cluster di staging deve esse isolatu (ùn deve esse micca intersezzione in porti, cartulari, etc.).

A nostra risorsa più preziosa hè u nostru tempu, è ùn avemu micca assai di questu.

Per un principiu più veloce, avemu sceltu Docker Swarm per a so simplicità è a flessibilità di l'architettura. A prima cosa chì avemu fattu hè di creà un manager è parechji nodi nantu à i servitori remoti:

$ 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

Dopu, crea una reta:


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

In seguitu, avemu cunnessu i nodi Gitlab-CI è Swarm in quantu à u cuntrollu remotu di i nodi da CI: installà certificati, stabilisce variàbili secreti, è stabilisce u serviziu Docker in u servitore di cuntrollu. Questu un articulu ci hà risparmiatu assai tempu.

In seguitu, aghjunghjemu i travaglii di creazione è distruzzione di stack à .gitlab-ci .yml.

Uni pochi di più impieghi sò stati aghjuntu à .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

Da u snippet di codice sopra, pudete vede chì dui buttoni (deploy_staging, stop_staging) sò stati aghjuntu à Pipelines, chì necessitanu l'azzione manuale.

Implementa Applicazioni cù Docker Swarm
U nome di stack currisponde à u nome di a filiera è questa unicità deve esse abbastanza. I servizii in a pila ricevenu indirizzi IP unichi, è porti, cartulari, etc. serà isolatu, ma u listessu da stack à stack (perchè u schedariu di cunfigurazione hè u listessu per tutti i stacks) - ciò chì vulemu. Implementemu a pila (cluster) usendu docker-compose.yml, chì descrive u nostru cluster.

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

Quì pudete vede chì i cumpunenti sò cunnessi da una reta (nw_swarm) è sò dispunibuli per l'altri.

I cumpunenti di u sistema (basatu nantu à redis, mysql) sò siparati da u gruppu generale di cumpunenti persunalizati (in i piani è quelli persunalizati sò spartuti cum'è servizii). A tappa di implementazione di u nostru cluster s'assumiglia à passà CMD in a nostra una grande maghjina cunfigurata è, in generale, praticamenti ùn difiere micca da a implementazione descritta in a Parte I. Evideraghju e differenze:

  • git clone... - uttene i fugliali necessarii per implementà (createconfig.py, install_venv.sh, etc.)
  • arriccia... && unzip... - scaricate è scomprime l'artefatti di custruzzione (utilità cumpilate)

Ci hè solu un prublema ancu micca descrittu: i cumpunenti chì anu una interfaccia web ùn sò micca accessibili da i navigatori di sviluppatori. Risolvemu stu prublema usendu proxy inversu, cusì:

In .gitlab-ci.yml, dopu avè implementatu u cluster stack, aghjunghjemu a linea di implementazione di u balancer (chì, quandu si cummette, aghjurnà solu a so cunfigurazione (crea novi schedarii di cunfigurazione nginx secondu u mudellu: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - vede u codice 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

Nant'à i computer di sviluppu, aghjurnà /etc/hosts; prescrive l'url à nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Dunque, a implementazione di clusters di staging isolati hè stata implementata è i sviluppatori ponu avà eseguisce in ogni numeru abbastanza per verificà e so attività.

Piani futuri:

  • Separate i nostri cumpunenti cum'è servizii
  • Avete per ogni Dockerfile
  • Detecta automaticamente i nodi menu caricati in a pila
  • Specificà i nodi per u mudellu di nome (piuttostu cà aduprà l'id cum'è in l'articulu)
  • Aghjunghjite un verificatu chì a pila hè distrutta
  • ...

Grazie speciale per articulu.

Source: www.habr.com

Add a comment