Terapkan Aplikasi dengan Docker Swarm

Sistem rekomendasi konten video online yang sedang kami kerjakan adalah pengembangan komersial tertutup dan secara teknis merupakan kelompok multi-komponen dari komponen berpemilik dan sumber terbuka. Tujuan penulisan artikel ini adalah untuk menjelaskan implementasi sistem pengelompokan kawanan buruh pelabuhan untuk situs pementasan tanpa mengganggu alur kerja proses kami yang sudah mapan dalam waktu terbatas. Narasi yang disajikan untuk perhatian Anda dibagi menjadi dua bagian. Bagian pertama menjelaskan CI/CD sebelum menggunakan docker swarm, dan bagian kedua menjelaskan proses implementasinya. Mereka yang tidak tertarik membaca bagian pertama dapat dengan aman melanjutkan ke bagian kedua.

Bagian I

Kembali ke tahun yang sangat jauh, proses CI / CD perlu diatur secepat mungkin. Salah satu syaratnya adalah tidak menggunakan Docker untuk penerapan mengembangkan komponen karena beberapa alasan:

  • untuk operasi komponen yang lebih andal dan stabil dalam Produksi (yaitu, pada kenyataannya, persyaratan untuk tidak menggunakan virtualisasi)
  • pengembang terkemuka tidak ingin bekerja dengan Docker (aneh, tapi begitulah)
  • sesuai dengan pertimbangan ideologis manajemen R&D

Infrastruktur, tumpukan, dan perkiraan persyaratan awal untuk MVP disajikan sebagai berikut:

  • 4 server Intel® X5650 dengan Debian (satu mesin yang lebih kuat telah dikembangkan sepenuhnya)
  • Pengembangan komponen kustom sendiri dilakukan di C ++, Python3
  • Alat pihak ketiga utama yang digunakan: Kafka, Clickhouse, Airflow, Redis, Grafana, Postgresql, Mysql, …
  • Pipeline untuk membuat dan menguji komponen secara terpisah untuk debug dan rilis

Salah satu pertanyaan pertama yang perlu dijawab pada tahap awal adalah bagaimana komponen kustom akan diterapkan di lingkungan apa pun (CI/CD).

Kami memutuskan untuk menginstal komponen pihak ketiga secara sistemik dan memperbaruinya secara sistemik. Aplikasi khusus yang dikembangkan dalam C++ atau Python dapat digunakan dalam beberapa cara. Diantaranya, misalnya: membuat paket sistem, mengirimkannya ke repositori gambar yang dibuat, lalu menginstalnya di server. Untuk alasan yang tidak diketahui, metode lain dipilih, yaitu: menggunakan CI, file aplikasi yang dapat dieksekusi dikompilasi, lingkungan proyek virtual dibuat, modul py diinstal dari requirements.txt, dan semua artefak ini dikirim bersama dengan konfigurasi, skrip, dan lingkungan aplikasi yang menyertai ke server. Selanjutnya, aplikasi diluncurkan sebagai pengguna virtual tanpa hak administrator.

Gitlab-CI dipilih sebagai sistem CI/CD. Pipa yang dihasilkan terlihat seperti ini:

Terapkan Aplikasi dengan Docker Swarm
Secara struktural, gitlab-ci.yml terlihat seperti ini

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

Perlu dicatat bahwa perakitan dan pengujian dilakukan pada citranya sendiri, di mana semua paket sistem yang diperlukan telah diinstal dan pengaturan lainnya telah dibuat.

Meskipun masing-masing skrip dalam pekerjaan ini menarik dengan caranya sendiri, tetapi tentu saja saya tidak akan membicarakannya, deskripsi masing-masing akan memakan banyak waktu dan ini bukan tujuan artikel. Saya hanya akan menarik perhatian Anda pada fakta bahwa tahap penerapan terdiri dari urutan skrip panggilan:

  1. buatconfig.py - membuat file settings.ini dengan pengaturan komponen di berbagai lingkungan untuk penerapan selanjutnya (Praproduksi, Produksi, Pengujian, ...)
  2. install_venv.sh - membuat lingkungan virtual untuk komponen py di direktori tertentu dan menyalinnya ke server jarak jauh
  3. siapkan_init.d.py — menyiapkan skrip start-stop untuk komponen berdasarkan template
  4. menyebarkan.py - terurai dan restart komponen baru

Waktu berlalu. Tahap pementasan digantikan oleh praproduksi dan produksi. Menambahkan dukungan untuk produk di satu distribusi lagi (CentOS). Menambahkan 5 server fisik yang lebih kuat dan selusin server virtual. Dan semakin sulit bagi pengembang dan penguji untuk menguji tugas mereka di lingkungan yang kurang lebih dekat dengan kondisi kerja. Pada saat ini, menjadi jelas bahwa tidak mungkin dilakukan tanpa dia ...

