该篇文章记录的是这个博客的整个搭建流程.

Hugo + PaperMod + Drone

目标

Logseq 用来日常记录使用,不对外开放

使用博客作为日常分享以及知识产出的对外平台

第一目标是轻量

其次是自动化

随后是美观

方案选型

简单调研了几个平台:

WordPress:不够轻量,放弃

Typecho:不够轻量,放弃(主要是提供的有后台编辑工具,我用不到,性价比就很低了,并且也就导致不够轻量)

Solo:强制加入社区,导出还需要积分,不是我想要的,放弃

Holo:与上面类似吧,没仔细研究

Hugo:目前使用的,够轻量,没有多余的内容,没有平台,只是静态资源生成工具


可以看到,个人的博客目前是比较小的,暂时是不考虑做的很大,因此轻量是当前的一个重要因素

希望的是所有的数据在自己手里,以写 Markdown 的方式记录博客,Hugo 就刚好满足

关于 Hugo 不做过多介绍,贴一个官网:https://gohugo.io/

关于主题

看了一些之后,考虑了两个:

PaperMod

LoveIt

LoveIt 还是比较丰富的,看起来漂亮一点儿,甚至我当时都考虑在主题上面放弃 “轻量”,但后来发现开源协议是GPL3,也就是版权和许可声明必须保留,放弃了

在此声明一下:刚才又去看了下,看着是 MIT的,不知道是不是我之前看错了,不过也因为此吧,放弃了,正好也感觉有点儿重

PaperMod 整体看着还是很干净简约的,目前我就是要这种风格吧

开发工具

Typora 吧,不需要系统性的

生成新的博客的时候,直接使用 Hugo 命令:hugo new posts/first-blog/index.md

随后用 Typora 打开编写

关于图片

可以看到我上面的命令里面新建文章的时候,用的是 /first-blog/index.md,这种是为了解决引入本地文件的问题

也就是假如我们想直接使用项目中的图片或者文件采用如下方法:

  1. 新建博客时候中间多一层目录,实际的 md 文件放在这个目录中,叫做 index.md,该目录中定义资源目录,如 pics 用于存放图片,files 用来存放可以下载的附件

  2. index.md 中引用到图片或者文件的时候,直接写相对路径

     ![测试照片](pics/test.png)
    

整体的目录结构:

themes
static
...
assets
content:
  posts:
    first-blog:
      files
      pics:
        test.png
      index.md	

参考了该博客:

https://www.yuweihung.com/posts/2021/hugo-blog-picture/

另外,这个博主的风格挺好看的,这个字体我也去查了,附上链接,后续也会改成这个 霞鹜文楷

网站部署

emm,这个这次不打算过多介绍,每个人都有自己的办法,本质上就只是在服务器上部署一个静态目录即可

因为 Hugo 生成的页面是静态的,直接放到服务器上即可

我这边直接采用的是 “宝塔”,直接创建网站即可,https 就去用 letsencrypt,现在这个网站有点儿限制了,不过也还是目前最简单可用的证书

附上链接:

宝塔:

https://www.bt.cn/new/index.html

加密证书:

https://letsencrypt.osfipin.com/

最终,我的博客地址就是:

https://blog.chunxiao.site

自动化 CI

有了上面的网站之后,其实就可以手动发布部署了,操作流程如下:

  1. 本地建文章并编写
  2. 本地 hugo server 查看
  3. 本地执行 hugo 生成静态资源文件,网站根目录的 public 目录下
  4. sftp 工具将生成的静态资源放到服务器网站目录

当然了,能用程序去做的事情,肯定不要自己去做,不然买的服务器闲着不动不就亏死了

提到自动化,肯定第一想到的就是 jenkins,但是最近贯穿整个生活的都是“轻量”,因此,jenkins 首先被排除了

去 Google 搜,多搜几个,就找到了 drone,随后去 github 上找,一看 star 很多,基本就确定了用这个了

发现呢,这个需要用到 docker,虽说 docker 都用过,但一直都是浅尝辄止,仅会 docker run 至于后续的 docker-compose 等根本不会,怎么办?

学呗

于是,去学习了 Docker 的基本使用、基本构建镜像、docker-compose 的基本使用

可以看到,全是基本使用,因为我现在的重心不在这个上面,能用 docker 正常运行并且启动 CI 执行流程即可

