Axton
Always dream. Always explore.
无垠

更新到下一代服务器架构

才不是更新到XXX博主呢

缘起

前几天看到 网红羊驼 LWL 大佬的一篇文章 更新至第三代服务器环境 | Docker 萌新体验记,正好随着手头的服务器变多,多服务器管理越来越麻烦,我自己也在更新服务器的架构,就想着写一篇文章讲讲我实现的(和打算实现的)新架构 来水一把,于是有了这篇文章。

设计

LWL 给他的架构取了一个听起来很高端的名字叫 LFS,那么我也给新架构取了个名字叫 ABS 防抱死制动系统 其实是 Axton Base System,既然是下一代那么就可以叫 ABS Next Generation,简称 ABS-NG,嗯,听起来骚多了。

跑偏了。

我把所有服务分为两类:基础服务模块服务。基础服务是 Nginx、PHP、MySQL、Radis 和 Python 这些每一台服务器上都有、频繁使用而又不会经常修改的服务。为了确保稳定性和方便配置,这些服务使用的是常规编译方式直接安装。而模块服务是一些可能只部署在部分服务器上的程序,为了方便部署,大部分模块使用了 Docker。

基础服务没什么好讲的,唯一要花点心思的就是编译参数了。这一点有很多大佬写过文章了,我也就不赘述了。而模块服务才是新架构的重点。我们讲讲其中的部分模块。

这些模块也分为两类,中心模块分布式模块。听起来很高端,其实它们只是运行的地方不同而已。中心模块运行在作为“控制中枢”的一台内网的服务器上,方便控制所有其他服务器;分布模块运行在公网服务器上,只服务于这一台服务器。先来讲讲分布式模块。


SSL 证书管理模块

这个模块从最早的那个 Python 检查 SSL 证书 项目发展而来,现在已经可以做到扫描指定目录下的 Nginx 配置文件来自动管理 SSL证书,包括自动检查、自动申请和续期(使用 acme.sh)等。这个模块基于 Python3 的 Docker 镜像修改而来。

不过如果直接作为分布式模块部署,这个模块还会出一些问题,如 LWL 在文章中所说:

其次还有自动证书签发问题。目前的自动证书换新机制是由各服务器各自为政进行签发,如果进行分布式部署,这样做势必会导致在一个周期内签发的证书数量大幅上涨,带来潜在的安全问题。同时,分布式部署会导致无法预知对特定请求进行响应的服务器,因此无法使用 web 方式进行签发(无法确定 Let’s Encrypt 的请求会被分配到哪个服务器);而如果使用 DNS 方式签发,则多服务器同时续签时将有可能导致互相覆盖验证记录,同样存在问题。

所以这个模块还有一个中心模块(后面会讲到),它们能够协同工作。

基础服务更新模块

作为一个重度懒癌患者,每次 Ngnix 更新时我都会陷入“想更新而懒得更新”的矛盾心理。于是我花了点时间写了这个模块,它可以做到定时检查基础服务软件的更新,并在更新可用时提醒我。一旦确认,它就会使用我的常用编译参数编译安装新版本并自动升级。同时,一旦编译中出现问题,它也会自动停止并回滚到最近版本。至于为什么不全自动更新...我怕不经过我的确认它可能会炸...(事实证明会炸的总是会炸,后面会说到_(:з」∠)_ )

这个模块由一系列 Shell 脚本和 Python 脚本构成,为了方便更新基础服务而没有使用 Docker。相关脚本我会在整理后放在 Github 上。

服务健康度检查模块(开发中)

要什么 UptimeRobot! 由于不想过于依赖第三方服务,我计划了这个模块。这个模块会部署在所有服务器上,可以互相检查其他服务器上公开服务的可用性,也会收集自己所在服务器的 CPU 使用率、内存使用率、网络流量和进程数等一系列信息,还会分析 Nginx 的日志,并把收集到的所有信息上传给中心服务器上的中枢模块(后面会讲到)。

这个模块本质上是一个 Python 脚本,依然基于 Python3 的 Docker 镜像构建。

管理模块(开发中)

其实这个模块应该叫做被管理模块,因为这是中心服务器控制其他服务器的入口。当然安全性方面花了一些工夫,比如限制访问 IP、权限检查、高危操作通知和延时执行等。这个模块可以管理同一台服务器上的所有模块和基础服务,甚至包括管理模块自己。中枢模块高度依赖于这个模块来管理各服务器。

这个模块计划也由一系列 Shell 脚本和 Python 脚本构成,很没有新意地 仍然基于 Python3 的 Docker 镜像构建。