Bagian II

Terapkan Aplikasi dengan Docker Swarm

Jadi, kluster kami adalah sistem spektakuler dari beberapa lusin komponen terpisah yang tidak dijelaskan oleh Dockerfiles. Anda hanya dapat mengonfigurasinya untuk diterapkan ke lingkungan tertentu secara umum. Tugas kita adalah menerapkan klaster ke lingkungan pementasan untuk mengujinya sebelum pengujian pra-rilis.

Secara teoritis, beberapa cluster dapat berjalan secara bersamaan: sebanyak tugas dalam keadaan selesai atau hampir selesai. Kapasitas server yang kami miliki memungkinkan kami menjalankan beberapa cluster di setiap server. Setiap cluster pementasan harus diisolasi (tidak boleh ada persimpangan di port, direktori, dll.).

Sumber daya kita yang paling berharga adalah waktu kita, dan kita tidak memiliki banyak waktu.

Untuk permulaan yang lebih cepat, kami memilih Docker Swarm karena kesederhanaan dan fleksibilitas arsitekturnya. Hal pertama yang kami lakukan adalah membuat pengelola dan beberapa node di server jarak jauh:

$ 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

Selanjutnya, buat jaringan:


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

Selanjutnya, kami menghubungkan node Gitlab-CI dan Swarm dalam hal kendali jarak jauh node dari CI: memasang sertifikat, menyetel variabel rahasia, dan menyiapkan layanan Docker di server kontrol. Yang ini artikel menyelamatkan kita banyak waktu.

Selanjutnya, kami menambahkan tugas pembuatan dan penghancuran tumpukan ke .gitlab-ci .yml.

Beberapa pekerjaan lagi telah ditambahkan ke .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

Dari cuplikan kode di atas, Anda dapat melihat bahwa dua tombol (deploy_staging, stop_staging) telah ditambahkan ke Pipelines, yang memerlukan tindakan manual.

Terapkan Aplikasi dengan Docker Swarm
Nama tumpukan cocok dengan nama cabang dan keunikan ini sudah cukup. Layanan dalam tumpukan menerima alamat ip unik, dan port, direktori, dll. akan diisolasi, tetapi sama dari tumpukan ke tumpukan (karena file konfigurasinya sama untuk semua tumpukan) - yang kami inginkan. Kami menyebarkan tumpukan (cluster) menggunakan docker-compose.yml, yang menggambarkan klaster kami.

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

Di sini Anda dapat melihat bahwa komponen terhubung oleh satu jaringan (nw_swarm) dan tersedia satu sama lain.

Komponen sistem (berdasarkan redis, mysql) dipisahkan dari kumpulan umum komponen khusus (dalam paket dan yang khusus dibagi sebagai layanan). Tahap penerapan klaster kami terlihat seperti meneruskan CMD ke dalam satu gambar konfigurasi besar kami dan, secara umum, secara praktis tidak berbeda dari penerapan yang dijelaskan di Bagian I. Saya akan menyoroti perbedaannya:

  • klon git... - dapatkan file yang diperlukan untuk digunakan (createconfig.py, install_venv.sh, dll.)
  • ikal... && buka zip... - unduh dan unzip build artefak (utilitas yang dikompilasi)

Hanya ada satu masalah yang belum dijelaskan: komponen yang memiliki antarmuka web tidak dapat diakses dari browser pengembang. Kami mengatasi masalah ini menggunakan proxy terbalik, dengan demikian:

Di .gitlab-ci.yml, setelah menerapkan tumpukan cluster, kami menambahkan baris penerapan penyeimbang (yang, saat dilakukan, hanya memperbarui konfigurasinya (membuat file konfigurasi nginx baru sesuai dengan templat: /etc/nginx/conf. d/${CI_COMMIT_REF_NAME}.conf) - lihat kode docker-compose-nginx.yml)

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

buruh pelabuhan-menulis-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

Pada komputer pengembangan, perbarui /etc/hosts; resepkan url ke nginx:

10.50.173.106 staging_BRANCH-1831_cluster.dev

Jadi, penerapan cluster staging terisolasi telah diterapkan dan pengembang sekarang dapat menjalankannya dalam jumlah yang cukup untuk memeriksa tugas mereka.

Rencana masa depan:

  • Pisahkan komponen kami sebagai layanan
  • Miliki untuk setiap Dockerfile
  • Secara otomatis mendeteksi node yang kurang dimuat dalam tumpukan
  • Tentukan node berdasarkan pola nama (daripada menggunakan id seperti di artikel)
  • Tambahkan tanda centang bahwa tumpukan telah dihancurkan
  • ...

Terima kasih khusus untuk sebuah artikel.

Sumber: www.habr.com

Tambah komentar