Halo 博客搭建日记 (Docker, Nginx反代)

Halo 博客搭建日记 (Docker, Nginx反代)

_

背景

偶然看见halo博客框架,自带管理后台,搭建也比较方便(绝对不是因为自己菜),就试着搭建一下,感觉效果还不错,写一篇随笔记录一下。

自己云服务器还有其他服务,于是想实现用不同的子域名访问对应的服务,这时就想到了nginx的反向代理,再加上服务器经常换,有迁移需求,于是采用容器化的方案。除此之外还有SSL证书问题也想一并解决

于是目标便定下来了:
容器化部署halo博客框架和nginx,nginx负责反向代理,用子域名访问blog与其他服务,Let’s Encrypt申请证书,certbot实现证书续签。

搭建环境

1.一台东京云服务器 2C2G50G 峰值30Mbps Ubuntu Server 24.04 LTS 64bit(配有1penal方便管理)
2.一条闲置域名 (服务器不在国内,不需要备案)

大体思路

1.Nginx 与 halo框架 部署与测试

1.1 Nginx

我的配置文件保存路径将设置成: /opt/1panel/docker/compose/nginx
打算直接在 1panel 完成 Nginx 的部署,
在部署之前需要在<配置文件保存路径>目录下创建nginx.conf这个文件,不然可能编排失败(会被识别成目录,但是我们要挂载是这个文件)

# nginx.conf 参考文件
# 运行用户,默认是 nginx(容器内已创建该用户)
user  nginx;
# 工作进程数,建议设为 CPU 核心数,auto 表示自动匹配
worker_processes  auto;

# 错误日志路径与级别:debug > info > notice > warn > error > crit
error_log  /var/log/nginx/error.log notice;
# 进程 PID 文件路径
pid        /var/run/nginx.pid;

# 事件模块配置
events {
    # 单个工作进程的最大并发连接数
    worker_connections  1024;
    # 启用多路复用 I/O 模型(epoll 是 Linux 最优选择)
    use epoll;
}

