Distribuera applikationer med hjÀlp av Docker Swarm

Det online rekommendationssystemet för videoinnehÄll som vi arbetar med Àr en sluten kommersiell utveckling och representerar tekniskt sett en flerkomponentskluster av eget och öppen kÀllkods komponenter. Syftet med denna artikel Àr att beskriva implementeringen av ett docker swarm kluster under staging-miljön, utan att störa den befintliga arbetsflödet i vÄra processer under begrÀnsad tid. Den redovisning som presenteras för er Àr indelad i tvÄ delar. Den första delen beskriver CI/CD innan anvÀndningen av docker swarm, och den andra - processen för dess implementering. Den som inte Àr intresserad av att lÀsa den första delen kan gÀrna gÄ direkt till den andra.

Del I

I ett avlÀgset Är krÀvdes det att snabbt stÀlla in CI/CD-processen. Ett av villkoren var att inte anvÀnda Docker för distribution av de utvecklade komponenterna av flera skÀl:

  • för att komponenterna skulle fungera mer pĂ„litligt och stabilt i produktion (det vill sĂ€ga ett krav pĂ„ att inte anvĂ€nda virtualisering)
  • ledande utvecklare ville inte arbeta med Docker (konstigt nog, men sĂ„ var det)
  • pĂ„ grund av ledningens ideologiska övertygelser i R&D

Infrastrukturen, stacken och ungefÀrliga krav för MVP sÄg ut som följande:

  • 4 servrar IntelÂź X5650 med Debian (en kraftfullare maskin uteslutande för utveckling)
  • Utvecklingen av egna anpassade komponenter pĂ„gĂ„r i C++, Python3
  • De huvudsakliga tredjepartsverktygen som anvĂ€nds: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, 

  • Bygg- och testpipelines för komponenter separat för debug och release

En av de första frÄgorna som mÄste lösas i det inledande skedet Àr hur distributionen av de anpassade komponenterna kommer att göras i nÄgon miljö (CI/CD).

Tredjepartskomponenter vÀljer att installera och uppdatera systematiskt. Anpassade applikationer, som utvecklas i C++ eller Python, kan distribueras pÄ flera sÀtt. Bland dem, till exempel, skapande av systempaket, skickande av dessa till ett repository med byggda bilder och deras efterföljande installation pÄ servrar. Av okÀnda anledningar valdes en annan metod, nÀmligen: med hjÀlp av CI kompileras körbara filer för applikationerna, en virtuell projektmiljö skapas, py-moduler installeras frÄn requirements.txt och alla dessa artefakter skickas tillsammans med konfigureringsfiler, skript och relaterad miljö för applikationer till servrarna. DÀrefter startas applikationerna av en virtuell anvÀndare utan administratörsrÀttigheter.

Gitlab-CI valdes som CI/CD-system. Den resulterande pipeline sÄg ungefÀr ut sÄ hÀr:

Distribuera applikationer med hjÀlp av Docker Swarm
Strukturen för gitlab-ci.yml sÄg ut pÄ följande sÀtt

---
variables:
  # minimi CPU-version pÄ servrarna dÀr klustret distribueras
  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

Det Àr vÀrt att notera att byggandet och testningen utförs pÄ sin egen bild, dÀr alla nödvÀndiga systempaket redan Àr installerade och andra instÀllningar Àr genomförda.

Även om varje av dessa skript i jobben Ă€r intressanta pĂ„ sitt sĂ€tt, kommer jag sjĂ€lvklart inte att berĂ€tta om dem, eftersom en beskrivning av varje skulle ta betydande tid och det inte Ă€r syftet med artikeln. Jag vill bara pĂ„peka att deploy-stadiet bestĂ„r av en sekvens av anrop av skript:

  1. createconfig.py — skapar en fil settings.ini med komponentinstĂ€llningar i olika miljöer för efterföljande deployment (Preproduction, Production, Testing, 
)
  2. install_venv.sh — skapar en virtuell miljö för py-komponenter i en viss katalog och kopierar den till fjĂ€rrservrar
  3. prepare_init.d.py — förbereder start- och stopp-skript för komponenter baserade pĂ„ en mall
  4. deploy.py — distribuerar och startar om nya komponenter

Tiden gick. Staging-fasen ersattes av preproduction och production. Produktsupport lades till för ytterligare en distribution (CentOS). Fem kraftfulla fysiska servrar och ett dussin virtuella tillkom. Och för utvecklare och testare blev det allt svĂ„rare att testa sina uppgifter i en miljö som nĂ€rmade sig ett produktionslĂ€ge. Vid denna tidpunkt blev det uppenbart att vi inte kunde klara oss utan det


Del II

Distribuera applikationer med hjÀlp av Docker Swarm

