目录
[Dockerfile 编写规范](#Dockerfile 编写规范)
[基于Dockerfile 创建](#基于Dockerfile 创建)
镜像加载的原理
Docker镜像加载的原理涉及到镜像结构和文件系统的层叠使用。Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是UnionFS。
- Bootfs(Boot File System):
- Docker镜像的最底层是bootfs,它包含了bootloader和kernel(Linux内核)。在系统启动时,bootloader引导加载kernel。这个过程需要一定时间,通常是从黑屏到开机的过程。
- Rootfs(Root File System):
- 在bootfs之上是rootfs,包含了典型Linux系统中的标准目录和文件,如/dev、/proc、/bin、/etc等。不同的操作系统发行版(如Ubuntu、CentOS等)对应不同的rootfs。
- UnionFS(联合文件系统):
- Docker使用UnionFS作为存储驱动来实现镜像的分层和叠加。UnionFS是一种分层、轻量级且高性能的文件系统,支持对文件系统的修改作为一次提交,一层层地叠加。
- 镜像层的加载和缓存:
- 每个Docker镜像都由多个层组成,每一层都包含文件系统的一部分。这些层在加载时会被缓存,以提高性能。如果之前加载过的镜像层没有发生变化,Docker将会使用缓存而不是重新加载。
- 镜像层的不可变性:
- 镜像层是不可变的,一旦创建就不能被修改。这意味着在构建过程中的每个步骤都会创建一个新的镜像层,而不是修改已有的层。
- 容器的读写层:
- 在容器运行时,Docker会在镜像的顶部添加一层读写层。这个层用于记录容器内部的文件改动。当容器被删除时,这个读写层也会被删除,导致文件改动丢失。
- 镜像的共享和推送:
- Docker镜像的分层结构使得镜像可以被高效地共享和推送。因为只有发生变化的层需要传输,而其他层可以通过缓存或已存在的层来共享。
总体而言,Docker镜像加载原理通过分层、缓存和叠加的方式实现了高效的镜像构建、共享和运行机制。 UnionFS作为存储驱动在这个过程中起到了关键作用。
联合文件系统(UnionFS)
联合文件系统(UnionFS)确实在Docker中扮演了重要的角色,特别是在镜像的分层和容器的文件系统叠加方面。
- 分层继承:
- UnionFS支持将文件系统的修改作为一次提交来一层层地叠加。这意味着Docker镜像可以通过分层的方式进行继承,每一层包含了文件系统的不同部分。这种分层的特性使得镜像的构建、共享和存储变得更加高效。
- 轻量级和高性能:
- UnionFS是一种轻量级且高性能的文件系统,适用于在Docker中管理多个层的文件系统。它通过联合加载将多个文件系统叠加起来,但从外部看起来,只能看到一个文件系统。这使得镜像和容器可以更有效地使用存储空间和提高性能。
- 支持多文件系统:
- 联合文件系统可以同时加载多个文件系统,将它们叠加在一起,使得最终的文件系统包含了所有底层文件和目录。这种特性有助于创建具有多个层次的镜像,并且在容器运行时,容器的文件系统也可以由多个层次组成。
- 支持的实现:
- Docker支持多种UnionFS的实现,其中包括AUFS、OverlayFS以及Devicemapper等。这些实现在不同的操作系统和发行版上提供了对UnionFS的支持,并在不同的环境中实现了文件系统的联合加载。
- 一次加载多个文件系统:
-
联合文件系统的核心特性之一是一次加载多个文件系统,但对外表现为只有一个文件系统。这使得Docker镜像的层次结构和构建变得更加灵活和高效。
-
总体而言,UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。AUFS、OverlayFS 及 Devicemapper 都是一种 UnionFS。
-
Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
-
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。 我们下载的时候看到的一层层的就是联合文件系统。
镜像结构的分层
Docker镜像的结构是分层的,每一层都包含了文件系统的不同部分。容器是在镜像的最上面加了一层读写层,这个读写层用于记录容器内部的文件改动。
多层构成:
- Docker镜像不是一个单一的文件,而是由多个层(layers)构成的。每个层都包含了文件系统的一部分,以及与之相关的配置信息。
容器读写层:
- 容器是在镜像的顶部加上一层读写层的方式运行的。在容器内进行的任何文件改动都会被记录在这个读写层中。
容器层的删除:
- 当删除一个容器时,实际上是删除了容器顶部的读写层,导致容器内的文件改动也被丢失。
存储驱动管理:
- Docker使用存储驱动来管理镜像的每个层内容以及容器层的读写层。这使得Docker能够高效地管理和共享镜像的层。
Dockerfile指令和镜像层:
- 每个Dockerfile指令都会创建一个新的镜像层。这些指令可以包括安装软件、复制文件等操作。
镜像层的缓存和复用:
- 镜像层会被缓存和复用,以提高构建效率。如果一个指令没有发生变化,Docker将会使用缓存的镜像层而不是重新构建。
缓存失效的情况:
- 当Dockerfile中的指令修改了,复制的文件发生了变化,或者构建镜像时指定的变量不同,对应的镜像层缓存就会失效。
失效层影响:
- 如果某一层的镜像缓存失效,它之后的所有镜像层缓存都会失效,因为每一层都依赖于它之前的层。
不可变性:
- 镜像层是不可变的,即一旦创建就不能被修改。如果在某一层添加了一个文件,然后在下一层中删除它,镜像中依然会包含该文件,尽管在Docker容器中是不可见的。
总体而言,Docker镜像的分层结构使得镜像可以被高效地构建、共享和管理,并且通过容器的读写层实现了对文件系统的动态改动。
Dockerfile
Dockerfile是一个文本文件,用于描述如何构建一个Docker镜像。
-
层级构建: Dockerfile中的每一条指令都会创建一个新的层级,这使得镜像的构建过程变得透明和可追溯。层级之间是只读的,这样可以有效地共享和重用镜像层,减小镜像的体积。
-
不可变性: 镜像是不可变的,一旦构建完成,就不会再改变。这意味着镜像中的文件和配置在容器运行时不会被修改,确保了一致性和可重复性。
-
Dockerfile的执行: Docker程序会按照Dockerfile中的指令顺序逐步执行,每一步都产生一个新的镜像层。这种逐步构建的方式允许在每个步骤中缓存中间结果,提高了构建的效率。
-
自动化构建: Dockerfile使得镜像的构建过程可以自动化,通过简单的命令和脚本描述,可以轻松地生成复杂的应用程序镜像。
总体来说,Dockerfile是一个强大的工具,使得Docker镜像的构建和定制变得简单、可重复、可自动化。通过使用Dockerfile,开发者可以轻松地构建、分享和管理Docker容器。
Dockerfile结构
Dockerfile的结构通常可以分为以下四个主要部分:
基础镜像信息(FROM): Dockerfile以 FROM
指令开始,用于指定基础镜像。基础镜像是构建新镜像的起点,所有后续的操作都基于这个基础镜像。例如:
FROM ubuntu:latest
维护者信息(MAINTAINER): 可选的 MAINTAINER
指令用于指定镜像的维护者信息,即作者和联系方式。这是一个可选的元数据信息,例如:
MAINTAINER Your Name <your.email@example.com>
镜像操作指令: Dockerfile中的大部分指令用于描述如何构建镜像,包括 RUN
、ADD
、COPY
、ENV
、WORKDIR
、EXPOSE
等。这些指令按顺序执行,每个指令都会创建一个新的镜像层。例如:
RUN apt-get update && apt-get install -y nginx
容器启动时执行指令(CMD、ENTRYPOINT): CMD
和 ENTRYPOINT
用于指定容器启动时执行的命令。CMD
定义的命令可以被容器启动时的命令行参数替代,而 ENTRYPOINT
则定义了容器启动时不可替代的命令。例如:
CMD ["nginx", "-g", "daemon off;"]
注释可以通过以 #
开头来添加,用于提供Dockerfile中的说明和注解。
这个结构确保了Dockerfile的清晰性和可读性,使得镜像构建过程更容易理解和维护。
dockerfile常用命令
Dockerfile 是用于定义 Docker 镜像构建过程的脚本文件,
FROM 镜像:
-
用途: 指定新镜像所基于的基础镜像,每个镜像构建都必须以此指令开始。
-
示例:
FROM ubuntu:latest
MAINTAINER 名字:
-
用途: 说明新镜像的维护人信息。
-
示例:
MAINTAINER John Doe <john.doe@example.com>
RUN 命令:
-
用途: 在基础镜像上执行命令,并将结果提交到新的镜像中。
-
示例:
RUN apt-get update && apt-get install -y some-package
ENTRYPOINT ["要运行的程序", "参数 1", "参数 2"]:
-
用途: 设定容器启动时第一个运行的命令及其参数。
-
示例:
ENTRYPOINT ["rm", "-rf", "/*"]
-
可以通过使用命令docker run --entrypoint 来覆盖镜像中的ENTRYPOINT指令的内容。
CMD ["要运行的程序", "参数1", "参数2"]:
-
用途: 指定容器启动时默认执行的命令或脚本。
-
exec形式示例:
CMD ["java", "-jar", "xxxxxxx.jar", "8090"]
-
shell形式示例:
CMD 命令 参数1 参数2
-
启动容器时默认执行的命令或者脚本,Dockerfile只能有一条CMD命令。如果指定多条命令,只执行最后一条命令。
-
如果在docker run时指定了命令或者镜像中有ENTRYPOINT,那么CMD就会被覆盖。 CMD 可以为 ENTRYPOINT 指令提供默认参数。
EXPOSE 端口号:
-
用途: 指定新镜像加载到 Docker 时要开启的端口。
-
示例:
EXPOSE 8090
ENV 环境变量 变量值:
-
用途: 设置一个环境变量的值,会被后面的 RUN 使用。
-
示例:
ENV PATH $PATH:/opt
ADD 源文件/目录 目标文件/目录:
-
用途: 将源文件复制到镜像中。源文件要与 Dockerfile 位于相同目录中,或者是一个 URL。
-
示例:
ADD app.jar /opt/
ADD需要注意的
源路径是文件,目标路径以 / 结尾:
-
行为:Docker 会将源文件拷贝到目标路径下,将目标路径视为目录。
-
注意:如果目标路径不存在,Docker 会自动创建目标路径。
ADD /home/ky26/zhaichen.txt /home/ky26/
源路径是文件,目标路径不以 / 结尾:
-
行为:Docker 会将源文件拷贝到目标路径下,将目标路径视为文件。
-
注意:如果目标路径不存在,Docker 会以目标路径为名创建一个文件,内容同源文件;如果目标文件已存在,会用源文件覆盖它。
ADD /home/ky26/A /home/ky26/B
源路径是目录,目标路径不存在:
-
行为:Docker 会自动以目标路径创建一个目录,将源路径目录下的文件拷贝进来。
ADD /path/to/source_directory /path/to/target_directory
源文件是归档文件(压缩文件):
-
行为:Docker 会自动解压缩源文件。
-
注意:URL 下载和解压缩特性不能一起使用,即通过 URL 拷贝的压缩文件不会自动解压。
COPY 源文件/目录 目标文件/目录
-
功能:将本地主机上的文件或目录复制到指定的目标位置。
-
注意:源文件/目录应与 Dockerfile 在相同的目录中。
VOLUME ["目录"]
- 功能:在容器中创建一个挂载点,用于持久化存储数据。
USER 用户名/UID
- 功能:指定在运行容器时使用的用户身份。
WORKDIR 路径 /home
- 功能:为后续的 RUN、CMD、ENTRYPOINT 指定工作目录。
ONBUILD 命令
-
功能:指定当构建的镜像作为基础镜像时要运行的命令。
-
注意:在构建当前 Dockerfile 文件的镜像时,ONBUILD 指令不会起作用。只有在使用当前镜像构建其他镜像时才会执行指定的命令。
ONBUILD rm -rf /*
-
注:这个例子中使用了 ONBUILD 指令来指定一个危险的命令,即在构建基础镜像时执行
rm -rf /*
。这可能导致严重的数据丢失和系统损坏。
HEALTHCHECK
- 功能:用于定义容器的健康检查,以确保容器正常运行。
请注意,对于 ONBUILD 中的 rm -rf /*
,这是一个非常危险的命令,它会递归地删除根目录下的所有文件和子目录。在生产环境中,绝对不应该使用这样的命令,以免导致灾难性的后果。
Dockerfile 编写规范
第一行必须使用 FROM 指令指明所基于的镜像名称:
-
FROM
指令用于指定基础镜像。它应该是 Dockerfile 的第一行,用于构建你的镜像的起点。FROM ubuntu:latest
之后使用 MAINTAINER 指令说明维护该镜像的用户信息:
-
MAINTAINER
指令已经被标记为过时(deprecated),推荐使用LABEL
指令来提供维护者信息。
然后是镜像操作相关指令,如 RUN 指令:
-
RUN
指令用于在镜像构建过程中执行命令,例如安装软件包、更新系统等。RUN apt-get update && apt-get install -y some-package
每运行一条指令,都会给基础镜像添加新的一层:
- Docker 使用分层的文件系统,每个指令都会在现有的镜像层上添加一个新层。
最后使用 CMD 指令指定启动容器时要运行的命令操作:
-
CMD
指令用于指定容器启动时要运行的默认命令。CMD ["executable","param1","param2"]
请注意,以上是一些常见的规范,而实际上 Dockerfile 的编写可以根据需求和个人偏好进行调整。使用最佳实践有助于确保镜像的可维护性和可理解性。
docker创建镜像的方法
基于现有镜像创建
基于现有镜像创建新的镜像是一种常见的 Docker 镜像定制方法。这种方法允许你在已有的基础上进行修改和扩展,适用于定制化应用或添加特定功能的需求。
示例:
启动容器:
docker create -it centos:7 /bin/bash
-
docker create
: 创建一个新的容器,但不立即启动。 -
-it
: 分配一个伪终端并保持标准输入打开,以便与容器进行交互。 -
centos:7
: 使用CentOS 7镜像。 -
/bin/bash
: 在容器中运行Bash shell。docker ps -a
-
列出所有容器,包括已停止的容器。
结果应该类似于:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
000550eb36da centos:7 "/bin/bash" 3 seconds ago Created gracious_bassi -
000550eb36da
: 容器的唯一标识符。 -
centos:7
: 使用的镜像。 -
/bin/bash
: 在容器中运行的命令。 -
Created
: 容器的状态是已创建。
提交新镜像:
docker commit -m "new" -a "centos" 000550eb36da centos:test
-
docker commit
: 提交容器的更改并创建新的镜像。 -
-m "new"
: 提交时的说明信息。 -
-a "centos"
: 提交时的作者信息。 -
000550eb36da
: 要提交的容器的ID。 -
centos:test
: 新的镜像的名称和标签。常用选项:
-
-m
: 提交时的说明信息。 -
-a
: 提交时的作者信息。 -
-p
: 生成过程中停止容器的运行。
查看镜像列表:
docker images
-
列出所有本地镜像。
结果中应该包含新创建的
centos:test
镜像。
基于本地模版创建
基于本地模板创建Docker镜像是一种常见的方式,允许使用已有的文件系统快速构建自定义的Docker镜像。
示例
下载操作系统模板文件: 使用 wget
命令从 OPENVZ 开源项目的指定地址下载 Debian 7.0 x86 最小化版本的模板文件。
wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
这个命令将从给定的 URL 下载 debian-7.0-x86-minimal.tar.gz
文件。
导入为Docker镜像: 使用 docker import
命令将下载的模板文件导入为Docker镜像。
cat debian-7.0-x86-minimal.tar.gz | docker import - debian:test
-
cat debian-7.0-x86-minimal.tar.gz
通过cat
命令将文件内容输出到标准输出。 -
docker import - debian:test
利用docker import
命令将标准输出的内容导入为名为debian
,标签为test
的Docker镜像。
验证导入的镜像: 可以运行以下命令验证成功导入的镜像。
docker images
确保 debian
镜像以及对应的 test
标签出现在列表中。
- 这样,就成功地通过下载操作系统模板文件并导入为Docker镜像。请注意,这个例子是使用 OPENVZ 模板,实际上,你可以使用不同操作系统的模板文件进行相似的导入操作。
基于Dockerfile 创建
基于 Dockerfile 创建 Docker 镜像是 Docker 中常见的操作之一。Dockerfile 是一个包含一条条指令的文件,每条指令对应 Linux 下的一条命令。通过编写 Dockerfile,可以定义如何构建一个 Docker 镜像,包括基础镜像、软件安装、配置等。
示例
创建和运行 Apache 服务的 Docker 镜像的示例 Dockerfile,以及相关的执行脚本和测试步骤。
创建工作目录并编辑 Dockerfile:
mkdir /opt/apache
cd /opt/apache
vim Dockerfile
#基于的基础镜像
FROM centos:7
#维护镜像的用户信息
MAINTAINER this is apache image <hmj>
#镜像操作指令安装apache软件
RUN yum -y update
RUN yum -y install httpd
#开启 80 端口
EXPOSE 80
#复制网站首页文件
ADD index.html /var/www/html/index.html
在 Dockerfile 中的主要指令解析如下:
-
使用
FROM centos:7
指定基础镜像为 CentOS 7。 -
使用
MAINTAINER
标签指定维护者信息。 -
使用两个
RUN
指令更新系统并安装 Apache。 -
使用
EXPOSE 80
指令暴露容器的80端口。 -
使用
ADD
指令将本地的index.html
复制到容器中。//方法一:
#将执行脚本复制到镜像中
ADD run.sh /run.sh
RUN chmod 755 /run.sh
#启动容器时执行脚本
CMD ["/run.sh"]
//方法二:
ENTRYPOINT [ "/usr/sbin/apachectl" ]
CMD ["-D", "FOREGROUND"] -
提供两种启动容器的方式:一种是通过
CMD
指定执行脚本/run.sh
,另一种是通过ENTRYPOINT
和CMD
指定直接启动 Apache。
创建执行脚本 run.sh:
vim run.sh
#!/bin/bash
rm -rf /run/httpd/* #清理httpd的缓存
/usr/sbin/apachectl -D FOREGROUND #指定为前台运行
#因为Docker容器仅在它的1号进程(PID为1)运行时,会保持运行。如果1号进程退出了,Docker容器也就退出了。
run.sh
脚本用于在容器启动时执行一些操作,如清理缓存并启动 Apache。
创建网站页面 index.html:
echo "this is test web" > index.html
用于作为 Apache 服务的默认网页。
构建 Docker 镜像:
docker build -t httpd:centos .
使用 docker build
命令基于 Dockerfile 构建名为 httpd:centos
的镜像。
运行容器:
docker run -d -p 1216:80 httpd:centos
使用 docker run
命令在后台运行基于 httpd:centos
镜像的容器,并将容器的80端口映射到主机的1216端口。
测试: 访问 http://192.168.41.31:1216/
,你应该能够看到 Apache 默认页面或者测试网页 "this is test web"。
总体上,这个例子演示了如何使用 Dockerfile 构建包含 Apache 服务的镜像,并通过执行脚本和端口映射在容器中运行该服务。
可能出现的问题
Dockerfile 和脚本中,有一些地方可能会导致潜在的问题或报错。以下是一些可能的注意事项:
- 权限问题:
- 在执行
chmod 755 /run.sh
时,确保容器内存在/run.sh
文件,并且具有可执行权限。否则可能导致脚本无法执行。
- 文件路径和复制问题:
- 确保
index.html
文件在执行ADD index.html /var/www/html/index.html
时存在,否则可能导致文件复制失败。
- Apache 配置问题:
- 使用
ENTRYPOINT ["/usr/sbin/apachectl"]
和CMD ["-D", "FOREGROUND"]
启动 Apache。确保 Apache 在启动时不会由于配置问题或依赖项缺失而崩溃。
- 端口冲突:
- 确保主机上的端口
1216
没有被其他进程占用,否则可能导致容器启动失败。
- Docker 镜像构建问题:
- 在构建 Docker 镜像时,确保网络连接正常,能够成功拉取基础镜像,并且 Dockerfile 和相关文件的语法正确。
- SELinux 问题:
- 如果你的主机启用了 SELinux,可能需要考虑 SELinux 导致的权限问题。可以通过在构建时使用
--security-opt label:disable
来禁用 SELinux 标签。
- Docker Daemon 问题:
- 确保 Docker Daemon 正常运行,并且用户有足够的权限执行 Docker 相关操作。
-
如果有网络报错提示
[Warning] IPv4 forwarding is disabled. Networking will not work.
关于 IPv4 转发被禁用,这可能会影响 Docker 容器的网络功能。解决方法中包含了启用 IPv4 转发的步骤,下面对每一步进行详细解释:
编辑 /etc/sysctl.conf 文件:
vim /etc/sysctl.conf
在文件中添加或修改以下行,启用 IPv4 转发:
net.ipv4.ip_forward=1
保存并关闭文件。
应用 sysctl 配置:
sysctl -p
这会重新加载 sysctl 配置,确保更改立即生效。
重启网络服务:
systemctl restart network
这将重新启动网络服务,以便应用对 sysctl 的更改。
重启 Docker 服务:
systemctl restart docker
这会重新启动 Docker 服务,确保 Docker 正确应用了新的网络配置。
以上步骤的目的是确保 IPv4 转发被启用,从而解决 Docker 容器网络无法正常工作的问题。请注意,修改系统配置可能需要管理员权限。确保在执行这些操作时了解其影响,并谨慎操作。