这篇是简化版流程,不展开写排错细节,默认你就是想要一套从零到成功上线的操作步骤。

最终目标是把 Halo 跑在:

  • 博客地址https://blog.glaube-ty.top

  • 后台地址https://blog.glaube-ty.top/console/


一、准备信息

先确认你已经有:

  • 一台阿里云轻量应用服务器

  • 一个域名,例如glaube-ty.top

  • 这个域名能在阿里云 DNS 控制台里管理解析

  • 服务器公网 IP

这套流程默认使用:

  • Halo 2

  • MySQL 8

  • Docker Compose

  • Nginx 反代

  • Let’s Encrypt HTTPS

  • 子域名blog.glaube-ty.top


二、登录服务器,安装 Docker

先用 SSH 登录服务器。

1)安装 Docker

按照你服务器系统正常安装 Docker,并启动 Docker 服务。

我在购买的时候直接选择默认安装了Docker了,就不需要再安装了。

阿里云轻量应用服务器是可以选择直接默认安装Halo的,但是我还是喜欢自己手动部署最新版的。

2)解决普通用户无法执行 Docker 的问题

把当前用户加入 docker 组:

sudo usermod -aG docker admin

然后:

  • 退出 SSH

  • 重新登录服务器

登录后检查:

groups
docker info

小提示

如果 docker info 报权限错误,说明你没有重新登录,这一步要重来。


三、创建 Halo 目录

mkdir -p ~/halo
cd ~/halo

四、写 Docker Compose 配置

新建 docker-compose.yml

cat > docker-compose.yml <<'EOF'
services:
  mysql:
    image: mysql:8.0
    container_name: halo-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 改成你自己的强密码
      MYSQL_DATABASE: halo
      MYSQL_USER: halo
      MYSQL_PASSWORD: 改成你自己的强密码
    command:
      - --default-authentication-plugin=mysql_native_password
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_general_ci
    volumes:
      - ./mysql-data:/var/lib/mysql

  halo:
    image: registry.fit2cloud.com/halo/halo:2
    container_name: halo
    restart: always
    depends_on:
      - mysql
    ports:
      - "8090:8090"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      HALO_EXTERNAL_URL: http://blog.glaube-ty.top
      SPRING_R2DBC_URL: r2dbc:pool:mysql://mysql:3306/halo
      SPRING_R2DBC_USERNAME: halo
      SPRING_R2DBC_PASSWORD: 改成你自己的强密码
      SPRING_SQL_INIT_PLATFORM: mysql
    volumes:
      - ./data:/root/.halo2
EOF

小提示

  • 密码建议只用:字母 + 数字 + 下划线

  • 不建议继续用 H2,直接上 MySQL,后面更省心

  • Halo 镜像这里直接用国内源,不要先折腾 Docker Hub


五、启动 Halo 和 MySQL

docker compose up -d

查看状态:

docker ps

看日志:

docker logs -f halo
docker logs -f halo-mysql

小提示

只要容器正常启动,就先继续,不要急着看根路径 / 能不能打开。


六、配置域名解析

去阿里云 DNS 控制台,给你的域名加一条解析:

  • 主机记录:blog

  • 记录类型:A

  • 记录值:你的服务器公网 IP

也就是:

blog.glaube-ty.top -> 你的服务器公网 IP

解析后检查:

nslookup -type=A blog.glaube-ty.top

小提示

看到解析到你的服务器 IP 就可以继续。


七、安装并配置 Nginx

1)安装 Nginx

sudo yum install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

2)写反代配置

直接覆盖 /etc/nginx/conf.d/halo.conf

