Triển khai ứng dụng với Docker Swarm

Hệ thống đề xuất nội dung video trực tuyến mà chúng tôi đang nghiên cứu là một sự phát triển thương mại khép kín và về mặt kỹ thuật là một cụm đa thành phần gồm các thành phần độc quyền và nguồn mở. Mục đích của bài viết này là để mô tả việc triển khai hệ thống phân cụm docker swarm cho một trang dàn dựng mà không làm gián đoạn quy trình làm việc đã thiết lập của các quy trình của chúng tôi trong một thời gian giới hạn. Câu chuyện được trình bày để bạn chú ý được chia thành hai phần. Phần đầu tiên mô tả CI / CD trước khi sử dụng docker swarm và phần thứ hai mô tả quá trình triển khai nó. Những người không quan tâm đến việc đọc phần đầu tiên có thể chuyển sang phần thứ hai một cách an toàn.

Phần I

Quay trở lại năm xa xôi, cần phải thiết lập quy trình CI / CD càng nhanh càng tốt. Một trong những điều kiện là không sử dụng Docker để triển khai các thành phần được phát triển vì nhiều lý do:

  • để hoạt động ổn định và đáng tin cậy hơn của các thành phần trong Sản xuất (trên thực tế, yêu cầu không sử dụng ảo hóa)
  • các nhà phát triển hàng đầu không muốn làm việc với Docker (kỳ lạ, nhưng nó là như vậy)
  • theo những cân nhắc về ý thức hệ của quản lý R&D

Cơ sở hạ tầng, ngăn xếp và các yêu cầu ban đầu gần đúng cho MVP được trình bày như sau:

  • 4 máy chủ Intel® X5650 với Debian (một máy mạnh hơn được phát triển đầy đủ)
  • Phát triển các thành phần tùy chỉnh riêng được thực hiện trong C ++, Python3
  • Các công cụ chính của bên thứ 3 được sử dụng: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql,…
  • Các đường ống để xây dựng và thử nghiệm các thành phần riêng biệt để gỡ lỗi và phát hành

Một trong những câu hỏi đầu tiên cần được giải quyết ở giai đoạn ban đầu là cách các thành phần tùy chỉnh sẽ được triển khai trong bất kỳ môi trường nào (CI/CD).

Chúng tôi quyết định cài đặt các thành phần của bên thứ ba một cách có hệ thống và cập nhật chúng một cách có hệ thống. Các ứng dụng tùy chỉnh được phát triển bằng C++ hoặc Python có thể được triển khai theo nhiều cách. Trong số đó, ví dụ: tạo các gói hệ thống, gửi chúng đến kho lưu trữ các hình ảnh đã xây dựng và sau đó cài đặt chúng trên các máy chủ. Vì một lý do không xác định, một phương pháp khác đã được chọn, cụ thể là: sử dụng CI, các tệp thực thi ứng dụng được biên dịch, môi trường dự án ảo được tạo, mô-đun py được cài đặt từ tests.txt và tất cả các tạo phẩm này được gửi cùng với cấu hình, tập lệnh và môi trường ứng dụng đi kèm đến máy chủ. Tiếp theo, các ứng dụng được khởi chạy dưới dạng người dùng ảo không có quyền quản trị viên.

Gitlab-CI được chọn làm hệ thống CI/CD. Đường ống kết quả trông giống như thế này:

Triển khai ứng dụng với Docker Swarm
Về mặt cấu trúc, gitlab-ci.yml trông như thế này

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

Điều đáng chú ý là việc lắp ráp và thử nghiệm được thực hiện trên hình ảnh của chính nó, nơi tất cả các gói hệ thống cần thiết đã được cài đặt và các cài đặt khác đã được thực hiện.

Mặc dù mỗi kịch bản này trong công việc đều thú vị theo cách riêng của nó, nhưng tất nhiên tôi sẽ không nói về chúng, việc mô tả từng kịch bản sẽ mất rất nhiều thời gian và đây không phải là mục đích của bài viết. Tôi sẽ chỉ thu hút sự chú ý của bạn vào thực tế là giai đoạn triển khai bao gồm một chuỗi các tập lệnh gọi:

  1. createconfig.py - tạo tệp settings.ini với cài đặt thành phần trong các môi trường khác nhau để triển khai tiếp theo (Tiền sản xuất, Sản xuất, Thử nghiệm, ...)
  2. cài đặt_venv.sh - tạo một môi trường ảo cho các thành phần py trong một thư mục cụ thể và sao chép nó vào các máy chủ từ xa
  3. chuẩn bị_init.d.py - chuẩn bị các tập lệnh bắt đầu dừng cho thành phần dựa trên mẫu
  4. triển khai.py - phân hủy và khởi động lại các thành phần mới

Thời gian trôi qua. Giai đoạn dàn dựng đã được thay thế bằng tiền sản xuất và sản xuất. Đã thêm hỗ trợ cho sản phẩm trên một bản phân phối khác (CentOS). Đã thêm 5 máy chủ vật lý mạnh hơn và hàng chục máy chủ ảo. Và ngày càng trở nên khó khăn hơn đối với các nhà phát triển và người thử nghiệm khi kiểm tra các nhiệm vụ của họ trong một môi trường ít nhiều gần với trạng thái làm việc. Lúc này, rõ ràng là không thể làm gì nếu không có anh ấy ...

