全网统一账户实践

分享下目前我们全网的账号管理体系。

整体的账户管理思路是分而治之。主要分为下面三类账户:
1. 办公网账户,也就是大家熟悉的域账户。对于办公网账户,全网用户一人一账户,在 OpenLDAP 的基础上做了一些开发,这是进入公司内部的大门,所有新入职的员工都会分配一个该账号,不管是在办公室连接 Wi-Fi 还是在家连接 anyconnect VPN,访问 confluence/jira 等基础办公设置,都需要通过此账户进行登录认证。
2. 生产网账户,主要用来访问线上、线下机器资源。工程师访问线上生产、测试机器,登录线下自助机树莓派(raspberry) 均需要通过此账户认证,整个 user/group 的分配、HBAC/sudo 的控制、密码/公钥的管理均在 freeIPA/IDM 上实现,freeIPA 是 RedHat 支持的一整套集成安全信息管理解决方案系统,又称 IDM。
3. 数据库账户,这块比较小众,简单带过。
下面会针对上面两大块分别介绍。

办公网账户

该账号与所有人息息相关,从入职第一天起到你的 lastday,都需要登录该账户才可以访问办公资源,包括常见的 Wi-Fi/Gitlab/Jira/Confluence/Jenkins/Cisco Anyconnect VPN/Zabbix/Grafana/跳板机(intermediate host) 以及自己开发的各种内部系统。

这里面需要先简单描述下目前我们的多网分离的架构,多网是指办公网(包含国际线路)、生产网、测试网、专线网(到医院 HIS/LIS 等信息系统)、OOB(带外管理网),这五张大网是处于「部分」分离的状态的,比如,办公网到生产网/测试网/专线网/OOB 是完全分离的状态,除非通过跳板机(后面全部以 ih 代指)登录之后再访问;生产网跟测试网也是几乎分离的,除了极个别的公共服务。以办公网登录生产、测试网为例,在进入用户验证这步之前,会先通过 IP <-> MAC <-> 用户身份的一一映射绑定,先通过最基本 IP:PORT 的 ACL 方式来控制住大部分的请求。所以在你能通过网页打开 git,通过 ssh 协议 pull/push git 仓库的时候,说明已经通过 TCP/IP 层面的验证机制以及用户级别的验证机制,具体如何实现的会在后面的博客中说明。

默认情况下,OpenLDAP 支持的 schema 非常有限;另外不同应用接入方式都不大一样,需要逐一尝试。

比如 LDAP 跟 Anyconnect 的对接,官方提供的 schema 仅仅基本可用,离生产还有一段距离,默认只支持添加一组 IP/Netmask,以及一条 IP 层面的 ACL,为此需要自行扩展维护一套 cisco.ldif 文件,这个是我们自行维护的一个 schema 文件,根据我们业务的情况,新增了一组 IP/netmask 以及若干的 ACL。下面是一个普通用户的数据文件示例:

从 slapcat 导出的字段数据可以看到,为了实现 Wi-Fi 账号跟 OpenLDAP 的共享,加入了 samba 相关的 schema,具体的可以看这个文件

再比如,对于 anyconnect/ih 的认证,需要增加两步验证机制,这里引入 OTP(privacyIDEA) 以及 Radius 作为跟 OpenLDAP 的桥梁例,具体的交互图可以看这里:

最终实现了用户的 CRUD 在 OpenLDAP 层面控制。用户的两步验证,下图可以看到,privacyIDEA 支持十余种的 token,包括 hOTP/mOTP/sms/email/Yubikey 等等:

privacyIDEA 不仅仅支持我们使用的 ldapresolver,sql/passwd/scim 的 resolver 都有很好的支持。目前我们使用 Google Authenticator 是基于事件的 HOTP(RFC4226):

用来管理 OpenLDAP 的工具有不少,包括我们使用的经典的 phpldapadmin,除此之外,fusiondirectoryweb2ldap 都是不错的选择。同时为了支持用户的自助修改、重置密码,定期修改用户密码,我们引入了 LTB,一个非常神奇的 LDAP 工具箱集合,目前仅支持通过邮件找回密码的链接来重置密码:

