使用WSL创建精简的Hexo写作环境

这篇文章将介绍一种方法,使用Windows Subsystem for Linux (WSL), Alpine Linux发行版, Git等工具,配置一个方便易用的的Hexo博客编写系统。

如果结合已有的网站服务器,还可以使用Git快速进行站点部署与更新。

前言

如您所见,这个网站的渲染生成器是Hexo。众所周知,Hexo是跑在Node.js上的玩意,但npm环境配置是出了名的复杂,甚至在我看来还不是那么优雅,略显混乱,在Windows上更是如此。

过去在Node.js上开发项目,总是避免不了和系统里的npm斗智斗勇,几番折腾,最后竟然无法正常地生成Hexo博客。实际上,也并不是没有考虑过切换博客生成后端,但迁移的成本同样存在,而且选择也较为有限。

不可否认,目前有许多优秀的Node.js管理系统,例如volta之类,但这些工具在Windows下支持也不能算好。而采用Linux作为主桌面?在目前还不能算特别方便。

所以,是否有一种方法,既能有效地管理npm,从而为Hexo创建一个隔离的环境,又能保证整体性能不差,还与Windows轻松兼容呢?

笔者认为,在2021年年底的当下,微软提供的Linux子系统,也就是WSL,是解决这一问题的比较优雅方法。它能满足上面的所有设计要求。以下是具体方法。

预备条件

  • 启用了 Windows Subsystem for Linux 功能的 Windows 10+ 操作系统
  • Alpine Linux的MINI ROOT FILESYSTEM(最小根文件系统)压缩包,可以在downloads | Alpine Linux找到
  • 其他必须工具,如git

关于Alpine Linux

Alpine Linux是一个非常精简的Linux发行版,本文编写时,其最新版minirootfstar.gz包,仅2.6MB大小,可以在为我们提供尽可能的环境隔离同时,大幅度节省隔离环境所需的额外空间开销。

MINI ROOT FILESYSTEM的作用是?

用于我们手动导入Alpine Linux到WSL中,作为我们的Hexo环境。不过,目前微软商店里已经上架了Alpine WSL应用程序包,和手动导入效果类似,可供选择。如果选择从应用商店安装,请跳过下面导入Alpine Linux一节。

为什么使用Alpine Linux发行版?

Alpine Linux发行版体积比目前绝大多数其他通用的发行版(无论是Debian系还是CentOS系)都要小得多。先前,Alpine由于musl的原因,兼容性不如其他glibc发行版。但后来docker普及之后,许多软件都提供了运行于Alpine Linux的二进制文件。包括Visual Studio Code远程插件,也提供了Alpine Linux的支持。因此,就本文描述的任务而言,选用Alpine Linux是完全没有问题的。

当然,如果更喜欢使用其他发行版,也是完全没有问题的,操作流程和下文整体上还是相同的。注意一下部分命令的替换就好。

导入Alpine Linux

解开GZip包

这一步,我们要导入Alpine Linux系统作为一个WSL的发行版(Distro)。

首先下载Alpine Linux的.tar.gz包,然后用解压工具,解压出.tar包。记住/复制这个.tar包的路径。例如,D:\alpine-minirootfs-3.15.0-x86_64.tar

注意事项

只需要将压缩包的第一层.gz部分解开,得到里面的.tar包即可。不需要解开.tar包里面的文件系统。

建立目录并导入发行版

在你希望放置这个发行版相关系统文件的地方,创建一个文件夹,这里为了举例子,我们取D:\AlpineHexoBlog。这个路径不一定需要在C盘,可以在你想的合理的位置。名字也是随意的。

接着,我们输入下述命令。

1
wsl --import AlpineHexo D:\AlpineHexoBlog D:\alpine-minirootfs-3.15.0-x86_64.tar --version 2

这个命令的意思是,将我们解压得到的.tar包,作为一个Linux发行版,导入一个新的发行版,并将相关文件放置在D:\AlpineHexoBlog目录下,这个发行版的名称叫AlpineHexo(这个名字可以随意),并指定使用WSL版本2(关于WSL1和2的差别,请自行参阅微软官方文档)。

这样,一个自定义的发行版就导入完成了。可以在普通命令行/Powershell里面输入wsl -d AlpineHexo(注意名称替换)进入该发行版的终端。如果电脑装有Windows Terminal,也可以在标签页启动列表中直接选择该发行版。

安装Hexo相关环境

切换到Alpine Linux终端,执行命令。

1
apk add nodejs npm git

等待安装完成。

安装Hexo

这部分的步骤和Hexo官方文档部分相同,因此直接放出命令如下:

1
npm install -g hexo-cli

创建博客资源主目录

参考Setup | Hexo

1
hexo init MyBlog

