使用docker swarm部署应用

我们正在开发的在线视频内容推荐系统是一个封闭的商业开发,从技术上来说是一个由其自己的组件和开源组件组成的多组件集群。 本文的目的是描述在登台平台下引入docker swarm集群系统的过程,而不会在有限的时间内中断我们流程的现有工作流程。 提请您注意的叙述分为两部分。 第一部分描述使用docker swarm之前的CI / CD,第二部分描述实现过程。 那些对阅读第一部分不感兴趣的人可以安全地转到第二部分。

第一部分


在遥远的一年中,需要尽快配置CI / CD流程。 其中一个条件是不使用Docker 部署开发的组件,原因有以下几个:

  • 使生产中的组件更可靠,更稳定地运行(即实际上要求不使用虚拟化)
  • 领先的开发人员不想使用Docker(很奇怪,但这仅仅是那样)
  • 由于意识形态原因,研发管理

MVP的基础结构,堆栈和示例初始要求如下:

  • 4台带Debian的Intel®X5650服务器(一台功能强大的机器完全可以开发)
  • 自定义组件的开发在C ++,Python3中进行
  • 3rdparty使用的主要工具:Kafka,Clickhouse,Airflow,Redis,Grafana,Postgresql,Mysql,...
  • 分别管道组装和测试组件以进行调试和发布

在初始阶段要解决的第一个问题是如何在任何环境(CI / CD)中部署自定义组件。

第三方组件决定系统安装并进行系统更新。 用C ++或Python开发的自定义应用程序可以通过多种方式进行部署。 其中,例如:创建系统软件包,将其发送到收集的映像的存储库中,然后将其随后安装在服务器上。 由于未知的原因,选择了另一种方法,即使用CI,编译可执行的应用程序文件,创建虚拟项目环境,安装来自requirements.txt的py-modules并将所有这些工件与配置,脚本和随附的应用程序环境一起发送到服务器。 接下来,从没有管理员权限的虚拟用户启动应用程序。

Gitlab-CI被选为CI / CD系统。 产生的管道如下所示:

图片
在结构上,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 *\n\tStrictHostKeyChecking no\n\n" > ~/.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. prepare_init.d.py-根据模板准备组件的起止脚本
  4. deploy.py-解压缩并重新启动新组件

时间过去了。 过渡阶段已被预生产和生产所取代。 产品支持已添加到另一个分发工具包(CentOS)。 添加了5个功能更强大的物理服务器和12个虚拟服务器。 对于开发人员和测试人员来说,在或多或少接近工作状态的环境中运行任务变得越来越困难。 这时,很明显,没有他就无法做...

第二部分


图片

因此,我们的集群仍然是由Dockerfiles未描述的数十个独立组件组成系统的奇观。 您只能将其配置为仅整体部署到特定环境。 我们的任务是在预发布测试之前将集群部署到暂存环境中以运行它。

从理论上讲,可以有多个同时工作的集群:处于完成状态或接近完成状态的任务数量之多。 服务器可用的容量使我们可以在每个服务器上运行多个集群。 每个暂存群集必须隔离(端口,目录等上不应有交叉点)。

最宝贵的资源是我们的时间,而我们却没有太多。

为了快速起步,他们选择了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 

接下来,我们根据CI节点的远程管理连接了Gitlab-CI和Swarm节点:安装证书,设置秘密变量以及在管理服务器上设置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),需要手动干预。

图片
堆栈的名称与分支的名称相对应,并且唯一性就足够了。 堆栈中的服务接收唯一的IP地址以及端口,目录等。 将被隔离,但对于每个堆栈来说都是相同的(因为所有堆栈的配置文件都是相同的)-这就是我们所实现的。 我们使用描述集群的docker-compose.yml部署堆栈 (集群)。

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 


在这里,您可以看到组件通过一个网络(nw_swarm)连接并且可以相互访问。

系统组件(基于redis,mysql)与定制组件的公共池(计划和定制被划分为服务)分开。 我们集群的部署阶段看起来像是将CMD转移到一个大型配置映像,并且总体上来说,与第一部分中描述的部署几乎没有什么不同。我着重强调以下差异:

  • git clone ...-我们获得进行部署所需的文件(createconfig.py,install_venv.sh等)
  • curl ... &&解压缩...-下载并解压缩汇编工件(已编译的实用程序)

仅有一个尚未描述的问题:具有Web界面的组件无法从开发人员浏览器访问。 我们使用反向代理解决了这个问题,因此:

在.gitlab-ci.yml中,部署集群堆栈后,添加平衡器部署行(提交时仅更新其配置(使用模板创建新的nginx配置文件:/etc/nginx/conf.d/${CI_COMMIT_REF_NAME►.conf))-参见代码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 


在开发计算机上,更新/ etc /主机; 向nginx注册网址:

10.50.173.106 staging_BRANCH-1831_cluster.dev

因此,已经实现了隔离的暂存群集的部署,并且开发人员现在可以以足够的数量启动它们以测试其任务。

进一步的计划:

  • 将我们的组件作为服务分开
  • 为每个Dockerfile制作
  • 自动检测堆栈中较少加载的节点
  • 通过名称模式设置节点(而不是像文章中那样使用id)
  • 添加检查堆栈是否已损坏
  • ...

特别感谢您的文章

Source: https://habr.com/ru/post/zh-CN471528/


All Articles