除了自助机服务,LTB 还支持 LDAP 性能、用户使用数据的监控等若干非常实用的脚本。
这么重要的基础服务稳定性肯定是需要保障的,通过 syncprov 模块实现双 master 的写的高可用,通过多个 slave 同步 master,前端挂 Haproxy 实现读的高可用:

对用户来说,他们看到的是下面这个样子:

PACKT 出版的 Mastering OpenLDAP 是本通俗易懂的书,浏览一遍之后应该能轻松应对上面的内容。 

生产网账户

这里的生产网泛指包含线上生产测试服务器线下自助机树莓派等在内的所有 *nix 系统。所有需要登录如上机器的用户都需要获得该账户的权限之后,才能访问基于 HBAC 机制的主机以及颗粒度更细致的用户执行权限。

整个系统基于 freeIPA/IDM 实现,默认情况下,每台机器有一个专门用来运行进程(我们所有的 JVM 进程都跑在 vm 上,不会出现混跑的状态)的 worker 用户,所有的相关的 bin/, log/, lib/ 等目录文件都通过 Jenkins 统一打包到 ~/worker/ 下面,实现跟系统文件的隔离。/home/worker/ 的权限如下:

worker/ 下面所有的目录文件权限都如上所示。这样,只要用户在 bm_be_sre 这个组下面,就能实现 worker/ 下面也即 JVM 相关服务的读取权限:

下图可以看到,appCenter 这个服务是跑在 worker 下面的:

类似的,如果想实现 worker/ 下面的写操作,比如执行 bin/ 下面的脚本,将用户加到 30002(sre) 这个组即可,这里完全可以各自的需求自定义。所有的 sudo/su 相关权限设置全部在 IPA 的 Policy 操作即可,比如下面这个赋予的是 sre 这个组的用户可以执行 /bin/su – worker、/bin/su worker 这两个单独的比较危险的命令以及 sre_allow 命令行组里面基础命令(/bin/chmod, /bin/chown, /bin/cp 等等):

目前单台 8G/4core 的 IPA 支撑着 1000+ 的线上生产测试的物理虚拟机以及线下 100+ 的树莓派,应付起来绰绰有余,CPU 平均 1% 利用率、disk IO 平均 10%。

对我们的用户来说,在经过办公网/VPN 的 IP ACL 过滤之后,通过办公网账号结合 OTP 登录线上的任意一台跳板机(ih),然后通过 ih 再登录线上的机器,跟办公网账户类似的是,需要定期修改密码。对有权限看到的用户来说,他们看到的是下面这样

以上的两块账户体系算是把当年在阿里的基础核心账户体系复制了一遍,对最终用户来说,体验相对来说会更好些,当然我们没法也暂时没必要实现类似阿里郎那种监控每台入网设备所有操作的系统。

需要注意的是,在 IPA 上新增的用户,或者新增 sudo 权限,由于 sssd 的缓存,不会立即生效,可以通过 sss_cache 或者删除缓存文件可以让规则立即生效,这部分可以以类似 hook 的方式触发,每次修改用户权限的时候自动触发该操作。

freeIPA 结尾的 A 表示 audit,实际情况是目前还不支持 audit 功能,所以这块功能我们通过 snoopy 结合 rsyslog 方式进行收集审计

对于办公网账户,目前通过 python-ldap 实现了多个层次的自助以及服务的打通,freeIPA 还未进行这块的开发工作,最终的效果类比阿里内外,一个 portal 实现不同用户的自助服务。

数据库账户

第三块,或者说非常小众的一块账户是 DB 的账户,这里面主要涉及 MySQL 以及 NoSQL(Redis/ElasticSearch) 的账户划分管理审计。

MySQL 的账户管理我们通过 saltstack 的 mysql_user 进行管理,我们用的saltstack 是 2015.8.7,对 MySQL 5.7 的支持有不少 bug,2016 开头的版本对此都修复了,嫌麻烦的不妨直接把 salt 给升级了或者做 diff 给 2015.8.7 打 patch,主要原因是 5.7 之前存储用户表的密码字段是 password 而 5.7 之后变成了 authentication_string。

