用 Jsonnet 声明式编写 Kubernetes 清单:深入解读 kube-libsonnet 及其在 sealed-secrets 中的实战 📅 发布时间:2026/9/16 13:39:46 👁 浏览次数: 用 Jsonnet 声明式编写 Kubernetes 清单深入解读 kube-libsonnet 及其在 sealed-secrets 中的实战【免费下载链接】sealed-secretsA Kubernetes controller and tool for one-way encrypted Secrets项目地址: https://gitcode.com/GitHub_Trending/se/sealed-secrets本篇文章围绕当前仓库中 vendored 的 kube-libsonnet 库README.md展开讲解如何用 Jsonnet 以对象合并 隐藏字段约定的方式声明式编写 Deployment、Service、Ingress、RBAC 等 Kubernetes 清单并展示它如何被 sealed-secrets 项目的控制器部署清单controller.jsonnet 等真实消费。读完本文你将掌握 kube-libsonnet 的构造器 API、下划线约定、工具函数与测试方法论并能直接看懂 sealed-secrets 仓库根目录下三个 Jsonnet 部署文件的生成逻辑。一、kube-libsonnet 是什么从 kube-manifests 中独立出来的 Jsonnet 对象库kube-libsonnet 是一个用 Jsonnet 编写的 Kubernetes 对象库。根据其 README.md 的说明这个仓库最初的内容来自 bitnami-labs 的 kube-manifests 项目中的lib/目录时间点为 2018 年 3 月目的是为常见的 Kubernetes 对象如Deployment、Service、Ingress等提供一份可复用的 Jsonnet 清单模板。由于下游项目包括 sealed-secrets需要同时消费 kube-manifests 与这份对象库kube-manifests 后续改为通过 git submodule 的方式引用本库$ git submodule add https://github.com/bitnami-labs/kube-libsonnet $ cat .gitmodules [submodule lib] path lib url https://github.com/bitnami-labs/kube-libsonnet而在 sealed-secrets 仓库中这份库并不是以 submodule 形式引入而是通过 jsonnet-bundlerjsonnetfile.json 与 jsonnetfile.lock.json被 vendored 到了 vendor_jsonnet/kube-libsonnet/ 目录下库的入口文件是 kube.libsonnet。从源码结构看该库的核心设计目标有两点对象即 Jsonnet 对象每个 Kubernetes 对象都是一个可被合并的 Jsonnet object天然支持增量覆盖隐藏字段承载辅助逻辑以::隐藏字段定义大量 helper让常见场景单 Pod 单端口 Service、环境变量、滚动更新策略等只需声明式填写即可。二、两大核心约定可选辅助字段与下划线约定ku.be.libsonnet 的头部注释L1-L53明确记录了两条设计约定这也是读懂整个库的关键。2.1 可选辅助字段Optional helpers对象遵循 Kubernetes API 常规 schema但额外提供若干隐藏双冒号::辅助字段用于覆盖常见场景。例如Service.target_pod可以为单 Pod / 单端口的常见情况自动生成合适的selector和ports块。如果不想使用辅助字段只需为辅助字段本应生成的 Kubernetes 标准字段提供显式值辅助逻辑就会被忽略。kube.Service(my-svc) { target_pod: $.deploy.spec.template, }Service构造器kube.libsonnet#L149-L179会基于target_pod推导出spec.selector与spec.ports并派生一批便捷字段供 Ingress 规则、URL 拼接等场景使用hostname.namespace.svchost_colon_porthost:porthttp_urlhttp://host:port/name_port可直接嵌入 Ingress 的{ serviceName, servicePort }结构2.2 下划线约定The Underscore ConventionKubernetes API 大量使用 JSON 数组来表达无序集合或键值映射这与 Jsonnet 强大的对象合并操作相冲突。为此库将这类数组的Jsonnet 原生变体放在以_结尾的隐藏字段中。最典型的例子是Container中的env_kube.libsonnet#L217-L242kube.Container(foo) { env_: { FOO: bar }, }它会生成符合规范的container.envJSON 数组{ env: [ { name: FOO, value: bar } ] }类似的下划线字段在库中大量存在形成统一的模式对象下划线字段生成的 Kubernetes 字段Containerenv_/args_/ports_/volumeMounts_env/args/ports/volumeMountsPodSpeccontainers_/initContainers_/volumes_containers/initContainers/volumesListitems_itemsRoleBindingsubjects_/roleRef_subjects/roleRefNetworkPolicyingress_/egress_ingress/egressStatefulSetvolumeClaimTemplates_volumeClaimTemplates其中ports_、volumes_、volumeMounts_依赖工具函数mapToNamedListkube.libsonnet#L82将{foo: {a: b}}转换为[{name: foo, a: b}]并对 name 做hyphenate下划线转连字符处理env_则通过envList判断值是对象还是字符串分别生成valueFrom或value。值得注意的是PodSpec对容器顺序的处理default_containerkube.libsonnet#L260-L266默认取第一个容器若存在多个容器则强制命名为default并置于数组首位其余容器按对象字段顺序排列。另外initContainers_是天然有序的命名对象如果顺序至关重要源码注释明确建议直接操作initContainers数组必要时用super.initContainers做前后拼接。三、常用对象构造器速览库的主体是一个大的对象字面量返回一个构造器集合最终由kube.libsonnet的顶层{...}导出。所有资源构造器都基于私有基础构造器_Object(apiVersion, kind, name)kube.libsonnet#L121-L130它统一生成metadata.name、metadata.labels.name将:替换为-与空的metadata.annotations。3.1 工作负载类Deploymentkube.libsonnet#L381-L428apps/v1默认replicas: 1、minReadySeconds: 30、revisionHistoryLimit: 10。其滚动更新策略会根据 Pod 是否挂载 PVC 自动切换无状态应用使用maxSurge: 25%maxUnavailable: 25%有状态应用退化为maxSurge: 0maxUnavailable: 1源码注释戏称为 Poor-mans StatelessSet主要服务于replicas1场景。StatefulSetkube.libsonnet#L451-L489apps/v1serviceName默认取 namevolumeClaimTemplates_通过std.prune去除空字段因为 StatefulSet 对 no-op 字段变更也极其敏感。Job / CronJobkube.libsonnet#L491-L537Job默认restartPolicy: OnFailure、completions: 1、parallelism: 1CronJob默认concurrencyPolicy: Forbid、successfulJobsHistoryLimit: 10、failedJobsHistoryLimit: 20。DaemonSetkube.libsonnet#L539-L560apps/v1默认 RollingUpdate 且maxUnavailable: 1。Pod / PodSpeckube.libsonnet#L255-L299PodSpec默认terminationGracePeriodSeconds: 30、imagePullSecrets: []并断言至少一个容器PodSpec.ports(proto)可按协议汇总所有容器的端口号。3.2 网络与存储类Service见上文 2.1 节默认type: ClusterIP。Ingresskube.libsonnet#L562-L572extensions/v1beta1内置断言所有 path 必须以/开头防止写出非绝对路径。PersistentVolumeClaimkube.libsonnet#L194-L215通过storage必填字段声明容量storageClass可选默认accessModes: [ReadWriteOnce]。ConfigMapkube.libsonnet#L326-L337内置断言data中所有值必须是字符串避免误用非字符串值。3.3 配置注入与密钥类Secretkube.libsonnet#L348-L354通过data_传入明文构造器自动std.base64编码生成data。SecretKeyRef / ConfigMapRef / FieldRef / ResourceFieldRefkube.libsonnet#L340-L379均为EnvVarSource子类型SecretKeyRef与ConfigMapRef还会断言引用的 key 确实存在于对应的 Secret/ConfigMap 中。卷助手EmptyDirVolume()、HostPathVolume(path, type)、GitRepoVolume(repo, revision)、SecretVolume(secret)、ConfigMapVolume(configmap)kube.libsonnet#L301-L324其中SecretVolume/ConfigMapVolume直接从对象引用推导卷名消除手写secretName/name的不一致风险。3.4 平台与 RBAC 类CustomResourceDefinitionkube.libsonnet#L579-L597apiextensions.k8s.io/v1beta1通过toLower/singulars自动推导singular/plural/listKind并自动生成metadata.name plural . group。ServiceAccount / Role / ClusterRole / Group / Userkube.libsonnet#L599-L620ClusterRole是Role的 kind 变体Group/User用于 RBAC subject。RoleBinding / ClusterRoleBindingkube.libsonnet#L622-L642通过roleRef_/subjects_引用对象自动展开为roleRef含apiGroup与带namespace的subjects数组。NetworkPolicykube.libsonnet#L695-L708policyTypes由ingress_/egress_是否为空自动推导配合工具函数podLabelsSelector与podsPorts可快速生成 Pod 选择器与端口列表。3.5 工具函数层除构造器外库还提供一组纯函数kube.libsonnet#L63-L119均有 unittests.jsonnet 中的断言覆盖objectValues/objectItems返回对象的值数组 /[key, value]对数组排除隐藏字段hyphenate将_全部替换为-parseOctal解析八进制字符串如755→ 493断言每位 8siToNum将 SI 单位后缀m/K/M/G/T/P/E及Ki/Mi/Gi/Ti/Pi/Ei换算为数值mapToNamedList、filterMapByFields、toLower/toUpper用于字段名/标签名规范化。四、bitnami.libsonnet公司级约定的再封装bitnami.libsonnet 在kube.libsonnet之上叠加了 Bitnami 内部约定主要提供三个构造器ElbService(name, cloud, internal)L40-L47按云厂商生成 LoadBalancer 注解——AWS 启用连接 draining、PROXY protocol并可选 internalGKE 则设置externalTrafficPolicy: Local以保留真实源 IP。Ingress(name)L49-L107强制要求host与target_svc默认单服务路径/通过cert_provider选择证书签发渠道kcm已废弃、cm-dnscert-manager route53 DNS-01默认或cm-httpcert-manager ACME HTTP并自动生成对应的 annotations 与tls.secretNamename-cert。PromScrape(port)L109-L120为 Pod 打上prometheus.io/scrape/prometheus.io/port/prometheus.io/path注解。PodZoneAntiAffinityAnnotation(pod)L122-L141生成跨可用区failure-domain.beta.kubernetes.io/zone权重 50与跨节点kubernetes.io/hostname权重 100的软反亲和规则。五、在 sealed-secrets 中的真实落地controller.jsonnet 全家桶kube-libsonnet 并不是孤立存在的库——sealed-secrets 仓库根目录下的三个 Jsonnet 部署清单就是它最直接的消费方这也是理解该库价值的最佳实战案例。5.1 最小部署controller-norbac.jsonnetcontroller-norbac.jsonnet 通过import vendor_jsonnet/kube-libsonnet/kube.libsonnet引入库L8并用kube-fixes.libsonnet修补 CRD 构造器为apiextensions.k8s.io/v1新版本kube-fixes.libsonnet。文件中kube.CustomResourceDefinition(bitnami.com, v1alpha1, SealedSecret)生成 SealedSecret CRD并内嵌kubecfg.parseYaml(importstr schema-v1alpha1.yaml)[0]读取 schema-v1alpha1.yaml 作为 OpenAPI schemakube.Service(sealed-secrets-controller)通过target_pod: $.controller.spec.template直接联动 Deployment 模板自动生成 selector 与端口kube.Deploymentkube.Container组合出控制器 Pod暴露http(8080) 与metrics(8081) 端口、readinessProbe/livenessProbe指向/healthz、只读根文件系统 /tmpemptyDir 卷并施加runAsNonRoot、seccompProfile: RuntimeDefault、drop ALL capabilities 等安全上下文。5.2 推荐部署controller.jsonnetcontroller.jsonnet 在最小部署之上叠加 RBAC 与网络代理能力几乎用遍了 kube-libsonnet 的 RBAC 构造器kube.ServiceAccount、kube.ClusterRole(secrets-unsealer)、kube.Role(sealed-secrets-key-admin)、kube.Role(sealed-secrets-service-proxier)kube.ClusterRoleBinding与kube.RoleBinding通过roleRef_/subjects_引用对象完成绑定其中 service-proxier 绑定使用kube.Group(system:authenticated)作为 subject源码注释特别提醒system group 没有 namespace不能用subjects_的魔法下划线字段。5.3 可观测性扩展controller-podmonitor.jsonnetcontroller-podmonitor.jsonnet 在推荐部署之上附加一个 PrometheusPodMonitormonitoring.coreos.com/v1selector.matchLabels指向name: sealed-secrets-controllerpodMetricsEndpoints抓取http端口、interval: 30s并设置sampleLimit: 1000。配合控制器源码中的 Prometheus 指标见 pkg/controller/metrics.go即可完成集群内的指标采集。5.4 库自身的 SealedSecret 构造器有趣的是kube.libsonnet 甚至内置了SealedSecret(name)构造器kube.libsonnet#L644-L656它支持两种数据来源——datalines_读取由kubeseal | jq -r .spec.data导出的文本文件减少importstr样板否则要求显式提供data并断言base64Decode(data) ! 。这与仓库中 kubeseal 命令cmd/kubeseal/main.go的输出格式直接对应。六、测试方法论从单测到 k3s 端到端验证README 对测试给出了清晰的说明这里结合 tests/Makefile 展开6.1 一键全量测试make tests该命令对应docker-compose-teststarget会创建临时 k3s 配置目录 → 用docker-compose up -d拉起 k3s dummy 容器作为 Kube API 后端 → 等待 e2e 容器执行完校验后docker-compose down并清理临时目录。这套栈足以支撑kubecfg validate对清单做真实 API 校验。6.2 轻量测试复用本地集群如果不想启动完整的 kube-api 栈将改用本地已配置的 Kubernetes 环境可运行make -C tests test-srcs test-kube其中test-srcs聚合unittests、lint、parse、diff四个子目标unittests直接执行jsonnet unittest*.jsonnetunittests.jsonnet 用一连串std.assertEqual验证hyphenate、parseOctal、siToNum、toUpper/toLower、podRef、podsPorts、podLabelsSelector等纯函数lint对全部*.jsonnet与*.libsonnet执行jsonnetfmt --test失败时提示make fix-lint lint自动格式化parse逐个jsonnet file /dev/null验证语法与求值diff将当前输出与golden/目录下的黄金文件做diff -u防止意外回归变更确认后执行make gen-golden diff更新基线。test-kube对应validatetarget先探测kubectl api-versions是否有可用集群若不可用则跳过退出码 0否则运行kubecfg validate --ignore-unknownfalse校验所有*-validate.jsonnet文件。test-simple-validate.jsonnet 是该体系下的典型样例它在一个kube.List()中组装 Namespace、ServiceAccount、Role、RoleBinding、ConfigMap、Secret、Service、bitnami.Ingress、Pod、Deployment、StatefulSet、DaemonSet、Job、CronJob 与 NetworkPolicy其中 NetworkPolicy 充分使用了podLabelsSelector与podsPorts帮助函数并包含egress到 kube-system DNSUDP 53的示例。对应的黄金基线存放在 tests/golden/ 目录如test-simple-validate.json、test-sealedsecrets.json等。七、总结与上手建议kube-libsonnet 的核心价值在于用 Jsonnet 的对象合并能力替代手写 YAML 的复制粘贴用下划线约定化解数组 vs 对象的 API 摩擦用断言把配置错误前移到求值期。在 sealed-secrets 项目中它承担了从 CRD、Service、Deployment 到整套 RBAC 的清单生成配合kubecfg与jsonnet-bundler形成完整的声明式发布链路。上手路径建议先通读 kube.libsonnet 头部注释理解两条约定再对照 unittests.jsonnet 与 test-simple-validate.jsonnet 复现测试最后以 controller-norbac.jsonnet → controller.jsonnet → controller-podmonitor.jsonnet 的递进关系观察库对象如何在真实部署中组合需要调整部署时用jsonnet controller.jsonnet输出清单再配合kubecfg应用到集群即可。【免费下载链接】sealed-secretsA Kubernetes controller and tool for one-way encrypted Secrets项目地址: https://gitcode.com/GitHub_Trending/se/sealed-secrets创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考