海外云服务器搭建 Grafana Loki + Alloy + Grafana 集中式日志聚合系统完整教程(2026最新版)
Meta Description: 海外云服务器搭建 Grafana Loki 日志聚合系统完整教程:Docker Compose 一键部署 Loki + Grafana Alloy + Grafana 三件套,覆盖 Nginx/系统/Docker 容器日志采集、LogQL 查询语法入门、标签基数(cardinality)避坑、日志保留(Retention)配置、Nginx 反向代理 + Let's Encrypt HTTPS、数据备份与安全加固,另含 Promtail 已于 2026 年 3 月 2 日 EOL 的迁移说明、服务器配置推荐表、服务商价格参考表、自建与 Grafana Cloud 日志托管成本对比表,以及 9 条常见问题 FAQ。
> 关键词:Grafana Loki 搭建教程、Loki 日志聚合、Grafana Alloy 部署、Promtail EOL 迁移、海外云服务器日志系统、LogQL 查询语法、Docker 日志采集、Nginx 日志采集、日志保留 retention 配置、自建 ELK 替代方案
前言:日志不是"多存一点",而是"出事时能查到那一行"
一句话答案:Grafana Loki 是一个"像 Prometheus 一样只索引标签、不索引全文"的日志聚合系统,用一套 Docker Compose 就能在海外云服务器上把 Loki(存储与查询)+ Grafana Alloy(采集)+ Grafana(可视化)三件套装好;它默认监听 3100 端口,能把 Nginx 访问日志、系统日志、Docker 容器日志统一收集到一个地方,用 LogQL 按标签秒级检索,磁盘占用通常只有原始日志的十分之一左右。
如果你已经按本站的教程搭好了 Prometheus + Grafana 和 Uptime Kuma,那么你手里其实已经有了"数字"(CPU、内存、QPS 的趋势)和"红绿灯"(服务是死是活)。但当一次故障真的发生时,这两样都回答不了下面这个问题:
- 凌晨 3 点接口大面积 502,具体的错误堆栈是什么? 是数据库连接池耗尽,还是上游超时? - 用户说"我下单失败了",你怎么在几百万行日志里定位到他这一次请求? - 某台机器磁盘 IO 突然飙高,是哪个进程、在写什么? - 上周改的 Nginx 配置,到底有没有生效?访问日志里能不能看出来?
这些问题的答案,只存在于日志里。指标告诉你有问题,日志告诉你为什么。可观测性有个业界公认的"三支柱"说法:Metrics(指标)、Logs(日志)、Traces(链路)。本站的 Prometheus + Grafana 教程 覆盖了指标层,Uptime Kuma 教程 覆盖了可用性层,而日志层一直是空缺的——本文就是来补上这一块的。
传统上做日志聚合,大家第一反应是 ELK(Elasticsearch + Logstash + Kibana)或它的开源分支 OpenSearch。它很强大,但代价是:全文索引意味着每写一条日志都要建一份倒排索引,一台小 VPS 跑不动,起步就要 8G 内存,还要维护一整套 JVM 组件。而 Loki 走的是完全相反的路子——它只给日志打标签、按标签建索引,日志正文压缩后存起来,查询时先按标签缩小范围再顺序扫描。代价是"不能全文模糊搜索",换来的是一台 2 核 4G 的机器就能撑起几个站的日志,成本降到 ELK 的零头。
本文给出可直接复制执行的完整命令,以 Ubuntu 22.04(Docker 方式)为主线,覆盖:买服务器 → 装 Docker → 一套 Compose 拉起 Loki/Grafana/Alloy → 写 Alloy 采集配置 → Grafana 接入数据源并查日志(LogQL 入门)→ 开启日志保留(这一步不做磁盘会被写满) → Nginx 反向代理 + Let's Encrypt 自动 HTTPS → 备份升级与安全加固,最后附服务器配置推荐表、服务商价格参考表、自建与 Grafana Cloud 的成本对比表,以及 9 条常见问题。
> 🚀 还没有海外云服务器?通过 5.chengzicloud.cloud 选购阿里云/AWS/腾讯云国际版,享专属折扣和中文技术支持。
一、先分清边界:Loki、ELK、Prometheus、云日志服务各管什么
在动手前先做一次选型。日志系统的选型错误不是"多花点钱",而是整篇文章的命令都不适用——因为它们的定位根本不同。本站已经有过"监控"与"可用性"两篇专文,读者最容易困惑的就是"这三个到底有什么区别、我要装哪个"。下面这张边界表一次说清,本文只负责 Loki 这一层。
| 既有 / 相邻方案 | 它回答的问题 | 与本文的关系 | |---|---|---| | Prometheus + Grafana(本站已有专文) | "CPU/内存/磁盘/连接数现在是多少、趋势如何" | 它是指标层:数字与趋势。本文是日志层:正文与错误堆栈。两者互补,不是二选一 | | Uptime Kuma(本站已有专文) | "服务活着吗、多久没响应、挂了通知谁" | 它是可用性层。它只说"挂了",不告诉你"为什么挂"——那个答案在日志里 | | Umami 网站统计(本站已有专文) | "有多少人来了、看了哪些页" | 它是业务层流量分析,与日志无重叠 | | Promtail(官方采集器) | "把日志文件推给 Loki" | 已于 2026 年 3 月 2 日 EOL,本文改用官方指定的继任者 Grafana Alloy(见第二节) | | ELK / OpenSearch | "全文索引 + 模糊检索 + 复杂聚合" | 它索引日志正文,能全文搜;代价是资源与运维成本高一个数量级。本文是标签索引的轻量替代 | | 云厂商日志服务(阿里云 SLS / 腾讯云 CLS / AWS CloudWatch Logs) | "托管日志,按量付费" | 开箱即用,但只擅长自家资源、按写入量计费、跨厂商统一视图做不了;本文是它的自托管替代 | | Nginx 反向代理 + Let's Encrypt SSL(本站已有专文) | "怎么给 Web 服务加域名和 HTTPS" | 本文第八节会用到它,但只讲 Loki/Grafana 特有的配置,其余直接复用那篇,不重复 | | Grafana Loki(本文) | "那次报错,具体是哪一行、哪个请求、什么堆栈" | 只讲这一层 |
一张表判断你该装哪个:
| 你的情况 | 推荐方案 | |---|---| | 只想知道服务挂没挂 | Uptime Kuma(1核1G 够) | | 想看性能趋势、做容量规划 | Prometheus + Grafana | | 出事要查错误堆栈、要按请求串联 | Loki(本文) | | 要全文模糊搜索、做复杂聚合、数据量 TB 级 | ELK / OpenSearch,或直接买云日志服务 | | 三者都要(真实运维的常态) | Prometheus 管指标 + Uptime Kuma 管可用性 + Loki 管日志,一套 Grafana 统一看板 |
一个必须提前说清的能力边界:Loki 的设计前提是"标签少而稳定,日志正文多而乱"。它不是搜索引擎——你不能像用 Google 那样在日志里搜任意一段文字(虽然 |= 过滤器能在候选块上扫描,但那是线性扫描,代价随范围增长)。如果你需要"模糊匹配 + 高亮 + 复杂聚合",Loki 会让你失望;如果你需要的是"按 job/host/level 快速缩小范围,再定位到具体错误",Loki 是性价比最高的选择。把这条边界说在前面,比装完才发现选错工具要有价值。
二、Loki 到底怎么存日志:标签索引 + 压缩块,以及它的命门
先把原理讲清楚,因为这决定了你后面怎么配、怎么不踩坑。
2.1 一句话原理:只索引标签,不索引正文
在 Loki 里,每一条日志属于一个 log stream(日志流),它由一组标签唯一确定。标签就是键值对,例如 job="nginx-access"、host="web-01"、level="error"。Loki 只对这组"标签组合"建索引,日志正文被压缩成 chunk(块) 丢进对象存储(本地磁盘或 S3/MinIO)。查日志时,Loki 先用标签把范围缩小到几个 log stream,再在这些块里顺序扫描匹配你给的关键词。
这个设计带来三个直接后果,全都是你必须提前知道的:
① 成本极低。 官方给的经验值是压缩后约为原始日志的 1/10 左右(同服务日志重复度高、压缩比好)。对比 ELK"每行都建倒排索引",同样的日志量,Loki 的磁盘与内存占用往往低一个数量级。这也是"2 核 4G 小机器就能跑"的根本原因。
② 标签基数(cardinality)是命门。 所有标签组合都会在内存里维护一份索引。如果某个标签的取值数量爆炸(比如把 user_id、request_id、trace_id、ip、URL 全路径当成标签),Loki 的内存会瞬间被撑爆,症状是 ingester 疯狂 GC 甚至 OOM 被杀。结论:标签必须是"低基数、高复用"的维度(环境、主机名、服务名、日志级别、机房),而 user_id / trace_id 这类高基数字段,应该放进日志正文或结构化元数据里,而不是标签。这条是 Loki 与 ELK 最大的思维差异,也是新手把 Loki 用崩的第一大原因。
③ 不支持纯全文模糊搜索。 你可以在已缩小范围后做 |=(包含)、!=(不含)、|~(正则)过滤,但那是在候选块上线性扫描,范围越大越慢。要快,就得靠标签先把范围切小。 所以"怎么打标签"这件事,比"怎么写查询"更重要。
2.2 最重要的时事变化:Promtail 已于 2026 年 3 月 EOL
网上绝大多数中文 Loki 教程,采集端写的都是 Promtail。但官方文档现在的措辞是:
> Promtail is end of life (EOL) as of March 2, 2026. Commercial support has ended. No future support or updates will be provided. All future feature development will occur in Grafana Alloy.
也就是说——Promtail 已经停止支持、不再有新功能,官方明确要求迁移到 Grafana Alloy。所以你如果照着两三年前的老教程装 Promtail,装起来的是一套已经 EOL 的组件。本文的采集端一律使用 Grafana Alloy,这也是官方 getting-started 示例 compose 里已经替换成的组件。
好消息是迁移不难:官方提供了把 Promtail 配置一条命令转成 Alloy 配置的迁移工具;而对新部署的人来说,你根本不用管 Promtail 的语法——Alloy 的 loki.source.file + loki.write 两个组件就能覆盖绝大多数采集场景,语法比 Promtail 更清晰(见第五节)。
> 📌 一句话记法:Loki 是"存和查",Alloy 是"采集和推送",Grafana 是"看"。三者当前推荐版本为 Loki 3.7.x、Alloy 1.20.x、Grafana 13.x(请以官方 Releases 页为准,本文命令用通用标签,不写死小版本)。
三、服务器怎么选:瓶颈是磁盘 IO 与标签基数,不是 CPU
Loki 的资源画像和 GitLab 那种"内存吃货"不同,也和 Uptime Kuma 那种"几乎不吃资源"不同:它主要吃磁盘 IO 和内存,对 CPU 的需求反而不高。
第一,内存是硬约束,但取决于你打了多少标签。 一台机器、几十个 stream 的场景,1~2G 内存绰绰有余;但只要标签基数上去(比如给每个容器都打一堆动态标签),内存就会陡增。所以"宁可 1核2G 也不要 2核1G" 这条规律在这里同样适用——内存不够时,被 OOM Killer 杀的是 ingester,表现为"日志写不进去、查询报错"。
第二,磁盘要 SSD,容量按"保留天数 × 日增量 ÷ 10"估。 关键提醒:Loki 的日志保留(retention)默认是关闭的,写进去的日志会永久保留(官方原文:compactor.retention-enabled flag is not set, so the logs sent to Loki live forever)。不做任何配置的话,磁盘一定会被写满。第七节会专门讲怎么开 retention。容量估算大致是:先用 du 看一天原始日志量,除以 10 得到 Loki 实际占用,再乘以保留天数,最后留 50% 余量。
第三,CPU 几乎不挑,除非你开了大量正则解析。 单机日志场景 1~2 核足够;只有当你在 Alloy 里对每条日志做复杂的 loki.process 管道解析(正则提取、JSON 解析、指标生成)时,CPU 才会成为瓶颈——这些解析最好在采集端做,但也要注意别把采集机 CPU 打满。
第四,小规模用本地磁盘就够,规模上去再上对象存储。 Loki 的存储后端支持 filesystem(本地磁盘)、S3、GCS、Azure、Swift。单机(monolithic)部署用 filesystem 最省事;当日志量到几百 GB、或要做多副本时,再切到对象存储——本站已有 MinIO 自建对象存储教程,Loki 可以直接把 MinIO 当成 S3 后端用。
服务器配置推荐(按日志规模)
| 使用场景 | 推荐配置 | 为什么够用 |
|---|---|---|
| 1 台机器日志,尝鲜/测试 | 1核 2G + 40GB SSD | 少量 stream,常驻内存几百 MB,最省钱 |
| 1~3 台机器,正式使用(推荐) | 2核 4G + 100GB SSD | 留出 ingester 内存余量,Nginx/系统/Docker 三类日志一起收 |
| 5~10 台机器,保留 30 天 | 2核 8G + 200GB SSD | 标签与 stream 数量上升,内存要跟上;磁盘按日增量估算 |
| 多源高流量,Loki 与 Grafana 分离 | 4核 8G ×2,或 Loki + MinIO/S3 | 采集、存储、查询互相争资源时,拆开部署更稳 |
| 有大量结构化解析需求 | 4核 8G 起 | loki.process 正则/JSON 解析吃 CPU,采集端要留余量 |
> ⚠️ 不要把 Loki 和你的业务重负载塞在同一台 1 核小机器上。 Loki 的查询(尤其是跨天范围的大扫描)会瞬间吃满磁盘 IO,把同机业务拖慢。日志系统最值钱的能力是"出事时还能查得到",如果它和业务一起被拖垮,那它就白装了。
服务商价格参考
下面按"1核2G / 2核4G"这一实际推荐档整理公开官网参考区间:
| 服务商 | 1核2G 月付 | 2核4G 月付 | 数据中心 | 优点 | |---|---|---|---|---| | 阿里云国际版 ECS(突发性能型) | 约 $13~$20 | 约 $24~$36 | 新加坡/香港/硅谷 | 中文面板、支持支付宝、生态完整 | | AWS Lightsail | 约 $7(2GB 档) | 约 $12~$20(4GB 档) | 全球 20+ 区域 | 固定月费、含流量额度、开箱即用 | | 腾讯云国际版 CVM(轻量型) | 约 $10~$16 | 约 $20~$30 | 香港/新加坡/东京 | 低延迟到国内、微信支付 | | Vultr | 约 $10~$12(2GB 档) | 约 $20~$24(4GB 档) | 全球 32 个 | 按小时计费、随时销毁重建 | | DigitalOcean | 约 $12(2GB 档) | 约 $24(4GB 档) | 全球 14 个 | 文档完善、社区教程多 |
> 💡 日志机的选址原则和监控机一样:尽量靠近产生日志的业务机,因为 Alloy 是持续不断地推送日志,跨地域传输会消耗带宽;如果业务在多个地域,可以在每个地域各放一个 Alloy,统一推到同一个 Loki。通过 5.chengzicloud.cloud 购买以上平台还可享额外折扣。
> 声明:本文价格数据采集于 2026 年 9 月,仅为公开官网参考区间。云服务器价格随区域、计费方式和活动浮动,请以各厂商官网实时价格为准。除特别标注外,金额单位均为美元(USD)。
自建 Loki vs Grafana Cloud 日志托管
如果你不想自己运维,Grafana 官方提供了托管版(Grafana Cloud Logs,底层就是 Loki)。下面是官方定价页的公开档位,用来对比:
| 方案 | 免费额度 | 付费档与用量价 | |---|---|---| | 自建 Loki(本文) | 无 | 只有服务器成本:2核4G 约 $20~$24/月,磁盘另算;日志量不额外计费 | | Grafana Cloud Free | 每月 50 GB 写入、14 天保留 | 免费档够个人小站用 | | Grafana Cloud Pro | 超出免费额度后按量:处理 $0.050/GB、写入 $0.400/GB、保留 $0.100/GB | 另有 $19/月 平台费(含指标等基础额度),日志保留可到 30 天 |
> 🔍 怎么选:日志量在几十 GB/月、且不想碰运维 → Grafana Cloud 免费档很划算;日志量大(几百 GB/月)、或数据合规要求必须自持 → 自建 Loki 的边际成本近乎为零,只要付服务器费。这条"托管省钱还是自建省钱"的分界线,取决于你的量和运维意愿,没有标准答案——把两个数字摆出来,比空喊"自建更自由"诚实。
四、部署:一套 Compose 拉起 Loki + Grafana + Alloy
以 Ubuntu 22.04 + Docker 为主线(Debian 12 同样适用)。全程命令可直接复制执行。
第 1 步:系统初始化与防火墙
先更新系统并装好基础工具:
`bash
apt update && apt upgrade -y
apt install -y curl wget vim ufw ca-certificates
`
配置防火墙。三个端口都不要对全网开放:Loki 的 3100、Grafana 的 3000、Alloy 的 12345 默认全部只在本机监听,外部访问一律走 Nginx 反向代理(第八节):
`bash
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status
`
第 2 步:安装 Docker 与 Compose 插件
`bash
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker version
docker compose version
`
看到 Docker 与 Compose 的版本号输出即安装成功。如果 docker compose 报 "command not found",说明只装了旧版 docker-compose,先补装插件:
`bash
apt install -y docker-compose-plugin
`
> 🔴 CentOS 7 用户注意:Loki、Grafana、Alloy 官方分发的都是静态链接的 Go 二进制 + 官方容器镜像,因此 CentOS 7 的 glibc 2.17 不是问题(不会出现 code-server / Ollama / Meilisearch 那种 GLIBC_2.xx not found)。CentOS 7 上真正要补的是 Compose v2 插件——照着上面两条命令补装即可。
第 3 步:准备目录与三个配置文件
`bash
mkdir -p /opt/loki-stack && cd /opt/loki-stack
mkdir -p loki-data grafana-data alloy-data
`
(1)Loki 配置 loki-config.yaml —— 单机(monolithic)模式,本地文件系统存储:
`yaml
auth_enabled: false
server: http_listen_port: 3100 grpc_listen_port: 9095 log_level: warn
common: path_prefix: /loki storage: filesystem: chunks_directory: /loki/chunks rules_directory: /loki/rules replication_factor: 1 ring: kvstore: store: inmemory
schema_config: configs: - from: 2024-04-01 store: tsdb object_store: filesystem schema: v13 index: prefix: index_ period: 24h
limits_config: retention_period: 168h allow_structured_metadata: true
compactor: working_directory: /loki/compactor delete_request_store: filesystem retention_enabled: true retention_delete_delay: 2h retention_delete_worker_count: 150
ruler:
storage:
type: local
local:
directory: /loki/rules
`
下面这张表当作"每个配置段在干什么"的说明书,比在 YAML 里写注释更可靠(本博客的 Markdown 渲染器会把配置块里的井号注释当成标题,所以配置文件里一律不写注释):
| 配置段 | 作用 | 必须注意 |
|---|---|---|
| auth_enabled: false | 关闭多租户鉴权 | 单机自建场景简化配置;对外暴露时必须靠 Nginx 层鉴权 |
| server.http_listen_port: 3100 | Loki HTTP 端口 | 查询与写入 API 都在这里 |
| common.storage.filesystem | 用本地磁盘存 chunk 和索引 | 规模上去后可换成 S3/MinIO |
| schema_config.store: tsdb / schema: v13 | 索引类型与版本 | from 必须是过去的日期,写未来日期会导致索引不建 |
| limits_config.retention_period: 168h | 日志保留 7 天 | 必须配合 compactor 一起开才真正生效 |
| compactor.retention_enabled: true | 打开保留删除 | 默认关闭——不写这段,日志永久保留、磁盘必满 |
| compactor.delete_request_store | 删除请求的存储后端 | Loki 3.x 开保留时必填,漏了会启动失败 |
(2)Alloy 采集配置 alloy-config.alloy —— 采集本地日志文件并推给 Loki:
`alloy
loki.source.file "local" {
targets = [
{ __path__ = "/var/log/nginx/access.log", job = "nginx-access" },
{ __path__ = "/var/log/nginx/error.log", job = "nginx-error" },
{ __path__ = "/var/log/syslog", job = "syslog" },
]
forward_to = [loki.write.local.receiver]
}
loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}
`
Alloy 的配置是 HCL 风格,注释用 //,与上面 Loki 的 YAML 不同,可以放心加说明:
`alloy
// 从容器内读取宿主机挂载进来的日志文件,标签 job 用于后续查询过滤
loki.source.file "nginx" {
targets = [{ __path__ = "/var/log/nginx/access.log", job = "nginx-access" }]
forward_to = [loki.write.local.receiver]
}
`
(3)编排文件 compose.yaml:
`yaml
services:
loki:
image: grafana/loki:latest
container_name: loki
restart: unless-stopped
command: -config.file=/etc/loki/local-config.yaml
volumes:
- ./loki-config.yaml:/etc/loki/local-config.yaml:ro
- ./loki-data:/loki
ports:
- "127.0.0.1:3100:3100"
grafana: image: grafana/grafana:latest container_name: grafana restart: unless-stopped environment: GF_SECURITY_ADMIN_USER: admin GF_SECURITY_ADMIN_PASSWORD: change-me-please GF_USERS_ALLOW_SIGN_UP: "false" volumes: - ./grafana-data:/var/lib/grafana ports: - "127.0.0.1:3000:3000"
alloy:
image: grafana/alloy:latest
container_name: alloy
restart: unless-stopped
command:
- run
- --server.http.listen-addr=0.0.0.0:12345
- --storage.path=/var/lib/alloy/data
- /etc/alloy/config.alloy
volumes:
- ./alloy-config.alloy:/etc/alloy/config.alloy:ro
- /var/log:/var/log:ro
- ./alloy-data:/var/lib/alloy/data
ports:
- "127.0.0.1:12345:12345"
`
> 📌 上面用 :latest 是为了让你拿到官方最新稳定版、快速起步。但生产环境建议把三个镜像固定成主版本标签(例如 grafana/loki:3、grafana/grafana:13、grafana/alloy:1)或具体版本号,这样升级可控、不会某天被 latest 悄悄换掉一个大版本。
这里有两条必须遵守的红线:
① 所有端口绑定写法必须是 127.0.0.1:PORT:PORT,不能只写 PORT:PORT。 只写 3100:3100 会让服务监听 0.0.0.0,而 Docker 自己写的 iptables 规则会绕过 ufw/firewalld——你以为防火墙挡住了,实际上 Loki 的写入 API 和 Grafana 后台已经裸奔在公网上。这是自建服务最常见的安全事故,Loki 尤其危险,因为它的写入 API 默认不鉴权。
② Grafana 首次启动就要设强密码。 上面 GF_SECURITY_ADMIN_PASSWORD 只是占位,务必改成你自己的强密码(至少 12 位、含大小写与符号),否则默认 admin/admin 会把后台拱手让人。
第 4 步:拉起服务并验证
`bash
docker compose up -d
docker compose ps
`
三个容器都应是 running。逐个验证:
`bash
docker compose logs --tail=30 loki
docker compose logs --tail=30 alloy
curl -s http://127.0.0.1:3100/ready
curl -s http://127.0.0.1:3100/metrics | grep loki_ingester_streams_created_total | head -1
`
/ready 返回 ready 说明 Loki 就绪;最后一条能看到 loki_ingester_streams_created_total 指标,说明 Loki 已开始接收日志流。Grafana 用浏览器访问 http://<服务器IP>:3000 需要先放行 3000(仅用于首次配置),配好 Nginx 反代后立即回收:
`bash
ufw allow 3000/tcp
`
五、采集配置进阶:把 Nginx / Docker / 系统日志都收进来
Alloy 的采集能力都体现在 loki.source.* 系列组件上。下面给出三种最常见的采集场景,配置可叠加使用。
5.1 系统日志(journald)
Ubuntu 的 /var/log/syslog 可能不存在(现代发行版用 systemd journal)。改用 loki.source.journal 直接读 journald:
`alloy
loki.source.journal "syslog" {
max_age = "12h"
relabel_rules = loki.relabel.journal.rules
forward_to = [loki.write.local.receiver]
}
loki.relabel "journal" { forward_to = []
rule { source_labels = ["__journal__systemd_unit"] target_label = "unit" } rule { source_labels = ["__journal__hostname"] target_label = "host" } }
loki.write "local" {
endpoint {
url = "http://loki:3100/loki/api/v1/push"
}
}
`
5.2 Docker 容器日志
采集本机所有容器的 stdout/stderr,最简单的做法是挂载 Docker socket:
`alloy
discovery.docker "containers" {
host = "unix:///var/run/docker.sock"
}
loki.source.docker "containers" {
host = "unix:///var/run/docker.sock"
targets = discovery.docker.containers.targets
forward_to = [loki.write.local.receiver]
}
`
对应地,compose 里给 alloy 容器加一行挂载(只读):
`yaml
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
`
> ⚠️ 安全提示:挂载 docker.sock 等于给该容器接近宿主机的权限。仅在你信任该镜像的前提下使用;如果只关心少数几个服务,更安全的做法是把它们的日志目录直接挂载成文件路径,用 loki.source.file 采集。
5.3 用 loki.process 提纯日志(结构化解析)
原始 Nginx 访问日志是一整行字符串。用 loki.process 把它解析成结构化字段,查询时就能按状态码、方法过滤:
`alloy
loki.process "nginx" {
stage.regex {
expression = "^(?P<remote_addr>\\S+) - - \\[(?P<time>[^\\]]+)\\] \"(?P<method>\\S+) (?P<path>\\S+) [^\"]*\" (?P<status>\\d{3}) (?P<bytes>\\d+)"
}
stage.labels {
values = {
status = "",
method = "",
}
}
forward_to = [loki.write.local.receiver]
}
`
关键纪律:stage.labels 里只放低基数维度——status、method 可以;path、remote_addr 不行,它们取值太多,会把标签基数撑爆。高基数字段应该用 stage.structured_metadata 放进结构化元数据,而不是标签。
六、Grafana 接入 Loki:从数据源到第一条 LogQL
6.1 添加 Loki 数据源
登录 Grafana(http://<服务器IP>:3000),依次进入 Connections → Data sources → Add data source → Loki,在 Connection URL 里填:
`
http://loki:3100
`
因为 Grafana 和 Loki 在同一个 Docker 网络里,直接用容器名 loki 即可。不要填 127.0.0.1:3100——那是 Grafana 容器自己的 localhost,连不到 Loki 容器。点 Save & test,看到 "Data source successfully connected" 即成功。
6.2 LogQL 入门:三分钟学会最常用的查询
LogQL 是 Loki 的查询语言,语法和 PromQL 很像,分两类:
① 日志查询(Log queries) —— 返回日志行,结构是「标签选择器 + 过滤器」:
`logql
{job="nginx-access"}
{job="nginx-access"} |= "500"
{job="nginx-access", status="500"} |= "timeout"
{job=~"nginx-.*"} != "health"
{unit="sshd"} |~ "(?i)invalid user|failed password"
`
② 指标查询(Metric queries) —— 把日志当数据源算指标,在日志查询后接一个范围聚合函数:
`logql
sum by (status) (count_over_time({job="nginx-access"}[5m]))
rate({job="nginx-error"}[1m])
topk(5, sum by (path) (count_over_time({job="nginx-access"} | json [1h])))
`
两条最实用的经验:
- 过滤器从快到慢排列:|=(包含,最快)> !=(不含)> |~(正则,最慢)。先用标签选择器把范围切到最小,再用 |= 粗筛,最后才用正则。把 |~ ".*" 这种写法当默认,是查询慢的头号原因。
- | json / | logfmt 解析后,字段可直接当过滤条件:如果日志是 JSON 格式,{job="app"} | json | level="error" 比 |= "\"level\":\"error\"" 干净得多。
6.3 做一个"错误日志看板"
在 Grafana 新建 Dashboard:加一个 Logs 面板(数据源选 Loki,查询 {job="nginx-error"}),再加一个 Time series 面板查 sum(count_over_time({job="nginx-error"}[5m])),就能一屏看到"错误量趋势 + 错误原文"。这正是 Loki + Grafana 的组合价值:指标告诉你"错误在涨",面板下方直接能看到"涨的是哪些错误"。
如果你已经按本站 Prometheus + Grafana 教程 建过 Grafana,那么 Loki 只是同一个 Grafana 里的第二个数据源——不需要重装,一个 Grafana 可以同时连 Prometheus 和 Loki,把指标与日志放在同一块看板上联动排查。
七、开启日志保留(Retention):不改这一步,磁盘迟早被写满
这是本文最关键、也最容易被跳过的一节。官方文档白纸黑字写着:compactor.retention-enabled 默认不设置,写进去的日志会永久保留。也就是说,Loki 默认不删日志。第四节的 loki-config.yaml 里我们已经写好了 retention 配置,这里解释它为什么必须这么写,以及怎么验证。
7.1 保留为什么必须靠 Compactor
Loki 的保留删除不是由某个后台定时器随手清理,而是由 Compactor(压缩器) 承担的。它同时干两件事:压缩索引文件,以及按保留期删除过期日志。这带来一个硬性要求:
> Compactor 必须单实例运行(run as a singleton)。
单机(monolithic)部署天然只有一个进程,符合要求;但如果你以后上微服务模式,务必只让一个实例跑 compactor,多个 compactor 同时删同一份数据会出问题。
7.2 三个必须同时满足的条件
结合官方文档,retention 真正生效需要三件事同时成立:
| 条件 | 配置项 | 漏掉的后果 |
|---|---|---|
| 开启删除 | compactor.retention_enabled: true | 日志永久保留,磁盘写满 |
| 声明删除请求后端 | compactor.delete_request_store: filesystem | Loki 3.x 启动报错,服务起不来 |
| 设定保留时长 | limits_config.retention_period: 168h | 开了删除但没有期限,行为不符合预期 |
改完配置后重启 Loki(这是唯一需要重启的一步,因为配置段没有热加载):
`bash
cd /opt/loki-stack
docker compose restart loki
docker compose logs --tail=20 loki | grep -i compactor
`
7.3 验证保留是否真的在跑
不用真等 7 天,更实用的验证方式是看 compactor 的运行日志与指标:
`bash
docker compose logs loki | grep -iE "retention|compactor|deleted"
curl -s http://127.0.0.1:3100/metrics | grep -E "loki_compactor_(apply_retention|delete)" | head -10
`
只要能在日志里看到 compactor 周期性地输出"apply retention",说明删除链路是活的。另外两个和保留配套的细节:
- 磁盘告警要提前做。 即使开了保留,日志量突增(比如被刷接口、日志级别误开到 debug)也会在几小时里吃满磁盘。建议用本站 Uptime Kuma 篇 给这台机器加一个磁盘用量监控,或用 Prometheus 篇 的 node_exporter 磁盘指标做 80% 阈值告警。 - 对象存储的 lifecycle 要长于 retention。 如果你以后把存储切到 S3/MinIO,官方明确提醒:对象存储上配置的生命周期策略必须长于 Loki 的保留期,否则两边都删会导致查询报错。
八、Nginx 反向代理 + Let's Encrypt:给 Grafana 一个域名和 HTTPS
Loki 的 3100 端口不要暴露到公网——它的写入 API 默认不鉴权,任何能访问的人都可以往你的 Loki 里灌垃圾日志(轻则磁盘爆,重则查询被拖死)。只把 Grafana 的 3000 通过 Nginx 反代出去,并且走 HTTPS。 申请证书与 Nginx 基础配置直接复用本站 Nginx 反向代理 + Let's Encrypt SSL 教程,这里只给 Grafana 专属的 server 段:
`nginx
server {
listen 80;
server_name grafana.yourdomain.com;
return 301 https://$host$request_uri;
}
server { listen 443 ssl http2; server_name grafana.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/grafana.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/grafana.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
`
Grafana 反代的三个必写头:
| 头 | 作用 | 漏掉的后果 |
|---|---|---|
| Host $host | 告诉 Grafana 真实域名 | 登录后跳回 127.0.0.1:3000,页面打不开 |
| X-Forwarded-For / X-Real-IP | 传真实客户端 IP | 登录日志里所有来源都显示 127.0.0.1 |
| X-Forwarded-Proto $scheme | 声明是 HTTPS | Grafana 生成 http 链接、出现混合内容告警 |
> 📌 Loki 数据源也要走 HTTPS 吗? 不需要,也不能。 Grafana 通过 Docker 内网 http://loki:3100 访问 Loki,这条链路不出容器网络,无需加密。对外只暴露 Grafana 一个入口,Loki 和 Alloy 的管理端口永远留在 127.0.0.1 上。
九、备份、升级与安全加固
9.1 备份:两个目录,别漏 Grafana
| 要备份的东西 | 路径 | 说明 |
|---|---|---|
| Loki 日志数据 | /opt/loki-stack/loki-data | chunk 与索引。若已切对象存储,备份对象存储即可 |
| Grafana 配置与看板 | /opt/loki-stack/grafana-data | 最容易漏——你画的所有看板、设的数据源、建的账号都在这里 |
| 三个配置文件 | loki-config.yaml / alloy-config.alloy / compose.yaml | 纯文本,纳入 Git 管理最省事 |
备份命令(建议先停容器再打包,避免拷到不一致的数据库文件):
`bash
cd /opt/loki-stack
docker compose stop
tar czf /root/loki-stack-backup-$(date +%F).tar.gz loki-data grafana-data loki-config.yaml alloy-config.alloy compose.yaml
docker compose start
`
更稳妥的做法是配合文件系统快照,或按本站 数据备份与容灾恢复教程 做异地同步。并且——备份这件事本身也要被监控,否则"备份默默失败"是自托管里最贵的一种故障:可以用 Uptime Kuma 篇 的 Push 心跳监控,让备份脚本每次成功就上报一次。
9.2 升级:三个组件分开升
Loki、Grafana、Alloy 是三个独立镜像,升级时分开做、逐次验证,不要一次性全升:
`bash
cd /opt/loki-stack
docker compose pull grafana
docker compose up -d grafana
docker compose logs --tail=20 grafana
`
确认 Grafana 正常后,再升 Loki(大版本升级前务必先备份 loki-data,Loki 的 schema 变更不可逆)。Alloy 是采集端,升级影响最小,可以最后升。
9.3 安全加固清单
- Grafana:设强密码(GF_SECURITY_ADMIN_PASSWORD)、关闭注册(GF_USERS_ALLOW_SIGN_UP: "false")、开启登录失败限速,必要时在 Nginx 层加 basic auth 或 Cloudflare Access。
- Loki 3100 端口:绝不对外;若确有跨机写入需求,在 Nginx 层加 basic auth,或把 Loki 的 auth_enabled 打开并用 X-Scope-OrgID 头区分租户(微服务/多租户场景)。
- Alloy 12345 端口:只在本机监听,它是 Alloy 的管理/调试界面,暴露出去等于泄露采集配置。
- docker.sock:只在需要采集容器日志时挂载,且优先用只读挂载;能不用就不用。
- 别把敏感信息当标签:标签会进索引并出现在 UI 上,token、密码、手机号、身份证永远不要做成标签,也不要写进日志正文。
十、常见问题 FAQ
Q1:Loki 和 ELK / OpenSearch 到底选哪个?
核心区别是索引方式:Loki 只索引标签(低基数、按标签快速缩小范围,正文压缩存储),ELK 对日志正文做全文倒排索引(能模糊搜索、能复杂聚合,但每行都要建索引)。本站尚未有 ELK 专文,这里给出判断口径:日志量不大(几十 GB/月)、以"按服务/主机/级别定位错误"为主 → Loki 性价比明显更高;需要全文模糊搜索、高亮、TB 级复杂聚合 → ELK/OpenSearch 或云日志服务。最简单的记法:把 Loki 理解成"日志版的 Prometheus"。
Q2:教程里的 Promtail 还能用吗?为什么你用的是 Alloy?
不能继续指望它了。 官方文档明确写着 Promtail 已于 2026 年 3 月 2 日 EOL(end of life),商业支持结束、不再有功能更新,后续开发全部转往 Grafana Alloy。所以网上那些装 Promtail 的老教程,装的是已停维护的组件。官方提供了从 Promtail 迁到 Alloy 的配置转换工具,但新部署的人直接用 Alloy 就行,不必先学 Promtail 语法。本文采集端全部使用 Alloy 的 loki.source.* + loki.write。
Q3:日志把磁盘写满了,怎么办?
按可能性从高到低排查:① 没开 retention(compactor.retention_enabled 默认关闭,日志默认永久保留)——先按第七节开保留;② 日志量本身突增(被刷接口、误开 debug 级别、某服务疯狂打错误日志)——去 Grafana 里按 count_over_time 看哪个 job 在暴涨;③ 标签基数失控——把 user_id/trace_id/URL 当标签会产生海量 stream,既吃内存又吃磁盘索引。应急时可以先手工清理 loki-data/chunks 里最老的目录,但正确做法是修配置,而不是反复手工删。
Q4:能不能给日志打上 request_id / user_id 标签,方便按用户查日志?
绝对不能。 这是 Loki 的头号杀手——这类字段取值数量几乎无限,每个新取值都可能产生一个新的 log stream,Loki 必须在内存里为每个 stream 维护索引,结果是内存瞬间被撑爆(ingester OOM)。正确姿势:这些字段放进日志正文,或用 stage.structured_metadata 存成结构化元数据(不进标签索引),查询时用 | json 解析后再过滤,例如 {job="app"} | json | user_id="12345"。记住一句话:标签是"维度",不是"数据"。
Q5:1核1G 的机器能跑吗?配置怎么定?
个人小站、单机日志、保留 7 天的场景,1核2G 就能跑得很稳(注意是 2G 内存不是 1G——内存是 ingester 的硬约束)。不建议 1核1G:一旦标签基数或日志量上来,ingester 会被 OOM Killer 杀掉,表现为"日志莫名停止写入"。正式使用建议 2核4G + 100GB SSD;多台机器统一收(5~10 台)上 2核8G。瓶颈排序是:内存(标签基数)> 磁盘 IO > CPU。
Q6:数据会丢吗?怎么备份?
Loki 的日志数据在 loki-data,Grafana 的看板与账号在 grafana-data——两个都要备(grafana-data 最容易被漏掉)。规范做法是停容器后打包,或配合文件系统快照;若已用对象存储后端,则备份对象存储即可。另外强烈建议用 Uptime Kuma 的 Push 监控 给备份脚本加个"心跳"——备份失败这件事必须有人告诉你,否则你会在真正需要恢复的那天才发现从来没成功过。
Q7:采集到的是中文日志,会不会乱码?时区不对怎么办?
Loki 内部按 UTC 存储时间戳,查询时 time 字段也会以 UTC 呈现,看起来会"差 8 小时"——这是正常现象,在 Grafana 里把右上角时区切成 Asia/Shanghai(或"浏览器本地时区")即可。中文日志乱码通常出在采集端:确认 Alloy 读取的文件本身是 UTF-8(用 file /var/log/xxx.log 检查),以及 loki.source.file 没有按错误的编码读取。Loki 存储的是字节流,只要写入时是 UTF-8,Grafana 就能正常显示。
Q8:想同时收 Nginx + Docker + 系统日志,配置会互相冲突吗?
不会。loki.source.* 各组件相互独立,可以同时定义多个,各自 forward_to 到同一个 loki.write。但要注意两点:① 标签要区分开(job="nginx-access" / job="docker" / job="syslog"),否则查询时全糊在一起;② 别重复采集(比如同时用 loki.source.docker 和 loki.source.file 收同一个容器的日志,会出现重复行)。建议先只收一类,验证通了再叠加。
Q9:能采集国内服务器的日志,统一推到海外的 Loki 吗?
可以,但要注意带宽:Alloy 是持续推流,跨地域(尤其跨境)推日志会消耗可观流量,还可能因链路抖动丢日志。推荐做法有两种:① 在每个地域各放一个 Alloy,就近推给同一个 Loki(Alloy 自带重试,链路短更稳);② 国内机器先在本地按标签粗筛,只把 error 级别日志推给海外 Loki,降低跨境流量。如果日志合规上不允许出境,那就只能在国内单独部署一套 Loki——数据能不能出境,是架构问题,不是配置问题。
十一、总结
用海外云服务器搭建日志聚合系统,整条链路其实就是九步:买一台 SSD 机器 → 系统初始化并收敛端口 → 装 Docker → 写 loki-config.yaml(记得开 retention)→ 写 Alloy 采集配置 → 一套 Compose 拉起 Loki + Grafana + Alloy → Grafana 添加 Loki 数据源 → 用 LogQL 查日志并做错误看板 → Nginx 反代 Grafana + HTTPS → 定期备份 loki-data 与 grafana-data。一台 2核4G 的机器,换来的是"出事时能查到那一行错误"的能力,这正是指标和可用性监控都给不了的东西。
真正决定这套日志系统好不好用的,不是命令,而是三个判断:第一,标签要"低基数"——把 user_id/trace_id 当标签是 Loki 的头号杀手,标签是维度不是数据;第二,retention 必须显式打开——官方默认不删日志,不改这一步磁盘迟早写满;第三,采集端别再用 Promtail——它已于 2026 年 3 月 EOL,新部署直接用 Grafana Alloy。
最后回到可观测性的全局:指标(本站 Prometheus 篇)告诉你"哪里不对",可用性(本站 Uptime Kuma 篇)告诉你"是不是挂了",日志(本文 Loki)告诉你"为什么"。 三者拼在一起,才是一套完整、且成本仍然可控的自托管可观测体系。把每一层的边界说清楚,比把某一个工具吹成万能更有价值。
延伸阅读: - Prometheus + Grafana 监控告警系统完整教程:从指标采集到可视化告警 - Uptime Kuma 轻量监控告警 + 公网状态页完整教程 - MinIO 自建对象存储完整教程(S3 兼容) - Docker + Portainer 容器管理平台搭建完整教程 - Nginx 反向代理 + Let's Encrypt 免费 SSL 证书配置完整教程 - 数据备份与容灾恢复完整教程:快照 + 异地备份 + 恢复演练
> 本文由 5.chengzicloud.cloud 提供,点击访问首页了解更多海外云服务器部署方案和专属优惠。