目前所有的 MySQL 账户用户通过 git 管理而密码通过 salt pillar 管理,审计则使用了 Percona 的 Audit Log Plugin 通过 rsyslog 打到中心服务器收集分析。

对于 Redis/ES 来说,由于上面存储的数据为非敏感数据,所有的都通过 IP 层面的 ACL 进行控制访问。

一张复杂网络的生长过程

虽然不是网络出身,但是对于我们全网架构的「生长」过程还是比较了解的,分几个重要的时间点讲讲里面有意思的事情。

15 年下旬

公司业务还没起飞,全网架构简单,应付一天 1k+ 的订单了(有效挂号单)绰绰有余,机房跟第三方的卡管机构通过一根 10M 的 SDH 专线打通,所有到院方的请求先经过第三方的卡管机构,由其转发再进行业务层面的处理,实际的带宽峰值不到 500kbps。

16 年初

正式开始批量接入三甲医院,同时跟医院数据打通的方式也由原先的经过第三方卡管机构的非直连方式变成了直连方式,所有接入的院区通过两根双活的专线跟我们机房打通,两根专线的意义很重大,一旦一根专线被挖掘机挖断,可以在 1s 之内切换到另外一根,上层业务通过重试机制可以做到几乎无感知。新闻上报道的某某地区由于光纤被挖掘机挖断造成网络中断的问题在后来专线数量增加的情况下时有发生,但是到目前为止对于这种双主的架构并无影响,考虑到成本等因素,暂时不会做 N + 2。

为了控制 IP 地址的分配以及部分安全性的考虑,跟院方接入全部采用双向 nat 的方式,双向 nat 的问题之前的博客有过介绍,只不过之前是通过 iptables 实现,本次通过专业的路由设备实现。经过双向的 IP 地址转后,我们对院方的地址全部隐藏,另外由于院内自助机的 IP 全部由我们自行分配,通过双向 nat 之后,我们有更大的自主权。下面这张图演示了 IP 地址转换的过程:

与此同时,为了快速实现新接入医院的稳定上线,保守采用了双防火墙双路由器的策略,通过 BGP/BFD 协议实现单条线路失败的快速收敛,总成本控制在 20K 左右:

医院的数据通过一根 2M 的专线,一根 10M 的专线汇聚到我们的核心机房,核心机房通过主流的三层结构互联。

对于办公网来说,为了实现访问 google 等国际联路,我们通过在办公网的路由器上设立 GRE 隧道的方式连接到国内的专线服务商,再由该服务商进行国际流量的转发,由于跨了国内的公网环境,会出现偶尔的延时增高以及中断的问题。

16 年中

随着接入院区的爆发式增长,成本成了很大的一个问题,原先所有院区接入同一个核心机房的方式不仅成本无法得到控制,单点也成为了一个迫在眉睫的问题。原先三层的简单架构也无法满足日后的水平横向扩展。为了解决上面的问题,花了大量时间跟北京具有运营商资质的公司进行了大量的商务沟通,实现了专线、带宽资源的大幅度下降。在保证稳定性的前提下,2M 的专线做到了平均 450RMB/m,10M 的 1500RMB/m,50M 的 4000RMB/m,100M 的 6000RMB,同时引入了阶梯价格的策略,量大会更加具有竞争力,当然三大运营商的价格会比鹏博士、天地祥云的高些,毕竟稳定性也高的多(这个后来验证确实如此)。最终跟北京三大运营商以及几家准运营商达成了友好的合作,实现了双方的共赢。上面的问题谈妥了,接下来自然引入了下面几个核心思想:
1. 链路类型,优先选用 SDH/MSTP 协议的,G.703 次之。
2. 运营商,成本跟稳定性下的权衡,优先选用电信/电信通,联通/天地祥云次之。
3. 专线接口类型,优先选用双绞线电口,光纤单模次之,方便排查问题,同时默认接入千兆全双工,百兆全双工作为备选。
4. 建立中立的中转机房,所有院区的专线先通过中转机房再进入核心机房,实现了跨区域专线的成本以及稳定性的大幅提升。
5. 在上条的基础上,全网引入骨干网 msr(metropolitan small router)、核心网 csr(core small router)、汇聚网 csw (core switch),专线 dll(dedicated leased line),边界路由器 edr(edge router),边界交换机 esw(edge switch)。优先选择中低端廉价设备,比如 msr 使用 Cisco2921/K9,csw 使用 WS-C3750X-24T-F,esw 使用 WS-2960X-24TS-L 等型号,通过 HSRP/BFD 等协议实现分而治之。

