网站切换到 Hexo

一、Hexo 安装

Hexo 安装根据官方文档直接操作即可,安装前提是需要先安装 Nodejs(这里不再阐述直接略过)

1
npm install -g hexo-cli

Hexo 命令行工具安装完成后可以直接初始化一个样例项目,init 过程会 clone https://github.com/hexojs/hexo-starter.git 到本地,同时自动安装好相关依赖

1
2
# mritd.com 为目录名,个人习惯直接使用网站域名作为目录名称
hexo init mritd.com

进入目录启动样例站点

1
2
3
4
# 进入目录
cd mritd.com
# 启动本地服务器进行预览
hexo serve

hexo_demo

二、主题设置

基本的样例博客启动完成后就需要选择一个主题,主题实质上才决定博客功能,这里目前使用了 Fluid 主题,这个主题目前兼具了个人博客所需的所有功能,而且作者提交比较活跃,文档也比较全面。

1
2
3
4
# 下载主题
git clone https://github.com/fluid-dev/hexo-theme-fluid.git themes/fluid
# 切换到最新版本
(cd themes/fluid && git checkout -b v1.8.3 v1.8.3)

接下来修改 _config.yml 配置切换主题即可

1
2
3
4
# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: fluid

然后重新启动博客进行预览: hexo cl && hexo s

fluid_demo

关于主题其他配置可自行阅读 官方文档,文档有时可能更新不及时,可同时参考仓库内的 _config.yml 配置。

三、文章导入

关于 jekyll 博客的文章如何导入到 Hexo 中网上有很多脚本;但是实际上两个静态博客框架都是支持标准的 Markdown 语法书写的文章进行渲染,唯一区别就是每篇文章上的 “头”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
catalog: true
categories:
- [Kubernetes]
- [Golang]
date: 2018-11-25 11:11:28
excerpt: 最近在看 kubeadm 的源码,不过有些东西光看代码还是没法太清楚,还是需要实际运行才能看到具体代码怎么跑的,还得打断点 debug;无奈的是本机是 mac,debug 得在 Linux 下,so 研究了一下 remote debug
keywords: kubeadm,debug
multilingual: false
tags:
- Golang
- Kubernetes
title: 远程 Debug kubeadm
index_img: img/remote_debug.jpg
---

具体文章内容......

所以直接复制 jekyll 的 md 文件到 source/_posts 目录,并修改文档头部即可。

四、自动更新

目前博客部署在自己的 VPS 上,以前都是将博客生成的静态直接使用 nginx 发布出去的;但是面临的问题就是每次博客更新都要手动去 VPS 更新,虽然可以写一些 CI 脚本但是并不算智能;得益于 Golang 官方完善的标准库支持,这次直接几行代码写一个静态服务器,同时拦截特定 URL 来更新博客:

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
package main

import (
"fmt"
"net/http"
"os"
"os/exec"
"path"
)

func main() {
http.Handle("/", fileServerWithCustom404(http.Dir("/data")))
http.HandleFunc("/update", update)

fmt.Println("Updating WebSite...")
_, err := gitPull()
if err != nil {
fmt.Printf("WebSite update failed: %s", err)
}

fmt.Println("HTTP Server Listen at [:8080]...")
_ = http.ListenAndServe(":8080", nil)
}

// POST 请求 /update 触发 git pull 更新博客
func update(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("only support POST method.\n"))
return
}
bs, err := gitPull()
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bs)
}

// 包装一下 404 状态码,返回自定义的 404 页面
func fileServerWithCustom404(fs http.FileSystem) http.Handler {
fsh := http.FileServer(fs)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := fs.Open(path.Clean(r.URL.Path))
if os.IsNotExist(err) {
r.URL.Path = "/404.html"
}
fsh.ServeHTTP(w, r)
})
}

func gitPull() (msg []byte, err error) {
cmd := exec.Command("git", "pull")
cmd.Dir = "/data"
return cmd.CombinedOutput()
}

五、Docker 化

