网上大部分教程都是将 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 hook
和rsync
两种自动部署解决方案。
本文主要介绍 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
并添加 git
和 www-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 建站就全部配置部署完毕了。