Drone

还是简单介绍一下这个吧,不然没有实际内容了

Drone 官方网站

GitHub 地址

Drone is a self-service Continuous Integration platform for busy development teams.

【Drone 是为繁忙的开发团队提供的自助式持续集成平台。】

可以看到,就是简单好用

截几张官网特性图

image-20230502130222799

image-20230502130334395

image-20230502130534045

image-20230502130544754

image-20230502130605077

总结一下

  1. 流水线都是配置出来的,配置文件还是 .yml 的,易懂易学习
  2. 基于 docker,不会有什么脏数据,用完即销毁
  3. 后台页面运行起来很快,轻量(自己使用感受)

本人使用场景

image-20230502143953002

嗯对,就是这么个流程

一些基本的使用我这里不多写,可以参考一些官方的或者其他博客的,我给个路标:

https://yeasy.gitbook.io/docker_practice/ci/drone

我只解释一些关键点

Drone 服务

Drone 需要有台服务器去运行它,所以我们要有一台可以运行的服务器,如上图就是 drone 服务器

运行的方式可以是 docker,使用官方的就行,对照我的解释一下:

version: '3'

services:

  drone-server:
    image: drone/drone:2.3.1
    ports:
      - "8088:80"
    volumes:
      - drone-data:/data:rw
      - ./ssl:/etc/certs
    restart: always
    command:
      - git config --global core.autocrlf input
    environment:
      - DRONE_SERVER_HOST=${DRONE_SERVER_HOST:-https://drone.yeasy.com}
      - DRONE_SERVER_PROTO=${DRONE_SERVER_PROTO:-https}
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET:-secret}
      - DRONE_GITHUB_SERVER=https://github.com
      - DRONE_GITHUB_CLIENT_ID=${DRONE_GITHUB_CLIENT_ID}
      - DRONE_GITHUB_CLIENT_SECRET=${DRONE_GITHUB_CLIENT_SECRET}
      - DRONE_USER_CREATE=username:xiaohanqing,admin:true

  drone-agent:
    image: drone/drone-runner-docker:1
    restart: always
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:rw
    command:
      - git config --global core.autocrlf input
    environment:
      - DRONE_RPC_PROTO=http
      - DRONE_RPC_HOST=drone-server
      - DRONE_RPC_SECRET=${DRONE_RPC_SECRET:-secret}
      - DRONE_RUNNER_NAME=${HOSTNAME:-demo}
      - DRONE_RUNNER_CAPACITY=2
    dns: 114.114.114.114

volumes:
  drone-data:

首先,这是一个 docker-compose 的配置文件

里面定义了两个 docker服务,也就是这个启动之后,会启动两个 docker

分别是 drone-serverdrone-agent,看名字也能看出来,大概就是 Drone 的一个主服务,一个守护服务

image 没什么好说的,docker 镜像

ports:端口映射,上面意思是把 docker 中的 80 端口映射到我主机的 8088 端口

volumes:目录映射,上面意思就是把 docker 中的 /data 目录映射到主机上的 ./drone-data,后面跟了个 rw 意思就是读写权限都有

command:指的是跟着执行的命令,后面解释为什么要加个这一行,官方上面是没有的

environment:就是一些环境变量,照着来就行了,反正数据都有。哦对了,这里面有一行叫这个:

- DRONE_USER_CREATE=username:xiaohanqing,admin:true

这个是要自己加上的,username 后面的值 xiaohanqing 是我的 github 账号,改成自己的,运行起来之后 drone 后台管理页面需要用 github 授权,这样配置之后,登录到后台之后,自己就是管理员,可以进行一些操作。不然的话,有些功能受限

最后启动时候,就是 docker-compose up -d 后台启动,启动就会把上面两个 docker 运行起来

可以通过 docker ps 查看运行中的容器

到这一步的时候,后台页面就可以打开了,安全期间,这里就不暴露我的域名了,反正用 NG 反代之后,大概就是

drone.xxx.xxx

页面长这样:

image-20230502145656686

点 Continue,就会让去 github 授权,授权之后就好使了

image-20230502145810341

可以看到我上面已经部署了 75 次了,有成功有失败,就是各种尝试

配置文件

kind: pipeline
type: docker
name: blog-publish

