1. 背景

之前也尝试过Github Action相关的东西进行语雀和博客的继续集成,可是它会依靠语雀Webhook、函数核算服务、Github Action等,其间一个出问题,那么整个流程就不可用。因此在“尽量减少外部依靠”的思路下,除了有必要的Hexo库房和语雀,从头在Kubernetes集群上构建了基于Elog Hexo博客的继续集成流程。

你需求做的前期准备工作是:

  1. 一个原始hexo git库房
  2. 一个安装了Ingress、Cert Manager的K8s集群和K8s操作经历;没有的话能够参阅前面的文章搭建一套云上云下的轻量K3s集群
  3. 喝一杯咖啡的时间

2. 流程介绍

语雀写作,Kubernetes布置——Elog+Hexo博客继续集成

整个博客自动化的流程如上图所示:

  1. Kubernetes集群中的elog-yuque-syner通过Elog定时获取是否有内容更新
  2. 当在语雀等平台完结内容写作,内容发生变化后,syner中的elog感知到之后建议blog Pod资源的删去
  3. Kubernetes集群中的Deployment控制器就会拉起一个新的Pod
  4. 新的Pod首先会从Git拉取基础的Hexo站点装备
  5. Pod中的Elog东西会拉取语雀中更新过后的博客内容
  6. Hexo生成网站内容,Caddy发动,博客从头布置完结

3. 操作方法

3.1. 密钥装备

从本地SSH私钥文件创建Secrets, 需求确认这个私钥对应的公钥已经在git库房中装备过

kubectl crete ns blog
kubectl create - n blog secret generic private-key --from-file=id_rsa=<SSH私钥文件途径>

3.2. 布置博客

按如下blog.yaml文件修正对应装备后,布置Hexo博客相关的资源至集群中

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发动时会默认执行一次博客重建,能够用来验证任务是否能够执行

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是否正常删去

hexo kubectl -n blog logs -l app=elog-yuque-syncer
# 正常回来如: pod "hexo-caddy-66956dcbfd-ppdpc" deleted

4. 附录

东西镜像Dockerfile如下

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