Hexo持续集成部署笔记

Hexo部署笔记这篇文章里,我用我自己的亲自实践详述了如何把Hexo部署在自己的服务器上。

但是有一天,我突然想到一个问题。为了写个博客,我要在电脑上装Node.js,装Hexo,装Git,还在服务器上煞有介事地建了一个git仓库,结果只是用来当一个静态文件的中转站,把自己电脑上的静态文件拉过来,然后触发git的钩子把这些静态文件再一股脑复制到Nginx的目录中。如果有一天我换了电脑,那个博客的源文件都在原来的电脑上,服务器上只有一堆傻乎乎的静态文件,想想都觉得很划不来。于是,我干脆把Hexo博客的源文件也通过git上传到了服务器上,换电脑或者需要用别的电脑工作时就用git把源文件clone下来,然后再像之前那样生成静态文件,上传到服务器来更新博客。

于是新的问题冒了出来:我都把源文件传到服务器上了,我为什么不能让服务器帮我做这些生成静态文件并部署到Nginx上的事情,非要在别的电脑上吭哧吭哧地安装Node.js、安装Hexo,然后执行hexo ghexo d这样的重复操作?这样搞好后,换了新电脑,我唯一需要做的就是把源文件clone下来,改好了提交上去,别的都不用管。

我写博客这么想,写代码的程序员自然也会这么想,写好代码提交,编译测试发布一气呵成,根本不用在自己电脑上一套流程跑一通(对于许多大项目甚至是不可能在自己的小机器上执行编译测试这些操作的),这波操作就是程序员不时会提到的持续集成和持续部署,英语缩写分别是CI和CD。

现在,假设我的电脑上,除了系统本身、博客的源文件、Git客户端外,一无所有。而服务器上,什么都没有。博客的源文件是从老电脑那里复制出来的,所以hexo的那些框架,自己的老文章,都有。Git客户端要么是Linux、Mac系统可以很方便地安装,要么是Windows系统可以从网上下载一个绿色便携版。服务器上本来可以有一个存了静态文件的仓库,但是对于这个持续集成部署的教程来说,并没有什么用处,干脆就把它直接去掉了。

以下服务器的命令皆基于Ubuntu。如果你使用的是CentOS等系统,那么安装软件、软件配置文件的位置可能会有些不同。

创建Git仓库

首先,服务器得先安装git。

1
$ sudo apt install git

我们可以创建一个git用户专门用来管理Git仓库,用下面的命令就可以创建名为git的用户,并为其创建一个家目录/home/git,同时将其默认shell设为/usr/bin/git-shell使登录者除了git操作外不能做其他事。

1
$ sudo useradd -m -s /usr/bin/git-shell git

设置好git用户的密码,之后上传文件可能会用到。

1
$ sudo passwd git

现在可以创建一个存放hexo源文件的仓库了,这里命名为hexo.git(上一篇也是这个名字,但是存的是生成后的静态文件,现在我们拥有的是一个什么都没有的干净的服务器)。

1
2
3
4
$ cd /home/git
$ sudo -u git mkdir hexo.git
$ cd hexo.git
$ sudo -u git git init --bare

sudo -u git表示后面的命令通过git用户执行,因为我们禁用了git用户的shell,所以需要这种方式来让git用户执行命令。

git init --bare建立一个裸git仓库。这个仓库将用来存储上传的文件和历史记录,但不能直接在其上面修改,是git远端仓库的常见创建方式。

暂且离开这个git仓库。一个cd ~切回到用户的家目录。

在本地机器的设置

初始化hexo博客目录

如果你在本地机器有hexo博客的源文件目录,就跳过此步。如果你没有,按照一般情况是要通过hexo init来建立一个的,但是我们不想在本地机器安装hexo,因此只好手动模拟hexo init的操作来初始化的操作了:

1
$ git clone --recurse-submodules https://github.com/hexojs/hexo-starter.git hexo

命令最后的hexo表示git将hexo博客初始文件放在哪一个目录,你可以改成你喜欢的名字。

设置远程仓库

假设把本地的hexo博客源文件目录为hexo,那么首先进入这个目录:

1
$ cd hexo

如果是通过上述的git clone命令或者hexo init得到的hexo博客源文件目录,那么进入目录后会自带一个远程仓库origin,指向https://github.com/hexojs/hexo-starter.git。所以,我们直接创建另一个远程仓库,这里命名为upload,你可以选择任何你喜欢的名字:

