使用 Docker 打包 Hexo 写作环境。

前言

上篇文章中,我简单提到了在升级 Hexo 3 过程中遇到的一些痛苦。其实还有很多问题没有在上篇文章中提及。比如,我的渲染器使用的是 pandoc ,在 2.x 的时候,hexo-tag-plugins 里头的文字可以通过调用 renderSync 方法传给 markdown 进行渲染,而到了 3.0 ,这些文字无论怎么做都不能被 pandoc 或者 markdown 渲染。

早在我决定挥刀动斧定制我的个人博客之前,我早已经做好了发生这种情况的心理准备。我的博客最早是使用 Ruhoh 编写的,从主题到渲染器进行了大量的个人定制,后面出于种种原因我决定切换到Hexo ,于是我又花了很多力气把原来的博客移植到 Hexo 2.x 上。与其说是移植,不如说是一个基于 Hexo 2.x 的山寨版。现在 Hexo 一旦做了大幅更新,相当于我又得经历和前两次一样的折腾,实在是有心无力呀。考虑再三,我决定放弃使用 Hexo 3 ,而将我的博客所使用的环境 冻结 为 2.8.3 。

但这样又带来了问题:为了方便使用我的主题和插件的朋友们,我已经对这几个项目进行了升级以支持最新的 Hexo 3 。如果我只在我的机器上安装 Hexo 2.8.3 ,那我接下来维护这几个项目就变得非常不方便。

出于以上的原因,我决定使用 Docker 将 Hexo 2 和 Hexo 3 两套写作环境各自打包成镜像。这样,当我需要编写自己的博客时,就使用 Hexo 2 的镜像;当我需要维护几个 Hexo 主题和插件时,就使用 Hexo 3 的镜像。我也顺便将整个过程记录下来,形成此文,以供其他朋友参考借鉴。

安装 Docker

这里介绍 Mac 用户的安装方法。对于 Mac 用户,要安装 Docker 有两种选择:

  1. 安装命令行版本的 Boot2Docker 。这种方式比较适合熟悉命令行操作的用户。
  2. 安装图形化版本的 Kitematic 。Kitematic 提供了一个图形化的界面,比较适合不想折腾太多命令,只想尽快上手的用户。

我选择的是 Boot2Docker ,安装方法如下:

  1. 访问 boot2docker/osx-installer 的发布页面;
  2. 在 “Downloads” 区域,点击 Boot2Docker-x.x.x.pkg 下载最新版本的 Boot2Docker ;
  3. 双击该安装包安装 Boot2Docker 。
如果由于网速问题无法下载,DaoCloud 提供了镜像包,可以移步到 这里 下载。

安装完成后,在 Launchpad 中可以看到两个新图标:

与 Linux 上的 Docker 不同,在 Mac 系统中,Boot2Docker 的 Docker Daemon 是在 VirtualBox 虚拟机上跑的。因此在 Boot2Docker 里的 localhost 地址并不是本机的地址,而是虚拟机的地址。后面在介绍访问 Hexo 的 server 时会讲到如何获取虚拟机的地址。

制作和发布镜像

本节主要讲述如何自己创建打包、构建和发布 Hexo 的 Docker 镜像。如果你只对如何使用打包好镜像感兴趣,可以跳到下一节

创建一个 Docker 镜像主要有两种方法:

  1. 第一种方法是直接交互式运行一个现有的镜像,在里头完成所有必要的配置,然后使用 docker commit 命令建成一个新的镜像;
  2. 另一种方法是编写 Dockerfile ,然后执行 docker build 命令打包创建出一个新镜像。

编写 Dockerfile

Dockerfile 的编写比较简单,可以参考 Working with Docker ImagesDockerfile tutorial。Docker 比较有趣的一点是可以像搭积木一样,在 DockerHub 上现有的镜像的基础上做扩展,我所使用的基础镜像是 ubuntu:14.04 ,也可以使用 User Contributed 的 node.js 镜像(例如 nano/nodejs )做扩展,省下安装配置 node.js 的麻烦。

Hexo 2 的 Dockerfile

Hexo 2 主要用于我个人使用,为了方便自己,我把平常写博所用的依赖工具诸如 pandoc 和 yui-compressor 都打包了进来:

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
# Docker file for Hexo 2
FROM ubuntu:14.04
MAINTAINER Joseph Pan <cs.wzpan@gmail.com>
# use aliyun's mirror for faster download speed
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
# add pandoc repository
RUN sed -i 's/deb mirror.lupaworld.com/ubuntu vivid main universe/g' /etc/apt/sources.list
# install all dependencies
RUN apt-get update && \
apt-get install -y nodejs curl git-core pandoc yui-compressor && \
update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10 && \
curl -L https://npmjs.org/install.sh | sh && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

ENV HEXO_VERSION 2.8.3

# install hexo
RUN npm install -g hexo@${HEXO_VERSION}

RUN mkdir /root/blog
VOLUME /root/blog
WORKDIR /root/blog

EXPOSE 4000

对于有 shell 基础的朋友,以上的代码应该基本都可以自说明。这里主要讲一下 VOLUME 的作用,顾名思义,VOLUME 用于表明它后面跟着的那个目录将是一个挂载点。当我运行该镜像时,可以通过 -v 参数将本地的博客目录挂载到虚拟机里,从而实现在本地编辑而在虚拟机上渲染。

Hexo 3 的 Dockerfile

