服务器运维的几重境界

标题所说的运维是一个比较宽泛的概念,基本上与游戏本身无关的、程序员(主要针对服务器)又要去做的事情都涵盖在内,包括版本管理、发布部署、集群管理、服务器容灾、服务器扩容、数据备份恢复、监控警报等等。

“虽然我很笨,但是我很勤奋。”

就像愚公移山,最原始的运维靠的是蛮力。愚公的愚主要就体现在做事情太直接,缺乏进一步的思考。

要打版本?新建个目录把代码check出来,make一下就编译好了,再改一下版本号就大功告成。

怎么部署呢?scp拷贝到生产环境,再运行脚本解压缩重启一下。

要加新服务器?在新机器上安装好游戏运行要用的软件和库,用vi修改全局配置文件把新机器加上去,再用scp拷贝到每台服务器,最后全重启一遍。

服务器挂了怎么办?有备用的空闲机器就可以直接用了,如果没有还得花时间装软件。当然改全局配置再整体重启是免不了的了。

问题是很显然的吧。运维人员主要在做重复性的工作,服务器需要24小时值守。时间久了一定会疲惫的,而且各种手动修改操作很容易误操作带来不可挽回的损失。

“机器能做的事情交给机器去做,但是关键的操作还是得靠人啊!”

到这个阶段开始考虑编写脚本或者引入工具了,目的是为了减少重复性的工作。

打版本使用写好的自动化脚本,要打版本的时候一行命令版本包就出来了。版本号可能在代码库的某个文件里,打版本之前需要手动改一下提交,做的好点的会在脚本里做自去递增。更进一步会用上可持续集成工具(如jenkins),这样在网页上点一下就能出版本,打版本的事情就可以完全交给非技术人员了。

更新部署这类线上操作还是要交给运维人员来手动做,但是会编写大量的脚本将操作简化。特别是对于服务器集群的批量操作,通常会在集群中选一台主机作为中控机/跳板机,直接在中控机中使用expect + ssh脚本批量操作远程主机。需要更新的版本和公用配置也可以从中控机上批量分发出去,可以用脚本,也可以用rsync进行同步。

引入监控系统监控服务器的运行状态,有异常情况时通过短信电话通知。

通过使用自动化脚本,运维人员大量的日常事务得到了简化。虽然还是要24小时待命,可能时不时得半夜起床处理线上问题,但至少不用轮流加夜班,可以每天回家睡觉了。

“我们已经不是两三岁的小孩了。”

运行在服务器上的程序就像没长大的小孩子,一有风吹草动就又哭又闹。运维就像是同时照顾几十个小孩的保姆,一刻不得闲。实际上我们完全可以通过良好的软件设计来提高程序的可用性。可以使用守护进程保证进程宕了之后能自动重新启动,最好能注册为服务在系统重启后也能恢复服务,如果能做到恢复之前的会话状态就更好了。相互之间有交互的进程不要设计成强制要求特定启动顺序,也就是说要有进程之间设计自动重连的机制。数据库要能在故障时自动进行主从库切换。

对运维友好的程序有了一定的自我修复能力,这样一来大部分问题就不再是需要立即着手解决的了,我们终于可以睡个安稳觉了!

注:后面介绍的3个工具基本上是我这几天陆续看来的,这里只是做一下介绍和展望,具体实践有待后面继续探索。

“Linux上装应用也能像苹果AppStore一样便捷吗?”

答案是完全可以,我们要用的 工具是Docker。简单的说,Docker可以用来把一个程序和这个程序运行所需要的整套环境打包成一个镜像,这个镜像可以被分发到任意一个Linux系统中运行,唯一的要求是这个系统中安装了Docker。

Docker官方提供了一个镜像发布中心DockerHub,用户可以自由发布制作好的镜像供他人使用,也有各种软件维护者发布的官方镜像。DockerHub听起来像是Docker平台上的GitHub,实际上意义远不止于此,我认为其地位应该相当于Linux界的AppStore。

试想我现在想在某台服务器上运行nginx,我只需要从DockerHub下载nginx镜像,再docker run启动一个容器来运行这个镜像,我不需要规划把nginx安装到系统的哪个目录,也不关心这个nginx的版本到底依赖于哪个版本的libc。同样如果我想要一个apache服务器,那么我就下载一个apache镜像下来运行,完全不用先准备一套jdk环境。不同应用之间是完全独立的,即使需要不同的jdk版本也不会产生冲突。

游戏版本的发布过程也将发生深刻变化。我们不再需要在每台服务器上安装游戏运行的依赖库,转而制作一个包含了所有依赖的基础镜像。需要发布版本时把编好的程序和配置添加到基础镜像中就得到了一个游戏版本镜像,这个镜像拷贝到任意装了Docker的Linux系统上都能一致地运行。

最后我们的服务器集群每台主机上需要安装的唯一软件就是Docker,服务器的配置工作大幅简化。

“像生态系统一样会自我修复。”

前面所说程序的自我修复能力,其实只能部分解决软件方面的缺陷,对硬件故障是无能为力的。在服务器集群的长期运行中,出现硬件故障的概率是100%。主机不再可用时唯一的办法是换用备机,可问题是服务器列表往往是做一份配置文件分发到每台服务器的,换用备机必须把新的配置重新分发,如果有必要还得一一重启……

这里要用到的是分布式数据管理工具etcd(类似的还有zookeeper、doozer等)。应用etcd我们可以把服务器共用配置存储在分布式环境,注意不是存在某台主机上,而是由多台主机共同维护并保证其一致性,这样一来任何一台主机故障了都不会影响数据的完整性。同时各个游戏组件可以作为观察者向etcd注册自己感兴趣的配置,于是我们只要通过etcd修改配置,集群中的所有主机都能得到通知并更新自己保存的服务器列表了。

我们甚至可以做得更智能一点,运行一个监控程序监视各个系统组件的运行状态,如果发现某节点故障了无法提供服务,那么自动先择备用节点替换之,并通过etcd更新配置。

如此一来,整个故障恢复过程完全是自动化的,我们的集群就像生态系统一样有了自我修复的能力。

“聪明的程序员善于抽象,卓越的程序员在抽象之上抽象。”

最后顺着上面的思路介绍一个工具:fleet。fleet对分布式程序的运行模式作了进一步抽象,把分布式系统分为Service和Machine两个独立的部分。

比如游戏中用一个game进程承载一个区的玩家,那么game就是Service,体现为一个Docker镜像。如果我们要运行5个区,但是只有4台服务器,那么就选其中一个服务器运行两个game,等到有多出来的服务器的时候再转一个game过去运行就好了。如果有一台机器突然坏了呢?好说,从所有服务器中找一台空闲点的再运行一个game,顺便把这个game的新ip+port通过etcd广播一下就行了。只要有一个调度机制,这些都可以自动完成,完全不需要人工干预,我们只需要在系统负载过高的时候扔几台主机进集群完事。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181212G1M7ZY00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券