Halo 博客系统 Docker 部署指南

本文档基于现有的 docker-compose.yaml 文件,详细说明如何使用 Docker 部署 Halo 博客系统及其配套的 PostgreSQL 数据库。

1. 环境准备

  • 确保目标服务器已安装 DockerDocker Compose 插件(注意:Docker Compose 有 V1 和 V2 两个版本,命令格式略有不同)

    • Docker Compose V2(新版,推荐):使用 docker compose 命令
    • Docker Compose V1(旧版):使用 docker-compose 命令
    • 可以通过运行 docker compose versiondocker-compose --version 检查已安装的版本
  • Linux/NAS 服务器

    • 推荐使用 Docker Compose V2,大多数现代发行版已默认安装
    • 如果版本较旧,可以通过包管理器升级或使用官方安装脚本
  • Windows 本地测试

    • 需要安装 Docker Desktop(包含 Docker Compose V2)
    • 建议配置 WSL2 后端以获得更好的性能
    • 需要将 docker-compose.yaml 中的路径修改为 Windows 格式(如 ./data/halo2
  • 宿主机资源建议:至少 2 核 CPU,2GB 以上内存。根据配置文件中的资源限制(mem_limitcpus),系统最高会使用约 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 配置文件具有以下优势:

  1. 参数更全面:支持所有 PostgreSQL 参数,包括环境变量无法设置的
  2. 可维护性更强:便于版本控制、备份和审计
  3. 支持高级特性:可启用 JIT、分区表优化等
  4. 性能监控增强:可配置 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

📝 操作步骤

如果这是你第一次运行数据库(数据目录为空),建议按以下步骤操作:

  1. 先启动一次数据库:不挂载 pg_hba.confpostgresql.conf,让 PostgreSQL 自动初始化并生成默认配置文件

  2. 复制默认配置:进入容器将默认的配置文件复制出来

    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
    
  3. 修改配置:根据上述模板修改这两个配置文件

  4. 重新启动:修改 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 模式,提升了并发性能。
  • 数据库连接: 采用 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. 访问与初始化

  1. 等待两个容器的状态均变为 healthy
  2. 在浏览器中访问:http://<宿主机IP>:8090
  3. 如果你已经配置好了反向代理(如 Nginx 或 Traefik)和域名解析,可以直接访问:https://blog.metarl.cc.cd/
  4. 首次访问会进入 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"

📝 使用方法

  1. 将脚本保存为 backup.sh 并添加执行权限:chmod +x backup.sh

  2. 修改脚本中的路径为实际路径

  3. 可以使用 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 "日志清理完成"

📝 使用方法

  1. 将脚本保存为 clean_logs.sh 并添加执行权限:chmod +x clean_logs.sh

  2. 可以使用 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

📝 使用方法

  1. 将脚本保存为 health_check.sh 并添加执行权限:chmod +x health_check.sh

  2. 可以使用 crontab 设置定时检查(建议每小时检查一次):

    # 每小时执行一次健康检查
    0 * * * * /volume1/docker/halo/scripts/health_check.sh >> /volume1/docker/halo/logs/health.log 2>&1
    

8. 常见问题与注意事项

8.1 部署前注意事项

⚠️ 重要提醒

  1. 不要随意修改 PostgreSQL 性能参数:文档中的 PostgreSQL 参数是针对 N100 处理器和机械硬盘专门调优的。如果你的硬件配置不同(如使用 SSD 或更大内存),建议使用 pgconfigurator.cybertec-postgresql.com 在线工具重新生成适合你环境的参数。

  2. 修改参数可能导致问题

    • 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 网络配置问题
  • 排查步骤

    1. 检查容器状态:docker compose ps
    2. 查看数据库日志:docker compose logs halodb
    3. 确认网络连接:docker compose exec haloapp ping halodb

错误3:访问博客显示 502 Bad Gateway

  • 原因:Halo 应用未正常启动或健康检查失败

  • 解决

    1. 查看 Halo 日志:docker compose logs haloapp
    2. 检查健康端点:curl http://localhost:8090/actuator/health/readiness
    3. 确认外部 URL 配置是否正确:--halo.external-url 必须与你访问的地址一致

错误4:内存使用过高

内存使用过高,容器被 OOM Kill

  • 原因mem_limit 设置过低,或 JVM 内存参数配置不当

  • 解决

    1. 适当增加 mem_limit
    2. 调整 JVM 参数:JVM_OPTS 中的 -Xmx 不应超过 mem_limit
    3. 建议 JVM 最大堆内存设为容器内存限制的 50-75%

8.3 安全加固建议

  1. 修改默认密码

    • 数据库密码(POSTGRES_PASSWORD)应该修改为强密码
    • 如果使用外部数据库管理工具连接,需要在 pg_hba.conf 中添加对应的 IP 段
  2. 限制外部访问

    • 只暴露必要的端口(8090 和 5555)
    • 生产环境建议通过反向代理(如 Nginx)访问,并通过防火墙限制直接 IP 访问
  3. 定期更新

    • 关注 Halo 和 PostgreSQL 的安全更新
    • 及时更新镜像版本:docker compose pull

8.4 性能优化建议

  1. 监控资源使用

    # 查看容器资源使用情况
    docker stats
    
    # 查看详细的 CPU 和内存使用
    docker stats --no-stream haloapp halodb
    
  2. JVM 调优

    • -Xms256m:初始堆内存,可以设为与 -Xmx 相同以避免动态调整
    • -Xmx512m:最大堆内存,不应超过容器内存限制(建议留 256MB 给其他开销)
    • -XX:MaxGCPauseMillis=20:GC 停顿时间目标,值越小 GC 越频繁
  3. 数据库连接池

    • Halo 默认使用 R2DBC 连接池,无需额外配置
    • 如果遇到连接数过多的问题,可以调整 PostgreSQL 的 MAX_CONNECTIONS