Deploji Aplikojn kun Docker Swarm

La interreta videoenhava rekomendsistemo pri kiu ni laboras estas fermita komerca evoluo kaj estas teknike plurkomponenta aro de proprietaj kaj malfermfontaj komponentoj. La celo de skribi ĉi tiun artikolon estas priskribi la efektivigon de la docker-svarma clustering-sistemo por sursceniga retejo sen interrompi la establitan laborfluon de niaj procezoj en limigita tempo. La rakonto prezentita al via atento estas dividita en du partojn. La unua parto priskribas CI / KD antaŭ uzi docker-svarmon, kaj la dua priskribas la procezon de ĝia efektivigo. Tiuj, kiuj ne interesiĝas legi la unuan parton, povas sekure transiri al la dua.

Parto I

Reen en la malproksima, malproksima jaro, necesis starigi la CI/KD-procezon kiel eble plej rapide. Unu el la kondiĉoj estis ne uzi Docker por deplojo evoluigitaj komponantoj pro pluraj kialoj:

  • por pli fidinda kaj stabila funkciado de komponantoj en Produktado (tio estas, fakte, la postulo ne uzi virtualigon)
  • ĉefaj programistoj ne volis labori kun Docker (stranga, sed tiel estis)
  • laŭ la ideologiaj konsideroj de la R&D-administrado

Infrastrukturo, stako kaj proksimumaj komencaj postuloj por MVP estis prezentitaj jene:

  • 4 Intel® X5650-serviloj kun Debian (unu plia potenca maŝino estas plene evoluinta)
  • Disvolviĝo de propraj kutimaj komponantoj estas farita en C++, Python3
  • Ĉefaj triaj iloj uzataj: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, ...
  • Duktoj por konstruado kaj testado de komponantoj aparte por sencimigo kaj liberigo

Unu el la unuaj demandoj, kiujn oni devas trakti en la komenca etapo, estas kiel kutimaj komponantoj estos deplojitaj en iu ajn medio (CI / KD).

Ni decidis sisteme instali triajn komponantojn kaj ĝisdatigi ilin sisteme. Propraj aplikoj evoluigitaj en C++ aŭ Python povas esti deplojitaj laŭ pluraj manieroj. Inter ili, ekzemple: krei sistemajn pakaĵojn, sendi ilin al la deponejo de konstruitaj bildoj kaj poste instali ilin sur serviloj. Pro nekonata kialo, alia metodo estis elektita, nome: uzante CI, aplikaĵaj ruleblaj dosieroj estas kompilitaj, virtuala projekta medio estas kreita, py-moduloj estas instalitaj de requirements.txt, kaj ĉiuj ĉi tiuj artefaktoj estas senditaj kune kun agordoj, skriptoj kaj la akompananta aplikaĵa medio al serviloj. Poste, aplikaĵoj estas lanĉitaj kiel virtuala uzanto sen administrantorajtoj.

Gitlab-CI estis elektita kiel la CI/CD-sistemo. La rezulta dukto aspektis kiel ĉi tio:

Deploji Aplikojn kun Docker Swarm
Strukture, gitlab-ci.yml aspektis tiel

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

Indas noti, ke la muntado kaj testado estas faritaj sur sia propra bildo, kie ĉiuj necesaj sistemaj pakoj jam estis instalitaj kaj aliaj agordoj estis faritaj.

Kvankam ĉiu el ĉi tiuj skriptoj en laboroj estas interesa siamaniere, sed kompreneble mi ne parolos pri ili.La priskribo de ĉiu el ili prenos multe da tempo kaj ĉi tio ne estas la celo de la artikolo. Mi nur atentigos vian atenton pri tio, ke la deploja stadio konsistas el sinsekvo de vokaj skriptoj:

  1. createconfig.py - kreas dosieron settings.ini kun komponaj agordoj en diversaj medioj por posta disfaldo (Preproduktado, Produktado, Testado, ...)
  2. instal_venv.sh - kreas virtualan medion por py-komponentoj en specifa dosierujo kaj kopias ĝin al foraj serviloj
  3. prepari_init.d.py — preparas start-haltajn skriptojn por la komponanto surbaze de la ŝablono
  4. deploy.py - malkomponiĝas kaj rekomencas novajn komponantojn

La tempo pasis. La ensceniga stadio estis anstataŭigita per antaŭproduktado kaj produktado. Aldonita subteno por la produkto sur unu plia distribuo (CentOS). Aldonitaj 5 pli potencaj fizikaj serviloj kaj dekduo da virtualaj. Kaj fariĝis pli kaj pli malfacile por programistoj kaj testantoj testi siajn taskojn en medio pli-malpli proksima al la laborstato. En ĉi tiu tempo, evidentiĝis, ke estis neeble fari sen li ...

