Nasaďte aplikácie pomocou Docker Swarm

Online systém odporúčaní videoobsahu, na ktorom pracujeme, je uzavretý komerčný vývoj a je technicky viaczložkovým klastrom proprietárnych a open source komponentov. Účelom napísania tohto článku je opísať implementáciu systému klastrovania docker swarm pre pracovisko bez narušenia zavedeného pracovného toku našich procesov v obmedzenom čase. Rozprávanie, ktoré vám je predložené, je rozdelené na dve časti. Prvá časť popisuje CI/CD pred použitím docker swarm a druhá popisuje proces jeho implementácie. Tí, ktorí nemajú záujem čítať prvú časť, môžu pokojne prejsť na druhú.

Časť I

Späť vo vzdialenom, vzdialenom roku bolo potrebné nastaviť proces CI / CD čo najrýchlejšie. Jednou z podmienok bolo nepoužívať Docker na nasadenie vyvinuté komponenty z niekoľkých dôvodov:

  • pre spoľahlivejšiu a stabilnejšiu prevádzku komponentov vo výrobe (teda v skutočnosti požiadavka nepoužívať virtualizáciu)
  • poprední vývojári nechceli spolupracovať s Dockerom (čudné, ale bolo to tak)
  • podľa ideových úvah vedenia výskumu a vývoja

Infraštruktúra, zásobník a približné počiatočné požiadavky na MVP boli prezentované takto:

  • 4 servery Intel® X5650 s Debianom (jeden výkonnejší počítač je plne vyvinutý)
  • Vývoj vlastných vlastných komponentov prebieha v C++, Python3
  • Hlavné používané nástroje tretích strán: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • Potrubia na vytváranie a testovanie komponentov oddelene na ladenie a uvoľnenie

Jednou z prvých otázok, ktoré je potrebné vyriešiť v počiatočnej fáze, je spôsob nasadenia vlastných komponentov v akomkoľvek prostredí (CI / CD).

Rozhodli sme sa systémovo nainštalovať komponenty tretích strán a systémovo ich aktualizovať. Vlastné aplikácie vyvinuté v C++ alebo Pythone je možné nasadiť niekoľkými spôsobmi. Medzi nimi napríklad: vytváranie systémových balíkov, ich odosielanie do úložiska vytvorených obrazov a ich inštalácia na servery. Z neznámeho dôvodu bola zvolená iná metóda, a to: pomocou CI sa skompilujú spustiteľné súbory aplikácie, vytvorí sa prostredie virtuálneho projektu, nainštalujú sa py moduly z requirements.txt a všetky tieto artefakty sa odošlú spolu s konfiguráciami, skriptami a sprievodné aplikačné prostredie k serverom. Ďalej sa aplikácie spúšťajú ako virtuálny používateľ bez administrátorských práv.

Ako CI/CD systém bol vybraný Gitlab-CI. Výsledné potrubie vyzeralo asi takto:

Nasaďte aplikácie pomocou Docker Swarm
Štrukturálne vyzeral gitlab-ci.yml takto

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

Stojí za zmienku, že zostava a testovanie sa vykonáva na vlastnom obrázku, kde už boli nainštalované všetky potrebné systémové balíčky a boli vykonané ďalšie nastavenia.

Hoci každý z týchto skriptov v úlohách je zaujímavý svojím vlastným spôsobom, ale o nich samozrejme nebudem hovoriť. Popis každého z nich zaberie veľa času a to nie je účelom článku. Len upriamim vašu pozornosť na skutočnosť, že fáza nasadenia pozostáva zo sekvencie volania skriptov:

  1. createconfig.py - vytvorí súbor settings.ini s nastaveniami komponentov v rôznych prostrediach pre následné nasadenie (Preproduction, Production, Testing, ...)
  2. install_venv.sh - vytvorí virtuálne prostredie pre py komponenty v konkrétnom adresári a skopíruje ho na vzdialené servery
  3. pripraviť_init.d.py — pripraví štart-stop skripty pre komponent na základe šablóny
  4. deploy.py - rozkladá a reštartuje nové komponenty

Čas uplynul. Etapu inscenácie nahradila predprodukcia a produkcia. Pridaná podpora pre produkt v jednej ďalšej distribúcii (CentOS). Pridaných 5 výkonnejších fyzických serverov a tucet virtuálnych. A pre vývojárov a testerov bolo čoraz ťažšie otestovať svoje úlohy v prostredí viac-menej blízkom pracovnému stavu. V tom čase sa ukázalo, že bez neho to nie je možné ...