# HTTP 核心模块配置
http {
    # 引入 MIME 类型映射文件
    include       /etc/nginx/mime.types;
    # 默认 MIME 类型(二进制流)
    default_type  application/octet-stream;

    # 日志格式定义,main 是格式名称
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # 访问日志路径,使用上面定义的 main 格式
    access_log  /var/log/nginx/access.log  main;

    # 启用高效文件传输模式
    sendfile        on;
    # 减少网络报文段数量(需配合 sendfile on 使用)
    tcp_nopush      on;
    # 优化 TCP 连接的关闭流程
    tcp_nodelay     on;

    # 长连接超时时间
    keepalive_timeout  65;

    # 开启 gzip 压缩(基础场景可关闭,高并发建议开启)
    # gzip  on;

    # 引入 conf.d 目录下的所有 .conf 文件(虚拟主机配置)
    include /etc/nginx/conf.d/*.conf;
}

在1pnael创建编排(docker compose)
配置文件保存路径: /opt/1panel/docker/compose/nginx

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    restart: always
    volumes:
      - ./conf.d:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
      - /etc/letsencrypt:/etc/letsencrypt # ssl证书 这个路径方便Let’s Encrypt申请完证书与续签完直接使用
      - ./logs:/var/log/nginx #  日志
    environment:
      TZ: Asia/Shanghai
    ports:
      - "80:80"
      - "443:443"
    networks:
      - public-network  # nginx与其他需要开放的服务共用的网络,便于容器之间通讯

networks:
  public-network:
    # 核心配置:声明为外部网络,存在则用,不存在则创建
    external: false
    name: public-network  # 固定全局唯一网络名
    driver: bridge  # 可选,默认就是bridge,可省略

1.2 halo框架

参考指南:Halo文档 - 使用 Docker Compose 部署

version: "3"

services:
  halo:
    image: registry.fit2cloud.com/halo/halo:2.22 # 这里使用的是Halo 社区版镜像,可以检测一下版本有没有更新
    restart: on-failure:3
    depends_on:
      halodb:
        condition: service_healthy
    networks:
      - halo_network
      - public-network # 如果更改了网络名这里记得改
    volumes:
      - ./halo2:/root/.halo2
    ports:
      - "8090:8090"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health/readiness"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 30s
    environment:
      # JVM 参数,默认为 -Xmx256m -Xms256m,可以根据实际情况做调整,置空表示不添加 JVM 参数
      - JVM_OPTS=-Xmx256m -Xms256m
    command:
      - --spring.r2dbc.url=r2dbc:pool:postgresql://halodb/halo
      - --spring.r2dbc.username=halo
      # PostgreSQL 的密码,请保证与下方 POSTGRES_PASSWORD 的变量值一致。
      - --spring.r2dbc.password=<密码>
      - --spring.sql.init.platform=postgresql
      # 外部访问地址,请根据实际需要修改
      - --halo.external-url=http://localhost:8090/
  halodb:
    image: postgres:15.4
    restart: on-failure:3
    networks:
      - halo_network
      - public-network # 如果更改了网络名这里记得改
    volumes:
      - ./db:/var/lib/postgresql/data
    healthcheck:
      test: [ "CMD", "pg_isready" ]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      - POSTGRES_PASSWORD=<密码>
      - POSTGRES_USER=halo
      - POSTGRES_DB=halo
      - PGUSER=halo

networks:
  halo_network:
  public-network:  # 如果更改了网络名这里记得改
    external: true # 外部网络

2.Nginx反向代理配置

当前目录: ./conf.d
重命名default.conf -> default.conf.bak 消除此配置文件的影响
新建文件subdomain.conf或<...>.conf

# subdomain.conf
# 1. 匹配 pan.web.com 子域名,转发到网盘服务
server {
    listen 80;
    # 精准匹配子域名
    server_name pan.web.com;

    # 反向代理配置
    location / {
        proxy_pass http://<网盘容器名>:5212/;
        # 传递客户端真实信息,必填
        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;
    }
}

# 2. 匹配 blog.web.com 子域名,转发到博客服务
server {
    listen 80 default_server;
    server_name blog.web.com;

    location / {
        proxy_pass http://<halo容器名>:8090/;
        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_buffer_size 16k;
        proxy_buffers 4 64k;
        proxy_busy_buffers_size 128k;
        # 延长超时时间,避免后端处理慢导致的 500
        proxy_connect_timeout 60s;
        proxy_read_timeout 120s;
    }
}

# 3. 匹配主域名 web.com,转发到默认主站服务
server {
    listen 80;
    server_name web.com;

    location / {
        proxy_pass http://<容器名,比如halo容器>:8090;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

启动两个编排并检查防火墙端口是否开放,测试是否能访问。

3.SSL证书与HTTPS访问

参考教程:使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期

这里申请的是泛域名SSL证书,域名托管在Cloudflare,使用DNS-01验证,
不同的域名服务商可能需要不同操作,这里只测试了Cloudflare

3.1 Let’s Encrypt申请SSL证书

安装 Certbot DNS 插件
apt install -y certbot python3-certbot-dns-cloudflare
获取域名服务商 API 密钥

在Cloudflare获取Token,此处略过

新建 Cloudflare 密钥配置文件

~/.secrets/certbot/cloudflare.ini:

# cloudflare.ini
# Cloudflare API 配置
dns_cloudflare_api_token = 你的 Cloudflare API Token

设置文件权限(避免其他用户读取):

chmod 600 ~/.secrets/certbot/cloudflare.ini
申请泛域名证书

执行以下命令,通过 DNS-01 验证申请泛域名证书:

certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
  --dns-cloudflare-propagation-seconds 60 \  # 等待 DNS 记录生效的时间
  -d <域名> \  # 主域名
  -d *.<域名> \  # 泛域名,匹配所有子域名(pan/blog 等)
  --email 你的邮箱@xxx.com \
  --agree-tos \
  --no-eff-email

申请成功后,证书路径应该为 /etc/letsencrypt/live/<域名>/

3.2 泛域名证书的自动续期

测试续期(不会真的执行续约仅测试):
certbot renew --dry-run
添加定时任务

切换root
编辑 crontab 配置

crontab -e

在新行添加:

0 3 * * * certbot renew --quiet && docker exec <nginx容器名> nginx -s reload

每天凌晨 3 点自动检查证书是否需要续期,续期成功后重载 Nginx 配置,让新证书生效

3.3 nginx 配置HTTPS

如果使用HTTPS访问,需要修改nginx配置,可以新建一个配置https-subdomain.conf或https-<...>.conf,并重命名subdomain.conf->subdomain.conf.bak,避免影响

# ========== 全局规则:所有 HTTP 请求强制跳转到 HTTPS ==========
server {
    listen 80;
    server_name <域名> pan.<域名> blog.<域名>;
    return 301 https://$host$request_uri;
}

# ========== 1. HTTPS 配置:pan.<域名> 转发到 <网盘容器名> 容器 ==========
server {
    listen 443 ssl;
    server_name pan.<域名>;

    # SSL 证书配置(路径和你 Certbot 申请的一致)
    ssl_certificate /etc/letsencrypt/live/<域名>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<域名>/privkey.pem;

    # SSL 安全优化配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers HIGH:!aNULL:!MD5:!3DES;
    ssl_session_timeout 10m;
    ssl_session_cache shared:SSL:10m;

    # 反向代理配置(复用原有逻辑)
    location / {
        proxy_pass http://<网盘容器名>:5212/;
        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;  # 传递 HTTPS 协议标识
    }
}

# ========== 2. HTTPS 配置:blog.<域名> 转发到 <halo容器名> 容器 ==========
server {
    listen 443 ssl;
    server_name blog.<域名>;

    # 复用同一套 SSL 证书
    ssl_certificate /etc/letsencrypt/live/<域名>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<域名>/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers HIGH:!aNULL:!MD5:!3DES;

    # 反向代理配置(保留原有缓冲区+超时优化)
    location / {
        proxy_pass http://<halo容器名>:8090/;
        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;

        # 原有优化参数
        proxy_buffer_size 16k;
        proxy_buffers 4 64k;
        proxy_busy_buffers_size 128k;
        proxy_connect_timeout 60s;
        proxy_read_timeout 120s;
    }
}

# ========== 3. HTTPS 配置:主域名 <域名> 转发到 <halo容器名> 容器 ==========
server {
    listen 443 ssl;
    server_name <域名>;

    # 复用 SSL 证书
    ssl_certificate /etc/letsencrypt/live/<域名>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<域名>/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # 反向代理配置
    location / {
        proxy_pass http://<halo容器名>:8090;
        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;
    }
}

重载nginx后测试是否能以https访问

4.主题推荐

Clarity

总体评价

实用性 4.5 / 5
美观性 4.5 / 5
折腾完感觉好麻烦,不如回到博客园写()

参考文献:

1.Halo文档 - 使用 Docker Compose 部署
2.使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期

Docker Compose 部署 WordPress(Nginx + MariaDB + Redis + WordPress) 2026-01-07
红米AX6 扩容 刷Uboot+openwrt 经历 2026-01-08

评论区