You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

81 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 25 | 微服务为什么要容器化?
专栏前面的文章我主要给你讲解了微服务架构的基础组成以及在具体落地实践过程中的会遇到的问题和解决方案这些是掌握微服务架构最基础的知识。从今天开始我们将进一步深入微服务架构进阶的内容也就是微服务与容器、DevOps之间的关系。它们三个虽然分属于不同领域但却有着千丝万缕的关系可以说没有容器的普及就没有微服务架构的蓬勃发展也就没有DevOps今天的盛行其道。
之后我还会具体分析它们三者之间是如何紧密联系的,今天我们先来看微服务为什么要容器化。
## 微服务带来的问题
单体应用拆分成多个微服务后能够实现快速开发迭代但随之带来的问题是测试和运维部署的成本的提升。相信拆分微服务的利弊你早已耳熟能详我讲个具体的例子。微博业务早期就是一个大的单体Web应用在测试和运维的时候只需要把Web应用打成一个大的WAR包部署到Tomcat中去就行了。后来拆分成多个微服务之后有的业务需求需要同时修改多个微服务的代码这时候就有多个微服务都需要打包、测试和上线发布一个业务需求就需要同时测试多个微服务接口的功能上线发布多个系统给测试和运维的工作量增加了很多。这个时候就需要有办法能够减轻测试和运维的负担我在上一讲给出的解决方案是DevOps。
DevOps可以简单理解为开发和运维的结合服务的开发者不再只负责服务的代码开发还要负责服务的测试、上线发布甚至故障处理等全生命周期过程这样的话就把测试和运维从微服务拆分后所带来的复杂工作中解放出来。DevOps要求开发、测试和发布的流程必须自动化这就需要**保证开发人员将自己本地部署测试通过的代码和运行环境,能够复制到测试环境中去,测试通过后再复制到线上环境进行发布**。虽然这个过程看上去好像复制代码一样简单,但在现实时,本地环境、测试环境以及线上环境往往是隔离的,软件配置环境的差异也很大,这也导致了开发、测试和发布流程的割裂。
而且还有一个问题是拆分后的微服务相比原来大的单体应用更加灵活经常要根据实际的访问量情况做在线扩缩容而且通常会采用在公有云上创建的ECS来扩缩容。这又给微服务的运维带来另外一个挑战因为公有云上创建的ECS通常只包含了基本的操作系统环境微服务运行依赖的软件配置等需要运维再单独进行初始化工作因为不同的微服务的软件配置依赖不同比如Java服务依赖了JDK就需要在ECS上安装JDK而且可能不同的微服务依赖的JDK版本也不相同一般情况下新的业务可能依赖的版本比较新比如JDK 8而有些旧的业务可能依赖的版本还是JDK 6为此服务部署的初始化工作十分繁琐。
而容器技术的诞生恰恰解决了上面这两个问题为什么容器技术可以解决本地、测试、线上环境的隔离解决部署服务初始化繁琐的问题呢下面我就以业界公认的容器标准Docker为例来看看Docker是如何解决这两个问题的。
## 什么是Docker
Docker是容器技术的一种事实上已经成为业界公认的容器标准要理解Docker的工作原理首先得知道什么是容器。
容器翻译自英文的Container一词而Container又可以翻译成集装箱。我们都知道**集装箱的作用就是,在港口把货物用集装箱封装起来,然后经过货轮从海上运输到另一个港口,再在港口卸载后通过大货车运送到目的地。这样的话,货物在世界的任何地方流转时,都是在集装箱里封装好的,不需要根据是在货轮上还是大货车上而对货物进行重新装配**。同样在软件的世界里容器也起到了相同的作用只不过它封装的是软件的运行环境。容器的本质就是Linux操作系统里的进程但与操作系统中运行的一般进程不同的是容器通过[Namespace](https://en.wikipedia.org/wiki/Linux_namespaces)和[Cgroups](https://zh.wikipedia.org/wiki/Cgroups)这两种机制可以拥有自己的root文件系统、自己的网络配置、自己的进程空间甚至是自己的用户ID空间这样的话容器里的进程就像是运行在宿主机上的另外一个单独的操作系统内从而实现与宿主机操作系统里运行的其他进程隔离。
Docker也是基于Linux内核的Cgroups、Namespace机制来实现进程的封装和隔离的那么Docker为何能把容器技术推向一个新的高度呢这就要从Docker在容器技术上的一项创新Docker镜像说起。虽然容器解决了应用程序运行时隔离的问题但是要想实现应用能够从一台机器迁移到另外一台机器上还能正常运行就必须保证另外一台机器上的操作系统是一致的而且应用程序依赖的各种环境也必须是一致的。Docker镜像恰恰就解决了这个痛点具体来讲就是**Docker镜像不光可以打包应用程序本身而且还可以打包应用程序的所有依赖甚至可以包含整个操作系统**。这样的话你在你自己本机上运行通过的应用程序就可以使用Docker镜像把应用程序文件、所有依赖的软件以及操作系统本身都打包成一个镜像可以在任何一个安装了Docker软件的地方运行。
Docker镜像解决了DevOps中微服务运行的环境难以在本地环境、测试环境以及线上环境保持一致的难题。如此一来开发就可以把在本地环境中运行测试通过的代码以及依赖的软件和操作系统本身打包成一个镜像然后自动部署在测试环境中进行测试测试通过后再自动发布到线上环境上去整个开发、测试和发布的流程就打通了。
同时无论是使用内部物理机还是公有云的机器部署服务都可以利用Docker镜像把微服务运行环境封装起来从而屏蔽机器内部物理机和公有云机器运行环境的差异实现同等对待降低了运维的复杂度。
## 微服务容器化实践
Docker能帮助解决服务运行环境可迁移问题的关键就在于Docker镜像的使用上实际在使用Docker镜像的时候往往并不是把业务代码、依赖的软件环境以及操作系统本身直接都打包成一个镜像而是利用Docker镜像的**分层机制**在每一层通过编写Dockerfile文件来逐层打包镜像。这是因为虽然不同的微服务依赖的软件环境不同但是还是存在大大小小的相同之处因此在打包Docker镜像的时候可以分层设计、逐层复用这样的话可以减少每一层镜像文件的大小。
下面我就以微博的业务Docker镜像为例来实际讲解下生产环境中如何使用Docker镜像。正如下面这张图所描述的那样微博的Docker镜像大致分为四层。
* 基础环境层。这一层定义操作系统运行的版本、时区、语言、yum源、TERM等。
* 运行时环境层。这一层定义了业务代码的运行时环境比如Java代码的运行时环境JDK的版本。
* Web容器层。这一层定义了业务代码运行的容器的配置比如Tomcat容器的JVM参数。
* 业务代码层。这一层定义了实际的业务代码的版本比如是V4业务还是blossom业务。
![](https://static001.geekbang.org/resource/image/3d/7d/3df442f8c8eaec6184826028ad5a5f7d.png)
这样的话每一层的镜像都是在上一层镜像的基础上添加新的内容组成的以微博V4镜像为例V4业务的Dockerfile文件内容如下
```
FROM registry.intra.weibo.com/weibo_rd_content/tomcat_feed:jdk8.0.40_tomcat7.0.81_g1_dns
ADD confs /data1/confs/
ADD node_pool /data1/node_pool/
ADD authconfs /data1/authconfs/
ADD authkey.properties /data1/
ADD watchman.properties /data1/
ADD 200.sh /data1/weibo/bin/200.sh
ADD 503.sh /data1/weibo/bin/503.sh
ADD catalina.sh /data1/weibo/bin/catalina.sh
ADD server.xml /data1/weibo/conf/server.xml
ADD logging.properties /data1/weibo/conf/logging.properties
ADD ROOT /data1/weibo/webapps/ROOT/
RUN chmod +x /data1/weibo/bin/200.sh /data1/weibo/bin/503.sh /data1/weibo/bin/catalina.sh
WORKDIR /data1/weibo/bin
```
FROM代表了上一层镜像文件是“tomcat\_feed:jdk8.0.40\_tomcat7.0.81\_g1\_dns”从名字可以看出上一层镜像里包含了Java运行时环境JDK和Web容器Tomcat以及Tomcat的版本和JVM参数等ADD就是要在这层镜像里添加的文件 这里主要包含了业务的代码和配置等RUN代表这一层镜像启动时需要执行的命令WORKDIR代表了这一层镜像启动后的工作目录。这样的话就可以通过Dockerfile文件在上一层镜像的基础上完成这一层镜像的制作。
## 总结
今天我给你讲解了微服务拆分后相比于传统的单体应用所带来的两个问题一个是测试和发布工作量的提升另一个是在弹性扩缩容时不同微服务所要求的软件运行环境差异带来的机器初始化复杂度的提升而Docker利用Docker镜像对软件运行环境的完美封装正好解决了这两个问题。
正是因为Docker可以做到一处通过、到处运行所以对业务的价值极大解决了以前应用程序在开发环境、测试环境以及生产环境之间的移植难的问题极大提高了运维自动化的水平也为DevOps理念的流行和业务上云提供了基础。
可见容器化改造对微服务是十分必要的但Docker也不是“银弹”同样会产生新的复杂度问题比如引入Docker后旧的针对物理机的运维模式就无法适应了需要一种新的针对容器的运维模式。所以接下来我将分三期给你详细讲解微服务容器化后该如何运维。
## 思考题
Docker的概念乍一看与虚拟机有些类似你认为它们有什么不同之处吗分别适合什么应用场景
欢迎你在留言区写下自己的思考,与我一起讨论。