1. 镜像
镜像是什么呢?通俗地讲,它是一个只读的文件和文件夹组合。它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础。所以你想启动一个容器,那首先必须要有一个镜像。镜像是 Docker 容器启动的先决条件。
简单来讲,镜像是一个特殊的文件系统,它提供了容器运行时所需的程序、软件库、资源、配置等静态数据。即镜像不包含任何动态数据,镜像内容在构建后不会被改变。
如果你想要使用一个镜像,你可以用这两种方式:
- 自己创建镜像。通常情况下,一个镜像是基于一个基础镜像构建的,你可以在基础镜像上添加一些用户自定义的内容。例如你可以基于centos镜像制作你自己的业务镜像,首先安装nginx服务,然后部署你的应用程序,最后做一些自定义配置,这样一个业务镜像就做好了。
- 从功能镜像仓库拉取别人制作好的镜像。一些常用的软件或者系统都会有官方已经制作好的镜像,例如nginx、ubuntu、centos、mysql等,你可以到 Docker Hub 搜索并下载它们。
镜像的实现原理:
镜像是由一系列的镜像层(layer )组成,每一层代表了镜像构建过程中的一次提交,当我们需要修改镜像内的某个文件时,只需要在当前镜像层的基础上新建一个镜像层,并且只存放修改过的文件内容。分层结构使得镜像间共享镜像层变得非常简单和方便。
更具体的讲解见:Docker镜像实现原理
2. 容器
2.1 容器的概念
镜像和容器的关系,就像是面向对象程序设计中的类
和实例
一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的本质是一个进程,但与直接在宿主执行的进程不同,容器进程有自己独立的命名空间隔离和资源限制。因此容器可以拥有自己的root
文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。在容器内部,无法看到主机上的进程、环境变量、网络等信息,这是容器与直接运行在主机上进程的本质区别。
容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。
通俗地讲,容器是镜像的运行实体。
- 容器是基于镜像创建的可运行实例,并且单独存在,一个镜像可以创建出多个容器。运行容器化环境时,实际上是在容器内部创建该文件系统的读写副本。 这将添加一个容器层,该层允许修改镜像的整个副本。
- 容器文件系统的本质是在镜像层上面创建的读写层,运行中的容器对任何文件的修改都存在于该读写层,当容器被删除时,容器中的读写层也会随之消失。
- 容器的文件系统设计成写时复制,而不是每一个容器都单独拷贝一份镜像文件,写时复制是指在容器中,只有需要修改某个文件时,才会把文件从镜像层复制到容器层,写时复制的操作将会复制整个文件,如果文件过大,将会大大降低文件系统的性能,因此当我们有大量文件需要被修改时,可能会出现明显的延迟。好在,写时复制操作只在第一次修改文件时触发,对日常使用没有太大影响。当文件或目录被删除时,并不会真正从镜像中删除它,因为镜像层是只读的,会创建一个特殊的文件或文件夹,这种特殊的文件或文件夹会阻止容器的访问。
这里不理解的话可以查看docker联合文件系统的内容:Docker底层:联合文件系统 。
如图 1 所示。
为什么要设置成写时复制呢?
每个容器单独拷贝一份镜像文件会占用较多的磁盘空间。假设我有3个程序都用到了jdk镜像,那该jdk镜像就得复制3份。如果改成写时复制的话,只需要一份jdk镜像,新的内容写在顶层即可。比如用docker images查看镜像的时候,把所有镜像的SIZE加起来可能有十几个G,但是到目录下查看,实际上只有几个G,这是因为很多镜像复用了同一个底层的镜像,起到了节省磁盘空间的作用。
一方面保护镜像文件,另一方面可以共享相同的镜像层。
2.2 容器的生命周期
容器的生命周期是容器可能处于的状态,容器的生命周期分为 5 种。
created:初建状态
running:运行状态
stopped:停止状态
paused:暂停状态
deleted:删除状态
各生命周期之前的转换关系如图所示:
3. 仓库
仓库(Repository)是存储和分发 Docker 镜像的地方。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。以 Ubuntu 镜像 为例,ubuntu 是仓库的名字,其内包含有不同的版本标签,如,16.04, 18.04。我们可以通过 ubuntu:16.04,或者 ubuntu:18.04 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 ubuntu,那将视为 ubuntu:latest。仓库名经常以 两段式路径 形式出现,比如 jwilder/nginx-proxy,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
镜像仓库分为公共镜像仓库和私有镜像仓库。
- 公共镜像仓库一般是 Docker 官方或者其他第三方组织(阿里云,腾讯云,网易云等)提供的,允许所有人注册和使用的镜像仓库。使用docker pull命令可以从仓库拉取镜像,Docker 默认会从 docker.io 拉取镜像。使用docker login命令登录镜像服务器后,可以使用docker push命令推送镜像到自己创建的仓库中。
- 除了公开镜像仓库,你也可以构建自己的私有镜像仓库,搭建教程:搭建私有镜像仓库教程
注册服务器(Registry)和仓库(Repository)的区别:
注册服务器是存放仓库的实际服务器,而仓库则可以被理解为一个具体的项目或者目录;注册服务器可以包含很多个仓库,每个仓库又可以包含多个镜像。例如我的镜像地址为 docker.io/centos,docker.io 是注册服务器,centos 是仓库名。 它们之间的关系如图 1 所示。最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。注册服务器的地址(镜像源)可以在 /etc/docker/daemon.json 文件中查看与修改。
4. 镜像、容器、仓库,三者之间的联系
从图中可以看到,镜像是容器的基石,容器是由镜像创建的。一个镜像可以创建多个容器,容器是镜像运行的实体。仓库就非常好理解了,就是用来存放和分发镜像的。
本文源自:拉勾教育课程:由浅入深吃透 Docker,讲师:郭少 前 360 高级容器技术专家