Halo 博客系统 Docker 部署指南
本文档基于现有的 docker-compose.yaml 文件,详细说明如何使用 Docker 部署 Halo 博客系统及其配套的 PostgreSQL 数据库。
1. 环境准备
-
确保目标服务器已安装 Docker 和 Docker Compose 插件(注意:Docker Compose 有 V1 和 V2 两个版本,命令格式略有不同)
- Docker Compose V2(新版,推荐):使用
docker compose命令 - Docker Compose V1(旧版):使用
docker-compose命令 - 可以通过运行
docker compose version或docker-compose --version检查已安装的版本
- Docker Compose V2(新版,推荐):使用
-
Linux/NAS 服务器:
- 推荐使用 Docker Compose V2,大多数现代发行版已默认安装
- 如果版本较旧,可以通过包管理器升级或使用官方安装脚本
-
Windows 本地测试:
- 需要安装 Docker Desktop(包含 Docker Compose V2)
- 建议配置 WSL2 后端以获得更好的性能
- 需要将 docker-compose.yaml 中的路径修改为 Windows 格式(如
./data/halo2)
-
宿主机资源建议:至少 2 核 CPU,2GB 以上内存。根据配置文件中的资源限制(
mem_limit和cpus),系统最高会使用约 3 个 CPU 核心和约 1.4GB 内存(Halo 1024MB + DB 384MB)。
2. 目录与配置文件准备
在执行部署命令之前,需要准备好映射到容器内的数据卷(Volumes)目录。
docker-compose 文件内容:
services:
haloapp:
image: halohub/halo:2.23.1
restart: unless-stopped
depends_on:
halodb:
condition: service_healthy
volumes:
- /volume1/docker/halo/halo2:/root/.halo2
- /etc/localtime:/etc/localtime:ro
ports:
- "8090:8090"
networks:
- halo-network
healthcheck:
test: ["CMD-SHELL", "wget -q -O - http://localhost:8090/actuator/health/readiness || exit 1"]
interval: 20s
timeout: 3s
retries: 3
start_period: 25s
environment:
# JVM 启动参数配置:设置初始和最大内存、使用 ZGC 垃圾回收器等优化参数,ZGC 可以做到极低的停顿时间
- JVM_OPTS=-Xms256m -Xmx512m -XX:+UseZGC -XX:+AlwaysPreTouch -XX:MaxGCPauseMillis=20 -Dfile.encoding=UTF-8
# 开启 Halo 性能模式
- HALO_PERFORMANCE_MODE=true
# 缓存过期时间设置(3600秒 = 1小时)
- HALO_CACHE_EXPIRE_SECONDS=3600
# Java 工具选项,同样指定使用 ZGC
- JAVA_TOOL_OPTIONS=-XX:+UseZGC
# 启动命令及参数,覆盖镜像的默认启动命令
command:
- --spring.r2dbc.url=r2dbc:pool:postgresql://halodb:5432/halo # 数据库响应式连接地址 (使用 Docker 服务名连接)
- --spring.r2dbc.username=halo # 数据库用户名
- --spring.r2dbc.password=openpostgresql # 数据库密码
- --spring.sql.init.platform=postgresql # 数据库初始化平台类型
- --halo.external-url=https://blog.metarl.cc.cd/ # 博客的外部访问地址
- --spring.codec.max-in-memory-size=10MB # 内存编解码最大限制(用于处理较大的请求负载,如上传文件)
- --server.tomcat.use-nio2=true # Tomcat 启用 NIO2 异步非阻塞 IO 模式,提升并发性能
# 资源限制配置
mem_limit: 1024m # 限制最大内存使用量为 1024MB(提升以应对峰值内存使用)
cpus: 1.5 # 限制最多使用 1.5 个 CPU 核心
init: true # 启用 init 进程,妥善处理僵尸进程和优雅停机的信号传递
# PostgreSQL 数据库服务配置
halodb:
image: postgres:16-bookworm
restart: unless-stopped
volumes:
- /volume1/docker/halo/data:/var/lib/postgresql/data
- /volume1/docker/halo/pg_hba.conf:/etc/postgresql/16/main/pg_hba.conf
- /volume1/docker/halo/postgresql.conf:/etc/postgresql/16/main/postgresql.conf
ports:
- "5555:5432"
networks:
- halo-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U halo || exit 1"]
interval: 5s
timeout: 3s
retries: 3
environment:
- POSTGRES_PASSWORD=openpostgresql # 数据库管理员/默认用户密码
- POSTGRES_USER=halo # 数据库默认用户名
- POSTGRES_DB=halo # 默认创建的数据库名称
# 数据库资源限制配置
mem_limit: 384m # 限制最大内存为 384MB
cpus: 1.5 # 限制最多使用 1.5 个 CPU 核心
# 资源限制 (ulimit)
ulimits:
nofile:
soft: 65536 # 软限制最大打开文件数
hard: 65536 # 硬限制最大打开文件数
shm_size: 512m # 共享内存大小,PostgreSQL 对共享内存依赖较高,主要用于进程间通信和数据缓冲
networks:
halo-network:
driver: bridge
⚠️ 注意(重要):
配置文件中使用了类似群晖 NAS 的绝对路径(如/volume1/docker/halo/...)。
- 如果是部署到 NAS/Linux 服务器:请确保宿主机上存在这些路径。
- 如果是在 Windows 本地测试部署:请将
docker-compose.yaml中的挂载路径修改为相对路径(例如将/volume1/docker/halo/halo2改为./data/halo2)。
2.1 创建数据目录
需要在宿主机创建以下目录结构:
- Halo 主程序数据目录:
/volume1/docker/halo/halo2 - PostgreSQL 数据目录:
/volume1/docker/halo/data
2.2 准备 PostgreSQL 配置文件
docker-compose.yaml 中配置了挂载两个 PostgreSQL 配置文件:
- pg_hba.conf:客户端认证配置文件(
/volume1/docker/halo/pg_hba.conf) - postgresql.conf:PostgreSQL 主配置文件(
/volume1/docker/halo/postgresql.conf)
📝 为什么推荐使用 postgresql.conf?
相比使用 Docker 环境变量,postgresql.conf 配置文件具有以下优势:
- 参数更全面:支持所有 PostgreSQL 参数,包括环境变量无法设置的
- 可维护性更强:便于版本控制、备份和审计
- 支持高级特性:可启用 JIT、分区表优化等
- 性能监控增强:可配置
pg_stat_statements等扩展
推荐配置(postgresql.conf 模板,针对 N100 处理器 4 核 /8GB 环境):
# 连接配置
max_connections = 100
superuser_reserved_connections = 3
# 内存配置
shared_buffers = '2048 MB'
work_mem = '32 MB'
maintenance_work_mem = '320 MB'
huge_pages = off
effective_cache_size = '6 GB'
# I/O 配置
effective_io_concurrency = 1
random_page_cost = 4
# 监控配置
shared_preload_libraries = 'pg_stat_statements'
track_io_timing = on
track_functions = pl
# 复制配置
wal_level = replica
max_wal_senders = 0
synchronous_commit = on
# 检查点配置
checkpoint_timeout = '15 min'
checkpoint_completion_target = 0.9
max_wal_size = '1024 MB'
min_wal_size = '512 MB'
# WAL 写入配置
wal_compression = on
wal_buffers = -1
wal_writer_delay = 200ms
wal_writer_flush_after = 1MB
# 后台写入器配置
bgwriter_delay = 200ms
bgwriter_lru_maxpages = 100
bgwriter_lru_multiplier = 2.0
bgwriter_flush_after = 0
# 并行查询配置
max_worker_processes = 4
max_parallel_workers_per_gather = 2
max_parallel_maintenance_workers = 2
max_parallel_workers = 4
parallel_leader_participation = on
# 高级特性
enable_partitionwise_join = on
enable_partitionwise_aggregate = on
jit = on
max_slot_wal_keep_size = '1000 MB'
track_wal_io_timing = on
maintenance_io_concurrency = 1
wal_recycle = on
pg_hba.conf 配置文件模板:
# PostgreSQL Client Authentication Configuration File
# TYPE DATABASE USER ADDRESS METHOD
# 允许本地 Unix 套接字连接(本地安全连接,无需密码)
local all all trust
# 允许本地 TCP 连接(仅本机 localhost)
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
# 允许 Docker 网络连接 (Halo 应用连接) - 使用 scram-sha-256 密码认证
host all halo 172.0.0.0/8 scram-sha-256
# 拒绝所有其他 IPv4 连接
host all all 0.0.0.0/0 reject
# 拒绝所有其他 IPv6 连接
host all all ::/0 reject
配置说明:
- pg_hba.conf 中的
172.0.0.0/8覆盖了 Docker 默认分配的 172.17.0.0/16 等网段 - 如果需要从外部(如宿主机上的数据库管理工具)访问 5555 端口,需要额外添加一行:
host all all 你的IP/32 scram-sha-256
📝 操作步骤:
如果这是你第一次运行数据库(数据目录为空),建议按以下步骤操作:
先启动一次数据库:不挂载
pg_hba.conf和postgresql.conf,让 PostgreSQL 自动初始化并生成默认配置文件复制默认配置:进入容器将默认的配置文件复制出来
docker compose exec halodb cat /etc/postgresql/16/main/pg_hba.conf > /volume1/docker/halo/pg_hba.conf docker compose exec halodb cat /etc/postgresql/16/main/postgresql.conf > /volume1/docker/halo/postgresql.conf修改配置:根据上述模板修改这两个配置文件
重新启动:修改
docker-compose.yaml取消注释挂载行,然后重启服务docker compose down docker compose up -d
3. 核心配置解析 (供学习与参考)
为了更好地理解该系统的运作方式,以下是核心配置的详细解析:
3.1 Halo 主程序 (haloapp)
- 基础镜像:
halohub/halo:2.23.1 - 网络与端口: 连接到自定义的桥接网络
halo-network,将宿主机的8090端口映射到容器内部的8090端口。 - JVM 性能优化:
- 启用了 ZGC 垃圾回收器 (
-XX:+UseZGC),这能极大地降低 Java 程序的 GC 停顿时间。 - 开启了 Halo 性能模式 (
HALO_PERFORMANCE_MODE=true) 并将 Tomcat 设为 NIO2 异步非阻塞 IO 模式,提升了并发性能。
- 启用了 ZGC 垃圾回收器 (
- 数据库连接: 采用 R2DBC 响应式连接 (
r2dbc:pool:postgresql://halodb:5432/halo),通过 Docker 服务名halodb直接进行内网通信。 - 外部访问地址: 绑定了域名
--halo.external-url=https://blog.metarl.cc.cd/。 - 安全配置: 仅开启了
init: true,用于妥善处理进程信号及优雅停机,未开启特权模式(privileged: true)以确保容器安全。
3.2 PostgreSQL 数据库 (halodb)
- 基础镜像:
postgres:16-bookworm(PostgreSQL 16 版本) - 外部暴露: 宿主机的
5555端口映射到容器内的5432端口(方便外部数据库管理工具连接)。 - N100 处理器专属调优: 通过 postgresql.conf 文件配置了大量针对 N100 处理器(4核/8GB)和机械硬盘(WD Red Pro)的深度调优参数:
shared_buffers = '2048 MB': 共享缓冲区大小,设为 2GB 以充分利用可用内存random_page_cost = 4: 针对机械硬盘调整随机访问代价为 4.0(SSD 通常为 1.1)jit = on: 启用即时编译以优化查询性能max_connections = 100: 最大并发连接数effective_cache_size = '6 GB': 优化器假设的可用磁盘缓存大小work_mem = '32 MB': 工作内存,控制排序和哈希操作的内存使用maintenance_work_mem = '320 MB': 维护操作内存,用于 VACUUM、CREATE INDEX 等
- 性能监控: 启用了
pg_stat_statements扩展和 I/O 时间跟踪,便于性能分析和调优
4. 部署步骤
4.1 启动服务
将 docker-compose.yaml 文件放置在目标服务器的目录中。打开终端,切换到该目录,执行以下命令在后台启动服务:
# 启动所有服务并在后台运行(Docker Compose V2)
docker compose up -d
# 如果使用的是旧版 Docker Compose V1,使用以下命令
# docker-compose up -d
4.2 查看运行状态与日志
启动后,可以检查容器的运行状态和健康检查结果:
# 1. 查看容器状态(关注 STATUS 栏,等待显示为 healthy)
docker compose ps
# 2. 查看 Halo 运行日志(按 Ctrl+C 退出日志查看)
docker compose logs -f haloapp
# 3. 查看数据库运行日志
docker compose logs -f halodb
5. 访问与初始化
- 等待两个容器的状态均变为
healthy。 - 在浏览器中访问:
http://<宿主机IP>:8090。 - 如果你已经配置好了反向代理(如 Nginx 或 Traefik)和域名解析,可以直接访问:
https://blog.metarl.cc.cd/。 - 首次访问会进入 Halo 的初始化引导界面,按照提示设置管理员账号、密码以及网站基础信息即可完成搭建。
6. 后续维护建议
- 数据备份:日常维护中,重点备份
/volume1/docker/halo/halo2和/volume1/docker/halo/data两个目录即可。 - 密码安全:当前配置中的数据库密码为
openpostgresql,如果在非安全的内网环境中部署,建议修改密码。 - 升级版本:如需升级 Halo,只需将
docker-compose.yaml中的halohub/halo:2.23.1修改为最新版本号,然后重新执行以下命令:
# 拉取最新镜像
docker compose pull
# 重启服务
docker compose up -d
7. 自动化维护脚本
为了简化日常维护工作,可以创建以下自动化脚本。建议将这些脚本放在 /volume1/docker/halo/scripts/ 目录下。
7.1 自动备份脚本
#!/bin/bash
# 文件名:backup.sh
# 用途:备份 Halo 博客系统的所有数据
# 配置
BACKUP_DIR="/volume1/docker/halo/backups" # 备份存储目录
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="halo_backup_$DATE"
KEEP_DAYS=7 # 保留最近 7 天的备份
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 停止容器(可选,确保数据一致性)
cd /path/to/docker-compose-directory # 替换为你的 docker-compose.yaml 所在目录
docker compose stop haloapp
# 备份 Halo 数据目录
tar -czf "$BACKUP_DIR/$BACKUP_NAME_halo2.tar.gz" /volume1/docker/halo/halo2
# 备份 PostgreSQL 数据目录
tar -czf "$BACKUP_DIR/$BACKUP_NAME_postgres.tar.gz" /volume1/docker/halo/data
# 重新启动容器
docker compose start haloapp
# 清理旧备份
find "$BACKUP_DIR" -name "halo_backup_*" -mtime +$KEEP_DAYS -delete
echo "备份完成: $BACKUP_NAME"
📝 使用方法:
将脚本保存为
backup.sh并添加执行权限:chmod +x backup.sh修改脚本中的路径为实际路径
可以使用 crontab 设置定时备份:
# 每天凌晨 2 点执行备份 0 2 * * * /volume1/docker/halo/scripts/backup.sh >> /volume1/docker/halo/logs/backup.log 2>&1
7.2 日志清理脚本
#!/bin/bash
# 文件名:clean_logs.sh
# 用途:清理 Docker 容器日志,防止日志文件占用过多磁盘空间
# 配置
MAX_LOG_SIZE=50M # 单个日志文件最大大小
KEEP_DAYS=7 # 保留最近 7 天的日志
# 创建日志目录
mkdir -p /volume1/docker/halo/logs
# 清理 Halo 容器日志
if [ -f /var/lib/docker/containers/*/haloapp-*.log ]; then
truncate -s $MAX_LOG_SIZE /var/lib/docker/containers/*/haloapp-*.log
fi
# 清理 PostgreSQL 容器日志
if [ -f /var/lib/docker/containers/*/halodb-*.log ]; then
truncate -s $MAX_LOG_SIZE /var/lib/docker/containers/*/halodb-*.log
fi
# 清理旧日志文件
find /volume1/docker/halo/logs -name "*.log" -mtime +$KEEP_DAYS -delete
echo "日志清理完成"
📝 使用方法:
将脚本保存为
clean_logs.sh并添加执行权限:chmod +x clean_logs.sh可以使用 crontab 设置定时清理(建议每周执行一次):
# 每周日凌晨 3 点执行日志清理 0 3 * * 0 /volume1/docker/halo/scripts/clean_logs.sh >> /volume1/docker/halo/logs/clean.log 2>&1
7.3 健康检查脚本
#!/bin/bash
# 文件名:health_check.sh
# 用途:检查 Halo 和 PostgreSQL 容器运行状态
# 检查 Halo 容器
if docker compose ps haloapp | grep -q "Up"; then
echo "✓ Halo 容器运行正常"
else
echo "✗ Halo 容器未运行,尝试重启..."
docker compose restart haloapp
fi
# 检查 PostgreSQL 容器
if docker compose ps halodb | grep -q "Up"; then
echo "✓ PostgreSQL 容器运行正常"
else
echo "✗ PostgreSQL 容器未运行,尝试重启..."
docker compose restart halodb
fi
# 检查 Halo 健康端点
if curl -sf http://localhost:8090/actuator/health/readiness > /dev/null 2>&1; then
echo "✓ Halo 应用健康检查通过"
else
echo "✗ Halo 应用健康检查失败"
fi
# 检查磁盘使用情况
DISK_USAGE=$(df -h /volume1/docker | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 80 ]; then
echo "⚠️ 警告:磁盘使用率超过 80%!当前使用率:${DISK_USAGE}%"
else
echo "✓ 磁盘使用率正常:${DISK_USAGE}%"
fi
📝 使用方法:
将脚本保存为
health_check.sh并添加执行权限:chmod +x health_check.sh可以使用 crontab 设置定时检查(建议每小时检查一次):
# 每小时执行一次健康检查 0 * * * * /volume1/docker/halo/scripts/health_check.sh >> /volume1/docker/halo/logs/health.log 2>&1
8. 常见问题与注意事项
8.1 部署前注意事项
⚠️ 重要提醒:
不要随意修改 PostgreSQL 性能参数:文档中的 PostgreSQL 参数是针对 N100 处理器和机械硬盘专门调优的。如果你的硬件配置不同(如使用 SSD 或更大内存),建议使用 pgconfigurator.cybertec-postgresql.com 在线工具重新生成适合你环境的参数。
修改参数可能导致问题:
POSTGRES_WORK_MEM设置过大会导致内存溢出(OOM)POSTGRES_MAX_CONNECTIONS设置过高会消耗过多系统资源POSTGRES_JIT=off适合机械硬盘环境,但 SSD 环境建议开启
8.2 常见错误排查
错误1:容器启动失败
容器启动失败,提示 "Cannot link to a non-running container"
- 原因:Halo 容器在 PostgreSQL 未就绪时就开始连接
- 解决:确保
depends_on配置中有condition: service_healthy,这样 Halo 会等待数据库健康检查通过后才启动
错误2:数据库连接失败
数据库连接失败,提示 "Connection refused"
-
可能原因:
- PostgreSQL 容器未完全启动(等待状态变为 healthy)
pg_hba.conf配置不正确,拒绝了 Halo 的连接- Docker 网络配置问题
-
排查步骤:
- 检查容器状态:
docker compose ps - 查看数据库日志:
docker compose logs halodb - 确认网络连接:
docker compose exec haloapp ping halodb
- 检查容器状态:
错误3:访问博客显示 502 Bad Gateway
-
原因:Halo 应用未正常启动或健康检查失败
-
解决:
- 查看 Halo 日志:
docker compose logs haloapp - 检查健康端点:
curl http://localhost:8090/actuator/health/readiness - 确认外部 URL 配置是否正确:
--halo.external-url必须与你访问的地址一致
- 查看 Halo 日志:
错误4:内存使用过高
内存使用过高,容器被 OOM Kill
-
原因:
mem_limit设置过低,或 JVM 内存参数配置不当 -
解决:
- 适当增加
mem_limit值 - 调整 JVM 参数:
JVM_OPTS中的-Xmx不应超过mem_limit - 建议 JVM 最大堆内存设为容器内存限制的 50-75%
- 适当增加
8.3 安全加固建议
-
修改默认密码:
- 数据库密码(
POSTGRES_PASSWORD)应该修改为强密码 - 如果使用外部数据库管理工具连接,需要在
pg_hba.conf中添加对应的 IP 段
- 数据库密码(
-
限制外部访问:
- 只暴露必要的端口(8090 和 5555)
- 生产环境建议通过反向代理(如 Nginx)访问,并通过防火墙限制直接 IP 访问
-
定期更新:
- 关注 Halo 和 PostgreSQL 的安全更新
- 及时更新镜像版本:
docker compose pull
8.4 性能优化建议
-
监控资源使用:
# 查看容器资源使用情况 docker stats # 查看详细的 CPU 和内存使用 docker stats --no-stream haloapp halodb -
JVM 调优:
-Xms256m:初始堆内存,可以设为与-Xmx相同以避免动态调整-Xmx512m:最大堆内存,不应超过容器内存限制(建议留 256MB 给其他开销)-XX:MaxGCPauseMillis=20:GC 停顿时间目标,值越小 GC 越频繁
-
数据库连接池:
- Halo 默认使用 R2DBC 连接池,无需额外配置
- 如果遇到连接数过多的问题,可以调整 PostgreSQL 的
MAX_CONNECTIONS
评论交流
欢迎留下你的想法