最近正好准备入学,于是想搭建一个博客,重新系统地记录一下自己的学习,本来想简单的部署一个静态博客并托管到 github,又正好看到cloudcone有折扣,打算买了个VPS学习一下,于是脑袋一拍,决定把博客部署到VPS中去。

准备工作

  1. 一台VPS:我在cloudcone购买
  2. 一个可解析的域名:我在万网购买
  3. 一个github账号

思路

  1. 创建一个hexo博客文件夹 blog/ 并上传到到 github
  2. 创建一个blog-webhook文件夹用来管理webhook服务,监听blog仓库更新后,拉取更新并重新渲染hexo的静态文件
  3. webhook 和 hexo 都进行容器化管理,使用nginx-proxy-manager进行反向代理,博客则只暴露静态文件给 nginx

项目

blog

最终blog的文件结构

1
2
3
4
5
6
7
8
9
BLOG  
├── node_modules
├── scaffolds
├── source
├── themes
├── _config.yml
├── db.json
├── Dockerfile
└── package.json

创建一个默认blog

详细配置可参考hexo官网

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装 Hexo
npm install -g hexo-cli

# 建站
hexo init blog

# 进入博客
cd blog

# 安装依赖
npm install

# 本地调试
hexo s

为blog添加容器化能力

在blog根目录创建Dockerfile,用于构建 Docker 镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 使用官方 Node.js 的轻量级 Alpine 版本作为基础镜像
FROM node:20-alpine

# 设置容器中的工作目录为 /app/blog(后续命令都在这里执行)
WORKDIR /app/blog

# 全局安装 hexo-cli(用于执行 hexo 命令)
RUN npm install -g hexo-cli

# 将宿主机的 package.json 复制到容器中当前工作目录(用于安装依赖)
COPY package.json ./

# 安装生产依赖(跳过 devDependencies,适用于部署场景)
RUN npm install --production

# 将项目所有文件复制到容器中(包括 source、themes、_config.yml 等)
COPY . .

# 开放 4000 端口(Hexo 本地服务器默认监听该端口)
EXPOSE 4000

为blog添加 git 能力

在 GitHub 上创建远端仓库

  1. 打开 https://github.com/new

  2. 填写信息:

    • Repository name: blog
    • Description: (可选)
    • Visibility: Public 或 Private(按需)
    • 初始化选项:不要勾选 README、.gitignore 或 License
  3. 点击「Create repository」

完整的本地 Git 初始化 + 推送流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 进入 Hexo 博客目录
cd blog

# 初始化 Git 仓库
git init

# 添加所有文件
git add .

# 提交 commit
git commit -m "init hexo blog"

# 关联远端仓库(替换成你刚刚创建的仓库地址)
git remote add origin https://github.com/你的用户名/blog.git

# 推送(我的默认分支名为 develop)
git push -u origin develop

至此我们完成关于博客部分的创建工作。

blog-webhook

最终blog-webhook目录结构

1
2
3
4
5
6
7
blog-webhook
├── webhook
│ └── scripts
│ └── refresh-blog.sh
├── Dockerfile
├── hooks.json
└── docker-compose.yml
1
2
3
4
5
# 创建 blog-webhook 文件夹
mkdir blog-webhook && cd blog-webhook

# 创建 webhook 文件夹,所有webhook服务都集中在这里
mkdir webhook && cd webhook

创建博客刷新脚本

1
2
3
mkdir scripts && cd scripts

touch refresh-blog.sh

编辑 refresh-blog.sh

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
48
49
50
51
#!/bin/bash
set -e

# 配置默认值
BRANCH="${HEXO_BRANCH:-develop}"
GIT_REPO="${GIT_REPO:-https://github.com/your_username/blog.git}" # 默认仓库地址

# 输出当前操作信息
echo "============================================="
echo "开始刷新博客内容"
echo "仓库: $GIT_REPO"
echo "分支: $BRANCH"
echo "时间: $(date)"
echo "============================================="

# 进入Hexo博客目录
echo "进入博客目录..."
cd /app/blog || { echo "错误:无法进入博客目录"; exit 1; }

echo "修复目录权限..."
chown -R $(id -u):$(id -g) .