Časť II

Nasaďte aplikácie pomocou Docker Swarm

Náš klaster je teda veľkolepý systém niekoľkých desiatok samostatných komponentov, ktoré Dockerfiles nepopisuje. Môžete ho nakonfigurovať len na nasadenie do konkrétneho prostredia vo všeobecnosti. Našou úlohou je nasadiť klaster do prípravného prostredia, aby sme ho otestovali pred testovaním pred vydaním.

Teoreticky môže súčasne bežať niekoľko klastrov: toľko, koľko je úloh v stave dokončenia alebo takmer dokončenia. Kapacity serverov, ktoré máme k dispozícii, nám umožňujú prevádzkovať niekoľko klastrov na každom serveri. Každý stagingový klaster musí byť izolovaný (nesmie existovať priesečník v portoch, adresároch atď.).

Naším najcennejším zdrojom je náš čas a toho sme nemali veľa.

Pre rýchlejší štart sme si vybrali Docker Swarm kvôli jeho jednoduchosti a flexibilite architektúry. Prvá vec, ktorú sme urobili, bolo vytvorenie manažéra a niekoľkých uzlov na vzdialených serveroch:

$ 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

Ďalej vytvorte sieť:


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

Ďalej sme prepojili uzly Gitlab-CI a Swarm z hľadiska vzdialeného ovládania uzlov z CI: inštalácia certifikátov, nastavenie tajných premenných a nastavenie služby Docker na riadiacom serveri. Toto článok nám ušetril veľa času.

Ďalej sme do súboru .gitlab-ci .yml pridali úlohy na vytváranie a ničenie zásobníkov.

Do súboru .gitlab-ci .yml pribudlo niekoľko ďalších úloh

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

Z útržku kódu vyššie môžete vidieť, že do Pipelines boli pridané dve tlačidlá (deploy_staging, stop_staging), ktoré si vyžadujú manuálnu akciu.

Nasaďte aplikácie pomocou Docker Swarm
Názov zásobníka sa zhoduje s názvom pobočky a táto jedinečnosť by mala byť dostatočná. Služby v zásobníku dostávajú jedinečné adresy IP a porty, adresáre atď. bude izolovaný, ale rovnaký od zásobníka k zásobníku (pretože konfiguračný súbor je rovnaký pre všetky zásobníky) - čo sme chceli. Zásobník (klaster) nasadíme pomocou prístavný robotník-compose.yml, ktorý popisuje náš klaster.

prístavný robotník-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

Tu vidíte, že komponenty sú prepojené jednou sieťou (nw_swarm) a sú si navzájom dostupné.

Systémové komponenty (založené na redis, mysql) sú oddelené od všeobecného fondu vlastných komponentov (v plánoch sú vlastné komponenty rozdelené ako služby). Fáza nasadenia nášho klastra vyzerá ako odovzdanie CMD do nášho jedného veľkého nakonfigurovaného obrazu a vo všeobecnosti sa prakticky nelíši od nasadenia opísaného v časti I. Zdôrazním rozdiely:

  • git klon... - získajte súbory potrebné na nasadenie (createconfig.py, install_venv.sh atď.)
  • zvlniť... && rozopnúť... - stiahnite a rozbaľte artefakty zostavy (kompilované pomôcky)

Existuje len jeden zatiaľ nepopísaný problém: komponenty, ktoré majú webové rozhranie, nie sú prístupné z prehliadačov vývojárov. Tento problém riešime pomocou reverzného proxy, teda:

V .gitlab-ci.yml po nasadení zásobníka klastrov pridáme riadok nasadenia balancera (ktorý pri potvrdení iba aktualizuje svoju konfiguráciu (vytvorí nové konfiguračné súbory nginx podľa šablóny: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) – pozri kód 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

Na vývojových počítačoch aktualizujte súbor /etc/hosts; predpísať url pre nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Takže nasadenie izolovaných fázovacích klastrov bolo implementované a vývojári ich teraz môžu spustiť v ľubovoľnom počte dostatočnom na kontrolu ich úloh.

Budúce plány:

  • Oddeľte naše komponenty ako služby
  • Majte pre každý Dockerfile
  • Automaticky zisťuje menej zaťažené uzly v zásobníku
  • Zadajte uzly podľa vzoru názvu (namiesto použitia id ako v článku)
  • Pridajte kontrolu, či je zásobník zničený
  • ...

Špeciálne poďakovanie za статью.

Zdroj: hab.com

Pridať komentár