我是 Gemini-2.0-flash-exp 打造的 AI 助手,我的小脑袋瓜可厉害啦,帮你咻咻咻地概括文章重点!✨

本文介绍了如何将Hexo博客部署到Debian 12 VPS上,而非传统的GitHub Pages。文章主要采用了第二种部署方式:本地生成静态文件,然后通过Git Hooks自动部署到VPS。具体步骤包括:在VPS上安装Git,创建git用户并赋予sudo权限,关闭git用户的shell权限,初始化git仓库,配置SSH免密登录。接着,创建网站目录并配置Nginx。最后,详细讲解了如何配置Git Hooks,使本地push到VPS Git仓库时,能自动将网站文件部署到网站根目录。文章还涉及了用户组管理,并提供了本地Hexo配置的修改示例,方便读者进行部署。

网上大部分教程都是将 Hexo 部署到 GitHub Pages 上面,本文主要介绍如何部署到 VPS。

VPS 环境:Debian 12

准备工作

网上流传的武功秘籍分为两种:

  • 将 Hexo 项目上传到 VPS 上面后执行 hexo server,之后配置 Nginx 反向代理,让域名指向 http://localhost:4000。
  • 将 Hexo 在本地通过 hexo generate 生成静态文件,在通过 hexo deploy 部署到 VPS 上面,使用 Nginx 直接做 Web 服务器。
  • 相比第二种方式,第一种每次写博客与更新博客时候的操作会很繁琐。所以我们使用第二种方式进行部署,这样既可以将静态文件 deploy 到 VPS 上,也可以上传到 Github 上用作备份,操作性和安全性上都要胜于前者。
  • 而对于第二种方式而言,常用的又有 git hookrsync 两种自动部署解决方案。

本文主要介绍 git hook 部署过程。

Git Hooks 自动部署

部署原理

我们在本地编辑文本,然后使用 Git 远程部署到 VPS 的 Git 仓库。hexo d 命令实际上只 deploy 了本地的 public 文件夹,Git Hooks 实际上就是当 Git 仓库收到最新的 push 时,将 Git 仓库接受到的内容复制到 VPS 上的网站目录内。相当于完成了手动将 public 文件夹复制到 VPS 的网站根目录里。

安装配置 Git

安装 Git

通过 SSH 连接 VPS,执行:apt-get install git,完成后通过 git --version 查看 Git 版本,若显示版本信息则说明安装成功。

创建 git 用户

执行:adduser git,根据提示设置密码,其他信息可以一路空格。

赋予 git 用户 sudo 权限

安装 sudo:

apt update
apt install sudo

执行:

chmod 740 /etc/sudoers
nano /etc/sudoers

找到以下内容:

## User privilege specification
root ALL=(ALL:ALL) ALL
# 在 root ALL=(ALL:ALL) ALL 这一行下面添加
git ALL=(ALL:ALL) ALL

保存退出后,修改回文件权限:

chmod 440 /etc/sudoers

关闭 git 用户 shell 权限

我们也可以通过:

ssh git@VPS IP

ssh 连接服务器,登录到服务器上,对服务器进行各种操作,这通常很不安全,也不合适,我们只需要能对仓库操作就可以了,不需要更大的权限。

因此我们关闭 git 用户 shell 权限,执行:

nano /etc/passwd

将最后一行的 git:x:1001:1001:,,,:/home/git:/bin/bash 修改为 git:x:1001:1001:,,,:/home/git:/usr/bin/git-shell

这样,git 用户可以正常通过 ssh 使用 git,但无法登录 shell,因为我们为 git 用户指定的 git-shell 每次一登录就自动退出。

初始化 git 仓库

cd /home/git                # 切换到git用户目录
mkdir blog.git              # 创建git仓库文件夹,以blog.git为例
cd blog.git                 # 进入仓库目录
git init --bare             # 使用--bare参数初始化为裸仓库,这样创建的仓库不包含工作区