sudo tee /etc/nginx/conf.d/halo.conf > /dev/null <<'EOF'
server {
    listen 80;
    server_name blog.glaube-ty.top;

    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_http_version 1.1;

        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_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
EOF

检查并重启:

sudo nginx -t
sudo systemctl restart nginx

八、放行服务器防火墙端口

去阿里云轻量应用服务器防火墙里放行:

  • TCP 22

  • TCP 80

  • TCP 443

小提示

Halo 运行在 8090,但因为后面走 Nginx 反代,所以公网只需要放行 80 和 443
8090 不需要长期对外开放。


九、用域名打开 Halo 后台

浏览器访问:

http://blog.glaube-ty.top/console/

如果能看到 Halo 初始化页面,就说明:

  • Docker 正常

  • Halo 正常

  • 域名正常

  • Nginx 反代正常

然后在初始化页面里填写:

  • 站点标题

  • 用户名

  • 邮箱

  • 密码

完成初始化。

小提示

如果根路径 / 打不开,不要慌。
优先访问 /console/


十、申请 HTTPS 证书(手动 DNS 验证)

因为前面 HTTP 验证比较容易出问题,这里直接用手动 DNS 验证

执行:

sudo yum install -y certbot
sudo certbot certonly --manual --preferred-challenges dns -d blog.glaube-ty.top

执行后,Certbot 会让你添加一条 TXT 记录。

例如它会提示你:

  • 记录名:_acme-challenge.blog.glaube-ty.top

  • 记录值:一长串随机字符

这时候去阿里云 DNS 控制台新增:

  • 记录类型:TXT

  • 主机记录:_acme-challenge.blog

  • 记录值:Certbot 当前页面给你的那一串值

小提示

主机记录不要写完整域名,只写:

_acme-challenge.blog

不是写成:

_acme-challenge.blog.glaube-ty.top

十一、确认 TXT 已生效,再继续

不要急着在 Certbot 那里按回车,先查 TXT:

nslookup -type=TXT _acme-challenge.blog.glaube-ty.top 8.8.8.8
nslookup -type=TXT _acme-challenge.blog.glaube-ty.top dns25.hichina.com
nslookup -type=TXT _acme-challenge.blog.glaube-ty.top dns26.hichina.com

只要查到的值和 Certbot 当前显示的值一致,就回到 Certbot 那个界面,按回车继续。

成功后,证书会保存在:

/etc/letsencrypt/live/blog.glaube-ty.top/fullchain.pem
/etc/letsencrypt/live/blog.glaube-ty.top/privkey.pem

十二、切换 Nginx 为 HTTPS

/etc/nginx/conf.d/halo.conf 改成下面这份:

sudo tee /etc/nginx/conf.d/halo.conf > /dev/null <<'EOF'
server {
    listen 80;
    server_name blog.glaube-ty.top;

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name blog.glaube-ty.top;

    ssl_certificate /etc/letsencrypt/live/blog.glaube-ty.top/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.glaube-ty.top/privkey.pem;

    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:8090;
        proxy_http_version 1.1;

        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 https;

        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}
EOF

检查并重启:

sudo nginx -t
sudo systemctl restart nginx

十三、把 Halo 外部地址改成 HTTPS

回到 docker-compose.yml,把这行:

HALO_EXTERNAL_URL: http://blog.glaube-ty.top

改成:

HALO_EXTERNAL_URL: https://blog.glaube-ty.top

然后重启 Halo:

cd ~/halo
docker compose down
docker compose up -d

十四、最终测试

浏览器访问:

  • https://blog.glaube-ty.top

  • https://blog.glaube-ty.top/console/

命令行测试:

curl -I https://blog.glaube-ty.top

如果都正常,说明部署完成。


十五、最后收尾

1)删除临时 TXT 记录

DNS 里的 _acme-challenge.blog 这条 TXT 可以删掉了。

2)公网端口只保留

  • 22

  • 80

  • 443

如果已经通过 Nginx 反代,不再需要公网开放 8090。


十六、以后证书到期怎么手动续期

因为这次用的是:

sudo certbot certonly --manual --preferred-challenges dns -d blog.glaube-ty.top

所以证书不会自动续期

到期前,手动续期流程和第一次申请几乎一样:

1)先看证书到期时间

sudo certbot certificates

或者:

sudo openssl x509 -enddate -noout -in /etc/letsencrypt/live/blog.glaube-ty.top/fullchain.pem

2)到期前重新执行续期命令

sudo certbot certonly --manual --preferred-challenges dns -d blog.glaube-ty.top

3)按照当前这次 Certbot 提示,重新添加 TXT

注意:

  • 必须用当前这次的新 TXT 值

  • 不要继续用旧值

  • 主机记录仍然写:_acme-challenge.blog

4)先查 TXT 是否生效,再回 Certbot 按回车

nslookup -type=TXT _acme-challenge.blog.glaube-ty.top 8.8.8.8
nslookup -type=TXT _acme-challenge.blog.glaube-ty.top dns25.hichina.com
nslookup -type=TXT _acme-challenge.blog.glaube-ty.top dns26.hichina.com

5)续期成功后,重启 Nginx

sudo nginx -t
sudo systemctl restart nginx

小提示

因为这次证书路径没变,所以续期成功后,Nginx 继续读取原来的证书路径即可。