最近看上了一个Hexo
博客的主题,就打算本身也开一个博客。惊奇的发现 github 上竟然已经有了 CI 功能(是我火星了)。因而乎,一个经过 GitHub Actions 持续部署博客到 Kubernetes 中的想法就出现了。经过这种方式,咱们能够实现 0 停机时间更新 Hexo、弹性扩缩、健康检查和故障转移等等。。。不过其实都没啥意义,一个博客而已,跑在 1c1g 的机器上,不必引入这种重型工具。html
但这架不住我闲得慌,也就有了如今这一篇记录。本文基本是纯记录,对于不少事情不会有太多介绍和解释。node
最后我仍是没有使用 Kubernetes,而是转为使用了K3s
。缘由很简单,Kubernetes 他实在是太大了,光是一个 Etcd 就够我这个可怜的 1c1g 主机喝一壶了。虽然 K8s(Kubernetes)多是不能用了,但咱们还有 K3s 啊。mysql
K3s是 Rancher Lab 在 18 年 7 月发行的一个基于 K8s 的容器编排工具。虽然在使用上和 K8s 几乎如出一辙,可是为了减小资源的使用,k3s 删除了不少云服务相关的插件( Cloud Provider)和存储插件。同时,k3s 使用 sqlite 做为默认的数据库,这能够进一步减小 k3s 的资源使用率,对于单机部署来讲天然是极好的。固然,k3s 也支持配置 etcd 以及其余数据库,如 mysql、postgresql。nginx
就在这几天(2020 年 1 月 13 号),k3s 正式 release 了 1.0 版本。这意味着 K3S 已经能够正常的使用了。因此我也是第一时间的在本身的云主机上部署了一套来玩。不过值得一提的是,k3s 中的容器并不是是使用 docker 来运行的,而是使用了containderd
,这个工具经过crictl
交互,子命令和 docker 都差很少。若是想要使用 docker 做为 container runtime,须要在启动参数中指定。git
一般状况下,咱们只须要运行一行curl -sfL https://get.k3s.io | sh -
就能够完成 K3s 的安装了。github
假如咱们不幸遇到了一些网络问题,没法顺利的下载 k3s 二进制文件的话。那么咱们能够如今 k3s 的release页面中找到本身须要的版本,并想办法把它下载下来。sql
下载下来之后,咱们进入到文件所在的目录,并经过 scp 命令,将这个文件上传到咱们的机器中。docker
# 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 &)
安装完成后,咱们能够经过一下命令来判断安装是否成功。npm
# 脚本安装的能够用这个指令 kubectl get all # 或 k3s kubectl get all
在安装完后,可能仍是没法从外网访问这个 K3s,这时候要看一下 iptables 或者其余防火墙设置。对于阿里云、腾讯云之类的云主机,咱们还须要更改安全组设置,容许 6443 端口的入向流量进入。
在 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: master on: push: branches: - master jobs: 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 push
这一步中,会用到根目录的一个 dockerfile 文件。经过 dockerfile,咱们能够将应用部署到任意同架构同系统的机器中。关于docker和dockerfile的更多信息能够查看官方文档。
对于 hexo 博客而言,经过hexo g
编译成静态文件后咱们就能够直接经过访问public
文件夹中的 index.html 文件来预览咱们的博客了。因此在这里咱们能够经过将静态文件打包到一个 nginx 镜像中,并暴露 80 端口来为提供 http 请求提供服务。
FROM nginx:1.17.7-alpine EXPOSE 80 EXPOSE 443 COPY public /usr/share/nginx/html
打包好镜像后,咱们须要将其 push 到某一个镜像仓库中,我用的是七牛云,由于不要钱。
最后,咱们须要通知 k3s 部署服务或者升级服务。经过在设置好 kubeconfig 文件,咱们便能使用 kubectl 远程访问 apiserver。在经过kubectl apply -f deployment.yaml
部署上最新的配置文件便可。
在这里我又偷了点懒,应用的文件名虽然叫作deployment.yaml
,可是其实service
和ingress
的配置我也一并写入其中了,其完整配置以下:
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: blog namespace: blog spec: 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: v1 kind: Service metadata: name: blog namespace: blog spec: ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app: blog sessionAffinity: ClientIP type: ClusterIP --- apiVersion: apps/v1 kind: Deployment metadata: name: blog namespace: blog spec: 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... 中。