ofo 基于 K8S 容器云平台的实践
技术
作者:王强
译者:小君君
2018-08-14 10:54

近几年随着容器技术生态日趋成熟,国内外互联网以及传统 IT 企业都积极拥抱容器技术,并在测试以及生产环境中大量使用容器化部署,容器技术降低了 IT 运维成本,缩短了业务交付周期。基于主流容器编排技术(Docker/Kubernetes)的容器云平台集上线发布,容器管理,弹性伸缩,资源监控等功能的 PaaS 平台正逐渐流行起来。 

今天讲的内容分为两部分。第一部分分享 ofo 基于 Kubernetes 开发的 PaaS 平台的功能简介。第二部分是关于容器化落地的案例。

容器化平台 

如今国内已经有很多容器云平台产品,但结合我们自身需求的迫切度,放弃 OpenShift  而采用独立开发,主要是基于对 PaaS 代码、实现自主可控性和对底层  Kubernetes/Docker 的优化定制。


 Ruly PaaS 主要功能  

系统资源 Dashboard,通过这里可以查看集群内总体资源使用概览,以及具体节点资源使用情况。Kubernetes 集群、Namespace 管理是做相关的 Kubernetes 集群及  Namespace 的基本管理。通过 Ruly PaaS 可以管理多个 Kubernetes 集群,当前 Ruly PaaS 管理了 4 个 Kubernetes 集群,包括国内及海外生产/测试集群。

容器化的  CI/CD,底层实现基于 Jenkins Jnlp CI/CD 开发的 API 服务,将 Git 代码打包成 Docker image 镜像,作为容器应用自动部署在 Kubernetes 指定集群里。应用服务和分布式任务管理是基于业务的容器化落地相关需求来实现。统一配置管理是基于 Kubernetes ConfigMap 做的相关应用配置服务,主要是管理线上业务的一些配置文件,比如:Nginx 的配置文件,业务日志收集 sidecar flume agent 的配置文件等。

最下面是资源审计/账号分组管理,以及操作审计。我们会根据角色 QA、运维,以及各个不同的研发 Team 作相关账号分组。每个相关部门的同学只能访问到指定的应用。操作审计除了在 PaaS 中的基本操作会做审计记录之外,在 PaaS  WebShell 中对容器操作也会记录下来。

PaaS 架构以及主要功能  

这是 PaaS 的整体架构,从基础层往上层开始做。从最开始做的基础架构选型、容器宿主机选型、操作系统选型优化、Kubernetes/Docker 选型及相关压测优化等。同时还包括搭建底层的基础服务。比如:镜像仓库集群,包括国内和海外的镜像 Replica 同步机制等。 

我们目前并行在做自有 IDC 机房的容器基础设施适配工作。上层工作流层,主要是 CI/CD 功能部分,是 Ruly PaaS 的主要功能模块。代码审计功能主要是集成了 SonarQube 作业务代码扫描。持续交付,默认  Kubernetes Rolling Update 方式。 

运维模块,通过 PaaS 中的应用及 Pod 事件列表,可以看到某个具体业务应用上线/扩缩/健康检查失败/OOM 等事件,并根据这些事件作报警。弹性伸缩除支持基于 Kubernetes  HPA 容器扩缩之外,还支持集群宿主机节点的动态扩容。最上面一层,主要是做服务治理,APM 全链路监控。 

CI/CD 功能 

目前应用业务的编程语言种类主要有:Node 8、Go、PHP 7、Java 8、C++等。为方便开发者快速掌握 Dockerfile 编写,抽象出这些编程语言的 Dockerfile 模板。开发者在做自己业务的容器化部署时可以选择相应的模板,去补全相应的模板参数,比如:部署路径,是否可以作业务日志搜集。也可以完全手写一个完整的 Dockerfile。应用类型,目前支持的是 Kuberneetes  Deployment 和 Statefulset,默认业务以无状态应用为主,少量业务为有状态应用。 

线上应用可以配置指定容器副本数,CPU 及内存资源限制,环境变量等。对于 Node 8 或者 Go 业务,环境变量能为测试或生产集群指定相应的环境变量。业务根据其寻找相应的配置。健康检查策略主要是利用 Kubernetes 标准 HTTP GET、TCP 端口探活,自定义 SHELL 命令行作容器的健康检查。应用业务上线有一些配置文件。Ruly PaaS 将配置文件导入 PaaS 的配置中心。在 CI/CD 时将该应用与配置项做关联。 

上线部署 

上线部署时运维/开发或 QA 同学输入指定 commit id,作构建及部署。底层构建的镜像 Tag 主要由自增 ID、集群 ID、commit ID 构成。镜像构建完成后上传到镜像仓库,请求 Ruly PaaS CDAPI 向指定 Kubernetes 集群中部署。 