# 拉取最新代码
echo "拉取最新代码 (分支: $BRANCH)..."
git fetch origin "$BRANCH" || { echo "错误:fetch 失败"; exit 1; }
git reset --hard "origin/$BRANCH" || { echo "错误:reset 失败"; exit 1; }

# 安装/更新依赖
echo "安装依赖..."
npm install || { echo "错误:依赖安装失败"; exit 1; }

# 清理旧文件
echo "清理旧文件..."
rm -rf public || echo "警告:public 删除失败"

# 生成新内容
echo "生成静态文件..."
npx hexo generate || { echo "错误:生成静态文件失败"; exit 1; }

# 验证生成结果
if [ -f "public/index.html" ]; then
echo "============================================="
echo "博客刷新成功!"
echo "最后更新时间: $(date)"
echo "============================================="
else
echo "============================================="
echo "错误:未找到生成的静态文件!"
echo "============================================="
exit 1
fi

创建 webhook 服务

在webhook目录下创建hooks.json文件,管理github hook 通知。注意替换关键字段

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
[
{
"id": "hexo-webhook",
"execute-command": "/app/scripts/refresh-blog.sh", // 执行脚本在容器内的位置
"command-working-directory": "/app/scripts", // 执行脚本在容器内的目录
"response-message": "Blog refresh triggered",
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hmac-sha1",
"secret": "secret", // secret
"parameter": {
"source": "header",
"name": "X-Hub-Signature"
}
}
},
{
"match": {
"type": "value",
"value": "value", // 格式为 your_username/blog,
"parameter": {
"source": "payload",
"name": "repository.full_name"
}
}
}
]
}
}
]

添加容器化能力

在 webhook目录下创建 Dockerfile 文件

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
# 使用官方 Node.js 的 Alpine 精简版本作为基础镜像
FROM node:20-alpine

# 安装必要的工具:
# - webhook:用于监听 GitHub Webhook 请求
# - git:用于拉取远程仓库代码
# - bash:用于执行 bash 脚本(如 refresh-blog.sh)
RUN apk add --no-cache \
webhook \
git \
bash

# 设置容器的工作目录为 /app(后续命令都在这个目录下执行)
WORKDIR /app

# 将本地 scripts 目录复制到容器的 /app/scripts 中
COPY ./scripts ./scripts

# 给刷新脚本加上可执行权限,确保后续可以直接执行
RUN chmod +x ./scripts/refresh-blog.sh

# 开放 9000 端口,用于 webhook 服务监听
EXPOSE 9000

# 启动 webhook 服务
# -verbose:打印详细日志
# -hooks:指定 webhook 配置文件路径(需要你在挂载中提供 hooks.json)
# -hotreload:配置文件变更时自动重新加载
CMD ["webhook", "-verbose", "-hooks", "/etc/webhook/hooks.json", "-hotreload"]

管理 blog 和 blog-webhook 镜像服务

在 blog-webhook 根目录下创建 docker-compose.yml 管理不同的镜像

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
48
49
50
services:
hexo:
container_name: hexo
build:
context: ./blog
dockerfile: Dockerfile
volumes:
- static_files:/app/blog/public
- hexo_node_modules:/app/blog/node_modules
user: "0:0"
restart: always
command: sh -c "cd /app/blog && npm install --unsafe-perm --no-optional && hexo generate && tail -f /dev/null"
networks:
- webproxy

webhook:
container_name: hexo-webhook
build:
context: ./webhook
dockerfile: Dockerfile
volumes:
- ./webhook:/etc/webhook
- ./blog:/app/blog
- static_files:/app/blog/public
- hexo_node_modules:/app/blog/node_modules
user: "0:0"
ports:
- "9001:9000"
restart: always
networks:
- webproxy

hexo-static:
image: nginx:alpine
container_name: hexo-static
volumes:
- static_files:/usr/share/nginx/html:ro
networks:
- webproxy
ports:
- "8080:80"

volumes:
static_files:
hexo_node_modules:

// 我自己定义的外部网络,支持不同镜像间通信
networks:
webproxy:
external: true

为blog-webhook添加 git 能力

操作与为blog添加 git 能力相同。

部署


本网站由 Nooobad 使用 Stellar 1.33.1 主题创建。
除非另有说明,本博客中的所有文章均采用 CC BY-NC-SA 4.0 许可协议。转载时请注明文章来源。