1
$ git remote add upload git@example.com:hexo.git

命令里的add后面的upload即为远程仓库名字。后面git@example.com:hexo.git表示example.com指向的主机(也就是服务器)上的git用户有一个Git仓库是hexo.git,请按实际情况更改。

假如有类似于提示not a git repository的错误,那么这说明你的hexo博客源文件目录没有初始化为一个git仓库,这一般是迁移源文件目录时没有带上隐藏的.git目录导致的,用git init初始化为一个git仓库即可。不要忘记git add .git commit以方便将文件上传到服务器。

然后,把文件上传到服务器:

1
$ git push upload master

upload为远程仓库名,master是本地仓库默认的分支。

现在,回到服务器环境的搭建上来。

为git用户指定密钥对

我们即将安装Jenkins。Jenkins从git仓库中提取出源文件,然后执行构建过程。这一切都是自动的,我们需要为git用户指定密钥对,并将私钥告诉Jenkins,从而让Jenkins免密登录git用户执行操作。

首先生成密钥对:

1
$ ssh-keygen

一路回车,在家目录的.ssh文件下会有密钥对产生:一个是私钥id_rsa,一个是公钥id_rsa.pub

我们为git用户创建ssh有关的文件夹,把公钥导入到git用户。

1
2
3
4
5
$ sudo -u git mkdir /home/git/.ssh
$ sudo -u git chmod 700 /home/git/.ssh
$ PUBKEY=`cat ~/.ssh/id_rsa.pub`
$ sudo -u git sh -c "echo $PUBKEY | cat >> /home/git/.ssh/authorized_keys"
$ unset PUBKEY

测试一下是否能免密码登录git用户:

1
$ ssh -T git@example.com

如果出现SSH登录的欢迎消息,然后有fatal: Interactive git shell is not enabled之类的字样,说明登录成功。

常见的持续集成环境有Jenkins、gitlab-ci,更有像Travis这样的基于GitHub的持续集成环境。在本地搭建持续集成环境中,Jenkins是非常常见的持续集成工具,而gitlab-ci对于硬件环境要求较高(其实对于真正的必须要进行持续集成部署的环境,如大中型软件开发公司,这点硬件环境并不高,甚至可以说根本不够,但是我们只是持续集成部署一个博客来玩,顺便可能搞点别的小玩意而已,为此来个4GB内存以上的云服务器还是吃不消的),因此我们在这里用Jenkins作为我们的持续集成工具。

安装Docker

你可能会疑问,说好的装Jenkins,怎么先要装Docker呢?Docker作为一种轻量级的环境隔离方案,已经发展壮大,并被广泛应用于各个行业。而持续集成部署这一领域,正是Docker大放异彩的地方!

虽然我们这里只是部署个博客,但是一般用于持续集成的服务器会处理各式各样的工程,不同的工程所依赖的环境各不相同,比如工程A要版本1的库运行,工程B要版本2的库运行,如果把不同版本的库放在一起,势必会造成库的版本的混乱,而Docker提供的隔离环境就很好地解决了这个问题。

另外,许多程序员为了保持机器的“干净”,不喜欢直接安装各种各样的软件和库,但自己有时候又不得不用,于是就会在Docker的一个容器上安装这些环境然后运行应用,运行完后把容器销毁,什么都不会留下,满足了洁癖。

更何况,网络上已经提供了许许多多服务的Docker镜像,在想要某个服务时,不需要四处翻安装指南,只要下载这个Docker镜像,然后启动,做点简单配置,服务便启动了起来,非常方便。总之,装个Docker是绝对不亏的。

废话不多说,开始安装docker。出于通用性考虑,我们下载Docker的官方安装脚本以用来安装,由于国内网络环境,特意指定镜像为阿里云镜像,方便之后Docker的下载与更新:

1
2
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.com --mirror Aliyun

待Docker安装完成后,将自己的用户加入docker用户组,这样以后启动Docker不需要使用sudo:

1
2
$ sudo usermod -aG docker $USER
$ newgrp docker

由于国内非常神奇的网络环境,在从Docker下载(或者叫拉取)镜像时,使用默认的镜像服务地址很可能慢到怀疑人生,所以我们需要配置一下镜像加速。如果你是阿里云或腾讯云的服务器,它们各自提供了加速获取Docker镜像的方式,阿里云需要登录账号,在容器镜像服务里找到镜像中心、镜像加速器项;腾讯云则有仅腾讯云服务器可访问的镜像加速服务https://mirror.ccs.tencentyun.com。为了通用,我们使用网易云的镜像加速服务。