上线部署方式除支持默认的 RollingUpdate 外,还支持“手动部署”:在部署完一个新容器后,检查当前业务日志,在 PaaS 中手动确认上线下一个容器或其余副本。“金丝雀”:部署方式是等量副本数部署新版本业务,全部将线上流量切到新版本业务容器中。 

线上业务容器化后,我们会配置自动扩缩策略,先根据 CPU/内存一些经验值作为阈值,运行一段时间后观察负载情况做一些具体阈值调整,包括副本数。 

资源审计 

 提供集群,Namespace 以及项目等维度资源的使用情况,主要是为容器或宿主机的升降配,扩缩等提供参考。 

高级功能 

这里主要是容器化落地。而后会有一些定制化的需求,比如前面介绍的部分配置中心功能,基于 Kubernetes ConfigMap 实现,增加了版本控制便于回滚。

服务定义  

基于 Kubernetes SVC 来实现。大部分 http/gRPC 型业务是为了保证性能最佳,都是以 4 层服务的形式导出。使用 Kubernetes svc spec external TrafficPolicy: Local 方式,不对客户端 IP 做 SNAT。 

第一,性能最优最稳定。第二,后端容器可以拿到客户端的真实 IP,为指定应用定义服务,导出服务 IP 和端口后,下游业务只需访问这个服务 IP 加端口即可,而无需关心后端有多少个容器。关于 SVC SNAT 模式下带来的稳定性问题社区有过反馈,说一下我们采用的解决方案。 

在去年 12 月选型做基本测试时发现这个问题:单一并发短连接对  SVC 后端 Nginx 容器压测,约 2 分钟后就会有 1 至 2 笔 1 秒 到 3 秒的请求,同时客户端会有 5** 的响应码。我们通过对容器内及宿主机进行抓包分析,发现经过宿主机上 SNAT 后到容器内,出现了 Socket tuple5 元组重合的问题导致了 TCP 重传。使用 4 层 LB 做为 Kubernetes SVC。同时 external Traffic Policy:Local 方式,所有向外部暴露的服务都是采用这种方式。 

流量复制 

我们 QA 和安全部门的同学需要一种方案可以模拟线上真实流量复制到测试的容器集群中。对于安全团队来说,需要做线上流量旁路监听,流量复制。目前我们的内部版本是 v0.1-alpha,这一版基于 Envoy 实现,存在一些弊端:无法做流量翻倍,由于引入一层 Envoy 代理,性能有所损失,无法做全流量复制,只能复制某一个或几个 Pod 流量。 

Webshell 

这是从 PaaS 0.1 实现的功能,便于开发者连接容器,做实时 Debug。这种查看方式与原有 ECS 部署差不多,用于看线上实时输出日志,进程状态等。 

分布式任务 

这是基于 Kubernetes 原生的 Cronjob 和 job 做的,跑一些分布式任务。 

特权容器(privileged)

主要用于优化容器性能以及容器问题诊断,比如 TCP listen backlog 需要特权容器模式对该容器配置系统参数。另外,特权容器内可以运行 strace 以及类似的诊断工具对容器进程性能做诊断分析。

初始化容器(initContainers)  

Ruly PaaS 对 Kubernetes initContainers 的支持结合了安全因素以及性能优化因素。特权容器,可以修改容器本身的系统参数(sysctl),以及运行一些类 strace 的诊断工具。 

一个业务容器,本身在特权容器模式下是有风险的,遵循最小权限原则。我们的做法是在业务容器内的 initContainers 阶段,执行系统参数优化脚本。执行完成后,实际业务就集成在这优化过的系统环境中。该业务容器不必再使用特权模式。

主机网络 

在业务容器化迁移过程中,适配现有的自注册架构业务而采取的方案。这种架构业务是基于 Zookeeper 或 Spring Cloud Eureka 来做服务发现。业务服务启动时要把自己的 ip 及端口注册在服务注册中心,供下游访问。由于迁移过程中,下游业务没有立即容器化,还是运行在原来的 ECS 宿主机上。这需要在不更改业务服务代码的前提下,采用主机网络模式启动。该模式下业务注册的 ip 及端口可以被下游非容器化业务直接访问。 

容器及基础设施优化 


硬件系统选型,大部分用 16 核/32 G 的虚机作为 Kubernetes 的节点。随着大量后端业务容器化后,我们也采用一些 32 核/64 G 的虚拟机。 

操作系统相关优化最低标准是单机支持 TCP 的百万并发,借助内核本身的优势,主要是优化一些内核参数配置:/ect/sysctl.conf、/etc/security/limits.conf、TCP、iptables、arp表、句柄数等参数限制(net.nf_conntrack_max,net.core.somaxconn,fs.file-max,net.ipv4.neigh.default.gc_thresh*等),对于 limits.conf 文件主要是优化非 root 权限文件句柄数(nofile),进程数(nproc)等。以此便于容器内 www 权限应用(nodejs/go/java)能尽量使用最优性能的基础配置。对于宿主机操作系统的优化,我们编写了优化脚本,以统一打包作为宿主机操作系统的镜像。 

