Docker 入门知识(镜像篇)
使用Docker镜像和仓库
第二篇主要探讨Docker镜像:用来启动容器的构建基石。
- 什么是镜像
- 如何对镜像进行管理
- 如何修改镜像
- 如何创建、储存和共享自己创建的镜像
- 介绍用来存储镜像的仓库和用来存储仓库的Registry
2.1 什么是Docker镜像 (抄的)
Docker 镜像是由文件系统叠加而成(是一种文件的存储形式)。最底端是一个文件引 导系统,即 bootfs,这很像典型的 Linux/Unix 的引导文件系统。Docker 用户几乎永远不会和 引导系统有什么交互。实际上,当一个容器启动后,它将会被移动到内存中,而引导文件系统则会被卸载(unmount),以留出更多的内存供initrd磁盘镜像使用。
到目前为止,docker看起来还很像一个典型的linux虚拟化栈。实际上,docker镜像的第二层是root文件系统rootfs,它位于引导文件系统之上.rootfs可以是一种或多种操作系统(如Debian或者Ubuntu文件系统)
。
在传统的linux引导过程中,root文件系统会最先以只读的方式加载,当引导结束并完成了完整性检查之后,它才会被切换为读写模式,但是在docker里,root文件系统永远只能是只读状态,而且docker利用联合加载(union mount,http://cn.wikipedia.org/wiki/Union_mount)技术又会在root文件系统层上加载更多的只读文件系统。联合加载指的是一次同时加载多个文件系统,但是在外面看起来只能看到一个文件系统,联合加载会将各文件系统叠加在一起,这样最终的文件系统会包含所有底层的文件和目录。
docker将这样的文件系统成为镜像,一个镜像可以放在另一个镜像的顶部。位于下面的镜像成为父镜像。最底层的镜像称为基础镜像。最后,当从一个镜像启动容器时,docker会在该镜像的最顶层加载一个读写文件系统。我们想在docker中运行的程序就是在这个读写层中执行的
。
听上去比较迷惑,到时候让马佬解释吧,我也不太懂liunx。

当docker第一次启动一个容器时,初始的读写层是空的,当文件系统发生变化时,这些变化都会应用到这一层上。比如,如果想修改一个文件,这个文件首先会从该读写层下面的只读层复制到该读写层。该文件的只读版本依然存在,但是已经被读写层中的该文件副本所隐藏。
通常这种机制被称为写时复制。每个只读镜像层都是只读的,并且以后永远不会变化。当创建一个新容器时,docker会构建一个镜像栈,并在栈的最顶端添加一个读写层。这个读写层再加上其下面的镜像层以及一些配置数据,就构成了一个容器。容器是可以修改的,并且是可以启动和停止的。容器的这种特点加上镜像分层框架,使我们可以快速构建镜像并运行包含我们自己的应用程序和服务的容器。
2.2 列出镜像
使用 docker images 命令列出 Docker 镜像列表。

为了区分同一个仓库下的不同镜像,Docker 使用了一种称为标签 (tag) 的功能。每个镜像在列出来时都带有一个标签,如 3.6,latest,alpine 等等。这种机制使得在同一个仓库中可以存储多个镜像。
如果仔细观察,就会发现上图有xxxx/xxxxx的镜像名称。这是在构建容器时指定仓库的标签。说到这个,就不得不先说一下Docker Hub
Docker Hub 中有两种类型的仓库:用户仓库 (user repository) 和顶层仓库 (top-level repository)
。用户仓库的镜像都是由 Docker 的用户创建。而顶层仓库是由 Docker 内部的人来管理的。
用户仓库的命名由用户名和仓库名两部分组成,如 hemingway2003/php_fpm_mysql_plugin
用户名:hemingway2003
仓库名:php_fpm_mysql_plugin
与之相对,顶层仓库只包含仓库部分,如 golang 仓库。顶层仓库由 Docker 公司和能够提供优质基础镜像的厂商管理,用户可以基于这些基础镜像构建自己的镜像。同时顶层仓库也代表了各厂商和 Docker 公司的一种承诺,即顶层仓库中的镜像是架构良好、安全并且是最新的。
2.3 拉取镜像
可以使用 docker pull 将镜像拉取到本地。
虽然在执行 docker run 的时候会根据情况自动下载对应镜像并直接启动容器。但是并不推荐。
2.4 查找镜像
可以通过 docker search 命令来查找所有 Docker Hub 上公共的可用镜像

注:也可以在 Docker Hub 网站上在线查找可用镜像。
该命令会返回如下信息:
- NAME:仓库的名称
- DESCIRPTION:镜像描述
- STARS:用户评价。反映出这个镜像的受欢迎程序
- OFFICIAL:是否官方。由上游开发者管理的镜像(如fedora镜像由Fedora团队管理)
- AUTOMATED:自动构建。表示这个镜像是由Docker Hub的自动构建(Automated Build)流程创建的
2.5 ***构建镜像
接下来是有关修改,更新,管理这些镜像。
构建Docker 镜像有以下两种方法
- 使用 docker commit 命令
- *使用 docker build 命令和 Dockerfile 文件 (推荐)
本章会着重讲解使用 Dockerfile 来构建 Docker 镜像。在此之前,先介绍下 docker commit 命令。
2.5.1 创建 Docker Hub 账号
构建镜像的很重要一环就是如何共享和发布镜像。我们可以选择将镜像推送到 Docker Hub 或者用户私有的 Registry 中。首先,我们需要在 https://hub.docker.com/
注册一个账号。
注册完毕之后,我们使用 docker login
命令来查看一下

这条命令会完成登陆到 Docker Hub 的工作,并将认证信息存储起来以供后面使用。亦可以使用命令 docker logout
执行登出功能
2.5.2 使用 Docker Commit 功能创建镜像
创建 Docker 镜像的第一种方式是使用 docker commit
命令。有点像修改代码一样的感觉。
1. 首先,要创建一个新的容器

任意容器都可以。也可以自行按照上一章创建一个。
- 设置定制容器
docker commit 1f180e5111f7 a1429950957/xiong
本命令需要两个必填项1f180e5111f7
部分为刚刚创建容器的ID ,a1429950957/xiong
为一个目标用户和仓库名称我们亦可以在 docker commit 命令中添加新的选项,来完成更多的操作:docker commit -m "a new malao_icu" -a "xiong" 1f180e5111f7 a1429950597/xiong
上述命令中,-m 表示镜像的提交信息,-a 是上传作者信息
2.5.2 使用 Dockerfile 来构建镜像
推荐使用 Dockerfile 的定义文件和 docker build
命令来构建镜像。推荐使用 Dockerfile 文件来代替 docker commit
指令的原因是通过前者构建镜像的话更具备可重复性、透明性以及幕等性(Idempotence)
一旦有了 Dockerfile ,我们就可以使用 docker build
命令来构建一个新的镜像
*其实在 2.5.1 中 malao/icu 就是一个使用 Dockerfile 的例子。只不过是将这个镜像作为基本镜像使用。接下来来看一下Dockerfile是如何写的。

该 Dockerfile 由一系列指令和参数组成。每条指令,如 FROM ,都必须为大写字母,且后面要跟随一个参数。Dockerfile 中的指令会按照从上到下的顺序依次执行,所以要合理安排指令的顺序。
每条指令都会创建一个新的镜像层并对镜像进行提交。Docker 大体上按照如下顺序来执行 Dockerfile 中的指令。
- Docker 从基础镜像中运行一个容器
- 执行一条指令,对容器进行修改
- 执行类似 docker commit 的操作,提交一个新的镜像层
- Docker 再基于刚提交的镜像运行一个新容器
- 执行下一条指令,直到所有指令执行完毕注:Dockfile也支持注释,注意以#开头
每个Dockerfile的第一条指令必须是FROM,该指令指定了一个已经存在的镜像,后续指令都将基于该镜像进行,这个镜像备被称为基础镜像 (base image)。
2.5.3 基于 Dockerfile 构建新镜像
执行 docker build 命令时,Dockerfile 中的所有指令都会被执行并且提交,并且在该命令成功结束之后返回一个新的镜像。
docker build -t ="a1429950957/xiong:v2" .
- -t:新镜像的名称
- .:表示Dockerfile文件的路径,如果处于当前目录的时候,用 . 表示
此时,如果没有错误的话,镜像应该已经成功运行了。
我们使用 docker run -d -p 8090:8090 --name malao_nb a1429950957/xiong:v2"
来检查一下是否成功。
在这里使用了一个新的标志 -p ,该标志用来控制 Docker 在运行时应该公开哪些网络接口给外部。运行一个容器时,Docker可以通过两种方法来在宿主机上分配端口:
- Docker可以在宿主机上随机选择一个位于32768~61000的一个比较大的端口号来映射到容器中的8090端口上(如果 将
-p 8090:8090
改变成为-p 8090
那么就会随机映射) - 可以在Docker宿主机中指定一个具体的端口号来映射到容器的8090端口上(上面的演示案例,大部分时间,我都建议这么使用)注:其实可以在端口前添加IP地址绑定到特定的网络接口
-p 127.0.0.2:8090:8090
还可以有很多变种-p 127.0.0.3:8090
Docker 还提供了一个 -P 的 参数,该参数可以用来对外公开在Dockerfile中通过EXPOSE指令公开的所有端口
2.5.4 Dockerfile 指令
具体的官网文档:https://docs.docker.com/engine/reference/builder/(英文) 有时间让马佬翻译补充
下面是个大概解释:

2.6 将镜像推送到 Docker Hub
提交到docker hub
docker push a1429950957/xiong
使用这条命令来提交我们刚创建的 docker 镜像 如果镜像名称后不添加 tag 的话。默认上传 latest 。
注释:在马佬的文章中,要先行创建一个xiong 的 Repository 的仓库,但是如果在上传镜像时候没有对应仓库,Docker Hub 会根据上传信息自行创建一个公开仓库。所以还是推荐提前创建好仓库。我懒了。
2.6.1 自动构建 (Automated Builds)
- 除了从命令行构建和推送镜像,*Docker Hub还允许我们定义自动构建(Automated Builds)*
- 实现方式:
- 为了使用自动构建,我们只需要将Github或BitBucket中含有Dockerfile文件的仓库连接到Docker Hub即可
- 向Github或BitBucket仓库推送代码时,将会触发一次镜像构建活动并创建一个新镜像
- 在之前该工作机制被称为可信构建(Trusted Build)
- 备注:自动构建同样支持私有Github和BitBucket仓库在 Docker Hub 中添加自动构建的任务第一步是设置 GitHub 或者 BitBucket 账号连接到 Docker Hub 。在Docker Hub 镜像页面会看到有个 Builds 页面

然后抽空我让马佬尝试一下。
2.7 删除镜像
删除镜像跟删除容器的操作差不多。
docker rmi xxxxx
多个用空格隔开
甚至可以删除全部镜像
docker rmi 'docker images -a -q'