利用 GitLab 实现 Hexo 博客的 CI/CD

CI 与 CD

在介绍我的骚操作之前,我先聊聊对 CI/CD 的理解。

CI 为 Continuous Integration 的缩写,中文意思为持续集成,可以理解为代码变动提交后,自动执行代码编译、代码打包、代码测试的这么一个流程。

CD 对应多个英文名称:Continuous Deployment(持续部署) 和 Continuous Delivery(持续交付)。先说说持续部署,对于一个成熟的 CI/CD 的流程而言,代码变更经过编译、打包、测试之后的下一步就是部署环节。

而持续交付一般是指,研发尽快地向客户交付,比如尽快实现功能上线。通过设计完善的 CI/CD 流程,一般可以实现持续交付的目标。

PS:对于上面提到的「持续」,可以理解为每完成一个完整的部分,就向下一个环节交付。

CI/CD 方案

早期部署方式

Hexo 的部署方式「千千万」,但套路基本可以归整为以下套路:

  1. 本地写博客
  2. 本地生成静态文件
  3. 「复制」静态文件到 VPS 或者 GitHub Page 仓库

个人不是很喜欢把博客仓库暴露出去,所以早期我是在 GitLab 建了私人仓库存放原始文件,本地更新博客之后,用 rsync 的方式把新的静态文件传到个人 VPS 上。

在我自建了私有集群之后,因为 VPS 比较多,服务容器在各 VPS 上飘来飘去,这种部署方式就不适用了。

实现方式

在之前的文章 手把手教你玩转 Docker(二) 中,有提到我目前的 Docker 集群方案。其中两个点比较关键:一是所有的服务都以容器形式运行,一是使用 Portainer 管理 Docker 集群。

因为 Portainer 提供了对服务在线更新的 WebHook,所以基于 GitLab 自带的 CI/CD 功能实现 Hexo 博客的持续部署,就非常轻松了。

如上所述,CI/CD 其实本质上是一套流程,流程规则可以自行定义。在本文研究的主题下,流程分为三步:第一步是编译 Hexo 博客并生成静态文件;第二步是将静态文件打包成可提供 Web 服务的镜像;第三步则是通过 Portainer 的钩子触发服务更新。

配置文件

为了减少编译打包时间,第一步中尽量使用体积小的镜像。为了减少 VPS 的资源消耗,第二步中也应如此,所以我这边使用了 Nginx 的 Alpine 系列镜像。

好了,废话不多说,直接上 .gitlab-ci.yml 的配置内容吧。

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
stages:
- compile
- build
- deploy

# CONTAINER_RELEASE_IMAGE 根据自己的仓库名修改
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_DRIVER: overlay2
CONTAINER_RELEASE_IMAGE: registry.gitlab.com/xxx/xxx:latest

compile:
stage: compile
image: node:lts-alpine
script:
- npm install
- ./node_modules/hexo/bin/hexo generate
artifacts:
paths:
- public/
expire_in: 20 minutes

build:
stage: build
image: docker:stable
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
- docker info
- docker build -f ./Dockerfile -t $CONTAINER_RELEASE_IMAGE public
- docker push $CONTAINER_RELEASE_IMAGE

# 根据自己的钩子修改下方的 URL
deploy:
stage: deploy
script:
- curl https://xxx.xxx.xxx/api/webhooks/xxx -X POST

为了保证服务更新的成功率,可以在 deploy 环节加个错误判断和重试次数,具体的看 GitLab 官方文档即可。

下面给出我的 Dockerfile 文件,供参考。

1
2
3
4
5
6
FROM nginx:alpine

COPY . /usr/share/nginx/html

RUN chmod 777 /usr/share/nginx/html -R \
&& sed -i 's/#error_page 404/error_page 404/' /etc/nginx/conf.d/default.conf