语雀写作,Kubernetes部署——Elog+Hexo博客持续集成

1. 背景

之前也尝试过Github Action相关的工具进行语雀和博客的持续集成,但是它会依赖语雀Webhook、函数计算服务、Github Action等,其中一个出问题,那么整个流程就不可用。因此在"尽量减少外部依赖"的思路下,除了必须的Hexo仓库和语雀,重新在Kubernetes集群上构建了基于Elog+Hexo博客的持续集成流程。

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

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

2. 流程介绍

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

  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仓库中配置过

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
相关推荐
景天科技苑10 小时前
【云原生开发】K8S多集群资源管理平台架构设计
云原生·容器·kubernetes·k8s·云原生开发·k8s管理系统
wclass-zhengge11 小时前
K8S篇(基本介绍)
云原生·容器·kubernetes
颜淡慕潇11 小时前
【K8S问题系列 |1 】Kubernetes 中 NodePort 类型的 Service 无法访问【已解决】
后端·云原生·容器·kubernetes·问题解决
昌sit!19 小时前
K8S node节点没有相应的pod镜像运行故障处理办法
云原生·容器·kubernetes
A ?Charis1 天前
Gitlab-runner running on Kubernetes - hostAliases
容器·kubernetes·gitlab
北漂IT民工_程序员_ZG1 天前
k8s集群安装(minikube)
云原生·容器·kubernetes
2301_806131361 天前
Kubernetes的基本构建块和最小可调度单元pod-0
云原生·容器·kubernetes
SilentCodeY1 天前
containerd配置私有仓库registry
容器·kubernetes·containerd·镜像·crictl
Huazie1 天前
一篇搞定 Hexo Diversity 主题接入!支持多主题自由切换!
javascript·github·hexo
binqian1 天前
【k8s】ClusterIP能http访问,但是不能ping 的原因
http·容器·kubernetes