ปรับใช้แอปพลิเคชันด้วย Docker Swarm

ระบบแนะนำเนื้อหาวิดีโอออนไลน์ที่เรากำลังดำเนินการคือการพัฒนาเชิงพาณิชย์แบบปิด และในทางเทคนิคแล้วเป็นกลุ่มองค์ประกอบหลายองค์ประกอบที่เป็นกรรมสิทธิ์และโอเพ่นซอร์ส จุดประสงค์ของการเขียนบทความนี้คือเพื่ออธิบายการนำระบบการทำคลัสเตอร์ docker swarm ไปใช้สำหรับไซต์การแสดงละครโดยไม่รบกวนขั้นตอนการทำงานที่กำหนดไว้ของกระบวนการของเราในเวลาที่จำกัด เรื่องราวที่นำเสนอให้คุณสนใจแบ่งออกเป็นสองส่วน ส่วนแรกอธิบาย CI / CD ก่อนใช้ docker swarm และส่วนที่สองอธิบายขั้นตอนการนำไปใช้งาน ผู้ที่ไม่สนใจอ่านส่วนแรกสามารถไปยังส่วนที่สองได้อย่างปลอดภัย

ส่วนที่ XNUMX

ย้อนกลับไปในปีที่ห่างไกล จำเป็นต้องตั้งค่ากระบวนการ CI / CD ให้เร็วที่สุด เงื่อนไขข้อหนึ่งคือห้ามใช้ Docker สำหรับการปรับใช้ พัฒนาส่วนประกอบด้วยเหตุผลหลายประการ:

  • เพื่อการทำงานที่เชื่อถือได้และเสถียรยิ่งขึ้นของส่วนประกอบในการผลิต (นั่นคือข้อกำหนดที่จะไม่ใช้การจำลองเสมือน)
  • นักพัฒนาชั้นนำไม่ต้องการทำงานกับ Docker (แปลก แต่ก็เป็นเช่นนั้น)
  • ตามการพิจารณาเชิงอุดมการณ์ของฝ่ายบริหาร R&D

โครงสร้างพื้นฐาน สแต็ก และความต้องการเริ่มต้นโดยประมาณสำหรับ MVP ถูกนำเสนอดังนี้:

  • 4 เซิร์ฟเวอร์ Intel® X5650 พร้อม Debian (อีกหนึ่งเครื่องที่มีประสิทธิภาพได้รับการพัฒนาอย่างเต็มที่)
  • การพัฒนาส่วนประกอบที่กำหนดเองดำเนินการใน C ++, Python3
  • เครื่องมือหลักของบุคคลที่สามที่ใช้: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • ไปป์ไลน์สำหรับการสร้างและทดสอบส่วนประกอบแยกกันสำหรับการดีบักและการเปิดตัว

หนึ่งในคำถามแรกๆ ที่ต้องระบุในระยะเริ่มต้นคือการปรับใช้คอมโพเนนต์แบบกำหนดเองในสภาพแวดล้อมใดๆ (CI / CD)

เราตัดสินใจติดตั้งส่วนประกอบของบุคคลที่สามอย่างเป็นระบบและอัปเดตอย่างเป็นระบบ แอปพลิเคชันแบบกำหนดเองที่พัฒนาด้วย C++ หรือ Python สามารถปรับใช้ได้หลายวิธี ตัวอย่างเช่น การสร้างแพ็คเกจระบบ ส่งไปยังที่เก็บอิมเมจที่สร้างขึ้น จากนั้นติดตั้งบนเซิร์ฟเวอร์ ด้วยเหตุผลที่ไม่ทราบสาเหตุ จึงมีการเลือกวิธีอื่น กล่าวคือ: การใช้ CI, ไฟล์เรียกทำงานของแอปพลิเคชันได้รับการคอมไพล์, สร้างสภาพแวดล้อมโครงการเสมือน, โมดูล py ได้รับการติดตั้งจาก requirement.txt และสิ่งประดิษฐ์ทั้งหมดเหล่านี้จะถูกส่งไปพร้อมกับการกำหนดค่า สคริปต์ และ สภาพแวดล้อมแอปพลิเคชันที่มาพร้อมกับเซิร์ฟเวอร์ ถัดไป แอปพลิเคชันจะเปิดตัวเป็นผู้ใช้เสมือนโดยไม่มีสิทธิ์ของผู้ดูแลระบบ

Gitlab-CI ได้รับเลือกให้เป็นระบบ CI/CD ไปป์ไลน์ที่ได้มีลักษณะดังนี้:

ปรับใช้แอปพลิเคชันด้วย Docker Swarm
โครงสร้าง gitlab-ci.yml มีลักษณะดังนี้

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

