最近看上了一个Hexo博客的主题,就打算本人也开一个博客。惊奇的发现 github 上竟然曾经有了 CI 性能(是我火星了)。于是乎,一个通过 GitHub Actions 继续部署博客到 Kubernetes 中的想法就呈现了。通过这种形式,咱们能够实现 0 停机工夫更新 Hexo、弹性扩缩、健康检查和故障转移等等。。。不过其实都没啥意义,一个博客而已,跑在 1c1g 的机器上,没必要引入这种重型工具。

但这架不住我闲得慌,也就有了当初这一篇记录。本文根本是纯记录,对于很多事件不会有太多介绍和解释。

事先筹备

  • 一个 Github 账号
  • 一个曾经配置好的 hexo 博客。这方面教程太多了,不赘述了
  • 一个云主机,配置无所谓
  • 肯定的 Linux 操作常识
  • 经得住折腾

部署Kubernetes?还是K3s吧

最初我还是没有应用 Kubernetes,而是转为应用了K3s。起因很简略,Kubernetes 他切实是太大了,光是一个 Etcd 就够我这个可怜的 1c1g 主机喝一壶了。尽管 K8s(Kubernetes)可能是不能用了,但咱们还有 K3s 啊。

啥是K3s

K3s是 Rancher Lab 在 18 年 7 月发行的一个基于 K8s 的容器编排工具。尽管在应用上和 K8s 简直截然不同,然而为了缩小资源的应用,k3s 删除了很多云服务相干的插件( Cloud Provider)和存储插件。同时,k3s 应用 sqlite 作为默认的数据库,这能够进一步缩小 k3s 的资源使用率,对于单机部署来说天然是极好的。当然,k3s 也反对配置 etcd 以及其余数据库,如 mysql、postgresql。

就在这几天(2020 年 1 月 13 号),k3s 正式 release 了 1.0 版本。这意味着 K3S 曾经能够失常的应用了。所以我也是第一工夫的在本人的云主机上部署了一套来玩。不过值得一提的是,k3s 中的容器并非是应用 docker 来运行的,而是应用了containderd,这个工具通过crictl交互,子命令和 docker 都差不多。如果想要应用 docker 作为 container runtime,须要在启动参数中指定。

在服务器中装置K3s

脚本装置

通常状况下,咱们只须要运行一行curl -sfL https://get.k3s.io | sh -就能够实现 K3s 的装置了。

手动装置K3S

如果咱们可怜遇到了一些网络问题,无奈顺利的下载 k3s 二进制文件的话。那么咱们能够当初 k3s 的release页面中找到本人须要的版本,并想方法把它下载下来。

下载下来当前,咱们进入到文件所在的目录,并通过 scp 命令,将这个文件上传到咱们的机器中。

# windows中可能须要先开启openssh性能,如果不能开启openssh的话,咱们能够装置git bash来运行这个命令scp k3s root@xxx.xxx.xxx

上传完后,登录到咱们的主机中,咱们须要为其赋予启动权限,并将 K3S 挪动到/usr/local/bin

# 为k3s赋予可执行权限chmod +x k3s# 挪动文件到/usr/local/bin中mv k3s /usr/local/bin# 后盾运行k3s,这样启动k3s会应用默认的配置,更多配置请看https://rancher.com/docs/k3s/latest/en/installation/install-options/(k3s server &)

装置实现后,咱们能够通过一下命令来判断装置是否胜利。

# 脚本装置的能够用这个指令kubectl get all# 或k3s kubectl get all

在装置完后,可能还是无奈从外网拜访这个 K3s,这时候要看一下 iptables 或者其余防火墙设置。对于阿里云、腾讯云之类的云主机,咱们还须要更改平安组设置,容许 6443 端口的入向流量进入。

Action 你的博客

