查看原文
其他

Qunar 云原生容器化落地实践

The following article is from 青云技术社区 Author 邹晟


作者|邹晟 去哪儿网基础平台技术专家

背景

近几年,云原生和容器技术非常火爆,且日趋成熟,众多企业慢慢开始容器化建设,并在云原生技术方向上不断的探索和实践。基于这个大的趋势, 2020 年底 Qunar 也向云原生迈出了第一步——容器化。

云原生是一系列可以为业务赋能的技术架构准则,遵循它可以使应用具有扩展性、伸缩性、移植性、韧性等特点。云原生也是下一代技术栈的必选项,它可以让业务更敏捷。通过实践 DevOps、微服务、容器化、可观测性、反脆弱性(chaos engineering)、ServiceMesh、Serverless 等云原生技术栈,我们便可以享受到云原生带来的技术红利。


Qunar 容器化发展时间线

一项新技术要在企业内部落地从来都不是一蹴而就的,Qunar 的容器化落地也同样如此。Qunar 的容器后落地主要经历了 4 个时间节点: 

  • 2014 - 2015:

    业务线同学开始尝试通过 Docker、Docker-Compose 来解决联调环境搭建困难的问题,不过由于 Docker-Compose 的编排能力有限、无法解决真实的环境问题,因此容器化最后也没有推行起来。

  • 2015 - 2017: 

    ops 团队把为了提高 ELK 集群的运维效率,把 ES 集群迁移到了 Mesos 平台上。后来随着 K8s 生态的成熟,把 ES 集群从 Mesos 迁移到了 K8s 平台,运维效率得到了进一步的提升。

  • 2018 - 2019: 

    在业务需求不断增加的过程中,业务对测试环境的交付速度和质量有了更高的要求,为了解决 MySQL 的交付效率问题( 并发量大时,网络 IO 成为了瓶颈,导致单个实例交付时长在分钟级),为了解这个问题,我们把 MySQL 容器化,通过 Docker on host 的模式可以在 10 秒之内就可以交付一个 MySQL 实例。

  • 2020 - 2021:  

    云原生技术已经非常成熟了,Qunar 也决定通过拥抱云原生来为业务增加势能。在各个团队齐心协力的努力下,300+ 的 P1、P2 应用已经完成了容器化,并且计划在 2021 年年底全部业务应用实现容器化。


落地过程与实践

容器化整体方案介绍


Qunar 在做容器化过程中,各个系统 Portal 平台、中间件、ops 基础设施、监控等都做了相应的适配改造,改造后的架构矩阵如下图所示。

  • Portal:Qunar 的 PaaS 平台入口,提供 CI/CD 能力、资源管理、自助运维、应用画像、应用授权(db 授权、支付授权、应用间授权)等功能。

  • 运维工具:提供应用的可观测性工具, 包括 watcher(监控和报警)、bistoury Java 应用在线 Debug)、qtrace(tracing 系统)、loki/elk(提供实时日志/离线日志查看)。

  • 中间件:应用用到的所有中间件,mq、配置中心、分布式调度系统 qschedule、dubbo 、mysql sdk 等。

  • 虚拟化集群:底层的 K8s 和 OpenStack 集群。

  • Noah:测试环境管理平台,支持应用 KVM/容器混合部署。


CI/CD 流程改造


CI/CD 改造前

CI/CD 改造后

主要改造点:

  1. 应用画像: 把应用相关的运行时配置、白名单配置、发布参数等收敛到一起,为容器发布提供统一的声明式配置。

  2. 授权系统: 应用所有的授权操作都通过一个入口进行,并实现自动化的授权。

  3. K8s 多集群方案: 通过调研对比,KubeSphere 对运维优化、压测评估后也满足我们对性能的要求,最终我们选取了 KubeSphere 作为多集群方案。


中间件适配改造


改造关注点:由于容器化后,IP 经常变化是常态,所以各个公共组件和中间件要适配和接受这种变化。


应用平滑迁移方案设计


为了帮助业务快速平滑地迁移到容器,我们制定了一些规范和自动化测试验证等操作来实现这个目标。

  1. 容器化的前置条件: 应用无状态、不存在 post_offline hook(服务下线后执行的脚本)、check_url 中不存在预热操作。

  2. 测试环境验证: 自动升级 SDK、自动迁移。我们会在编译阶段帮助业务自动升级和更改 pom 文件来完成 SDK 的升级,并在测试环境部署和验证,如果升级失败会通知用户并提示。

  3. 线上验证: 第一步线上发布,但不接线上流量,然后通过自动化测试验证,验证通过后接入线上流量。

  4. 线上 KVM 与容器混部署:保险起见,线上的容器和 KVM 会同时在线一段时间,等验证期过后再逐步下线 KVM。

  5. 线上全量发布: 确认服务没问题后,下线 KVM。

  6. 观察: 观察一段时间,如果没有问题则回收 KVM。


