使用 Jenkins+Docker 进一步提高 Hexo 博客部署效率

使用 Jenkins+Docker 进一步提高 Hexo 博客部署效率

部署方式的转变

方式一:使用 GitHub Pages 服务 + 自定义域名访问

每次更新的步骤是:

hexo g 👉 hexo d 👉 漫长的等待 👉 通过域名访问 👉 漫长的加载过程 …

优点:

  • 步骤少,更新方便。

缺点:

  • 访问速度太慢了
  • 如果更换机器迁移十分麻烦

方式二:不使用 GitHub Pages,在 ECS 部署运行

笔记:Linux 下搭建 Hexo 博客环境
笔记:Linux 下部署个人站点后的运行问题

需要做的迁移工作:

  1. 先在服务器配置好 nodegithexo 环境。
  2. 把本地的博客根目录上传到服务器
  3. 在服务器执行 hexo d && hexo s 运行
  4. 开放 4000 端口,配置域名解析。
  5. 配置 nginx,将 yuzh.xyz 映射到 http://ip:4000

每次更新的只需要上传文章(*.md)到服务器的 hexo/source/_post/ 下即可,无需重启,但不可避免有时候需要重新启动加载资源文件。

这种方式我觉得前期配置过于繁琐,不能达到「开箱即用」的效果。

方式三:使用 Docker + 容器镜像服务 + ECS 部署运行

笔记:Hexo 迁移至 Docker

迁移工作:

  1. 「服务器」安装好 Docker
  2. 「本地」编写好 Dockerfile
  3. 「本地」构建自己的 hexo 博客镜像,上传到容器镜像服务
  4. 「服务器」 docker pull 步骤 3 构建的镜像
  5. 「服务器」启动 nginx 容器,配置好映射关系(容器到宿主机端口映射,容器内的 nginx 配置)

以上步骤只需执行一次,每次的更新部署步骤:

  1. 本地 hexo 打一个压缩包
  2. scp 上传包到服务器
  3. 在服务器删除原来的 hexo 根目录,解压步骤 2 上传的最新文件
  4. 执行 docker 命令:docker run -p 4000:4000 -v [宿主机的博客根目录]:[容器内的博客根目录] -d 容器ID

这种方式比上面两种已经好很多了,hexo 博客nginx 已经构建成一个 docker 镜像,放到哪里都能用。
每次更新只需要打一个包上传到服务器,然后替换原来的数据卷目录就能访问最新内容。如果需要重启,只需要重新启动 hexo 容器即可。

总结以上几种方式

都并非完全解放双手,都需要经历 上传包、访问服务器、启动 的步骤。

那么有没有一种好的方式可以做到:我只想写好博客内容,服务器自动拉取最新的内容并自动部署运行呢?

还真有,那就是使用持续集成(CI),使用持续集成可以做到软件应用的自动化发布、测试、部署的,使得开发人员可只专注于功能实现不用兼顾繁琐的测试运维工作。

持续集成功能很强大,用做博客自动化发布部署不免有点大材小用,不过正好有这样的需求可以接触持续集成不是更好吗?🤓

使用 Git + Docker + Jenkins + ECS 部署运行

变更仓库

基于 Github Page 服务部署的时候,我们通过 hexo d 部署到 github 仓库中的只是编译后的文件,即:hexo/public 目录下的全部内容。

而文章和主题等资源都是在本地的 hexo/source 中,当更换电脑之后,仅仅通过克隆仓库中的内容是不能够完成博客环境的迁移的。

所以这次摒弃 github 仓库作为博客运行的代理服务器身份,另外创建一个 gitee 仓库,将 hexo 整个目录作为整个仓库。至于为什么使用 gitee,当然是因为快啊!试想每次拉取最新博客内容都这么慢还做什么持续集成?

总的来说更换仓库目的是:

git 库(GitHub Pages)不再作为博客部署代理服务器,而是用做 CICD 的代码来源,实际部署运行是在我自己的服务器上。

  1. 由于项目是直接从原来的 hexo 根目录拷贝过来的,需要删除原来的与 git 有关的所有文件,以便与自己重新生成一个 gitee 仓库。
  2. hexo/public/themes 中是定制的主题,这种主题文件一般都会对应一个 git 仓库,需要将里面的 git 相关文件也删除。因为一个 git 库不能包含另外一个 git 库。
  3. 后续如果需要更新主题,将主题拷贝进来配置成功之后再删除 git 相关文件。
  4. 不要删除 node_modules 文件夹,少了它博客容器不能执行 hexo 命令!!