在 K3s 装置实现后,咱们就能够开始筹备将 Hexo 博客的 github 我的项目安排上 Github Actions 了。Github Actions 是由 Github 官网提供的一套 CI 计划,对于 CI 不太理解的,能够看看阮一峰的介绍。总之,通过 Actions,咱们能够在每次提交代码的时候,让 GitHub 主动帮咱们实现编译->打包->测试->部署的整个过程。Actions 能做的操作十分的多,具体的阐明也能够参考阮一峰的博客。

以下是本博客的 Actions 配置,这个配置只会在 master 收到 push 时触发。随后会应用hexo-cli将博客编译成动态文件;再将这些动态文件打包到一个 docker 镜像中,并 push 到我的镜像仓库;最初通过 kubectl 将我的利用配置部署到 k3s 服务上。只须要将这个 yaml 文件放在我的项目根目录下的.github/workflows文件夹中,再 push 到 GitHub 上,咱们的配置就会失效了。

name: masteron:  push:      branches:      - masterjobs:  build:    runs-on: ubuntu-latest    steps:    - uses: actions/checkout@v1    # node 编译    - name: build      uses: actions/setup-node@v1    - run: |        npm i -g hexo-cli        npm i        hexo clean        hexo g    # docker build,并push    - name: Docker push      uses: azure/docker-login@v1      with:        login-server: reg.qiniu.com        username: ${{ secrets.REGISTRY_USERNAME }}        password: ${{ secrets.REGISTRY_PASSWORD }}    - run: |        docker build -t reg.qiniu.com/holo-blog/blog:${{ github.sha }} -t reg.qiniu.com/holo-blog/blog .        docker push reg.qiniu.com/holo-blog/blog:${{ github.sha }}        docker push reg.qiniu.com/holo-blog/blog    # 让K8s利用deployment    - run: |        sed -i 's/{TAG}/${{ github.sha }}/g' deployment.yaml    - name: deploy to cluster      uses: steebchen/kubectl@master      env:        KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}        KUBECTL_VERSION: "1.15"      with:        args: apply -f deployment.yaml    - name: verify deployment      uses: steebchen/kubectl@master      env:        KUBE_CONFIG_DATA: ${{ secrets.KUBE_CONFIG_DATA }}        KUBECTL_VERSION: "1.15"      with:        args: '"rollout status -n blog deployment/blog"'

在这个配置中咱们应用到了三个 secrets,别离是镜像仓账号密码和 kubeconfig,通过 secrets 调用敏感信息能够防止在 actions 日志或者代码仓库中将这部分信息裸露进去。咱们能够在 GitHub 我的项目settings-secrets页面中设置 secrets。KUBE_CONFIG_DATA这个密钥是 kubeconfig 文件的 base64 版,咱们能够通过以下命令来失去这个 secrets。

kubectl config view | base64# 或k3s kubectl config view | base64

通过这种形式失去的 kubeconfig 中的 apiserver 的地址可能不正确(是 127.0.0.1),咱们能够先将 kubeconfig 保留成文件,在批改实现后在通过cat kubeconfig | base64失去正确的 config 信息。

Docker打包与推送

Docker push这一步中,会用到根目录的一个 dockerfile 文件。通过 dockerfile,咱们能够将利用部署到任意同架构同零碎的机器中。对于docker和dockerfile的更多信息能够查看官网文档。

对于 hexo 博客而言,通过hexo g编译成动态文件后咱们就能够间接通过拜访public文件夹中的 index.html 文件来预览咱们的博客了。所以在这里咱们能够通过将动态文件打包到一个 nginx 镜像中,并裸露 80 端口来为提供 http 申请提供服务。

FROM nginx:1.17.7-alpineEXPOSE 80EXPOSE 443COPY public /usr/share/nginx/html

打包好镜像后,咱们须要将其 push 到某一个镜像仓库中,我用的是七牛云,因为不要钱。

Apply你的降级!

