Distribuere applikasjoner med Docker Swarm

Den online anbefalingssystemet for videoinnhold som vi jobber med, er en lukket kommersiell utvikling og utgjĂžr teknisk sett en multi-komponentklynge av egne og open source-komponenter. MĂ„let med denne artikkelen er Ă„ beskrive implementeringen av docker swarm-klyngesystemet for staging-miljĂžet, uten Ă„ forstyrre den etablerte arbeidsflyten vĂ„r innenfor begrenset tid. Den fortellingen som presenteres for deg, er delt inn i to deler. Den fĂžrste delen beskriver CI/CD fĂžr bruken av docker swarm, og den andre — prosessen med dens implementering. De som ikke er interessert i Ă„ lese den fĂžrste delen, kan trygt gĂ„ videre til den andre.

Del I

I et fjernt Är var det nÞdvendig Ä sette opp CI/CD-prosessen sÄ raskt som mulig. En av betingelsene var Ä ikke bruke Docker til distribusjon av utviklede komponenter av flere grunner:

  • for mer pĂ„litelig og stabil drift av komponentene i produksjon (det vil si kravet om ikke Ă„ bruke virtualisering)
  • de ledende utviklerne Ăžnsket ikke Ă„ jobbe med Docker (merkelig, men det var faktisk slik)
  • av ideologiske grunner fra ledelsen i R&D

Infrastrukturen, staken og de omtrentlige spesifikasjonene for MVP ble presentert som fĂžlger:

  • 4 servere IntelÂź X5650 med Debian (en kraftigere maskin fullt ut til utvikling)
  • Utviklingen av egne tilpassede komponenter foregĂ„r i C++, Python3
  • De viktigste 3rdparty-brukte verktĂžyene: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, 

  • Bygg- og testpipelines for komponenter separat for debug og release

Ett av de fÞrste spÞrsmÄlene som mÄ lÞses i den tidlige fasen, er hvordan distribusjonen av tilpassede komponenter vil finne sted i et hvilket som helst miljÞ (CI/CD).

Tredjeparter har besluttet Ä installere og oppdatere systemisk. Tilpassede applikasjoner, utviklet i C++ eller Python, kan implementeres pÄ flere mÄter. Blant dem er: opprettelse av systempakker, sending av disse til et repository av kompilte bilder og deres pÄfÞlgende installasjon pÄ serverne. Av en ukjent grunn ble en annen metode valgt, nemlig: med CI kompilert kjÞrbare filer for applikasjoner, opprettet et virtuelt miljÞ for prosjektet, installert py-moduler fra requirements.txt, og alle disse artefaktene blir sendt sammen med konfigurasjoner, skript og tilhÞrende applikasjonsmiljÞer til serverne. Deretter startes applikasjonene av en virtuell bruker uten administratorrettigheter.

Gitlab-CI ble valgt som CI/CD-system. Den resulterende pipeline sÄ omtrent slik ut:

Distribuere applikasjoner med Docker Swarm
Strukturelt sÄ gitlab-ci.yml slik ut

---
variables:
  # minimum CPU-versjon pÄ serverne der klyngen deployeres
  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 er viktig Ä merke seg at byggingen og testing skjer pÄ sitt eget bilde, hvor alle nÞdvendige systempakker allerede er installert, og andre konfigurasjoner er gjort.

Selv om hver av disse skriptene i jobber har sin egen interesse, kommer jeg selvfÞlgelig ikke til Ä forklare dem da beskrivelsen av hver av dem ville ta betydelig tid, og det er ikke mÄlet med artikkelen. Jeg vil bare pÄpeke at distribusjonsfasen bestÄr av en sekvens av skriptoppringninger:

  1. createconfig.py — oppretter filen settings.ini med komponentinnstillinger i forskjellige miljþer for videre distribusjon (Preproduction, Production, Testing, 
)
  2. install_venv.sh — oppretter et virtuelt miljþ for py-komponentene i en spesifikk katalog og kopierer det til eksterne servere
  3. prepare_init.d.py — forbereder start- og stopp-skriptene for komponentene basert pĂ„ en mal
  4. deploy.py — distribuerer og gjenstarter nye komponenter

Tiden gikk. Staging-fasen ble erstattet av preproduksjon og produksjon. StĂžtte for produktet ble lagt til i enda en distribusjon (CentOS). Fem kraftige fysiske servere og et dusin virtuelle ble lagt til. Utviklerne og testerne fikk det stadig vanskeligere Ă„ teste oppgavene sine i et miljĂž som var mer eller mindre likt det operative. Det ble klart i denne perioden at vi ikke kunne klare oss uten det


Del II

Distribuere applikasjoner med Docker Swarm

SĂ„, klustret vĂ„rt fremstĂ„r som et imponerende syn, et system bestĂ„ende av et par dusin separate komponenter som ikke er beskrevet av Dockerfile-filer. Å konfigurere det for distribusjon til et spesifikt miljĂž kan kun gjĂžres som helhet. VĂ„rt mĂ„l er Ă„ distribuere klustret til staging-miljĂžet for testing fĂžr pre-release.

Teoretisk kan det vÊre flere klustre som kjÞrer samtidig: sÄ mange som oppgavene som er i fullfÞrt eller nÊr fullfÞrt tilstand. Kraften som er tilgjengelig pÄ serverne vÄre tillater oss Ä kjÞre flere klustre pÄ hver server. Hvert staging-kluster mÄ vÊre isolert (det mÄ ikke vÊre overlapping nÄr det gjelder porter, kataloger osv.).

Den mest verdifulle ressursen er tiden vÄr, og vi hadde lite av den.

For Ä starte raskere valgte vi Docker Swarm pÄ grunn av dens enkelhet og fleksibilitet. Det fÞrste vi gjorde, var Ä opprette en manager og flere noder pÄ de eksterne serverne:

$ 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              Leiar              19.03.2

Vidare, oppretta vi eit nettverk:


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

Vidare, knytte vi Gitlab-CI og Swarm-nodene for fjernadministrasjon av nodene frÄ CI: installasjon av sertifikat, konfigurering av hemmelige variablar, samt konfigurasjon av Docker-tjenesten pÄ styringsserveren. Dette article sparte oss mykje tid.

Vidare la vi til jobbar for oppretting og sletting av stakken i .gitlab-ci.yml.

I .gitlab-ci.yml kom det til fleire jobbar

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

FrÄ den ovanfor viste kodebiten ser vi at det er lagt til to knappar i Pipelines (deploy_staging, stop_staging) som krev manuell inngriping.

Distribuere applikasjoner med Docker Swarm
Namn pĂ„ stakken samsvarar med namnet pĂ„ greina, og denne unikheit bĂžr vere tilstrekkeleg. Tjenestene i stakken fĂ„r unike IP-adresser, medan portar, katalogar og sĂ„ vidare vil vere isolerte, men like for kvar stakk (sidan konfigurasjonsfilen er den same for alle stakkar) — noko vi har oppnĂ„dd. Stakken (klynga) rullast ut med hjelp av docker-compose.yml, der klustret vĂ„rt er beskrevet.

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

Her ser ein at komponentane er kopla saman med eit nett (nw_swarm) og er tilgjengelege for kvarandre.

Systemkomponentar (basert pÄ redis, mysql) er separert frÄ den generelle bassenget av tilpassa komponentar (i planane er det ogsÄ tilpassa som skal delast som tenester). Steget for distribusjon av klynga vÄr ser ut som Ä overfÞre CMD til vÄrt eine store konfigurerte bilete, og skiller seg i det heile tatt ikkje frÄ distribusjonen som er beskriven i Del I. Eg vil understreke skillene:

  • git clone 
 — vi hentar fila som er nĂždvendige for Ă„ gjennomfĂžre distribusjonen (createconfig.py, install_venv.sh osv.)
  • curl
 && unzip 
 — vi lastar ned og pakkar ut byggartefaktane (kompilerte verktĂžy)

Det er bare ett ubeskrevet problem igjen: komponentene med webgrensesnitt er ikke tilgjengelige fra utviklernes nettlesere. Vi lÞser dette problemet med en reverse proxy pÄ fÞlgende mÄte:

I .gitlab-ci.yml etter distribusjonen av klyngen, legger vi til linjen for distribusjon av lastbalanserer (som kun oppdaterer sin konfigurasjon ved forpliktelse (lager nye konfigurasjonsfiler for nginx etter mal: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) — se koden 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Ă„ utviklernes datamaskiner oppdaterer vi /etc/hosts; vi skriver inn URL-en til nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

SÄ, distribusjonen av isolerte staging-klynger er gjennomfÞrt, og utviklerne kan nÄ kjÞre dem i et hvilket som helst tilstrekkelig antall for Ä teste oppgavene sine.

Fremtidige planer:

  • Dele komponentene vĂ„re som tjenester
  • Opprette hver Dockerfile
  • Automatisk bestemme mindre belastede noder i klyngen
  • Sette noder etter navnemal (og ikke bruke id som i artikkelen)
  • Legge til sjekk for at klyngen er Ăždelagt
  • 


Spesiell takk til artikkelen.

Kilde: habr.com

KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster