基于 Jenkins + Swarm 的微型绿蓝发布方案

/ Linux / 1 条评论 / 582浏览

前言

由于目前就职的是一家做物联网相关产品的科技公司, 为了帮助公司解决后端发布更新时导致服务不可用问题及帮助项目实现未来业务扩张后集群运行等问题, 结合公司实际情况, 探索合适的技术栈来解决这些问题。最终决定使用 Jenkins + Swarm 来进行实施. 至于采用这套技术栈的原因, 大概可以总结为以下几点:

综上所述, 在容器编排选型上面就直接抛弃了 k8s 而选择轻量化的 swarm. 包括镜像管理这一块, 也是直接去掉了公共的存储仓库而选择直接在目标机器操作.

技术要点

实施过程

docker & swarm 安装

参见 Dokcer-CE & Docker Compose 安装

为了做到足够简单, 并没有去配置 Docker 的远程API, 后面统一使用 Jenkins 远程SSH操作

Traefik部署

参见 Traefik-V2.0 & Docker-Compose 最佳实践

~~项目使用的是 Swarm 模式, 所以上面文章的内容需要作适量改动, 详情参照官网 https://docs.traefik.io/v2.0/providers/docker/#swarmmode~~ 项目因为只有单节点, 所以 Traefik 只是以 compose 部署的非集群方式运行

Jenkins部署

参见Jenkins + Git + Maven持续构建部署环境搭建

这次搭建过程处理上述的3个插件之外, 还使用到一个轻量的插件 Publish Over SSH, 通过它来实现传输文件和执行命令以及管理主机(不使用 scp & ssh 的原因是这个插件可以通过密码登录, 不用修改 sshd 服务) 另外赠送一篇记录解决 Jenkins 安装插件缓慢问题的文章 Jenkins for Docker 跳过插件安装及插件加速镜像设置 另外 Jenkins 需要的 Git Maven JDK 等组件我这边都是通过 Jenkins 内部安装的, 就不细讲了

ELK部署

日志收集分析使用的业界常用的 ELK 方案(找了很久没有找到合适的轻量替代方案), 搭配阿里的 log-pilot 收集器体验非常好, ~~为了减少系统采销, 这里使用的是 5.6.16 版本~~ 实际实施发现 5.x 功能太弱 7.x 消耗太高, 最终使用的 6.8.5 版本 docker-compose.yaml

version: '3.7'
services:
  # https://yq.aliyun.com/articles/674327
  log-pilot:
    image: registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.7-filebeat
    restart: always
    privileged: true
    environment:
    - PILOT_TYPE=filebeat
    - LOGGING_OUTPUT=elasticsearch
    - ELASTICSEARCH_HOSTS=elasticsearch:9200
    # - ELASTICSEARCH_USER=
    # - ELASTICSEARCH_PASSWORD=
    volumes:
    - "/:/host:ro"
    - "/etc/localtime:/etc/localtime:ro"
    - "/var/run/docker.sock:/var/run/docker.sock:ro"
  # https://blog.csdn.net/qq_38906421/article/details/88644315
  # https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html
  elasticsearch:
    image: elasticsearch:6.8.5
    restart: always
    environment:
    - ES_JAVA_OPTS=-Xms256m -Xmx256m
    - xpack.security.enabled=true
    volumes:
    - "{es_data_path}:/usr/share/elasticsearch/data/:rw"
  # https://www.elastic.co/guide/en/kibana/current/docker.html
  kibana:
    image: kibana:6.8.5
    restart: always
    environment:
    - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    - XPACK_MONITORING_ENABLED=true
    - ELASTICSEARCH_USERNAME=kibana
    - ELASTICSEARCH_PASSWORD={password}
    ports:
    - 5601:5601

综合实现

上面的部分基本就是环境准备, 主要的实现还是在 Jenkins 里面的脚本, 主要方案就是打包项目之后, 将项目发送到目标机器之后, 在通过 Docker 编译, 最终使用 docker stack deploy 命令发布服务到 Swarm 简单记录一下使用到的脚本配置(下面的脚本属于和项目绑定的关系, 不适用于任何其他项目, 仅作为参考)

KEYVALUE备注
Source filesmodule/target/app.war传输的文件
Remove prefixmodule/target/忽略目录
Remote directory/root/docker-build/$JOB_BASE_NAME-$BUILD_NUMBER/存储路径
# 包名
PN=app.war
# 镜像名字
IN=domain/project:$BUILD_NUMBER
# 工作目录
WD=/root/docker-build/$JOB_BASE_NAME-$BUILD_NUMBER/

# 准备
cd $WD

# 构建
cat << EOF > Dockerfile
FROM tomcat:8.5.47-jdk8-openjdk

RUN \
echo 'tomcat.util.http.parser.HttpParser.requestTargetAllow=|{}' >> /usr/local/tomcat/conf/catalina.properties && \
rm -rf /usr/local/tomcat/webapps/*

ENV TZ=Asia/Shanghai
ENV JAVA_OPTS="-Xmx256m -Djava.security.egd=file:/dev/urandom"

ADD $PN /usr/local/tomcat/webapps/$PN

HEALTHCHECK --interval=10s --timeout=3s --start-period=5m --retries=3 CMD curl --silent --fail http://localhost:8080/app/ || exit 1
EOF
docker build -t $IN .

# 发布
cat << EOF > docker-compose.yaml
version: '3.7'
services:
  app:
    image: $IN
    networks:
    - global
    labels:
    - "traefik.enable=true"
    - "traefik.http.middlewares.gzip.compress=true"
    - "traefik.http.middlewares.ssl.headers.sslRedirect=true"
    - "traefik.http.routers.app_http.entrypoints=http"
    - "traefik.http.routers.app_http.rule=Host(\`api.domain.org\`)"
    - "traefik.http.routers.app_http.middlewares=gzip"
    - "traefik.http.routers.app_https.entrypoints=https"
    - "traefik.http.routers.app_https.rule=Host(\`api.domain.org\`)"
    - "traefik.http.routers.app_https.middlewares=gzip"
    - "traefik.http.routers.app_https.tls.certresolver=acme-resolver"
    - "traefik.http.services.app.loadbalancer.server.port=8080"
    # index_topic 实测不能出现下划线
    - "aliyun.logs.${index_topic}=stdout"
    deploy:
      mode: replicated
      replicas: 2
      update_config:
        parallelism: 1
        order: start-first
networks:
  global:
    name: net-global
    external: true
EOF
docker stack deploy --resolve-image never --orchestrator swarm -c docker-compose.yaml $JOB_BASE_NAME

之所以没有将 labels 放到 deploy 之下, 是因为 Traefik 是以非集群模式运行的 labelsdeploy 之下时 labels服务 的标签 labelsservice 之下时 labels容器 的标签

附录

#!/bin/sh
while true
do
current=`date "+%Y-%m-%d %H:%M:%S"`  
timeStamp=`date -d "$current" +%s` 
echo $timeStamp-`curl -I -m 1 -f http://domain:port/path -o /dev/null -s -w %{http_code}`
sleep 1
done
  1. zyx

    大佬牛批..

    回复