分布式系统部署、监控与进程管理的几重境界

原文:http://blog.csdn.net/Solstice/article/details/6406944linux

 

目录(?)[+]算法

陈硕 (giantchen_AT_gmail)数据库

Blog.csdn.net/Solstice  t.sina.com.cn/giantchen编程

陈硕关于分布式系统的系列文章:http://blog.csdn.net/Solstice/category/802325.aspx安全

本做品采用“Creative Commons 署名-非商业性使用-禁止演绎 3.0 Unported 许可协议(cc by-nc-nd)”进行许可。 
http://creativecommons.org/licenses/by-nc-nd/3.0/服务器

约定:本文只考虑 Linux 系统,文中涉及的“服务程序”是以 C++ 或 Java 编写,编译成二进制可执行文件(binary 或 jar),程序启动的时候通常会读取配置文件(或者以其余方式得到配置信息),同一个程序每一个服务进程的配置文件可能略有不一样。“服务器”这个词有多重含义,为避免混淆,本文以 host 指代服务器硬件,以“服务端程序/进程”指代服务器软件(或者具体说 Web Server 和 Sudoku Solver,这两个都是服务软件)。网络

 

在进入正题以前,先看一个虚构但典型的例子:Sudoku Solver。(Sudoku Solver 是个均质的无状态服务,分布式系统中进程的状态迁移不是本文的主题。)多线程

假设大家公司的分布式系统中有一个专门求解数独(Sudoku)的服务程序,这个程序是大家团队开发并维护的。一般 Web Server 会使用这个 Sudoku Solver 提供的服务,用户经过 web 页面提交一个 Sudoku 谜题,web server 转而向 Sudoku Solver 寻求答案。每一个 Web Server 会同时跟多个 Sudoku Solver 联系,以实现负载均衡。系统的消息结构大体以下,每一个圆角矩形是一个进程,运行在各自的 host 上:架构

幻灯片18

上图中的 Web Server 请不要简单理解为 httpd + cgi,它其实泛指一切客户端,自己多是个 stateful 的服务程序。

固然,系统不是一开始就是这样,它经历了多步演化。

four

一开始 (a),只有一个 Sudoku Solver,也只有一台 Web Server,是个简单的一对一 (1:1) 的使用关系;

随后 (b),随着业务量增长,一台 host 不堪重负,因而又部署了几台 Sudoku Solver,变成了一对多 (1:n) 的使用关系;

再后来 (c),一台 Web Server 撑不住了,因而部署了几台 Web Server,造成了咱们一开始看到的多对多 (m:n) 的使用关系;

(d) 中的状况留到文末再讲。

 

在分布式系统中部署并运行 Sudoku Solver,须要考虑如下几个问题:

  • Sudoku Solver 如何部署到多台 host 上运行?是把可执行文件拷过去吗?程序用到的库怎么办?配置文件怎么办?
  • 如何启动服务程序 Sudoku Solver ?若是每一个 Solver 的配置文件稍有不一样(好比每一个 Solver 有本身的 service name),那么配置文件是自动生成吗?
  • Sudoku Solver 的 listening port 如何配置?如何保证它不与其余服务程序重复?
  • 若是程序 crash,谁来重启?可否自动重启?开发/运维人员可否及时收到 alert?
  • 若是想主动重启 Sudoku Solver,要不要登陆到那台 host 上去 kill ?仍是可以远程控制?
  • 若是要升级 Sudoku Solver 程序,如何从新部署?如何(尽可能)作到不中断服务?
  • Web Server 如何知道那些 Sudoku Solver 的地址?是否是静态写到 Web Server 的配置文件里?
  • 若是 Sudoku Solver 所在的 host 发生硬件故障,管理人员是否能马上得知这一情况?Web Server 可否自动 fail over 到其余 alive 的 Solver 上?
  • 部署新的 Sudoku Solver 以后,Web Server 可否自动开始使用新的 Solver 而无需重启?(重启 Web Server 彷佛不是大问题,这里咱们进一步考虑 client 是个有状态的服务,应该尽可能避免重启。)
  • 程序能否安全地退役?比方说公司再也不作求解 Sudoku 的业务,那么关闭所有 Sudoku Solver 会不会对其余业务形成影响?

这些问题能够大体归结为几个方面:部署(含升级)可执行文件与配置文件、监控进程状态、管理服务进程,合起来可称为运维 operation。

根据公司的规模和技术水平不一样,分布式系统的运维分为几重境界,如下是我对各重境界的简要描述。

 

境界1:全手工操做

