cicd流程设计,使用jenkins+argocd完成cicd流程。

设计思路

该cicd流程会以argocd这个持续部署的工具作为中心,沿用该工具里面大部分的概念,那我们先看看argocd的里面的概念。

argocd概念

  • cluster: 定义集群信息,其中apiserver访问地址会作为唯一标识
  • repository: 定义k8s资源/模板文件所在仓库(并非应用源代码所在仓库)
  • project: 定义项目,可以做一些限定
    • destination: cluster + namespace组成
    • source repository
    • 资源黑白名单
    • role
  • application: 具体运行的应用了,它本身与应用的代码仓库并没有直接关系,仅仅代表一个可独立运行的实例

application名字定义

  • 部门 + 环境 === namespace
  • 集群
  • 应用名

情况:

  • 一个集群多测试环境
  • 一个环境多集群
  • 同名应用属于不同部门

名字格式: [部门]-[集群代号]-[环境]-[应用名]

集群代号列表

集群名 集群代号
wekube-prod wkbp

流程

创建application流程

假设最终运行的application是yunwei-wkbp-prod-ultron-api,那么它是这么来的:

  • 应用名: ultron-api
  • 部门: yunwei
  • 集群code: wkbp
  • 环境: prod

规定/约束:

  • [部门] === project
  • [部门]-[环境] === namespace
  • 除了应用名,其他部分不允许包含特殊符号

最终用户使用流程将变为:

输入应用名 >>> 选择部门 >>> 选择部门所允许的集群code以及环境

注:前期少数资源的创建均通过手动方式

构建流程

不同的应用类型,构建方式不同。

构建类型

  • java: java类型的应用,一般选用maven构建工具,构建环境需要维护maven本地仓库的缓存,加速构建
  • nodejs: npm包管理工具,需要缓存node_modules,加速构建
  • golang: 如果使用govendor,项目本身自带所有依赖,无需管理包;如果使用go mod等,需要维护包缓存

构建环境

使用jenkins on k8s,如果是默认策略,agent是作为cloud资源,即用即起。构建任务需要等待agent初始化,依赖于基于共享存储的包缓存管理;我们也可以使用agent池,携带本地存储

自主系统

应用发布流程

  1. 使用gitlab的tag触发webhook,一旦有新tag,就发送webhook到ultron平台
  2. webhook接口解析repo信息,解析得到项目信息,触发jenkins构建打包推送应用docker镜像,并且发布

注:针对多服务仓库,无法使用tag触发某单一模块构建

init创建流程

  1. 添加webhook
  2. 创建jenkins的job
  3. 创建argocd的应用
  4. 初始化argocdrepo应用对应的values文件

应用如何有效的划分

  • 概念:部门、业务线、环境、应用组/应用

以上组成目录结构:

部门/
└── 业务线
    ├── 测试
    │   └── 应用组-应用
    └── 生产
        └── 应用组-应用

需求

  • 前期支持java的jar包以及tomcat类型,构建方式为maven,支持多应用git仓库
  • 采用先激活注册,后webhook调用的方式(部分信息无法直接从repo中获取,需要手动绑定)
  • Jenkinsfile + Dockerfile组合的方式管理构建,鉴权信息由jenkins统一管理
  • 提取不同应用的helm模板

DO

应用需要:

  • git地址
  • 项目类型 –> 选择构建方式,部署模板
    • Java-app
    • java-tomcat
    • node-web
    • node-frontend
    • go
  • 部署应用名 –> data-wkbp-prod-apiservices-blacklist
    • 部门名 -> data
    • 集群名 -> wkbp
    • 环境 -> prod
    • 应用组名 -> apiservices, 应用名最终为: 应用组名-模块名
    • 模块名 -> backlist
  • 是否为多模块构建
  • 是否支持webhook触发
    • 如果是多模块,默认不支持webhook触发,(如果在tag中携带模块名,也是可以支持)
    • 如果是单模块,默认webhook触发构建
  • 包路径 -> target/blacklist.jar

安排

2019-09-09

  • 分类
  • 数据库表结构设计
  • Jenkinsfile以及Dockerfile模板准备
  • helm模板准备

待定

问题

1. 是否直接使用Docker自身的多阶段构建?

针对java的打包,需要依赖于maven仓库,需要解决本地mavne仓库存储的问题,如果每次都从公司仓库拉取,效率过低,无法忍受。

同样,python以及node.js也有这个问题。

最好的情况就是build阶段包不更新,这时候python和node.js不会再次拉取包,但是java依然需要重新生成构建层,无法跳过。

以上问题使得多阶段构建不太优雅。

总结:包缓存目录必须能够跟构建命令同一环境

比如使用构建容器构建,构建时能够挂载宿主机包缓存目录(分布式存储/非分布式存储),现成的工具,就是jenkins

2. 应用无法平滑上线,部分请求502

traefik这里没有根据code做失败重试的逻辑,发版的平滑依赖于readinessProbepreStop两个阶段,而业务开发在这方面的意识薄弱,不太会去管优雅启停的问题,比如应该提供健康监测接口,在完成预备阶段(启动,预热等)后更改接口状态为ready,再比如关闭时,应该能正确捕获部分信号,做一些收尾工作,然后停止。如果开发不做,我们可以先通过sleep特定时间后,再停止应用,比如:

# 这里15秒需要结合具体应用,一般为最慢任务完成周期
lifecycle:
  preStop:
    exec:
      command:
        - "sleep"
        - "15"

Pod关闭流程为:

  1. 将Pod对应的Endpoint从Service的Endpoints列表中移除
  2. 执行preStop阶段(如果有),同时terminationGracePeriodSeconds开始计时
  3. Pod的容器应用接收SIG_TERM信号,停止应用
  4. 如果2和3阶段执行时间超过terminationGracePeriodSeconds,那么会强制kill,该值默认30s

视频: gcs最佳实践

3. 容器内直接调整sysctls,会报错

注:容器container,宿主机host

container和host默认情况下,子系统是隔离的,自然相关的sysctls部分参数也是隔离的,称之为namespaced,主要以下几类:

  • kernel.shm*
  • kernel.msg*
  • kernel.sem
  • fs.mqueue.*
  • net.*(部分)

为了保证host安全,sysctls中分为safe sysctlsunsafe sysctls,safe的可以直接在pod中更改,而unsafe的,则需要在kubelet启动参数中允许,才能够设置,否则对应的pod并不会启动。

kubelet --allowed-unsafe-sysctls \
  'kernel.msg*'

目前,1.14版本,safe的有:

  • net.core.somaxconn(文档上没有,但是实际可设,issue中有提到)
  • net.ipv4.ip_local_port_range
  • net.ipv4.tcp_syncookies

  • kernel.shm_rmid_forced

修改方式:

1. 添加initContainers:

       initContainers:
       - command:
         - sh
         - -c
         - sysctl -e -w net.core.somaxconn=32768;  sysctl -e -w net.ipv4.ip_local_port_range="1025 65535";
         image: alpine:3.6
         imagePullPolicy: IfNotPresent
         name: sysctl
         resources: {}
         securityContext:
           privileged: true

2. 使用注解的方式