
Grafana Tempo 与 Jsonnet Memcached 库在 Kubernetes 上以 Tanka 部署与定制 Memcached 缓存【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo本指南以仓库 vendored 的example/tk/vendor/github.com/grafana/jsonnet-libs/memcached库 README 为核心讲解如何通过 Tanka 与 Jsonnet Bundler 在 Kubernetes 上声明式地部署 Memcached并结合 Grafana Tempo 微服务部署示例example/tk/tempo-microservices/main.jsonnet 与 operations/jsonnet/microservices/memcached.libsonnet说明其真实用法。读完本文你将掌握库的安装初始化流程、memcached对象的核心参数max_item_size、memory_limit_mb等、基于 Jsonnet 面向对象继承的覆盖与扩展技巧、_config参数的取舍原则以及在 Tempo 场景下如何配合 StatefulSet、exporter 与反亲和策略落地部署。背景为什么 Tempo 需要 Memcached JsonnetGrafana Tempo 是高吞吐、低依赖的分布式追踪后端。在微服务形态部署中查询路径上会产生大量可缓存的中间结果例如块索引、搜索结果等Memcached 常被用作分布式缓存层来降低查询延迟与后端存储压力。Tempo 官方在 example/tk 中为高级用户提供了 Jsonnet/Tanka 形式的微服务部署示例并在 operations/jsonnet/microservices/config.libsonnet 中默认规划了 3 副本、connection_limit: 4096、memory_limit_mb: 1024的 Memcached 集群。而 memcached Jsonnet 库就是被 vendored 到 example/tk/vendor/github.com/grafana/jsonnet-libs/memcached 下、用于运行 Memcached on Kubernetes的一组可扩展配置。它本身与具体存储后端无关却以极低的代码量核心仅一个memcached.libsonnet文件解决了缓存实例 指标 exporter 无头 Service 调度策略的完整部署问题这也是它在 Tempo 部署栈中被直接引用的原因。环境准备Tanka 与 Jsonnet Bundler使用该库前需先安装两个工具Tanka基于 Jsonnet 的 Kubernetes 配置工具负责将 Jsonnet 渲染为 K8s 清单并执行tk apply官方安装指引见 https://tanka.dev/。Jsonnet BundlerjbJsonnet 依赖管理工具负责拉取并 vendoring 第三方库安装方式见 https://tanka.dev/install#jsonnet-bundler。在 Tempo 的 Jsonnet 示例中库已经通过jb被固定到 vendor 目录见 example/tk/jsonnetfile.json 与其 lock 文件 example/tk/jsonnetfile.lock.json因此无需再次安装但如果你在自己的配置仓库中从零开始使用该库则需遵循下文安装流程。初始化 Tanka 应用与安装库如果你还没有一个 Tanka 应用先创建新的该命令会从当前 kubeconfig context 复制凭据appyour_app_name mkdir -p $app cd $app tk init随后安装 memcached 库jb install github.com/grafana/jsonnet-libs/memcached安装完成后库会出现在 vendor 目录下与 Tempo 仓库中 example/tk/vendor/github.com/grafana/jsonnet-libs/memcached 的结构一致包含memcached.libsonnet与jsonnetfile.json。最小部署在 default namespace 中运行 Memcached假设要在默认 namespaceTanka 术语中称为 environment中运行在文件environments/default/main.jsonnet中添加如下内容local memcached import memcached/memcached.libsonnet; memcached { // Memcached instance used to cache chunks. memcached_chunks: $.memcached { name: memcached, max_item_size: 2m, memory_limit_mb: 4096, }, }这段配置的核心是 Jsonnet 的对象合并与$根引用$.memcached指向库内定义好的抽象模板对象通过{ ... }局部覆盖生成一个名为memcached_chunks的具体实例。这里仅覆盖了三个参数其余参数全部继承默认值详见下文参数表。应用配置tk apply default执行后Tanka 会渲染出一个包含 memcached 容器、prometheus exporter 容器、StatefulSet 与 headless Service 的完整资源集。核心参数速查与源码级解读以下是 memcached.libsonnet 中定义的全部可覆盖参数及其默认值均可在你自己的实例中按需覆盖参数默认值说明name无必须指定实例名称缺失时抛错must specify name同时作为 StatefulSet / Service 名称max_item_size1mMemcached 单条目最大尺寸映射为启动参数-Imemory_limit_mb1024Memcached 内存上限MB映射为启动参数-moverprovision_factor1.2内存超配系数参与计算内存请求量cpu_requests500mCPU 请求量cpu_limits3CPU 上限Tempo 的 operations/jsonnet/microservices/memcached.libsonnet 中将其覆盖为null即不设 CPU limitconnection_limit1024最大连接数映射为启动参数-cTempo 生产配置为 4096memory_request_overhead_mb100内存请求额外开销MB用于给页缓存等留余量memory_request_bytes派生计算ceil((memory_limit_mb * overprovision_factor) memory_request_overhead_mb) * 1024 * 1024即超配后的内存请求memory_limits_bytes派生计算max(memory_limit_mb * 1.5 * 1024 * 1024, memory_request_bytes)内存上限取 1.5 倍与请求量的较大者exporter_cpu_requests/exporter_cpu_limits/exporter_memory_requests/exporter_memory_limitsnullmemcached-exporter 容器的资源请求/限制null表示不注入对应字段use_topology_spreadfalse是否使用拓扑分布约束false时回退到反亲和anti-affinitytopology_spread_max_skew1拓扑分布最大不均衡度仅use_topology_spread为真时生效min_ready_secondsnullStatefulSet 的就绪最短等待秒数null时不注入extended_options[]额外 Memcached 扩展选项数组非空时拼为--extended逗号分隔串传入派生参数背后的资源计算逻辑memory_request_bytes与memory_limits_bytes是典型的派生字段带::的 Jsonnet 隐藏字段其计算逻辑直接体现在 memcached.libsonnet 中memory_request_bytes:: std.ceil((self.memory_limit_mb * self.overprovision_factor) self.memory_request_overhead_mb) * 1024 * 1024, memory_limits_bytes:: std.max(self.memory_limit_mb * 1.5 * 1024 * 1024, self.memory_request_bytes),请求量 内存上限 × 1.2 超配系数 100MB 开销。超配的目的是让 K8s 调度的内存请求略高于 Memcached 自身的-m限制避免页缓存、网络缓冲等额外内存将 Pod 推入 OOM。上限量max(内存上限 × 1.5, 请求量)为 Memcached 数据面之外的临时内存留出缓冲。计算得到的字节数还要经过库内置的util.bytesToK8sQuantity辅助函数转换为 K8s quantity 字符串如1536Mi其实现采用递归求 1024 因子的方式选择[, Ki, Mi, Gi]后缀与 Kubernetes 的 quantity 解析规范apimachineryresource/quantity.go保持一致。这些逻辑最终通过$.util.resourcesRequests(...)与$.util.resourcesLimits(...)写入容器资源声明见 memcached.libsonnet。定制与扩展Jsonnet 对象继承的威力库作者有意选择 Tanka/Jsonnet 来管理这些配置其核心动机是用户可以在不改动fork库的前提下直接通过对象合并覆盖任何字段。因为 Jsonnet 配置本身就是代码整个东西都是配置覆盖几乎任何部分是完全可以接受的。覆盖容器资源请求与限制例如要为 memcached 容器覆盖资源 requests/limits只需在实例上继续叠加memcached_container::local memcached import memcached/memcached.libsonnet; memcached { // Memcached instance used to cache chunks. memcached_chunks: $.memcached { name: memcached, max_item_size: 2m, memory_limit_mb: 4096, memcached_container:: $.util.resourcesRequests(1, 2Gi) $.util.resourcesLimits(2, 4Gi), }, }memcached_container::中的表示与库内已构造好的容器对象合并::表示该覆盖是隐藏字段、不会直接作为 K8s 资源输出$.util.resourcesRequests(1, 2Gi)则来自 ksonnet-util 的 util 工具集。覆盖导出的其他对象你还可以覆盖派生对象本身例如statefulSet调整副本数、镜像、min_ready_secondsservice调整端口或注解默认即无头 Servicespec.withClusterIp(None)memcached_exporter设置 exporter 的资源请求仅当对应字段非null时才会注入见 memcached.libsonnet 的条件逻辑。Tempo 中的真实覆盖案例Tempo 官方微服务 Jsonnet 对库的覆盖是非常典型的实战样本。在 operations/jsonnet/microservices/memcached.libsonnet 中local memcached import memcached/memcached.libsonnet; memcached { memcached:: { cpu_limits:: null, connection_limit: $._config.memcached.connection_limit, memory_limit_mb: $._config.memcached.memory_limit_mb, deployment: {}, ... }, // Dedicated memcached instance used to cache query results. memcached_all: $.memcached { name: memcached, max_item_size: 5m, }, }connection_limit、memory_limit_mb从统一的$._config.memcached由 operations/jsonnet/microservices/config.libsonnet 提供副本数 3、连接数 4096、内存 1024MB取值实现单点配置、多处引用memcached_all将实例命名为memcached并将max_item_size提升到5m额外叠加了memcached_vpa基于vpaForController生成 Vertical Pod Autoscaler与memcached_pdb基于pdbForController生成 Pod Disruption Budget这些是库本身不提供的周边治理能力。此外在 example/tk/tempo-microservices/main.jsonnet 中示例环境把memcached.replicas覆盖为 1又因为本地 k3d 单节点无法满足反亲和约束特意通过memcached_all::将statefulSet.spec.template.spec.affinity清空为{}见 main.jsonnet让多个 memcached 副本可以在同一节点上运行。这正体现了以覆盖方式按环境调整库默认行为的推荐用法。_config字典的使用准则库作者会在两种情况下把配置项放进_config字典而不是作为顶层字段该参数必须由用户提供值如namespace该参数被多处引用用上述字段覆盖方式逐个改容易出错如cluster_dns_suffix。这条准则的目的是如果不这样做_config字段就会退化成它声明的那些 Job 本身——既然整个东西都是配置把公共参数集中到_config、把差异化参数放在各自对象上是更清晰、更易维护的组织方式。Tempo 的operations/jsonnet/microservices/config.libsonnet正是这一准则的体现memcached: { replicas, connection_limit, memory_limit_mb }集中存放再由memcached.libsonnet用$._config.memcached.*引用。库生成的资源全貌从 memcached.libsonnet 的源码结构可推断一次tk apply会生成以下四类资源除 Deployment/VPA/PDB 等周边对象外核心均由库产出memcached 容器镜像来自$._images.memcachedTempo 中固定为memcached:1.6.45-alpine见 config.libsonnet暴露client端口 11211启动参数形如-m 1024 -I 1m -c 1024 -v当extended_options非空时追加--extendedopt1,opt2。memcached-exporter 容器镜像为prom/memcached-exporter:v0.16.0见 config.libsonnet暴露http-metrics端口 9150固定参数--memcached.addresslocalhost:11211与--web.listen-address0.0.0.0:9150为 Prometheus 抓取 Memcached 指标提供入口。StatefulSetstatefulSet.new(name, replicas, [memcached_container, memcached_exporter], [])replicas在库中从$._config.memcached_replicas读取Tempo 则覆盖为从$._config.memcached.replicas读取默认使用$.util.antiAffinity让副本分散到不同节点开启use_topology_spread后改为kubernetes.io/hostname维度的ScheduleAnyway软拓扑分布约束。无头 ServiceserviceFor(statefulSet) spec.withClusterIp(None)即 Headless Service。无头 Service 让应用层可以通过 DNS 直接解析到各 memcached Pod IP是 Memcached 这类客户端侧分片/一致性哈希缓存的标准接入方式——Tempo 查询组件正是通过它发现全部缓存节点。在 Tempo 微服务示例中完整落地在本地 k3d 集群中复现整套部署含 Memcached的步骤可参考 example/tk/readme.md# 创建本地集群要求 k3d v3.2.0、tanka v0.12.0 k3d cluster create tempo --api-port 6443 --port 3000:80loadbalancer # 可选导入特定版本镜像 export TEMPO_IMAGE_TAG3.0.0 k3d image import grafana/tempo:${TEMPO_IMAGE_TAG} --cluster tempo # 部署微服务形态含 memcached tk apply tempo-microservices部署完成后Memcached 会以名为memcached的 StatefulSet Headless Service 形态出现在集群中通过 exporter 的 9150 端口指标即可在 Grafanahttp://localhost:3000/dashboards由 tempo-mixin 提供面板中观察缓存命中率与内存水位。清理环境k3d cluster delete tempo小结Memcached Jsonnet 库用不到百行 Jsonnet 封装了 K8s 上运行 Memcached 的全部关键要素容器参数映射-m/-I/-c、内存请求/限制的自动推导、exporter 指标暴露、StatefulSet 副本管理与反亲和调度、无头 Service 接入方式。它的设计哲学——完全可覆盖的配置 慎用_config集中公共参数——使其既能开箱即用也能像 Tempo 官方那样按环境深度定制。理解这套模式后你不仅能在 Tempo 栈中驾驭它也能把同样的 Jsonnet 组合与覆盖手法迁移到自己的任何 K8s 工作负载上。【免费下载链接】tempoGrafana Tempo is a high volume, minimal dependency distributed tracing backend.项目地址: https://gitcode.com/GitHub_Trending/tempo1/tempo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考