把 117KB 配置塞进 64KB 限制:节点镜像元数据的压缩与裁剪实践

最近在给云原生节点镜像打包内置组件时,遇到了一个底层的硬限制:底层基础设施的镜像管理系统对单条元数据属性(Metadata Property)设置了严格的 64KB(65,536 字符)长度上限。 随着组件功能越来越丰富,里面内嵌的 CRD、OpenAPI Schema、网络拓扑模板越来越大,打包出来的配置字符串直接冲到了 117KB+,导致镜像在注册和导入阶段直接报错拦截。 由于下游控制器和模板引擎在节点初始化时必须依赖这些声明式配置,我们不能直接删减核心功能。本文记录我们如何在不破坏任何模板语法、类型校验和向后兼容的前提下,一步步把体积从 117KB 压进 64KB 安全线以内的过程。 问题与约束 1. 为什么配置会超标? 我们需要在节点镜像元数据中预置一整套 Kubernetes 声明式清单: Package 扩展包定义与 CRD:基础控制器与系统组件清单。 Data Values Schema:用于参数校验和默认值填充的 OpenAPI v3 Schema。 渲染模板:带有动态宏(如 #@ 指令)的 YAML/YTT 模板。 这套配置的流转链路如下: 原始 YAML 清单 → 内联处理与预打包 → 压缩并 Base64 编码 → 写入镜像元数据属性 → 节点启动时由控制面解压并渲染 2. 核心限制 硬限制:单个属性字符串长度必须 ≤ 65,536 字符。 零破坏:所有的优化必须是无损的,下游模板引擎的动态渲染、参数校验和默认值行为必须与优化前 100% 一致。 第一步:升级压缩算法(Gzip → Brotli L11) 面对最初 117KB+ 的体积,最直接的想法是换一个压缩率更高的算法。 此前流水线里默认用的是标准的 gzip (Deflate)。我们针对这组结构高度重复的 YAML/JSON 文本,拉了几种主流算法做基准测试: 压缩算法 压缩等级 构建期压缩耗时 运行时解压耗时 最终 Base64 字符数 Gzip 6 / 9 < 10ms < 1ms ~107,000 - 117,000 Zstd 19 ~15ms < 1ms ~72,000 Brotli 11 ~90ms < 2ms 66,109 为什么选择 Brotli L11? 匹配一次构建、多次分发的场景:节点镜像在构建阶段多花几十毫秒做高强度压缩是完全可以接受的,只要运行时解压足够快即可(Brotli 解压耗时小于 2ms)。 文本字典与大窗口优势:Brotli 拥有更大的滑动窗口(最高 16MB),且内置了专门针对 JSON/YAML/XML 等结构化文本的静态字典。在 Kubernetes 这种大量重复 key 和缩进的场景下,Brotli 相比 Gzip 带来了接近 40% 的体积下降。 结果:Brotli L11 把体积从 117KB 压到了 66,109 字符。虽然效果显著,但离 65,536 的上限依然超出了 573 个字符。这说明光靠通用压缩算法已经不够了,必须对文本内容本身动手术。 ...

2026-08-21 · 2 分钟 · 411 字 · 涯余

Kubernetes 的 Server-Side Apply

从 Kubernetes 1.18 开始,可以看到一个明显的变化就是资源的 YAML 在 metadata 部分多了很多信息: apiVersion: v1 kind: Namespace metadata: creationTimestamp: "2021-02-18T03:03:40Z" managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:status: f:phase: {} manager: kube-apiserver operation: Update time: "2021-02-18T03:03:40Z" name: default resourceVersion: "199" uid: 2f4536b5-7302-4dc1-9620-052a567c917c spec: finalizers: - kubernetes status: phase: Active 比如这个 NS 的例子, 其中大部分都是 mangedFields部分.简单来讲,managedFields 字段是用来声明一个资源的各个字段的具体的管理者是谁.我们可以参考一个有点类似的案例, Node 的 conditions 字段: conditions: - lastHeartbeatTime: "2021-02-18T03:05:25Z" lastTransitionTime: "2021-02-18T03:05:25Z" message: Flannel is running on this node reason: FlannelIsUp status: "False" type: NetworkUnavailable - lastHeartbeatTime: "2021-02-18T07:34:05Z" lastTransitionTime: "2021-02-18T03:03:36Z" message: kubelet has sufficient memory available reason: KubeletHasSufficientMemory status: "False" type: MemoryPressure Contaions 是一个列表,不同的 Component 负责上报自己所观察到的信息,最终汇总到 Node 的 Status 下面.然后再合并汇总出一个整体的 Ready 状态. mangedFieds 也是有点类似的思路, 一个资源的不同部分是由不同组件/人负责维护的,显式的声明这种维护关系有助于各方协同地维护一个资源完整的状态.更直观的,我们可以看一个 Deployment 的例子. 首先,我们使用如下的 deployment 文件来创建 ...

2021-02-18 · 5 分钟 · 895 字 · 涯余

OpenKruise 简介

Kubernetes 作为一个 building blocks 来说,已经逐渐趋于稳定。大家对于其功能的增强,已经逐渐开始以 CRD + Controller Pattern 为主。另一方面,仍然有不少尝试是针对像 Deployment/StatefulSet/DaemonSet 这样的原始功能的。OpenKruise项目就是专注于此。原生的三种 Workload 能满足于日常的需求,但并不完善。在 OpenKruise 项目中,我们可以看到,多种 Workload 之间提供的功能是有很多共性的,我们可以提炼出他们的一些共同的功能模块, 比如: 分批升级 灰度更新 原地升级 SideCar管理 PVC管理 Pod 固定名字 … 这些原子功能有些原生的 workload 已经具有,有些没有。OpenKruise项目通过将这些原子功能排列组合,提供了一些功能增强型的 workload.下面将一一介绍。 SidecarSet SidecarSet 定义了一些 sidecar 模板,然后通过 webhook 的方式注入到通过标签匹配的 Pod 中,这和 istio 等的方式比较像。它比较有用的地方在于,假设你希望给很多 Pod 注入一些通用的附加功能,比如 log, metrics或者init等功能,那么通过 SidecarSet 是一种比较好的方式。下面是一个 yaml 示例: # sidecarset.yaml apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet metadata: name: test-sidecarset spec: selector: matchLabels: app: nginx strategy: rollingUpdate: maxUnavailable: 2 containers: - name: sidecar1 image: centos:6.7 command: ["sleep", "999d"] # do nothing at all volumeMounts: - name: log-volume mountPath: /var/log volumes: # this field will be merged into pod.spec.volumes - name: log-volume emptyDir: {} apiVersion: v1 kind: Pod metadata: labels: app: nginx # matches the SidecarSet's selector name: test-pod spec: containers: - name: app image: nginx:1.15.1 这个示例给 test-pod 注入了一个 sidecar1, 同时也加入了一个 log-volume. 另外,依据对 SidecarSet 不同字段的更新,相应的 Pod 有两种更新方式 ...

2020-11-21 · 4 分钟 · 649 字 · 涯余

etcd 的 clock diff 问题

最近碰到一个客户问题,最初始的现象是一个resource的处理逻辑一直不生效,看 k8s apiserver 的日志如下: 这里面没有 call webhook 相关的日志。主要的错误有两类,一是超时,暂时不清楚为啥。另一类是 object conflict, 这个比较奇怪。因为一般 obejct conflict 不会这么普遍,而且一般 controller 都会对此种情况做特殊处理,会自动恢复。从这个日志中能看出来的问题不多,继续排查一下业务相关的 contrller看看线索: 也是大量的 object conflict,非常异常的场景。而且还有一个 invalid object。到这只能怀疑是不是 etcd 出问题了,因为正常的 controller/apiserver 逻辑不太会触发这么多的此类错误。看 etcd 的日志: 大量的 clock diff 日志,日志意思应该是有一台节点的时间不同步。盲猜可能会导致数据存储的异常,修复时间问题后重启了 apiserver以及相应的 contorller 之后问题修复。看官方提到的 issue 里: Is there any other side effects when the system clock differs on each node, 只说了会影响设置有 TTL 的 object,目前看起来影响远远大于这个。 这个问题的警示是,ETCD的作用太核心了,基于 Kubernetes 的容器平台在监控层面应该尽可能收集多的 etcd 的 metrics 信息,以供管理员决策和提供告警。如果日志不好收集,apiserver 的错误码应该也能提供不少信息。 另一方面,NTP等服务应该作为默认部署的组件,以确保 master 节点在进行调整,维护,扩容或者新增 master 节点的时候,保证时间同步。而不是一次性地校准一次就不管了。

2020-11-17 · 1 分钟 · 68 字 · 涯余

关于 OAM 与云原生应用

OAM 是指阿里开源的一个应用模型,主要是云原生场景。年初的时候出来到现在,断断续续地了解过一些,之前一直没有特别想清楚,其实现在也是,但多多少少有一点点感悟,在这里记录一下. 云原生应用的现状 这里主要就是说围绕 kubernetes 生态的应用现状,基本上就是一团糟。没有标准,没有好的开源产品,没有社区来推动这个事情。先看看目前有啥: Helm: 适合作为rpm/apt类似的东西,不适合直接给 end user 用 Application CRD: 社区不活跃,结构定义也不好 Operator: 过于复杂,目前仅有的统一规范是 ocp 弄的 operator framework. 尚未推广开来 其他林林总总的各种 ad-hoc 方案 这就导致云厂商要么是完全自己做一套新的应用模型,要么是用上面这一类现成的,但功能受限,扩展不易。且用户需要有一定的前置知识才能用好。 OAM 的主要优点 目前看来其最主要的优点是角色分离。Kubernetes YAML 的一个主要问题就是,它是一个大而全的 cofig 方案,不宜手写,只适合运维方向。如果想要做成用户友好的模型,OAM提供了一种可能性: 将大的 config 按角色分拆开,开发人员写一部分,运维人员写一部分,各自只关心各自的部分,最终再合并成一个整体的 config. 细节问题比如具体分成几个角色,谁来合并,可以再迭代改进。但这个分离的设计可以认为是走在一个正确的方向上。 其他的几个重要的方面有: 跨平台性。从设计上来讲,一开始设计成跨平台的思路当然是对的。但实际上可能还是90%以上的人主要是用在 k8s 上,所以 spec 设计时比较贴近于 k8s 的设计。其他平台的实现估计会花不少时间,这也是导致目前没有其他平台实现的主要原因 插件系统。OAM 提供的 traits 能力,可以理解为对应用的一个插件系统。之前国内有厂商做过类似的东西,是一个非常好的尝试。以插件形式给应用附加能力,是一种非常自然和方便的用户体验,traits可以用来提供类似的能力。 用户体验 即使做了职责分离,OAM 看起来仍然是非常难以直接 edit 的东西。这也是之前我一直比较纠结的一个点。因为它更像一个面向开发者的 spec, 而不是一个对 end user 的产品。我们如何让用户方便地使用这个功能呢,有下面几种可能: 纯 UI 封装。这里需要 UI 设计的比较好,能够隐藏比较多的细节,不要暴露太多的底层名词给用户,不然用户就要去理解 OAM 是个什么东西了。而我们理想的形式就是让用户不去学习新概念。一个可能的点就是我上面说的,把 traits 叫做 插件等等。这里需要考虑的一个点是API怎么设计,因为一般UI/API都是必须的产品。如果直接把 OAM 用 API 暴露出来也不怎么友好,这就涉及到下面的第二点 我们能否再设计一层 spec, 简单一点的,能提供便捷的API,UI也方便? 这个诱惑力比较大,但其实又回到了远点,因为又回到了老的大的 config 上? 假设我们想做的更简单一些,那势必又无法全面覆盖 OAM 的能力。似乎唯一的可能性就是,我们换了一种 spec 格式,让用户更容易理解,但可以和 OAM 一一对应。 因为之前有厂商放出了一些 OAM UI 的截图,比如: ...

2020-10-22 · 1 分钟 · 132 字 · 涯余

Kubernetes Webhook Cert 方案

Kubernetes 里的 Cert 数据是挺零散的一块,而 Webhook 中又强制要求必须是 HTTPS, 在实际使用的过程中尝试了几种方案,发现各有优劣,也都有不少问题.下面将分别详述一下: Cert Manager Cert Manager 可能是最成熟的方案了,使用 Let’s Encrypt 来签发证书.只需要配置好响应的 CR (Issuer, Cert…等), Cert Manager会自动生成证书,比较方便. 缺点也很多,首先是引入了一个比较重的依赖,Cert Manager本身的稳定性也是一个问题.实际用的时候经常发现它的 apiservice Not Ready等问题.假设本来环境中已经有 Cert Manager,那么直接用它是一个比较好的选项,否则并不建议使用. 脚本 + CSR Resource 这种方式是利用 Kubernetes 内置的 CSR 资源来管理证书,最终 caBundle 就可以直接用 Kubernetes 集群自带的 cert 数据,但 Server CRT 和 Server KEY 需要自己用脚本生成.Github 上有现成的项目演示了这种用法: k8s-webhook-cert-manager. 其基本使用方式如下: ./generate_certificate.sh --service ${WEBHOOK_SERVICE_NAME} --webhook ${WEBHOOK_NAME} --secret ${SECRET_NAME} --namespace ${WEBHOOK_NAMESPACE} 我们需要提供SVC的名字和NS以生成cert数据.脚本也可以根據自己需求進行適當裁剪.当数据生成之后,使用的 CSR 资源我们还可以通过集群看到(一段时间后会被GC): [core@bastion ~]$ kubectl get csr NAME AGE REQUESTOR CONDITION csr-w6ldh 5m56s system:node:master3 Approved,Issued 需要注意的细节如下: ...

2020-10-16 · 1 分钟 · 138 字 · 涯余

Kubernetes in 2020: Let's Forget About Kubernetes

犹记得 Docker 横空出世的时候,像明星一般,吸引了无数的人去研究它,使用它,推广他。技术的狂热让我们隐隐约约觉得它似乎解决了无数的业内问题,即使它实际上只是适用于单机环境的一个容器实现。没关系,我们有 Mesos / Marathon, 或者其他类似的东西来弥补它的不足, Docker 仍然是我们关注和研究的核心,其他的都是陪衬。 Kubernetes 的出现改变了这一切,与 Docker 一样,从一开始它就拥有了极好的架构基础。在它的逐步成长中,Mesos/Marathon的先天不足逐渐被显露出来,并且逐渐被推至边缘地带,Docker 也渐渐地回到了它应有的位置上,成了软件行业中,无数层级抽象中的一环,在 Linux Kernel 之上,在 Kubernetes 之下,默默工作,只有在出问题时才会被人们想起。Kubernetes 成了新的核心。 而过去一年,我们看着 Kubernets 也在逐渐成熟,性能更好,功能更完善,一切都在向着更稳定的方向迈进,也在紧随着 Docker 走过的路。我们永远需要一个 Platform, 这个 Platform 是谁则是在不断变化的。 因为 Platform 的稳定,所以它应该被遗忘,因为我们在思考更高层级的东西。在这整个链条中,我们知道了起点是二进制,终点是人的需求,中间的路,我们走到了 Kubernetes 这里。那么 Kubernetes 的上面是什么?这可能是我们在今后的一年或者很多年里探索的答案。 过年的一年已经给了我们很多零碎的拼图: ServiceMesh, DevOps, GitOps, Application, Helm, Servrveless, Cloud Native…. 这些名词如雨后春笋般冒了出来,让人眼花撩乱。它们并不能总是解决所有的问题,但却给我们指明了一定的方向。 从代码自动到产品,这一个长久而又清晰的诉求,在 DevOps 提了这么多年的情况下,依然在被呼吁,说明我们仍未达成目标,因为它本身需要的前置条件太多, 我们需要良好的 CI/CD, 能够适配各种 VCS;我们需要简明清晰的语法,来描述出了流水线的 Steps;我们需要统一的 Artifact Storage, 用来存储诸如 Test Coverage Output, Docker Image, Binary…等各种各样的文件;我们需要完善的 Unit Test, Integration Test, 来保证能尽量自动化地检测产品的质量,我们需要蓝绿发布,滚动更新,自动的故障恢复…等等。即使这其中的每一步都有了优质的候选技术,将他们真正地串联起来成为真正的 GitOps, 才是更难的地方。因为成功的 GitOps 也需要达到像 Docker / Kubernetes 那样既是一个稳定的核心,也是一个扩展性良好的平台。而这正是开源社区的弱项,人们更容易被技术的偏好分心而不断 fork 分裂,而不像公司那样能有一个稳定的目标和节奏。Docker 和 Kubernetes 的成功历史都证明,公司 + 开源的模式才是最有效的。期待在新的一年里在此方面能有大的突破。 ...

2020-01-03 · 1 分钟 · 182 字 · 涯余

GitOps

Kubernetes 确定了统治地位之后,Service Mesh 和 GitOps 也渐渐火热起来。Service Mesh 领域内 , Istio 已经逐渐占了上风,但 GitOps 扔未有统治性的工具出现,尚处于早期阶段。 相较而言, Service Mesh 本身技术性更强一些,而 GitOps 涉及的面比较广。对企业来说,会有很多额外因素需要考虑,一方面要考虑旧有系统的兼容性,另一方面要考虑所采用工具的可占有性(是否可以自己部署,维护,开发?)。所以即使现在社区有功能比较完善的 GitOps 工具,也很难被企业所采用。 但就理念而言, GitOps 本身的前景非常广阔,最直观的改善就是对于研发人员效率的提升以及产品 Deploy 效率的提升。这些优点是以如下特性/流程为基础的: 让工具去处理杂事 研发人员使用熟悉的 Git ,专注于产品研发 使用声明式语法描述环境目标状态,类似于 kubernetes 使用 Pull Request 进行 Review 流水线集成 lint /test, 进行较为基本的自动化 check PR 通过即可直接上线 通过线上故障来总结反馈并修正自动化 check 这个流程的效率非常高,事实上很多企业会在改善自身研发流程的过程中多多少少都实行了类似的模式,但问题就在于很多使用了 ad-hoc 或者 无法产品化的手段。如何解决社区标准化的产品与企业应用场景之间的鸿沟,是 GitOps 亟待解决的问题之一。

2019-09-11 · 1 分钟 · 52 字 · 涯余

Cloud Native Artifact Registries

Helm3 的 design proposal 里面介绍了对于Chart Repo 融合到 Distribution 的提议。目前已经有了实验的 demo,理论上并没有什么问题。因为 Distribution的设计里并不是只能存镜像,而是一个开放的协议。我们可以想下目前企业在 DevOps 场景下,除了代码之外,有哪些文件需要存储 构建产物 二进制程序 Helm Charts 镜像仓库 测试报告 代码分析报告 Kustomize 文件 等等。为数众多,而且都是存储于不同系统的。构建的产物一般都暂存于构建系统内部,有失效时间。镜像在 Docker Hub 或者 Harbor 中,Helm Charts 在官方仓库或者 chartmusuem 这样的仓库中。基本上每一项都需要单独维护存储设施,分别要使用不同的客户端。Helm3 的这个迁移的决定,不止对 Helm 本身有很大的意义,对于其他的产品也具有很大的启发意义。Docker公司本身也在做这方面的 整合, Docker app 既可以用来分发 docker-compose 文件还有 CNAB 格式的应用。在可以遇见的将来,所有的 Artifacts 共用一个存储和客户端是可能的。 这有一另外的一个好处是,这是一个独立于云提供商的平台。不管是在 AWS/GCE/AZURE 还是其他云平台,不管是用 S3 还是其他存储,Distribution 这样的平台可以提供统一的接口。 另一方面,Kubernetes 平台上应用的分发格式也处于激烈的竞争阶段,目前常用的就有以下几个: Docker App Docker Compose CNAB Helm Kustomize Distribution 这样的平台能帮助用户减少很多使用负担和迁移成本。预测来看,Helm/Kustomize 最终的赢面大些,docker 可能只能继续专注于更底层的业务。 链接 Helm 3 Preview: Charting Our Future – Part 3: Chart Repositories Docker App and CNAB Cloud Native Artifact Registries evolve from Docker Container Registries

2019-08-26 · 1 分钟 · 89 字 · 涯余

作为公共组件的 apiserver

PDF 文件在此: apiserver 起因是因为一个要做一个新的项目,在综合考量 kubernetes 的各个库之后,发现 kubernetes 已经从主项目中把很多可以复用的项目都单独移出来了。而且包含了很多通用性很强的代码。作为以 Golang + Kubernetes 为核心的架构,这些代码基本上可以充当公司内部的公共组件,省去了很多重复工作。 大部分公司都很少有这个意识,去尽量做好公共组件这块。业务需求本身占用了太多时间,这种属于内部架构的问题基本都是靠零星时间来改善。Kubernetes 这些项目不是说多好,而是它作为一个极其其庞大的项目,在开发的过程中自己造的轮子一般的公司都会有类似的需求。研究,拆解,复用这些公共代码,能够很大程度弥补上公司里公共技术这块的缺失。 公共代码越多,项目的相似程度也越高,维护及使用也越简单方便。

2019-05-24 · 1 分钟 · 15 字 · 涯余

istio

整个容器化浪潮诞生了三个胜利者,分别是 docker, kubernetes, istio。 一个额外的胜利者是 golang。 Docker docker 的作用是: 用标准语法描述出一个服务的静态/运行时环境,无关语言与操作系统 资源隔离 被打死的是 lxc, 它有资源隔离的功能,但是缺点太明显: 没有上面的 docker 提供的第一个功能 架构不如 docker 的 cli/server 的简洁明了,易于学习 受威胁的是 VM 太慢了 性能损耗大 无法 API 化 Kubernetes 真正的 DCOS,核心优势: 统一资源模型 统一 API 模型 (引导业务 API 向其靠拢) Node 横向扩展 插件化 被打死的是 Mesos/Marathon,缺点 两层架构的低效率 (仁慈独裁与低效民主) C++ C++ 导致的难以 debug 难以扩展以增加新功能 另一个被打死的是 swarm/compose, 缺点 架构不清晰 自下而上地缓慢地堆功能 受威胁的是 YARN, 不如 kubernetes 通用性强 Istio 微服务相比于单体服务的一切问题都应该在这层解决, trace, 限流, metrics 等等 不需要修改业务代码 比手工的实现更完善,功能更强 受影响的:微服务业务代码 ...

2019-03-25 · 1 分钟 · 73 字 · 涯余

Kubernetes 笔记

API 声明式的 API API Response 错误处理 Resource Version Version API Group Runtime config REGEX 字段格式 PATCH 与 PUT Events 交互 输出 输入 API 声明式的 API 声明式: 结果是什么 命令式: 做什么 声明式的操作,相对于命令式操作,对于重复操作的效果是稳定的,这对于容易出现数据丢失或重复的分布式环境来说是很重要的。另外,声明式操作更容易被用户使用,可以使系统向用户隐藏实现的细节,隐藏实现的细节的同时,也就保留了系统未来持续优化的可能性 kubernetes 里的 API 都是声明式,我们描述好自己想要的 resource object,kubernetes 就会不断尝试去保证这个 resource object 按我们期望的方式存在. API Response 一般包含三部分 metadata: 元数据 annotations: 一些元信息.给第工具用来存储和解析原信息用的. labels: act as filter namespace: resource 所处的 namespace name: resource 名字 uuid: 唯一标识 creationTimestamp: 创建时间 deletionTimestamp: 计划删除的时间(graceful deletion) resourceVersion: 每个 resource 的内部版本,可以用来确定是否发生了变化.也用于做并发控制 generation spec: 具体描述,不同 resource 的属性不同。spec 里通过声明式的方式表明了期望的目标状态 status: resource 的当前状态 下面以展示一下 kubernetes node api 的 metadata 作为样例: ...

2017-08-18 · 3 分钟 · 452 字 · 涯余