Kubernetes 容器层面优化 

我们包括基础镜像的制作(本地化,如东八区时区)。性能优化方面,我们把容器内的优化脚本也打包到了基础镜像中,便于 initContainers 阶段执行。运行 go/node/java  业务的基础镜像是基于 Alpine Linux3.7、3.8 定制。 

还有其他优化,比如 Go 程序默认  TLS 握手时,本地默认要有一个证书 CA 环境。常规方式是安装一个 ca-certificates 包,容器化后 Go 业务做 TLS 握手,会扫描包里所有的证书文件,大概 30 个以上的文件 io 操作。实际只需要一个 CA 证书,所以我们把基础镜像中其他无用证书都删除。 

CentOS 7.3 主要是用来运行复杂的 C++ 业务,还有 PHP 7 业务场景。使用 strip 工具对编译好的二进制文件裁剪,使镜像保持最小。后续也会把这部分基础镜像用 Alpine Linux 来代替。 

kube-dns优化 

目前使用原生的 kube-dns 做容器 DNS 解析。最开始的方式是 HPA 自动扩缩。在自动缩容时,会增大业务响应时间,导致依赖的 DNS 业务超时。目前采用的方式是 kube-dns-autoscaler,它可以根据整个集群 CPU 核数,还有 Pod 数去做相应的比例增加。优化方面,主要是增大 cache-size,设置 neg-ttl,对无效的 DNS 设置一个缓存,增大 DNS 进程的 CPU/memory。 

日志的自动清理,以 DaemonSet 形式进行部署,在做容器化业务上线时,对在 PaaS  中的环境变量配置要清理的日志路径和日志文件保留时长。我们会定时清理有业务日志。

业务容器优化  

对于 GO 业务来说,GO 自身的 MPG 模型是根据 GOMAXPROCS 相关值来启动具体的工作线程。但 GO 容器化后,按照所在宿主机的 CPU 核数来启动工作线程。不是实际为该容器分配 CPU 核数,在有些业务场景下会出现更多的 CPU 争用。例如高 iowait,当我们在 PaaS 中设置环境变量 GOMAXPROCS。该值为容器指定具体的分配核数后,我们业务的 QPS 会提升一倍以上,平均响应时间会下降到原来的 1/3 甚至更多。宿主机上的 CPU iowait 也会降至正常水平。对于 Java 业务,它的新版 JDK 8 或 10 都提供一些相关参数(如:-XX:+UnlockExperimentalVMOptions),可以智能感知到 cgroup 的 CPU/memory 限制。 

业务容器化案例 

我们的一些容器化案例:API 业务基本无状态,对接 APP 端。有用户中心、位置服务、配置服务还有一些网关服务等。基于 node/go 开发,内部会使用 gRPC 方式访问底层依赖服务。 

gRPC 服务相当于在 API 服务后的服务。基于现在的 ZK 服务发现机制,要求容器启动后向 ZK 注册,容器销毁时需要反注册。这里涉及到业务容器安全退出问题,业务代码需要做信号(SIGINT/SIGTERM)处理。这个反注册和之前的 ECS 部署方式一致,容器化后不需要再修改代码。 

长连接服务 

我们的长连接服务有 APP 推送服务和 IoT 锁网关接入层服务。APP 推送是 6 月初全量容器化部署。包括 TCP 接入层,会话层,以及底层的持久化存储层。目前,并发连接数是百万级别,APP 推送的接入层是由 3 个 4 核 8G 的容器承载。这个 APP 推送分布式压测工具也是容器化部署。 

定时任务,利用分布式离线计算来做日志的离线分析。统一从 Redis 去读分布式任务分片,做日志文件下载,处理后会汇到 Kafka/Hbase 中。 

实时任务和实时日志分析是利用上游消息队列。Kafka,实时处理后发布到下游也有类似的消息队列服务。其他业务是一些常规性的 Daemon 后台服务,做一些数据格式拉取,再转换写入到另外一个持久化存储中去。 

结语 

我们的 Ruly PaaS 自上线以来,目前支撑了约 90% 的容器日常管理工作(上线部署,弹性扩缩,监控等),容器化的宿主机资源利用率提升了约 3 倍。后续为应对逐渐增多的有状态业务,以及企业微服务架构的容器化落地,我们会在 Ruly PaaS 迭代过程中,集成有状态存储的管理,ServiceMesh 等高级功能。谢谢大家! 


 王强  / ofo 容器云研发负责人

14 年以上互联网、安全、互金等开发架构经验,专注于基础架构服务设计实现。

 

361 comCount 0