Hexo on CI/CD 本地部署实战攻略
原文:https://blog.ilemonrain.com/linux/hexo-with-cicd.html
在此之前,每次安装调试博客,基本上都是要经历N个流程……
你需要先安装个LAMP/LNMP环境,然后还需要调试大量的参数;
这还没完;你需要选择个合适的博客程序(比如Wordpress、Typecho等),然后又是一番折腾……
好不容易折腾完了,这些基于PHP语言的程序,总归是有点0day漏洞的吧……
然后又开始无限的防御加固之旅,直到最后,折腾到筋疲力竭,纵使再有灵感,也无力去写博文……
其实上面这些就是我的真实写照:装个环境调试就要2个小时,然后还需要各种各样的迁移调试,服务器还需要做加固防止被洞穿了,等费神费力的搬好家,连想写Blog的力气都没有了(这就是你咕了1年半没更新Blog的借口?)
后来也是经过各路大神推荐,从Typecho搬到了Hexo,但发现一个最最最头大的问题:
那就是这玩意没有后台!
没有后台也就意味着,你每更新一次文章,你就需要手动去部署一次文件,然后部署过程中,你总会忘点什么事情或者手残删错文件,然后……一切重来。(
毕竟在座的各位都是有Github账号的人,你说什么,没有?
那你总归知道 git clone 怎么用吧(苦笑
所以今天给Blog除草的同时,也给大家带来一篇基于CI/CD实现自动化博客本地构建更新的教程。
前言
首先,想要让Hexo实现自动化构建,你需要:
- 一个Git仓库(可以是公有的如GitHub,也可以是私有的如Gitlab CE、Gitea)
- 一个CI平台(可以是公有的如Travis CI,也可以是私有的如Drone CI)
- 一个存放生成的静态网页文件的服务器
由于国内访问GitHub Pages的速度实在太过感人,故本教程将这些服务全部放在同一台VM上完成。
本教程将使用如下组合方案作为演示环境:
- 系统: Debian 10 (amd64)
- Web服务器:Nginx 1.17.10
- Node.JS版本:v12.16.4 (注意暂时不要使用v14版本,可能会导致生成的文件均为0KB,也就是无效文件)
- Git服务器:Gitea
- CI服务器:Drone CI
准备好了吗?那么现在就开始部署!
小提示:
如果你使用 Linux / macOS 作为你的写作平台,请跳转 Chapter 02.01 ;
如果你使用 Windows 作为你的写作平台,请跳转 Chapter 02.02。
博客撰写平台的准备工作
For Linux
首先,Hexo首先作为使用Node.JS语言的博客程序,自然你需要先安装Node.JS和垃圾制造者NPM。
这里推荐使用nvm安装,这样可以无视不同系统之间,自带Node.JS版本不一致的问题。
执行以下命令,安装nvm:
1 |
|
之后会出现以下提示:
=> Downloading nvm from git to ‘/root/.nvm’
=> 正克隆到 ‘/root/.nvm’…
remote: Enumerating objects: 290, done.
remote: Counting objects: 100% (290/290), done.
remote: Compressing objects: 100% (257/257), done.
remote: Total 290 (delta 35), reused 97 (delta 20), pack-reused 0
接收对象中: 100% (290/290), 163.27 KiB | 5.44 MiB/s, 完成.
处理 delta 中: 100% (35/35), 完成.
=> Compressing and cleaning up git repository
=> Appending nvm source string to /root/.bashrc
=> Appending bash_completion source string to /root/.bashrc
=> Close and reopen your terminal to start using nvm or run the following to use it now:export NVM_DIR=”HOME/.nvm”[−s”NVM_DIR/nvm.sh” ] && . “You can’t use ‘macro parameter character #’ in math modeYou can’t use ‘macro parameter character #’ in math modeNVM_DIR/bash_completion” ] && . “$NVM_DIR/bash_completion” # This loads nvm bash_completion
出现这样的提示,表示nvm已经安装完成,可以继续下一步的操作了。
之后执行命令,安装 v12.16.3
版本的Node.JS:
1 |
|
出现以下结果:
Downloading and installing node v12.16.3…
Downloading https://nodejs.org/dist/v12.16.3/node-v12.16.3-linux-x64.tar.xz…100.0%
Computing checksum with sha256sum
Checksums matched!
Now using node v12.16.3 (npm v6.14.4)
Creating default alias: default -> v12.16.3
到这里,Node.JS已经安装完成了。我们可以尝试验证下Node.JS是否安装成功。
执行命令:
1 |
|
返回结果:
v12.16.3
执行命令:
1 |
|
返回结果:
6.14.4
如果两条命令都返回了正确的结果,说明Node.JS安装成功。
之后,执行命令,安装Hexo CLI:
1 |
|
返回结果:
/root/.nvm/versions/node/v12.16.3/bin/hexo -> /root/.nvm/versions/node/v12.16.3/lib/node_modules/hexo-cli/bin/hexo
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@~2.1.2 (node_modules/hexo-cli/node_modules/chokidar/node_modules/fsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {“os”:”darwin”,”arch”:”any”} (current: {“os”:”linux”,”arch”:”x64”})
+ hexo-cli@3.1.0
added 81 packages from 322 contributors in 5.927s
Hexo CLI安装成功。
之后安装 Git :
1 |
|
到这里,博客撰写平台已经准备好了,我们先放在一旁,稍后继续使用。
For Windows
不过不是每个人都会拥有一台MacBook,也不是每个人都会用Linux作为桌面环境。
大部分人其实都在用Windows的(
所以如果你用的是Windows平台的话,看这里 xD
相对来讲的话,Windows平台上的准备工作会简单很多,
毕竟图形化嘛,点点点就好了,敲命令是什么,听起来就头疼(
首先,你需要下载并安装一堆东西(敲黑板,请准备好下载工具,比如IDM什么的
下载安装Node.JS v12:
如果你人在海外,或者你的网速很好:
如果你人在国内:
https://npm.taobao.org/mirrors/node/v12.16.3/node-v12.16.3-x64.msi
安装过程取默认值即可。
向导中有个Chocolatey的选择,不用勾选,用不到,反而会让你多花一个小时安装这一个东西的(
安装完成后,打开命令提示符(也就是cmd)
如果你人在海外,或者网速很好,请继续往下看(
如果你人在国内,执行以下命令,将NPM镜像源切换到国内,加速NPM安装过程:
1 |
|
然后执行命令,安装Hexo CLI:
1 |
|
安装完成后,执行命令,确认安装状态:
1 |
|
如果你能得到的东西大概长这个样子的:
hexo-cli: 3.1.0
os: Windows_NT 10.0.19041 win32 x64
node: 12.16.3
v8: 7.8.279.23-node.35
uv: 1.34.2
zlib: 1.2.11
brotli: 1.0.7
ares: 1.16.0
modules: 72
nghttp2: 1.40.0
napi: 5
llhttp: 2.0.4
http_parser: 2.9.3
openssl: 1.1.1g
cldr: 36.0
icu: 65.1
tz: 2019c
unicode: 12.1
安装成功。
然后接下来安装Git:
如果你人在海外,或者你的网速很好:
https://github.com/git-for-windows/git/releases/download/v2.26.2.windows.1/Git-2.26.2-64-bit.exe
如果你人在国内:
https://npm.taobao.org/mirrors/git-for-windows/v2.26.2.windows.1/Git-2.26.2-64-bit.exe
安装过程中一路默认值即可(妈妈说过,不要什么东西都往C盘装(
到这里,博客撰写平台已经准备好了,我们先放在一旁,稍后继续使用。
关于博客撰写工具
Hexo作为使用Markdown语言的博客程序(和Typecho一样),趁手的撰写工具非常重要。
如果你使用 Windows/macOS 平台,个人推荐使用Typora,绝对舒适的Markdown写作体验:
如果你使用 Linux 平台,什么都别说了,vim天下第一!(被打死
总之,萝卜白菜,各有所爱;
别人推荐的不一定是最好的,适合自己的才是(
Gitea - Git服务器安装
从现在开始,才算是进入了重头戏:CI/CD平台的搭建与调试。
首先,你需要一个前端Web服务器作为反代工具:
至于用什么,看你使用习惯了(
本文以Nginx为例,安装过程不在本文的范围内(什么,你问我怎么装?宝塔/LAMP/AppNode/Oneinstack它们不香么(被榨汁
然后我们安装Git服务器:Gitea。
(小提示:从此处开始,所有涉及到的路径非固定值,如果你有较强的动手能力,可以尝试手动更换到你喜欢的路径)
首先下载Gitea:
1 |
|
然后安装后台服务管理器supervisor:
1 |
|
(小提示:你也可以选择其他喜欢的方式托管后台服务,具体请参考Gitea官方文档: https://docs.gitea.io/zh-cn/linux-service/ )
之后,新建服务项目:
1 |
|
里面写入如下内容:
1 |
|
之后保存退出。
新建一个专门用来跑Gitea的用户:
1 |
|
(小提示:如果你不喜欢单独新建用户运行程序,或者想要简化部署,完全可以使用root用户运行,但这样安全性会稍微降低)
调整Gitea目录的用户权限:
1 |
|
之后重启Supervisor,将Gitea跑起来:
1 |
|
或者可以执行命令,先测试是否能够正常启动,然后再重启Supervisor:
1 |
|
确认能够正常启动后,访问 **http://你的IP地址:3000/ ** ,打开Gitea页面:
如果能看到这个页面,说明Gitea启动成功,可以继续配置过程。
点击右上角的注册(点登录也可以),会来到初始化配置向导:
如果你的部署规模不太大的话,数据库类型选择SQLite3即可。
如果部署在大规模的环境(比如你需要用Gitea装其他的东西,或者多人协作使用),建议使用MySQL部署。
一般设置中的 Gitea 基本 URL 请填写公网可达的URL:
- 如果你使用公网IP访问,填写格式为
http://你的IP地址:3000
(端口号请和 HTTP 服务端口 保持一致) - 如果你使用域名访问(前置反代),请填写实际可访问的域名
其他的参数,按需填写。
之后下面的 管理员账号设置 中,输入用户名密码邮箱,作为Gitea系统的第一个账号,也同时作为Gitea的管理员账号。(不能是admin)
在配置成功后,点击最下方的 立即安装 完成配置向导。
如果一切顺利,将会来到Gitea的控制面板:
到此Gitea的初始配置完成,我们继续来配置Drone CI。
Drone CI - CI服务器搭建
在我们完成Gitea搭建后,我们已经拥有了属于我们自己的Git服务器。
那么接下来,为了实现CI/CD的伟大目标(不是),我们继续开始CI服务器的搭建。
首先点击右上角的头像,下拉列表选择 设置 ,打开用户设置面板:
之后导航栏点击 应用 ,打开应用授权设置:
拉到最下方,我们可以看到 管理 OAuth2 应用程序 一项:
在此,我们需要为Drone CI创建OAuth2应用授权:
- 应用名称填写任意名称即可 (比如
Drone
) - 重定向URI请填写 可以访问到你的Drone CI 的地址,具体填写方法接下来讲解
如果使用公网IP直接访问你的Drone CI,请填写 http://你的IP地址:6080
(地址和端口号非唯一,可根据实际需要修改,具体参考下文);
如果使用反代访问(域名访问)请填写 https://drone.example.com/login
(示例域名,请根据实际需要填写)
(小提示:Drone不支持Path访问,例如 https://myserver.example.com/drone/login
,请使用二级域名访问,例如 https://drone.example.com/login
)
输入完成后,点击 创建应用 ,完成配置。
创建完成后,会跳转到 编辑 OAuth2 应用程序 页面,请在此使用记事本类的工具立即保存 客户端ID 和 客户端密钥 信息,这一组密钥你将只有这一次能看见,刷新或者重新打开页面,客户端密钥都会消失,忘记密钥只能点击 重新生成密钥 以进行重置!
保存好密钥信息后,我们回到Shell终端,继续部署Drone CI。
接下来,我们使用命令,来生成Drone使用的Token:
1 |
|
生成如下结果:
bea26a2221fd8090ea38720fc445eca6
这32位Token同样非常重要,同样保存下来。
在此之后,因为Drone CI是Docker-Based CI,所以我们需要安装Docker。
执行一下命令,安装Docker:
1 |
|
会得到如下的结果
# Executing docker install script, commit: 26ff363bcf3b3f5a00498ac43694bf1c7d9ce16c
+ sh -c ‘apt-get update -qq >/dev/null’
+ sh -c ‘DEBIAN_FRONTEND=noninteractive apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null’
+ sh -c ‘curl -fsSL “https://download.docker.com/linux/debian/gpg" | apt-key add -qq - >/dev/null’
Warning: apt-key output should not be parsed (stdout is not a terminal)
+ sh -c ‘echo “deb [arch=amd64] https://download.docker.com/linux/debian buster stable” > /etc/apt/sources.list.d/docker.list’
+ sh -c ‘apt-get update -qq >/dev/null’
+ ‘[‘ -n ‘’ ‘]’
+ sh -c ‘apt-get install -y -qq –no-install-recommends docker-ce >/dev/null’
稍后即可完成Docker的安装。
我们现在开始正式部署Drone CI:
首先,我们收集部署Drone所需要的信息:
- DRONE_GITEA_SERVER: Gitea服务器的公网可达地址
- DRONE_GITEA_CLIENT_ID: OAuth2 应用程序中的客户端ID
- DRONE_GITEA_CLIENT_SECRET: OAuth2 应用程序中的客户段密钥
- DRONE_RPC_SECRET: Drone CI Token (就是刚才生成的32位Token)
- DRONE_SERVER_HOST: Drone CI的公网可达地址(完整的URL)
- DRONE_SERVER_PROTO: Drone CI访问使用的协议 (http/https)
这些参数看起来很头疼?别急,慢慢来(
我给几个示例:
- Example 1: 所有的服务都使用Nginx反代(Behind Nginx)
Gitea服务器地址:
1
https://gitea.example.com/
Drone服务器地址:
https://drone.example.com/
DRONE_GITEA_SERVER:
https://gitea.example.com/
DRONE_GITEA_CLIENT_ID:
ce2260d6-f800-4dbe-96d2-194778574814
DRONE_GITEA_CLIENT_SECRET:
wIfoK0ZZ1UclmODEtoom0MQIqvBbw-RNAW0eHqjGd9g=
DRONE_SERVER_HOST:
drone.example.com
DRONE_SERVER_PROTO:
https
- Example 2: 所有服务都使用IP+端口
Gitea服务器地址:
1
http://100.100.100.100:3000/
Drone服务器地址:
http://100.100.100.100:6080/
DRONE_GITEA_SERVER:
http://100.100.100.100:3000/
DRONE_GITEA_CLIENT_ID:
ce2260d6-f800-4dbe-96d2-194778574814
DRONE_GITEA_CLIENT_SECRET:
wIfoK0ZZ1UclmODEtoom0MQIqvBbw-RNAW0eHqjGd9g=
DRONE_SERVER_HOST:
100.100.100.100:6080
DRONE_SERVER_PROTO:
http
执行以下命令,部署Drone CI:(请将对应的变量替换为实际值)
1 |
|
在启动成功后,使用命令:
1 |
|
确认是否部署成功,返回如下结果:
{“level”:”info”,”msg”:”main: internal scheduler enabled”,”time”:”2020-05-15T11:04:01Z”}
{“acme”:false,”host”:”100.100.100.100:6080
“,”level”:”info”,”msg”:”starting the http server”,”port”:”:80”,”proto”:”http”,”time”:”2020-05-15T11:04:01Z”,”url”:”http://100.100.100.100:6080
“}
{“interval”:”30m0s”,”level”:”info”,”msg”:”starting the cron scheduler”,”time”:”2020-05-15T11:04:01Z”}
则证明部署成功。
接下来,在 登录你的Gitea账号 的情况下,打开Drone CI的WebUI地址(根据实际情况而定):
http://100.100.100.100:3000/
如果出现以下错误提示:
则说明你填写的信息有误(比如Drone CI启动参数和Gitea中设定的OAuth2参数不一致),此时可以删除Drone CI的Docker实例并尝试重新部署:
1 |
|
如果一切顺利,会出现以下提示:
点击 应用授权 即可完成Drone CI到Gitea的注册。
在点击应用授权之后,如果能够顺利跳转到以下页面:
则说明Drone CI服务器部署成功。
接下来,仅仅有一个Drone CI服务器是不够的,我们还需要部署对应的Runner才能实现自动化操作。
简单来讲,我们拥有了“指挥官”是没有用的,我们还需要有“士兵和装备”才能打仗。
还记得上面我们刚刚用到的那些变量参数么?
没错,我们要复用其中的部分参数:
- DRONE_RPC_PROTO: Drone CI访问使用的协议 (http/https)
- DRONE_RPC_HOST: Drone CI的公网可达地址,不要带http/https前缀
- DRONE_RPC_SECRET: Drone CI Token (32位Token)
- DRONE_RUNNER_CAPACITY: Drone Runner 单次可以并发运行的任务数量
- DRONE_RUNNER_NAME: Drone Runner的名称 (随意设置)
执行以下命令,部署Drone Runner:(请将对应的变量替换为实际值)
1 |
|
在启动成功后,使用命令:
1 |
|
如果得到如下返回结果:
time=”2020-05-15T11:21:09Z” level=info msg=”starting the server” addr=”:3000”
time=”2020-05-15T11:21:09Z” level=info msg=”successfully pinged the remote server”
time=”2020-05-15T11:21:09Z” level=info msg=”polling the remote server” arch=amd64 capacity=2 endpoint=”http://100.100.100.100:6080
“ kind=pipeline os=linux type=docker
则说明Drone Runner启动成功。
至此,已经完成了Drone CI的配置,以及与Gitea的对接。
是不是有点迫不及待的准备开始Hexo的安装配置了?那么,Now Let’s GO!
Hexo - 初始化博客
服务器端的工作暂时告一段落,我们先回到客户端(也就是博客撰写平台)上。
首先,我们前往Gitea页面,创建Hexo仓库。
点击右上角的 + 号,然后点击 创建仓库 :
之后在创建仓库页面中,输入你的仓库名称;
由于仓库内会包括关于你的服务器的某些敏感数据(比如服务器地址、服务器密码等),建议勾选 将仓库设为私有 ,将仓库转换为私有仓库。
仓库描述为可选项,可以写上,也可以不写。
完成后点击 创建仓库 ,完成仓库的初始化配置。
之后我们会来到仓库页面中,由于新建的仓库为空仓库,所以我们需要将这个空仓库clone到本地,以备进行Hexo的安装操作:
如果你在Chapter 02中安装好了 hexo-cli
,请继续往下看;
如果你还没安装好 hexo-cli
,仅需一个简单的命令即可:
1 |
|
我们首先创建一个新文件夹(因为Hexo的初始化必须在一个空文件夹中):
1 |
|
之后进入该文件夹中,执行命令,初始化我们的hexo blog:
1 |
|
会得到这样的结果:
INFO Cloning hexo-starter
https://github.com/hexojs/hexo-starter.git
Cloning into ‘C:UsersiLemonrainDesktopmyhexoblog’…
remote: Enumerating objects: 30, done.
remote: Counting objects: 100% (30/30), done.
remote: Compressing objects: 100% (24/24), done.
Receiving objects: 84% (136/161)sed 12 (delta 4), pack-reused 131 eceiving objects: 83% (134/161)
Receiving objects: 100% (161/161), 31.79 KiB | 161.00 KiB/s, done.
Resolving deltas: 100% (74/74), done.
Submodule ‘themes/landscape’ (https://github.com/hexojs/hexo-theme-landscape.git
) registered for path ‘themes/landscape’
Cloning into ‘C:/Users/iLemonrain/Desktop/myhexoblog/themes/landscape’…
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 1067 (delta 0), reused 0 (delta 0), pack-reused 1063
Receiving objects: 100% (1067/1067), 3.22 MiB | 653.00 KiB/s, done.
Resolving deltas: 100% (585/585), done.
Submodule path ‘themes/landscape’: checked out ‘73a23c51f8487cfcd7c6deec96ccc7543960d350’
[32mINFO [39m Install dependenciesejs@2.7.4 postinstall C:UsersiLemonrainDesktopmyhexoblognode_modulesejs
node ./postinstall.jsThank you for installing EJS: built with the Jake JavaScript build tool (
https://jakejs.com/
)npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@2.1.3 (node_modulesfsevents):
npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@2.1.3: wanted {“os”:”darwin”,”arch”:”any”} (current: {“os”:”win32”,”arch”:”x64”})added 253 packages from 450 contributors in 6.318s
5 packages are looking for funding
runnpm fund
for detailsINFO Start blogging with Hexo!
则说明Hexo初始化完成。
我们可以先看下Hexo长什么样子,在此目录下执行命令:
1 |
|
然后访问 http://localhost:4000
打开看看:
没错!简单的几个命令,我们就成功的创建了属于自己的Blog!
不过 “革命尚未成功,同志仍需努力”,我们的目标是星辰大海的嘛(
在命令行下按下两次 Ctrl+C ,停止Hexo服务器的运行,我们接下来继续配置Git仓库和Drone CI:
打开Drone CI控制面板:
有的人会疑惑,为什么Repo列表还是空白的?
不用紧张,点击右上角的 SYNC 就可以啦~
稍待一会,Gitea里面的Repo列表将会被同步过来;如果超过一分钟没有反应,请手动刷新:
我们点击这个Repo,打开配置页面:
由于我们没有为此仓库启用CI/CD能力,所以这时只需要点击 ACTIVATE REPOSITORY 就可以啦~
之后往下翻,会有个 Secrets 选项卡:
这里,我们将会用来保存CI/CD过程中,不想被其他人知道的“秘密”,比如服务器的连接方式、一些不想被人看见的配置参数等。
我们来创建如下Secret:
- ssh_host: 100.100.100.100 (部署目标服务器的地址)
- ssh_password: mypassword123 (部署目标服务器的登录密码)
- ssh_key: —–BEGIN RSA PRIVATE KEY—– (部署目标服务器的私钥,如果你使用密钥登录)
- ssh_passphrase: keypass456 (部署目标服务器私钥密码,如果你使用密钥登录)
将这些信息填写进Secret中,每写一条,点击一次 ADD A SECRET ,直到全部添加完成:
这样就完成了自动构建的配置,接下来,我们为此仓库写对应的CI/CD文件(.drone.yml):
参考配置文件如下:
1 |
|
(小提示:如果你使用用户名+密码登录,参数选取host+port+username+password;如果你使用用户名+密钥登录,参数选取host+port+username+key;如果你使用用户名+密钥+密钥密码登录;参数选取host+port+username+key+passphrase。)
将Drone配置文件放在Hexo同目录下,即可完成Hexo自动化构建配置。
接下来,我们将这个本地仓库与Gitea上的仓库进行初始化同步:
1 |
|
在弹出的登录提示中,输入你的 Gitea用户名和密码 ,即可完成初始化同步。
在完成配置,推送到仓库的瞬间,Gitea会自动触发Drone CI的构建请求,Drone CI会根据提交的最新分支,自动进行构建,就像这样:
此时去打开你的Blog,发现是不是已经成功刷出来刚构建好的Blog啦:
从现在开始,你只需要使用本地Markdown编辑器,编写好的文章直接使用Git推送到仓库,之后的事情,就拜托“高科技”啦~