容器化落地过程中碰到的问题

如何兼容过去 KVM 的使用方式,并支持 preStart、preOnline hook 自定义脚本?


KVM 场景中 hook 脚本使用场景介绍:

  • preStart hook : 用户在这个脚本中会自定义命令,比如环境准备。

  • preOnline hook:用户会定义一些数据预热操作等,这个动作需要在应用 checkurl 通过并且接入流量前执行。

问题点:

K8s 原生只提供了 preStop、postStart 2 种 hook, 它们的执行时机没有满足上述 2 个 KVM 场景下业务用到的 hook。

分析与解决过程:

  1. preStart hook在 entrypoint 中注入 preStart hook 阶段,容器启动过程中发现有自定义的 preStart 脚本则执行该脚本,至于这个脚本的位置目前规范是定义在代码指定目录下。

  2. preOnline hook由于 preOnline 脚本执行时机是在应用 checkurl 通过后,而应用容器是单进程,所以在应用容器中执行这个是行不通的。而 postStart hook 的设计就是异步的,与应用容器的启动也是解耦的, 所以我们初步的方案选择了 postStart hook 做这个事情。实施方案是 postStart hook 执行后会不断轮询应用的健康状态,如果健康检测 checkurl 通过了, 则执行 preOnline 脚本。脚本成功后则进行上线操作, 即在应用目录下创建 healthcheck.html 文件,OpenResty 和中间件发现这个文件后就会把流量接入到这个实例中。

按照上面的方案,Pod 的组成设计如下:


发布过程读不到标准输入输出


场景介绍:

在容器发布过程中如果应用启动失败,我们通过 K8s API 是拿不到实时的标准输入输出流,只能等到发布设置的超时阈值,这个过程中发布人员心里是很焦急的,因为不确定发生了什么。如下图所示,部署过程中应用的更新工作流中什么都看不到。

问题点:

K8s API 为什么拿不到标准输入输出?

分析与解决过程:

  1. 通过 kubectl logs 查看当时的 Pod 日志,什么都没有拿到,超时时间过后才拿到。说明问题不在程序本身,而是在 K8s 的机制上;

  2. 查看 postStart Hook 的相关文档,有一段介绍提到了 postHook 如果执行时间长或者 hang 住,容器的状态也会 hang 住,不会进入 running 状态, 看到这条信息,大概猜测到罪魁祸首就是这个 postStart hook 了。

基于上面的猜测,把 postStart hook 去掉后测试,应用容器的标准输入可以实时拿到了。

找到问题后,解决方法也就简单了,把 postStart hook 中实现的功能放到 Sidecar 中就可以解决。至于 Sidecar 如何在应用容器的目录中创建 healthcheck.html 文件,就需要用到共享卷了。新的方案设计如下:

使用上述方案后,发布流程的标准输入输出、自定义 hook 脚本的输出、Pod 事件等都是实时可见的了, 发布过程更透明了。


并发拉取镜像超时


场景介绍:

我们的应用是多机房多集群部署的,当一个应用的新版本发布时,由于应用的实例数较多,有 50+ 个并发从 harbor 拉取镜像时,其中一些任务收到了镜像拉取超时的报错信息,进而导致整个发布任务失败。超时时间是 kubelet 默认设置的 1 分钟。

分析与解决:

通过排查最终确认是 harbor 在并发拉取镜像时存在性能问题,我们采取的优化方案是通用的 p2p 方案,DragonFly + Harbor。


并发大时授权接口抗不住


场景介绍:

应用发布过程中调用授权接口失败,K8s 的自愈机制会不断重建容器并重新授权,并发量比较大,最终把授权服务拖垮。 

我们的容器授权方案如下:

  1. Pod init 容器启动时进行调研授权接口进行授权操作,包括 ACL 和 mysql 的白名单。

  2. 容器销毁时会执行 Sidecar 容器的 preStop hook 中执行权限回收操作。

问题点:

ACL 授权接口涉及到了防火墙,QPS 比较低,大量容器进行 ACL 授权时把服务拖垮 。

分析与解决:

为了解决上述的问题,限量和降低授权接口调用次数是有效的解决方式。我们采取了下面几个措施:

  1. init 容器中的重试次数限制为 1 次。         

  2. 授权接口按应用和 IP 限流, 超过 3 次则直接返回失败,不会再进行授权操作。           

  3. ACL 中涉及的一些通用的端口,我们统一做了白名单,应用无需再进行授权操作。


Java 应用在容器场景下如何支持远程 Debug


KVM 场景 Debug 介绍:

