单体应用拆分成多个微服务后,能够实现快速开发迭代,但随之带来的问题是测试和运维部署的成本的提升。相信拆分微服务的利弊你早已耳熟能详,我讲个具体的例子。微博业务早期就是一个大的单体 Web 应用,在测试和运维的时候,只需要把 Web 应用打成一个大的 WAR 包,部署到 Tomcat 中去就行了。后来拆分成多个微服务之后,有的业务需求需要同时修改多个微服务的代码,这时候就有多个微服务都需要打包、测试和上线发布,一个业务需求就需要同时测试多个微服务接口的功能,上线发布多个系统,给测试和运维的工作量增加了很多。这个时候就需要有办法能够减轻测试和运维的负担,我在上一讲给出的解决方案是 DevOps。
DevOps 可以简单理解为开发和运维的结合,服务的开发者不再只负责服务的代码开发,还要负责服务的测试、上线发布甚至故障处理等全生命周期过程,这样的话就把测试和运维从微服务拆分后所带来的复杂工作中解放出来。DevOps 要求开发、测试和发布的流程必须自动化,这就需要保证开发人员将自己本地部署测试通过的代码和运行环境,能够复制到测试环境中去,测试通过后再复制到线上环境进行发布。虽然这个过程看上去好像复制代码一样简单,但在现实时,本地环境、测试环境以及线上环境往往是隔离的,软件配置环境的差异也很大,这也导致了开发、测试和发布流程的割裂。
而且还有一个问题是,拆分后的微服务相比原来大的单体应用更加灵活,经常要根据实际的访问量情况做在线扩缩容,而且通常会采用在公有云上创建的 ECS 来扩缩容。这又给微服务的运维带来另外一个挑战,因为公有云上创建的 ECS 通常只包含了基本的操作系统环境,微服务运行依赖的软件配置等需要运维再单独进行初始化工作,因为不同的微服务的软件配置依赖不同,比如 Java 服务依赖了 JDK,就需要在 ECS 上安装 JDK,而且可能不同的微服务依赖的 JDK 版本也不相同,一般情况下新的业务可能依赖的版本比较新比如 JDK 8,而有些旧的业务可能依赖的版本还是 JDK 6,为此服务部署的初始化工作十分繁琐。
Docker 是容器技术的一种,事实上已经成为业界公认的容器标准,要理解 Docker 的工作原理首先得知道什么是容器。
容器翻译自英文的 Container 一词,而 Container 又可以翻译成集装箱。我们都知道,集装箱的作用就是,在港口把货物用集装箱封装起来,然后经过货轮从海上运输到另一个港口,再在港口卸载后通过大货车运送到目的地。这样的话,货物在世界的任何地方流转时,都是在集装箱里封装好的,不需要根据是在货轮上还是大货车上而对货物进行重新装配。同样,在软件的世界里,容器也起到了相同的作用,只不过它封装的是软件的运行环境。容器的本质就是 Linux 操作系统里的进程,但与操作系统中运行的一般进程不同的是,容器通过Cgroups这两种机制,可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至是自己的用户 ID 空间,这样的话容器里的进程就像是运行在宿主机上的另外一个单独的操作系统内,从而实现与宿主机操作系统里运行的其他进程隔离。
Docker 也是基于 Linux 内核的 Cgroups、Namespace 机制来实现进程的封装和隔离的,那么 Docker 为何能把容器技术推向一个新的高度呢?这就要从 Docker 在容器技术上的一项创新 Docker 镜像说起。虽然容器解决了应用程序运行时隔离的问题,但是要想实现应用能够从一台机器迁移到另外一台机器上还能正常运行,就必须保证另外一台机器上的操作系统是一致的,而且应用程序依赖的各种环境也必须是一致的。Docker 镜像恰恰就解决了这个痛点,具体来讲,就是Docker 镜像不光可以打包应用程序本身,而且还可以打包应用程序的所有依赖,甚至可以包含整个操作系统Docker 镜像不光可以打包应用程序本身,而且还可以打包应用程序的所有依赖,甚至可以包含整个操作系统Docker 镜像解决了 DevOps 中微服务运行的环境难以在本地环境、测试环境以及线上环境保持一致的难题。如此一来,开发就可以把在本地环境中运行测试通过的代码,以及依赖的软件和操作系统本身打包成一个镜像,然后自动部署在测试环境中进行测试,测试通过后再自动发布到线上环境上去,整个开发、测试和发布的流程就打通了。
网友评论