创建Docker的配置文件:

1
2
sudo mkdir -p /etc/docker
touch /etc/docker/daemon.json

编辑/etc/docker/daemon.json,其内容如下:

1
2
3
4
5
6
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
]
}

保存之,重新启动docker服务。

1
2
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

先测试一下docker是否正常安装:

1
$ docker run --rm hello-world

稍等一会,如果输出了一些类似Hello from Docker!的语句,那么Docker就安装成功了!

我们可以让Docker开机启动:

1
$ sudo systemctl enable docker

安装Jenkins

现在终于可以安装Jenkins了。既然我们安装了Docker,不如干脆就直接在Docker上运行Jenkins好了。根据官网的教程,推荐使用jenkins/jenkins镜像并定制我们自己的镜像,那么我们就用这个镜像吧。

下面的教程基本上借鉴自Jenkins官方文档

我们希望在Jenkins容器内也运行Docker来进行持续集成,所以我们会启动dind容器,也就是Docker in Docker,并将其和Jenkins容器连接。

  1. 我们创建一个Docker内部的网络。同一个网络内的容器可以像同一个内网内的主机一样进行网络访问。
1
$ docker network create jenkins
  1. 作为容器,当它结束运行,镜像被删除后,其内部的所有数据都会清空,这是Docker的优势之一,但是有时候我们会希望有些数据能够持久保存下去,比如Jenkins的一些配置,安装好的插件,构建的历史记录等。这时后我们需要为容器创建一个数据卷,以持久保存这些数据。另外,数据卷也可以起到容器间的文件共享的作用。

对dind容器内Docker的访问需要TLS证书,这个证书会在容器启动时生成,我们Jenkins容器需要这个证书来访问Docker,因此需要一个数据卷来共享证书,这里命名为jenkins-docker-certs。除此之外,我们还需要一个数据卷来持久化存储Jenkins的一些数据,命名为jenkins-data

1
2
$ docker volume create jenkins-docker-certs
$ docker volume create jenkins-data
  1. 启动dind容器。
1
2
3
4
5
6
7
8
9
10
11
12
13
$ docker container run \
--name jenkins-docker \
--rm \
--detach \
--privileged \
--network jenkins \
--network-alias docker \
--env DOCKER_TLS_CERTDIR=/certs \
--volume jenkins-docker-certs:/certs/client \
--volume jenkins-data:/var/jenkins_home \
--volume /etc/docker/daemon.json:/etc/docker/daemon.json \
--publish 2376:2376 \
docker:dind

我们一行一行来讲述这一段命令。

第一行就是启动Docker容器的命令,后面都是参数。

第二行--name指定被启动Docker容器的名称,比如这里就把启动的容器命名为jenkins-docker。如果不指定这一行,Docker会自动为容器指定一个名字,不会与其他容器名字冲突。

第三行--rm表示当容器退出后自动删除该容器实例。一般情况下,当一个容器运行程序完毕退出后,它的实例并不会被删除,其内部文件被保留以方便调试,用户数据也保留。而我们启动dind容器只是为了提供一个用于产生所需输出的Docker隔离环境,每次运行结束后没有保留产生的内部文件的必要,因此指定此参数。

第四行--detach表示容器将在后台运行。

第五行--privileged表示当前容器将运行于特权模式。一般情况下,Docker容器运行于非特权模式,也就是说,Docker容器内部的root用户并不能为所欲为,许多系统相关的操作权限都不可用,其中就包括在Docker中启用Docker。只有添加了这个参数后,才能够顺利在容器中启动Docker。

第六行--network指定容器所在的Docker网络。这里指定其网络为之前创建的jenkins

第七行--network-alias指定容器在所在网络中的主机名。这里指定为docker,之后任何在容器所在的网络内的其他容器需要访问容器时,就像访问一个主机名为docker的主机一样。

第八行--env DOCKER_TLS_CERTDIR=/certs指定了容器内的一个环境变量DOCKER_TLS_CERTDIR,并将其设为/certs。这一个设定将启用这个容器服务的TLS功能,并指定容器内服务管理TLS证书的根目录。由于容器被运行在特权模式,存在一定的安全风险,因此访问需要证书,以提高安全性,只有拥有证书的容器才能访问。

