Docker是云技术和IT技术的未来,这一点正在被越来越多的公司认识到,但是对于传统企业来说,如何将传统的应用迁移到Docker是一个迫切需要解决的问题。传统企业,尤其是国内的传统企业,IT建设普遍滞后,当大多数互联网公司都在大面积使用Docker的时候,传统企业还在为虚拟化、公有云[注]和敏捷开发挣扎。很多企业都在提工业4.0,但是信息化建设却没有提高到一定的高度。企业的信息化程度,很大程度上决定了企业的生产效率,而 Docker则从应用层面提出了非常好的解决方案。本文也将从迁移的角度来阐述如何在传统企业中使用Docker。
传统应用容器化迁移的思考
对于传统应用来说,使用和不使用Docker可能并不能直接给企业带来好处,相反使用中遇到了问题肯定会给企业带来麻烦。那我们为什么要使用 Docker呢?Docker最大的好处是标准化应用交付,当然随之提高了工作效率,并节约了成本,据国外的统计使用Docker平均可以提升60%的效率,同时节约40%的成本。我们可以把Docker比作一条软件的生产线,当你有了这条生产线后,带来的效率提升是质的飞跃。不过,当你在引进 Docker这条生产线的时候,你肯定会有很多的疑虑,比如:
如何能保证现有生产线到Docker的平滑过渡?
如何确保员工能够快速的上手?
如何合理利用现有的资源,比如数据库、网络等?
如何使用新的服务框架?
如何重新定义软件层的依赖?
如何处理操作系统的问题?
如何重新定义软件的生命周期?
传统应用的容器化迁移建议
当我们需要迁移一个应用的时候,首先需要了解一个应用当前正在使用的资源有哪些。你需要了解当前系统的拓扑结构,现有的拓扑结构是迁移的最好参考,传统的单体模式下,很多应用程序运行在一台主机之上,但在微服务体系下,我们更倾向于通过容器来解耦合,实际上,并不是所有系统都适合于解耦合,我们的进程间通讯是必须要考虑的因素,任何一个企业在迁移过程中都不愿意去重[注]新对一个模块进行重编码,因为有的程序可能根本就找不到代码。本文结合我们的实际经验阐述传统应用在容器化迁移中需要考虑的一些因素。
操作系统
如果你的应用是基于Windows系统,并且不打算使用Linux,可以直接忽略本文(很多应用是可以迁移到Linux的,比如Java、PHP、 Python等)。对于进程来说,它是只工作在Ring 3还是工作在Ring 0-3是至关重要的,如果你进程涉及到内核的操作,需要非常的小心,因为他可能会引起系统崩溃。我们知道通常system call是会从用户态转到内核态,所以基本都会涉及到内核操作。我所说的内核操作是指你自己写了相应的驱动或者内核模块,因为你的kernel代码肯定是更容易出问题的。因此,我们需要了解容器并不是虚拟机,它本身是一个进程,一个受到诸多管控的进程。即使你的容器里面的内核版本很高,但骨子里都是你的 Host内核。因此,容器的隔离并不会像虚拟机一样彻底,对于一个虚拟机,即使你的内核操作引起的崩溃,也不会导致其他虚拟机崩溃,但是,容器则会和大家一起崩溃。所以,我们应当尽量通过容器来隔离不涉及内核操作的应用。如果您不清楚是否涉及内核,也不用担心,目前基本上95%以上的Linux应用都不直接涉及内核操作。我们一般的web服务器,后端服务,基本都不涉及内核操作。
CPU
对于单个应用程序,首先需要考虑的是CPU问题,需要几个CPU,需要多强的CPU。如果是CPU消耗性的程序,要在不影响其他应用情况下,尽可能多的分配CPU。CPU一般是稀缺资源,需要结合实际使用的上限值、下限值和均值做规划。最好的方法就是利用ps、top、htop等工具进行跟踪,最好能跟踪一个软件运行周期,得到软件的CPU上限和下限。CPU的跟踪结果会左右后续的容器资源限定值。
内存的大小
使用容器的一大目的就是要限定应用的资源使用,因此我们需要获得容器的初始内存大小,运行中内存大小以及最大内存。这一部分可以通过vmstat等工具来获得。需要指出的是,内存和CPU都是利用CGroup内核技术进行控制的,所以控制的是使用上限,并不是分配以后就专属于某个容器。只不过如果你的买游戏应用使用内存超过了上限,程序会触发OOM(out of memory),可能会被Kill掉,例如你是通过tomcat来封装应用的,由于启动时候需要使用更多的内存,可能会存在被Kill的可能性。这些都可以通过docker log和系统日志来进行查看。
进程模型
随着高级语言的出现,熟悉操作系统进程模型的人越来越少。父进程是什么,子进程是什么,进程树是怎么样的,线程是怎么构建的都需要弄清楚。这有助于理解应用在一台服务器中所处的位置,以及它能带来的影响。这一部分是通过ps命令来查看的。
网络部分
应用使用的是TCP还是UDP,使用了哪些端口,同时数据包的大小是怎么样的,网络的上下行负载在什么程度。这些可以通过tcpdump、 sar和netstat来获得,有了这些才能设计出容器将要使用的网络模型,是桥接、Host、Overlay、自定义还是网络插件,每一种容器网络都有一定的特点,性能和复杂度也各不相同,我们需要选择最合适的模型来进行规划,而不是选择最复杂的。
用户管理及安全
传统应用系统里面可能有自己的一套安全体系,比如ldap、 active directory等,因此这一部分也是需要考虑的,而安全这部分,也需要考虑证书、防火墙等问题。容器可以有效的提升用户管理的效率,我们可以把 ldap做到容器里面,同时也可以只暴露需要的端口,降低被攻击的可能性。
日志处理
传统的应用有的可能没有日志管理(+微信关注网络世界),有的可能使用syslog或者rsyslog,然后通过中心日志服务器来进行日志汇总。有了容器以后,我们可以借助诸如Ghostcloud的第三方平台,集中化收集日志。可以在daemon层面,也可以在容器层面来进行收集。有了容器之后,你可以很方便的收集全系统日志,如果你有一个基于日志的大数据[注]平台,可以在改动很小的情况下做分析。
共享存储及数据库
很多传统应用都会使用跨主机的文件服务,比如NFS、CIFS。当迁移含有这些服务的应用时,需要添加外部挂在卷。对于MySQL或者Mongo等数据库,在迁移的时候需要考虑是否将数据持久化进容器,还是通过外部的挂在卷。如果通过外部挂在卷,需要考虑容器的HA。同时,如果你的数据库没有办法提供Linux平台的支持,你的数据库肯定是不能做迁移的,当然如果数据库提供Linux下的客户端,你可以将数据库和应用做一次拆分,再做迁移。对于存储和磁盘的跟踪也是必须注意的,Host的RAID怎么做,是否需要支持动态扩展,应用程序的运行目录大小及层次结构,应用程序的运行时磁盘操作,应用程序的持久化操作以及应用的日志及Crash Core都是需要考虑的地方。这一部分,需要一些专业的知识,同时借助于strace等跟踪工具来进行分析,这些将决定后续容器的挂载卷及空间分配,同时也会决定容器文件系统设计方案。
服务发现
传统企业或者应用可能会用到诸如ESB的服务总线,但是随着开源技术的出现和微服务架构的兴起,我们完全可以借助于开源项目或者第三方服务来实现,用的比较多的是consul、etcd 和Zookeeper,其中ZK也是使用非常多的高可用中间件,只不过前两者更专注于服务发现,后者是一个通用的组件,并不仅针对服务发现。以前企业里面涉及到服务发现,很多时候都需要对微服务的方式进行统一约定,而由于容器本身有一个外部的daemon引擎,其服务发现方式可以通过 Registrator内置到daemon里面,跟传统模式也有区别。
持续集成
很多传统应用出现的时候还没有敏捷、DevOps和CI/CD,但是随着移动互联网的高速发展,软件的更新越来越频繁,很多时候一个系统需要在一天内部署几十次。通过不断重复的部署和测试,可以极大的提高软件的稳定性。Docker的快速启动和镜像仓库是天生为CI/CD设计的,以前我们启动一个虚拟机需要几分钟,而启动容器只需要几秒钟,有了这种能力以后集群式的和并行的持续集成才能成为可能。对于持续集成目前业界也没有一个通用的模式,而且每个团队可能习惯的方式和关注点都不一样,因此存在很多定制的地方,有的以镜像为引导,有的以代码为引导,没有最好的方式,只有最适合团队的方式;同时,持续集成的流程也没有最好的,只有更好的。需要不断在开发过程中进行优化。以前国内很多企业受一些国外厂商的影响,花了巨资去购买流程及其复杂的流程软件,其实现在看来并不都适合国内的开发环境。一个简单的例子就是,由于众所周知的原因,国内访问外网的速度就很慢,而现在很多开源项目都放在github上,国外很多看似理所当然的事情,并不适合国内环境。