着重说下最后的两点,对于第 4 点,其架构图如下:

可以看到,这一阶段引入了 msr/csw 等角色,将运营商的专线接入诸如鹏博士、天地祥云等中立机房的进行中转,最终汇聚到核心的机房。每一台中转机房的 msr 承载 20 条左右的专线,通过上方的 csw 进行线性扩展,目前每条专线的带宽在 2~10M 左右,所以不会给机器造成很大的压力。

对于第 5 点,在核心骨干汇聚分成之后,一方面是实现了多网分离,即生产网、测试网、OOB 网,办公网以及到院方的专线网的分离,极大的方便了 tcp/ip 层面的访问控制,在这个阶段,所有对人的访问控制(人访问线上线下)全部通过在网络设备上加白名单的方式实现,算是实现了基础的访问控制,虽然比较繁琐。另外一方面全网由之前的树根结构扩展到了树根枝干末梢叶子的网状结构,扩展性以及冗余性都有了很大的提高,下面这张图是相对比较成型的网络拓扑:

这个阶段,除了院内、机房的大幅度改造之外,办公网尤其是国际网络也做了优化,从原先的 GRE 隧道方式一步到位成了专线直连方式,办公网通过专线直接到服务商节点,节点直接到第一线的香港节点,同时,为了避免所有流量都走国际网络,我们在内网通过 DNS view 的方式,将主要被墙的域名解析丢给 Google DNS,其余则解析丢给国内 DNS,DNS 层面解析结束之后,TCP/HTTP 的流量则通过在路由层面通过不同的 ACL 规则实现国内国际流量的划分。下图是日常访问 Google 的 ICMP 延时:

16 年底

为了进一步控制成本,响应国产化的号召,原先一家院区 4 台设备(2 台防火墙、2 台路由器)的架构,在保证稳定性不变的情况下,优化成了由 2 台带路由功能的防火墙(USG 6306)组成的专线网络:

在同样秒级切换的情况下,成本降低到了原先的一半不到。同时,考虑到后续接入非三甲院区的特殊性,目前正在调研舍弃大型物理硬件,通过 4G 网卡配套 VPN 的方式来进行网络层面的直连。

在优化成本以及性能的基础上,还进行了下面几点的优化:
1. 通过 PowerDNS 的 API 实现了上千条内网的 DNS 正反向解析,这对于使用 mtr 细颗粒度的排查定位问题提供了很大的帮助
2. QoS 的优化,优先走生产上的业务流量,其次生产上的非核心(日志、监控等),在 msr 上通过 CoPP 实现
3. 全网设备的配置备份问题,所有设备的配置每日自动备份到内网的 gitlab 上
4. 为了实现生产网访问被墙的资源,在办公网搞了两套 tinyproxy,生产的机器通过 http_proxy,访问前端的 Haproxy 实现了网络资源的自由访问
5. 办公网无线的 LDAP 认证,这块在全网统一账户实践有所涉及

从 15 年底到 16 年底的一年多时间,通过不断的演变,一个相对比较完善有很强冗余性的大网基本建设完成,该架构支撑目前每天的 100k 订单(有没有发现一年期间增长了多少倍),40k 的有效挂号量没有任何瓶颈。

在建设初期遇到不少跟院方通信的网络中断问题,起初做贼心虚以为是自身的问题,后来通过监控不断的完善,实现了分钟级别的问题发现与诊断。