steps:

  - name: build
    image: hugo-base:alpine-v1.2
    volumes:
      - name: blog
        path: /data
    commands:
      - hugo
      - cp -r public/* /data

  - name: scp
    image: appleboy/drone-scp
    volumes:
      - name: blog
        path: /blog.chunxiao.site
    settings:
      host: xxx.xxx.xxx.xxx
      username: drone-scp
      password: aaaaaa
      port: 22
      target: /www/wwwroot
      source: /blog.chunxiao.site
      overwrite: false
      debug: true



volumes:
  - name: blog
    host:
      path: /data/blogs_host

kind:照着写就行,意思是流水线类型

type:意思就是用 docker,drone 支持的有其他的

name:起个名

steps:流水线的每一步

volumes:目录映射

​ name:起个名字,这个名字就是一个标识,可以在 step 中使用

​ host.path:指的是这个名字在主机中的目录

step 中:

​ name:起个名

​ image:选个镜像(hugo-base:alpine-v1.2 大家不要用啊,这是我本地自己整的)

​ volumes:

​ name:对应上面说的最外层的那个 volumes 中的 name

​ path:docker 中的目录

​ commands:可以执行的命令

​ settings:镜像提供的一些配置,用到哪个镜像去找对应的文档

几个注意点:

  1. 这个是基于 docker 的,流水线中的每一步都是一个 docker,因此,我们需要用到 volumes 来把每一步中产生的数据存起来,也就是第二步中是没有任何第一步的数据的,除非使用 volumes 做映射。比如 A(第一个 step) 中 /data 目录映射到主机的 /data/common 中,A运行过程中产生的一些数据就可以放到 /data 中,同时主机的 /data/common 中也就有了这些数据,这时候 B(第二个 step) 中也配置了 /data 映射到主机的 /data/common,那么 B 在启动一开始 /data 中就有 A 的产出数据
  2. 配置的 step 中不用写拉取 git 的操作,实际上在自定义的 step 执行之前,drone 会先进行一次拉取操作
  3. 当前目录就是 git 项目的根目录

解释一下两个 step

build流程

目的就是让 hugo 生成静态资源,因此需要一个带有 hugo 应用的镜像

之前一直有问题,于是考虑自己弄一个(其实后来找到根本原因后发现跟这个没关系)

既然要弄,就得搞一个轻量的,于是去搜了下轻量相关的 docker,发现排的靠前的有这个:alpine(https://hub.docker.com/_/alpine

并且刚好发现,alpine 这个系统也可以直接软件库中按照 hugo,于是自己基于 alpine 整了个 hugo-base 镜像

DockerFile 贴上:

FROM alpine
RUN apk update && \
    apk add hugo && \
    hugo version

随后使用 docker 构建命令生成自己的本地镜像:docker build -t hugo-base:alpine-v1.2 .

这个建议直接用 docker hub 里面别人做好的就行,不建议自己搞一个,我是之前遇到的一些问题,我怀疑跟 hugo 版本或者什么有关,才自己搞的,最后发现其实没关系的

可以看到, volumes 里面配置了: docker 中的 /data 映射到主机上的 /data/blogs-host

随后执行了两个命令:

  1. 执行 hugo 生成静态资源
  2. 把静态资源复制到 /data 目录,也就是主机的 /data/blogs_host 目录

注意:

git 忽略文件中记得把 public 目录忽略

scp流程

由于我这个 drone 服务器与博客服务器不在一块儿(没办法,服务器多)

因此需要再多一个 scp 的操作,把生成的静态资源部署到博客所在的服务器目录中

参考 drone 官方文档:https://plugins.drone.io/plugins/scp

注意点:

需要用到 ssh 登录远程机器,所以需要提供远程服务器的账号密码,这个还是比较危险的,并且这个还需要放到 github 上,切记把项目改成私有的,但是为了安全期间,还是要做一层基本的拦截。因此这里一定不要使用 root 账号,单独建一个账号,给这个账号有限的权限

关于目录的问题,整了很久,都没有达到我要的效果,比如我是想把 ./* 放到远程的 /www/wwwroot/blog.chunxiao.site/下,但是怎么搞都不行,都会多一层目录,尝试用文档是的 tar_exec 参数,但并没有效果。后来实在没办法了,就搞成了上面那种方式,大家可以试一试,有更好的方式可以留言告知我一下,谢谢(当然了,现在还没有接入评论系统,晚些天接入)

这个很基本啊,并且肯定是有安全问题的,正常应该是去做一些脚本处理逻辑的

并且使用了几次后就发现问题了,这个时间太长,因为是全量的,所以不符合要求,正在看 rsync 的,rsync 的好了之后就会放弃这种方式

遇到问题整理

其实挺多的,后来逐渐找到了根本原因,总结之后是两个

发布到服务器上后样式丢失

本地没问题,本地执行 hugo 后直接手动上传到博客服务器网站目录中没问题,但是走了 drone 自动化就没样式了

F12 看了之后发现报错:

Failed to Find a Valid Digest in the 'Integrity' Attribute

就这种类似错误吧,是加载一个 css 时候异常,导致样式没了

当时也找了好久不知道啥原因,一直以为是服务器上的配置问题,毕竟所有的都是新的

后来实在不行了,去找了前端大哥(感谢林叔儿),果然是大哥,没一会儿让我去掉一个东西试试,发现去掉就好使;后来又帮我找到了两篇相关内容:

https://blog.51cto.com/MyIO/5096208

https://yqc.im/hugo-failed-to-find-a-valid-digest-in-the-integrity-attribute/

终于弄明白是怎么一回事了:

应该是 hugo 或者 paperMod 使用到了一个 Blazor的工具,这个工具呢会做这样一件事:

会检查文件的SHA-256哈希值,确保不会出现加载不一致文件的风险

但是,git 在 windows 下会自动将文件中的换行符 CRLF 转成 LF ,这样就造成文件的哈希值变化,就被拦住了

所以,这个问题的原因就找到了,解决方案基本也确定了,就是统一这个换行符

统一换行符

我这样做了之后是好使的:

  1. 清空博客服务器中网站的内容(记得先做备份)

  2. 本地 git 配置(windows):

    git config --global core.autocrlf true
    

    意思是:提交时自动地把行结束符 CRLF 转换成 LF ,而在签出代码时把 LF 转换成 CRLF

  3. drone 服务器上(做拉取操作的机器,Linux)配置:[开始时候的配置就是干这个用的]

    git config --global core.autocrlf input
    

    意思是:在提交时把 CRLF 转换成 LF,签出时不转换

  4. 重新提交一次代码,push,执行上面的流水线任务

Hugo 生成的文件少

遇到过几次,之前因为一直有上面的那个大问题,没怎么解决这个。但上面问题解决之后,发现我在服务器上执行 hugo 后,生成的文件比较少,观察了之后发现是没有生成 HTML

后来无意中取看了下 themes/PaperMod 目录,发现是空目录,才发现,原来在服务器上 git 下来的数据中没有主题信息。

原因是:之前主题的时候使用的是这种 git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod

这种就导致单独的这个 PaperMod 目录也是一个 git 项目,需要单独去克隆拉取出来(应该不太准确,但是肯定是跟 Git 有关),放弃这种方式,让这个主题就是我们项目中的一些文件就行了

最新变动

以上流程用了几次之后就发现了问题,使用 scp 的话每次需要的时间太长了

所以就考虑放弃之前那台服务器,博客也放在了这台drone服务器上,目前原域名是重定向过来的,随后就可以直接打到这台机器了

最新配置文件

Drone 服务器与博客服务器合成一个的话,就简单多了,贴上最新的配置:

kind: pipeline
type: docker
name: blog-publish

steps:

  - name: build
    image: hugo-base:alpine-rsync
    volumes:
      - name: blog
        path: /data
    commands:
      - hugo
      - rsync -a --delete --exclude '.user.ini' public/ /data

volumes:
  - name: blog
    host:
      path: /www/wwwroot/blog.x-r.cc

当然了,那个 hugo-base:alpine-rsync 是我自己本地打的镜像,就是 alpine 安装了 hugo 和 rsync 的

hugo-base                   alpine-rsync   afcf7f89ce5e   2 hours ago     76MB

可以看到,还是有点儿大的,不过也还能接受,比官方提供的 hugo 镜像小多了,下面是官方的

plugins/hugo                latest         5343f140bc4b   4 months ago    119MB

写在最后

写的还是有点多的

最后列举出来一些 Todo 项,后续完成一个勾一个

Bye ~