Hexo博客迁移至CNB自动部署

背景

本文记录了将Hexo博客从传统Docker部署方案迁移至CNB(Cloud Native Build)自动部署平台的完整过程,包括遇到的问题和解决方案。

原先部署方案

部署架构

原先使用GitLab CI/CD + Docker的自动化部署方式:

  1. 构建环境:基于GitLab CI/CD的自动化构建

  2. 构建流程

    • 当Dockerfile变更时触发构建阶段
    • 使用Docker构建包含Node.js和Hexo的镜像
    • 推送镜像到私有镜像仓库
    • 当相关文件变更时触发部署阶段
    • 通过SSH连接到服务器执行git pull
    • 使用docker-compose拉取新镜像并重新创建容器
  3. 部署方式:通过GitLab CI/CD自动部署到服务器Docker环境

原有Dockerfile结构

1
2
3
4
5
6
7
8
9
10
11
12
FROM node
RUN npm config set registry https://registry.npmmirror.com
RUN npm i hexo-cli -g
RUN hexo init /app/hexo
WORKDIR /app/hexo
RUN git clone [主题仓库] themes/next
RUN npm install hexo-generator-searchdb
RUN npm install hexo-asset-img --save
# 初始化必要页面
RUN hexo new page categories && \
sed -i "s/title: categories/title: 分类/" source/categories/index.md && \
sed -i "/^---$/i type: categories" source/categories/index.md

原有GitLab CI/CD配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# .gitlab-ci.yml
stages:
- build
- deploy

build:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build --no-cache -t [私有镜像仓库]/hexo:latest .
- docker push [私有镜像仓库]/hexo:latest
rules:
- changes:
- Dockerfile

deploy:
stage: deploy
image: docker-executor:latest
script:
- ssh root@[服务器地址] "cd /app/hexo && git pull"
- docker-compose --context production pull
- docker-compose --context production up -d --force-recreate
rules:
- changes:
- Dockerfile
- docker-compose.yml
- config/*
- blog/**/*

存在的问题

  1. 基础设施依赖:需要维护GitLab Runner和私有镜像仓库
  2. 构建资源消耗:每次构建都需要完整的Docker镜像构建过程
  3. 部署复杂性:需要配置SSH密钥、Docker context等多个组件
  4. 维护成本高:需要管理GitLab CI/CD环境和相关基础设施

现在的CNB部署方案

CNB平台优势

CNB(Cloud Native Build)是腾讯云提供的云原生构建平台,具有以下优势:

  • 全托管的CI/CD环境
  • 支持多种构建环境和插件
  • 与Git仓库深度集成
  • 支持自动触发和部署