由于只是用来维护几个主题和插件项目,Hexo 3 的 Dockerfile 就要简单很多。因为我仍然可能需要维护 hexo-renderer-pandoc 项目,我也将 pandoc 打包了进来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Docker file for Hexo 3
FROM ubuntu:14.04
MAINTAINER Joseph Pan <cs.wzpan@gmail.com>
# use aliyun's mirror for faster download speed
RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
# add pandoc repository
RUN sed -i 's/deb mirror.lupaworld.com/ubuntu vivid main universe/g' /etc/apt/sources.list
# install all dependencies
RUN apt-get update && \
apt-get install -y nodejs curl git-core pandoc && \
update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10 && \
curl -L https://npmjs.org/install.sh | sh && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

# install hexo
RUN npm install -g hexo-cli

RUN mkdir /root/blog
VOLUME /root/blog
WORKDIR /root/blog

EXPOSE 4000

构建镜像

完成了 Dockerfile 的编写后,可以准备构建镜像了。但由于显而易见的网络原因,国内获取镜像的过程可能很慢。建议切换到 DaoCloud 的镜像(DaoCloud 真是业界良心啊!):

1
2
3
4
5
$ boot2docker ssh
$ sudo su
$ echo "EXTRA_ARGS=\"--registry-mirror=http://3f0704d3.m.daocloud.io\"" >> /var/lib/boot2docker/profile && exit
$ exit
$ boot2docker restart

注意以上方法只适用于 Mac OS 用户,其他系统的用户请自行查阅 Docker加速器

完成后开始构建 Docker 镜像,依旧是在 boot2docker 中敲入如下命令:

1
2
→ docker build -t wzpan/hexo:v2 -f ~/hexo/v2
→ docker build -t wzpan/hexo:v3 -f ~/hexo/v3
为了便于区分,本文一律使用 $ 来表示在 bash 上执行的命令,使用 来表示在 boot2docker 上执行的命令。

比较有趣的是 Docker 会对 Dockerfile 里的每一行命令都单独创建一个临时的镜像,以缓存每一步的结果。这样,即使构建过程中由于脚本错误或网络问题各种意外原因导致构建失败,当下一次发起创建时也会自动从上一次构建活动的最后一步成功步骤开始继续下一步的操作。

发布镜像

发布镜像到 DockerHub 就像推送代码到 Github 那样简单:

1
2
→ docker push wzpan/hexo:v2
→ docker push wzpan/hexo:v3

如果由于意外原因中断了发布过程,下一次尝试 push 操作可能会提示 push my-repo/my-app is already in progress 错误而无法继续。解决办法是重启一下 boot2docker :

1
$ boot2docker restart

使用镜像

完成了镜像的打包发布后,镜像的使用就显得非常简单有趣了。比如,如果要使用 Hexo 2 来渲染我自己的博客,我只需要在 boot2docker 中执行以下命令:

1
→ docker run --rm -p 4000:4000 -v ~/Documents/hexo-blog:/root/blog wzpan/hexo:v2 

其中,--rm 命令用于告诉 docker 当我结束当前的 container 时自动将该 container 删除,-p 命令用于告诉 docker 将虚拟机的 4000 端口映射到本机的 4000 端口。

之后就可以像往常一样使用 Hexo 的命令:

1
2
3
4
5
6
# 开启预览服务器
→ hexo server -i 0.0.0.0
# 生成博客
→ hexo generate
# 发布博客
→ hexo deploy
上面的 -i 0.0.0.0 不可省略,否则浏览器可能无法正常预览。

如果我需要在 Hexo 3 上维护几个项目,那么只需使用另一条命令:

1
→ docker run --rm -p 5000:4000 -v ~/Documents/test-blog:/root/blog wzpan/hexo:v3

有意思的是,此时 -p 命令告诉 docker 将虚拟机的 4000 端口映射到本机的 5000 端口。也就是说,通过映射到本地上的不同端口,我可以同时在虚拟机中的不同 container 里运行两个虚拟机端口号均为 4000 的服务!

Hexo 的服务器启动后,我们就可以在本地的浏览器预览页面了。但前面提过,在 Boot2Docker 里的 localhost 地址并不是本机的地址,而是虚拟机的地址。因此我们还需要获取一下虚拟机的地址:

1
$ boot2docker ip

将得到一个 ip 地址,那么就可以通过访问 http://虚拟机地址:映射端口号 来预览站点了。

对于一台新机器,要搭建一个 Hexo 博客写作环境也变得前所未有的简单。我只需安装好 docker ,clone 下我的博客仓库,然后使用如下一条命令下载安装所需版本的镜像:

1
→ docker pull wzpan/hexo:v2

博客的写作环境就妥妥儿的搭好了!

忍不住唱起歌来——

让我们 Docker 作伴, 活的潇潇洒洒。 Emacs 奔腾, 共享 Github 代码。 HHKB, 敲出 Hacker 喜悦。 轰轰烈烈, 把握 Coding 年华。 ……

《Duang》

后话

使用 Docker 来打包我的 Hexo 环境,不仅使我可以很方便的在不同版本的 Hexo 之间来回切换,也省去了我每换一台机就要重新配置写作环境的痛苦。目前已知的不足是服务器的更新明显比本地环境慢,有时博客已经写了好长一大段话,浏览器怎么刷新都没有显示最新的内容。这时就只能重启下 Hexo 了。这个问题在 Linux 环境中并不存在,猜测是 Mac 的 boot2docker 的虚拟化机制的问题。但总体而言,使用 Docker 的方式还是非常清爽的,今后我也会积极采用这种方式。人生苦短,我用 Docker 。

Comments