SÄ vÄrt kluster utgör verkligen ett synnerligen intressant system med ett par dussin separata komponenter, som inte Àr beskrivna av Dockerfile:er. Det Àr endast möjligt att konfigurera det för deployment i en viss miljö som helhet. VÄrt mÄl Àr att deploya klustret i staging-miljön för att testa det innan pre-release-testning.

Teoretiskt kan det finnas flera samtidiga kluster: sÄ mÄnga som det finns uppgifter i fÀrdigt tillstÄnd eller nÀra fÀrdigstÀllande. De resurser som finns tillgÀngliga pÄ vÄra servrar tillÄter oss att köra flera kluster pÄ varje server. Varje staging-kluster mÄste vara isolerat (det fÄr inte förekomma överlappning av portar, kataloger och sÄdant).

Den mest vÀrdefulla resursen Àr vÄr tid, och vi hade inte mycket av den.

För en snabbare start valde vi Docker Swarm pÄ grund av dess enkelhet och flexibilitet i arkitekturen. Det första vi gjorde var att skapa en manager och flera noder pÄ fjÀrrservrar:

$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
kilqc94pi2upzvabttikrfr5d     nop-test-1     Klar               Aktiv                                  19.03.2
jilwe56pl2zvabupryuosdj78     nop-test-2     Klar               Aktiv                                  19.03.2
j5a4yz1kr2xke6b1ohoqlnbq5 *   nop-test-3     Klar               Aktiv              Ledare              19.03.2

Vidare, skapade vi ett nÀtverk:


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

Vidare kopplade vi Gitlab-CI och Swarm-noderna för fjÀrrhantering av noder frÄn CI: installation av certifikat, instÀllning av hemliga variabler, samt konfiguration av Docker-tjÀnsten pÄ styrservern. HÀr Àr den artikeln sparade oss mycket tid.

Vidare lade vi till jobb för skapande och radering av stackar i .gitlab-ci.yml.

Det lades till nÄgra fler jobb i .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

Av ovanstÄende kodsnutt kan vi se att det tillades tvÄ knappar (deploy_staging, stop_staging) i Pipelines, som krÀver manuell ÄtgÀrd.

Distribuera applikationer med hjÀlp av Docker Swarm
Namn pĂ„ stacken motsvarar grenenamn och denna unikhet bör vara tillrĂ€cklig. TjĂ€nster i stacken fĂ„r unika ip-adresser, och portar, kataloger osv. kommer att vara isolerade men konsekventa frĂ„n stack till stack (eftersom konfigurationsfilen Ă€r densamma för alla stackar) – vilket var vad vi efterstrĂ€vade. Stacken (klustret) lanserar vi med hjĂ€lp av docker-compose.yml, som beskriver vĂ„rt kluster.

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

Det syns att komponenterna Àr sammankopplade via ett nÀtverk (nw_swarm) och Àr tillgÀngliga för varandra.

Systemkomponenter (baserade pÄ redis, mysql) Àr separerade frÄn den allmÀnna poolen av anpassade komponenter (i planerna ska Àven de anpassade delas upp som tjÀnster). Deploy-stadiet för vÄr kluster ser ut som en överföring av CMD till vÄr stora, konfigurerade bild och skiljer sig i stort sett inte frÄn den deployment som beskrivs i del I. Jag vill understryka skillnaderna:

  • git clone 
 — vi fĂ„r filer som behövs för att genomföra deploy (createconfig.py, install_venv.sh etc.)
  • curl
 && unzip 
 — vi laddar ner och packar upp byggartefakter (kompilerade verktyg)

Det finns fortfarande ett problem som inte har beskrivits: komponenterna med webbgrÀnssnitt Àr inte tillgÀngliga frÄn utvecklarens webblÀsare. Vi löser detta problem med hjÀlp av en reverse proxy pÄ följande sÀtt:

I .gitlab-ci.yml, efter deployment av klusterstacken, lĂ€gger vi till en rad för deployment av lastbalanseraren (som vid commits endast uppdaterar sin konfiguration (skapar nya konfigurationsfiler för nginx enligt mallen: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) — se kod 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

PĂ„ utvecklarnas datorer uppdaterar vi /etc/hosts; vi skriver in URL:en till nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

SÄ, deployment av de isolerade staging-klustren har genomförts och utvecklarna kan nu köra dem i vilket antal som helst som krÀvs för att kontrollera sina uppgifter.

Framtida planer:

  • Dela upp vĂ„ra komponenter som tjĂ€nster
  • Skapa en separat Dockerfile för varje komponent
  • Automatiskt identifiera mindre belastade noder i stacken
  • Angiv noder enligt namnmall (och anvĂ€nd inte id som i artikeln)
  • LĂ€gg till kontroll för att sĂ€kerstĂ€lla att stacken har raderats
  • 


Ett sÀrskilt tack för artikeln.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster