Ontplooi toepassings met Docker Swarm

Die aanlyn video-inhoud-aanbevelingstelsel waaraan ons werk, is 'n geslote kommersiële ontwikkeling en is tegnies 'n multi-komponent-kluster van eie- en oopbronkomponente. Die doel van die skryf van hierdie artikel is om die implementering van die docker-swermgroeperingstelsel vir 'n opstelterrein te beskryf sonder om die gevestigde werkvloei van ons prosesse in 'n beperkte tyd te ontwrig. Die narratief wat aan u aandag gegee word, word in twee dele verdeel. Die eerste deel beskryf CI / CD voordat docker swarm gebruik word, en die tweede beskryf die proses van die implementering daarvan. Diegene wat nie daarin belangstel om die eerste deel te lees nie, kan gerus aanbeweeg na die tweede.

Deel I

Terug in die verre, verre jaar, was dit nodig om die CI / CD-proses so vinnig as moontlik op te stel. Een van die voorwaardes was om nie Docker te gebruik nie vir ontplooiing ontwikkel komponente om verskeie redes:

  • vir meer betroubare en stabiele werking van komponente in produksie (dit is in werklikheid die vereiste om nie virtualisering te gebruik nie)
  • toonaangewende ontwikkelaars wou nie met Docker werk nie (vreemd, maar dit is hoe dit was)
  • op die ideologiese gronde van N&O-bestuur

Infrastruktuur, stapel en benaderde aanvanklike vereistes vir MVP is soos volg aangebied:

  • 4 Intel® X5650-bedieners met Debian (een meer kragtige masjien is volledig ontwikkel)
  • Ontwikkeling van eie pasgemaakte komponente word uitgevoer in C ++, Python3
  • Belangrikste nutsmiddels van derdepartye wat gebruik word: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • Pyplyne vir die bou en toets van komponente afsonderlik vir ontfouting en vrystelling

Een van die eerste vrae wat in die aanvanklike stadium aangespreek moet word, is hoe pasgemaakte komponente in enige omgewing (CI / CD) ontplooi sal word.

Ons het besluit om derdeparty-komponente sistemies te installeer en dit sistemies op te dateer. Pasgemaakte toepassings wat in C++ of Python ontwikkel is, kan op verskeie maniere ontplooi word. Onder hulle, byvoorbeeld: die skep van stelselpakkette, stuur dit na die bewaarplek van geboude beelde en installeer dit dan op bedieners. Vir 'n onbekende rede is 'n ander metode gekies, naamlik: deur gebruik te maak van CI, word toepassingsuitvoerbare lêers saamgestel, 'n virtuele projekomgewing word geskep, py-modules word vanaf requirements.txt geïnstalleer, en al hierdie artefakte word saam met konfigurasies, skrifte en die gepaardgaande toepassingsomgewing na bedieners. Vervolgens word toepassings as 'n virtuele gebruiker sonder administrateurregte geloods.

Gitlab-CI is as die CI/CD-stelsel gekies. Die gevolglike pyplyn het iets soos volg gelyk:

Ontplooi toepassings met Docker Swarm
Struktureel het gitlab-ci.yml so gelyk

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

Dit is opmerklik dat die samestelling en toetsing op sy eie beeld uitgevoer word, waar al die nodige stelselpakkette reeds geïnstalleer is en ander instellings gemaak is.

Alhoewel elkeen van hierdie skrifte in poste op sy eie manier interessant is, maar natuurlik sal ek nie daaroor praat nie. Die beskrywing van elkeen van hulle sal baie tyd neem en dit is nie die doel van die artikel nie. Ek sal u aandag net daarop vestig dat die ontplooiingstadium uit 'n reeks oproepskrifte bestaan:

  1. createconfig.py - skep 'n settings.ini-lêer met komponentinstellings in verskeie omgewings vir daaropvolgende ontplooiing (voorproduksie, produksie, toetsing, ...)
  2. install_venv.sh - skep 'n virtuele omgewing vir py-komponente in 'n spesifieke gids en kopieer dit na afgeleë bedieners
  3. prepare_init.d.py - berei begin-stop-skrifte voor vir die komponent gebaseer op die sjabloon
  4. deploy.py - ontbind en herbegin nuwe komponente

Tyd het verbygegaan. Die verhoogfase is vervang deur voorproduksie en produksie. Bygevoeg ondersteuning vir die produk op nog een verspreiding (CentOS). Bygevoeg 5 meer kragtige fisiese bedieners en 'n dosyn virtuele bedieners. En dit het al hoe moeiliker geword vir ontwikkelaars en toetsers om hul take te toets in 'n omgewing min of meer naby aan die werkende toestand. Op hierdie tydstip het dit duidelik geword dat dit onmoontlik was om sonder hom klaar te kom ...