这个大概是高校实验室的水平,分布式系统的规模不大,可能十来台机器上下。分布式系统的实现者为在校学生。

系统彻底是手工搭起来,host 的 IP 地址静态配置。

部署:编译以后手工把可执行文件拷贝到各台机器上,或者放到公用的 NFS 目录下。配置文件也手工修改并拷贝到各台机器上(或者放到每一个 Sudoku Solver 本身单独的 NFS 目录下)。

管理:手工启动进程,手工在命令行指定配置文件的路径。重启进程的时候须要登录到 host 上并 kill 进程。

升级:若是须要升级 Sudoku Solver,则须要手工登录多台 hosts,能够拷贝新的可执行文件覆盖原来的,并重启。

配置:Web Server 的配置文件里写上 Sudoku Solver 的 ip:port。若是部署了新的 Sudoku Solver,多半要重启 Web Server 才能发挥做用。

监控:无。系统不是真实的商业应用,仅仅用做学习研究,发现哪儿不对劲了就登录到那台 host 上去看看,手工解决问题。

这个级别可算是“过家家”,系统时零时不灵,能够跑跑测试,发发 paper。

境界2:使用零散的自动化脚本和第三方组件

这大概是刚起步的公司的水平,系统已经投入商业应用。公司的开发重心放在实现核心业务,添加新功能,暂时还顾不上高效的运维,或许系统的运维任务由开发人员或网管人员兼任。公司已经有了基本的开发流程,代码采用中心化的版本管理工具(好比 SVN),有比较正式的 QA sign-off 流程。

公司内网有 DNS,能够把 hostname 解析为 IP 地址,host 的 IP 地址由 DHCP 配置。公司内部的 host 的软硬件配置比较统一,好比硬件都是 x86-64 平台,操做系通通一使用 Ubuntu 10.04 LTS,天天机器上安装的 package 和第三方 library 也是彻底同样的(版本号也相同),这样任何一个程序在任何一台 host 上都能启动,不须要单独的配置。

假设各台 host 已经配置好了 ssh authentication key 或者 GSSAPI,不须要手工输入密码。若是要在 host1, host2, host3, host4 上运行 md5sum 命令,看一下各台机器上的 SudokuSolver 可执行文件的内容是否相同,能够在本机执行:

for h in host1 host2 host3 host4; do ssh $h md5sum /path/to/SudokuSolver/version/bin/sudoku-solver ; done

公司的技术人员有能力配置使用 cron、at、logrotate、rrdtool 等标准的 linux 工具来将部分运维任务自动化。

部署:可执行文件必须通过 QA 签署放行才能部署到生产环境(若有必要,QA 要签署可执行文件的 md5)。为了可靠性,可能不会把可执行文件放到 NFS 上(若是 NFS 故障,整个系统就瘫痪了)。有可能采用 rsync 把可执行文件拷贝到本机目录(考虑到可执行文件比较大,估计不适合直接放到版本管理库里),而且用 md5sum 检查拷贝以后的文件是否与源文件相同。部署可执行文件这一步骤应该能够用脚本自动执行(比方说 ssh $host rsync /path/to/source/on/nfs /path/to/local/copy/)。为了让 C++ 可执行文件拷到 host 上就能用,那么一般采用静态连接,以免 .so 版本不一样形成故障。

Sudoku Solver 的配置文件会放到版本管理工具里,每一个 Solver instance 可能有本身的 branch,每次修改都必须入库。程序启动的时候用的配置文件必须从 SVN 里 check-out,不能手工修改(减小人为错误)。

管理:第一次启动进程的时候,会从 SVN check-out 配置文件;之后重启进程的时候能够从本地 working copy 读取配置文件(以免 SVN 服务器故障对系统形成影响),只在改过配置文件以后才要求 svn update。服务进程使用 daemon 方式管理 (/sbin/init 或 upright 工具),crash 以后会马上自动重启(利用 respawn 功能)。服务进程通常会随 host 启动而启动(放到 /etc/init.d 里),若是要重启 hostA 上的服务进程,能够经过 ssh 远程操做(好比在本机运行 ssh hostA /etc/init.d/sudoku-solver restart )。进程管理是分散的,每台 host 运行哪些 service 彻底由本机是的 /etc/init.d 目录决定。把一个 service 从一台 host 迁移到另外一台 host,须要登陆到这两台 host 上去作一些手工配置。

升级:可执行文件也有一套版本管理(不必定经过 SVN),发布新版本的时候严禁覆盖已有的可执行文件。比方说,如今运行的是

/path/to/SudokuSolver/1.0.0/bin/sudoku-solver

那么新版本的 Sudoku Solver 会发布到

/path/to/SudokuSolver/1.1.0/bin/sudoku-solver

这么作的缘由是,对于 C++ 服务程序,若是在程序运行的时候覆盖了原有的可执行文件,那么可能会在一段时间以后出现 bus error,程序因 SIGBUS 而 crash。另外,若是程序发生 core dump,那么验尸 (post mortem) 的时候必须用“产生 core dump 的可执行文件”配合 core 文件。若是覆盖了原来的可执行文件,post mortem 没法进行。

配置:Web Server 的配置文件里写上 Sudoku Solver 的 host:port (比 境界1 有所提升,这里依赖 DNS,一般 DNS 有一主一备,可靠性足够高)。不过 Web Server 的配置文件和 Sudoku Solver 的配置文件是独立的,若是新增了 Sudoku Solver 或者迁移了 host,除了修改 Sudoku Solver 的配置文件,还有修改全部用到它的 Web Server 的配置文件。这在系统规模比较小的时候尚且可行,系统规模一大,这种服务之间的依赖关系会变得隐晦。若是关闭了某个服务程序,可能一不当心形成其余组的某个服务失灵。如孟岩在《经过一个真实故事理解SOA监管》举的那个例子同样。

监控:公司会使用一些开源的监控工具(如下以 Monit 为例)来监控每台 host 的资源使用状况(内存、CPU、磁盘空间、网络带宽等等)。必要的话能够写一些插件,使之能监控咱们本身写的服务程序 (Sudoku Solver)。可是这些监控工具一般只是观察者,它们与进程管理工具是独立的,只能看,不能动。这些监控工具备本身的配置文件,这些配置须要与 Sudoku Solver 的配置同步修改。Monit 能够管理进程,可是它判断服务进程是否能正常工做是经过定时轮询,不必定能马上(几秒钟)发现问题。

 

在这个境界,分布式系统已经基本可用了,但也有一些隐患。

配置零散

每一个服务程序有本身独立的配置,可是整个系统没有全局的部署配置文件(比方说哪一个服务程序应该运行在哪些 hosts 上)。

服务程序的配置文件和用到此服务的客户端程序的配置是独立的,若是把 Sudoku Solver 迁移到另外一台 host,那么不只要修改 Sudoku Solver 的配置,还要修改用到 Sudoku Solver 的 Web Server 的配置,以及监控 Sudoku Solver 的 Monit 的配置。若是忘记修改其中一处,就会形成系统故障。

分布式系统中服务程序的依赖关系是个使人头疼的问题,“依赖”还好办(程序的做者知道我这个服务程序会依赖哪些其余服务),“被依赖”则比较棘手(如何才能知道停掉我这个程序会不会让公司其余系统崩溃?)。这也从一个侧面证实使用 TCP 协议做为惟一的 IPC 手段的必要性,若是采用 TCP 通信,为了查出有哪些程序用到了个人 Sudoku Solver (假设 listening port 是 9981),那么我只要运行 netstat -tpn |grep 9981 就能找到如今的客户;或者让 Sudoku Solver 本身打印 accept(2) log,连续检查一周或这一个月就能知道有哪些程序用到了 Sudoku Solver。

进程管理分散

若是 hostA 发生硬件故障,如何能快速地用一台备用服务器硬件顶替它?可否先把它上面原来运行的 Sudoku Solver 迁移到空闲的 hostB 上,而后通知 Web Server 用 hostB 上的 Sudoku Solver?“通知 Web Server”这一步要不要重启 Web Server?

境界3:自制机群管理系统,集中化配置

这多是比较成熟的大公司的水平。

境界 2 中的分散式进程管理已经不能知足业务灵活性方面的需求,公司开始整合现有的运维工具,开发一套本身的机群管理软件。我尚未找到一个开源的符合个人要求的机群管理软件,如下虚构一套名为 Zurg (名字取自科幻电影《第五元素》,拼写稍有不一样;Zurg 也是《玩具总动员》中的一个反派角色。)的分布式系统管理软件。

Zurg 的架构很简单,典型的 master slave 结构,见陈硕在《多线程服务器的适用场合》中对“管理 Linux 服务器机群”的描述。

幻灯片32

在《分布式系统的工程化开发方法》中谈到了 Zurg 的功能需求:

幻灯片39

到了这一境界,平常的管理运维工做已经再也不须要反复执行 ssh,常见任务均可以经过 Zurg 来完成。

部署:只须要向 master 发一条指令,master 会命令 slaves 从指定的地点 rsync 新的可执行文件到本地目录。

