Loki 单机 Linux Docker 搭建指南
1. 适用场景
本文档适用于以下部署条件:
- Spring Boot 2.x 项目
- Java 后端项目
- 单机 Linux 服务器
- 使用 Docker / Docker Compose 部署
- 业务日志目录为
/home/admin/app/logs/visa - 需要快速检索日志、查看异常堆栈、按时间范围筛选日志
当前项目的日志格式参考 src/main/resources/logback.xml,核心格式如下:
xml
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %L %msg%n</pattern>
当前日志滚动策略为:
- 按天切分
- 单文件超过
20MB自动继续拆分 - 错误日志单独输出到
error子目录
以上策略可以正常接入 Loki + Promtail + Grafana。
2. 方案说明
本方案采用以下组件:
Loki:日志存储与查询服务Promtail:读取日志文件并推送到 LokiGrafana:图形化查看日志
整体流程如下:
- Spring Boot 按现有
logback配置继续写本地日志文件 - Promtail 持续采集
/home/admin/app/logs/visa目录下的日志 - Promtail 将日志推送到 Loki
- Grafana 连接 Loki 后提供检索界面
3. 目录规划
建议在 Linux 服务器创建统一的观测目录:
bash
mkdir -p /opt/visa-observe/loki/data
mkdir -p /opt/visa-observe/promtail/positions
mkdir -p /opt/visa-observe/grafana-data
建议最终目录结构如下:
text
/opt/visa-observe/
├── docker-compose.yml
├── grafana-data/
├── loki/
│ ├── data/
│ └── loki-config.yaml
└── promtail/
├── positions/
└── promtail-config.yaml
4. 前置准备
4.1 检查 Docker
执行以下命令确认 Docker 已安装:
bash
docker -v
docker compose version
如果服务器没有安装 Docker 和 Docker Compose,需要先安装。
4.2 确认日志目录
确认业务日志目录已经存在,并且日志文件会持续写入:
bash
ls -lh /home/admin/app/logs/visa
如果存在 error 子目录,也建议一并确认:
bash
ls -lh /home/admin/app/logs/visa/error
5. 创建 docker-compose.yml
在 /opt/visa-observe 目录下创建 docker-compose.yml:
yaml
version: "3.8"
services:
loki:
image: grafana/loki:2.9.8
container_name: loki
restart: unless-stopped
ports:
- "3100:3100"
volumes:
- /opt/visa-observe/loki/loki-config.yaml:/etc/loki/local-config.yaml
- /opt/visa-observe/loki/data:/loki
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9.8
container_name: promtail
restart: unless-stopped
volumes:
- /opt/visa-observe/promtail/promtail-config.yaml:/etc/promtail/config.yml
- /home/admin/app/logs/visa:/home/admin/app/logs/visa:ro
- /opt/visa-observe/promtail/positions:/tmp
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki
grafana:
image: grafana/grafana:10.4.3
container_name: grafana
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- /opt/visa-observe/grafana-data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin123
- GF_SERVER_ROOT_URL=http://localhost:3000
depends_on:
- loki
6. 创建 Loki 配置
在 /opt/visa-observe/loki 目录下创建 loki-config.yaml:
yaml
auth_enabled: false
server:
http_listen_port: 3100
common:
path_prefix: /loki
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2024-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: loki_index_
period: 24h
storage_config:
filesystem:
directory: /loki/chunks
compactor:
working_directory: /loki/compactor
limits_config:
reject_old_samples: true
reject_old_samples_max_age: 168h
allow_structured_metadata: false
7. 创建 Promtail 配置
在 /opt/visa-observe/promtail 目录下创建 promtail-config.yaml:
yaml
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: visa-log
static_configs:
- targets:
- localhost
labels:
job: visa-log
app: visa
__path__: /home/admin/app/logs/visa/**/*.log
pipeline_stages:
- multiline:
firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'
- regex:
expression: '^(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[(?P<thread>[^\]]+)\] (?P<level>[A-Z]+)\s+(?P<logger>\S+) (?P<line>\d+) (?P<message>[\s\S]*)$'
- timestamp:
source: time
format: '2006-01-02 15:04:05.000'
- labels:
level:
- output:
source: message
8. 为什么这个配置适合当前项目
8.1 支持 20MB 自动拆分后的日志采集
你当前日志采用 SizeAndTimeBasedRollingPolicy,超过 20MB 后会继续生成新文件,例如:
text
/home/admin/app/logs/visa/2026-06-26.0.log
/home/admin/app/logs/visa/2026-06-26.1.log
/home/admin/app/logs/visa/2026-06-26.2.log
Promtail 配置中的:
yaml
__path__: /home/admin/app/logs/visa/**/*.log
可以匹配:
- 当前目录下的普通日志
error子目录中的错误日志- 后续因为滚动产生的新日志文件
因此超过 20MB 自动拆分后,仍然可以继续监控。
8.2 支持 Java 异常堆栈
Java 异常通常是多行输出,如果不做合并,Grafana 中会看到很多碎片化日志。
这里使用了 multiline 配置:
yaml
- multiline:
firstline: '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}'
它会把不是以时间开头的后续行,自动合并到上一条日志中,适合 Java 堆栈输出。
8.3 控制标签数量,避免查询变慢
当前配置只把 level 提升为标签,没有把 thread 和 logger 提升为标签。
这样做的原因是:
thread变化太多,容易造成高基数logger也可能很多,标签过多会影响 Loki 查询性能
当前最适合先保留:
jobapplevel
9. 启动服务
进入目录后启动:
bash
cd /opt/visa-observe
docker compose up -d
查看容器状态:
bash
docker ps
查看 Loki 日志:
bash
docker logs -f loki
查看 Promtail 日志:
bash
docker logs -f promtail
查看 Grafana 日志:
bash
docker logs -f grafana
10. 基础验证
10.1 检查 Loki 是否可用
bash
curl http://127.0.0.1:3100/ready
返回 ready 说明正常。
10.2 检查 Promtail 是否成功采集
执行:
bash
docker logs promtail | tail -n 50
重点关注是否存在以下问题:
- 无法读取日志目录
- 配置文件语法错误
- 无法连接
loki:3100
10.3 检查 Grafana 是否可访问
浏览器访问:
text
http://服务器IP:3000
默认账号密码:
- 用户名:
admin - 密码:
admin123
11. 配置 Grafana 数据源
登录 Grafana 后按以下步骤操作:
-
进入
Connections
-
选择
Data sources