Deel II

Ontplooi toepassings met Docker Swarm

Dus, ons groepering is 'n skouspelagtige stelsel van 'n paar dosyn afsonderlike komponente wat nie deur Dockerfiles beskryf word nie. U kan dit slegs opstel vir ontplooiing na 'n spesifieke omgewing in die algemeen. Ons taak is om die groepering in 'n opstelomgewing te ontplooi om dit te toets voor voorvrystellingtoetsing.

Teoreties kan daar verskeie groepe gelyktydig loop: soveel as wat daar take is in die voltooide toestand of naby aan voltooiing. Die kapasiteit van die bedieners tot ons beskikking stel ons in staat om verskeie groepe op elke bediener te laat loop. Elke staging cluster moet geïsoleer wees (daar moet geen kruising in hawens, gidse, ens. wees nie).

Ons waardevolste hulpbron is ons tyd, en ons het nie veel daarvan gehad nie.

Vir 'n vinniger begin het ons Docker Swarm gekies as gevolg van sy eenvoud en argitektuur buigsaamheid. Die eerste ding wat ons gedoen het, was om 'n bestuurder en verskeie nodusse op die afgeleë bedieners te skep:

$ 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

Skep dan 'n netwerk:


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

Vervolgens het ons Gitlab-CI- en Swarm-nodusse gekoppel in terme van afstandbeheer van nodusse vanaf CI: die installering van sertifikate, die opstel van geheime veranderlikes en die opstel van die Docker-diens op die beheerbediener. Hierdie een статья het ons baie tyd gespaar.

Vervolgens het ons stapelskepping en vernietigingstake by .gitlab-ci .yml gevoeg.

Nog 'n paar poste is by .gitlab-ci .yml gevoeg

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

Uit die bogenoemde kodebrokkie kan jy sien dat twee knoppies (deploy_staging, stop_staging) by Pipelines gevoeg is, wat handmatige aksie vereis.

Ontplooi toepassings met Docker Swarm
Die stapelnaam pas by die taknaam en hierdie uniekheid behoort voldoende te wees. Dienste in die stapel ontvang unieke IP-adresse, en poorte, gidse, ens. sal geïsoleer wees, maar dieselfde van stapel tot stapel (omdat die konfigurasielêer dieselfde is vir alle stapels) - wat ons wou hê. Ons ontplooi die stapel (cluster) met behulp van Docker-compose.yml, wat ons cluster beskryf.

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

Hier kan jy sien dat die komponente deur een netwerk (nw_swarm) verbind is en vir mekaar beskikbaar is.

Stelselkomponente (gebaseer op redis, mysql) word geskei van die algemene poel van pasgemaakte komponente (in planne en pasgemaakte word as dienste verdeel). Die ontplooiingstadium van ons groepie lyk soos om CMD in ons een groot gekonfigureerde beeld deur te gee en verskil oor die algemeen feitlik nie van die ontplooiing wat in Deel I beskryf word nie. Ek sal die verskille uitlig:

  • git kloon... - kry die lêers wat nodig is om te ontplooi (createconfig.py, install_venv.sh, ens.)
  • krul... && trek uit... - laai en unzip bou-artefakte (saamgestelde nutsprogramme)

Daar is net een nog onbeskryfde probleem: komponente wat 'n webkoppelvlak het, is nie toeganklik vanaf ontwikkelaars se blaaiers nie. Ons los hierdie probleem op met behulp van omgekeerde instaanbediener, dus:

In .gitlab-ci.yml, nadat ons die groepstapel ontplooi het, voeg ons die reël by van die ontplooiing van die balanseerder (wat, wanneer dit gepleeg word, slegs sy konfigurasie opdateer (skep nuwe nginx-konfigurasielêers volgens die sjabloon: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - sien docker-compose-nginx.yml-kode)

    - 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

Op die ontwikkelingsrekenaars, werk /etc/hosts; skryf url vir nginx voor:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Dus, die ontplooiing van geïsoleerde opstelklusters is geïmplementeer en ontwikkelaars kan dit nou in enige getal uitvoer wat genoeg is om hul take na te gaan.

Toekomsplanne:

  • Skei ons komponente as dienste
  • Het vir elke Dockerfile
  • Bespeur outomaties minder gelaaide nodusse in die stapel
  • Spesifiseer nodusse volgens naampatroon (eerder as om ID te gebruik soos in die artikel)
  • Voeg 'n tjek by dat die stapel vernietig is
  • ...

Spesiale dankie vir 'n artikel.

Bron: will.com

Voeg 'n opmerking