1. 背景
之前也尝试过Github Action相关的工具进行语雀和博客的持续集成,但是它会依赖语雀Webhook、函数计算服务、Github Action等,其中一个出问题,那么整个流程就不可用。因此在"尽量减少外部依赖"的思路下,除了必须的Hexo仓库和语雀,重新在Kubernetes集群上构建了基于Elog+Hexo博客的持续集成流程。
你需要做的前期准备工作是:
- 一个原始hexo git仓库
- 一个安装了Ingress、Cert Manager的K8s集群和K8s操作经验;没有的话可以参考前面的文章搭建一套云上云下的轻量K3s集群
- 喝一杯咖啡的时间
2. 流程介绍
整个博客自动化的流程如上图所示:
- Kubernetes集群中的elog-yuque-syner通过Elog定时获取是否有内容更新
- 当在语雀等平台完成内容写作,内容发生变动后,syner中的elog感知到之后发起blog Pod资源的删除
- Kubernetes集群中的Deployment控制器就会拉起一个新的Pod
- 新的Pod首先会从Git拉取基础的Hexo站点配置
- Pod中的Elog工具会拉取语雀中更新过后的博客内容
- Hexo生成网站内容,Caddy启动,博客重新部署完成
3. 操作方法
3.1. 密钥配置
从本地SSH私钥文件创建Secrets, 需要确认这个私钥对应的公钥已经在git仓库中配置过
typescript
kubectl crete ns blog
kubectl create - n blog secret generic private-key --from-file=id_rsa=<SSH私钥文件路径>
3.2. 部署博客
按如下blog.yaml文件修改对应配置后,部署Hexo博客相关的资源至集群中
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: init-script
namespace: blog
data:
# elog相关配置来源请参考地址 https://elog.1874.cool/notion/write-platform
elog.env: |
YUQUE_USERNAME=<语雀用户名>
YUQUE_PASSWORD=<语雀密码>
YUQUE_LOGIN=<账号登陆名>
YUQUE_REPO=<仓库ID>
elog.config.js: |
module.exports = {
write: {
platform: 'yuque-pwd',
'yuque-pwd': {
username: process.env.YUQUE_USERNAME,
password: process.env.YUQUE_PASSWORD,
login: process.env.YUQUE_LOGIN,
repo: process.env.YUQUE_REPO,
onlyPublic: false,
onlyPublished: true,
linebreak: false,
},
},
deploy: {
platform: 'local',
local: {
outputDir: './source/_posts',
filename: 'title',
format: 'matter-markdown',
catalog: false,
formatExt: '',
},
},
image: {
enable: false
},
}
init.sh: |
#!/bin/sh
mkdir -p /root/.ssh/
cp /etc/ssh-key/id_rsa /root/.ssh/
chmod 600 /root/.ssh/id_rsa
cd /tmp/
ssh-keyscan -p 22 github.com >> ~/.ssh/known_hosts
# 这里需要修改为你自己的git仓库地址和文件夹名
git clone git@github.com:demo/site.git
cd ./site
npm --registry https://registry.npmmirror.com install
rm -rf source/_posts/*
cp /tmp/script/elog.config.js ./
elog sync -e /tmp/script/elog.env
hexo generate
cp -R ./public/* /tmp/public/
yuque-sync.sh: |
#!/bin/sh
cd /cache
cp /tmp/script/elog.config.js ./
while true; do
result=$(elog sync -e /tmp/script/elog.env)
echo $result
if echo "$result" | grep -q "没有需要同步的文档"; then
sleep 15
else
kubectl --token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) delete pods -l app=hexo-caddy
sleep 5m
fi
done
---
apiVersion: v1
kind: ConfigMap
metadata:
name: caddy-config
namespace: blog
data:
Caddyfile: |
:80 {
root * /var/www/html
file_server
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: hexo-caddy
namespace: blog
spec:
selector:
matchLabels:
app: hexo-caddy
replicas: 1
template:
metadata:
labels:
app: hexo-caddy
spec:
initContainers:
- name: init
image: registry.cn-hangzhou.aliyuncs.com/custom-toolkit/toolkit:hexo-git-openssh-elog
imagePullPolicy: Always
command: ["sh"]
args: ["/tmp/script/init.sh"]
volumeMounts:
- name: private-key
mountPath: /etc/ssh-key
readOnly: true
- name: script-vol
mountPath: /tmp/script
- name: public-vol
mountPath: /tmp/public
containers:
- name: caddy
image: caddy:2.4.0-alpine
command: ["caddy"]
args: ["run", "--config", "/etc/caddy/Caddyfile"]
volumeMounts:
- name: public-vol
mountPath: /var/www/html
- name: caddy-vol
mountPath: /etc/caddy/
ports:
- containerPort: 80
volumes:
- name: private-key
secret:
secretName: private-key
- name: public-vol
emptyDir: {}
- name: script-vol
configMap:
name: init-script
- name: caddy-vol
configMap:
name: caddy-config
---
apiVersion: v1
kind: Service
metadata:
labels:
app: hexo-caddy
name: hexo-caddy-service
namespace: blog
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
app: hexo-caddy
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hexo-caddy
namespace: blog
annotations:
# Traefik Ingress中间件配置,将HTTP重定向到为HTTPS
traefik.ingress.kubernetes.io/router.middlewares: default-redirectscheme-https@kubernetescrd
# Cert Manager中的issuer按需修改
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- secretName: hexo-blog-cert
hosts:
- <你的博客域名>
rules:
- host: <你的博客域名>
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: hexo-caddy-service
port:
number: 80
总结下上面配置中的注意点,必须修改的包括:
- 语雀账号和仓库配置
- Hexo博客仓库地址和文件夹名
- 博客域名
可能需要修改的包括:
- 集群内的Ingress配置,用于重定向
- 集群内的Cert Manager Issuer,用于为你的博客域名自动签发证书
3.3. 部署定时同步任务
等待第二步中的博客部署成功后,按如下elog-yuque-syncer.yaml文件部署定时同步专用的Pod。Pod启动时会默认执行一次博客重建,可以用来验证任务是否能够执行
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-operator-sa
namespace: blog
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-operator-role
namespace: blog
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: authorization-role-binding
namespace: blog
subjects:
- kind: ServiceAccount
name: pod-operator-sa
roleRef:
kind: Role
name: pod-operator-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: elog-yuque-syncer
namespace: blog
spec:
selector:
matchLabels:
app: elog-yuque-syncer
replicas: 1
template:
metadata:
labels:
app: elog-yuque-syncer
spec:
serviceAccountName: pod-operator-sa
containers:
- name: syncer
image: registry.cn-hangzhou.aliyuncs.com/custom-toolkit/toolkit:hexo-git-openssh-elog
imagePullPolicy: Always
command: ["sh"]
args: ["/tmp/script/yuque-sync.sh"]
volumeMounts:
- name: script-vol
mountPath: /tmp/script
- name: cache-vol
mountPath: /cache
volumes:
- name: cache-vol
emptyDir: {}
- name: script-vol
configMap:
name: init-script
部署完毕后,执行以下命令观察博客Pod是否正常删除
ini
hexo kubectl -n blog logs -l app=elog-yuque-syncer
# 正常返回如: pod "hexo-caddy-66956dcbfd-ppdpc" deleted
4. 附录
工具镜像Dockerfile如下
bash
FROM --platform=linux/amd64 alpine@sha256:48d9183eb12a05c99bcc0bf44a003607b8e941e1d4f41f9ad12bdcc4b5672f86
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
apk add --no-cache curl git openssh nodejs npm && \
node -v && npm -v
RUN npm install -g hexo-cli @elog/cli@0.12.2 && \
rm -rf /var/cache/apk/*
RUN wget https://dl.k8s.io/release/v1.28.4/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \
chmod a+x /usr/local/bin/kubectl