下面列一些有趣的问题:
最常见的,某院区由于专线被挖断,单条专线中断,平均一个月一次。院方机房全部或者部分掉电,导致所有专线中断;院方机房制冷设备故障,导致专线设备罢工死机。院方线下自助机网络设备故障,导致该区域自助机全部脱网,类似的事情不一而足。再比如我们的 RPC 服务访问某院区 HIS,凡是下一跳为 10.222.0.45 均能够访问终点 IP,凡是下一跳为 10.222.0.41 则无法访问同样的的终点 IP,经过抓包发现由于院方的两台核心路由故障导致。最喜剧的性的事,某院区偷懒将我们跟 HIS 直连的 IP 划分到他们的核心网络,由于 arp-proxy 的问题导致全院 HIS 宕机数个小时。当然医院 HIS 宕机其实是件很频繁的事,之前仅仅因为处在一个封闭落后并且无知的环境,很多时候重启应用重启机器问题就算是绕过去了,现在一下子暴露在了一支「正规军」面前,一下子露馅了。

树莓派(raspberrypi)、saltstack 在线下自助机运维上的应用

目前每家院区都分布了从数台到近百台规模不等的自助机,覆盖了北京市属 22 家医院的三十多个院区,一千多台的日常变更、升级管理、甚至常人看来很简单的开关机成了摆在眼前的一大问题。下面这篇博客会抛出 4 个问题并且分享下我们线上的实战经验。

1. 开关机,一个看似很幼稚的问题
你去市面上问一个的有点经验的运维,「服务器怎么开关机」,他可能第一反应是,「服务器要关机吗?」如果你接着问,「如果现在就有这么一个需要,需要先关机过段时间再开机,怎么办?」,他很可能会告诉你,「远程卡啊,这不是标配吗,再不行,电话个驻场的帮忙开下呗」。 以上的情况对于机房的服务器是适用的,但是对于自助机,放在院内各个大厅角落,物理环境恶劣,随时断网断电,尘埃飞扬,更别谈恒温空调 UPS 之类的,要实现开关机,真不是一件容易的事情。我们在院内用的自助机,说白了就是一台组装的 PC,仅仅是通过串口并口连接了一些读卡器扫码器等外置设备。既然是消费级别的 PC,就不会有远程卡这么「高端」设备,当然,对于 PC 级别来说,市面上是有跟企业级别 iDRAC/iLO/iMana 类似技术的设备的:Intel 的 AMT,最终的实现效果跟远程卡几乎一样,另外一种能实现开机的方式就是通过 Wake-on-LAN 这种古老的协议。

有开就有关,AMT 能开能关,Wake-on-LAN 就没办法了。但是基本思想很简单,需要在每台机器上安装一个 agent,通过此 agent 来下发关机以及后续的变更操作。

有了上面的思路,我们来对比下 AMT 以及 Wake-on-LAN 这两种方案的优劣,如下图:

对于目前的业务来说,我们最看重的是经济成本以及可扩展性,很明显,Wake-on-LAN 完爆 AMT。

有了大方向,下面是具体的实现,首先是至少有一台常年开机的机器用来唤醒其他的机器,选自助机肯定不行,这些机器本身就会出现白天开机晚上关机的状态,后来想到了树莓派,设备小,可以完全放进一台自助机里面,只要该自助机的电源线不拔,就能保证树莓派的常年开机状态;管理简单,默认使用的 Raspbian 系统,源的同步,账户的管理,监控、变更等等跟我们线上完全打通;经济成本低,一台树莓派配上 SD 卡电源线插座传感器(这个后面会单独提)不到 400。有点需要注意的是,很多情况下院内会有多个 vlan,正常情况下 Wake-on-LAN 不支持跨网段的唤醒,一般来说,放在不同位置的自助机网段会不大一样,加上我们需要监控不同位置的温湿度烟雾的状态,所以最终决定出单家院区 4 台树莓派的标准配置。下图是一套树莓派的配置图:

搞明白了上面的核心问题,接下来要解决的是流程类的问题,每上一个新院区,我们会直接格式化 SD 卡灌装定制的 raspbian 系统,salt 初始化,zabbix/freeipa 注册,实施人员到现场之后插电接网即可。

