Razmestite aplikacije z Docker Swarm

Sistem za priporočanje spletnih video vsebin, na katerem delamo, je zaprt komercialni razvoj in je tehnično večkomponentna skupina lastniških in odprtokodnih komponent. Namen pisanja tega članka je opisati implementacijo sistema združevanja rojev dockerjev za uprizoritveno mesto, ne da bi v omejenem času motili ustaljeni potek dela naših procesov. Predstavljena pripoved je razdeljena na dva dela. Prvi del opisuje CI/CD pred uporabo docker swarma, drugi del pa opisuje postopek njegove implementacije. Tisti, ki jih prvi del ne zanima, lahko mirno preidejo na drugega.

I. del

Nekoč je bilo treba čim hitreje vzpostaviti proces CI/CD. Eden od pogojev je bil neuporaba Dockerja za uvajanje razvil komponente iz več razlogov:

  • za bolj zanesljivo in stabilno delovanje komponent v produkciji (to je pravzaprav zahteva, da se ne uporablja virtualizacija)
  • vodilni razvijalci niso želeli delati z Dockerjem (čudno, ampak tako je bilo)
  • glede na ideološke premisleke vodstva R&R

Infrastruktura, sklad in približne začetne zahteve za MVP so bile predstavljene na naslednji način:

  • 4 strežniki Intel® X5650 z Debianom (en zmogljivejši stroj je v celoti razvit)
  • Razvoj lastnih komponent po meri poteka v C ++, Python3
  • Glavna uporabljena orodja tretjih oseb: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • Cevovodi za izdelavo in testiranje komponent ločeno za odpravljanje napak in izdajo

Eno od prvih vprašanj, ki jih je treba obravnavati v začetni fazi, je, kako bodo komponente po meri nameščene v katerem koli okolju (CI / CD).

Odločili smo se za sistemsko namestitev komponent tretjih oseb in njihovo sistemsko posodobitev. Aplikacije po meri, razvite v C++ ali Python, je mogoče namestiti na več načinov. Med njimi na primer: ustvarjanje sistemskih paketov, pošiljanje v repozitorij zgrajenih slik in nato namestitev na strežnike. Iz neznanega razloga je bila izbrana druga metoda, in sicer: z uporabo CI se prevedejo izvršljive datoteke aplikacije, ustvari navidezno projektno okolje, namestijo se moduli py iz requirements.txt in vsi ti artefakti se pošljejo skupaj s konfiguracijami, skripti in spremljevalno aplikacijsko okolje do strežnikov. Nato se aplikacije zaženejo kot navidezni uporabnik brez skrbniških pravic.

Za sistem CI/CD je bil izbran Gitlab-CI. Nastali cevovod je izgledal nekako takole:

Razmestite aplikacije z Docker Swarm
Strukturno je bil gitlab-ci.yml videti takole:

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

Omeniti velja, da montaža in testiranje poteka na lastni podobi, kjer so že nameščeni vsi potrebni sistemski paketi in opravljene druge nastavitve.

Čeprav je vsaka od teh skript na delovnem mestu zanimiva na svoj način, seveda ne bom govoril o njih, saj bo opis vsakega od njih vzel veliko časa in to ni namen članka. Opozoril bom le na dejstvo, da je stopnja uvajanja sestavljena iz zaporedja klicnih skriptov:

  1. createconfig.py - ustvari datoteko settings.ini z nastavitvami komponent v različnih okoljih za kasnejšo uvedbo (predprodukcija, produkcija, testiranje, ...)
  2. install_venv.sh - ustvari virtualno okolje za komponente py v določenem imeniku in ga kopira na oddaljene strežnike
  3. pripravi_init.d.py — pripravi start-stop skripte za komponento na podlagi predloge
  4. deploy.py - razgradi in ponovno zažene nove komponente

Čas je minil. Fazo uprizoritve sta nadomestili predprodukcija in produkcija. Dodana podpora za izdelek v še eni distribuciji (CentOS). Dodanih je 5 zmogljivejših fizičnih strežnikov in ducat virtualnih. Razvijalcem in preizkuševalcem je postajalo vse težje preizkušati svoje naloge v okolju, ki je bolj ali manj blizu delovnemu stanju. Takrat je postalo jasno, da brez njega ni mogoče ...

