Docker 镜像是什么
- Docker image 本质上是一个 read-only只读文件, 这个文件包含了文件系统、源码、库文件、依赖、工具等一些运行 application 所必须的文件. (类似纳戒, 可随时随地使用炼丹)
- 我们可以把 Docker image 理解成一个模板, 可以通过这个模板实例化出来很多容器. ( image 和 container 就类似 类与对象 的关系, )
- image 里面是一层层文件系统 Union FS。联合文件系统,可以将几层目录挂载到一起,形成一个虚拟文件系统.
每一层文件系统我们叫做一层 layer,联合文件系统可以对每一层文件系统设置三种权限,只读(readonly)、读写(readwrite)和写出(whiteout-able),但是 docker镜像中每一层文件系统都是只读的。
Docker 镜像由多个只读 layer 组成,每一层只保存相对于下一层的文件变化。
举例:
layer1 Ubuntu 基础层 / ├── bin/ │ └── bash ├── etc/ │ ├── os-release │ └── example.conf 内容:version=1 ├── usr/ │ └── bin/ │ └── apt └── var/ └── log/ └── apt/ └── history.log Layer 2 Nginx 安装层 / ├── etc/ ← 与 Layer 1 重合 │ ├── nginx/ │ │ └── nginx.conf │ └── example.conf 内容:version=2 ├── usr/ ← 与 Layer 1 重合 │ └── sbin/ │ └── nginx └── var/ ← 与 Layer 1 重合 ├── log/ │ └── nginx/ │ └── access.log └── www/ └── html/ └── index.html 内容:Welcome to nginx! Layer 3 · 自定义网页层 / └── var/ ← 与前两层重合 └── www/ ← 与 Layer 2 重合 └── html/ └── index.html 内容:Hello Docker!最终容器看到的, 同名目录合并内容,同名文件由上层遮盖下层. 比如etc目录下不重叠的部分(os-release和nginx)被合并, 重叠的部分(example.conf)被更高层覆盖:
为什么设计镜像
主要有两个目的:
- 为了屏蔽环境差异解决云端和本地一致性的问题
由于开发环境和测试环境不一致, 所以在应用部署到一个新环境时, 如果通过手工或写一些脚本的方式进行部署, 用户为每个应用打包过程比较繁琐,需要配置和给中修改等操作,非常费劲, 还可能出错。 - 加速软件分发速度, 节省存储空间.
如果有一天需要把软件部署到十个机器上, 但是这些机器的环境都不一样, 每个环境重新部署一次很麻烦, 而如果目标机器上安装了Docker, 只需要运行几行命令就可以把仓库里发布的镜像一键部署上去.
如果一台 Ubuntu 的机器上存了成千上万份docker镜像, 那所占的空间会非常大, 但是如果所有镜像都是基于Ubuntu,多个容器不需要各自复制一份完整的 ubuntu文件系统, 只需要共享一份即可.
docker 最大的贡献就是定义了容器镜像的分层的存储格式, docker 镜像技术的基础是联合文件系统(UnionFS), 其文件系统是分层的. 这样既可以充分利用共享层, 又可以减少存储空间占用.
打包的缺点:
- 如果我们一个软件可能就200K, 但是打包上OS之后多了几十M, 那软件就变大了, 但是这个问题相比于解决环境差异来说, 是相对可控的.
- 分层带来的问题: OverlayFS 需要按照层的优先级查找文件:如果上层存在同名文件,就使用上层版本;如果不存在,再考虑下层。因此,层数较多时,某些首次文件查找操作可能产生额外开销。如果我们要找最下层的一个文件, 可能我们要从上到下找一遍, 到最后一层才找到, 可能有性能损失.
Docker 镜像命令
Docker镜像命令概述:
| 命令 | 别名 | 功能 | 备注 |
|---|---|---|---|
| docker images | docker image ls / docker image list | 列出本地镜像 | 必须掌握 |
| docker tag | docker image tag | 给镜像打标签,可用于推送镜像仓库 | 必须掌握 |
| docker pull | docker image pull | 从镜像仓库拉取镜像 | 和镜像仓库命令相同,也可以归类为镜像操作命令,必须掌握 |
| docker push | docker image push | 推送镜像到仓库 | 和镜像仓库命令相同,也可以归类为镜像操作命令,必须掌握 |
| docker rmi | docker image rm / docker image remove | 删除本地镜像 | 必须掌握 |
| docker build | docker image build | 通过 Dockerfile 制作镜像 | 必须掌握 |
| docker save | docker image save | 将指定镜像保存成 tar 归档文件 | 必须掌握 |
| docker load | docker image load | 导入使用 docker save 命令导出的镜像 | 必须掌握 |
| docker image inspect | — | 查看镜像详细信息 | 必须掌握 |
| docker history | docker image history | 查看镜像历史 | — |
| docker import | docker image import | 从归档文件 docker export 中创建镜像 | — |
| docker image prune | — | 删除不使用的镜像 | — |
docker images, docker tag, docker pull, docker push
见 Dokcer(六) Docker镜像仓库
docekr rmi
功能: 删除镜像.
语法
dockerrmi[OPTIONS]IMAGE[IMAGE...]别名: docker image rm, docker image remove
关键参数:
- -f :强制删除;
- –no-prune :不移除该镜像的过程镜像,默认移除;
如果镜像没有容器在使用, 正常删除即可:
root@zzy-virtual-machine:/var/lib/containerddockerimages i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA busybox:1.35.0 98ad9d1a2be36.55MB2.15MB busybox:1.37.0 bdf57e528e456.77MB2.22MB busybox:1.38.0 fd7dc98638c86.81MB2.23MB root@zzy-virtual-machine:/var/lib/containerddockerrmi busybox:1.35.0 Untagged: busybox:1.35.0 Deleted: sha256:98ad9d1a2be345201bb0709b0d38655eb1b370145c7d94ca1fe9c421f76e245a如果镜像在被容器使用, 正确的删除方法是, 先删除容器, 再删除镜像:
# 容器在被使用, 不能直接删除镜像root@zzy-virtual-machine:/var/lib/containerddockerrmi busybox:1.35.0 Error response from daemon: conflict: unable to delete busybox:1.35.0(must be forced)- container b2285fde9b98 is using its referenced image 98ad9d1a2be3# 先删除容器root@zzy-virtual-machine:/var/lib/containerddockerrmb2285fde9b98 b2285fde9b98# 删除镜像root@zzy-virtual-machine:/var/lib/containerddockerrmi busybox:1.35.0 Untagged: busybox:1.35.0 Deleted: sha256:98ad9d1a2be345201bb0709b0d38655eb1b370145c7d94ca1fe9c421f76e245a如果镜像在被容器使用, 对repository:tag执行docker rmi -f时会发现只成功Untagged, 并没有Deleted, 虽然我们docker images已经查不到镜像, 但 image 对象仍可能存在,并继续被已有容器引用:
root@zzy-virtual-machine:/var/lib/containerddockerrmi-fbusybox:1.35.0 Untagged: busybox:1.35.0# 98ad9d1a2be3其实是之前busybox:1.35.0的镜像IDroot@zzy-virtual-machine:/var/lib/containerddockerps-aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ca360b22a99c 98ad9d1a2be3"sh"59seconds ago Exited(0)38seconds ago peaceful_allen然后强制删除 Image ID 后出现 Deleted,表示 image 对象已从 Docker 镜像存储中删除; 但容器仍然存在, 容器 snapshot 所需的数据不会因此被粗暴删除
root@zzy-virtual-machine:/var/lib/containerddockerrmi 98ad9d1a2be3 Error response from daemon: conflict: unable to delete 98ad9d1a2be3(must be forced)- image is being used by stopped container ca360b22a99c root@zzy-virtual-machine:/var/lib/containerddockerrmi-f98ad9d1a2be3 Deleted: sha256:98ad9d1a2be345201bb0709b0d38655eb1b370145c7d94ca1fe9c421f76e245a# 此时容器依然存在root@zzy-virtual-machine:/var/lib/containerddockerps-aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ca360b22a99c 98ad9d1a2be3"sh"3minutes ago Exited(0)2minutes ago peaceful_allen# 容器删除了, 相关 layer/snapshot 已经完全没人引用, 才能够回收对应存储空间root@zzy-virtual-machine:/var/lib/containerddockerrmca360b22a99c ca360b22a99c所以我们还是按照先删除容器, 再删除镜像的顺序进行删除.
最后来一个场景:
已经有一个镜像busybox:1.37.0, 然后我给它打了两个tag, 分别是cutettsx/test:v1.0和cutettsx/test:v2.0:
root@zzy-virtual-machine:/var/lib/containerddockertag busybox:1.37.0 cutettsx/test:v1.0 root@zzy-virtual-machine:/var/lib/containerddockertag busybox:1.37.0 cutettsx/test:v2.0 root@zzy-virtual-machine:/var/lib/containerddockerimages i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA busybox:1.37.0 bdf57e528e456.77MB2.22MB cutettsx/test:v1.0 bdf57e528e456.77MB2.22MB cutettsx/test:v2.0 bdf57e528e456.77MB2.22MB分别用cutettsx/test:v1.0和cutettsx/test:v2.0启动两个容器然后退出, 注意看docker ps -a的IMAGE列:
root@zzy-virtual-machine:/var/lib/containerddockerrun-itcutettsx/test:v1.0sh/# exitroot@zzy-virtual-machine:/var/lib/containerddockerrun-itcutettsx/test:v2.0sh/# exitroot@zzy-virtual-machine:/var/lib/containerddockerps-aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9048e0faf36c cutettsx/test:v2.0"sh"10seconds ago Exited(0)5seconds ago musing_lichterman 8353e9f0f04d cutettsx/test:v1.0"sh"16seconds ago Exited(0)12seconds ago compassionate_shamir然后docker rmi cutettsx/test:v1.0删除掉一个镜像, 可以发现虽然镜像在被使用, 但是删除并没有报错, 而是Untagged成功, 但是并没有Deleted, 因为之前docker tag只是打了个标签, 底层依然是被引用的镜像; 然后docker ps -a之后, 发现对应容器的IMAGE列从cutettsx/test:v1.0变成bdf57e528e45, 这其实就是busybox:1.37.0的镜像ID:
root@zzy-virtual-machine:/var/lib/containerddockerrmi cutettsx/test:v1.0 Untagged: cutettsx/test:v1.0 root@zzy-virtual-machine:/var/lib/containerddockerps-aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9048e0faf36c cutettsx/test:v2.0"sh"About a minute ago Exited(0)About a minute ago musing_lichterman 8353e9f0f04d bdf57e528e45"sh"About a minute ago Exited(0)About a minute ago compassionate_shamirdockerimages|grepbusybox:1.37.0 busybox:1.37.0 bdf57e528e456.77MB2.22MB U删除cutettsx/test:v2.0, 也是类似的现象
dockerrmi cutettsx/test:v2.0 Untagged: cutettsx/test:v2.0 root@zzy-virtual-machine:/var/lib/containerddockerps-aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9048e0faf36c bdf57e528e45"sh"2minutes ago Exited(0)2minutes ago musing_lichterman 8353e9f0f04d bdf57e528e45"sh"2minutes ago Exited(0)2minutes ago compassionate_shamir最后我们试图删除镜像bdf57e528e45, 才报错, 因为我们这已经是最后一个镜像ID, 这之后我们要删除镜像, 就只能先删除容器, 再删除镜像了 :
dockerrmi bdf57e528e45 Error response from daemon: conflict: unable to delete bdf57e528e45(must be forced)- image is being used by stopped container 8353e9f0f04d# 删除容器root@zzy-virtual-machine:/var/lib/containerddockerrm9048e0faf36c 8353e9f0f04d 9048e0faf36c 8353e9f0f04d# 删除镜像root@zzy-virtual-machine:/var/lib/containerddockerrmi bdf57e528e45 Untagged: busybox:1.37.0 Deleted: sha256:bdf57e528e45e4433820e045b29b4597825a1c9e38353532d90a01445013f82edocker save
功能: 将指定镜像保存成 tar 归档文件。
语法:
dockersave[OPTIONS]IMAGE[IMAGE...]别名: docker image save
关键参数: -o :输出到的文件。
# 保存一个镜像sudodockersave-omybusybox1.tar busybox:1.37.0# 保存多个镜像sudodockersave-omybusybox2.tar busybox:1.37.0 busybox:1.38.0zzy@zzy-virtual-machine:~$ ll-hmybusybox* -rw-------1root root2.2M9月2819:34 mybusybox1.tar -rw-------1root root4.3M9月2819:34 mybusybox2.tardocker load
功能: 导入使用 docker save 命令导出的镜像。
语法:
dockerload[OPTIONS]别名:
dockerimage load关键参数:
- –input , -i : 指定导入的文件,代替 STDIN
- –quiet , -q : 精简输出信息
首先可以看到, 我们没有busybox相关的镜像:
sudodockerimages i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA centos:7 be65f488b776 301MB76.1MB U cutettsx/test:1.23.3 f4e3b6489888 214MB56.9MB cutettsx/test:v1.0 fd7dc98638c86.81MB2.23MB hello-world:latest 5e230903533223.5kB7.08kB nginx:1.24.0 f6daac2445b0 218MB60.1MB U然后把我们刚才docker save的tar包给docker load出来:
zzy@zzy-virtual-machine:~$sudodockerload-imybusybox2.tar Loaded image: busybox:1.37.0 Loaded image: busybox:1.38.0发现busybox:1.37.0和busybox:1.38.0镜像都出现了:
zzy@zzy-virtual-machine:~$sudodockerimages i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA busybox:1.37.0 bdf57e528e456.77MB2.22MB busybox:1.38.0 fd7dc98638c86.81MB2.23MB centos:7 be65f488b776 301MB76.1MB U cutettsx/test:1.23.3 f4e3b6489888 214MB56.9MB cutettsx/test:v1.0 fd7dc98638c86.81MB2.23MB hello-world:latest 5e230903533223.5kB7.08kB nginx:1.24.0 f6daac2445b0 218MB60.1MB Udocker history
功能: 显示镜像历史
语法:
docker history [OPTIONS] IMAGE别名:
docker image history关键参数:
- -H , --human :大小和日期采用人容易读的格式展现, 默认开了
- –no-trunc :显示全部信息,不要隔断, 默认没开, 如果开启显示格式会很长, 不易读;
- -q, --quiet: 只显示镜像 id 信息;
看一下busybox镜像的层数, 发现就只有一层:
sudodockerhistorybusybox:1.38.0 IMAGE CREATED CREATED BY SIZE COMMENT fd7dc98638c84months ago BusyBox1.38.0(glibc), Debian134.58MB列名从左到右依次是层ID,层创建时间,创建的dockerfile命令,大小,注释
再看一下nginx镜像的层结构:
sudodockerhistorynginx:1.24.0 IMAGE CREATED CREATED BY SIZE COMMENT f6daac2445b03years ago CMD["nginx""-g""daemon off;"]0B buildkit.dockerfile.v0<missing>3years ago STOPSIGNAL SIGQUIT 0B buildkit.dockerfile.v0<missing>3years ago EXPOSE map[80/tcp:{}]0B buildkit.dockerfile.v0<missing>3years ago ENTRYPOINT["/docker-entrypoint.sh"]0B buildkit.dockerfile.v0<missing>3years ago COPY30-tune-worker-processes.sh /docker-ent…16.4kB buildkit.dockerfile.v0<missing>3years ago COPY20-envsubst-on-templates.sh /docker-ent…12.3kB buildkit.dockerfile.v0<missing>3years ago COPY10-listen-on-ipv6-by-default.sh /docker…12.3kB buildkit.dockerfile.v0<missing>3years ago COPY docker-entrypoint.sh /# buildkit 8.19kB buildkit.dockerfile.v0<missing>3years ago RUN /bin/sh-cset-x&&addgroup --syst… 65MB buildkit.dockerfile.v0<missing>3years ago ENVPKG_RELEASE=1~bullseye 0B buildkit.dockerfile.v0<missing>3years ago ENVNJS_VERSION=0.7.12 0B buildkit.dockerfile.v0<missing>3years ago ENVNGINX_VERSION=1.24.0 0B buildkit.dockerfile.v0<missing>3years ago LABELmaintainer=NGINX Docker Maintainers<d… 0B buildkit.dockerfile.v0<missing>3years ago /bin/sh-c#(nop) CMD ["bash"] 0B<missing>3years ago /bin/sh-c#(nop) ADD file:5d6b639e8b6bcc011… 92.7MBdocker image prune
功能: 删除不使用的镜像。
什么是不使用的镜像?
- 只要容器不使用, 就认为是不使用的镜像(这种理解比较危险, 因为我可能后面还要用, 现在只是pull下来了).
- 虚悬镜像dangling image, (也叫空悬镜像)
语法:
dockerimage prune[OPTIONS]关键参数:
- -a , --all : 删除全部不使用的镜像.
- 加上
-a选项, 是指容器不使用的镜像. - 默认是指虚悬镜像
- 加上
- –filter filter: 指定过滤条件
- -f, --force : 不提示是否删除
综合案例
案例一: 离线迁移
如果我们无法联网, 不能docker pull和docker push, 所以我们只能用docker save和docker load.
现在有两个服务器, 服务器1是Ubuntu, 服务器2是Windows, 首先确保两台服务器都安装了docker.
服务器 1 上将镜像保存为 tar 文件:
dockersave-omybusybox.tar busybox:1.38.0服务器2 上获取该 tar 文件, 并加载:
# Windows系统下用scp把ubuntu下的mybusybox.tar拷贝过来scp用户名@192.168.xxx.xxx:/data/xxx/mybusybox.tar C:\Users\xxx# 此时Windows上什么镜像都没有dockerimages i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA# docker load加载镜像dockerload-i.\mybusybox.tar Loaded image: busybox:1.38.0# 查看镜像dockerimages i Info → U In Use IMAGE ID DISK USAGE CONTENT SIZE EXTRA busybox:1.38.0 fd7dc98638c86.81MB2.23MB# 运行镜像, 发现可以正常使用dockerrun-itbusybox:1.38.0sh/# busybox ifconfigeth0 Link encap:Ethernet HWaddr E6:7B:96:82:24:74 inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:17 errors:0 dropped:0 overruns:0 frame:0 TX packets:3 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:1698(1.6KiB)TX bytes:126(126.0B)lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0(0.0B)TX bytes:0(0.0B)/# busybox lsbin dev etc home lib lib64 proc root sys tmp usr var /# busybox ping www.baidu.comPING www.baidu.com(180.101.51.73):56data bytes64bytes from180.101.51.73:seq=0ttl=63time=10.091ms64bytes from180.101.51.73:seq=1ttl=63time=15.014ms64bytes from180.101.51.73:seq=2ttl=63time=10.399ms ^C --- www.baidu.compingstatistics ---3packets transmitted,3packets received,0% packet loss round-trip min/avg/max=10.091/11.834/15.014 ms案例二:
- 看看镜像在云端和本地是怎么存储的
nginx:1.21.1镜像在服务端的镜像大小为51.26 MB:
pull到本地之后, 发现占用200MB大小:
root@zzy-virtual-machine:/home/zzydockerimages|grepnginx nginx:1.21.1 a05b0cdd4fc1 200MB53.8MB这说明服务端的镜像比拉到本地后占用空间小, 可以理解为服务端存储了一个压缩包, 下载到本地后被解压出来. 这样在服务端占用空间少, 而且传输文件也节省带宽.
- 服务端是否给1000个镜像, 就存放1000份
可以看到, 添加一个新的镜像(docker push cutettsx/test:v1)到我们的仓库的时候,如果 docker hub 发现已经有了是 mount 的,不是从本地推上去的;
然后把一个镜像打多个 tag (docker push cutettsx/test:v2), 推送给仓库, 可以看到是说层已经存在, 并不会重复存储