新的部署架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# .cnb.yml配置文件
master:
push:
- docker:
image: node:18-alpine
volumes:
- /root/.npm:cow
- node_modules:cow
stages:
- name: install dependencies
script:
- npm config set registry https://registry.npmmirror.com
- npm install -g hexo-cli
- hexo init hexo-build
- cd hexo-build
- npm install hexo-generator-searchdb hexo-asset-img
- name: init pages
script:
- cd hexo-build
- cp -r ../next ./themes/
- cp -r ../blog/* ./source/_posts/
- cp ../config/_config.yml ./
- cp ../config/_config.next.yml ./
- hexo new page categories
- hexo new page tags
- hexo new page about
- sed -i "s/title: categories/title: 分类/" source/categories/index.md
- sed -i "/^---$/i type: categories" source/categories/index.md
- sed -i "s/title: tags/title: 标签/" source/tags/index.md
- sed -i "/^---$/i type: tags" source/tags/index.md
- name: build hexo
script:
- cd hexo-build
- hexo clean
- hexo generate
- name: rsync 上传到服务器
image: tencentcom/rsync
settings:
user: root
key: ${SSH_PRIVATE_KEY}
port: 22
hosts:
- [服务器地址]
source: ./hexo-build/public/
target: /app/nginx/html/blog/
script:
- echo "博客部署完成\!"

迁移过程中的主要改变

1. 构建环境变化

  • 原先:GitLab CI/CD + Docker镜像构建
  • 现在:CNB云端直接构建,无需Docker镜像

2. 部署流程优化

  • 原先:Docker镜像推送→服务器拉取→容器重建
  • 现在:静态文件生成→rsync直接同步到nginx目录

3. 配置管理

  • 原先:配置文件分散在不同位置
  • 现在:集中在config目录,便于管理

3. 基础设施简化

  • 原先:需要维护GitLab Runner、私有镜像仓库、Docker环境
  • 现在:只需CNB平台,无额外基础设施

4. 主题管理

  • 原先:通过git clone获取主题
  • 现在:将主题文件直接包含在项目中

5. 部署目标变化

  • 原先:部署到Docker容器,通过端口4000提供服务
  • 现在:直接部署静态文件到nginx目录,性能更优

遇到的问题及解决方案

1. YAML语法错误

问题描述

1
YAMLException: bad indentation of a mapping entry

原因:sed命令中包含特殊字符和中文,YAML解析器无法正确处理

解决方案

1
2
3
4
5
# 错误写法
- sed -i "s/title: categories/title: 分类/" source/categories/index.md

# 正确写法
- "sed -i \"s/title: categories/title: 分类/\" source/categories/index.md"

使用双引号包裹整个命令,内部使用转义字符处理特殊字符。

2. Nginx 404错误和重定向循环

问题描述

1
rewrite or internal redirection cycle while internally redirecting to "/index.html"

原因:Hexo站点缺少必要的页面(categories、tags、about),导致Next主题出现内部重定向循环

解决方案
在构建过程中添加页面初始化步骤:

1
2
3
4
5
6
hexo new page categories
hexo new page tags
hexo new page about
# 配置页面类型
sed -i "/^---$/i type: categories" source/categories/index.md
sed -i "/^---$/i type: tags" source/tags/index.md

3. 构建顺序问题

问题描述:构建时找不到配置文件或主题文件

解决方案
调整构建步骤顺序,确保在初始化页面前先复制所有必要文件:

  1. 复制主题文件到themes目录
  2. 复制博客文章到source/_posts
  3. 复制配置文件
  4. 初始化页面
  5. 构建生成静态文件

4. 权限和密钥管理

问题描述:rsync部署时需要SSH私钥认证

解决方案

  • 使用CNB的密钥仓库功能存储敏感信息
  • 通过环境变量${SSH_PRIVATE_KEY}引用私钥
  • 配置imports字段导入环境变量

5. Nginx配置问题

5.1 Docker挂载路径不一致

问题描述

1
"/app/chlm-nginx/html/blog/index.html" is not found (2: No such file or directory)

原因:nginx配置中使用的路径与docker-compose.yml中的挂载路径不一致

1
2
3
# docker-compose.yml
volumes:
- ./html:/home/chlm/html # 实际挂载路径

但nginx配置中使用了错误路径:

1
root /app/chlm-nginx/html/blog;  # 错误路径

解决方案
修正nginx配置中的路径:

1
2
3
4
location / {
root /home/chlm/html/blog; # 正确路径
index index.html index.htm;
}

5.2 目录访问403错误

问题描述

1
directory index of "/home/chlm/html/blog/categories/" is forbidden

原因:访问目录时nginx尝试列出目录内容而不是查找index.html

解决方案
使用正确的try_files配置:

1
2
3
4
5
location / {
root /home/chlm/html/blog;
index index.html index.htm;
try_files $uri $uri/ $uri/index.html =404;
}

5.3 try_files配置导致的重定向循环

问题描述

1
rewrite or internal redirection cycle while internally redirecting to "/index.html"

原因:不当的try_files配置导致无限重定向

常见错误配置

1
2
# 错误:会导致循环重定向
try_files $uri $uri/ /index.html;

正确配置

1
2
# 正确:避免循环重定向
try_files $uri $uri/index.html =404;

5.4 Hexo博客的完整nginx配置

最终优化配置

1
2
3
4
5
location / {
root /home/chlm/html/blog;
index index.html;
try_files $uri $uri/ $uri/index.html =404;
}

注意:不要添加静态资源过滤规则,会与其他服务冲突:

1
2
3
4
5
6
# ❌ 错误:不要添加这样的配置
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
root /home/chlm/html/blog;
expires 1y;
add_header Cache-Control "public, immutable";
}

原因

  • 图片等静态资源可能由其他服务处理(如MinIO对象存储)
  • 过度的location匹配会导致路径冲突
  • 应该让专门的服务处理对应的资源类型

5.5 Nginx核心指令详解

root vs alias 的区别

root指令

  • 定义文档根目录,会将location路径附加到root路径后面
  • 适用于大多数静态文件服务场景
1
2
3
4
# 访问 /blog/index.html 会查找 /var/www/blog/index.html
location /blog/ {
root /var/www; # 实际路径: /var/www + /blog/
}

alias指令

  • 定义路径替换,直接将location路径替换为alias路径
  • 适用于需要路径映射的场景
1
2
3
4
# 访问 /blog/index.html 会查找 /var/www/hexo/index.html
location /blog/ {
alias /var/www/hexo/; # 直接替换为 /var/www/hexo/
}

选择原则

  • 当URL路径与文件系统路径结构一致时使用 root
  • 当需要将URL路径映射到不同的文件系统路径时使用 alias

try_files指令详解

作用:按顺序尝试多个文件/URI,直到找到存在的文件或执行fallback

语法try_files file1 file2 ... uri;

常见用法场景

  1. 静态网站路径重写(如Hexo博客):

    1
    2
    3
    4
    5
    6
    try_files $uri $uri/ $uri/index.html =404;
    # 访问 /categories/ 时:
    # 1. 先找 /categories/ 文件(通常不存在)
    # 2. 再找 /categories/ 目录(存在但是目录)
    # 3. 再找 /categories/index.html(Hexo生成的页面)
    # 4. 都找不到返回404
  2. SPA应用路由支持

    1
    2
    try_files $uri $uri/ /index.html;
    # 对于Vue/React等SPA应用,所有路由都回退到index.html
  3. 多语言站点回退

    1
    2
    try_files $uri $uri/ /zh/index.html /en/index.html =404;
    # 按语言优先级依次尝试
  4. 缓存穿透处理

    1
    2
    try_files $uri @backend;
    # 静态文件不存在时转发到后端服务

什么情况需要try_files

必须使用try_files的场景

  1. 静态网站生成器(Hexo/Hugo/Jekyll等):

    • URL结构:/categories/
    • 文件结构:/categories/index.html
    • 需要try_files处理目录到文件的映射
  2. 单页应用(SPA)

    • 前端路由:/user/profile
    • 实际文件:只有 /index.html
    • 需要所有路由都回退到index.html
  3. 伪静态URL

    • 用户友好URL:/article/123
    • 实际处理:/article.php?id=123

不需要try_files的场景

  1. 纯静态文件服务:文件路径与URL完全对应
  2. 纯代理服务:所有请求都转发到后端
  3. API服务:每个endpoint都有明确的处理逻辑

Hexo博客为什么需要try_files

1
try_files $uri $uri/ $uri/index.html =404;

原因分析

  1. URL美化:Hexo生成美化的URL结构

    • 访问:https://blog.com/categories/
    • 文件:/categories/index.html
  2. 目录结构映射

    • //index.html
    • /categories//categories/index.html
    • /archives/2025//archives/2025/index.html
  3. 兼容性处理:同时支持带trailing slash和不带的URL

5.6 调试技巧

查看目录结构

1
2
3
4
5
6
7
8
9
# 安装tree工具
yum install -y tree

# 查看blog目录结构
tree /app/chlm-nginx/html/blog

# 检查关键文件是否存在
ls -la /home/chlm/html/blog/index.html
ls -la /home/chlm/html/blog/categories/index.html

nginx日志分析

1
2
3
4
5
6
7
8
# 实时查看错误日志
tail -f /var/log/nginx/error.log

# 检查配置语法
nginx -t

# 重载配置
nginx -s reload

try_files调试

1
2
3
4
5
6
# 临时添加调试日志
location / {
root /home/chlm/html/blog;
try_files $uri $uri/ $uri/index.html =404;
# 在error.log中会显示try_files的尝试过程
}

最佳实践建议

1. 配置文件管理

  • 将所有配置文件集中存放在config目录
  • 使用版本控制跟踪配置变更
  • 敏感信息使用环境变量管理

2. 构建优化

  • 使用volumes缓存npm包和node_modules
  • 选择合适的基础镜像(如node:18-alpine)
  • 合理安排构建步骤顺序

3. 部署策略

  • 使用rsync进行增量同步
  • 配置部署前后的检查脚本
  • 保留部署日志便于问题排查

4. 问题排查

  • 关注CNB构建日志
  • 检查YAML语法格式
  • 验证文件路径和权限
  • 测试nginx配置

总结

通过将Hexo博客迁移至CNB平台,实现了:

  • 自动化部署:Git推送即可触发构建和部署
  • 环境一致性:云端标准化构建环境
  • 操作简化:减少了手动操作步骤
  • 可维护性:配置文件化,便于版本管理

迁移过程中主要的挑战在于YAML配置语法和Hexo页面初始化,通过合适的转义和构建步骤安排成功解决。CNB平台为静态博客提供了一个高效、稳定的自动化部署解决方案。