Appearance
通过 CI/CD 自动化完成运维工作
TODO
还没完成更新
持续集成、交付和部署 (CI/CD) 均为 DevOps 做法。 换句话说,它们就是将 DevOps 理念付诸实践的技术。 如果您对这个词并不熟悉,您可能会想:DevOps 到底是什么? 它与敏捷软件开发有什么关系? 了解软件开发做法演变的大背景以及敏捷和 DevOps 试图解决的问题,可以帮助您从 CI/CD 流程获得更多收益。
-- Jetbrain
简言之,我不想每次都手动配置一大堆服务,因此决定用 CICD 工具进行改进。内网有一个节点服务器部署了 Gitea,通过 Action 服务来进行自动化的运维
内网服务自动配置
目前一个内网服务从内网机器上部署好服务之后我还需要处理一堆手动配置内容。外网防火墙部分由于内网是统一域名的,因此只需要泛域名转发解析就行。但是剩下的依旧麻烦
- homepage 主页上配置好说明和入口
- nginx 上配置转发。选择是否本地就接入鉴权接口
- authelia 鉴权服务上配置规则,内网一份,外网一份
- 雷池防火墙配置认证规则。配置最外层 nginx 和防火墙通路
- 配置 CDN(方便外网访问)。目前是直接通过 Cloudfalre 的面板进行配置
五个接口的超长链路很明显影响配置效率,尤其是在内网存在近百个服务的情况。但是 CDN、外层 nginx 又没法和内网一样配置泛域名解析省事,总不能外网能全量访问内网服务吧,这样操作的风险巨大
因此我进行了项目的 CICD 改造,通过代码进行统一管理。只需要简单配置几行代码,甚至压缩到一行,就能配置好看板、nginx、鉴权、WAF 和 CDN
转发配置说明
简单介绍下实现方式吧,由于技术栈以纯原生 python 为主(懒得装第三方包),因此配置文件用容易读取和校验的 json。一条配置包含多个内容,最外层是所在设备(自动翻译成对应 IP),内部则是以项目名为键,值则是元数据。程序自动读取默认或填写的信息显示到看板,元数据中包含服务命名和端口描述等工具,这些也由对应的程序自动解析并推送给对应的服务
配置文件含义解析如下,现在在一个简单的 json 文件中可以包含大量的信息,甚至不在乎元数据只填项目名称和转发信息一行就能自动打通服务的全流程转发。只需要在每次代码上传的时候解析然后更新服务就行
- 显示名:OpenSpeedTest
- icon:dashboard-icons 服务的 openspeedtest.png
- desc:略
- 转发信息
- 子域名:speed
- 服务配置:把 10000 端口的 http 服务转发到 https 默认的 443 端口
- 远程转发:分成两块,remote 代表对外网监听 speed 子域名,2fa 代表远程鉴权为两步验证
json
{
"Node": {
"OpenSpeedTest": {
"icon": "openspeedtest",
"desc": "Test the web speed",
"relay": "name=speed,tun=http-10000,remote=2fa"
}
}
}
防火墙配置方案
实际上防火墙配置起来非常麻烦,我原本以为就两种状态,针对 API 的纯检测和针对 WEB 的跳转页面动态监测,后端统一走 authelia 鉴权。但是因为 authelia 的鉴权需要带上 cookies,有时候网页也需要绕过 authelia,所以配置超级加倍。雷池这边开关机器人检测两种状态、是否指向鉴权两种状态,一共就是雷池 4 种 x 鉴权 3+1 种 = 16 种配置。好在能通过剪枝大幅降低配置复杂度
其实最大的问题在于全 16 种命名和管理会混乱,一般只需要鉴权默认的 bypass,1fa,2fa 默认命名
除了默认模式一定走 WEB 端口和出于代码复用兼容性考虑(内网也用同一套规则)可以用 1fa,其他雷池状态 authelia 只有验证(肯定走最严格的两步验证)和不验证两种选择,因此状态实际上只有 雷池 4 种 + 默认 1fa
实测雷池的兼容性比 authelia 强,web 模式仅对网页首次加载验证,而 authelia 鉴权需要所有请求都加 cookies。不存在网页不兼容雷池的人机验证但是兼容鉴权跳转的情况。所以雷池不开机器人检测且指向鉴权这种情况不存在,状态喜-1
其实安检和接口模式理论上也可以剪枝,只保留接口模式。但是考虑到部分需要开放的网站不一定安全,因此还是保留安检模式,非必要不使用接口模式
模式 | 防火墙 | 鉴权 | 说明 |
---|---|---|---|
网页 | 人机验证、流量检测 | 指向 | 普通网站 |
安检 | 人机验证、流量检测 | 绕过 | 部分网站请求不带 cookies,鉴权无法跳转 |
接口 | 流量检测 | 绕过 | 请求不带 cookie/无法跳转 |
- 状态和配置命名关系
- 1fa,2fa: 指向雷池网页模式端口,鉴权服务正常配置
- bypass: 指向雷池安检模式端口,不配置和指向鉴权服务
- api: 指向雷池接口模式端口,不配置和指向鉴权服务
按需配置
实际上还有一个非常棘手的问题需要解决,那就是一个配置文件牵扯的服务太多了。假如发现项目的描述字数有点多,然后程序就会自动把看板、nginx、鉴权、waf、cdn 全部重启一遍,这样子的风险太高,操作也太多了
因此在寻找了大量方案后,我把原本直肠子的 CICD 脚本拆分成了多个部分。将原本的生成配置-传输配置-重启服务的脚本结合 action 的缓存功能拆成了多个部分
- 生成配置并比对:生成所有配置并和缓存中的配置文件进行比对,然后更新缓存
- 上传到服务器:读取缓存,把缓存中的文件 scp 到服务器
- 按需执行:根据第一步比对的内容解析按需执行脚本
- 清理垃圾文件:ssh 到服务器,清理垃圾文件
这个过程听上去负载,实际上一点都不简单。每一步都需要结合 gitea 的 actions 特性进行复杂的操作,踩了非常多的坑。我这里展示了部分代码,...
的代表删减掉了没意义的配置
- 在不同的 job 之间传递一个信息非常复杂,配置起来很折腾
- 在 step 中指定 id,然后把内容输出到 $GITHUB_OUTPUT 这个特殊变量
- 在 outputs 配置中指定 key 并配置输出对应的 step 和输出中对应的变量
- 在其他 job 的 needs 配置中添加有 outputs 的 job 名,通过 needs.job.outputs.key(记得填变量名)的方式引用
- 缓存的传递:这个相对是小问题,就是需要添加加载和缓存缓存会增加大量代码。而且如果最终配置没有变更,但是按需更新失败,程序会认为缓存不需要更新,导致部署失败。这个问题要么强制删除所有缓存,要么等 1.23 版本发布后添加手动运行 action 的触发检测
- 拆分上传:和单个 job 不同,按需更新脚本每次都上传删除一次缓存过于复杂。重跑单个按需更新脚本的时候需要手动执行上传和下载步骤,所以上传步骤不能和检测步骤一起执行,这样会增加成本
- 拆分删除:因为按需更新脚本的运行次数是未知的,因此必须在最后强制清理,而且由于 actions 运行不是完全按照上下顺序检测的,因此删除步骤也需要在 needs 中添加检测步骤,否则会直接把上传的代码在按需更新脚本前删掉
- 文件锁问题:不知道为什么有部分 python 脚本无法实现读取并关闭配置文件,然后通过
python example.py > 配置文件
的方式更新配置文件,这样读取出来是空的。只能先输出到其他文件中,然后移动覆盖
yaml
name: Push
...
jobs:
Check-Update:
runs-on: debian
outputs:
nginx: ${{ steps.nginx.outputs.check }}
steps:
- uses: actions/cache@v4
...
- uses: actions/scp-action@v0.1.7
...
- run: python3 auth-nginx.py > temp.yml && mv temp.yml nginx/auth.conf
- id: nginx
run: diff -rq nginx cache/nginx && echo "check=0" >> "$GITHUB_OUTPUT" || echo "check=1" >> "$GITHUB_OUTPUT"
- uses: actions/cache/save@v4
...
Upload-File:
runs-on: debian
steps:
- uses: actions/cache@v4
...
- uses: actions/scp-action@v0.1.7
...
Update-Nginx:
runs-on: debian
needs: [Check-Update]
if: needs.check-update.outputs.nginx == '1'
steps:
- uses: actions/ssh-action@v1.0.0
...
Clean-File:
runs-on: debian
needs: [Check-Update]
...
博客自动发布
由于我日常写一堆博客而且不喜欢每次写完还得手动推送和做一些收尾工作,因此 CICD 也需要提上日程
自动化推送
没啥好说的,目前 web 服务器用的是 vitepress,虽然确实比之前用的 docfisy 新且性能强、扩展性好,但是比起只需要配置一个 html 的旧框架来说还是复杂了很多
vitepress 是一个 ssg 框架,将需要 js 动态加载的 spa 网站的每个页面静态化,这样方便 seo 和加速用户访问时的响应速度。但是这也带来了一个巨大的问题,即使修改一点点内容也要全量编译,非常头大。更加令人头大的还有不会自动的打包引用的文件,需要你手动移动进去
好在打包后的文件是纯静态的,因此可以直接塞给 nginx,编译后只需要直接推送到服务器上就行(其实之前的也可以,只是因为和后台代码捆绑的比较厉害,所以没法直接放到 nginx 里)
- 检出代码
- npm install && bulid
- 移动静态资源
- 推送到服务器
SEO 自动优化
自动化 SEO,通过 API 在更新后自动提交。这个操作其实比较简单,只需要缓存上一次的更新 git 标签,通过自带的命令行工具比对发生变化的文件,根据规则进行重新即可
需要注意
- 使用 python 读取 config.mts 中的 sitemap 方便更改 hostname 后自动同步
- 需要过滤黑名单目录和缓存目录下的文件,比如说 assets 目录和同目录下的图片,然后重新路径
- 更新需要考虑 token 超过请求次数限制的问题,尤其是百度每天只给 10 条,可能需要读取更新实现并按照更新时间排序逐个请求直到 429
- 处理多家搜索引擎的 token 权限
bash
git diff --name-only HEAD^ HEAD
- 参考文档
- 必应:indexnow
- 百度:普通提交
- 谷歌:Indexing API
运维自动化
整点自动化运维步骤吧
脚本同步
由于我开虚拟机很无节制,目前内网常用 linux 虚拟机有 3 台,物理节点 3 个,腾讯云服务器 2 个,oracle 云 4 个。这 12 台机器总不能每写一次脚本我就手动同步一次吧。正好现在网络打通方案搞得差不多了,研究一下方案
外网服务器在打通网络后可以通过内外网统一域名来访问代码仓库,虽然说外网服务器基本不需要同步数据,但是为了方便同步运维脚本还是一起规划了吧
其实这个活非常简单,在之前基础设施即服务的那篇文章里把内网的虚拟机统一了一下基础环境之后,现在只需要自动 cd 到服务器上,然后 pull 一下就行
yaml
name: SyncCode
...
jobs:
Deploy-Conf:
runs-on: debian
steps:
- name: executing remote ssh commands using password
uses: ssh-action@v1.0.0
...
script: |
cd /script
git pull origin master