เป็นที่น่าสังเกตว่าการประกอบและการทดสอบนั้นดำเนินการในอิมเมจของตัวเองซึ่งได้ติดตั้งแพ็คเกจระบบที่จำเป็นทั้งหมดแล้วและทำการตั้งค่าอื่น ๆ

แม้ว่าแต่ละสคริปต์ในงานจะมีความน่าสนใจในแบบของตัวเองแต่แน่นอนว่าฉันจะไม่พูดถึงมันคำอธิบายของแต่ละสคริปต์จะใช้เวลามากและนี่ไม่ใช่จุดประสงค์ของบทความ ฉันจะดึงความสนใจของคุณไปที่ความจริงที่ว่าขั้นตอนการปรับใช้ประกอบด้วยลำดับของสคริปต์การโทร:

  1. createconfig.py - สร้างไฟล์ settings.ini พร้อมการตั้งค่าคอมโพเนนต์ในสภาพแวดล้อมต่างๆ สำหรับการปรับใช้ในภายหลัง (ก่อนการผลิต การผลิต การทดสอบ ...)
  2. install_venv.sh - สร้างสภาพแวดล้อมเสมือนสำหรับส่วนประกอบ py ในไดเร็กทอรีเฉพาะและคัดลอกไปยังเซิร์ฟเวอร์ระยะไกล
  3. การเตรียม_init.d.py — เตรียมสคริปต์ start-stop สำหรับส่วนประกอบตามเทมเพลต
  4. Deploy.py - ย่อยสลายและรีสตาร์ทส่วนประกอบใหม่

เวลาผ่านไป. ขั้นตอนการแสดงละครถูกแทนที่ด้วยขั้นตอนก่อนการผลิตและการผลิต เพิ่มการรองรับสำหรับผลิตภัณฑ์ในการแจกจ่ายอีกหนึ่งรายการ (CentOS) เพิ่มเซิร์ฟเวอร์จริงที่ทรงพลังอีก 5 เครื่องและเซิร์ฟเวอร์เสมือนอีกโหล และกลายเป็นเรื่องยากมากขึ้นสำหรับนักพัฒนาและผู้ทดสอบในการทดสอบงานของพวกเขาในสภาพแวดล้อมที่ใกล้เคียงกับสถานะการทำงานไม่มากก็น้อย ในเวลานี้เห็นได้ชัดว่าเป็นไปไม่ได้หากไม่มีเขา ...

ส่วนที่สอง

ปรับใช้แอปพลิเคชันด้วย Docker Swarm

ดังนั้น คลัสเตอร์ของเราจึงเป็นระบบที่น่าทึ่งซึ่งมีองค์ประกอบแยกกันสองสามองค์ประกอบที่ Dockerfiles ไม่ได้อธิบายไว้ คุณสามารถกำหนดค่าสำหรับการปรับใช้กับสภาพแวดล้อมที่เฉพาะเจาะจงโดยทั่วไปเท่านั้น งานของเราคือปรับใช้คลัสเตอร์ในสภาพแวดล้อมชั่วคราวเพื่อทดสอบก่อนการทดสอบก่อนเผยแพร่

ในทางทฤษฎี สามารถมีหลายคลัสเตอร์ที่ทำงานพร้อมกัน: ได้มากเท่าที่มีงานอยู่ในสถานะเสร็จสมบูรณ์หรือใกล้จะเสร็จสมบูรณ์ ความจุของเซิร์ฟเวอร์ที่เราจำหน่ายช่วยให้เราสามารถเรียกใช้หลายคลัสเตอร์ในแต่ละเซิร์ฟเวอร์ แต่ละคลัสเตอร์ staging จะต้องแยกออกจากกัน (ต้องไม่มีจุดตัดกันในพอร์ต ไดเร็กทอรี ฯลฯ)

ทรัพยากรที่มีค่าที่สุดของเราคือเวลาของเรา และเรามีไม่มากนัก

เพื่อการเริ่มต้นที่เร็วขึ้น เราเลือก Docker Swarm เนื่องจากความเรียบง่ายและความยืดหยุ่นของสถาปัตยกรรม สิ่งแรกที่เราทำคือสร้างตัวจัดการและโหนดต่างๆ บนเซิร์ฟเวอร์ระยะไกล:

$ 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

ต่อไป สร้างเครือข่าย:


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

ต่อไป เราเชื่อมต่อโหนด Gitlab-CI และ Swarm ในแง่ของการควบคุมระยะไกลของโหนดจาก CI: การติดตั้งใบรับรอง การตั้งค่าตัวแปรลับ และการตั้งค่าบริการ Docker บนเซิร์ฟเวอร์ควบคุม อันนี้ บทความ ช่วยเราประหยัดเวลาได้มาก

