hexo 使用 rsync 同步部署到云服务器

hexo 可以部署到 GitHub Pages / Vercel 或其他类似的托管平台,但是国内访问体验不好。我把 hexo 部署到了国内的腾讯云服务器上,这样速度会比较快,能带来更好的网页体验,但需要自己处理文件同步。本文记录了 macos 上如何使用 rsync 和 hexo-deployer-rsync 插件部署到云服务器。

本文记录的 rsync 同步部署方法未在 Windows 系统中测试。

什么是 rsync

rsync 是一款开源工具,用于文件同步和备份。它通过差异算法实现高效传输,支持增量备份,可以保持文件权限和属性,并通过 SSH 协议进行安全传输。rsync 在各种操作系统上广泛应用,提供可靠的文件同步和备份解决方案。

使用 rsync 部署 hexo 博客可以做到:

  1. 保证服务器文件和本地 hexo 生成的文件一致
  2. 本地有变动时增量同步,节省带宽速度快
  3. 同步删除服务器上的文章、页面、分类等信息
  4. 使用 ssh 协议,安全性有保证
  5. 压缩上传,速度快

安装 rsync

使用 rsync 部署 hexo 网站需要在本地和云服务器上分别安装 rsync。

macos 使用 brew 安装 rsync:

1
brew install rsync

ubuntu apt 安装 rsync:

1
apt install rsync

配置服务器公钥登录

如果你本机已有密钥对,将公钥上传到云服务器中,如果没有,使用 ssh-keygen 命令生成一个并将公钥上传到云服务器中。同时建议禁用密码登录并修改 ssh 端口。

生成密钥对时建议使用 ed25519,ed25519 的安全性在 RSA 2048 与 RSA 4096 之间,且性能在数十倍以上:

1
ssh-keygen -t ed25519  -C "your eamil"

安装 hexo rsync 插件

1
npm install hexo-deployer-rsync --save

配置插件

打开 _config.yml 文件,修改 deploy 配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
deploy:
type: rsync # 类型:rsync
host: 公网 IP
user: root # 系统用户
root: /www/wwwroot/blog/ # 实际的 hexo 部署目录
port: 22 # Default is 22
delete: true # Default is true 同步删除云服务器上的文件
progress: true # Default is true 是否显示进度
args: # 参数
rsh: # 指定要使用的远程 shell
key: # 自定义 SSH 私钥
verbose: true # Default is true 显示详细信息
ignore_errors: false # Default is false 忽略错误
create_before_update: false # Default is false 首先创建不存在的文件,然后更新现有文件

之后使用 hexo g && hexo d 命令测试是否已同步。如果遇到控制台错误根据提示解决。

与宝塔面板的兼容性

delete 参数为 true 时,会同步删除服务器上不存在于本地的文件。宝塔面板创建站点时会生成一个 .user.ini 文件,用于 php 防跨站。这个文件无法直接删除,但在本地系统中不存在。rsync 尝试删除时会报错,因为没有权限。静态网站不需要这个文件,所以可以删除。删除方法有两种:一是通过宝塔面板进入站点文件目录,找到并删除该文件;二是执行命令 chattr -i .user.ini 解除限制,然后删除文件。

延伸阅读

hexo-deployer-rsync 插件的实现比较简单,其实是根据配置的参数拼接 rsync 命令并执行。

源码:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
'use strict';

const color = require('picocolors');
const { spawn } = require('hexo-util');
const pathFn = require('path');

module.exports = function(args) {
if (!args.host || !args.user || !args.root) {
let help = '';

help += 'You should configure deployment settings in _config.yml first!\n\n';
help += 'Example:\n';
help += ' deploy:\n';
help += ' type: rsync\n';
help += ' host: <host>\n';
help += ' user: <user>\n';
help += ' root: <root>\n';
help += ' port: [port] # Default is 22\n';
help += ' delete: [true|false] # Default is true\n';
help += ' progress: [true|false] # Default is true\n';
help += ' args: <rsync args>\n';
help += ' rsh: <remote shell>\n';
help += ' key: <key>\n';
help += ' verbose: [true|false] # Default is true\n';
help += ' ignore_errors: [true|false] # Default is false\n';
help += ' create_before_update: [true|false] # Default is false\n\n';
help += 'For more help, you can check the docs: ' + color.underline('https://hexo.io/docs/deployment.html');

console.log(help);
return;
}

if (!Object.prototype.hasOwnProperty.call(args, 'delete')) args.delete = true;
if (!Object.prototype.hasOwnProperty.call(args, 'verbose')) args.verbose = true;
if (!Object.prototype.hasOwnProperty.call(args, 'progress')) args.progress = true;
if (!Object.prototype.hasOwnProperty.call(args, 'ignore_errors')) args.ignore_errors = false;
if (!Object.prototype.hasOwnProperty.call(args, 'create_before_update')) args.create_before_update = false;

const params = [
'-az',
process.platform === 'win32' ? pathFn.basename(this.public_dir) + '/' : this.public_dir,
args.user + '@' + args.host + ':' + args.root
];

if (args.port && args.port > 0 && args.port < 65536) {
params.splice(params.length - 2, 0, '-e');
if (args.rsh) {
if (args.key) {
params.splice(params.length - 2, 0, `'${args.rsh}' -i ${args.key} -p ${args.port}`);
} else {
params.splice(params.length - 2, 0, `'${args.rsh}' -p ${args.port}`);
}
} else if (args.key) {
params.splice(params.length - 2, 0, 'ssh -i ' + args.key + ' -p ' + args.port);
} else {
params.splice(params.length - 2, 0, 'ssh -p ' + args.port);
}
}

if (args.verbose) params.unshift('-v');
if (args.ignore_errors) params.unshift('--ignore-errors');
if (args.delete) params.unshift('--delete');
if (args.progress) params.unshift('--progress');
if (args.args) params.unshift(args.args);

if (args.create_before_update) {
// Create non-existing files before updating existing files.
// New files may be large documents, images and media, and we want to upload them first before updating links to them in the existing pages.
// Otherwise, the links may be updated first and temporarily point to new files that have not been uploaded yet.
params.unshift('--ignore-existing');
return spawn('rsync', params, {verbose: true}).then(() => {
params.shift();
return spawn('rsync', params, {verbose: true});
});
}
return spawn('rsync', params, {verbose: true});
};

hexo 使用 rsync 同步部署到云服务器
https://cui.cc/3d79e07b79d2/
作者
南山崔崔
发布于
2023年8月12日
许可协议