Parto II

Deploji Aplikojn kun Docker Swarm

Do, nia areto estas sensacia sistemo de kelkaj dekduoj da apartaj komponantoj, kiuj ne estas priskribitaj de Dockerfiles. Vi povas nur agordi ĝin por deplojo al specifa medio ĝenerale. Nia tasko estas deploji la areton en aranĝan medion por testi ĝin antaŭ antaŭ-eldono.

Teorie, povas ekzisti pluraj aretoj kurantaj samtempe: tiom da kiom estas taskoj en la finita stato aŭ proksimaj al kompletigo. La kapabloj de la serviloj je nia dispono permesas al ni ruli plurajn aretojn sur ĉiu servilo. Ĉiu sursceniga areto devas esti izolita (ne devas esti intersekciĝo en havenoj, dosierujoj, ktp.).

Nia plej valora rimedo estas nia tempo, kaj ni ne havis multe da ĝi.

Por pli rapida komenco, ni elektis Docker Swarm pro ĝia simpleco kaj arkitekturo-fleksebleco. La unua afero, kiun ni faris, estis krei administranton kaj plurajn nodojn sur la foraj serviloj:

$ 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

Poste, kreu reton:


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

Poste, ni konektis Gitlab-CI kaj Swarm-nodojn laŭ fora kontrolo de nodoj de CI: instali atestojn, agordi sekretajn variablojn kaj agordi la Docker-servon sur la kontrolservilo. Ĉi tiun artikolo ŝparis al ni multe da tempo.

Poste, ni aldonis stakajn kreajn kaj detruajn laborojn al .gitlab-ci .yml.

Kelkaj pliaj laboroj estis aldonitaj al .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

El la ĉi-supra koda fragmento, vi povas vidi, ke du butonoj (deploy_staging, stop_staging) estis aldonitaj al Pipelines, postulante manan agadon.

Deploji Aplikojn kun Docker Swarm
La staknomo kongruas kun la branĉonomo kaj ĉi tiu unikeco devus esti sufiĉa. Servoj en la stako ricevas unikajn ip-adresojn, kaj havenojn, dosierujojn ktp. estos izolita, sed same de stako al stako (ĉar la agorda dosiero estas sama por ĉiuj stakoj) - kion ni volis. Ni deplojas la stakon (areto) uzante docker-compose.yml, kiu priskribas nian areton.

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

Ĉi tie vi povas vidi, ke la komponantoj estas konektitaj per unu reto (nw_swarm) kaj disponeblas unu al la alia.

Sistemkomponentoj (bazitaj sur redis, mysql) estas apartigitaj de la ĝenerala grupo de kutimaj komponantoj (en planoj kaj kutimaj estas dividitaj kiel servoj). La disfalda stadio de nia areto aspektas kiel pasi CMD en nian unu grandan agorditan bildon kaj, ĝenerale, praktike ne diferencas de la deplojo priskribita en Parto I. Mi reliefigos la diferencojn:

  • git-klono... - akiru la dosierojn necesajn por deploji (createconfig.py, install_venv.sh, ktp.)
  • buklo... && malfermu... - elŝutu kaj malzipu konstruajn artefaktojn (kompilitaj iloj)

Estas nur unu ankoraŭ nepriskribita problemo: komponantoj, kiuj havas retan interfacon, ne estas alireblaj de retumiloj de programistoj. Ni solvas ĉi tiun problemon uzante inversan prokurilon, tiel:

En .gitlab-ci.yml, post deplojado de la cluster-stako, ni aldonas la linion de deplojado de la balancilo (kiu, kiam ĝi estas kompromitata, nur ĝisdatigas sian agordon (kreas novajn agordajn dosierojn de nginx laŭ la ŝablono: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - vidu docker-compose-nginx.yml kodon)

    - 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

Sur la evolukomputiloj, ĝisdatigu /etc/hosts; preskribi URL al nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Do, la disfaldiĝo de izolitaj sursceniga aretoj estis efektivigita kaj programistoj nun povas ruli ilin en ajna nombro sufiĉa por kontroli siajn taskojn.

Estontaj planoj:

  • Apartigu niajn komponantojn kiel servojn
  • Havu por ĉiu Dockerfile
  • Aŭtomate detektu malpli ŝarĝitajn nodojn en la stako
  • Specifu nodojn laŭ nomo ŝablono (prefere ol uzi id kiel en la artikolo)
  • Aldonu kontrolon, ke la stako estas detruita
  • ...

Specialan dankon pro artikolo.

fonto: www.habr.com

Aldoni komenton