上面命令中,MyBlog是可以自定义的文件夹名称,这个文件夹用于存储博客网站的源文件,如站点设置,博客文章原始文件等。在生成渲染网站时,hexo程序会根据这一目录里的配置生成静态网站,我们的大部分操作也都将在这个文件夹里完成。本文剩余部分中,这个目录将称为Hexo主文件夹

如果输出如下所示,说明创建成功了。

1
2
3
INFO  Cloning hexo-starter https://github.com/hexojs/hexo-starter.git
INFO Install dependencies
INFO Start blogging with Hexo!

接着进入这个新文件夹,执行npm install

1
2
cd MyBlog
npm install

这下,我们的站点就初始化完成了。我们可以任意对这个站点进行自定义,如主题自定义,撰写文章等。


(可选)使用Git管理站点内容历史记录

这就完了?不打算让博客内容管理更加自动化一些吗?比方说版本控制,自动部署等等。我们接下来就来介绍!

我们首先从使用Git管理站点内容历史记录开始说起。网站内容日新月异,因此有需要将我们的博客站点原始内容,如博客文章的原始Markdown文件,站点的配置文件,Node.js包依赖等,做一些历史备份。

还可以注意到,Hexo的站点文件,包括配置、文章等,实际上都可以被视为文本文件,因此Git就是负责管理版本历史的极佳选择。

以下方法不依赖于具体的Git云托管平台,因此不必纠结Github还是Gitee这种问题,原理都是一样的。

初始化Git仓库

进入Hexo主文件夹,并执行git init,这将在本地端创建一个初始Git仓库。

1
2
cd MyBlog
git init

接着,我们让git跟踪Hexo主文件夹内所有有用的文件,并进行第一次提交(commit)。其中-m参数后的是提交信息,内容任你填写。

1
2
git add .
git commit -m "Init commit."

推送到在线Git云托管平台

如果希望同步到在线的Git云托管平台,需要先自行在网页中创建一个空仓库,之后推送到该远程仓库中。这里以内网的一台Git服务器为例,创建的仓库地址为http://git.mysrv.in/dhao2001/MyBlog.git

1
2
git remote add origin http://git.mysrv.in/dhao2001/MyBlog.git
git push -u origin main

在另一个地方继续创作

下次,当重装了操作系统,或是更换了电脑,希望在另一个地方恢复相同的博客环境以及源文件,只需要这么做:

首先还是要搭好最基本的环境,也就是git, nodejs, npm。安装完成后克隆仓库到当前系统。

1
git clone http://git.mysrv.in/dhao2001/MyBlog.git

进入该文件夹,执行npm install,这将根据Hexo主文件夹里的package.json文件,安装所需的node.js包。

1
2
cd MyBlog
npm install

至此,在新环境中的博客编写环境已经准备就绪,就可以像在老地方一样创作和发布博客了。


(可选)使用Git进行页面部署

咦,怎么又是Git?上一节不是刚刚才讲过吗?

刚刚上一节的内容,主要是使用Git对博客的原始配置文件、原始代码、原始Markdown源文件进行管理。

而本节的内容是,使用Git工具,将本机上通过Hexo命令渲染生成的静态Html等站点资源文件,推送到远程服务器的指定Web目录内,供其他人通过Web服务器进行访问。

设置远程服务器

第一步,首先需要登录到博客网站主机,也就是部署在公网的Web服务器上。

接着,在服务器上安装git软件包。例如,如果使用的是Debian/Ubuntu系统,可以运行:

1
sudo apt install git

如果使用的是其他发行版,请参阅对应发行版的软件包安装手册。

设置Web服务器

关于Web服务器的选择

本教程以nginx作为Web服务器,若希望使用其他Web服务器如Apache,请自行调整以下步骤中Web服务器配置文件内容。

/var/www下新建一个文件夹,用于存放博客网站的静态Html文件,这里以blog作为文件夹名称示例。

1
mkdir -p /var/www/blog

接下来修改nginx的站点配置文件(Debian/Ubuntu系统位于/etc/nginx/sites-enabled/下),修改配置文件中的server块(当然,更建议新建一个server块,更方便区分和管理,详见其他教程)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80 default_server;
listen [::]:80 default_server;

root /var/www/blog;

index index.html;

server_name _;

location / {
try_files $uri $uri/ =404;
}
}

修改完配置文件后,重启nginx:

1
2
3
4
service nginx restart   # 使用系统服务管理重启nginx

# 如果上面的命令无效
nginx -s reload # 使用nginx命令重新读取配置文件

以上配置足以满足Hexo这样的静态网站部署要求。此时,可以考虑在/var/www/blog/下新建一个测试用Html文件,测试能否正常访问网站。

设置git仓库用于传输博客网站资源

设置好Web服务器后,如何使用git,把本机生成好的Hexo站点静态文件,推送到远程Web服务器上的网站目录,也就是本例中的/var/www/blog去呢?