进程管理与监控:Zurg 的主要功能就是进程管理和监控,比起通常的开源工具,Zurg 更具有一些优点。因为 Sudoku Solver 是由 Zurg Slave fork() 而得,那么当 Sudoku Solver crash 的时候,Zueg Slave 会马上收到 SIGCHLD,从而能马上向管理员报告状态并重启。这比 munit 的轮询要迅速得多。(还能够在 fork() 以前作一些手脚,让 Zueg Slave 能更方便地得到 Sudoku Solver 的存活状态。)

为了安全起见,Zurg Slave 在启动可执行文件的时候能够验证其 md5,这样避免错误版本的服务程序运行在生产环境。

Zurg Master 能够提供一个 Web 页面以供查看本机群内各个服务程序是否正常运行。而且提供一个接口(能够是 HTTP)让咱们能编写脚原本控制 Zurg master。

升级:若是要主动重启 Sudoku Solver,能够向 Zurg master 发出指令,不须要用 ssh & kill。Zurg 会保存每台 host 上服务进程的启动记录,以便过后分析。若是用境界 2 中的手动 /etc/init.d 管理方式,须要到每台机器上收集 log 才知道 Sudoku Solver 何时重启过。

另外也能够单独开发 GUI 程序,运行在运维人员的桌面上,重启多台 host 上的 Sudoku Solver 只须要点几下鼠标。

配置:零散的配置文件被集中的 Zurg 配置文件取代。

Zurg 配置文件会制定哪些 service 会在哪些 host 上运行,Zurg Master 读取配置文件,而后命令各个 Zurg Slave 启动相应的服务程序。比方说配置文件指定 Sudoku Solver 运行在 host一、host二、host3 上,那么 Zurg 会通知在 host一、host二、host3 上的 Zurg Slave 启动 Sudoku Solver。(固然,每台 host 上的 Zurg Slave 须要由 /etc/init.d 启动,其余的服务程序都由它负责启动。)

更重要的是,服务程序之间的依赖关系在 Zurg 配置文件里直接体现出来。比方说,在 Zurg 配置文件里指明 Web Server 依赖 Sudoku Solver,Web Server 的配置文件由 Zurg master 生成(可能会用到模板引擎,读入一个 Web Server 的配置模板),其中出现的 Sudoku Solver 的 host:port 由 Zurg master 自动填上,这样若是把 Sudoku Solver 从 hostA 迁移到 hostB,只须要改一处地方(Zurg 的配置),而 Sudoku Solver 和 Web Solver 的配置都由 Zurg master 自动生成。这样大大下降了犯错误的机会。

 

到了这一境界,分布式系统平常管理已经基本成熟,但在容错与负载均衡方面有较大的提高空间。

目前最大在障碍是 DNS,它限制了快速 Failover。比方说,若是 hostA 发生硬件故障,Zurg Master 当然能够在 hostB 上马上启动 Sudoku Solver,可是如何通知 Web Server 到 hostB 上享用服务呢?修改 DNS entry 的话(把 hostA 的域名解析到 hostB 的 IP),可能要好几分钟才能完成更新,由于 DNS 没有推送机制。

若是思路受限制于 host:port,那么会采起一些看似高级,实则笨拙的高可用 (high availability) 解决方案。比方说在内核里作作手脚,设法让两台机器共享同一个 IP,而后经过专门的心跳连线来控制哪台 host 对外提供服务,哪台是备用机。若是那台“主机”发生故障,能够快速(几秒钟)切换到备用机,由于 hostname 和 IP 地址是相同的,客户端不用从新配置或重启,只要从新链接 TCP 就能完成 failover。若是在错误的道路上走得更远一点,可能还会设法把 TCP 链接一同迁移到备用机,这样客户端都不须要断开并重连。

 

Load balance 也受限于 DNS。

若是发现现有的 4 个 Sudoku Solver 不堪重负,又部署了 4 台 Sudoku Solver,如何通知各个 Web Server 把新的 Sudoku Solver 加到链接池里?

有一些 ad hoc 的手段,比方说每一个 Web Server 有一个管理接口,能够透过这个接口向它动态地增减 Sudoku Solver 的地址。借助这个管理接口,咱们也能够作一些计划中的联机迁移。比方说要主动把某个 Sudoku Solver 从 hostA 迁移到 hostB,咱们能够先在 hostB 上启动 Sudoku Solver,而后透过 Web Server 的管理接口把 hostB:9981 添加到 Web Server 的链接池中,再把 hostA:9981 从链接池中删掉,最后停掉 hostA 上的 Sudoku Solver。这对计划中的 Sudoku Solver 升级是可行的,能作到避免中断 Web Server 服务。对于 failover,这种作法彷佛稍显不够方便,由于要让 Zurg Master 理解 Web Server 的管理接口,会给系统带来循环依赖。(正常状况下,Zurg Master 不该该知道/访问它管理的服务程序的接口细节,这样 Sudoku Solver 升级的时候不用升级 Zurg Master。)