第九行--volume jenkins-docker-certs:/certs/client则是将先前创建的数据卷jenkins-docker-certs和容器内的目录/certs/client建立映射,容器对目录/certs/client的读写便是对数据卷jenkins-docker-certs的读写,反之亦然。

第十行--volume jenkins-data:/var/jenkins_home则是将先前创建的数据卷jenkins-data和容器内的目录/var/jenkins_home建立映射。这样,容器内运行的Docker容器就可以访问Jenkins的一些数据。

第十一行--volume /etc/docker/daemon.json:/etc/docker/daemon.json将容器内的docker配置文件和主机上的配置文件建立映射,这样便可以加速拉取镜像。

第十二行--publish 2376:2376将容器内部的2376端口映射为容器外(也就是主机)的2376端口。这样方便我们查看这个容器内部的Docker后台的情况。

最后一行即要启动的docker容器其对应的镜像docker:dind

  1. 基于官方的Jenkins Docker镜像,创建一个属于我们自己的镜像。

我们首先想一下,我们需要什么呢?我们用dind镜像构建了一个Docker服务器,那么Jenkins Docker镜像里面应该支持连接到这个Docker服务器,因此需要安装一个Docker客户端。所以我们的Dockerfile可以这么写:

1
2
3
4
5
6
7
8
9
10
11
12
FROM jenkins/jenkins:lts-slim
USER root
RUN apt-get update && apt-get install -y apt-transport-https \
ca-certificates curl gnupg2 \
software-properties-common
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN apt-key fingerprint 0EBFCD88
RUN add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable"
RUN apt-get update && apt-get install -y docker-ce-cli
USER jenkins

做的事情很简单,先是在Jenkins官方镜像的基础上,首先切换到root用户,执行安装Docker客户端的操作(Jenkins官方镜像有好几个版本,这里的lts-slim是比较小而且修改比较方便的一个,基于Debian系统,因此用的是Debian的安装方式)。之后切回到Jenkins官方镜像采用的jenkins用户,这使得运行起来的容器更安全。

然后我们用这个Dockerfile创建一个自己的Jenkins镜像:

docker build -t myjenkins:1.1 .

注意最后面的那个点,它表示根据当前目录下的Dockerfile文件创建镜像。现在我们就创建好了属于自己的Jenkins镜像。

  1. 现在我们运行自己的Jenkins镜像。
1
2
3
4
5
6
7
8
9
10
11
12
$ docker container run \
--name jenkins-runner \
--rm \
--detach \
--network jenkins \
--env DOCKER_HOST=tcp://docker:2376 \
--env DOCKER_CERT_PATH=/certs/client \
--env DOCKER_TLS_VERIFY=1 \
--publish 8080:8080 \
--volume jenkins-data:/var/jenkins_home \
--volume jenkins-docker-certs:/certs/client:ro \
myjenkins:1.1

第一行到第四行意义和之前启动dind时讲述过的相同。

第五行--network jenkins将容器加入名为jenkins的网络中,这也是之前dind所在的网络,因此该容器可以通过之前--network-alias设定的主机名docker来访问dind。

第六行至第八行的三行--env配置就是用来访问dind容器内部的Docker的。Docker其实分为客户端和服务端,一般我们安装的时候两者都会安装,但也可以只安装客户端,并指定DOCKER_HOST环境变量让客户端访问远程机器的Docker服务端。同时我们指定访问需要对TLS证书的认证,也就是第九行的环境变量。所需的TLS证书所在路径由第八行的DOCKER_CERT_PATH指定,这里是/certs/client,当然这是容器内部的路径。

第九行--publish 8080:8080将容器的8080端口映射到主机的8080端口,以便之后访问Jenkins的页面。如果你想把它映射到主机的80端口,那么应该使用--publish 80:8080

第十行将创建好的jenkins-data数据卷映射到容器内文件系统的路径/var/jenkins_home

第十一行将创建好的jenkins-docker-certs数据卷映射到容器内文件系统的路径/certs/client上,也就是第八行指定的TLS证书路径。最后的ro表示只读映射,即容器内对应的路径只能读,不能写。

第十二行为我们自己的Jenkins镜像myjenkins:1.1

完成!现在Jenkins已经启动,我们可以在访问服务器的8080端口来配置Jenkins了!

Jenkins初始化

Jenkins在第一次启动时需要几分钟,需要耐心等待。在本地浏览器打开http://example.com:8080example.com表示服务器的域名或IP地址),等待解锁Jenkins的页面出现。

