DockOne微信分享(二一九):跨境时尚电商SHEIN基于Kubernetes的DevOps实践
【编者的话】SHEIN PaaS参考了很多优秀的社区实践方案,最终使用了基于GitLab + Jenkins + Kubernetes + Harbor 的一套CI/CD工具集,一年多以来,总结开发出了一套公司自己的持续集成和发布的方案。本次分享着重介绍DevOps实践中几个比较值得分享的经验总结。
PaaS 平台介绍
PaaS 平台主要围绕 Dockerfile 和 DeployYml 这两个核心元数据,实现应用管理和流程管理等功能,目前完成了代码扫描,镜像构建,发布单审核,应用发布,流量控制,应用回滚,日志查看,运行状态查看,多集群管理 等功能。(Kubernetes 高可用集群搭建参考了优秀的社区项目: https://github.com/easzlab/kubeasz )
PaaS
对发布到PaaS的项目代码没有额外的限制(不需要在代码的指定路径包含 Dockerfile、Jenkinsfile 等构建依赖)。充分利用 Dockerfile 的多阶段构建功能,在 Dockerfile 中包含三个阶段:克隆、编译、运行(运行环境只包含构建结果,实现了镜像的最小化)。同时借助 PaaS 提供的基础镜像构建功能,开发人员可以将项目依赖添加到编译镜像中进行缓存,实现镜像构建加速。这样在保证构建灵活性的同时又保证了构建效率。
应用 Dockerfile 示例
因为 Dockerfile 多阶段构建功能的使用,很多构建逻辑不需要在 Jenkinsfile 实现,复杂度降低,为提高可扩展性,减少因为逻辑变更带来的维护成本,PaaS 支持传参触发构建,也支持更新 job xml 配置增加参数等,更重要的是使用 jenkins library 功能,项目构建 job 中只包含传参和函数入口调用等极少的代码,真正的处理逻辑在 jenkins library 中实现,这样在保证了可扩展性的同时又减少了维护成本。
jenkins-library 示例
通过图形化配置生成的 Deployment 等 yaml, 没有在 Jenkins 中发布,而是存储在数据库并直接调用 Kubernetes 接口部署,这样构建和发布分离,有很多使用方式上的考虑,比如,构建一次修改发布多次;只有构建没有发布(借助 Dockerfile 的多阶段构建,将编译结果发布到仓库,用于后续物理部署[特例]);快速回滚(从数据库中直接取出历史版本直接发布即可实现);蓝绿发布、流量控制等复杂逻辑(在平台代码中实现,比使用 Jenkinsfile 增加了复杂度更合适)。
配置应用
应用回滚
发布到 PaaS 上的应用,可以使用多种方式去了解服务状态,比如:查看重启次数及上一次失败原因(error或者oom),查看启动事件(了解应用为什么 pending 或者启动失败),查看控制台日志,查看落盘日志(即使服务没有成功也可以看异常日志),进入容器查看进程状状态及配置(用于测试外部依赖的连通性等)。
启动次数原因
启动事件
控制台日志查看
落盘日志查看
进入容器
因为集群的差异性,目前还需要用户在不同集群单独创建应用,才可以构建发布到不同的集群。当然,理想的状态是,更抽象的定义应用,屏蔽集群差异,任意指定集群发布。这方面还有很长的路要走。
多集群管理(参考 Wayne)
告警管理平台介绍
告警管理平台:主要围绕告警规则和告警群组进行开发。支持添加企业微信、电话、短信告警群;告警规则和抓取规则的图形化配置;支持多集群。
告警规则配置
抓取规则导入
企业微信告警
如下是集群管理员和用户日常查看的监控图:(后续将用户常看的服务指标在 PaaS 中直接展示)
集群视角
节点视角
Pod 视角
提升集群稳定性实践
在集群搭建运行一年多以来,目前已趋于稳定,期间遇到了很多问题,这里做了些能够增加集群稳定性的总结。
1、配置资源限额可以有效的提高节点和集群稳定性,需要创建应用时为每个服务添加默认资源限额。同时创建 limit-range,为每个没有指定资源配额的服务增加默认限制。
2、低版本内核存在诸多已知问题,比如,3.10 内核集群, calico node 频繁重启解决:sysctl -w net.ipv4.tcp_tw_recycle=0,目前使用内核 4.18.16 版本,稳定运行半年未发现内核相关异常。
3、Pod 长时间 terminating 状态,可能导致 kubelet 状态异常,登陆宿主机,kill -9 对应容器进程临时解决。需要添加 terminating 状态监控,并使用 my_init 改造 Dockerfile,解决因为僵尸进程等进程回收失败导致的无法删除问题。
4、真正做到平滑升级,需要服务:配置就绪探针、配置存活探针、配置 sleep 10 的终止前钩子(如果注入了 istio sidecar,sidecar 也需要配置终止前钩子)、代码处理 SIGTERM 信号主动中断连接。另外如果使用执行命令进行健康检查,所执行的命令不能太耗资源性能,否则可能导致服务异常时,节点负载过高。
5、设置 kubelet 资源预留时,谨慎配置系统资源预留,发现在 Master 节点为 kubelet 配置资源预留导致 Master 组件频繁重启故障。
6、添加节点线程数,打开文件句柄数监控,提前发现异常服务,防止主机资源耗尽。
查看进程子进程数:
find /proc/*/task -maxdepth 0 -type d |while read dir; do count=$(ls "$dir" | wc -l); echo "$dir : $count"; done
查看进程打开文件句柄数:
find /proc/*/fd -maxdepth 1 -type d | while read dir; do count=$(find "$dir" | wc -l); echo "$dir : $count"; done
Q & A
Q:请问 CI/CD 中发布镜像是以 Master 分支吗,平时开发与 Master 分支是怎么样进行管理的。如何保证镜像是从测试到生产是使用同一个镜像,保持环境的一致性?
A:项目中创建多个应用,每个应用跟分支绑定,构建时可以选择指定的 CommitID 或者 tag 版本。测试,预发布,生产环境网络是隔离的,没有做到镜像统一,但实现了测试单和发布单功能,由测试人员和项目管理员保证发布代码的规范性。
Q:这样怎么保证上线的功能是正常测试过的呢?还是通过代码比对后,保证代码一致性,预发布,生产环境重新测试?
A: 因为我们多个环境网络是强制隔离的,代码一致性目前只能通过测试人员去保证,在测试环境会生成测试单,并且构建时是可以指定构建的代码版本的,测试单和代码版本进行关联,在生产环境发布时会有发布单,这个也是要测试和项目。
Q:CI/CD 在发布时再管理 Dockerfile,还是由开发进行维护 Dockerfile 文件?需要制定哪些规范?有没有可供大家参考的?
A:Dockerfile 有 PaaS 平台统一管理,和应用关联,不和代码关联。使用多阶段构建,目前还在开发图形化编写 Dockerfile 功能,限制使用的克隆镜像,编译镜像和运行镜像,只允许在给定的基础镜像基础上进行自定义,并使用 my_init 作为启动命令,以解决僵尸进程等异常进程导致的 Pod 无法删除问题。
Q:不好意思,不太明白后面限制和删除是什么意思,能详细讲解一下吗?
A: “限制”是使用运维提供的编译和允许镜像作为基础镜像。”删除”是通过规范 Dockerfile,引入 init 系统,规避多进程容器带来的容器支持的不是太好的多进程问题。
Q:Kubernetes 的网络是采用的什么架构,有做策略吗?
A:网络目前使用的是开源项目 Calico,目前没有做网络策略限制,只是每个项目有相对独立的一组机器,简单的物理隔离。
Q:日志处理方面有没有什么好的经验,踩过哪些坑?
A:日志使用 EFK ,统一落盘到指定 cephfs storageclass,由运维配置 fluentd 配置文件,但这种方式增加了很多运维工作量,目前还在开发 fluentd-operator,实现动态配置数据源和过滤规则等。
Q:Jenkins 是使用的 Kubernetes 的组件部署,还是在用脚本或者pipeline方式进行发布的呢?怎么做的金丝雀呢?
A:Jenkins Master 部署在 Kubernetes 中,发布在 PaaS 上做,和 Jenkins 没有关联,Jenkins 只负责代码扫描和镜像构建。
Q:问一下上预生产时,代码是否已合并到 Master 了?如果是合并后上预生产时失败了,是在 Master 拉 hotfix 分支进行修复还是回滚 Mastar?
A:分支是研发自己管控,是否合并 Master 是研发自己决定,回滚也是研发自己定,分支不切。
Q: 你们应用发布到生产环境是一个应用单独发布么,如果有多个应用一起发布怎么处理?
A:目前是每个应用单独发布,已经有反馈应用关联发布,暂未实现,还在需求收集中。
Q:如何通过 EFK 监控服务的错误日志?当有错误日志时是否有相关的告警?
A: 目前监控了 Kubernetes 各组件的错误日志,在 Grafana 中做告警和展示,还未做到服务错误日志告警,另外,公司有日志中心,部分服务会将日志接到日志中心处理。
Q:请问如果基于 GitLab + Jenkins + Rancher 封装一套 CI/CD 系统这个方案可信度怎么样?需要注意啥问题?或者说有没有更好的方案?
A:前端组目前使用 GitLab Runner 做构建,如果不考虑自研的话,可以借鉴下 GitLab 的 CI 功能。
Q:集群是 kubeam 搭建的还是二进制部署?etcd 和 Master 分开的么?
A:Kubernetes 搭建参考了 https://github.com/easzlab/kubeasz ,二进制部署,可以关注下这个项目 :),三个 etcd 节点和 Master 共用。
Q:Prometheus 这边有和其它数据库结合存储么?是只使用它自带的存储么?你们一般存多久数据?
A:Prometheus 使用的是 Ceph 存储,使用联邦模式,联邦节点存 14 天,子节点存两天。
Q:磁盘 IO,网络 IO 的资源配额怎么设置?
A:磁盘 IO 有测试过修改 cgroup 实现,但目前线上还没有做。网络带宽限制 Calico 已经支持,通过配置 Annotation 就可以实现。
Q:请问服务发现怎么做的?
A:除了一些 Java 服务使用了 Consul,其他服务还是使用的 Kubernetes 的 Service 直接互相调用。
Q:请问一下,解决 Pod terminating 的具体方法是什么:
A:可以参考下: https://www.jianshu.com/p/159d34e93d42 。
Q:集群内外的服务交互怎么做的,谢谢。
A:因为使用的是开源的 Calico 网络方案,暂时没有自研,集群外部访问 Pod IP 需要在网关节点配置路由才能实现,目前还是通过 Ingress 统一出口。也还在调研其他网络方案。
Q:请问配置中心这块是如何做的?
A: ConfigMap 加 Nacos(测试环境),另外运维在开发自己的配置中心。
以上内容根据2019年8月6日晚微信群分享内容整理。分享人 王国庆,南京希音电子商务有限公司PaaS平台工程师,负责PaaS平台的搭建及维护 。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。