分布式模块讲得差不了,当然还有一些比较私人的模块就不讲了(逃

接着讲讲中心模块。


凭证管理模块

这个模块已经计划很久了,这一次趁架构更新就直接上了。这个模块的开发动力是我发现由于我使用的 API 越来越多,手头积了许多 API 的 SK,但是并没有一个好的方式妥善保管,经常找不到一个 SK 又不敢重新申请,怕不知道在哪里使用着的旧 SK 作废。

于是我花了些时间写了这个模块,它可以帮我妥善保存各种 SK,并通过我封装好的简单的接口在其他程序里调用,也可以通过 API 或者图形界面添加新的 SK。所有数据会每天定时加密后同步到其他服务器的 Radis 中以保证程序调用足够快速,通过对应密码解密即可获得数据。

比如在 Python 中这样就可以调用:

import secretdb

sk = secretdb.get(sk_name, password)

当然保存到不只有 SK,还有...(突然警惕

这个模块只有一个 Python 脚本,基于 Python3 的 Docker 镜像构建。

SSL 证书管理中心模块

这就是上面提到的 SSL 证书管理模块的中心模块啦。不像 LWL 大佬设计的方案这么高端,在我最初的设想中中心模块的主要任务是负责维护和同步主要的几张通配符证书,这几张证书可以覆盖我绝大部分的域名,而同步使用 rsync。分布式的 SSL 证书管理模块会负责将这些证书部署到正确的位置,当然也会管理其他证书。

当然现实没有这么简单。在准备开始写这个模块的时候我才突然意识到:特么内网服务器不通公网,证书怎么自动续期啊!!!但又不想只是为了续期证书而开公网导致安全性降低,最后只好凑合一下,把主要证书的续期工作分配给其中一台公网服务器上的 SSL 证书管理模块,中心模块只负责把新的证书 rsync 回来并 rsync 到其他服务器上以及保存和更新证书的 Key...(菜了好多啊)

和分布式的 SSL证书管理模块一样,这个模块基于 Python3 的 Docker 镜像构建。

通知管理模块(开发中)

Axton 特慢邮寄系统了解一下

这个模块是所有服务的通知中心,所有要发送的通知都会先到达这里再被发送出去,目前支持邮件(阿里云接口)、短信(腾讯云接口)和 Webhook 三种通知方式。由于使用的第三方接口都有频率限制(尤其是短信),这个模块自己维护了一个通知队列,可以做到合并时间相近的通知、适时延迟低优先级通知等来保证通知发送不会触发频率限制。

这个模块目前仍在开发,技术栈还有待简化(目前在 Nginx 的 Docker 镜像里面塞了 Python 和 PHP)。不过我的目标是有一个简单可维护的技术架构并且可以提供像凭证管理模块那样简洁的 API。

import absnotification

notification_id = absnotification.send(title, content, {send_mode_dict}, priority)

和中心 SSL 证书管理模块一样,这个模块也有不连公网没法用的问题,最后在一台公网服务器里塞了一个转发模块凑合了一下。

中枢模块(开发中)

这大概已经不能算是一个模块了,因为它太复杂了。作为整个新架构的核心它又分为了许多子模块,其实是一大堆 PHP、Python 脚本、Julia 脚本和 Shell 脚本粗暴地直接塞在中心服务器的 Nginx 后面,还有好几个 Docker 镜像(比如 Julia)。

这个模块的核心是提供一个 Web UI 来方便地管理所有其他服务器,所以功能复杂且安全要求高(所以要咕咕很久了...)。Web UI 并不直接暴露于公网,而是要通过 VPN 进入内网才能访问。Web UI 计划提供性能监控和预警、网站和数据库管理、Docker 管理、文件管理和基础服务管理等等一大堆功能(然后意识到等于是自己写了一个服务器面板...)。稍稍介绍下已实现的和打算实现的功能吧。

这个系统做了单点登录,这意味着在任何一个系统内的子管理面板登录都会在整个系统中登录。仍在开发中的权限管理系统会按照登录的入口不同赋予单次会话最小的权限,并在需要其他权限进行操作时向用户请求授权,尽可能地保证系统的安全性。

目前接近完成的部分大概只有网站管理和基础服务管理了。不过个人认为最棒的一个功能就是自动网站迁移了。(就算只有我一个人用我也要写 UI!界面是第一生产力!(逃)

https://acdn.flyhigher.top/wp-content/uploads/2018/11/trans.gif

只要这么一拖,整个网站的配置、文件和关联的数据库都会自动迁移。

https://acdn.flyhigher.top/wp-content/uploads/2018/11/copy.gif

复制也可以。甚至在计划接入域名服务商的 DNS API 做到自动切换域名指向(但阿里云的 DNS API 似乎不能直接从内网访问,需要回头测试下)。

基础服务管理现在能做的只有启动、停止、重启、重载和更新某个基础服务。

https://acdn.flyhigher.top/wp-content/uploads/2018/11/update.gif

这就是上文提到过的“一旦确认就会自动升级”。

在这个模块的开发中我第一次大规模使用了 Julia 语言开发一部分功能(对就是前段时间火过一阵子的那个语言)。Julia 开发起来真的很方便,要不是我不太熟练这个语言开发可能会更舒服,尤其是分数计算和复数计算不要太好用,模块中很多数学方面的计算都用 Julia 来实现了。

此外在网页设计中,我一开始使用的是轮询来保证页面上的数据能够实时更新,但是后来随着页面越来越复杂,轮询变得越来越多;同时由于每个页面都不一样,也不方便合并轮询,干脆上了 WebSocket。然而 WS 的服务端又很折腾,最后花了很久才完成了个别几个项目的 WS。

不过话说回来,这个模块的复杂也导致新架构的“Bug 首杀”就发生在这个模块里,这也导致我不得不在小高考的前一天抱着仅有的手机躲在学校的自习教室里疯狂找 Bug...

事故的起因是我接连数天收到来自 SSL 证书管理模块的通知,其表示包括博客主站在内的多个站点的证书即将到期。理论上证书应该在触发通知之前(也就是到期 15 天之前)就会自动续期,因此我意识到有什么东西出问题了。这几个站点的证书用的都是同一张,即是由中心 SSL 证书管理模块管理的那张通配符证书。一番操作之后我发现在中心服务器上的那张证书已经正确地被更新了,其他服务器也正确地同步了新的证书。

这就奇怪了,证书没问题,难不成是 Nginx 的锅?我先是 Reload 了一下 Nginx,发现无效;又 Restart 了一下,还是不行, Nginx 发回的始终是已经不存在了的老证书。

折腾半天之后,终于在与 Zohar 大佬交流一番后被一句“nginx -s stop && nginx 有这样子重启过?”猛然点醒然后跑去从 SSH 进服务器执行了一下。

嗯,启动的时候报错称端口绑定失败。这时候我已经意识到问题所在了,应该是在更新 Nginx 完毕后旧进程没有正确退出导致将近大半个月以来在跑的一直是旧的 Nginx 进程...因为 Nginx 最近一次的更新是我第一次从上面说到的“基础服务管理”里面操作的(就是录上面那个 GIF 图的时候),后来发现不巧的是更新脚本多打了一个“.”导致新版本编译成功后旧的进程没有正确退出;后来 Reload 等操作也是通过“基础服务管理”进行的,在逻辑里所有输出都被抛弃了所以根本看不到错误输出...

于是手动杀掉旧进程重新启动新的 Nginx,再手动触发一次 SSL 证书检查,发现证书正常更新了...

我发现这种低级错误的时候真想把自己打一顿...于是之后修改了更新脚本和“基础服务管理”功能,加上了输出内容的获取,至少现在不会出这种太低级的错误了。

Zohar: 我依然喜欢命令行,面板难免有 Bug

最后还是命令行救了我,真香


其他

当然还有一些是为新架构服务的内容。随便讲两个吧。

Docker 管理

看起来我在这个新架构里面用了很多 Docker,但我真的是 Docker 萌新什么都不会...一开始装 Docker 的时候从包管理器安装的版本还有问题死活启动不了,后面又被 Network 的配置折腾得死去活来。现在 Docker 的管理就是朴素流了,Docker Compose 一把梭,Swarm 什么都是不存在的。

由于我比较菜,虽然写了面板来管理 Docker,但这仅限于一些简单的操作。由于没有时间仔细看文档来开发一些“进阶的” Docker 管理功能,我最后不得已部署了一个 Portainer 镜像来对 Docker 做一些进阶设置。

Setup.sh(开发中)

像 LWL 一样,为了方便在新的服务器上快速部署环境并向中心服务器注册以便被控,我也写了一个部署脚本 Setup.sh。不同于 LWL 的 Deploy.sh,我的目标是可以利用这个脚本实现通用的、可配置的环境部署(通过 JSON 进行配置)。目前我还在完善这个脚本,之后会开源在 Github(但愿不会咕咕咕)。


呼,终于总结的差不多了。如果你能看到这里那么恭喜你坚持看完了上面 5000+ 字的菜鸡总结。这个新架构的目标是尽可能实现自动化,但是在实际操作后才发现有的时候不用心的自动化会也是 Bug 的来源...通过这个大坑自己学到了不少新知识, Docker、Shell 脚本什么的,但也发现要学习的还有很多。这个新架构也还需要很长时间的开发,至少短期内是不可能完成的了,希望可以和我一起慢慢进步吧。

水完撒花!溜了溜了

赞赏
本文链接:https://flyhigher.top/develop/1245.html
本文采用 CC BY-NC-SA 3.0 Unported 协议进行许可

回复 秋のかえで 取消回复

textsms
account_circle
email

无垠

更新到下一代服务器架构
才不是更新到XXX博主呢 缘起 前几天看到 网红羊驼 LWL 大佬的一篇文章 更新至第三代服务器环境 | Docker 萌新体验记,正好随着手头的服务器变多,多服务器管理越来越麻烦,我自己也在更…
扫描二维码继续阅读
2018-11-04