实际上,在服务器和本地机器之间传输文件有很多方法。为了方便,考虑Hexo提供的部署工具集合,我们可以继续使用Git工具完成这项任务。

使用Git传输网站资源主要分成两个过程:

  • 第一步,先将本机生成的静态文件,通过Git传输到Web服务器上。
  • 第二步,使用Git钩子,将传输到Web服务器上的网站资源,复制到/var/www/blog,供Web用户访问。

为了达到这一目标,需要按照以下方法,在本机和远程Web服务器上分别调整配置。

远程Web服务器的配置

首先,在远程Web服务器上,创建一个用户,用户名可以自拟。当然,考虑使用Git的习惯,甚至可以给这个用户起名为git,如本例接下来所示。

1
adduser -s /bin/git-shell git

关于Git Shell

出于安全考虑,可以将git用户的默认shell指定为只有简单功能的git-shell,执行命令结束后就会退出。

注意,/bin/git-shell是Debian/Ubuntu系发行版的默认路径,新版本或其他发行版请务必确认git-shell的具体位置,并将/bin/git-shell替换成真实路径。

为了配置自动部署,建议使用密钥对进行认证。SSH密钥对可以在本机上使用ssh-keygenputty等工具生成。

1
2
3
4
5
6
sudo -u git /bin/bash -i        # 以git用户身份进行下列操作
cd ~
mkdir .ssh
cd .ssh
vim authorized_keys # 将公钥添加到authorized_keys中
chmod 600 authorized_keys

完成用户创建后,选择任意一处地方,创建一个git裸仓库,该仓库就是用于传输网站资源的git仓库。如果没有其他要求的话,git用户的家目录/home/git似乎就是一个不错的选择。

1
2
cd ~
git init -b main --bare myblog.git

为了在上传网站资源文件后,git可以自动帮我们将更新的资源文件,拷贝到/var/www/blog文件夹,我们需要在git仓库中创建一个post-receive钩子。

1
vim myblog.git/hooks/post-receive

添加如下内容:

1
2
#!/bin/sh
git --work-tree=/var/www/blog --git-dir=/home/git/myblog.git checkout -f

保存该文件,回到终端,赋予该文件可执行权限。

1
chmod 744 myblog.git/hooks/post-receive

也不要忘记将nginx网站根目录/var/www/blog所有权转移给用户git,否则将无法进行写入和更新操作!

1
chown -R git:git /var/www/blog

这样,远程Web服务器端的配置就完成了。

本机端的操作

这部分的配置也可以参考官方文档One-Command Deployment | Hexo

回到本机上的WSL Hexo环境。首先切到Hexo主文件夹。

1
cd MyBlog

为了使用Git进行一键部署,我们需要安装Hexo的Git一键部署插件。

1
npm install hexo-deployer-git --save

_config.yml最后deploy下添加一节。

_config.yml
1
2
3
4
5
deploy:
- type: git
repo: git@mysrv.in:myblog.git
branch: main
message:

记得把mysrv.in替换成你的远程Web服务器地址。如果有其他需求,如指定认证使用的私钥位置、端口等,可以参考我先前写的文章SSH配置主机别名及指定认证私钥路径,修改ssh_config文件实现。

完成以后,试试运行命令:

1
hexo g -d

然后再访问一下博客站点,是不是已经成功将Hexo内容部署上去了呢?


常见问题的解决

本机部署命令执行成功,但远程服务器上访问到的资源未更新

在调试网站并刷新时,建议使用Ctrl+F5快捷键强制刷新,这将强制浏览器重新请求网站文件,从而可以避免缓存对调试结果造成影响。

其次,检查网站目录/var/www/blog的内容是否已经更新,以及nginx配置文件正确性。

如果明明git推送成功,但却没更新到网站目录中,需要检查post-receive钩子内容是否正确,路径是否正确,权限是否正确。

在这里,post-receive钩子文件对以上用户(本例中为git)必须具备可执行权限。同时,nginx的站点文件夹(本例中为/var/www/blog)必须对git用户开放写入权限,对nginx用户www-data开放读取权限。

正确的权限是,权限8进制代码为744755,所有者为git:git。可以通过下列命令指定。

如果不确定,可以以git用户身份尝试以下命令:

1
2
3
cd ~
chown git:git myblog.git/hooks/post-receive
chmod 744 myblog.git/hooks/post-receive

再以root身份执行以下命令:

1
2
sudo chown -R git:git /var/www/blog
sudo chmod -R 744 /var/www/blog

修改博客后更新资源时,发现生成的页面字母大小写异常

这是由于默认的Hexo Git插件对大小写不敏感导致的。需要修改<Hexo主目录>/.deploy_git/.git/下面的config文件,在[core]一节中添加一行ignorecase = false:

1
2
3
4
5
6
7
8
9
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
+ ignorecase = false
[branch "master"]
remote = git@mysrv.in:myblog.git
merge = refs/heads/master