注意:裸仓库没有工作区,因为服务器上的 Git 仓库纯粹是为了共享,所以不让用户直接登录到服务器上去改工作区,并且服务器上的 Git 仓库通常都以 .git 结尾。

创建网站目录

cd /home/wwwroot/           # 切换目录
# 由于我想直接将博客放在/home/wwwroot/下,所以并没有创建blog目录
mkdir blog                  # 创建网站目录,以blog为例

配置 SSH

cd /home/git                # 切换到git用户目录
mkdir .ssh                  # 创建.ssh目录
cd .ssh
nano authorized_keys

然后将本地的公钥复制到 authorized_keys 文件里 (公钥即本地执行 cat ~/.ssh/id_rsa.pub (如 C:/Users/Your User Name/.ssh/id_rsa.pub)查看的内容)。

如果本地没有公钥,可以在本地用 ssh-keygen 命令生成一个新的 ssh 密钥对:

ssh-keygen -t rsa -C "your_email@example.com"

当系统提示输入文件名时,可以按 Enter 键使用默认路径(通常是~/.ssh/id_rsa),也可以指定一个新的路径。

Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa): [Press Enter]

你可以选择为私钥设置一个密码短语,以增加额外的安全性,这里可以直接按 Enter 键跳过。

Enter passphrase (empty for no passphrase): [Type a passphrase or press Enter]
Enter same passphrase again: [Repeat the passphrase or press Enter]

生成完成后,系统会告诉你密钥对已经生成并存储在指定路径中。

Your identification has been saved in /home/username/.ssh/id_rsa.
Your public key has been saved in /home/username/.ssh/id_rsa.pub.
# Windows -> C:/Users/username/.ssh/id_rsa, C:/Users/username/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:... your_email@example.com
The key's randomart image is:
+---[RSA 2048]----+
|    .==+.        |
|    =.o.         |
|   o  .          |
|  .  . o.        |
|   . oSo.o.      |
|  E .oBo= .      |
| .  .++.+ o      |
|  o+.o . o       |
|   +=o . .       |
+----[SHA256]-----+

注意:收集所有需要登录的用户的公钥,就是他们自己的 id_rsa.pub 文件,把所有公钥导入到 /home/git/.ssh/authorized_keys 文件里,一行一个。

通常这样配置后,hexo -d 部署就不需要输入密码了,而是通过证书校验身份。

用户组管理

查看 Nginx 运行用户:

cat /etc/nginx/nginx.conf | grep user

输出示例:

user www-data;

查看 Php-fpm 运行用户:

cat /etc/php/8.2/fpm/pool.d/www.conf | grep -E '^(user|group) ='

输出示例:

user = www-data
group = www-data

创建用户组 webgroup 并添加 gitwww-data 到用户组:

groupadd webgroup
usermod -aG webgroup git
usermod -aG webgroup www-data

更改文件夹的组:

chgrp -R webgroup /home/wwwroot

更改文件和目录的所有者:

chown -R git:git /home/git
chown -R www-data:webgroup /home/wwwroot

设置文件夹和文件的权限:

chmod -R 770 /home/git
chmod -R 770 /home/wwwroot

设置 SGID 位,确保新创建的文件或文件夹自动继承该组:

# 只设置单层目录的方法
# chmod g+s /home/git
# chmod g+s /home/wwwroot

# 递归设置所有子目录的 SGID 位
find /home/git -type d -exec chmod g+s {} \;
find /home/wwwroot -type d -exec chmod g+s {} \;

安装配置 Nginx

由于我之前已经装过了 Nginx,可以参考前面的教程,以下nginx安装内容来源于网络,仅供参考。

安装 Nginx

执行:apt-get install nginx,若输入 nginx -V 可以看到 nginx 版本信息,则安装成功。

配置 nginx

执行:

cd /etc/nginx/sites-available   # 切换目录
cp default default.bak          # 备份默认配置
nano default                    # 修改配置

参考配置文件内容:

server {
    listen 80 default;              # 默认监听80端口
    root /home/wwwroot;             # 网站根目录
    server_name nightfury.top, www.nightfury.top;  # 网址
    access_log  /var/log/nginx/blog_access.log;
    error_log   /var/log/nginx/blog_error.log;
    error_page 404 =  /404.html;
    
    location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
        root /home/wwwroot;
        access_log   off;
        expires      1d;
    }

    location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
        root /home/wwwroot;
        access_log   off;
        expires      10m;
    }

    location / {
        root /home/wwwroot;
        if (-f $request_filename) {
        rewrite ^/(.*)$  /$1 break;
        }
    }

    location /nginx_status {
        stub_status on;
        access_log off;
    }
}

保存退出后,启动 nginx:

systemctl start nginx

设置开机自动启动:

systemctl enable nginx

查看运行状态:

systemctl status nginx

显示 running 表示成功运行。

配置 Git Hooks

创建 post-receive 文件

root 用户下执行(或者 git 用户下执行,就不需要 sudo -u git

cd /home/git/blog.git/hooks  # 切换到hooks目录下
sudo -u git nano post-receive

复制下面的内容到 post-receive 文件中:

#!/bin/bash
echo "post-receive hook is running..."

GIT_REPO=/home/git/blog.git
TMP_GIT_CLONE=/tmp/blog
PUBLIC_WWW=/home/wwwroot

rm -rf ${TMP_GIT_CLONE}
git clone $GIT_REPO $TMP_GIT_CLONE
# 如果想每次更新都删除掉原有所有内容则用下面这条命令
# rm -rf ${PUBLIC_WWW}/*
# 如果想每次更新都删除掉原有所有内容但排除掉一些文件(深度为1)则用下面这条命令(比如排除掉 .mp4 和 .png 文件)
find ${PUBLIC_WWW} -maxdepth 1 -mindepth 1 ! -name '.mp4' ! -name '*.png' -exec rm -rf {} +
cp -rf ${TMP_GIT_CLONE}/* ${PUBLIC_WWW}

为什么不直接将裸仓库克隆到 Web 根目录下呢?我之前也一直被这个问题困扰,感觉先克隆到 tmp 目录再拷贝到 Web 根目录是多此一举。后来我觉得可能是出于项目安全的考虑,在执行 cp 命令的时候,.git 作为隐藏目录不会被拷贝到 Web 根目录下,也就避免了将整个仓库历史暴露在 Web 服务中。


赋予可执行权限:

chmod +x post-receive

本地操作

尝试连接

在本地打开 Git Bash:

ssh git@VPS的ip

若默认端口不是 22,则需要在后面加上 -p 端口号:

ssh git@VPS的ip -p 2024

由于前面配置了证书,所以正常应该是不需要输入密码了,如果仍然需要输入密码,请检查 /home/git/.ssh/home/git/.ssh/authorized_keys 的权限和所属用户/组是否正确,可以用如下命令查询 ssh 日志:

journalctl -u sshd -xe

ssh 连接成功后返回结果应该如下:

->ssh git@nightfury.top
->Linux XXX #1 SMP PREEMPT_DYNAMIC Debian XXX

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to nightfury.top closed.

提示无法登录 shell 是正常的,因为我们在之前就为 git 用户指定了 git-shell 每次一登录就自动退出。

配置 Hexo

打开本地博客根目录下的_config.yml 文件,找到最后的 deploy 配置,修改为:

## Deployment

## Docs: https://hexo.io/docs/deployment.html

deploy:
  - type: git
    repo: git@github.com:szNightFury/szNightFury.github.io.git
    branch: master
  - type: git
    repo: git@nightfury.top:/home/git/blog.git
    branch: master

到此,Hexo 建站就全部配置部署完毕了。