首页天道酬勤qunar 去哪儿(核心素养落地)

qunar 去哪儿(核心素养落地)

admin 12-04 14:15 198次浏览

作者| | yqddp Qunar.com基础平台技术专家

背景

近年来,云原生技术和容器技术非常流行,日趋成熟。很多企业都在慢慢开始容器建设,并在云原生技术方向不断探索实践。基于这一大趋势,2020年底,去哪儿也迈出了——集装箱化超级小球的第一步。

云原生是一系列能够增强业务能力的技术架构标准。遵循它可以使应用程序具有可伸缩性、可扩展性、可移植性和弹性。云原生也是下一代技术栈的必备选项,可以让业务更加敏捷。通过实践云原生技术栈,如DevOps、微服务、容器化、可观察性、抗脆弱性(混沌工程)、ServiceMesh、无服务器等。我们可以享受云原生带来的技术红利。

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的模式,可以在10秒内交付一个MySQL实例。

2020 - 2021:

云原生技术非常成熟,去哪儿也决定通过拥抱云原生技术来增加业务势能。在所有团队的共同努力下,P1和P2的300个应用程序已经集装箱化,计划到2021年底,所有业务应用程序都将集装箱化。

00-

落地过程与实践

容器化整体方案介绍

去哪儿对Portal平台、中间件、ops基础设施、监控等做了相应的适配和改造。集装箱化过程中的每一个系统。转换后的框架矩阵如下图所示。

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

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

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

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

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

CI/CD 流程改造

主转换点:

应用画像:汇聚应用相关的运行时配置、白名单配置、发布参数等。并为容器发布提供统一的声明性配置。

授权系统:通过一个入口应用所有授权操作,实现自动授权。

通过调研对比,K8s多集群方案3360表明,经过运维优化和试压评估,KubeSphere能够满足我们的性能要求。最后,我们选择了KubeSphere作为多集群方案。

中间件适配改造

改革关注点:由于容器化后IP频繁变化是正常的,所有的公共组件和中间件都应该适应并接受这种变化。

应用平滑迁移方案设计

为了帮助企业快速顺利地迁移到容器,我们制定了一些规范和自我规范。

动化测试验证等操作来实现这个目标。

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

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

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

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

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

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

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

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

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

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

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

问题点:

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

分析与解决过程:

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

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

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

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

场景介绍:

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

问题点:

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

分析与解决过程:

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

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

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

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

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

并发拉取镜像超时

场景介绍:

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

分析与解决:

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

并发大时授权接口抗不住

场景介绍:

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

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

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

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

问题点:

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

分析与解决:

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

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

授权接口按应用和 IP 限流, 超过 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 功能失效。

分析与解决过程:

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

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

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

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

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

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

未来展望

多集群稳定性治理

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

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

提高资源利用率

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

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

ServiceMesh 方案落地

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

本文由博客一文多发平台 OpenWrite 发布!

【我与 U 的故事】遇见U 感受U 使用U 爱上U操作指南 物理云主机 UPHosthtml5中marquee标签如何用用Python编辑现有PDF页面vue route怎么使用及嵌套路由
阿里云解决方案架构师待遇(阿里解决方案架构师什么级别) 七日年化(化铝容器)
相关内容