前言

参考链接:

前置条件:已经在本地搭建好了Hexo博客,本地搭建博客也可以在本站找到对应教程。

网上有两种自动部署方案:rsync和git hook,本文用最常用的git hook。

吐槽:Hexo只是个静态博客,一点点页面只占几十M内存…也没必要用Docker,不方便本地修改内容再同步

git用户设置

  • 创建git用户

安装git和用得到的软件

1
apt update -y && apt install sudo vim git

创建git用户,根据提示输入密码(不显示)

1
adduser git
  • 赋予git用户sudo权限
1
2
3
4
5
# 获取sudoers文件读写执行权限
chmod 740 /etc/sudoers

# 修改sudoers文件
vim /etc/sudoers

找到root ALL=(ALL:ALL) ALL这一行并在下方新加一行:

1
git ALL=(ALL:ALL) ALL
1
2
# 改回sudoers文件权限为仅读写
chmod 400 /etc/sudoers
  • 配置git用户的ssh

先在电脑本地生成ssh密钥(如果已经有了就不用再生成),打开cmd输入:

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

然后在你的C盘/用户/用户名/.ssh/里会有私钥id_rsa和公钥id_rsa.pub,复制公钥的内容。

1
2
3
4
cd /home/git
mkdir .ssh
cd .ssh
vim authorized_keys

把复制的公钥粘贴进去并保存。

现在就配置好了ssh,我们可以在cmd中通过ssh git@VPS的IP连接到VPS的git用户,进行各种操作。

1
2
3
# 限制仅允许所有者读写、执行文件。防止未经授权的用户访问和修改关键的 SSH 文件,从而提高系统的安全性
chmod 600 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

目的:因为SSH密钥匹配免密登录,所以后面hexo d能直接通过ssh连接推送到VPS的仓库,而不用输入密码。

  • 关闭git用户shell权限

git用户不需要太大权限,只需要能操作git仓库即可。

因此可以关闭 git 用户的 shell 权限:

1
2
cd /
vim /etc/passwd

将最后一行的 git:x:1000:1000:,,,:/home/git:/bin/bash

修改为 git:x:1000:1000:,,,:/home/git:/usr/bin/git-shell

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

1
2
3
4
5
6
7
8
9
10
11
12
C:\Users\Administrator>ssh git@142.171.245.130
Linux lumengde.com 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64

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 142.171.245.130 closed.

如果报错:

1
2
3
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

那就把本地电脑的C盘/用户/用户名/.ssh/known.hosts文件删除,再次连接应该就行了。

创建博客网站目录

1
2
cd /
mkdir -p /var/www/blog

初始化blog.git博客仓库

1
2
3
4
5
6
7
8
9
10
# 进入git用户目录
cd /
cd /home/git

# 创建Hexo博客的仓库,比如blog.git
mkdir blog.git

# 使用--bare参数初始化为裸仓库,这样创建的仓库不包含工作区
cd blog.git
git init --bare

确保目录的用户权限组为git

1
2
3
4
5
6
7
8
9
root@lumengde:/# ls -l /var/www/blog
total 4
drwxr-xr-x 2 root root 4096 Feb 21 05:25 blog
root@lumengde:/# ls -l /home/git/blog.git
total 4
drwxr-xr-x 7 root root 4096 Feb 21 05:20 blog.git
root@lumengde:/# ls -l /home/git/.ssh
total 4
-rw-r--r-- 1 root root 576 Feb 21 05:17 authorized_keys

一般来讲执行这三个指令,返回的是root root,所以需要修改权限来变成git git

1
2
3
sudo chown git:git -R /var/www/blog
sudo chown git:git -R /home/git/blog.git
sudo chown git:git -R /home/git/.ssh

执行完再ls -l xxx检查一下。

配置git hooks脚本

通过编写 Git Hooks 脚本,本地上传博客后,能自动把仓库里的文件拉取到网站目录中。

1
2
3
4
5
6
7
8
9
10
11
12
cd /
cd /home/git/blog.git/hooks
vim post-receive