页面会要求输入管理员密码,在服务器上通过下面的命令获得管理员密码,本质是进入Docker容器jenkins-runner内执行cat命令输出文件内容:

1
$ docker exec jenkins-runner cat /var/jenkins_home/secrets/initialAdminPassword

将输出的那一行字符粘贴到页面的输入框中,点击继续,进入自定义Jenkins页面。选择“安装推荐的插件”即可。由于Jenkins插件的服务器在国外,因此下载速度可能比较慢,我尝试过一些加速方法,但几乎没有效果,因此请耐心等待。在找到优雅且可行的操作方法后,我会专门用一篇文章来说明如何操作。

推荐插件安装完毕后,进入创建第一个管理员用户界面,在这里创建一个管理员用户,填上所有信息,点击“保存并完成”,有一个实例配置界面,同样点击“保存并完成”,页面将显示Jenkins已就绪,点击“开始使用Jenkins”即可。

创建流水线

Jenkins是通过仓库中的Jenkinsfile来决定如何执行构建的,即使是图形界面生成,也要将其生成的Jenkinsfile上传到仓库,既然如此,不如来学习一下如何使用Jenkinsfile。在本地仓库的根目录那里新建一个文件Jenkinsfile

我们这里采用pipeline的方式来构建,因此Jenkinsfile由pipeline作为根节点。

1
2
3
pipeline {

}

每个pipeline都要指定一个agent表示运行环境。这里指定为any,表示任意节点(反正我们这里就一个)。

1
2
3
pipeline {
agent any
}

作为一个流水线,它分为不同的阶段,这里我们先进行第一阶段,命名为prepare。Jenkins在流水线启动前,将对应的仓库的代码checkout一份,但是这并不包含子模块。而hexo init之后的仓库是带有子模块的(即默认的hexo主题),因此我们要求Jenkins去获取子模块的代码。这一阶段我们命名为prepare,放在stages节点中。这个阶段执行获取子模块的命令。

1
2
3
4
5
6
7
8
9
10
pipeline {
agent any
stages {
stage('prepare') {
steps {
sh 'git submodule update --init --recursive'
}
}
}
}

接下来我们要在安装有Node.js的环境中生成静态文件。同时我们还要配置好源,安装好hexo。所以我们需要创建一份基于Node的安装了Hexo的Docker镜像,再在这个镜像上执行生成。

Dockerfile文件如下:

1
2
3
FROM node:slim

RUN npm config set registry https://mirrors.huaweicloud.com/repository/npm/ && npm install hexo-cli -g

所以,我们这个阶段内,指定agent为Dockerfile生成的镜像。但是,我们需要将reuseNode设为true,否则Jenkins将因为agent的不同开启一个独立的workspace,而我们希望这个阶段能够共享上一prepare阶段,也是默认的workspace。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pipeline {
agent any
stages {
stage('prepare') {
steps {
sh 'git submodule update --init --recursive'
}
}
stage('build') {
agent {
dockerfile {
filename 'Dockerfile'
reuseNode true
}
}
steps {
sh 'npm cache clean --force'
sh 'npm install --verbose'
sh 'hexo clean'
sh 'hexo g'
}
}
}
}

在build这个阶段成功后,在当前的目录下,会有一个public文件夹,这个文件夹里面便是静态的网页文件。我们把这个文件夹下的内容复制到一个专门的文件夹/var/jenkins_home/hexo_generated下,这样方便其他容器或者宿主机访问生成的文件。复制前,会将原来这个文件夹下的旧文件一并删除。为此再在后面加一个阶段deploy

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
pipeline {
agent any
stages {
stage('prepare') {
steps {
sh 'git submodule update --init --recursive'
}
}

stage('build') {
agent {
dockerfile {
filename 'Dockerfile'
reuseNode true
}
}
steps {
sh 'npm cache clean --force'
sh 'npm install --verbose'
sh 'hexo clean'
sh 'hexo g'
}
}

stage('deploy') {
steps {
sh 'rm -rf /var/jenkins_home/hexo_generated'
sh 'cp -r public /var/jenkins_home/hexo_generated'
}
}
}
}

在Jenkins管理界面创建流水线工程

我们现在已经完成了让Jenkins能够按照其正常工作的Jenkinsfile文件,但是Jenkins现在还不知道我们这个git项目的存在呢!可喜的是,Jenkins有一个Blue Ocean插件,它可以帮助我们轻松创建流水线工程。点击Jenkins的系统管理,点击插件管理,在可选插件选项卡里搜索Blue Ocean,选择那个名字就叫Blue Ocean的插件,安装即可。国内的网络环境可能会使得下载安装比较缓慢,请耐心等待。

