笔者写过的和 Docker 容器技术的一些其他系列文章:
- Docker 实战教程之从入门到提高 (一)
- Docker 实战教程之从入门到提高 (二)
- Docker 实战教程之从入门到提高 (三)
- Docker 实战教程之从入门到提高 (四)
- Docker 实战教程之从入门到提高 (五)
- Docker 实战教程之从入门到提高 (六)
- Docker 实战教程之从入门到提高 (七)
- Docker 实战教程之从入门到提高 (八)
- Docker 技术三大要点:cgroup, namespace 和 unionFS, 从理论到实践
在我们探讨容器和容器镜像的区别之前,对于容器技术的初学者来说,有必要先去了解,为什么从事云原生开发需要学习容器技术?
随着企业数字化进程的不断深入发展,云原生技术成为业界一个炙手可热的话题。所谓云原生,以容器、微服务、DevOps 等技术为基础,提供对应用进行分布式部署和统一管理的平台和体系。云原生是一系列思想和工具的集合。从其概念定义可看出,容器技术是云原生的基石。在云原生领域工作的开发人员,如果不了解和掌握容器技术,好比试图在浮沙堤上修建高楼大厦一般。
什么是容器
在介绍为什么我们需要容器之前,请大家先回忆一下自己的第一个 Hello World 程序的诞生史。在我们费尽千辛万苦,解决各种本地开发工具配置(编译器,环境变量等等)的问题之后,终于看到了 Hello World 的输出,那份喜悦之情不言而喻,但我们也绝不会忘记在遇到各种各样的环境配置问题时的沮丧和无助。
笔者在基于本地部署(On-Premises)的企业级管理软件领域工作十余年,遇到过一些同样版本的应用程序,在开发机和测试机上能够正常运行,部署到生产环境之后出现故障的棘手问题,最后经过排查,这些问题都是因为运行环境的差异所导致。
而容器技术作为一种轻量级、可移植和自包含的软件打包技术,使应用程序可以在几乎任何地方以相同的方式运行。采用容器技术,开发人员在自己本地开发环境创建并测试好的容器,无需任何修改就能够在生产系统的虚拟机、物理服务器或公有云主机上运行,从而能够彻底避免软件因为运行环境的差异而可能出现的各种运行问题。
初学者理解容器技术,可以从容器一词的字面含义入手。想象一下现实生活中的集装箱。我们要把一批货物从一个地方运输到另一个地方,只需要把货物按照标准规定打包放入集装箱内,集装箱与集装箱之间不会相互影响,集装箱也并不关心箱内到底装的是水果,衣物还是电子产品,所以这些货物的打包方式都遵循标准化规定。这些集装箱打包好之后,可以用通用的运输工具,比如汽车和货轮等进行运输。到了目的地之后,再用标准化的方式打开集装箱,取出里面的货物即可使用。
很多刚刚接触容器技术的朋友,不容易弄清楚容器,容器镜像和 Docker 这几个词的区别和联系。
我们首先来看容器和容器镜像。举个例子,执行命令行 docker search nginx
,搜索结果的一条条记录就是一个个容器镜像。
所谓镜像,就是一个静态概念,一个镜像由若干只读层(read-only layer)构成。
上图左边是 Docker
镜像的内部实现细节,我们能看到多个只读层叠加在一起,层与层之间通过指针关联,这些层能够在运行 Docker 的宿主机的文件系统上访问到。
Linux 的统一文件系统(union file system)技术将这些叠加的只读层合并成一个文件系统,该系统为这些只读层提供了一个统一的视角,从而为 Docker 的用户隐藏了多层的存在。
从 Docker 用户的视角出发,一个 Docker 镜像只存在一个文件系统,即上图右边所示。
这些文件系统的设计是 Docker 实现细节,一般情况下我们不用去深究。但如果您足够好奇,使用命令 sudo tree
浏览目录 /var/lib/docker
即可:
比如我用命令 docker images
浏览下载到本地的 docker 镜像:
其中一个叫 jerry-nginx 的镜像,是一个 web 应用,它的所有内容能在 /var/lib/docker
目录下的这个目录查到:
讲完了容器镜像,我们再来看容器。
容器和容器镜像一样,也是若干层的叠加,唯一区别是所有只读层的最上面一层,是一层可读可写层,如上图绿色图例所示。
初学者可以记住这个简单的公式:容器 = 容器镜像 + 可读可写层
我们如果用命令 docker ps --all
查看本机所有容器列表,会发现有的容器处于运行状态,有的处于退出状态。
因此,一个处于运行状态的容器(running container)包含一个可读写的文件系统加上隔离的进程空间。
容器里的进程可以对这个可读写文件系统内的文件进行修改、删除、创建等操作。
镜像里每一层其实都能在 docker 文件夹的 containers 子目录下找到:
上图每一个红色文件夹代表镜像里的一层,蓝色文件包含了该层运行时的日志文件,或者网络相关配置等。
做个实验:
ubuntu 这个容器执行结束后,使用 find / -name i042416.txt
文件,这说明 docker 运行时能对宿主机的文件系统进行写操作。
下面分析几个常用的易混淆的命令。
docker create
先看它的帮助文档:
试着执行以下:
产生一个输出 id:
7ee10851c3f1e53bbd35e5f196f34de560afa1a20d9bf1ced587630dbcda877b
create 创建的容器,状态变为 created:
docker create 命令给通过命令行传入的容器镜像创建了一个新的可读可写层,从而生成了一个新的容器实例:
然后再执行 docker start
,输入 docker create
创建的容器实例 ID,就可以启动这个容器实例了。
而 docker run
其实就是 docker create
和 docker start
这两个命令合二为一的版本。
总结
本文首先将容器这个对初学者来说比较陌生的概念,类比成现实世界中的集装箱,让读者对容器概念有一个直观的理解,接着阐述了容器和容器镜像的区别。
希望这篇文章能帮助大家理解容器和容器镜像的区别。
笔者写过的和 Docker 容器技术的一些其他系列文章:
- Docker 实战教程之从入门到提高 (一)
- Docker 实战教程之从入门到提高 (二)
- Docker 实战教程之从入门到提高 (三)
- Docker 实战教程之从入门到提高 (四)
- Docker 实战教程之从入门到提高 (五)
- Docker 实战教程之从入门到提高 (六)
- Docker 实战教程之从入门到提高 (七)
- Docker 实战教程之从入门到提高 (八)
- Docker 技术三大要点:cgroup, namespace 和 unionFS, 从理论到实践