作为一个在云计算领域学习工作的新人,对于部署方面的问题略有接触。正好看到了一篇文章,结合近我在做的一个线上部署打桩虚拟机调试的工作,算是做一点感想记录吧。 上古时代 部署工作自从计算机诞生以来有了吧。多年以前,人们都是登录到一台服务器上,用命令行安装(更早的时候是下载源码,编译)一些软件,更改一下配置文件,启动服务。 done!收工了。 这种做法要是管理一两台服务器还好,如果要管理一两百台服务器呢?手工一台一台如何装的过来? 命令式部署 这种方法称之为命令式部署。我们把一个需要安装的软件,它的依赖,如何配置,如何启动都用shell脚本写好,这是自动化部署的雏形。这一方面为出名的当属devstack的部署脚本了。那一千多行stack.sh其实也是个目录,细细扒下去,盘根错节,各种依赖,到各个子文件夹下去调各个子脚本。事实上,装devstack从来也不是无痛的。经常需要反反复复倒腾好几遍,才能搞定。以至于在成功装了一个devstack之后,赶紧打个快照压压惊~ 当然,这里除了产品本身部署复杂度之外,还有shell这个不给力的,支离破碎语言的锅。 另外,shell脚本本身也不能解决批量部署的问题,要你去给100台服务器装devstack,难道还要一个一个ssh上去,手动敲一遍? 这时候需要祭出ssh批量连接管理自动化工具了,比如python写的Fabric。你可以使用它批量连到几百台服务器上,下发操作指令,简单的如 apt-get install tmux ,或者复杂的你可以本地写好一个脚本,然后批量丢到服务器上去,再执行这个脚本。这样看来,安装程序,修改配置文件,启动服务都是可实现的。 但是,这些脚本都是命令描述式的。在脚本中充斥着各式各样的动词,比如 install,remove,start等等。如果我的期望是一台装好wordpress的服务器,它是如何装的又和我有什么关系呢? 描述式部署 这个问题已经有人想到了,而且轮子也已经发明出来了。这种描述式的部署方式被puppet,以及后来者ansible所采用。从这个时候起,我们更多的是在写配置文件,而不是写程序(例如DSL)。例如ansible,我们用一个配置文件指定哪些节点是什么角色,用另一个配置文件用来描述安装哪些服务。比如,我需要安装: - mysql - php - nginx 描述式部署少了命令式部署中那些??嗦嗦的动词,而且,通过角色的引入,我们也能更好的控制粒度,进行解耦。 事实上,这种方式是现在部署的事实标准(de facto standard)。 更进一步 然而,这种方法没有问题吗?也是有的,首先是依赖问题。安装C先要安装B,安装B先要安装A,我们不能一股脑儿把CBA写到配置文件中而不考虑其顺序。第二是所谓的dependency hell,如果C依赖的B依赖一个低版本的A,而这个A和服务B依赖的高版本的A冲突不兼容,冲突了,那怎么办?只好人肉找出原因,修掉bug。 另外一点是,部署依赖状态。试想,我要装一个包,但是这个包在这个服务器上已经装了一个旧版本的,怎么办?是卸掉旧的,重新装一个新的,还是直接升级?卸掉旧的又需要考虑哪些问题?新的包装失败了怎么办,能回滚吗?回滚不了该如何处理? 事实上,部署从来不是丢一个配置文件,执行一串命令这么简单。我们需要思前想后,考虑许多的可能性,并把这些逻辑都丢到我们的安装脚本或者配置文件中去。 为什么? 因为计算机运行其实是一个存储状态的过程。我初是听七牛的许世伟老师在teahour中提到过这个思想。你面对一台uptime 有好几年linux的服务器,早确定不了它的状态了。对一个未知的东西升个级,万一它崩溃怎么办? 所以线上升级慎之又慎,要联调,要演练,要测试环境都跑一遍。而且服务器上的软件越少越好,不必要的软件不要装,这些运维法则的背后,是降低状态演变的可能性。 没有银弹 喜欢买,不行分,多喝热水,重启试试! ---现代谚语 人生简单的不二箴言,蕴含着大哲理。为什么重启能够解决90%的问题,是因为重启之后是一个确定的状态,从一个确定的状态,通过确定的操作,可以转变成另一个确定的状态。 都是确定的,多好! 虽然没有银弹,但docker可能是一个答案。如果你把所有的服务,配置文件都打到一个镜像中去,然后到哪里都是docker启动,那么像上文说的,是从一个确定的状态(0状态),转移到另一个确定的状态,而且,没有副作用。如果一个服务一个镜像,那么那些相互干扰,依赖冲突也都不复存在了。