最初,咱们须要告诉 k3s 部署服务或者降级服务。通过在设置好 kubeconfig 文件,咱们便能应用 kubectl 近程拜访 apiserver。在通过kubectl apply -f deployment.yaml部署上最新的配置文件即可。

在这里我又偷了点懒,利用的文件名尽管叫做deployment.yaml,然而其实serviceingress的配置我也一并写入其中了,其残缺配置如下:

apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: blog  namespace: blogspec:  rules:  - host: dalaocarryme.com    http:      paths:      - backend:          serviceName: blog          servicePort: 80  - host: www.dalaocarryme.com    http:      paths:      - backend:          serviceName: blog          servicePort: 80  - host: blog.dalaocarryme.com    http:      paths:      - backend:          serviceName: blog          servicePort: 80  backend:    serviceName: blog    servicePort: 80---apiVersion: v1kind: Servicemetadata:  name: blog  namespace: blogspec:  ports:  - name: http    port: 80    protocol: TCP    targetPort: 80  selector:    app: blog  sessionAffinity: ClientIP  type: ClusterIP---apiVersion: apps/v1kind: Deploymentmetadata:  name: blog  namespace: blogspec:  progressDeadlineSeconds: 600  replicas: 2  revisionHistoryLimit: 10  selector:    matchLabels:      app: blog  strategy:    rollingUpdate:      maxSurge: 1      maxUnavailable: 1    type: RollingUpdate  template:    metadata:      labels:        app: blog    spec:      containers:      - image: reg.qiniu.com/holo-blog/blog:{TAG}        imagePullPolicy: IfNotPresent        livenessProbe:          failureThreshold: 3          httpGet:            path: /            port: 80            scheme: HTTP          initialDelaySeconds: 10          periodSeconds: 2          successThreshold: 1          timeoutSeconds: 2        name: blog        ports:        - containerPort: 80          name: 80tcp02          protocol: TCP        readinessProbe:          failureThreshold: 3          httpGet:            path: /            port: 80            scheme: HTTP          initialDelaySeconds: 10          periodSeconds: 2          successThreshold: 2          timeoutSeconds: 2        resources: {}        securityContext:          allowPrivilegeEscalation: false          capabilities: {}          privileged: false          readOnlyRootFilesystem: false          runAsNonRoot: false        stdin: true        terminationMessagePath: /dev/termination-log        terminationMessagePolicy: File        tty: true        volumeMounts:        - mountPath: /usr/share/nginx/html/db.json          name: db        - mountPath: /usr/share/nginx/html/Thumbs.json          name: thumbs      dnsConfig: {}      dnsPolicy: ClusterFirst      restartPolicy: Always      schedulerName: default-scheduler      securityContext: {}      terminationGracePeriodSeconds: 30      volumes:      - hostPath:          path: /data/db.json          type: ""        name: db      - hostPath:          path: /data/Thumbs.json          type: ""        name: thumbs

须要留神的是,在 docker build 时,我是用的 gitsha 来给镜像打的 tag,所以在 deployment 对象中,我的镜像 tag 那个留的是一个{TAG}占位符。而后咱们再用 sed 命令将这个占位符替换为 gitsha 即可。在 actions 中配置如下:

    - run: |        sed -i 's/{TAG}/${{ github.sha }}/g' deployment.yaml

对于 hexo 中的点击数据点赞数据,咱们能够通过挂载一个本地卷来将其长久化。这样咱们的镜像更新就不会将咱们的数据带走了。

如果心愿可能 0 中断更新,咱们能够部署两个 pod,或者部署 1 个 pod,然而将更新策略设置为 0 个maxUnavailable,这样就能够始终保障有一个 pod 在提供服务了。

其余还有很多细节须要留神,但在这里就不多做开展了。将来可能会做一系列对于 kubernetes 和 git 的文章,感兴趣能够关注以下。

本文中所用到的设置与代码都在 https://github.com/Okabe-Kuri... 中。