安装后,可以用docker restart jenkins-runner来重启我们的Jenkins容器,从而重启Jenkins确保插件生效。

首先,登录进入Jenkins的管理页面,左边应该有一个打开Blue Ocean,点击进入BlueOcean界面。

点击右上角的创建流水线按钮。这里会让你选择代码仓库,我们是自己在服务器上搭建的git仓库,所以直接点Git。

现在我们要连接到Git仓库。连接到Git仓库的方法有许多,HTTP,Git协议或SSH都能连接到Git仓库。这里我们懒得配置HTTP协议或Git协议连接了,直接上SSH协议。这里,我们的服务器域名是example.com,hexo仓库的文件放在git用户家目录的hexo.git目录下,因此仓库URL就是ssh://git@example.com/~/hexo.git

当你输入了SSH协议的URL后,Jenkins会提示你把下面的公钥注册到Git服务器上。我们可以复制公钥的内容,将其保存为一个文本文件,这里假设是jenkins.pub。把它上传到你的服务器上,因为我们的git用户使用的shell是git-shell,所以它基本上只能执行git命令,像ssh-copy-id等远程传输密钥的脚本很有可能最后会失败,所以必须在服务器上用别的管理员用户来给它添加SSH公钥。很麻烦,但是显然安全多了,不是吗?现在,如法炮制:

1
2
3
$ PUBKEY=`cat jenkins.pub`
$ sudo -u git sh -c "echo $PUBKEY | cat >> /home/git/.ssh/authorized_keys"
$ unset PUBKEY

jenkins.pub改成你公钥文件的路径。

点击创建流水线按钮,Jenkins将按照你的仓库URL把你的仓库克隆下来,并寻找仓库根目录下的Jenkinsfile文件,如果Jenkinsfile文件没有问题,那么稍等一会,一个Jenkins流水线工程就创建完毕了!

安装nginx

既然用了Docker,那就把用Docker贯彻到底吧。我们把nginx也运行在Docker上。

不过有一点,Docker不支持只把数据卷中的某个子文件夹映射到容器的某个目录,它只支持映射整个数据卷。为此我们需要做点文章,创建一个Dockerfile,将子目录通过链接指向网页存放目录。

Dockerfile文件如下:

1
2
3
FROM nginx

RUN mkdir -p /data/hexo_generated && mv /usr/share/nginx/html /usr/share/nginx/html-backup && ln -sf /data/hexo_generated /usr/share/nginx/html

保存成Dockerfile文件后,创建镜像,名字就叫hexo-nginx吧:

1
$ docker build -t hexo-nginx .

然后启动这个我们自己创建的容器:

1
2
3
4
5
6
7
$ docker container run \
--name hexo-nginx \
--rm \
--detach \
--publish 80:80 \
--volume jenkins-data:/data:ro \
hexo-nginx

我相信你对这些参数配置不陌生了,最后的hexo-nginx就是我们之前生成的镜像。

这个时候,只要打开网页http://example.com,就能看到博客的页面啦!

推送到远程Git仓库后自动执行构建

但是这还不够。我们之后会不断上传源代码到远程Git仓库,而我们希望能够每次上传后就能让Jenkins触发一次构建。应该怎么做呢?

Git有这么一个功能,当它检测到仓库内容有更新,它就可以执行某个脚本。因此,我们将为Git仓库/home/git/hexo.git建立一个在本地推送完成后触发执行的脚本:

1
$ sudo -u git vim /home/git/hexo.git/hooks/post-receive

输入以下内容,这个脚本将会查找Jenkins项目中git地址为ssh://git@example.com/~/hexo.git的项目,然后触发项目对仓库中master分支的构建。

1
2
3
#!/bin/sh

curl http://example.com:8080/git/notifyCommit?url=ssh://git@example.com/~/hexo.git&branches=master

为其添加可执行权限:

1
$ sudo -u git chmod +x /home/git/hexo.git/hooks/post-receive

美满!以后每次推送源代码后,服务器就会自动生成静态文件,并将其发送到nginx服务器对应的目录上,我们再也不用在自己电脑上安装hexo了!现在客户端唯一需要做的可能就只有:安装Git客户端,通过修改package.json来引入新的hexo插件和模块,非常开心地写博客了!