从容器构建镜像
- 步骤:
- 根据最终想要的东西包选择一个已存在的镜像来创建一个容器。
- 修改这个容器的文件系统。这些改动会被保存在容器的联合文件系统的新文件层
- 改动完成后将这些改动提交(commit)。一旦改动被提交,你就能够从新镜像创建新的容器了。
// 这条命令会启动一个运行bash shell的新容器。基于这个shell,你就能够输入命令来自定义你的容器了
docker run -it -name image-dev ubuntu:latest /bin/bash
docker commit old_container_name new_container_name
- 对容器内文件系统的改动都会被写入到新的文件层中,这个文件层归创建它的容器所有。(联合文件系统(UFS)挂载提供了容器的文件系统)
- Commit创建新镜像:使用-a选项为新镜像指定作者的信息,使用-m选项,设置关于提交的信息
- 启动原始容器时附带的命令会被提交到新镜像中,入口点(entrypoint)是更好的做法
- 一个入口点就是一个程序,它会在容器启动时被执行。如果入口点没有被设置,那么默认的命令会被直接执行。如果入口点被设置,那么默认的命令和它的参数就会作为参数传递给入口点。
- diff,它能够显示容器中文件系统的所有改动。这些改动包括添加、修改、删除文件和目录:
- A开头的行表示文件被添加。以C开头表示修改,以D开头表示删除。
深入联合文件系统
- 联合文件系统由多个层组成。每当对联合文件系统改动一次,改动会被记录到一个新的层中,这个新层放置于所有层的最上面。容器(和用户)访问文件系统所看到的,就是所有这些层的“联合”,或者说是自上而下的观察角度。
- 当你从联合文件系统读取一个文件时,系统会从存在该文件的、最上面的一层中读取。如果文件没有在最顶层被创建或者改动,那么读取操作就会沿着层不断向下找,直到找到存在这个文件的层。
- 文件修改和删除也通过修改最顶层来工作的。当一个文件被删除,一个删除记录就被写入到最顶层,它遮挡了底层该文件的所有版本。当一个文件被修改,修改也被写入到最顶层,它也同样遮挡了底层所有该文件的版本。联合文件系统实际上上会在最顶层添加一个文件来标记一个文件被删除。原始的文件和任何文件副本依旧保留在镜像的其他层中。
- 当只读层(read-only layer)上一个文件被修改了,那么这整个文件会在改动发生之前被复制到最上面的可写层(writable layer)。这对运行时性能和镜像的大小会有负面影响
- 层包含这一层的改动信息和元数据。当你向容器的文件系统提交容器的改动时,你也是以同样的方式保存了最顶层的一个副本。
- 当你提交一层时,一个新的ID会为这一层创建,所有文件改动的副本都会被保存,新层的元数据包含了之前生成的ID,还有更低一层的层ID(父层),还有新层被创建时的执行上下文(execution context)。
- 层ID和元数据形成了一个图,Docker和联合文件系统(UFS)使用这个图来构造镜像。从某些起始层开始,遍历寻找到它们的依赖层,然后这些层以栈的形式构造成镜像。
- 如果你想要复制一个镜像,那么你只需要从现有的镜像创建一个新的标签或仓库。你可以使用docker tag命令来完成。
- 创建容器会创建一个可写层,所有在可写层下面的层都是不可变的,
- 联合文件系统可能有一个层数量的限制。这个限制取决于文件系统,但42层限制在使用AUFS系统的计算机上是非常常见的。
- docker history命令来查看一个镜像的所有层。输出内容包含以下内容:
- 缩写的层ID
- 层的年龄
- 创建容器时的初始命令
-这一层的全部文件大小
- 层系统使其可以返回到某个镜像历史节点,并且使创建新分支变得更加简单方便。使用层系统来创建分支,这能够同时解决镜像大小和层增长问题。(每当你从同一个镜像创建一个容器,你就潜在地创建了一个新的分支)
- socker export命令会将扁平的联合文件系统的所有内容导出到标准输出或者一个压缩文件上。输出信息包含了所有从容器角度能够观察到的文件。(如果你需要在容器上下文外使用镜像中的文件系统,这是非常有帮助的。)也可以使用 docker cp 命令来完成这个目标(但如果你想要多个文件,导出整个文件系统可能是更直接的办法)
- docker import命令会将压缩格式的内容导入到一个新镜像中。import命令能够识别多种压缩或未压缩的压缩文件格式。