Postavite aplikacije uz Docker Swarm

Sistem za preporuku video sadržaja na mreži na kojem radimo je zatvorena komercijalna izrada i tehnički je višekomponentni klaster vlasničkih i komponenti otvorenog koda. Svrha pisanja ovog članka je da opiše implementaciju sistema docker swarm klasteriranja za mjesto za postavljanje bez narušavanja uspostavljenog toka rada naših procesa u ograničenom vremenu. Narativ koji vam je predstavljen podijeljen je na dva dijela. Prvi dio opisuje CI/CD prije korištenja docker roja, a drugi opisuje proces njegove implementacije. Oni koji nisu zainteresovani za čitanje prvog dela mogu sa sigurnošću da pređu na drugi.

I deo

Još daleke, daleke godine, bilo je potrebno što brže postaviti CI/CD proces. Jedan od uslova je bio da se ne koristi Docker za raspoređivanje razvijene komponente iz nekoliko razloga:

  • za pouzdaniji i stabilniji rad komponenti u proizvodnji (odnosno, u stvari, uslov da se ne koristi virtuelizacija)
  • vodeći programeri nisu hteli da rade sa Dockerom (čudno, ali tako je bilo)
  • prema ideološkim promišljanjima menadžmenta istraživanja i razvoja

Infrastruktura, stek i približni početni zahtjevi za MVP predstavljeni su na sljedeći način:

  • 4 Intel® X5650 servera s Debianom (jedan snažniji stroj je u potpunosti razvijen)
  • Razvoj sopstvenih prilagođenih komponenti se vrši u C++, Python3
  • Glavni korišteni alati treće strane: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql,…
  • Cjevovodi za izgradnju i testiranje komponenti odvojeno za otklanjanje grešaka i oslobađanje

Jedno od prvih pitanja koje treba riješiti u početnoj fazi je kako će prilagođene komponente biti raspoređene u bilo kojem okruženju (CI/CD).

Odlučili smo da sistemski instaliramo komponente treće strane i da ih sistemski ažuriramo. Prilagođene aplikacije razvijene u C++ ili Python-u mogu se implementirati na nekoliko načina. Među njima, na primjer: kreiranje sistemskih paketa, njihovo slanje u spremište izgrađenih slika i zatim njihovo instaliranje na servere. Iz nepoznatog razloga odabran je drugi metod, a to je: pomoću CI-a se kompajliraju izvršni fajlovi aplikacije, kreira se virtuelno projektno okruženje, instaliraju se py moduli iz requirements.txt, a svi ovi artefakti se šalju zajedno sa konfiguracijama, skriptama i prateće okruženje aplikacije na servere. Zatim se aplikacije pokreću kao virtuelni korisnik bez administratorskih prava.

Gitlab-CI je izabran kao CI/CD sistem. Rezultirajući cevovod je izgledao otprilike ovako:

Postavite aplikacije uz Docker Swarm
Strukturno, gitlab-ci.yml je izgledao ovako

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

Vrijedi napomenuti da se sklapanje i testiranje vrši na vlastitoj slici, gdje su svi potrebni sistemski paketi već instalirani i druga podešavanja su napravljena.

Iako je svaka od ovih skripti u poslovima zanimljiva na svoj način, naravno da o njima neću govoriti, opis svakog od njih će oduzeti dosta vremena i to nije svrha članka. Samo ću vam skrenuti pažnju na činjenicu da se faza implementacije sastoji od niza skripti za pozivanje:

  1. createconfig.py - kreira datoteku settings.ini sa postavkama komponenti u različitim okruženjima za naknadnu implementaciju (preprodukcija, proizvodnja, testiranje,...)
  2. install_venv.sh - kreira virtuelno okruženje za py komponente u određenom direktorijumu i kopira ga na udaljene servere
  3. pripremi_init.d.py — priprema start-stop skripte za komponentu na osnovu šablona
  4. deploy.py - razgrađuje i ponovo pokreće nove komponente

Vrijeme je prolazilo. Scenska faza je zamijenjena predprodukcijom i produkcijom. Dodata podrška za proizvod na još jednoj distribuciji (CentOS). Dodano 5 moćnijih fizičkih servera i desetak virtualnih. A programerima i testerima je postajalo sve teže da testiraju svoje zadatke u okruženju manje ili više bliskom radnom stanju. Tada je postalo jasno da je nemoguće bez njega ...