2. 如何优雅的开关机
上面的话题仅仅覆盖了基本的原理,在我们上线的早期,每个院区仅仅上线了个位数的机器,同时运行的院区在三四个左右。这里需要提到一个背景,每个院区的部分自助机,同一个院区的不同自助机,开关机时间都不同,比如友谊位于门诊大厅的自助机是每天早上 7:00 准时开机对外使用,但是二三楼的自助机则是每天早上 07:10 启用,还有一台位于急诊大厅的机器则是全年 24 小时开机,而对于同仁医院来说,所有的机器都是早上 06:45 开机。

早期,我们通过 saltstack/jenkins/git 在树莓派上维护对应的院区的 crontab 列表来实现自助机的开关机。

另外一个频繁出现的问题是,院方会经常性的提出修改自助机开关机服务时间的需求,每次运营转达给我们再经过 git/jenkins 提交的效率异常的低效。为了彻底的将这部分重复低效的工作从运维手里解放出来,我们为院方开发了一套开放平台(open),除了日常的开发者文档更新、开发接口 API 调用,接口测试用例等之外,一块重要的功能就是为院方开放了自助机服务器时间的自主修改查看权限,这样将日常繁杂的自助机时间管理下移到了院方手里。

目前该平台支持全局、个别自助机的开关机时间:

bangkok 上面的 KioskScheduler 进程定期会向 open 平台同步各个院区所有自助机的开关机时间,将其写入到 MySQL,底层通过 APScheduler 这个任务框架来调度,使用 BackgroundScheduler 调度器,默认的 MemoryJobStore 作业存储。每家院区的规则都分为全局跟单独两种,全局规则针对该院区的所有自助机有效,优先级低;单台自助机规则仅针对单台自助机规则生效,优先级高。KioskScheduler 根据以上优先级的不同,优先执行高优先级的规则。这样「积水潭 2017 年春节期间,除了 10、11 号自助机在每天的 08:00 ~ 16:00 开机之外,其余所有的自助机均关机」的规则就能顺利实现了。

同时,为了确保 KioskScheduler 运行正常,应用层面通过 monit 实现进程的监控,业务层面的规则执行与否以及是否达到预计,我们通过 python-nmap 实现了一个批量扫描的脚本,每次开关机时间点触发后的 5min/10min/15min 三个阶段,对命中规则的自助机进行批量的存活性扫描,对于未达到期望的自助机会触发报警到我们的自助机运维同事那边进行人工处理。

对于开机的方式,部署在院内的 4 台树莓派通过 syndic 机制收到请求后同时向目标机器发起唤醒的请求,根据之前的经验,单台树莓派的唤醒偶尔会出现唤醒失败的情况,另外一点 4 台里面只有保证至少有 1 台存活,那么开机就能成功触发。

3. 如何低成本实现 OOB 的核心功能?
先来看下行业标杆的 Dell 远程卡(OOB)提供的一些核心功能,如下图:

根据我们日常使用频率的统计,80% 以上的操作花在了如下的操作上:

1. 服务器的开关机,尤其是机器 hang 住了/kernel panic 之后的开关机
2. 由于各种原因无法 ssh 之后需要通过虚拟终端进入查看当前机器屏幕的状态
3. 机器主要零部件的硬件状态,健康状况

对于一台自助机来说,其核心是简化后的服务器,没有 raid 等企业功能,除此之外,提供的服务跟服务器并无二异,仅仅是加上了 GUI 而已。对于第一、二两点,上面的话题已经阐述过,比较遗憾的是目前并不能对机器的死机、蓝屏实现脱离系统的开关机重启以及实时屏幕的查看,但是对于日常的开关机,VNC 远程操控,绰绰有余;对于第三点,目前诸如磁盘等底层硬件的监控是放在应用层来监控,对于我们来说,个别自助机硬件层面的宕机并不会影响整个院区的使用,我们及时报修返厂更换即可解决此类问题,众所周知的是,对于绝大多数的医院,其院内的物理环境,比如尘埃浓度(PM10)、温湿度等跟正规的机房比相距甚远,早期,我们发现某院区的部分的自助机经常性的宕机蓝屏,经过若干天系统级别应用级别的排查,都无所发现,后来现场的志愿者提醒我们是不是温度太高导致的,现场勘查后发现,西区的部分自助机直接放在了仅有一顶透明帐篷遮蔽的半室外空间内,6 月市内温度 30 度的情况下,这部分自助机箱体的温度已经接近 40 度了,蓝屏在所难免了。