ต่อไป เราได้เพิ่มงานการสร้างและการทำลายสแต็กใน .gitlab-ci .yml

มีการเพิ่มงานอีกเล็กน้อยใน .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

จากข้อมูลโค้ดด้านบน คุณจะเห็นว่ามีการเพิ่มปุ่มสองปุ่ม (deploy_staging, stop_staging) ลงใน Pipelines ซึ่งต้องมีการดำเนินการโดยเจ้าหน้าที่

ปรับใช้แอปพลิเคชันด้วย Docker Swarm
ชื่อสแต็คตรงกับชื่อสาขาและความเป็นเอกลักษณ์นี้น่าจะเพียงพอแล้ว บริการในสแตกได้รับที่อยู่ IP เฉพาะและพอร์ต ไดเร็กทอรี ฯลฯ จะถูกแยกออก แต่เหมือนกันจากสแต็กหนึ่งไปยังอีกสแต็ก (เนื่องจากไฟล์คอนฟิกูเรชันเหมือนกันสำหรับทุกสแต็ก) - สิ่งที่เราต้องการ เราปรับใช้สแต็ค (คลัสเตอร์) โดยใช้ นักเทียบท่า-compose.ymlซึ่งอธิบายคลัสเตอร์ของเรา

นักเทียบท่า-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

ที่นี่คุณจะเห็นว่าส่วนประกอบต่างๆ เชื่อมต่อกันด้วยเครือข่ายเดียว (nw_swarm) และพร้อมใช้งานซึ่งกันและกัน

ส่วนประกอบของระบบ (ขึ้นอยู่กับ redis, mysql) จะถูกแยกออกจากกลุ่มทั่วไปของส่วนประกอบที่กำหนดเอง (ในแผนและส่วนประกอบที่กำหนดเองจะแบ่งออกเป็นบริการ) ขั้นตอนการปรับใช้ของคลัสเตอร์ของเราดูเหมือนการส่งผ่าน CMD ไปยังอิมเมจขนาดใหญ่ที่กำหนดค่าไว้ และโดยทั่วไปแล้ว ในทางปฏิบัติไม่แตกต่างจากการปรับใช้ที่อธิบายไว้ในส่วนที่ XNUMX ฉันจะเน้นความแตกต่าง:

  • โคลนคอมไพล์... - รับไฟล์ที่จำเป็นในการปรับใช้ (createconfig.py, install_venv.sh เป็นต้น)
  • ขด... && เปิดเครื่องรูด... - ดาวน์โหลดและเปิดเครื่องรูดสร้างสิ่งประดิษฐ์ (คอมไพล์ยูทิลิตี้)

มีปัญหาเดียวที่ยังไม่ได้อธิบาย: ส่วนประกอบที่มีอินเทอร์เฟซเว็บไม่สามารถเข้าถึงได้จากเบราว์เซอร์ของนักพัฒนา เราแก้ปัญหานี้โดยใช้พร็อกซีย้อนกลับ ดังนั้น:

ใน .gitlab-ci.yml หลังจากปรับใช้คลัสเตอร์สแต็กแล้ว เราเพิ่มบรรทัดของการปรับใช้บาลานเซอร์ (ซึ่งเมื่อคอมมิต จะอัปเดตการกำหนดค่าเท่านั้น (สร้างไฟล์การกำหนดค่า nginx ใหม่ตามเทมเพลต: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - ดูรหัสนักเทียบท่า-compose-nginx.yml)

    - docker stack deploy -c docker-compose-nginx.yml ${CI_ENVIRONMENT_NAME} --with-registry-auth

นักเทียบท่า-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

บนคอมพิวเตอร์สำหรับการพัฒนา ให้อัพเดต /etc/hosts; กำหนด URL เป็น nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

ดังนั้น การปรับใช้คลัสเตอร์ staging แบบแยกจึงถูกนำมาใช้ และตอนนี้นักพัฒนาสามารถเรียกใช้งานคลัสเตอร์จำนวนเท่าใดก็ได้ที่เพียงพอต่อการตรวจสอบงานของพวกเขา

แผนการในอนาคต:

  • แยกส่วนประกอบของเราออกเป็นบริการ
  • มีสำหรับแต่ละไฟล์ Dockerfile
  • ตรวจหาโหนดที่โหลดน้อยกว่าในสแต็กโดยอัตโนมัติ
  • ระบุโหนดตามรูปแบบชื่อ (แทนการใช้ id เหมือนในบทความ)
  • เพิ่มการตรวจสอบว่าสแตกถูกทำลาย
  • ...

ขอขอบคุณเป็นพิเศษสำหรับ บทความ.

ที่มา: will.com

เพิ่มความคิดเห็น