XNUMX. del

Razmestite aplikacije z Docker Swarm

Naša gruča je torej spektakularen sistem nekaj ducatov ločenih komponent, ki jih Dockerfiles ne opisuje. Na splošno ga lahko konfigurirate le za uvajanje v določeno okolje. Naša naloga je razmestiti gručo v uprizoritveno okolje, da jo preizkusimo pred testiranjem pred izdajo.

Teoretično lahko hkrati deluje več gruč: toliko, kolikor je nalog v stanju dokončanja ali blizu dokončanja. Zmogljivosti strežnikov, s katerimi razpolagamo, nam omogočajo, da na posameznem strežniku izvajamo več gruč. Vsaka uprizoritvena gruča mora biti izolirana (ne sme biti presečišč v vratih, imenikih itd.).

Naš najdragocenejši vir je naš čas in nismo ga imeli veliko.

Za hitrejši začetek smo izbrali Docker Swarm zaradi njegove enostavnosti in prilagodljive arhitekture. Najprej smo ustvarili upravitelja in več vozlišč na oddaljenih strežnikih:

$ 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

Nato ustvarite omrežje:


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

Nato smo povezali vozlišča Gitlab-CI in Swarm v smislu oddaljenega nadzora vozlišč iz CI: namestitev certifikatov, nastavitev tajnih spremenljivk in nastavitev storitve Docker na nadzornem strežniku. Tale članek nam je prihranilo veliko časa.

Nato smo v .gitlab-ci .yml dodali opravila ustvarjanja in uničenja skladov.

V .gitlab-ci .yml je bilo dodanih še nekaj delovnih mest

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

Iz zgornjega delčka kode lahko vidite, da sta bila v Cevovodi dodana dva gumba (deploy_staging, stop_staging), ki zahtevata ročno ukrepanje.

Razmestite aplikacije z Docker Swarm
Ime sklada se ujema z imenom veje in ta edinstvenost bi morala zadostovati. Storitve v skladu prejmejo edinstvene naslove IP, vrata, imenike itd. bo izoliran, vendar enak od sklada do sklada (ker je konfiguracijska datoteka enaka za vse sklade) - kar smo želeli. Sklad (gručo) razmestimo z uporabo docker-compose.yml, ki opisuje naš grozd.

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

Tukaj lahko vidite, da so komponente povezane z enim omrežjem (nw_swarm) in so na voljo ena drugi.

Sistemske komponente (temelji na redis, mysql) so ločene od splošnega nabora komponent po meri (v načrtih in tiste po meri so razdeljene kot storitve). Stopnja uvajanja naše gruče je videti kot prenos CMD v našo eno veliko konfigurirano sliko in se na splošno praktično ne razlikuje od uvajanja, opisanega v I. delu. Poudaril bom razlike:

  • git klon... - pridobite datoteke, potrebne za namestitev (createconfig.py, install_venv.sh itd.)
  • zvijaj ... && odpakiraj ... — prenesite in razpakirajte artefakte gradnje (prevedeni pripomočki)

Obstaja le ena še neopisana težava: komponente, ki imajo spletni vmesnik, niso dostopne iz brskalnikov razvijalcev. To težavo rešimo z uporabo povratnega proxyja, torej:

V .gitlab-ci.yml po razmestitvi sklada gruč dodamo vrstico za razmestitev izravnalnika (ki ob objavi samo posodobi svojo konfiguracijo (ustvari nove konfiguracijske datoteke nginx v skladu s predlogo: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) – glejte kodo 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 razvojnih računalnikih posodobite /etc/hosts; predpiši url za nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Tako je bila implementirana uvedba izoliranih uprizoritvenih gruč in razvijalci jih lahko zdaj zaženejo v poljubni količini, ki zadostuje za testiranje njihovih nalog.

Načrti za prihodnost:

  • Ločite naše komponente kot storitve
  • Za vsako Dockerfile
  • Samodejno zazna manj obremenjena vozlišča v skladu
  • Določite vozlišča po vzorcu imena (namesto uporabe ID-ja kot v članku)
  • Dodajte preverjanje, da je sklad uničen
  • ...

Posebna zahvala za статью.

Vir: www.habr.com

Dodaj komentar