Implementirajte aplikacije s Docker Swarmom

Sustav za preporuku online video sadržaja na kojem radimo je zatvoreni komercijalni razvoj i tehnički je višekomponentni klaster vlasničkih komponenti i komponenti otvorenog koda. Svrha pisanja ovog članka je opisati implementaciju sustava grupiranja docker swarm za početno mjesto bez ometanja uspostavljenog tijeka rada naših procesa u ograničenom vremenu. Pripovijest koja je predstavljena vašoj pozornosti podijeljena je u dva dijela. Prvi dio opisuje CI/CD prije korištenja docker swarma, a drugi opisuje proces njegove implementacije. Oni koji nisu zainteresirani za čitanje prvog dijela mogu sigurno prijeći na drugi.

I dio

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

  • za pouzdaniji i stabilniji rad komponenti u Produkciji (to je, zapravo, zahtjev da se ne koristi virtualizacija)
  • vodeći programeri nisu htjeli raditi s Dockerom (čudno, ali tako je bilo)
  • prema ideološkim promišljanjima menadžmenta istraživanja i razvoja

Infrastruktura, stog i približni početni zahtjevi za MVP predstavljeni su kako slijedi:

  • 4 Intel® X5650 poslužitelja s Debianom (jedan moćniji stroj je potpuno razvijen)
  • Razvoj vlastitih prilagođenih komponenti provodi se u C++, Python3
  • Glavni korišteni alati treće strane: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • Cjevovodi za izgradnju i testiranje komponenti zasebno za otklanjanje pogrešaka i izdavanje

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

Odlučili smo sistemski instalirati komponente trećih strana i sistemski ih ažurirati. Prilagođene aplikacije razvijene u C++ ili Pythonu mogu se implementirati na nekoliko načina. Među njima, na primjer: kreiranje sistemskih paketa, njihovo slanje u repozitorij izgrađenih slika i zatim njihovo instaliranje na poslužitelje. Iz nepoznatog razloga odabrana je druga metoda, naime: pomoću CI-ja kompajliraju se izvršne datoteke aplikacije, stvara se virtualno projektno okruženje, py moduli se instaliraju iz requirements.txt, a svi ti artefakti šalju se zajedno s konfiguracijama, skriptama i prateće aplikacijsko okruženje poslužiteljima. Zatim se aplikacije pokreću kao virtualni korisnik bez administratorskih prava.

Kao CI/CD sustav odabran je Gitlab-CI. Rezultirajući cjevovod izgledao je otprilike ovako:

Implementirajte aplikacije s Docker Swarmom
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

Vrijedno je napomenuti da se montaža i testiranje provodi na vlastitoj slici, gdje su već instalirani svi potrebni sistemski paketi i napravljene druge postavke.

Iako je svaka od ovih skripti u poslovima zanimljiva na svoj način, ali naravno neću govoriti o njima, opis svake od njih će oduzeti dosta vremena i to nije svrha članka. Samo ću vam skrenuti pozornost na činjenicu da se faza implementacije sastoji od slijeda poziva skripti:

  1. createconfig.py - stvara datoteku settings.ini s postavkama komponenti u različitim okruženjima za naknadnu implementaciju (Pretprodukcija, Proizvodnja, Testiranje, ...)
  2. install_venv.sh - stvara virtualno okruženje za py komponente u određenom direktoriju i kopira ga na udaljene poslužitelje
  3. pripremiti_init.d.py — priprema start-stop skripte za komponentu na temelju predloška
  4. implementirati.py - razgrađuje i ponovno pokreće nove komponente

Vrijeme je prolazilo. Uprizorenje je zamijenjeno pretprodukcijom i produkcijom. Dodana podrška za proizvod na još jednoj distribuciji (CentOS). Dodano 5 snažnijih fizičkih poslužitelja i desetak virtualnih. A razvojnim programerima i testerima postajalo je sve teže testirati svoje zadatke u okruženju manje-više bliskom radnom stanju. U to vrijeme postalo je jasno da je nemoguće bez njega ...

II dio

Implementirajte aplikacije s Docker Swarmom

Dakle, naš klaster je spektakularan sustav od nekoliko desetaka zasebnih komponenti koje Dockerfiles ne opisuje. Možete ga općenito konfigurirati samo za implementaciju u određeno okruženje. Naš je zadatak implementirati klaster u probno okruženje kako bismo ga testirali prije testiranja prije izdavanja.

Teoretski, može postojati nekoliko klastera koji rade istovremeno: onoliko koliko ima zadataka u dovršenom stanju ili blizu dovršetka. Kapaciteti poslužitelja kojima raspolažemo omogućuju nam pokretanje nekoliko klastera na svakom poslužitelju. Svaki pripremni klaster mora biti izoliran (ne smije postojati križanje u portovima, direktorijima itd.).

Naš najvrjedniji 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. Prvo što smo napravili bilo je stvaranje upravitelja i nekoliko čvorova na udaljenim poslužiteljima:

$ 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 izradite 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-ja: instaliranje certifikata, postavljanje tajnih varijabli i postavljanje Docker servisa na kontrolnom poslužitelju. Ovaj članak uštedio nam je puno vremena.

Zatim smo dodali poslove stvaranja i uništavanja hrpa u .gitlab-ci .yml.

Dodano je još nekoliko poslova 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 gumba (deploy_staging, stop_staging) dodana u Pipelines, što zahtijeva ručnu radnju.

Implementirajte aplikacije s Docker Swarmom
Naziv steka odgovara nazivu grane i ta bi jedinstvenost trebala biti dovoljna. Usluge u stogu primaju jedinstvene IP adrese, portove, direktorije itd. će biti izoliran, ali isti od stog do stog (jer je konfiguracijska datoteka ista za sve hrpe) - ono što smo htjeli. Raspoređujemo stog (klaster) pomoću doker-compose.yml, koji opisuje naš klaster.

doker-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 da su jedna drugoj dostupne.

Komponente sustava (temeljene na redis, mysql) odvojene su od općeg skupa prilagođenih komponenti (u planovima i prilagođene su podijeljene kao usluge). Faza implementacije našeg klastera izgleda kao prosljeđivanje CMD-a u našu jednu veliku konfiguriranu sliku i općenito se praktički ne razlikuje od implementacije opisane u I. dijelu. Naglasit ću razlike:

  • git klon... - dobiti datoteke potrebne za implementaciju (createconfig.py, install_venv.sh, itd.)
  • uviti... && raspakirati... - preuzimanje i raspakiranje artefakata izrade (kompilirani uslužni programi)

Postoji samo jedan još neopisani problem: komponente koje imaju web sučelje nisu dostupne iz preglednika programera. Ovaj problem rješavamo pomoću obrnutog proxyja, dakle:

U .gitlab-ci.yml, nakon postavljanja hrpe klastera, dodajemo redak postavljanja balansera (koji, kada se predaje, samo ažurira svoju konfiguraciju (stvara nove nginx konfiguracijske datoteke prema predlošku: /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čunalima ažurirajte /etc/hosts; propiši url za nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Dakle, implementacija izoliranih probnih klastera je implementirana i programeri ih sada mogu pokrenuti u bilo kojem broju dovoljnom za provjeru njihovih zadataka.

Planovi za buducnost:

  • Odvojite naše komponente kao usluge
  • Imati za svaku Dockerfile
  • Automatski otkrij manje opterećene čvorove u stogu
  • Navedite čvorove uzorkom naziva (umjesto korištenja ID-a kao u članku)
  • Dodajte provjeru da je stog uništen
  • ...

Posebna zahvala za članak.

Izvor: www.habr.com

Dodajte komentar