Docker 是 用Golang 编写的, 自 2013 年推出以来,受到越来越多的开发者的关注。不管是云服务还是微服务(Microservices),越来越多的厂商都开始基于 Docker 作为基础设施自动化的工具。接下来让我们一起来了解Docker吧。
Docker 是开源的应用容器引擎,可以将所有应用软件以及它的依赖打包成软件开发的标准化单元。
Docker 容器将软件以及它运行安装所需的一切文件(代码、运行时、系统工具、系统库)打包到一起,这就保证了不管是在什么样的运行环境,总是能以相同的方式运行。就好像 Java 虚拟机一样,“一次编写,到处运行(Write once, run anywhere)”,而 Docker 是“一次构建,到处运行(Build once,run anywhere)”。
Docker 是一种“容器即服务”(Docker Containers as a Service ,简称 CaaS),使得开发和IT运营团队可以对于应用的构建、发布、运行更加敏捷和可控。
概括的说: Docker 是为开发人员和系统管理员用于构建、发布、并运行分布式应用程序的开放式平台。该平台由 Docker 引擎(一个便携、轻巧的运行时和打包工具) 和 Docker Hub (一个共享应用程序和自动化工作流的云服务)等组成。Docker 可以使应用程序从组件迅速组装并消除了开发、质量保证和生产环境之间的摩擦问题。这样一来,IT部门可以更快地发布,而这些应用程序不管是运行在笔记本电脑、数据中心的虚拟机,还是任何的云,其运行过程和结果都是一致的。
我们再来看下 Docker 的 Logo 。很明显,这是一只鲸鱼,它托着许多集装箱。我们可以把宿主机可当做这只鲸鱼,把相互隔离的容器可看成集装箱,每个集装箱中都包含自己的应用程序。这 Logo 简直的太形象了!
镜像是一个包含操作系统完整 root 文件系统 的、只读的,由多层文件系统联合而成的打包文件。
Docker 为了让应用无感知的跑在容器中,提供了一套完整的 root 文件系统,比如官方镜像 library/ubuntu 就包含了一整套 root 文件系统。像 apache、nginx 都是基于该镜像构建的,由于 library/ubuntu 本身很大,所以 Docker 采用了分层存储的方式。
假装你已经安装了 Docker,上图通过 docker pull nginx 从 官方 Registry(下面会提到这是啥)拉取 nginx 镜像,拉取 nginx 相当于 library/nginx:latest,library 表示 nginx 是官方镜像,因此可以省略,:latest 表示拉取标签为 latest 的镜像。拉取后可以看到存在两个镜像,因为 nginx 镜像本身就是基于 library:ubuntu:16.04 镜像的。
上图通过 docker pull httpd 拉取了 apache 镜像,由于 ubuntu:16.04 镜像已经在本地存在了,因此拉取的时候不会重复拉取。从而节约拉取时间。这就是 Docker 分层存储的意义。
镜像的只读可以理解成以前的光盘 CD,是不可更改的。为了模拟实现对光盘 CD 的写的功能,会建立两层文件系统,一层是光盘 CD 的只读文件系统;另外一层是存放更改数据的可写的文件系统。从而实现模拟更改镜像的作用。Docker 也是采用这种类似的分层的方式。
从上图,可以看出 ubuntu:15.04 是由很多层文件系统(镜像)堆叠形成的,最底层是 root 文件系统(d3a1f33e8a5a)。这几层文件系统都被设置成只读的。多层文件系统利用了上面提到的 UnionFS、AUFS、OverlayFS,这是一类文件系统,这种联合挂载文件系统最早就是用于解决 CD 这种只读文件系统的修改问题,Docker 之前使用 AUFS,但是由于 AUFS 不被 linus 喜欢(被 linus 评价为稠密、不可读,无注释)导致 AUFS 一直没有被合并到 Linux 的主分支中。Docker 在 1.12 以后已经将默认的文件系统从 AUFS 替换成 OverlayFS2。因为 OverlayFS2 已经被合并进了 Linux 的主干分支中
上面我们拉取了 nginx 镜像到本地,我们可以使用 docker container start nginx(省略了 latest 标签)来运行这个镜像。运行之前会先创建一个容器(其实本质就是创建了一层可读写的文件系统,以提供程序运行时的读写支持),然后就会启动程序,让程序跑在一个隔离环境(不是虚拟环境)里。你还可以通过 docker container commit>来对当前层进行提交(就好像 Git 提交一样),从而形成一个新的镜像,但是这种方式是不推荐的;这是因为在程序运行过程中可能会产生一些垃圾文件,而如果这些垃圾文件被提交后,新的镜像又是不可修改的,只会增大镜像的体积。具体怎么创建镜像会在下面说到。
可以看到上图中在创建容器的时候其实就是创建了一个容器可读写层。你还可以通过 docker container stop 停止容器的运行,相当于 kill 掉容器内的正在运行的程序,但是创建容器时创建的可读写的文件系统依然存在。所以你依然可以通过 docker start 来重启程序。
镜像构建完成后,可以很容易的在宿主机器上运行,但是如果其他机器要使用这个镜像,我们就需要一个集中存储、分发镜像的服务,Docker Registry 就是这样的服务。一个 Docker Registry 可以包含多个仓库,每个仓库可以包含多个标签,每个标签对应一个镜像。
就拿上面的 library/nginx:latest 举例,library 表示这个镜像是官方镜像,如果不是官方镜像,这里一般填注册在 Docker Registry 的用户名;library/nginx 是仓库的名字,latest 是该仓库一个标签。
诚然,官方的 Docker Registry 是世界上最大的镜像分发服务,官方还提供了 Docker Registry 镜像 用于搭建私有镜像分发服务。而且 DockerHub 和社区一起制作了大量的、高质量的镜像,使得我们构建镜像更为方便。
过去: 发布项目时直接去线上服务器,拷贝一个tomcat,然后改端口号,然后部署应用到webapps文件夹下,重启就好。现在还有很多传统企业是这么做的。这么做的缺点很明显,应用之间相互影响。一个应用出现问题,CPU100%了,这个服务器上的其他应用一起凉凉。
现在: Docker容器可以将我们的应用程序打包封装到一个容器中,该容器包含了应用程序的代码、运行环境、依赖库、配置文件等必需的资源。容器之间达到进程级别的隔离,在容器中的操作,不会影响道宿主机和其他容器,这样就不会出现应用之间相互影响的情形!
docker容器可以实现开发、测试和生产环境的统一化和标准化。镜像作为标准的交付件,可在开发、测试和生产环境上以容器来运行,最终实现三套环境上的应用以及运行所依赖内容的完全一致。
过去: 规范的话,一个应用部署在一个虚拟机上!当时最大的体会就是一个,虚拟机非常重,构建速度慢,且占用资源多,一台物理机上只能起十来个虚拟机!
现在: 和虚拟机相比,容器仅需要封装应用和应用需要的依赖文件,实现轻量的应用运行环境,且拥有比虚拟机更高的硬件资源利用率。在微服务架构中,有些服务负载压力大,需要以集群部署,可能要部署几十台机器上,对于某些中小型公司来说,使用虚拟机,代价太大。如果用容器,同样的物理机则能支持上千个容器,对中小型公司来说,省钱!所有容器在一台机器上共享同一个操作系统内核,这样他们立即开始,并更有效地利用内存。Image(镜像) 是从分层文件系统的构建,这样他们能够共享公共文件,使得磁盘使用率和 Image (镜像) 的下载更加高效。
Docker 容器是基于开发的标准,允许容器运行在主流的 Linux 发布版和 Microsoft 操作系统作为所有的基础设施。
容器使得应用程序彼此隔离,而基础架构同时为应用程序提供了额外的保护层。
这样,我们可以专注于开发应用,其他的繁琐事交给 Docker 去做。
使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员通过 Dockerfile 进行镜像构建,结合持续集成系统进行集成测试,而运维人员则可以在生产环境中快速部署该镜像。甚至结合持续部署进行自动部署。
而且使用 Dockerfile 使镜像的构建透明化,不仅可以帮助开发人员理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
Microservices(微服务) 依赖于“基础设施自动化”,而 Docker 正是“基础设施自动化”的利器。可以说 Docker 的火爆,一定程度上也带动了微服务架构的兴起,而微服务的广泛应用也促进了 Docker 繁荣。可以说两者相辅相成。
Docker 和微服务架构简直就是浑然天成,站在 Docker 的角度,软件本质是容器的组合:业务逻辑容器、数据库容器、存储容器、队列容器……Docker 使得软件拆分成若干的标准化容器,然后像积木一样的搭建起来。这正是微服务的思想:软件把任务外包出去,让各种外部服务完成这些任务,软件本身只是底层服务的调用中心和组装层。