# 以下为填入的内容
GIT_REPO=/home/git/blog.git #仓库路径
TMP_GIT_CLONE=/tmp/blog #临时目录
PUBLIC_WWW=/var/www/blog #网站路径
rm -rf ${TMP_GIT_CLONE}
git clone $GIT_REPO $TMP_GIT_CLONE
rm -rf ${PUBLIC_WWW}/*
cp -rf ${TMP_GIT_CLONE}/* ${PUBLIC_WWW}

hexo d部署到VPS的blog.git仓库上时,Git Hooks 脚本自动执行流程:

  • 删除临时目录/tmp/blog里的所有内容
  • /blog.git仓库内的文件拷贝到临时目录/tmp/blog
  • 删除网站目录/www/blog内所有内容
  • 将目录/tmp/blog里的内容拷贝到网站目录/www/blog

赋予post-receive文件可执行权限

1
chmod +x post-receive

Nginx配置反向代理

Nginx Proxy Manager好用是好用,但是我们最好还是了解下Nginx怎么用,配置怎么写,以后图方便了再用NPM。

安装Nginx

官方链接,跟着做就行:【nginx: Linux packages】

【注意】中间安装Stable还是Mainline版本,选前者 Stable

nginx -v能够看到Nginx版本号说明安装成功。

配置Nginx

1
2
3
4
5
6
# 新版nginx文件结构和以前版本不一样,所以这一步跟往年教程不一样:
cd /etc/nginx/conf.d
# 第一次用的时候备份一下默认的配置文件
cp default.conf default.conf.bak
# 如果部署多个网站,server块都添加到这个default.conf里就行
vim default.conf

粘贴以下配置信息,server_name更改为你的域名:

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
server {
listen 80 default; #默认监听80端口,所有.conf文件只能有一个server设置了default【切记!!】
root /var/www/blog; #网站根目录
server_name blog.lumengde.com; #网址,可以加上www.xxx,英文逗号+空格来隔开
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 /var/www/blog;
access_log off;
expires 1d;
}

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

location / {
root /var/www/blog;
if (-f $request_filename) {
rewrite ^/(.*)$ /$1 break;
}
}

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

注意如果多于一个server,后续listen 80无需default。编辑好后,保存并退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动Nginx
systemctl start nginx

# 设置开机自启动
systemctl enable nginx

# 查看运行状态
systemctl status nginx

# 备用指令:
## 查看修改后的.conf配置语法是否正确
nginx -t
## 重启Nginx以更新配置
systemctl restart nginx

如果启动Nginx报错(很有用):

Nginx——Nginx启动报错Job for nginx.service failed because the control process exited with error code-CSDN博客

补充知识

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
旧版的Nginx配置文件为`/etc/nginx/sites-available`里的`default`;现在安装的新版配置文件为`/etc/nginx/conf.d`里的`default.conf`。



一般在第一次使用时,先备份一下默认配置文件`cp default.conf default.conf.bak`,`.bak`后缀表示这是一个备份文件。



在 Nginx 中,一个 `server` 块通常用于定义一个虚拟主机(Virtual Host),可以是一个网站。但是,`server` 块不仅仅可以用于定义网站,还可以用于其他的配置,例如反向代理、负载均衡等、HTTPS 服务等。



当 Nginx 启动时,它会加载所有配置文件,并将它们合并成一个整体的配置。所以我们可以把所有`server`块都写在`default.conf`里,或者使用多个不同的`xxx.conf`文件,编写各自的`server`块,一个文件叠加多个`server`块不用逗号隔开。



在Nginx的所有`.conf`配置中,只能有一个`server`块设为`default`,否则会报错。当客户端发送一个请求时,通常会包含一个主机头(Host Header)。这个主机头指定了客户端要请求的具体主机名(域名)。Nginx 根据请求的主机头来匹配相应的 `server` 块来处理请求。如果请求中的主机头与配置文件中的任何一个 `server` 块的 `server_name` 指令不匹配,Nginx 将会选择默认`server`块来处理这个请求。这样可以确保即使请求中没有明确指定主机头,Nginx 也能够正确地处理请求。



如果不使用 `default` 参数,Nginx 将按照配置文件中 `server` 块的顺序逐个匹配请求的主机头,而不会将任何一个 `server` 块标记为默认服务器块。这种情况下,必须确保每个请求都能匹配到相应的 `server` 块,否则请求会被发送到第一个 `server` 块中配置的网站。如果该网站有配置错误或无法处理该请求,可能会返回 404 错误页面或其他错误信息给客户端。



举个例子:如果其他域名例如`test.lumengde.com`解析到我的VPS IP上,但是我的Nginx没有对应的server(server_name匹配不到),则跳转到default server即我的博客上,网址不变。如果没设置默认server,则跳转第一个`server`块处理,即跳转到我的博客网站。

配置Hexo,同步到服务器

打开Hexo的配置文件_config.yml,修改以下内容:

1
2
3
4
5
6
7
8
9
url: 你的博客域名

deploy:
- type: git
repo: git@github.com:lumengde123/lumengde123.github.io.git
branch: main
- type: git
repo: git@你的VPS的IP:/home/git/blog.git
branch: master

原先使用Github Page需要用到CNAME,但是这里Nginx的server_name指定了域名对应的网站文件目录,所以非必须。

然后Hexo三连,博客的/public文件夹会同步到github的xxx.github.io仓库main分支VPS的blog.git仓库的master分支里。

域名解析

我是海外VPS,博客没备案且有违规文章,怕IP被封,所以套了cloudflare。以下是解析记录以供参考。

Type Name Content Proxy status TTL Actions
A blog 142.171.245.130 Proxied Auto Edit
A lumengde.com 142.171.245.130 Proxied Auto Edit

如果你也用了CloudFlare,需要把SSL/TLS改成Flexible(灵活模式),不然可能会报错误代码521,因为我们还没申请SSL证书,无法使用HTTPS,Nginx也没监听443端口。

此时,打开你的博客网站域名,就能访问博客主页。但是只能通过HTTP协议,即http://xxxx.com 来访问,我们Nginx配置监听的也是HTTP对应的80端口。后面还需要配置在SSL/TLS证书才能用https

报错解决:

如果无法正常访问,可以打开DNSPod 域名检测工具 网站健康免费诊断 - WHOIS查询_网络拨测_故障排查_证书_备案 - 腾讯云 DNSPod输入你的域名检查哪里有问题。

如果是国内云服务器,记得在安全组中打开80 443端口。并且国内服务器给域名备案才能访问。

成功访问后可以测测网站访问速度:网站测速-免费域名检测-网站测速-ping检测-域名污染-域名被墙-dns查询-IPv6网站测试-路由跟踪查询-劫持检测-SSL检测-TCP检测-UDP检测 (aicesu.io)

申请SSL证书

用Certbot给在Cloudflare上的domain申请Let’s Encrypt的泛域名证书

Certbot是一个ACME客户端,也是ACME客户端的参考实现,使用Python编写。Certbot默认使用Let's Encrypt作为证书颁发机构。选择Let’s Encrypt是因为它颁发的 SSL/TLS 证书是免费的。

泛域名证书,简单来说,申请了*.lumengde.com,则该域名的所有子域名都包含在内都可以使用该SSL/TLS证书,不用一个域名一个域名地申请,又乱又麻烦。

参考教程:更新:为 NGINX 配置免费的 Let’s Encrypt SSL/TLS 证书 (nginx-cn.net)

下载Let’s Encrypt客户端:

1
2
3
apt-get update
sudo apt-get install certbot
apt-get install python3-certbot-nginx

利用certbot 自动完成 NGINX 的 SSL/TLS 配置:

1
sudo certbot --nginx -d example.com -d *.example.com

使用 NGINX 插件生成证书,certbot会在 NGINX 配置中查找并修改包含 server_name 指令(含有您为其请求证书的域名)的 server 块。输入自己的邮箱即可。

证书生成后,NGINX 重新加载新设置。certbot 生成一条消息:Congratulations!xxxxx,显示证书成功生成,并指示证书在服务器上的位置。

如果用了cloudflare,这里会报错,直接看下一小节

1
2
3
Performing the following challenges:
Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA. You may need to use an authenticator plugin that can do challenges over DNS.
Client with the currently selected authenticator does not support any combination of challenges that will satisfy the CA. You may need to use an authenticator plugin that can do challenges over DNS.

解决办法:看下一小节。

此时我们再打开default.conf文件,能看到它自动为我们的server块添加了监听443端口和一些ssl配置。

重启Nginx更新一下配置即可

1
sudo systemctl reload nginx

然后输入https://xxx.com就能正常打开了(没开魔法访问可能需要等一会DNS缓存)

  • 自动更新Let’s Encrypt证书

我们申请的Let’s Encrypt证书只有90天有效期,到期需要手动更新,比较麻烦。这里我们添加一个cron任务来实现自动更新证书。

1
2
3
4
5
# 打开crontab文件
crontab -e

# 在文件中添加以下内容:
0 12 * * * /usr/bin/certbot renew --quiet

保存并关闭文件。所有已安装的证书将自动更新和重新加载。

该命令每天中午12点检查服务器上的证书是否会在未来 30 天内到期,如果是,则更新证书。--quiet 指令告知 certbot 不要生成输出。

Cloudflare申请Let’s Encrypt证书

参考教程:用Certbot给在Cloudflare上的domain申请Let’s Encrypt的泛域名证书 - 微言解语 (ausmis.com)

  • 获取Cloudflare API Tokens

打开Cloudflare,点击人像->My Profile->API Tokens->Create Token->选择Edit zone DNS->

Zone Resources可以Special zone选你的域名,仅该域名能使用该API Token(更安全),每次有新域名都申请各自的API Token;或者改为All zones,所有域名都可用(更方便)

最后点Continue to summary就好了。申请完Token之后要保存下来,仅显示一次且后面要用到,同时Cloudflare也提供一个验证的脚本,在shell上执行一下看看该token能否正常工作。

  • 配置cloudflare.ini

接下来开始准备cloudflare.ini文件,这个文件会被Certbot用于直接做DNS Challenge,所以非常重要,没有这个文件就无法生成泛域名证书了。

1
2
3
4
5
6
7
8
9
10
11
# 创建目录
mkdir -p ~/.secret/certbot

# 编辑ini文件
vim ~/.secret/certbot/cloudflare.ini

# Cloudflare API token used by Certbot
dns_cloudflare_api_token = 这里填入刚才获取的 API Token

# 设置文件正确权限
chmod 600 ~/.secret/certbot/cloudflare.ini

安装Certbot的Cloudflare API的插件。

1
sudo apt update && apt install python3-certbot-dns-cloudflare
  • 生成泛域名证书
1
certbot --dns-cloudflare -d lumengde.com -d *.lumengde.com -i nginx

提示:Which server blocks would you like to modify?,选择所有跟*.lumengde.com有关的server的序号,英文逗号隔开。直接回车则为全选。

脚本运行中要问你要Cloudflare的配置文件,输入下面字符串:

1
~/.secret/certbot/cloudflare.ini

然后它就会自动修改Nginx里的配置,网站开放443端口,可以使用https访问了。此时cf里把SSL/TLS改成Full或者Full(Strict)都行了。

再回到上一小节默认创建一个自动跟新证书的cron任务即可。

注意:生成的这一段话只需要在主域名的server块有就行,能作用到整个配置文件的所有server块中。

1
2
3
4
ssl_certificate /etc/letsencrypt/live/lumengde.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/lumengde.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

扩展阅读(看不懂):[记录]使用Certbot与Cloudflare插件申请通配符证书 - 掘金 (juejin.cn)