安装 Jenkins 容器

文档中心:http://jenkins.io/zh/doc/

1
2
3
4
5
6
7
8
docker run \
--rm \
-u root \
-p 8080:8080 \
-v jenkins-data:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$HOME":/home \
jenkinsci/blueocean

不想做搬运工,只记录填坑过程。这里有几个参数回顾一下:

  • --rm:容器退出之后自动删除
  • -u root:以 root 身份运行 jenkins 容器
  • -v /var/run/docker.sock:/var/run/docker.sock:让容器内可以执行宿主机的 docker 命令

PS: 容器中的 /var/jenkins_home 挂载在名为 jenkins-data 的数据卷上,该数据卷在宿主机的位置是:/var/lib/docker/volumes/jenkins-data/_data 记住这个位置待会要用。

启动之后的大概步骤:

  1. 输入密码解锁 jenkins(在启动时会打印在控制台,获取查看 jenkins 配置文件可以看到)
  2. 安装社区推荐插件
  3. 创建用户

Jenkins For Gitee 插件实现自动化部署运行

文档:https://gitee.com/help/articles/4193

文档写的很清楚,好像不需要我写什么了耶🤪

主要步骤:

  • 在 jenkins 安装 gitee 插件
  • 插件配置:添加码云链接配置
  • 插件配置:添加码云APIV5私人令牌
  • 新建 jenkins 构建任务(指定一个自由风格的任务)
  • 指定代码仓库
  • 编写构建脚本
  • 指定构建触发器
  • 配置 WebHook
  • 配置构建后步骤(非必须)
  • 测试 WebHook(代码 push 自动触发 jenkins 构建)

下面是构建脚本的内容,构建脚本是指定 Jenkins 把你的代码拉下来之后要执行的动作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 先删除更新之前的 hexo 博客容器
result=$(docker ps | grep blog | awk '{print $1}')
if [ -n "$result" ]; # -n:变量不存在 / -z:变量存在
then
# 如果已有博客在容器运行,先删掉再重新启动。
docker kill $(docker ps | grep blog | awk '{print $1}')
fi

# 再启动新的容器
docker run \
-d \
-p 4000:4000 \
-v /var/lib/docker/volumes/jenkins-data/_data/workspace/harrys-blog:/harrys-blog \
registry.cn-hangzhou.aliyuncs.com/yuzh/harrys-blog:latest

我们的 gitee 库(也就是博客根目录)在宿主机的位置是:/var/lib/docker/volumes/jenkins-data/_data/workspace/harrys-blog

配置之后每次我们更新博客时只需要将文章放在 gitee 库中,执行 push 操作。
gitee 检查到有代码更新后通过钩子程序发送一个 POST 请求给我的 Jenkins 服务器,接下来的工作就交给 Jenkins 去处理了。

测试钩子

构建记录

构建成功

以上方式是完全基于图形化界面的,如果需要搭建多个 Jenkins 环境难道得每次都重复这么多的配置步骤吗?
留个坑,使用 Jenkins 的构建脚本语言 Pipeline 搭建构建环境

基于容器的 Jenkins 真的比直接安装的 Jenkins 好吗?

官方文档的样例大多数都是基于 Docker 运行的 Jenkins,这不免引起我的疑问。基于容器的 Jenkins 真的比直接安装的 Jenkins 好吗?

如果我要自动化构建运行一个 Spring Boot 带后台接口测试页面的应用(需要 JDK + Maven + Node 环境),并且在宿主机通过 jar 包运行,在 Jenkins 容器中如何做到?😤

总结网上资源后的理解:

其实 Jenkins 装在哪不是很重要,Jenkins 本来就是个依赖很重的应用。不过对于每天需要操作几十几百台服务器的运维工程师来说,基于容器的 Jenkins 我可以拿来就用,不需要在每个机器做重复的配置工作了。

如果仅仅是简单的单体应用需要做持续集成,直接在服务器本机搭建一个 Jenkins 环境即可,启动一个 jenkins.war 就能用。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×