这种作法要求 Web Server 在开发的时候留下适当的维修探查通道,见陈硕《构建易于维护的分布式程序》中的推荐作法。

另一种 ad hoc 的手段,每一个 Sudoku Solver 在启动的时候本身主动往某个数据库表里 insert 或 update 本程序的 host:port。Web Server 的配置里写的不是 host:port,而是一条 SELECT 语句,用于找出它依赖的 Sudoku Solver 的 host:port,Web Server 还能够经过数据库触发器来及时获知 Sudoku Solver address list 的变化。这样增长或减小 Sudoku Server 的话,Web Server 几乎能够马上应对,也不须要透过管理接口来手工增减 Sudoku Solver 地址。数据库在这里扮演了 naming service 的角色,它的可用性直接影响了整个系统的可用性。

境界 3 是黎明前的黑暗,只要统一引入 naming service,抛开 DNS,容错和负载均衡的问题迎刃而解。

境界4:机群管理与 naming service 结合

这是业内领先的公司的水平。

前面分析到,使用 Zurg 机群管理软件能大大简化分布式系统的平常运维,可是它也有很大的缺陷——不能实现快速 failover。若是系统规模大到必定程度,机器出故障的频率会显著增长,这时候自动化的快速 failover 是必备的,不然运维人员疲于奔命救火。

实现简单而快速的 failover 不须要特殊的编程技巧,也不须要对 kernel 动手脚,只要抛弃传统的 DNS 观念,摆脱 host:port 的束缚,采用为分布式系统特制的 naming service 代替 DNS 便可。

幻灯片40

naming service 的功能是把一个 service_name 解析成 list of ip:port。比方说,查询 "sudoku_solver",返回 host1:998一、host2:998一、host3:9981。

naming service 与 DNS 最大的不一样在于它能把新的地址信息推送给客户端。比方说,Web Server 订阅了 "sudoku_solver",每当 sudoku_solver 发生变化,Web Server 就会马上收到更新。Web Server 不须要轮询,而是等候通知。

naming service 谁负责更新?

在境界 2 中,Sudoku Solver 会本身主动去 naming server 注册。到了境界 3,因为 Sudoku Solver 是有 Zurg 负责启动,那么 Zurg 知道 Sudoku Solver 运行在哪些 hosts 上,它会主动更新 naming service,不须要 Sudoku Solver 本身动手。

naming service 的可用性(availability)和一致性如何保证?

毫无疑问,一旦采用这种方案,naming service 是系统正常运转的关键,它的可用性决定了系统的可用性。naming service 绝对不能只 run 在一台服务器上,为了可靠性,应该用一组(一般是 5 台)服务器同时提供服务,固然,这须要解决一致性问题。目前实现高可用 naming service 的公认办法是 Paxos 算法,也有了一些开源的实现(ZooKeeper、KeySpace、Doozer)。

对程序设计的影响?

若是公司的网络库在设计的时候就考虑了 naming service,那么对程序设计来讲是透明的。配置文件里写的再也不是 host:port,而是 service_name,交给网络库去解析成 ip:port 地址列表。

为何 muduo 网络库没有封装 DNS 解析?

一方面由于 gethostbyname() 和 getaddrinfo() 作 DNS 解析是阻塞的,我一时没有时间写一个非阻塞的 DNS 库;另外一方面,由于在大规模分布式系统中 DNS 的做用不大,我宁愿花时间实现一个 naming service,而且为它编写 name resolve library。

 

在境界 3 中,每一个项目组有本身的 hosts,只运行本项目中的服务程序,每一个服务程序的 TCP 端口能够静态分配(好比 Sudoku Solver 固定使用 9981 端口),不担忧端口冲突。若是公司规模继续扩大,早晚会把 16-bit 的 port 命名空间用完,这时候给新项目分配端口号将成为问题。

到了境界 4,这一限制将被打破,服务程序能够 run 在公司内任何一台 host 上,也不用担忧端口冲突,由于 Zurg 会选择当前 host 的空闲端口来启动 Sudoku Solver,而且把选中的端口保存在 naming service 中。这样一来,TCP port 也实现了动态配置,Web Server 彻底能自动适应 run 在不一样 port 的 Sudoku Solver。

相关文章
相关标签/搜索