为了及时发现这类问题,我们后续在所有树莓派上全部引进了温度、湿度以及烟雾的传感器,成本非常低,一个 DHT11 的温湿度传感器加上一个 MQ-2 的烟雾传感器成本在 20RMB 以内,加上现成的 Adafruit_DHT/RPi.GPIO 的库直接调用,完美解决此类问题。通过分散在不同地点的四台树莓派,我们能够推断出当前院内的物理环境,这对改进目前自助机的物理位置有非常重要的意义。

下图是某院区蓝屏故障增大期间周边温度的变化,观察可以发现具备一致性关系:

类似的,我们在院方机房放置的连接专线的路由设备,同样发现多次类似的问题,下图是某院区机房空调设备故障温度升高导致设备丢包增大中断服务的监控:

很多看似简单的问题,一旦场景转变了,需要应对的措施也就随之改变了。

4. 如何有效实现自助机的变更操作
自助机上的变更,主要分为两类,一种是最为频繁的版本升级,包括自助机上挂号缴费程序的版本、第三方依赖的程序升级,这类变更的特点是,每次的程序升级,其升级包都相对较大,小的几兆,大的有上百兆;第二种变更更多的集中在日常的系统变更,比如修改 hosts 以对应不同的 nat 影射、增删改查注册表等系,对于这类变更,最大的特点是占用的带宽小,但是操作频繁。

针对这两种变更,分别设计了两种不完全一样的变更方式,但是底层都是通过 saltstack master/syndic/minion 的架构实现文件的传输。中心端一台 master(存在单点的问题),每家院区 4 台部署了 syndic 的树莓派,同时跑 master/syndic,向中心端注册,底层的自助机部署有 minion 向其中一台 syndic 注册,目前 minion 到 syndic 同样存在单点问题,后续考虑将 syndic 升级成 MultiSyndic。

对于第一种变更方式,我们将要升级的版本库文件打包上传至 git,通过 jenkins 将数据从打包机 git pull 下来之后,在打包机通过 salt 将升级的文件分发到 salt syndic 上,syndic 上会起 http 服务,自助机每次开机的时候会自动的向 http 服务检查是否有最新的版本,如果有的话则会升级,对于回滚,我们将版本号递增内容回滚至上一版本重启机器即可。
对于第二种变更方式,如果是修改注册表之类的变更,我们使用 cmd.run 直接将需要跑的 key 执行结束,类似下面这样:
cmd.run 'reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel" /v "{5399-XXX-CBA0}" /t reg_dword /d 1  /f'

如果是传输文件的变更,跟第一种方式类似,不同的是,文件落地到 syndic 之后,我们会直接通过 master/minion 的方式 push 到每台自助机,而不是主动 pull 的方式。同时为了确认文件的完整,每次从 master 到 syndic,从 syndic 到 minion 的两个关键步骤都会做一次 md5 校验,最终实现的效果可以参考早期的 saltpad 版本,下图是 bangkok 中变更的页面展示:

除了最基本的单次变更外,增加了多次执行的执行步骤以及执行步骤的模版功能,前者对于多合一的操作步骤很适用,后者对于经常使用的诸如修改 hosts 文件等很适用。

在这之前,我们尝试直接通过 salt master/minion 的方式进行日常的变更管理,这种方式对于线下的实施同事来说非常的不友好,其灵活性以及方便性远低于目前的方案,遂未在团队内推广使用。上面介绍的系统是目前在线的稳定系统,使用了一段时间整体反馈还不错,后续会优化版本控制以及更加自动的回滚等操作。

通过 noVNC 实现数千台自助机的实时可视化

背景很简单,目前我们运营维护着北京市属三甲医院数千台的自助机,这里面包含挂号取号机、检查报告机以及其他若干衍生出来的自助机种类,数量已经超过一千多台,不同的院区通过密密麻麻的专线跟我们的机房源源不断的进行着各种协议的数据传输与交互。

