使用 Jenkins和容器解决之前测试环境需要测试人员按照文档全手工命令行部署,流程复杂易出错,上线部署流程不透明且不易控制的问题;
使用Jenkins和容器解决实际工作中的环境差异,统一版本;
使用Kubernetes和Ceph解决第三方数据/缓存服务的运行和数据持久化;
使用Jenkins和Kubernetes解决多环境下开发、QA、预发布(及灰度,AB)、生产等环境的部署与维护;
使用Kubernetes内置健康检查机制,更快速的发现故障容器并自动恢复,解决以往项目多点部署监控覆盖不全面(自动化)问题;
使用Kubernetes和容器来替代在物理机中运行的KVM 虚机,提高资源利用率,解决虚机创建、迁移、扩容、故障恢复等难题。
50+ PHP
20+ NodeJS
70+ Java
60+ H5
ActiveMQ
Elasticsearch
Filebeat
Grafana
Hadoop(HDFS)
Httpd,2.2、2.4
Kibana
Logstash
Memcached
MongoDB
MySQL,5.6、5.7
Nexus
Nginx
PHP,5.5、5.6, 7.1
Redis
Solr
SonarQube
Tokyotyrant
Tomcat,6.0、7.0、8.5
Zipkin
ZooKeeper
由于部分项目调用关系复杂,测试周期长,因此存在同时运行多套测试环境需求
多环境的配置
使用KVM进行虚拟化,每台物理机运行3-5个虚拟机,每虚拟机运行1-5个项目,资源利用率不高
虽然虚拟机克隆简单,但仍然是系统级别,环境与服务需要安装配置,操作管理及扩容不便
原有的上线系统为自行开发的项目,功能单一且状态不透明
原测试环境发布为测试人员SSH进入虚机纯手工命令行操作,复杂易出差错
使用Docker解决运行环境的版本与依赖
使用Kubernetes运行各种服务,通过kube-dns完成内部解析
在Kubernetes上利用不同的Namespace区分不同环境,Nginx通过监听不同IP实现环境分离
在物理机上直接运行Docker+Kubernetes
使用Jenkins统一管理所有环境任务的部署与回滚,可靠易用、自动化程度高、流程可控
重复任务都配置到Jenkins,对于授权用户可一键执行,显著减少运维处理杂务时间,特别是项目的部署和回滚,做到基本上无需运维参与
为简化流程,提供更方便易用的部署与回滚操作,将所有操作封装到Jenkins任务中
减少对Jenkins插件的依赖
大规模使用"抽象化"的Shell脚本,高度可复用,便于维护
全部使用自由风格任务
替换Jenkins默认Shell,以便能直接加载Shell文件
/data/app/jenkins/config/bash-slice
将/data/app/jenkins/config/settings注入到Jenkins任务中每个Shell脚本头部(source 方法)
同时禁用 Jenkins 默认执行Shell时的-x参数
检查与设置变量
预处理(如果需要)
编译(如果需要)
DockerfileInit函数复制Dockerfile、Deployment与Service模版文件
在Dockerfile文件基础上根据当前项目生成Dockerfile
如果模版目录内存在与项目同名文件夹则使用,否则选择默认通用模版
DockerImage Build函数构建Docker镜像
默认镜像名称为registry.xxx.com/project/subject/project-name:scm-revision
为加快Tomcat启动速度,我们直接把项目目录放到webapps目录内,而不是复制war包,这个步骤可以减少10-30秒的解压包时间
在Harbor中,每个镜像都会跟代码库中的Tag对应
DockerLogin函数先登录Registry
DockerImage Push函数推送Docker镜像到仓库(Harbor)
ReplaceKubeFile函数将Deployment与Service模版中相关值进行替换
动态设置JAVA_OPTS(-Xms -Xmx)
根据LIMIT_MEM 自动设置JAVA_OPTS变量并传递到容器内
Xms 与 LIMIT_MIN_MEM 相等
Xmx 为 LIMIT_MAX_MEM 减去128MB值
使用kubectl更新、删除、创建Deployment & Service
至此,一个任务的流程就结束了。
载入payment文件同时引入(payment文件内置)默认变量
定义变量并传递给 PaymentController 函数,覆盖默认变量
在Jenkins一个Java任务中,除git/svn设置外,仅需要输入这些脚本即可
不同Java项目之间差异通过变量进行定义
对于用户,其只是在Jenkins上选择了一个代码的Tag,鼠标再点了一下 构建,简单至极
MySQL、MongoDB、Hadoop等服务需要对数据进行持久化,为能支持迁移与恢复,持久化的数据需要在任意节点都能访问
不少项目存在配置文件与公共库文件需共享给指定某些项目使用问题,在每个节点都存放一份肯定是不现实的,因此我们需要分布式的文件系统
刚开始时我们尝试了NFS,除性能稍慢之外并无其它问题,直到某天NFS故障导致所有客户端主机卡死
为解决NFS问题,临时搭建了一个GlusterFS集群,安装配置简单方便,但因仅作为过渡阶段解决方案所以并没有太深入研究
在经过一段时间的研究和测试后,我们选定了Ceph作为分布式文件系统。
第一阶段:使用了3个节点,每节点配置普通SAS一块作为Journal,配三块600G SAS盘作为OSD,能满足基本文件需求,但无法支撑MySQL。
第二阶段:使用3个节点,每节点配置三星PRO850 512G SSD一块作为 Journal, 配三块600G SAS盘作为OSD,将集群网络与公共网络分离(全千兆网络),然后由k8s通过 rbd 和 PV/PVC提供给数据库等服务使用 在实际日常使用中监控到的数据,IOPS 最高达到25K, 读写最高120MB/s。
第三阶段:为整个公司内部提供文件存储支持。经过一次在线扩容后,节点增到5个,OSD增加到17个。利用 CephFS挂载到Linux主机,通过iSCSI挂载rbd到Windows Server,提供高可靠大容量,支持在线扩容的内部分布式文件系统。
由Filebeat转发至Redis
Logstash从Redis中读出并发送至Elasticsearch集群
使用Redis中转也是为了解决过多客户端连接Elasticsearch导致报错问题
与Zabbix结合进行监控
使用Kibana查看和分析日志
通过命令行在服务器上直接操作
通过Dashboard上进行操作, 支持移动设备
工作中会遇到项目出错需要调试的情况,不同于虚拟机,开放一个用户通过SSH就可以,在容器中进行调试存在以下问题:
默认的kubectl用户(admin)权限太高
Dashboard默认权限也太高
容器中未安装和运行sshd服务,无法通过 ssh 连接
众多Pod中如何定位项目?
经过研究,我们通过为不同项目组(对应不同的Namespace)创建各自的账号,与TLS证书和RBAC权限组合,实现集群的权限控制,让开发者可以通过 kubectl 查看(get)Namespace中的 Pods, Services, Deployments等等资源,并可以通过 kubectl exec 进入容器执行命令。
RBAC for use java
Certificate for user java
容器以普通用户运行,需要sudo的操作在Jenkins生成Dockerfile过程中写入sudoers,限定操作,在启动后无法通过sudo/su等方式切换到root,因此可以一定程度的提高安全性
容器内项目文件为root所有且只读,指定的目录可写,容器运行用户与容器内服务运行用户分离,确保项目的代码文件也无法进行更改
在Harbor中按项目组新建私有Project, 创建一个Guest用户 web
Deployment 中增加 imagePullSecret,在namespace中创建 secret 使用 web 用户账号信息拉取镜像
因业务需要,容器启动后一些文件和目录需要修改权限:
/etc/hosts 文件需要替换
项目代码为 root 所以,httpd 以nobody用户运行,因此一些目录(如缓存等)需要nobody可写
为何选择sudo:
在镜像构建完成即同时设置了sudo权限,容器启动后即无法更改
严格限制,仅对必须执行的命令进行完整匹配的授权
除明确允许命令外一概无法通过sudo执行
Line5 - 15, 仅允许sudo cp -rf /tmp/hosts /etc/hosts, 因此:
先下载hosts文件保存为 /tmp/hosts
将当前/etc/hosts 附加到 /tmp/hosts
sudo cp -rf /tmp/hosts /etc/hosts 进行替换
Line17 - 32,检查全局变量WRITEABLE_DIRS不为空时,使用循环更改目标目录为nobody所有。
Prometheus 的结合
自动发现,监控与通知等还在研究
使用Zabbix进行监控
从外部监控 Service
监控 Deployments 状态,DESIRED、CURRENT与AVAILABLE之间的比例
监控Pod运行状态,采集重启次数过于频繁(AGE与RESTARTS比例)的Pod信息
目前我们的测试环境已经全部做到了容器化;
通过测试环境的长期运行与不断完善,生产环境容器化也正在进行中。
上线流程
在生产环境服务不中断、不增加机器和机柜的条件下完成
使用乾坤大挪移,先腾出一批机器搭建基本集群
然后逐批迁入服务至容器,与现有虚机服务并行运行
经过一段时间观察,逐步退出虚机服务,将腾出的机器重装系统后加入Kubernetes集群
同时将Ceph节点与Kubernetes API Server分布在不同机柜内,防止机柜电源/交换机故障
扩容的集群继续迁入更多服务,如此往复,最终完成平滑过渡
线下Harbor向线上Harbor进行复制
测试环境构建的镜像测试通过后直接应用到生产环境,挂载本环境配置文件即可
完善健康检查设置,调整资源限制(resources),防止资源超售可能导致的节点雪崩效应
继续完善脚本
研究Pipeline流水线
版本升级
更深入的研究
集群优化与分离