如何搭建Halo博客并记录详细搭建日记?

摘要:背景 偶然看见halo博客框架,自带管理后台,搭建也比较方便(绝对不是因为自己菜),就试着搭建一下,感觉效果还不错,写一篇随笔记录一下。 自己云服务器还有其他服务,于是想实现用不同的子域名访问对应的服务,这时就想到了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 折腾完感觉好麻烦,不如回到博客园写() 演示Demo 参考文献: 1.Halo文档 - 使用 Docker Compose 部署 2.使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期