由于各方面技术以及非技术层面的限制,只能使用微软的系统,为了管理这部分的机器,一方面我们通过 saltstack 来进行日常的产品升级以及变更,对于桌面的可视,比如我们需要知道当前这台自助机前患者的操作,就需要 VNC 来帮助了。

一年前自助机刚上线时候,没有很详细的考虑过自助机的大规模运维问题,当时每台自助机都在 Clonezilla 打包阶段打包进了 realVNC,通过一台 Win 跳板机的 realVNC 客户端去连接,realVNC 有书签的功能,所以当时需要查看每台自助机界面的运行情况相对比较方便,随着数量的增加,这种先登录一台 win 的跳板机,在使用 realVNC 书签查看的传统 CS 架构就捉襟见肘了。迁移到 web 上势在必行,本着可小动不大动的原则,调研了 realVNC 的各种 SDK,遗憾的是都不支持,加上 realVNC
有版权问题,放弃。相比之下,TightVNC 则不存在这类问题。

后来我们组的同学调研了几个基于 web 的 VNC 客户端,peer-vncnoVNCguacamole ,peer-vnc 活跃量太低,底层用的是 noVNC,guacamole 还处在孵化期(功能相当强大)并且比较重,放弃。最终选择在 noVNC 的基础上做一些工作。

由于 TightVNC 默认不支持 WebSockets, noVNC 提供了 websockify 这个工具来做 TCP socket 的代理,接受 WebSockets 的握手,转化解析成 TCP socket 流量,然后在 CS 两端传递。由于我们涉及到较多的机器信息需要维护管理,我们将所有的机器信息,比如自助机的编码,机器名,IP/MAC 相关的信息预先存储到 MySQL 里面,接着按照 token: host:port 既定格式生成每家院区的连接信息配置文件,这一步可以在 jenkins/rundeck 上建个 job 方便每次增删改查。接着就可以启动了转发了。

在浏览器中打开本地的 6080 端口即可。下图是集成到我们内部管理平台上的截图,可以快速定位到自助机的健康状况并通过 VNC 进入确诊当前的状态:

noVNC 默认情况下会以交互式的方式连接,在这个过程中会做身份权限校验(账号连接、读写控制),是否是 true color 等,这个对于生产不是很适用,我们后来将授权这块做在 Django 上,结合 LDAP 做登录认证。考虑到专线带宽的限制,默认关闭了 true color 开启了压缩。VNC 对带宽的消耗还是比较厉害的,平均下来,每开一个新链接,会消耗 1Mbps 左右的带宽,所以如果需要做实时的展示大屏,需要考虑这块的瓶颈。

下图是 noVNC 实时图像,由此我们可以看到当前这台自助机的实时状态并对问题的自助机采取一定的操作:

最后将其挂在 haproxy/Nginx 后面,Nginx 1.3 之后支持 websocket,一个快速搭建并可投入使用的自助机可视化平台就算完成了。该平台我们内部命名为 bangkok。

以上思想可以移植到 *nix 平台上,目前不少主流的云 OpenStack/OpenNebula/Digital Ocean 也集成进了 noVNC。

大陆地区的 DNS 劫持

撇开方院士的那个不谈,这种方式在大陆的流行情况我仅仅是听说,收到过我们客服的反馈而已,至于这个具体有多么的「生动」,我还真没遇到过(正常情况下,如果你的所有的流量都走 proxy,是不会遇到那种浏览器右下角弹广告的情况的,所以建议有能力的用户不管在哪里访问网络,都请主动走 proxy)。
直到我们开始启用了某个第三方的服务监控,才发现这个比例还不小,数据还是蛮有意思的,我找了两个列子,基本上一个 nslook 就能看出问题所在了。


这事要根本解决没强大的背景搞不定啊。

另外,这事对业务有没有影响,肯定是有的,但是有多大,还是要业务,比如面向最终用户的,一个访问了就是投诉过来了,但是如果是类似我们的这类看总体指标的业务,其实目前来看只要不是一个地区的大规模的劫持,还是能容忍的。