有了上面的静态服务器,写个 Dockerfile 将 Hexo 生成的静态文件打包即可:

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
FROM golang:1.15-alpine3.12 AS builder

ENV GO111MODULE on

COPY goserver /go/src/github.com/mritd/hexo/goserver

WORKDIR /go/src/github.com/mritd/hexo/goserver

RUN set -e \
&& go install

FROM alpine:3.12 AS dist

LABEL maintainer="mritd <mritd@linux.com>"

ENV TZ Asia/Shanghai
ENV REPO https://github.com/mritd/mritd.com.git

COPY --from=builder /go/bin/goserver /usr/local/bin/goserver

RUN set -e \
&& apk upgrade \
&& apk add bash tzdata git \
&& git clone ${REPO} /data \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& rm -rf /var/cache/apk/*

WORKDIR /data

CMD ["goserver"]

镜像运行后将使用 /data 目录最为静态文件目录进行发布,Hexo 生成的静态文件(public 目录)也会完整的 clone 到当前目录,此后使用 POST 请求访问 /update 即可触发从 Github 更新博客内容。

六、Travis CI 集成

所有就绪以后在主仓库增加 .travis.yml 配置来联动 travis ci;由于每次 push 到 Github 的内容实际上已经是本地生成的 public 目录,所以 CI 只需要通知服务器更新即可;强迫症又加了一个 Telegram 通知,每次触发更新完成后 Telegram 再给自己推送一下:

1
2
3
4
5
6
7
8
9
10
language: go

git:
quiet: true

script:
- curl -X POST ${CALLBACK}

after_script:
- curl -X POST https://api.telegram.org/bot${TELEGRAM_TOKEN}/sendMessage -d chat_id=${TELEGRAM_CHAT_ID} -d "text=mritd.com deployed."

七、gulp 优化

由于目前一些配图啥的还是存储在服务器本地,所以图片等比较大的静态文件仍然是访问瓶颈,这时候可以借助 gulp 来压缩并进行优化:

1
2
3
4
5
6
# 安装 gulp
npm install -g gulp
# 安装 gulp 插件
npm install gulp-htmlclean gulp-htmlmin gulp-minify-css gulp-uglify-es gulp-imagemin --save
# 重新 link 一下
npm link gulp

接下来编写 gulpfile.js 指定相关的优化任务

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
var gulp = require('gulp');
var minifycss = require('gulp-minify-css');
var uglify = require('gulp-uglify-es').default;
var htmlmin = require('gulp-htmlmin');
var htmlclean = require('gulp-htmlclean');
var imagemin = require('gulp-imagemin');

// 压缩html
gulp.task('minify-html', function() {
return gulp.src('./public/**/*.html')
.pipe(htmlclean())
.pipe(htmlmin({
removeComments: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
}))
.pipe(gulp.dest('./public'))
});

// 压缩css
gulp.task('minify-css', function() {
return gulp.src('./public/css/*.css')
.pipe(minifycss({
compatibility: '*'
}))
.pipe(gulp.dest('./public/css'));
});

// 压缩js
gulp.task('minify-js', function() {
return gulp.src('./public/js/*.js', '!./public/js/*.min.js')
.pipe(uglify())
.pipe(gulp.dest('./public/js'));
});

// 压缩图片
gulp.task('minify-images', function() {
return gulp.src('./public/img/*.*')
.pipe(imagemin(
[imagemin.gifsicle({'optimizationLevel': 3}),
imagemin.mozjpeg({'progressive': true}),
imagemin.optipng({'optimizationLevel': 7}),
imagemin.svgo()],
{'verbose': true}))
.pipe(gulp.dest('./public/img'))
});

// 默认任务
// 这里默认没有运行 minify-js,因为我发现 js 压缩以后 PageSpeed 评分
// 莫明其妙的降低了,目前只优先考虑桌面浏览器的性能,暂不考虑移动端
gulp.task('default', gulp.parallel(
'minify-html','minify-css','minify-images'
));

最后在每次部署时执行一下 gulp 命令即可完成优化: hexo cl && hexo g && gulp


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 国际许可协议进行许可,转载请注明出处。