-
URL 填写
http://loki:3100
-
点击
Save & test
如果显示成功,说明 Grafana 已经可以访问 Loki。
12. 常用查询语句
进入 Grafana 的 Explore 页面后,可以使用以下查询。
12.1 查询全部业务日志
logql
{job="visa-log"}
12.2 只看错误日志
logql
{job="visa-log", level="ERROR"}
12.3 搜索异常关键字
logql
{job="visa-log"} |= "Exception"
12.4 搜索某个业务关键字
logql
{job="visa-log"} |= "申请单"
12.5 搜索某个类打印的日志
因为 logger 当前没有作为标签,所以建议直接全文搜索,例如:
logql
{job="visa-log"} |= "com.cxwl"

13. 推荐的执行顺序
建议严格按以下顺序执行:
- 安装并确认 Docker 环境
- 创建
/opt/visa-observe目录结构 - 创建
docker-compose.yml - 创建
loki-config.yaml - 创建
promtail-config.yaml - 启动
docker compose up -d - 登录 Grafana 配置 Loki 数据源
- 进入 Explore 页面验证是否已经有日志
14. 与当前 logback 配置相关的建议
14.1 建议把 Linux 日志目录改成绝对路径
你当前 logback.xml 中配置的是:
xml
<property name="LOG_HOME" value="../logs" />
这个写法在 Linux Docker 场景下不够稳定,建议改成绝对路径,例如:
xml
<property name="LOG_HOME" value="/home/admin/app/logs" />
这样最终输出目录会更明确:
text
/home/admin/app/logs/visa
14.2 生产环境建议关闭 Logback 调试
你当前配置中:
xml
<configuration debug="true" scan="true">
建议生产环境改为:
xml
<configuration debug="false" scan="true">
原因:
debug="true"会输出 Logback 自身内部调试信息- 生产环境通常没有必要开启
14.3 不建议立即改成应用直推 Loki
当前场景下,更推荐保持以下模式:
- Java 应用继续按本地文件方式写日志
- Promtail 负责统一采集
这样做的优点是:
- 改动小
- 风险低
- 出问题时仍可直接登录 Linux 服务器查看本地日志
15. 常见问题
15.1 为什么 Grafana 查不到日志
优先排查以下问题:
/home/admin/app/logs/visa是否真的有日志文件- Promtail 是否挂载到了正确目录
- Promtail 配置文件是否写错
- Promtail 是否成功连接到 Loki
- Grafana 数据源 URL 是否写成了
http://loki:3100
15.2 日志滚动后是否会丢失
正常不会。
原因:
- Promtail 会记录读取位置
- 新生成的
.log文件仍会被__path__匹配
但仍建议不要频繁删除正在写入的日志文件。
15.3 历史压缩日志会不会实时采集
不建议依赖 .gz 压缩文件做实时采集。
建议原则如下:
- 正在写入或刚滚动出的日志,保持
.log - 需要长期归档时再压缩
15.4 如果 Java 项目也运行在 Docker 中怎么办
需要确保应用容器已经把日志目录挂载到宿主机,例如:
yaml
volumes:
- /home/admin/app/logs:/home/admin/app/logs
Promtail 采集的是宿主机目录,不是应用容器内部的临时文件系统。
16. 实际调试问题与解决方案
以下内容基于本次实际部署过程中遇到的问题整理,建议在首次搭建时重点关注。
16.1 Loki 启动时报配置文件无权限
问题现象:
text
failed parsing config: open /etc/loki/local-config.yaml: permission denied
原因分析:
- 宿主机挂载到容器内的
loki-config.yaml权限不足 - 宿主机上级目录权限不足,导致容器用户无法读取配置文件
- 某些 Linux 发行版开启了
SELinux,卷挂载可能还需要额外处理
解决方案:
先修正目录和文件权限:
bash
chmod 755 /opt
chmod 755 /opt/visa-observe
chmod 755 /opt/visa-observe/loki
chmod 644 /opt/visa-observe/loki/loki-config.yaml
chmod 755 /opt/visa-observe/loki/data
chown -R 10001:10001 /opt/visa-observe/loki/data
如果系统启用了 SELinux,先检查:
bash
getenforce
如果返回 Enforcing,建议在卷挂载时增加 :Z,例如:
yaml
volumes:
- /opt/visa-observe/loki/loki-config.yaml:/etc/loki/local-config.yaml:ro,Z
- /opt/visa-observe/loki/data:/loki:Z
验证方式:
bash
docker compose down
docker compose up -d
docker logs --tail=100 loki
curl http://127.0.0.1:3100/ready
16.2 Loki 返回 Ingester not ready
问题现象:
text
Ingester not ready: waiting for 15s after being ready
原因分析:
- 这是
Loki启动后的短暂预热状态 - 并不代表配置错误或服务异常
解决方案:
- 等待
15~30秒后重新检查
验证方式:
bash
curl http://127.0.0.1:3100/ready
当返回以下内容时说明服务正常:
text
ready
16.3 Grafana 启动失败并反复重启
问题现象:
text
GF_PATHS_DATA='/var/lib/grafana' is not writable.
mkdir: can't create directory '/var/lib/grafana/plugins': Permission denied
同时 docker ps 中可能看到:
text
grafana Restarting (1)
原因分析:
- 宿主机挂载目录
/opt/visa-observe/grafana-data对容器内 Grafana 用户不可写 - Grafana 无法创建插件目录和内部数据文件,因此启动失败
解决方案:
先创建并修正目录权限:
bash
mkdir -p /opt/visa-observe/grafana-data
chown -R 472:472 /opt/visa-observe/grafana-data
chmod -R 755 /opt/visa-observe/grafana-data
然后重启容器:
bash
cd /opt/visa-observe
docker compose down
docker compose up -d
如果系统启用了 SELinux,建议把卷挂载改为:
yaml
volumes:
- /opt/visa-observe/grafana-data:/var/lib/grafana:Z
验证方式:
bash
docker ps
docker logs --tail=100 grafana
curl -I http://127.0.0.1:3000
正常状态下应满足:
docker ps中grafana为Up- 可以看到
0.0.0.0:3000->3000/tcp curl返回302 Found并跳转到/login
16.4 UFW 已放行但浏览器仍无法访问 Grafana
问题现象:
ufw status已显示3000/tcp ALLOW- 服务器本机访问
http://127.0.0.1:3000正常 - 外部浏览器访问
http://公网IP:3000仍然失败
原因分析:
- 这通常不是 Linux 本机防火墙问题
- 更常见原因是云服务器安全组未放行
3000/tcp
解决方案:
先确认本机防火墙已放行:
bash
ufw allow 3000/tcp
ufw reload
ufw status
再确认服务本机访问正常:
bash
curl -I http://127.0.0.1:3000
如果本机正常但外部仍无法访问,需要到云平台控制台放行安全组:
- 协议:
TCP - 端口:
3000 - 来源:
0.0.0.0/0或指定办公公网 IP
验证方式:
- 服务器本机执行:
bash
curl -I http://127.0.0.1:3000
- 浏览器访问:
text
http://公网IP:3000
16.5 Promtail 首次启动 CPU 偏高
问题现象:
promtail初次启动后 CPU 使用率明显升高- 宿主机
top中看到promtail持续占用较高 CPU
原因分析:
- 首次启动时,Promtail 会扫描现有日志目录并补采历史日志
- 当前配置使用了递归路径
/home/admin/app/logs/visa/**/*.log - 同时还启用了
multiline和regex解析,首次扫描成本会更高
解决方案:
- 如果只是启动初期短时间 CPU 升高,可以先观察几分钟
- 如果历史日志很多,建议收窄采集范围
- 可以将普通日志和错误日志分开配置,避免全量递归扫描
示例:
yaml
scrape_configs:
- job_name: visa-log
static_configs:
- targets:
- localhost
labels:
job: visa-log
app: visa
__path__: /home/admin/app/logs/visa/*.log
- job_name: visa-error-log
static_configs:
- targets:
- localhost
labels:
job: visa-error-log
app: visa
__path__: /home/admin/app/logs/visa/error/*.log
验证方式:
bash
docker logs --tail=100 promtail
top
观察一段时间后,如果 CPU 明显下降,通常说明补采已完成。
17. 建议的上线后检查项
上线完成后,建议至少检查以下内容:
- Grafana 可以打开
- Loki 数据源测试通过
- Explore 页面能查到最近 15 分钟日志
ERROR级别日志可以独立筛选- 人为制造一条异常日志后能在 Grafana 中看到完整堆栈
- 日志超过
20MB滚动后,新文件仍能继续被采集
18. 一次性执行命令汇总
如果你想按步骤手工执行,可以参考以下命令顺序。
17.1 创建目录
bash
mkdir -p /opt/visa-observe/loki/data
mkdir -p /opt/visa-observe/promtail/positions
mkdir -p /opt/visa-observe/grafana-data
17.2 进入工作目录
bash
cd /opt/visa-observe
17.3 创建配置文件
手工创建以下文件:
docker-compose.ymlloki/loki-config.yamlpromtail/promtail-config.yaml
17.4 启动
bash
docker compose up -d
17.5 查看状态
bash
docker ps
curl http://127.0.0.1:3100/ready
docker logs promtail | tail -n 50
19. 后续可选优化
当前方案适合快速落地。后续如果日志量继续增长,可以考虑:
- 给 Grafana 配置登录安全策略
- 给 Loki 配置更长的数据保留周期
- 单独给错误日志配置不同的抓取任务
- 增加告警能力,例如发现
ERROR或Exception自动通知
20. 结论
对于当前项目,最稳妥、改动最小的方式是:
- Spring Boot 保持本地文件输出
- 使用 Promtail 采集
/home/admin/app/logs/visa/**/*.log - 使用 Loki 做日志存储
- 使用 Grafana 做查询和检索
该方案已经兼容你当前的:
- 单机 Linux
- Docker 部署
- Spring Boot 2.x
20MB自动拆分日志- Java 异常堆栈查看需求