在开发 Java 应用的过程中,通过远程 Debug 可以快速排查定位问题,因此是开发人员必不可少的一个功能。Debug 具体流程: 开发人员在 Noah 环境管理平台的界面点击开启 Debug, Noah 会自动为该 Java 应用配置上 Debug 选项,-Xdebug -Xrunjdwp: transport=dt_socket, server=y, suspend=n, address=127.0.0.1:50005,并重启该 Java 应用,之后开发人员就可以在 IDE 中配置远程 Debug 并进入调试模式了。

容器场景的 Debug 方案:

测试环境的 Java 应用默认开启 Debug 模式,这样也避免了更改 Debug 重建 Pod 的过程,速度从 KVM 的分钟级到现在的秒级。当用户想开启 Debug 时,Noah 会调用 K8s exec 接口执行 socat 相关命令进行端口映射转发,让开发人员可以通过 socat 开的代理连接到 Java 应用的 Debug 端口。

问题点:

容器场景下在用户 Debug 过程中,当请求走到了设置的断点后,Debug 功能失效。

分析与解决过程:

  1. 复现容器场景下 Debug,观察该 Pod 的各项指标,发现 Debug 功能失效的时候系统收到了一个 liveness probe failed,kill pod 的事件。根据这个事件可以判断出当时 liveness check 失败,应用容器才被 kill 的,应用容器重启代理进程也就随之消失了,Debug 也就失效了。

  2. 关于 Debug 过程 checkurl 为什么失败的问题,得到的答案是 Debug 时当请求走到断点时,整个 JVM 是 hang 住的,这个时候任何请求过来也会被 hang 住,当然也包括 checkurl,于是我们也特地在 KVM 场景和容器场景分布做了测试,结果也确实是这样的。

  3. 临时解决方案是把断点的阻断级别改为线程级的,这样就不会阻断 checkurl 了, idea 中默认的选项是 Suspend All,改为 Suspend Thread 即可。不过这个也不是最优解,因为这个需要用户手工配置阻断级别,有认知学习成本。

  4. 回到最初的问题上,为什么容器场景下遇到这个问题,而 KVM 没有,主要是因为容器场景 K8s 提供了自愈能力,K8s 会定时执行 liveness check, 当失败次数达到指定的阈值时,K8s 会 kill 掉容器并重新拉起一个新的容器。

  5. 那我们只好从 K8s 的 liveness 探针上着手了,探针默认支持 exec、tcp 、httpGet 3 种模式,当前使用的是 httpGet,这种方式只支持一个 url, 无法满足这个场景需求。经过组内讨论, 最后大家决定用这个表达式 (checkurl == 200) || (socat process && java process alive) 在作为应用的 liveness 检测方式,当 Debug 走到断点的时候, 应用容器就不会阻断了, 完美的解决了这个问题。

以上就是我们落地容器化过程中遇到的几个问题与我们的解决思路。其中很重要的一点是从 KVM 迁移到容器时需要考虑用户的使用习惯、历史功能兼容等要点,要做好兼容和取舍,只有这样容器化落地才会更顺畅。

未来展望

多集群稳定性治理

  1. 让可观测性数据更全面、覆盖度更广,进而完善我们的 APM 系统,提升排查问题效率。

  2. 通过实施混沌工程来验证、发现和消除容器化场景的稳定性盲区。

提高资源利用率

  1. 根据业务指标实现弹性扩缩容。

  2. 根据应用的历史数据智能的调整 requests。

ServiceMesh 方案落地

我们是基于 Istio 和 MOSN 以及当前的基础架构做的 mesh 方案,目前在测试阶段,这套方案落地后相信会让基础架构更敏捷。


推荐阅读


中国移动基于 Kubernetes 的物联网边缘计算应用实践
KubeSphere 团队招聘微服务研发、微服务解决方案架构师、资深容器产品市场经理
KubeSphere 社区 2021 年贡献者提名公开征集

关于 KubeSphere

KubeSphere (https://kubesphere.io)是在 Kubernetes 之上构建的开源容器混合云,提供全栈的 IT 自动化运维的能力,简化企业的 DevOps 工作流。

KubeSphere 已被 Aqara 智能家居、杭州数跑科技、本来生活、新浪、华夏银行、四川航空、国药集团、微众银行、紫金保险、中通、中国人保寿险、中国太平保险、中移金科、Radore、ZaloPay 等海内外数千家企业采用。KubeSphere 提供了开发者友好的向导式操作界面和丰富的企业级功能,包括多云与多集群管理、Kubernetes 资源管理、DevOps (CI/CD)、应用生命周期管理、微服务治理 (Service Mesh)、多租户管理、监控日志、告警通知、审计事件、存储与网络管理、GPU support 等功能,帮助企业快速构建一个强大和功能丰富的容器云平台。

 ✨ GitHub:https://github.com/kubesphere
 💻 官网(中国站):https://kubesphere.com.cn
 👨‍💻‍ 微信群:请搜索添加群助手微信号 kubesphere

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存