Phần II

Triển khai ứng dụng với Docker Swarm

Vì vậy, cụm của chúng tôi là một hệ thống ngoạn mục gồm vài chục thành phần riêng biệt không được Dockerfiles mô tả. Bạn chỉ có thể định cấu hình nó để triển khai cho một môi trường cụ thể nói chung. Nhiệm vụ của chúng tôi là triển khai cụm vào một môi trường dàn dựng để kiểm tra nó trước khi thử nghiệm trước khi phát hành.

Về mặt lý thuyết, có thể có một số cụm chạy đồng thời: bao nhiêu là có nhiệm vụ ở trạng thái đã hoàn thành hoặc sắp hoàn thành. Khả năng của các máy chủ theo ý của chúng tôi cho phép chúng tôi chạy một số cụm trên mỗi máy chủ. Mỗi cụm dàn phải được cách ly (không được có giao điểm trong các cổng, thư mục, v.v.).

Tài nguyên quý giá nhất của chúng ta là thời gian, và chúng ta không có nhiều thời gian.

Để bắt đầu nhanh hơn, chúng tôi đã chọn Docker Swarm do tính đơn giản và linh hoạt về kiến ​​trúc của nó. Điều đầu tiên chúng tôi làm là tạo một trình quản lý và một số nút trên máy chủ từ xa:

$ 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

Tiếp theo, tạo một mạng:


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

Tiếp theo, chúng tôi đã kết nối các nút Gitlab-CI và Swarm về mặt điều khiển từ xa các nút từ CI: cài đặt chứng chỉ, đặt biến bí mật và thiết lập dịch vụ Docker trên máy chủ điều khiển. Cái này bài viết đã tiết kiệm cho chúng tôi rất nhiều thời gian.

Tiếp theo, chúng tôi đã thêm các công việc tạo và hủy ngăn xếp vào .gitlab-ci .yml.

Một số công việc khác đã được thêm vào .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

Từ đoạn mã trên, bạn có thể thấy rằng hai nút (deploy_staging, stop_staging) đã được thêm vào Đường ống, yêu cầu thao tác thủ công.

Triển khai ứng dụng với Docker Swarm
Tên ngăn xếp khớp với tên nhánh và tính duy nhất này là đủ. Các dịch vụ trong ngăn xếp nhận các địa chỉ IP duy nhất và các cổng, thư mục, v.v. sẽ bị cô lập, nhưng giống nhau từ ngăn xếp này sang ngăn xếp khác (vì tệp cấu hình giống nhau cho tất cả các ngăn xếp) - điều chúng tôi muốn. Chúng tôi triển khai ngăn xếp (cụm) bằng cách sử dụng docker-compose.yml, mô tả cụm của chúng tôi.

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

Ở đây bạn có thể thấy rằng các thành phần được kết nối bởi một mạng (nw_swarm) và có sẵn cho nhau.

Các thành phần hệ thống (dựa trên redis, mysql) được tách biệt khỏi nhóm chung của các thành phần tùy chỉnh (trong các gói và tùy chỉnh được chia thành các dịch vụ). Giai đoạn triển khai cụm của chúng tôi trông giống như chuyển CMD vào một hình ảnh lớn được định cấu hình của chúng tôi và nói chung, thực tế không khác với triển khai được mô tả trong Phần I. Tôi sẽ nêu bật những điểm khác biệt:

  • bản sao git ... - lấy các tệp cần thiết để triển khai (createconfig.py, install_venv.sh, v.v.)
  • cuộn tròn... && giải nén... - tải xuống và giải nén các tạo phẩm xây dựng (tiện ích đã biên dịch)

Chỉ có một vấn đề chưa được mô tả: các thành phần có giao diện web không thể truy cập được từ trình duyệt của nhà phát triển. Chúng tôi giải quyết vấn đề này bằng cách sử dụng proxy ngược, do đó:

Trong .gitlab-ci.yml, sau khi triển khai ngăn xếp cụm, chúng tôi thêm dòng triển khai bộ cân bằng (khi cam kết, chỉ cập nhật cấu hình của nó (tạo các tệp cấu hình nginx mới theo mẫu: /etc/nginx/conf.d/${CI_COMMIT_REF_NAME}.conf) - xem mã 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

Trên các máy tính đang phát triển, hãy cập nhật /etc/hosts; chỉ định url cho nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Vì vậy, việc triển khai các cụm dàn biệt lập đã được triển khai và các nhà phát triển giờ đây có thể chạy chúng với số lượng bất kỳ đủ để kiểm tra các tác vụ của họ.

Các kế hoạch trong tương lai:

  • Tách các thành phần của chúng tôi thành các dịch vụ
  • Có cho mỗi Dockerfile
  • Tự động phát hiện các nút được tải ít hơn trong ngăn xếp
  • Chỉ định các nút theo mẫu tên (chứ không phải sử dụng id như trong bài viết)
  • Thêm kiểm tra xem ngăn xếp có bị hủy không
  • ...

đặc biệt cảm ơn cho Bài viết.

Nguồn: www.habr.com

Thêm một lời nhận xét