关注嘉为科技,获取运维新知
最近几年,几乎所有的大型互联网公司都在做两件同样的事情,一是重构前端框架,二是重构后端微服务。微服务很火,无论你用或者不用,它都将继续火下去,这是由日益庞大的系统的高可用需求决定的。
微服务注重单一职责的小型功能模块拆分,独立部署运维,模块间互相隔离,通过API交互来搭建系统。原本一个工程能搞定的事情,现在需要搭建很多工程;原本只需要部署运维一套应用,现在却需要部署运维很多套。于是,SpringBoot横空出世,解决了多工程的标准化规范和快速开发需求。
Docker之类的容器也应运而生,解决了多应用快速部署的运维需求。
今天我们的微服务实践之旅,介绍Docker的基本概念和入门应用。
Docker是一种容器技术,最开始是dotCloud公司的一个内部项目,开源后迅速火遍IT界,以至于dotCloud公司都改名为Docker,目前在国内外互联网公司都着有非常广泛的应用。
Docker有一个形象的比喻——集装箱,集装箱内部可以装任意类型的物资,而外部表现都是无差异的大型密封箱子,这样就解决了同一艘货轮同时运输食品和化学物品的难题,因为集装箱之间互不干扰。Docker就是承载不同软件服务及运行环境的集装箱。
Docker的LOGO是一条蓝鲸载着一堆集装箱,口号是Build, Ship, and Run Any App, Anywhere,这跟当年Java提出的Write Once, Run Anywhere有异曲同工之处。然而,Docker和Java是两种完全不一样的事物,没有人会把他们放在一起比较,Docker更多的会被拿来跟虚拟机做比较。
如果想通过虚拟机来运行Web应用,首先得给虚拟机分配CPU、内存等硬件资源,接着安装操作系统,这样才能让虚拟机运转起来,然后才能开始真正的应用部署。即便只是想在虚拟机上运行一个最简单的HelloWorld页面,也必须做前面这几件事情,而且装完后随随便便就是几个G的虚拟机文件。而用Docker来跑一个Web应用就简单多了,一行命令搞定,几乎不占任何多余的磁盘空间,毫秒级启动速度,效果跟虚拟机几乎完全一样。
上图是虚拟机(左)和Docker(右)的架构比较,可以看出它们之间主要的不同点在于中间层,虚拟机通过Hypervisor虚拟出多台客户机,应用最终运行在客户机的操作系统上;Docker则没有客户机的概念,直接运行在Docker引擎上,应用之间也是相互隔离的。可以这么说,Docker是轻量版的“虚拟机”,这种轻量给Docker带来了广阔的应用。
Docker起源于Linux操作系统,目前官方也开始支持较新版本的Windows操作系统,前提是需要开启Hyper-V。下面我们以CentOS为例来说明Docker的安装过程。
如果系统曾经装过Docker,那么先卸载掉旧版本,卸载脚本如下,不曾安装过则可忽略此步骤:
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine
然后开始安装依赖包,需保证操作系统可以访问互联网:
yum install -y yum-utils device-mapper-persistent-data lvm2
添加Docker的yum源,由于官方源指向国外仓库,下载速度可能会比较慢,国内镜像站会快很多,因此这里选用国内中科大的yum源,添加后刷新一下本地缓存:
yum-config-manager--add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
然后就可以用yum命令进行Docker安装了:
yum install docker-ce
docker-ce指的是社区版,对应的有docker-ee商业版。用yum命令安装Docker,默认会安装最新的Stable版本,也可以指定版本安装,一般采用默认最新稳定版就行。
安装完成后启动Docker:
systemctl start docker
运行hello-world镜像验证一下是否启动成功:
docker run hello-world
成功会看到Hello from Docker字样信息,其他信息在不同电脑上不完全一样:
如果需要让系统开机自动启动Docker,运行一下命令:
systemctl enable docker
Docker有三大组件,分别是镜像(Image)、容器(Container)和仓库(Registry),即使是上面最简单的hello-world测试例子,也存在这三大组件的影子。
镜像是容器的模板,容器是镜像的实例,一个镜像可以启动多个容器,它们之间的关系跟面向对象程序设计中类和实例的关系是相似的。在上面的例子中,hello-world是一个镜像,它本身是一个特殊的文件系统,里面包含运行hello-world所需要的程序、依赖库、配置信息、脚本等。通过docker run hello-world命令可以启动一个容器,容器启动后会按照镜像中定义的信息,顺序执行每一条命令。因此,根据同一个镜像文件启动的多个容器,它们之间的运行结果基本上是完全一样的,这样有效避免了类似在测试环境正常运行的功能到生产环境就出现Bug的“诡异事件”。
仓库是存放Docker镜像的地方,分为本地仓库和中央仓库。Docker镜像在制作完成后,可以看作是一个由多层文件组成的一个特殊程序包,可以在网络上进行分发,分发出去的镜像跟原始镜像完全一样,因此根据镜像创建的容器也是一样的。Docker提供了一个官方镜像仓库Docker Hub,上面已经包含非常多高质量的开源产品镜像,比如Nginx、IIS、Tomcat,也包括一些精简版的操作系统镜像,,比如centos、ubuntu等,这些镜像都可以拿来即用。当我们需要定制一个镜像时,往往是从这些高质量镜像中选用一个功能接近的,然后进行相应的改造,生成适用自身产品的镜像,用于产品的快速发布、上线。
当需要创建容器时,运行一下docker run [镜像名]:[版本号],Docker引擎会先在本地仓库搜索对应版本号的镜像,当本地找不到该镜像时,会根据配置的中央仓库地址到远程服务器上去搜,找到后下载到本地仓库。回头看一下前面docker run hello-world例子的输出截图,第一行是Unanle to find image ‘hello-world:latest’ locally,因为是第一次运行,所以本地还没有hello-world镜像;第二行开始从远程镜像仓库拉取镜像,最后才是启动容器,输出结果。再次运行docker run hello-world,会直接输出结果,不再有相关下载信息了。
中央仓库可以通过添加或修改在/etc/docker/daemon.json文件来指定,内容如下:
{ "registry-mirrors": ["https://registry.docker-cn.com"] }
现有装好Docker的Linux服务器一台,Java程序包ip.war一个,要求将这个ip.war部署并运行起来。
如果不用Docker的话,传统的部署方式我们需要做下面这些步骤:
安装JDK,并设置JAVA_HOME、PATH等环境变量;
安装Tomcat,并设置CATALINA_HOME等环境变量;
将ip.war放到Tomcat的webapps目录下,或者修改server.xml文件指向ip.war;
运行startup.sh脚本启动Tomcat。
传统方式我们就不动手做了。如果改用Docker来运行ip.war,上面的步骤统统可以省掉,只需要一行命令就够,如下:
docker run -v /data/package:/usr/local/tomcat/webapps -it --net=host tomcat:latest
说明一下,ip.war包里有一个/ip/IPServlet的API,用于获取客户端的IP地址。我本地的IP是192.168.128.1,服务器的IP是192.168.128.200。访问接口验证一下部署结果,没毛病。
注意,Linux服务器上可没有安装JDK和Tomcat,程序是在Docker容器里面运行的。
解释一下这行神奇的命令。
docker run tomcat:latest是命令主体,中间部分是参数列表。
-v /data/package:/usr/local/tomcat/webapps表示将Linux上的/data/package目录挂载到Docker容器里的/usr/local/tomcat/webapps目录下,这样当容器内读写/usr/local/tomcat/webapps目录时,实际上操作的是Linux上的/data/package目录,ip.war包就是放在这个目录下的。容器可以看作是一个无状态的黑盒,需要跟外界交互才能产生有效的结果,挂载目录就成了Docker黑盒与外界打交道的主要接口。
-net=host表示容器内直接共享主机的网络和端口,因此我们可以通过主机的IP和端口来访问实际上运行在容器中的服务。
-it指启用一个交互式的伪终端,国际惯例。
此外还有其他各种各样的参数,此处只是列出了运行Tomcat的最小化参数列表。
前面的例子已经给我们展示了Docker短小精悍的特点,这个特点可以让Docker在很多场合有用武之地,比如微服务就是Docker的主场之一。
在一个纯净的系统中,一行命令运行一个war包,意味着应用的迁移和水平扩展也能一行命令搞定,这对微服务来说简直就是雪中送炭。微服务架构讲究服务拆分和独立部署,程序包的粒度小、数量多,每个服务还要多副本、高可用,这用传统方式来运维的话工作量巨大,而用Docker来做就跟玩儿似的。服务挂了?没事,给我10秒钟,我再给你起5个!
Docker的轻量化,也使得同一台服务器上可以运行更多互不干扰的独立服务。微服务支持用异构语言来开发服务,想在一台Linux服务器上同时运行Java5、Java8、C#、Go、Python、PHP程序?没问题,Docker容器统统帮你实现,每种语言分别创建一个容器来运行,而且理论上都只需要一行命令就能让服务跑起来。
有一种程序跟Docker镜像很像,那就是iOS的App;有一种商店也跟Docker Hub类似,那就是AppStore。既然如此,未来何尝不可以通过Docker中央仓库来发布软件服务呢?事实上这正在发生。以后从网上下载一个软件,可能是exe,也可能是一个Docker镜像文件。将镜像load到电脑上的本地仓库,运行一条命令,一套或开源或商业的软件系统就运行起来了,所有复杂的操作、配置都已经封装在镜像里头,用户可以直接体验最终的结果。
似乎Docker强大的功能皆来源于镜像,然而,无论多么强大的镜像,最终都可以通过一个叫做Dockerfile的文件来构建。Dockerfile玄机几何,我们以后再介绍。
本文首发于微信公众号:嘉为科技,转载请注明出处。