使用 Tanka/Ksonnet 在 Kubernetes 上部署 Loki:微服务架构、多可用区 Ingester 与生产级配置详解
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
导读:本文以 production/ksonnet/README.md 为核心,系统讲解如何基于 Tanka(Ksonnet 的继任者)以声明式 Jsonnet 方式在 Kubernetes 上部署 Loki。文章覆盖 Tanka 环境初始化、
jb依赖安装、loki + gateway的完整配置清单(存储后端、TSDB Schema、认证网关)、微服务各组件资源规格,以及 README 中专门给出的多可用区(Multizone)Ingester 配置——这是面向生产环境高可用部署的关键能力。读完本文,你将掌握用tk apply一键交付整套 Loki 微服务集群,并理解multi_zone_ingester_*系列参数背后真实的调度、副本与迁移语义。
背景:从 Ksonnet 到 Tanka,为什么 Loki 官方仍然维护 ksonnet 目录
Loki 在production/ksonnet目录下保留了一套完整的Jsonnet 部署清单(loki.libsonnet),它由 Grafana Labs 以 Ksonnet 时代的 Jsonnet 库形式维护。虽然 Ksonnet 项目本身已停止维护,但官方 Tanka 安装文档 明确指出:Tanka 是 Ksonnet 的重新实现,并且被 Grafana Labs 用来在生产环境运行 Grafana Loki。因此这套 ksonnet 库是"Tanka 部署 Loki"的一等公民资源,而非历史遗留。
Tanka 部署的运行模式是microservices mode(微服务模式),即每个 Loki 组件(distributor、ingester、querier、query-frontend、query-scheduler、ruler、compactor、index-gateway 等)以独立 Deployment/StatefulSet 运行,与单二进制模式相对。从 loki.libsonnet 的组合顺序可以看到完整的服务拓扑:
(import 'distributor.libsonnet') + (import 'ingester.libsonnet') + (import 'querier.libsonnet') + (import 'query-frontend.libsonnet') + (import 'ruler.libsonnet') + (import 'query-scheduler.libsonnet') + (import 'memcached.libsonnet') + (import 'overrides-exporter.libsonnet') + (import 'index-gateway.libsonnet') + (import 'shipper.libsonnet') + (import 'compactor.libsonnet') + (import 'multi-zone.libsonnet') + (import 'memberlist.libsonnet') +前置条件:Tanka 与 jsonnet-bundler 的安装
官方要求Tanka v0.31.0 或更高版本(用于tk env系列命令),以及jb(jsonnet-bundler)v0.6.0 或更高版本来拉取 Jsonnet 依赖。在配置仓库中初始化一个 Tanka 应用:
mkdir <application name> cd <application name> tk init tk env add environments/loki --namespace=loki --server=<Kubernetes API server>其中--server指向 Kube API Server 地址(如https://localhost:6443)。tk env add会生成environments/loki/环境目录,后续所有 Jsonnet 清单都写在这个目录下的main.jsonnet中。
认证提示:Loki 本身不内置任何认证层(官方文档明确说明)。必须在前置部署一个反向代理做认证(如 nginx),防止未授权访问。ksonnet 库中的
gateway模块正是为这一目的设计的,详见下文"网关(gateway)模块"。
拉取 Loki 模块依赖
jb init # 如果已经执行过 tk init 则可跳过 jb install github.com/grafana/loki/production/ksonnet/loki@mainjb install会把 Loki 的 ksonnet 库及其传递依赖(ksonnet-util、kausal 库、consul mixin、jaeger-agent-mixin 等,见 production/ksonnet/loki/jsonnetfile.json)安装到vendor/目录。
最小可用配置:loki + gateway 组合
编辑environments/loki/main.jsonnet,将官方示例作为起点。下面是与 Tanka 官方文档一致的完整清单,并补充了各字段的取值说明:
local gateway = import 'loki/gateway.libsonnet'; local loki = import 'loki/loki.libsonnet'; loki + gateway { _config+:: { namespace: 'loki', htpasswd_contents: 'loki:$apr1$H4yGiGNg$ssl5/NymaGFRUvxIV1Nyr.', // 使用 TSDB shipper using_tsdb_shipper: true // 对象存储后端三选一:'s3'、'gcs'、'azure' storage_backend: 's3', // S3 变量(storage_backend = 's3' 时使用) s3_access_key: 'key', s3_secret_access_key: 'secret access key', s3_address: 'url', s3_bucket_name: 'loki-chunks', s3_bucket_region: 'us-east-1', s3_path_style: false, // GCS 变量(storage_backend = 'gcs' 时使用) // gcs_bucket_name: 'loki-chunks', // Azure 变量(storage_backend = 'azure' 时使用) // azure_container_name: 'loki-chunks', // azure_account_name: 'account', // azure_account_key: 'key', // TSDB 索引存储配置 loki+: { schema_config: { configs: [{ from: 'YYYY-MM-DD', store: 'tsdb', object_store: $._config.storage_backend, schema: 'v13', index: { prefix: '%s_index_' % $._config.table_prefix, period: '%dh' % $._config.index_period_hours, }, }], }, }, replication_factor: 3, memberlist_ring_enabled: true, }, }关键配置项的语义
| 配置项 | 默认值 / 取值范围 | 说明 |
|---|---|---|
namespace | 必填(config.libsonnet 中error 'must define namespace') | Kubernetes 命名空间,也是 Service DNS 名的组成部分 |
storage_backend | 必填,s3/gcs/azure | 决定object_store_config与thanos_object_store_config的生成分支(见 config.libsonnet) |
s3_path_style | false | 是否使用 path-style 访问 S3(自建 MinIO 等兼容对象存储通常需设为true) |
replication_factor | 默认3 | Ingester ring 副本因子,直接注入loki.ingester.lifecycler.ring.replication_factor |
memberlist_ring_enabled | 默认false | 为所有 ring 启用 memberlist 而不再依赖 Consul,详见下文 |
using_tsdb_shipper | true | TSDB shipper 模式,同时自动将 querier 切换为有状态部署(shipper.libsonnet 中stateful_queriers强制为true) |
index_period_hours | 默认24 | 索引表周期(1 天),schema_config中period字段由它生成 |
table_prefix | 默认取namespace | 索引表名前缀 |
关于from日期的生产建议
Tanka 官方文档特别强调:schema_config.configs[0].from表示该 schema 生效的第一天,建议设置为当前日期前 7 天以内(例如今天2021-01-15就设2021-01-08)。原因是 Loki 默认只接受过去 1 周(168h)内的日志(由reject_old_samples_max_age控制,见 config.libsonnet 中的reject_old_samples: true与reject_old_samples_max_age: '168h')。同时官方建议新装环境统一采用 TSDB(store: tsdb、schema: v13)。
对象存储配置的三套后端
config.libsonnet 根据storage_backend自动生成object_store_config:
- S3:拼接
s3://<access_key>:<secret>@<address>/<bucket>形式的 endpoint URL;若s3_access_key为空则生成无凭证 URL;s3_bucket_region非空时附带region。s3forcepathstyle由s3_path_style控制。 - GCS:
gcs.bucket_name来自gcs_bucket_name,且该字段在 config.libsonnet 中声明为必填(error 'must specify GCS bucket name')。 - Azure:
azure.container_name/account_name必填,account_key非空才写入,官方注释建议通过环境变量注入密钥而非明文写入清单。
若设置use_thanos_objstore: true,则会额外生成thanos_object_store_config并注入storage_config.object_store,这也是 config.libsonnet 中use_thanos_objstore分支的用途。
部署、预览与清理
配置完成后执行:
tk show environments/loki # 渲染出即将部署的全部 Kubernetes 清单 tk apply environments/loki # 部署到集群 tk delete environments/loki # 从集群删除环境tk show可先输出 YAML 供审查;所有清单由loki.libsonnet组合出的组件(gateway Deployment、distributor/querier Deployment、ingester StatefulSet、各 Service、PDB、ConfigMap、memcached、consul/memberlist 等)构成。
微服务组件的资源规格与高可用设计
各组件清单直接给出生产级默认参数,可结合源码核对:
- ingester(ingester.libsonnet):StatefulSet 3 副本,挂载
ingester-data(默认10Gi,fastStorageClass)与ingester-wal(默认150Gi)两块 PVC;请求1 CPU/7Gi、限制2 CPU/14Gi;TerminationGracePeriodSeconds = 4800(80 分钟)以保障 TSDB block 与 WAL 落盘;readiness 探针走/ready。 - distributor(distributor.libsonnet):Deployment 3 副本,请求
500m/2500Mi;默认启用 TopologySpreadConstraints(kubernetes.io/hostname,maxSkew=1,ScheduleAnyway)均匀打散;PDBmaxUnavailable=1。 - querier(querier.libsonnet):默认 Deployment 3 副本;在 TSDB shipper 模式下由 shipper.libsonnet 强制转为 StatefulSet,并为
/data/tsdb-index、/data/tsdb-cache挂载querier-dataPVC。 - query-frontend / query-scheduler / ruler / compactor / index-gateway / memcached:分别由 query-frontend.libsonnet、query-scheduler.libsonnet、ruler.libsonnet、compactor.libsonnet、index-gateway.libsonnet、memcached.libsonnet 定义。缓存默认接入 memcached(chunk cache 与 query-range results cache 均指向
memcached.<namespace>.svc.cluster.local)。
多可用区 Ingester(Multizone Ingesters):README 的核心专题
production/ksonnet/README.md 除指向 Tanka 安装文档外,专门给出多可用区 Ingester 的配置片段。这是 ksonnet 库面向生产高可用场景的核心能力:将 ingester 拆分为zone-a、zone-b、zone-c三个分区,实现可用区感知复制(zone-aware replication)——当单个可用区故障时,写入与查询仍可通过其余区完成。
官方给出的配置片段
_config+: { multi_zone_ingester_enabled: false, multi_zone_ingester_migration_enabled: false, multi_zone_ingester_replicas: 0, multi_zone_ingester_max_unavailable: 25, }(注意:README 中的数值为示意/默认态——enabled: false表示默认关闭该特性;启用时请按下文语义显式设置。)
参数语义与源码实现(multi-zone.libsonnet)
| 参数 | 默认值 | 语义 |
|---|---|---|
multi_zone_ingester_enabled | false | 是否启用多可用区 ingester。启用后默认ingester_statefulset/ingester_service被移除,替换为三个 zone 的 StatefulSet |
multi_zone_ingester_migration_enabled | false | 迁移模式。为true时保留默认ingesterStatefulSet 与 Service,且 PDB 被设为maxUnavailable=0(迁移期间不允许 Kubernetes 终止任何 ingester 副本) |
multi_zone_ingester_replicas | 3 | 三个 zone 的副本总数,每个 zone 实际副本数 =ceil(replicas / 3)(见newIngesterZoneStatefulSet) |
multi_zone_ingester_max_unavailable | max(1, floor(replicas/9)) | 滚动发布时允许的最大不可用副本数,写入rollout-max-unavailableannotation,由 rollout-operator 消费 |
multi_zone_ingester_replication_write_path_enabled/..._read_path_enabled | 均true | 分别控制写入(distributor/ruler)与读取(querier)路径是否注入distributor.zone-awareness-enabled: true,开启 zone-aware 复制 |
multi_zone_ingester_use_topology_spread | false | 为 zone StatefulSet 添加 TopologySpreadConstraints(kubernetes.io/hostname,maxSkew=multi_zone_ingester_topology_spread_max_skew,whenUnsatisfiable=multi_zone_ingester_topology_spread_when_unsatisfiable) |
multi_zone_ingester_name_prefix | 'ingester-zone' | zone StatefulSet 命名前缀,生成ingester-zone-a/b/c |
multi_zone_ingester_exclude_default | false | 为true时向 distributor/ruler 注入distributor.excluded-zones: zone-default,将默认 zone 排除在复制之外 |
multi_zone_default_ingester_zone | false | 为true时给默认 ingester 容器注入ingester.availability-zone: zone-default |
三个 zone 的生成逻辑
从 multi-zone.libsonnet 的源码结构可以看到:
- 每个 zone(a/b/c)生成三件套:StatefulSet(
ingester-zone-<zone>)、headless Service(ClusterIP: None,仅为提供 StatefulSet 的网络身份)、容器(通过ingester.availability-zone: zone-<zone>标志区分)。 - zone StatefulSet 使用
updateStrategy.type: OnDelete+rollout-group: ingester标签 +rollout-max-unavailableannotation,并配套ingester-rollout-pdb(maxUnavailable=1),交由rollout-operator做滚动发布管理(rollout-operator.libsonnet 中 DeploymentMaxSurge=0 / MaxUnavailable=1,保证同一时刻只有一个 operator 在运行)。 - 默认启用 podAntiAffinity:同 zone 的多个副本可以调度到同一节点,但不同 zone 的副本不允许同节点(matchExpressions:
rollout-group in [ingester]且name notin [当前zone名],topologyKey 为 hostname)。这样单节点故障只影响一个 zone。 - 启用 zone-aware 复制后,distributor/querier/ruler 均注入
distributor.zone-awareness-enabled: true(对应 Loki 的distributor.zone-awareness-enabled配置,实现复制时优先选择不同 zone 的 ingester 副本)。
迁移路径:从单区到多可用区
multi-zone.libsonnet 对ingester_statefulset、ingester_service、ingester_pdb的覆盖逻辑给出了清晰的迁移语义:
- 初始状态:
multi_zone_ingester_enabled: false,运行默认ingesterStatefulSet。 - 开启迁移:
multi_zone_ingester_enabled: true且multi_zone_ingester_migration_enabled: true—— 旧ingesterStatefulSet/Service 继续保留,PDB 强制maxUnavailable=0,同时三个 zone 的 StatefulSet 开始创建,数据逐步复制。 - 迁移完成:
multi_zone_ingester_migration_enabled: false—— 默认ingesterStatefulSet/Service/PDB 被移除,流量完全由三个 zone 接管。
Ring 后端:memberlist 与 Consul 的选择
config.libsonnet 默认使用 Consul 作为所有 ring(ingester/distributor/ruler/pattern-ingester)的 KV store;当memberlist_ring_enabled: true时,memberlist.libsonnet 会将所有 ring 切换为 memberlist,并:
- 通过 headless Service
gossip-ring.<namespace>.svc.cluster.local:7946(DNS+SRV)发现 gossip 成员; - 为 distributor/ingester/querier/ruler/compactor/query-scheduler/index-gateway 追加
gossip-ring端口(7946)与loki_gossip_member: true标签; - 提供
memberlist_cluster_label(默认<cluster>.<namespace>)与校验开关,防止跨集群 gossip 串扰; - 若未启用 migration,则彻底移除 consul 的 Deployment/Service/ConfigMap/RBAC 资源。
若从 Consul 平滑迁移到 memberlist,memberlist.libsonnet 还内置了multikv_migration_enabled/multikv_primary/multikv_secondary/multikv_mirror_enabled等参数,支持 primary-secondary 双 KV 存储的五步滚动迁移(先 multi 镜像、再交换主从、最后拆除)。
网关(gateway)模块:认证与路由
gateway.libsonnet 生成一个基于nginx:1.15.1-alpine的 3 副本 Deployment(gateway-secret 存放 htpasswd,gateway-config 渲染 nginx.conf),负责对外统一入口并完成 Basic Auth 与租户头注入:
location = /api/prom/push与location = /loki/api/v1/push→distributor(写入路径);location = /api/prom/tail与location = /loki/api/v1/tail→querier(WebSocket 升级支持);- 其余
~ /api/prom/.*与~ /loki/api/.*→query-frontend(查询路径); - 每个请求注入
X-Scope-OrgID(默认租户1,可用gateway_tenant_id覆盖),支持gateway_server_snippet追加自定义 server 指令。
日志采集:Grafana Alloy
Tanka 文档推荐的日志采集组件是 Grafana Alloy(替代已归档的 promtail)。在 Kubernetes 中安装 Alloy 后,将采集到的日志通过 gateway 的/loki/api/v1/push(或/api/prom/push)推送到本部署的 distributor 即可,具体安装步骤参见 Tanka 文档中引用的 Alloy 安装指南。
生产落地清单(Checklist)
namespace与storage_backend(s3/gcs/azure)必须显式设置,否则 Jsonnet 求值直接报错(见 config.libsonnet 中的error断言)。- 新装环境使用 TSDB:
store: tsdb、schema: v13,并设置using_tsdb_shipper: true。 schema_config.configs[0].from设为当前日期 7 天内,与reject_old_samples_max_age: 168h保持一致。- 生产开启
memberlist_ring_enabled: true免去 Consul 运维负担;如已有 Consul 依赖,按 memberlist 迁移五步走。 - 多可用区需求:置
multi_zone_ingester_enabled: true,按需调multi_zone_ingester_replicas与multi_zone_ingester_max_unavailable,先以migration_enabled: true过渡再切换。 - 前置反向代理认证(gateway 已内置 htpasswd + nginx),牢记 Loki 无内置认证。
参考资源(仓库内)
- 官方 Tanka 安装文档:docs/sources/setup/install/tanka.md
- ksonnet 入口组合:production/ksonnet/loki/loki.libsonnet
- 全局配置与默认值:production/ksonnet/loki/config.libsonnet
- 多可用区实现:production/ksonnet/loki/multi-zone.libsonnet
- Ring/memberlist 迁移:production/ksonnet/loki/memberlist.libsonnet
- 网关 nginx 配置:production/ksonnet/loki/gateway.libsonnet
- 组件规格:ingester / querier / distributor 见 production/ksonnet/loki/ingester.libsonnet、production/ksonnet/loki/querier.libsonnet、production/ksonnet/loki/distributor.libsonnet
【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考