1、Docker 镜像结构
Docker镜像的结构是分层的,这种结构是Docker镜像轻量化和高效性的关键。每个Docker镜像都由一系列的“镜像层”(image layers)组成,这些层通过UnionFS(联合文件系统)技术叠加在一起,形成一个完整的文件系统。镜像层之间可以共享,从而节省存储空间并提升效率。
Docker镜像层的主要组成部分包括:
- 基础镜像层(Base Image Layer):所有Docker镜像都起始于一个基础镜像层,这通常是一个轻量级的操作系统镜像,如Ubuntu、CentOS等。基础镜像层包含了操作系统最基本的文件和目录结构,如
/dev
、/proc
、/bin
、/etc
等。 - 应用镜像层(Application Image Layers):在基础镜像层之上,可以添加多个应用镜像层,用于安装和配置应用程序及其依赖项。Dockerfile中的每条指令(如
RUN
、COPY
、ADD
等)都会创建一个新的镜像层。 - 只读层(Read-Only Layers):除了最顶层的容器层之外,所有的镜像层都是只读的。这意味着一旦镜像被创建,其各个层的内容就不能被修改。
- 容器层(Container Layer):当Docker容器启动时,会在镜像的最顶层添加一个可写层,即容器层。容器中的所有更改(如文件写入、删除等)都会发生在这个可写层中,而不会影响到下层的镜像层。
UnionFS的作用:
UnionFS允许Docker将多个镜像层叠加在一起,形成一个统一的文件系统视图。这样,用户就可以看到一个完整的文件系统,而不需要关心底层的镜像层是如何组织的。同时,UnionFS还支持写时复制(Copy-on-Write, CoW)机制,这意味着当容器中的文件需要被修改时,系统会首先在容器层中创建一个该文件的副本,并在副本上进行修改,从而避免了对原始镜像层的直接修改。
2、镜像运行的基本原理
镜像运行的基本原理主要涉及到Docker容器的启动和镜像层的加载:
(1)镜像的加载:
- 当使用
docker run
命令启动一个容器时,Docker会首先查找本地是否存在指定的镜像。如果不存在,Docker会从远程仓库(如Docker Hub)下载镜像。 - 镜像的下载是按层进行的,Docker会检查每一层是否已经在本地存在,如果存在则跳过下载,只下载不存在的层。这样可以节省带宽和时间。
- 镜像层被加载到内存中,并通过UnionFS技术叠加在一起,形成一个完整的文件系统供容器使用。
(2)容器的启动:
- 在镜像层之上,Docker会创建一个新的可写层,即容器层。这个层是空的,用于存储容器运行过程中的所有更改。
- Docker会为容器分配一个隔离的进程空间,并启动容器的主进程。这个主进程通常是容器内运行的应用程序。
- 容器内的所有进程都在这个隔离的进程中运行,它们只能访问到UnionFS提供的文件系统视图,而无法直接访问宿主机的文件系统。
(3)容器的运行:
- 容器运行过程中,对文件系统的所有修改都会发生在容器层中。这些修改不会影响到底层的镜像层。
- 如果容器被删除,其容器层也会被删除,但底层的镜像层仍然保留在系统中,以便其他容器可以共享使用。
- Copy-on-Write 可写容器层
- 容器层以下所有镜像层都是只读的
- docker从上往下依次查找文件
- 容器层保存镜像变化的部分,并不会对镜像本身进行任何修改
- 一个镜像最多127层
docker commit 方式生成层,不利于审计,不安全。
【镜像构建示例】
##创建目录
[root@docker ~]# mkdir docker
[root@docker ~]# cd docker/
[root@docker docker]# ls
[root@docker docker]# vim Dockerfile
[root@docker docker]#
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
#构建镜像
[root@docker docker]# docker build -t busybox:test2 . #点表示当前路径
[+] Building 3.5s (6/6) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 94B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/2] FROM docker.io/library/busybox:latest 0.0s
=> [2/2] RUN touch /testfile #构建了一层 2.3s
=> exporting to image 0.6s
=> => exporting layers 0.4s
=> => writing image sha256:d3abbb52c0daeba65ca77aee1353c3491411ddf1d8097c84187c01365ae2993f 0.0s
=> => naming to docker.io/library/busybox:test2
[root@docker docker]# docker history busybox:test2
IMAGE CREATED CREATED BY SIZE COMMENT
d3abbb52c0da 7 minutes ago RUN /bin/sh -c touch /testfile # buildkit 0B buildkit.dockerfile.v0
<missing> 15 months ago BusyBox 1.36.1 (glibc), Debian 12 4.26MB
#使用我们构建的镜像运行一个容器,看是否能看到我们创建的文件
[root@docker docker]# docker run -it --rm --name xixi busybox:test2
/ # ls
bin etc lib proc sys tmp var
dev home lib64 root testfile usr
构建镜像默认是Dockerfile文件,可以使用 -f 选项来指定需要构建的文件。
# 示例:
[root@docker docker]# mv Dockerfile folian
[root@docker docker]#
[root@docker docker]# docker build -t busybox:test2 .
[+] Building 0.3s (1/1) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 2B 0.0s
ERROR: failed to solve: failed to read dockerfile: open Dockerfile: no such file or directory
[root@docker docker]#
[root@docker docker]# docker build -f /root/docker/folian -t busybox:test3 .
[+] Building 0.5s (6/6) FINISHED docker:default
=> [internal] load build definition from folian 0.0s
=> => transferring dockerfile: 90B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [1/2] FROM docker.io/library/busybox:latest 0.0s
=> CACHED [2/2] RUN touch /testfile 0.0s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:d3abbb52c0daeba65ca77aee1353c3491411ddf1d8097c84187c01365ae2993f 0.0s
=> => naming to docker.io/library/busybox:test2
【构建参数】
## 参数运用示例
[root@docker docker]# docker build -t busybox:v1 .
[root@docker docker]# docker history busybox:v1
IMAGE CREATED CREATED BY SIZE COMMENT
c93514553f67 45 minutes ago LABEL USER=fl@folian.org 0B buildkit.dockerfile.v0
<missing> 45 minutes ago RUN /bin/sh -c touch /testfile # buildkit 0B buildkit.dockerfile.v0
<missing> 15 months ago BusyBox 1.36.1 (glibc), Debian 12 4.26MB
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL USER=fl@folian.org
###
[root@docker docker]# ls
Dockerfile
[root@docker docker]# cp /etc/passwd .
[root@docker docker]# ls
Dockerfile passwd
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
[root@docker docker]# docker build -t busybox:v2 .
[+] Building 1.2s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.1s
=> => transferring dockerfile: 140B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [internal] load .dockerignore 0.1s
=> => transferring context: 2B 0.0s
=> [1/3] FROM docker.io/library/busybox:latest 0.0s
=> [internal] load build context 0.1s
=> => transferring context: 2.18kB 0.0s
=> CACHED [2/3] RUN touch /testfile 0.0s
=> [3/3] COPY passwd /passwd 0.2s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:bae9a7570e4b00c3ae38a8419e08d013f0f9de3ba5e2fdc060733ccedfa0f47b 0.0s
=> => naming to docker.io/library/busybox:v2 0.0s
[root@docker docker]# docker history busybox:v2
IMAGE CREATED CREATED BY SIZE COMMENT
bae9a7570e4b 11 seconds ago COPY passwd /passwd # buildkit 2.15kB buildkit.dockerfile.v0
<missing> 11 seconds ago LABEL Email=fl@folian.org 0B buildkit.dockerfile.v0
<missing> 50 minutes ago RUN /bin/sh -c touch /testfile # buildkit 0B buildkit.dockerfile.v0
<missing> 15 months ago BusyBox 1.36.1 (glibc), Debian 12 4.26MB
ADD
#创建压缩包,里面包含Dockerfile、passwd文件
[root@docker docker]# ls
Dockerfile passwd
[root@docker docker]# tar zcf test.tar.gz Dockerfile passwd
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt #把压缩包的东西解压到 /mnt
[root@docker docker]# docker build -t busybox:v3 .
[+] Building 0.5s (9/9) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 161B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/4] FROM docker.io/library/busybox:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 1.23kB 0.0s
=> CACHED [2/4] RUN touch /testfile 0.0s
=> CACHED [3/4] COPY passwd /passwd 0.0s
=> [4/4] ADD test.tar.gz /mnt 0.2s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => writing image sha256:9b6b064a9528e933c6d850e09bf53579f80d795a28e2f24d082c40bf64227d7d 0.0s
=> => naming to docker.io/library/busybox:v3 0.0s
[root@docker docker]#
[root@docker docker]# docker run -it --name test busybox:v3
/ # ls
bin etc lib mnt proc sys tmp var
dev home lib64 passwd root testfile usr
/ # ls /mnt/
Dockerfile passwd
/ # exit
[root@docker docker]#
RUN #在构建时执行命令
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat D
cat: D: No such file or directory
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt
RUN mkdir /folian
[root@docker docker]# docker build -t busybox:v4 .
[+] Building 2.9s (10/10) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 181B 0.0s
=> [internal] load metadata for docker.io/library/busybox:latest 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/busybox:latest 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 59B 0.0s
=> CACHED [2/5] RUN touch /testfile 0.0s
=> CACHED [3/5] COPY passwd /passwd 0.0s
=> CACHED [4/5] ADD test.tar.gz /mnt 0.0s
=> [5/5] RUN mkdir /folian 1.7s
=> exporting to image 0.5s
=> => exporting layers 0.3s
=> => writing image sha256:2d198ad98db6c6ef40b1bf59a1e7173c1bcbad4d5e0b6f5afb0cac35cedf1e95 0.1s
=> => naming to docker.io/library/busybox:v4 0.0s
[root@docker docker]# docker history busybox:v4
IMAGE CREATED CREATED BY SIZE COMMENT
2d198ad98db6 29 seconds ago RUN /bin/sh -c mkdir /folian # buildkit 0B buildkit.dockerfile.v0
<missing> 10 minutes ago ADD test.tar.gz /mnt # buildkit 2.27kB buildkit.dockerfile.v0
<missing> 4 hours ago COPY passwd /passwd # buildkit 2.15kB buildkit.dockerfile.v0
<missing> 4 hours ago LABEL Email=fl@folian.org 0B buildkit.dockerfile.v0
<missing> 5 hours ago RUN /bin/sh -c touch /testfile # buildkit 0B buildkit.dockerfile.v0
<missing> 15 months ago BusyBox 1.36.1 (glibc), Debian 12 4.26MB
[root@docker docker]# docker run -it --name test busybox:v4
/ # ls
bin etc home lib64 passwd root testfile usr
dev folian lib mnt proc sys tmp var
/ #
CMD #容器运行时会执行指定的命令
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt
RUN mkdir /folian
#CMD echo hello folian #同样的功能,这一个不会调用shell环境,下面个会
CMD ["/bin/bash","-c","echo hello folian"]
[root@docker docker]# docker run -it --name test busybox:v5 ##运行容器时会执行CMD后的,执行完就退出,不占用后台。
hello folian
[root@docker docker]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9f1c58f5648a busybox:v5 "/bin/sh -c 'echo he…" 9 seconds ago Exited (0) 6 seconds ago test
[root@docker docker]# docker logs test
hello folian
[root@docker docker]#
env #指定环境变量
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt
RUN mkdir /folian
ENV name=folian
CMD echo $name ##最好用要求的格式
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt
RUN mkdir /folian
ENV name=folian
CMD ["/bin/sh","-c","echo $name"]
[root@docker docker]# docker build -t busybox:v7 .
如果在运行容器时不想执行CMD的命令,可以替代:
如果不想被替代:有一个参数:ENTRYPOINT
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt
RUN mkdir /folian
ENV name=folian
CMD ["/bin/sh","-c","echo $name"]
ENTRYPOINT ["/bin/sh","-c","echo ending"]
[root@docker docker]# docker build -t busybox:v8 .
替代不了:
EXPOSE #端口暴露
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
RUN touch /testfile
LABEL Email=fl@folian.org
COPY passwd /passwd
ADD test.tar.gz /mnt
RUN mkdir /folian
ENV name=folian
CMD ["/bin/sh","-c","echo $name"]
ENTRYPOINT ["/bin/sh","-c","echo ending"]
EXPOSE 80 443
[root@docker docker]# docker build -t busybox:v9 .
WORKDIR
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
WORKDIR /mnt #指定运行目录,默认是 / 底下
[root@docker docker]# docker build -t busybox:v10 .
[root@docker docker]# docker run -it --rm --name test busybox:v10
/mnt #
/mnt #
VOLUME #数据卷
Mount 里为空的,就是没有将容器内任何的数据挂载到宿主机,他是完全独立的,没有任何东西通过宿主机写入容器里。
#如何把容器的某一个目录挂载到宿主机上:宿主机也能通过这个目录向容器里写东西。
[root@docker docker]# vim Dockerfile
[root@docker docker]# cat Dockerfile
#指定基础镜像
FROM busybox
ENV name=folian
EXPOSE 80 443
WORKDIR /mnt
RUN mkdir /folian
VOLUME /folian #指把容器里的 /folian目录挂载到宿主机的某个目录上,具体是哪个目录,是自动生成的,默认在 /var/lib/docker/……下。
[root@docker docker]# docker build -t busybox:v11 .
[root@docker docker]# docker run -it --rm --name test busybox:v11
/mnt # cd ..
/ # ls
bin dev etc folian home lib lib64 mnt proc root sys tmp usr var
/ # [root@docker docker]# docker inspect test
#被挂载到宿主机的位置,宿主机通过这个路径写
"Source": "/var/lib/docker/volumes/da19bbfc84318476cbe2258468750d89b9edf49779fa544801741a99a6ac0f52/_data",
#容器的目录:往宿主机的这个目录写东西,会被写进容器的 /folian 目录里。
"Destination": "/folian",
##宿主机
[root@docker docker]# cd /var/lib/docker/
[root@docker docker]# cd /var/lib/docker/volumes/
[root@docker volumes]# ls
backingFsBlockDev
c72fdfc9eb425816d85900a9213ae88ff2e589e72034b2875bcf251423afad2d
da19bbfc84318476cbe2258468750d89b9edf49779fa544801741a99a6ac0f52
metadata.db
[root@docker volumes]# cd da19bbfc84318476cbe2258468750d89b9edf49779fa544801741a99a6ac0f52/
[root@docker da19bbfc84318476cbe2258468750d89b9edf49779fa544801741a99a6ac0f52]# ls
_data
[root@docker da19bbfc84318476cbe2258468750d89b9edf49779fa544801741a99a6ac0f52]# cd _data/
[root@docker _data]# ls
[root@docker _data]#
##容器内
[root@docker ~]# docker attach test
/ #
/ #
/ # ls folian/
/ # ##此时什么也没有
##在宿主机上的目录里创建几个文件
[root@docker _data]# touch testfile{1..10}
[root@docker _data]#
##再在容器里查看
[root@docker ~]# docker attach test
/ #
/ #
/ #
/ # ls folian/
testfile1 testfile2 testfile4 testfile6 testfile8
testfile10 testfile3 testfile5 testfile7 testfile9
volume ##把容器内部的数据持久化到宿主机
如果容器关了:[root@docker _data]# ls 宿主机上这个目录下的东西也会消失
如何持久化?且看下下下下回!