在 Nomad 上以微服务模式部署 Loki:job.nomad.hcl 与 config.yml 全解析
在 Nomad 上以微服务模式部署 Lokijob.nomad.hcl 与 config.yml 全解析【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki本文以 examples/nomad/loki-microservices/README.md 及其配套的 job.nomad.hcl 与 config.yml 为主体系统讲解如何在 HashiCorp Nomad 集群中以微服务Microservices模式部署 Loki包括作业定义结构、统一配置文件的每个关键参数、运行与扩缩容操作以及面向生产的 HTTP 端点安全Consul Connect Traefik 认证与 GRPC 通信加密Vault PKI mTLS方案。读完本文你将能够基于这套示例搭建一套可横向伸缩、具备健康检查与安全防护的分布式 Loki 集群。微服务模式为什么需要它Loki 本质上是一个由众多微服务组成的分布式系统但它采用了一种独特的构建模型所有微服务都打包在同一个二进制中通过-target命令行参数决定启动时运行哪些组件参见 docs/sources/get-started/deployment-modes.md 中的说明。微服务分布式部署模式正是把每个组件作为独立进程运行每个进程通过-target指定自己的职责。与单机模式-targetall和高可用单机模式相比微服务模式的特点是维度单机模式HA 单机模式微服务模式持久性✅✅✅高可用❌✅✅执行路径分离❌❌✅运维复杂度低中高可扩展性低中高微服务模式以最高的运维复杂度换取最细粒度的扩展能力官方建议用于规模很大的集群或需要精确控制各组件伸缩与集群运维的场景。本文示例在 Nomad 中实际部署了 8 个组件compactor、ruler、distributor、ingester、querier、query-scheduler、query-frontend、index-gateway覆盖了读写链路的全部核心进程。Loki 微服务模式架构图前置条件与文件结构根据 examples/nomad/README.md 的说明运行这套示例需要满足以下条件硬性要求安装了较新版本的 Nomad且 Docker driver 健康可用Nomad 启用了 Consul 集成服务发现与健康检查依赖 Consul可访问 S3 存储示例默认使用 S3 兼容对象存储如 MinIO。可选要求Vault 集成用于安全地提供 S3 凭据配置了 Consul provider 的 Traefik用于在多个 Loki 实例之间负载均衡。示例目录包含三个文件文件作用README.md使用说明与生产建议job.nomad.hclNomad 作业定义8 个组件组、任务、服务注册、健康检查config.yml所有组件共用的 Loki 配置文件通过 Consul Template 注入一个值得注意的细节README 开篇提到使用 boltdb-shipper但仓库内实际的 config.yml 已采用TSDB 索引 S3 对象存储 index-gateway的组合store: tsdb、schema: v13、tsdb_shipper配置块并以 job.nomad.hcl 中的index-gateway组与之配套。实际部署时应以配置文件为准TSDB 是当前推荐的索引后端index-gateway 负责为 querier 提供统一的索引读取入口。作业定义全览job.nomad.hcl 的 8 个组件组job.nomad.hcl 定义了一个名为loki的作业内含 8 个 group每个 group 对应一个 Loki 组件每个组内部都以-target组件名启动同一个grafana/loki:${var.version}镜像组默认副本数启动 target说明compactor1compactor索引压缩、删除请求与保留策略执行ruler1ruler告警与记录规则评估distributor2distributor写入入口校验并转发日志流ingester2ingester内存写入、WAL 与 flushquerier2querier执行查询经 scheduler 调度query-scheduler2query-scheduler查询队列与调度query-frontend2query-frontend查询入口、缓存与拆分index-gateway1index-gatewayTSDB 索引读取网关作业文件顶部通过 HCL 变量控制版本variable version { type string description Loki version default 3.7.2 }所有任务统一使用 Docker driver、以nobody用户运行、kill_timeout 90s给 ingester 等组件足够的优雅退出时间以 flush WAL 数据并追加三个标准启动参数args [ -targetdistributor, -config.file/local/config.yml, -config.expand-envtrue, ]其中-config.expand-envtrue允许配置文件中使用${ENV_VAR}语法展开环境变量S3 凭据即通过此机制注入。有状态组件的关键配置ingester写路径核心使用ephemeral_disk的 sticky 模式让/alloc数据在作业更新时尽量保留WAL 就存放在NOMAD_ALLOC_DIR/data/walephemeral_disk { size 4000 sticky true }同时用 constraint 保证副本分布在不同故障域必须与配置文件中的instance_availability_zone保持一致constraint { distinct_property node.unique.name # distinct_property node.datacenter # distinct_property attr.platform.aws.placement.availability-zone }compactor、ruler、index-gateway同样配置了 sticky 的 ephemeral_disk用于存放索引目录、规则文件与压缩工作目录。服务注册与健康检查每个组都注册了 Consul 服务并通过meta携带两个标签——alloc_id取 Nomad 环境变量NOMAD_ALLOC_ID和component组件名这两者会被后文 Prometheus 的 relabel 规则消费service { name loki-distributor port http meta { alloc_id NOMAD_ALLOC_ID component distributor } ... }健康检查统一指向 Loki 内置的/ready端点例如check { name Loki distributor port http type http path /ready interval 20s timeout 1s }query-scheduler与index-gateway还通过network块固定了 GRPC 端口映射static 9096/static 9097以便其他组件通过 Consul DNS 稳定寻址如配置中的loki-query-scheduler.service.consul:9096。S3 凭据注入需要访问对象存储的组compactor、ruler、ingester、querier、index-gateway通过 template 生成环境变量文件template { data -EOH S3_ACCESS_KEY_IDaccess_key S3_SECRET_ACCESS_KEYsecret_access_key EOH destination secrets/s3.env env true }生产环境建议把access_key占位符替换为 Vault 模板参见 examples/nomad/README.md 中基于secret/minio/loki或 AWS secret engine 的写法避免明文凭据入库。运行与扩缩容在loki-microservices目录内直接运行nomad run job.nomad.hcl新版 Nomad 也支持显式的作业命令形式nomad job run job.nomad.hcl如需部署指定版本可覆盖variable.version默认值或从命令行传入nomad job run -varversion3.7.2 job.nomad.hcl扩缩容两种方式方式一修改作业文件中的count。注意本示例的作业由 8 个 group 组成README 中group loki是泛指需要按组分别调整各自group块内的count然后重新运行nomad run job.nomad.hcl应用变更。方式二使用 Nomad CLI 动态扩缩容推荐无需改文件nomad job scale loki distributor count其中loki是作业名distributor是组名count为目标副本数。得益于分布式部署模式你可以独立、精确地伸缩每个组件——例如只增加 querier 以提升查询吞吐或只增加 ingester 以承接写入洪峰这正是微服务模式相比单机模式的核心价值。统一配置解析config.yml所有组件共享同一份 config.yml它大量使用 Nomad 环境变量模板占位符{{ env ... }}由 Consul Template 在渲染时替换为实际值。逐块解读如下。server监听端口与 TLS 预留server: log_level: info http_listen_port: {{ env NOMAD_PORT_http }} grpc_listen_port: {{ env NOMAD_PORT_grpc }} # grpc_tls_config: # client_auth_type: RequireAndVerifyClientCert # client_ca_file: /secrets/certs/CA.pem # cert_file: /secrets/certs/cert.pem # key_file: /secrets/certs/key.pemHTTP/GRPC 端口直接取自 Nomad 动态分配的端口环境变量。注释掉的grpc_tls_config是为后文 mTLS 方案预留的开关启用后要求客户端证书必须通过 CA 校验RequireAndVerifyClientCert。common复制因子、ring 与故障域common: replication_factor: 2 # Tell Loki which address to advertise instance_addr: {{ env NOMAD_IP_grpc }} # Failure domain # Must be the same as specified in job constraints instance_availability_zone: {{ env node.unique.name }} zone_awareness_enabled: true ring: # Tell Loki which address to advertise in ring instance_addr: {{ env NOMAD_IP_grpc }} kvstore: store: consul prefix: loki/ consul: host: {{ env attr.unique.network.ip-address }}:8500replication_factor: 2每个日志流被复制到 2 个 ingesterinstance_addr与ring.instance_addr向 ring 宣告的地址取自NOMAD_IP_grpc解决多网卡/动态 IP 场景下的地址误宣告问题这也是 examples/nomad/README.md 特意强调不用 memberlist 而改用 Consul ring 的原因instance_availability_zone取自node.unique.name配合zone_awareness_enabled: true启用感知故障域的复制必须与作业文件中 ingester 组的 constraint 对应ring 状态存储使用Consulkvstore.store: consul前缀loki/由 distributor、ingester、compactor、ruler 等组件共享这也是各组件 ring 端点/distributor/ring、/ingester/ring等的数据来源。对应的 Ring 配置在源码中定义于 pkg/loki/common/common.go 的RingConfig与InstanceAddr字段。ingesterWAL 与回放控制ingester: wal: dir: {{ env NOMAD_ALLOC_DIR }}/data/wal flush_on_shutdown: true replay_memory_ceiling: 1GWAL 存放在 sticky 的/alloc/data/wal保证重启后可回放未 flush 的数据flush_on_shutdown: true让进程在退出时主动 flushreplay_memory_ceiling: 1G限制 WAL 回放期间的内存占用上限。frontend 与 frontend_worker查询链路接入frontend: scheduler_address: loki-query-scheduler.service.consul:9096 compress_responses: true log_queries_longer_than: 5s frontend_worker: scheduler_address: loki-query-scheduler.service.consul:9096query-frontend 与 querier 的 worker 都通过 Consul DNS 连接到 query-scheduler 的固定端口 9096实现查询请求的排队、调度与结果缓存compress_responses: true压缩响应减少带宽log_queries_longer_than: 5s记录慢查询。schema_config 与 storage_configTSDB S3schema_config: configs: - from: 2022-05-15 store: tsdb object_store: s3 schema: v13 index: prefix: index_ period: 24h storage_config: tsdb_shipper: active_index_directory: {{ env NOMAD_ALLOC_DIR }}/data/index cache_location: {{ env NOMAD_ALLOC_DIR }}/data/index-cache index_gateway_client: server_address: loki-index-gateway.service.consul:9097 aws: endpoint: https://minio.service.consul bucketnames: loki region: us-west-1 access_key_id: ${S3_ACCESS_KEY_ID} secret_access_key: ${S3_SECRET_ACCESS_KEY} s3forcepathstyle: true索引使用 TSDB 格式schema v13每日一个索引周期tsdb_shipper把活跃索引放在/alloc/data/indexNomad 更新作业时尽量保留并通过index_gateway_client指向 index-gateway 的 9097 端口读取共享索引对象存储指向 S3 兼容端点示例为https://minio.service.consulbucketnames: loki、s3forcepathstyle: trueMinIO 等兼容存储必需凭据来自-config.expand-envtrue展开的${S3_ACCESS_KEY_ID}/${S3_SECRET_ACCESS_KEY}。compactor、ruler 与 limits_configcompactor: working_directory: {{ env NOMAD_ALLOC_DIR }}/compactor delete_request_store: s3 compaction_interval: 24h retention_enabled: true ruler: alertmanager_url: https://alertmanager.service.consul enable_alertmanager_v2: true enable_api: true external_url: https://loki-ruler.service.consul rule_path: {{ env NOMAD_ALLOC_DIR }}/tmp/rules storage: type: local local: directory: {{ env NOMAD_TASK_DIR }}/rules wal: dir: {{ env NOMAD_ALLOC_DIR }}/data/ruler limits_config: reject_old_samples: true reject_old_samples_max_age: 168hcompactor 每 24h 压缩一次索引并将删除请求存储到 S3同时开启保留策略ruler 使用local规则存储规则文件通过 job.nomad.hcl 中 ruler 组的dynamic template从rules/**目录注入容器详见 examples/nomad/README.mdreject_old_samples拒绝超过 168h 的陈旧写入防止乱序数据污染。生产建议一统一采集各组件指标将 config.yml 中server.http_listen_port暴露的/metrics端点接入 Prometheus。由于所有组件都注册了 Consul 服务可以直接用consul_sd_configs一次性发现全部 8 个组件- job_name: loki consul_sd_configs: - services: - loki-compactor - loki-ruler - loki-distributor - loki-ingestor - loki-querier - loki-index-gateway - loki-query-frontend - loki-query-scheduler relabel_configs: - source_labels: [__meta_consul_service_metadata_alloc_id] target_label: instance - source_labels: [__meta_consul_service_metadata_component] target_label: componentrelabel 规则把 job 中service.meta里声明的alloc_id和component映射为 Prometheus 的instance与component标签从而能在告警与面板中区分同一组件的不同副本及组件类型。生产建议二用 Consul Connect 保护 HTTP 端点微服务模式下 HTTP 端点push/query 接口、ring 状态页不应直接暴露。README 给出了一套结合 Nomad bridge 网络、Consul Connect 与 Traefik 的加固方案。第一步网络设为bridge模式并显式声明三个端口——http、grpc以及专供健康检查使用的healthnetwork { mode bridge port http {} port health {} port grpc {} }第二步任务中发布全部三个端口不要漏掉health并在 Loki 配置中将 HTTP 绑定到回环地址 127.0.0.1使外部无法直接访问task distributor { driver docker user nobody kill_timeout 90s config { image grafana/loki:${var.versions.loki} ports [ http, health, # do not forget to publish health port grpc, ]server: http_listen_address: 127.0.0.1 http_listen_port: 80第三步注册服务时开启 Consul Connect sidecar proxy把本地 80 端口包起来仅通过expose开放/metrics与/ready两个路径给健康检查使用同时为 Traefik 打上路由与 basicauth 标签service { name loki-distributor port http meta { alloc_id NOMAD_ALLOC_ID component distributor } tags [ traefik.enabletrue, traefik.consulcatalog.connecttrue, traefik.http.routers.loki-distributor.entrypointshttps, traefik.http.routers.loki-distributor.ruleHost(loki-distributor.service.consul), traefik.http.middlewares.loki-distributor.basicauth.userspromtail:$$apr1$$wnih40yf$$vcxJYiqcEQLknQAZcpy/I1, traefik.http.routers.loki-distirbutor.middlewaresloki-distributorconsulcatalog, traefik.http.routers.loki-distributor-ring.entrypointshttps, traefik.http.routers.loki-distributor-ring.ruleHost(loki-distributor.service.consul) Path(/distributor/ring), traefik.http.middlewares.loki-distributor-ring.basicauth.usersdevops:$apr1$bNIZL02A$QrOgT3NAOx.koXWnqfXbo0, traefik.http.routers.loki-distributor-ring.middlewaresloki-distributor-ringconsulcatalog, ] check { name Loki distributor port health type http path /ready interval 20s timeout 1s } connect { sidecar_service { proxy { local_service_port 80 expose { path { path /metrics protocol http local_path_port 80 listener_port http } path { path /ready protocol http local_path_port 80 listener_port health } } } } } }注意Traefik 标签中的 basicauth 密码以$$转义$防止 Nomad 模板引擎提前展开/distributor/ring这类 ring 状态页单独路由并配置不同的凭据。其余组件compactor、ingester、ruler、query-frontend 等按相同模式套用即可作业文件中已为各组件预置了对应的 Traefik 路由标签见 job.nomad.hcl。basicauth 凭据可用如下命令生成docker run --rm httpd:alpine htpasswd -nb promtail password123生产建议三用 Vault PKI 与 mTLS 保护 GRPC 通信Consul Connect 无法用于组件间的 GRPC 通信——因为部分组件需要连接其他组件的所有实例如 ingester 与 querier 之间而 Connect 面向固定拓扑。README 给出的替代方案是用 Vault 的 PKI secret engine 为每个实例签发证书实现 mTLS。步骤 1在locals中定义证书文件与 Vault 返回字段的映射让后续模板更简洁locals { certs { CA issuing_ca, cert certificate, key private_key, } }步骤 2为每个任务添加dynamic template遍历local.certs生成三个文件CA、证书、私钥每份证书 TTL 10 天、包含服务通用名与实例 IP 的ip_sansdynamic template { for_each local.certs content { data -EOH {{- with secret pki/issue/internal ttl10d common_nameloki-component_name.service.consul (env attr.unique.network.ip-address | printf ip_sans%s) -}} {{ .Data.${template.value} }} {{- end -}} EOH destination secrets/certs/${template.key}.pem change_mode restart splay 5m } }证书续期时change_mode restart任务会自动重启以加载新证书splay 5m则避免所有实例同时重启。步骤 3启用配置文件中预留的 TLS 开关——服务端开启server.grpc_tls_config要求校验客户端证书各客户端启用grpc_client_config并指定 CA/证书/私钥路径与 snappy 压缩例如ingester_client: grpc_client_config: grpc_compression: snappy tls_enabled: true tls_ca_path: /secrets/certs/CA.pem tls_cert_path: /secrets/certs/cert.pem tls_key_path: /secrets/certs/key.pemconfig.yml 中query_scheduler、frontend、frontend_worker以及storage_config.tsdb_shipper.index_gateway_client均已预留同结构的注释块按需逐一取消注释并填写证书路径即可。从源码看配置背后的机制这套配置的每一处都对应 Loki 源码中的真实实现Ring 与地址宣告common.ring、instance_addr、instance_availability_zone等字段定义于 pkg/loki/common/common.go 的CommonConfig/RingConfig结构含InstanceAddr、InstanceInterfaceNames等字段ring 状态最终通过 Consul KV 共享各组件 ring 端点如/distributor/ring、/ingester/ring、/compactor/ring、/ruler/ring即由此驱动健康检查所有组的check都依赖 Loki 内置的/ready端点它反映组件是否完成启动与 ring 注册是 Consul 健康检查与 Consul Connectexpose的共同依据-config.expand-envtrue配合 template 生成的s3.env环境变量实现 S3 凭据与配置的运行时注入避免在配置文件中硬编码密钥。小结通过 job.nomad.hcl 与 config.yml 的组合可以快速在 Nomad 上拉起一套由 8 个可独立伸缩组件构成的分布式 LokiTSDB 索引 S3 对象存储、Consul 支撑的 ring 与服务发现、基于/ready的全面健康检查以及面向生产的 HTTPConsul Connect Traefik basicauth与 GRPCVault PKI mTLS双重加固方案。如果需要复杂度更低的起点可以参考仓库内的另外两套 Nomad 示例examples/nomad/loki-monolithic/README.md单机模式与 examples/nomad/loki-ha-monolithic/README.md高可用单机模式更多 Nomad 通用技巧负载均衡、Vault 凭据、ruler 规则注入可查阅 examples/nomad/README.md。根据集群规模与运维能力在三者之间选择才是部署 Loki 的正确姿势。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考