II deo

Postavite aplikacije uz Docker Swarm

Dakle, naš klaster je spektakularan sistem od nekoliko desetina odvojenih komponenti koje nisu opisane u Dockerfiles-u. Možete ga konfigurirati samo za primenu u određenom okruženju općenito. Naš zadatak je da postavimo klaster u scensko okruženje kako bismo ga testirali prije testiranja prije izdanja.

Teoretski, može postojati nekoliko klastera koji rade istovremeno: onoliko koliko ima zadataka u završenom stanju ili blizu završetka. Kapaciteti servera kojima raspolažemo omogućavaju nam da pokrenemo nekoliko klastera na svakom serveru. Svaki staging klaster mora biti izoliran (ne smije biti ukrštanja portova, direktorija, itd.).

Naš najvredniji resurs je naše vrijeme, a nismo ga imali puno.

Za brži početak, odabrali smo Docker Swarm zbog njegove jednostavnosti i fleksibilnosti arhitekture. Prva stvar koju smo uradili je kreiranje menadžera i nekoliko čvorova na udaljenim serverima:

$ 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

Zatim kreirajte mrežu:


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

Zatim smo povezali Gitlab-CI i Swarm čvorove u smislu daljinske kontrole čvorova iz CI: instaliranje certifikata, postavljanje tajnih varijabli i postavljanje Docker servisa na kontrolnom serveru. Ovaj članak uštedeli smo mnogo vremena.

Zatim smo dodali poslove kreiranja i uništavanja stekova u .gitlab-ci .yml.

Još nekoliko poslova je dodano u .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

Iz gornjeg isječka koda, možete vidjeti da su dva dugmeta (deploy_staging, stop_staging) dodana u cjevovode, što zahtijeva ručnu akciju.

Postavite aplikacije uz Docker Swarm
Ime steka odgovara imenu grane i ova jedinstvenost bi trebala biti dovoljna. Usluge u steku dobijaju jedinstvene IP adrese, portove, direktorijume itd. biće izolovano, ali isto od steka do steka (jer je konfiguracioni fajl isti za sve stekove) - ono što smo želeli. Mi postavljamo stek (klaster) koristeći docker-compose.yml, koji opisuje naš klaster.

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

Ovdje možete vidjeti da su komponente povezane jednom mrežom (nw_swarm) i dostupne su jedna drugoj.

Sistemske komponente (bazirane na redis, mysql) su odvojene od opšteg skupa prilagođenih komponenti (u planovima i prilagođene su podeljene kao usluge). Faza implementacije našeg klastera izgleda kao prenošenje CMD-a u našu jednu veliku konfiguriranu sliku i, općenito gledano, praktički se ne razlikuje od implementacije opisane u dijelu I. Istaknut ću razlike:

  • git klon... - nabavite datoteke potrebne za implementaciju (createconfig.py, install_venv.sh, itd.)
  • uviti... && otkopčati... - preuzmite i raspakujte artefakte izgradnje (kompajlirane uslužne programe)

Postoji samo jedan još neopisan problem: komponente koje imaju web interfejs nisu dostupne iz pretraživača programera. Ovaj problem rješavamo korištenjem obrnutog proxyja, na sljedeći način:

U .gitlab-ci.yml, nakon postavljanja steka klastera, dodajemo liniju za postavljanje balansera (koji, kada se urezuje, samo ažurira svoju konfiguraciju (kreira nove nginx konfiguracijske datoteke prema šablonu: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - pogledajte 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

Na razvojnim računarima, ažurirajte /etc/hosts; prepisati url za nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Dakle, implementacija izolovanih scenskih klastera je implementirana i programeri ih sada mogu pokrenuti u bilo kojem broju dovoljnom da provjere svoje zadatke.

Budući planovi:

  • Odvojite naše komponente kao usluge
  • Imati za svaki Dockerfile
  • Automatski otkrijte manje opterećene čvorove u stogu
  • Odredite čvorove prema uzorku imena (umjesto da koristite id kao u članku)
  • Dodajte provjeru da je stek uništen
  • ...

Posebno hvala za članak.

izvor: www.habr.com

Dodajte komentar