乐趣区

关于云计算:Qunar-云原生容器化落地实践

作者|邹晟 去哪儿网根底平台技术专家

背景

近几年,云原生和容器技术十分火爆,且日趋成熟,泛滥企业缓缓开始容器化建设,并在云原生技术方向上一直的摸索和实际。基于这个大的趋势,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 流程革新

次要革新点:

  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。

剖析与解决过程:

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 状态, 看到这条信息,大略猜测到罪魁祸首就是这个 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 受权时把服务拖垮。

剖析与解决:

为了解决上述的问题,限量和升高受权接口调用次数是无效的解决形式。咱们采取了上面几个措施:

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 性能生效。

剖析与解决过程:

  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 即可。不过这个也不是最优解,因为这个须要用户手工配置阻断级别,有认知学习老本。

  1. 回到最后的问题上,为什么容器场景下遇到这个问题,而 KVM 没有,次要是因为容器场景 K8s 提供了自愈能力,K8s 会定时执行 liveness check, 当失败次数达到指定的阈值时,K8s 会 kill 掉容器并从新拉起一个新的容器。
  2. 那咱们只好从 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 公布!

退出移动版