Docker入门-第3章-容器镜像深度解析

【Docker 从入门到实战】第 3 章:容器镜像深度解析------分层结构、Copy-on-Write、Dockerfile 构建与 Registry 分发一文搞定

摘要:镜像是 Docker 容器的基石,容器是镜像的运行实例。本章先解剖 hello-world 这个"最小的镜像",讲清 base 镜像与 rootfs 的关系;然后深入镜像的分层结构与 Copy-on-Write 特性;接着掌握构建镜像的两种方法(docker commit 与 Dockerfile);最后打通镜像分发全链路:Docker Hub、华为云 SWR、本地 Registry、企业级 Harbor。学完这一章,你将彻底理解 Docker 为什么轻量、镜像为什么分层、如何构建并分发自己的镜像。


一、先从最小的镜像说起:hello-world

hello-world 是 Docker 官方用来验证安装是否成功的镜像,它到底有多小?

bash 复制代码
docker pull hello-world
docker images
# hello-world  latest  d2c94e258dcb  16 months ago  13.3kB

不到 14KB! 一个完整可运行的镜像,居然只有 13.3kB。它的 Dockerfile 只有三行:

dockerfile 复制代码
FROM scratch          # 从 0 开始,白手起家
COPY hello /          # 把可执行文件 hello 复制到镜像根目录
CMD ["/hello"]        # 容器启动时执行 /hello

整个镜像里只有一个可执行文件 /hello,连最基本的 /bin/usr/lib/dev 都没有。它的全部功能就是打印那句 "Hello from Docker!"。

启示:hello-world 虽然是"完整"的镜像,但没有实际用途。我们真正常用的是能提供基本操作系统环境的 base 镜像


二、base 镜像与 rootfs:为什么 CentOS 只有 204MB?

2.1 Linux 操作系统 = 内核空间 + 用户空间

  • kernel:Linux 系统内核(内核空间);
  • rootfs :用户空间的文件系统,包含我们熟悉的 /dev/proc/bin 等目录。

2.2 base 镜像的两层含义

  1. 不依赖其他镜像,从 scratch 构建(scratch 本身是一个空镜像);
  2. 其他镜像可以之为基础进行扩展

所以能称作 base 镜像的,通常都是各种 Linux 发行版的镜像:Ubuntu、Debian、CentOS 等。

2.3 灵魂拷问:CentOS 怎么才 204MB?

我们平时安装一个 CentOS 至少好几个 GB,而 centos:7 镜像只有 204MB:

bash 复制代码
docker images centos:7
# centos  7  eeb6ee3f44bd  3 years ago  204MB

原因很简单:

  • base 镜像直接复用 Host 的 kernel,自己只需要提供 rootfs;
  • 精简后的 rootfs 只包含最基本的命令、工具和程序库(alpine 甚至不到 10MB);
  • 我们平时安装的 CentOS 还选装了软件、服务、图形桌面等,所以好几个 GB 不足为奇。

base 镜像提供的是最小安装 的 Linux 发行版。CentOS 的 Dockerfile 中那句 ADD 指令添加的 tar 包,就是 CentOS 7 的 rootfs,制作镜像时自动解压到 / 目录下。

2.4 关键知识点:容器用谁的 kernel?

不同 Linux 发行版的区别主要就是 rootfs(Ubuntu 用 upstart + apt,CentOS 用 systemd + yum),Linux kernel 差别不大。所以 Docker 能同时模拟多种 Linux 环境。

但要注意两点:

  1. base 镜像只在用户空间与发行版一致,kernel 版本与发行版不同。例如在 CentOS Stream 8(kernel 4.18.0)的宿主机上跑 ubuntu、centos:7 容器:
bash 复制代码
[root@docker ~]# uname -r
4.18.0-553.6.1.el8.x86_64        # 宿主 kernel 4.18.0

docker run -it ubuntu
root@4264749aa4af:/# uname -r
4.18.0-553.6.1.el8.x86_64        # 容器用的也是宿主 kernel
  1. 容器只能使用 Host 的 kernel,且不能修改。所有容器共用宿主内核,在容器里无法升级 kernel。如果应用对内核版本有硬性要求,就不适合用容器,虚拟机更合适。

三、镜像的分层结构与 Copy-on-Write

3.1 分层结构:每步操作生成一层

Docker Hub 上 99% 的镜像都是通过在 base 镜像上安装、配置软件构建出来的。Docker 镜像中引入层(layer)的概念,制作过程中的每一步操作都会生成一个新的镜像层。

容器由若干只读镜像层 + 最上面一个可写的容器层构成:

复制代码
┌─────────────────────────────┐
│   可写容器层(容器运行时可改)   │ ← 容器运行时加载
├─────────────────────────────┤
│   镜像层 3(如 RUN 安装 vim)   │ ┐
├─────────────────────────────┤ │ 只读镜像层
│   镜像层 2(如 COPY 文件)      │ │ (可被多个容器共享)
├─────────────────────────────┤ │
│   base 镜像层(如 ubuntu)      │ ┘
└─────────────────────────────┘

docker history 可以看到每一层的构建历史,docker image inspect ubuntu 可以查看分层详情。

3.2 为什么要分层?------ 共享资源

多个镜像从相同的 base 镜像构建而来时,Docker Host 磁盘上只需保存一份 base 镜像,内存中也只需加载一份,就可以为所有容器服务。

3.3 UnionFS:联合文件系统

UnionFS 的核心功能是把多个不同位置的目录联合挂载(union mount)到同一个目录下。使用镜像时,Docker 把所有的镜像层联合挂载到一个统一挂载点,对外表现为一个完整的 Linux 操作系统。

3.4 Copy-on-Write:容器层只记"变化"

当容器启动时,一个新的可写层被加载到镜像顶部,叫容器层 ;它下面的所有镜像层都是只读的。对容器的所有增删改查,都只发生在容器层:

操作 具体执行
创建文件 新文件只能被添加在容器层中
删除文件 依据分层结构由上往下查找,找到后在容器层中记录删除操作------UnionFS 会在容器层创建一个 whiteout 文件,把被删文件"遮挡"起来
修改文件 由上往下查找,找到后把镜像层中的数据复制到容器层进行修改,修改后的数据保存在容器层(这就是 copy-on-write)
读取文件 依据分层结构由上往下依次查找

重点理解**:只有当需要修改时才复制一份数据,这就是 Copy-on-Write。容器层保存的是镜像变化的部分,不会对镜像本身做任何修改------所以多个容器共享同一个镜像,各改各的互不影响。


四、构建镜像的两种方法

4.1 方法一:docker commit(不推荐)

三步走:运行容器 → 修改容器 → 保存为镜像。以在 ubuntu 中安装 vim 为例:

bash 复制代码
# ① 运行容器(-it 交互模式进入终端)
docker run -it ubuntu

# ② 在容器中安装 vim
apt-get update
apt-get install -y vim

# ③ 另开一个终端,先查询容器名,然后把容器保存为镜像
docker ps  # 查询容器名
docker commit cool_darwin ubuntu-with-vim    # cool_darwin 是容器名

# 验证
docker images           # ubuntu-with-vim 189MB(ubuntu 原镜像 78.1MB,变大了)
docker run -it ubuntu-with-vim
which vim               # /usr/bin/vim ✓

为什么 Docker 不推荐 commit?

  1. 手工方式,容易出错、效率低、可重复性弱;
  2. 更重要的:使用者不知道镜像是怎么创建出来的,里面是否有恶意程序,无法审计,存在安全隐患。

但必须学它:即便用 Dockerfile 构建,底层也是通过一层层 commit 完成的。理解 commit 有助于深入理解构建过程和分层结构。

4.2 方法二:Dockerfile(推荐)

Dockerfile 是一个文本文件,记录了镜像构建的所有步骤。

基础语法规则:

  1. 每条保留字指令必须大写,且后跟至少一个参数;
  2. 指令从上到下顺序执行
  3. # 表示注释;
  4. 每条指令都会创建一个新的镜像层并提交
bash 复制代码
# 命令格式
docker build -f [Dockerfile路径] [构建上下文路径]

# 示例:-t 指定镜像名,. 表示 build context 为当前目录
docker build -t ubuntu-with-vim-dockerfile .

build context(构建上下文) :Docker 把该目录下的所有文件发送给 daemon,供 Dockerfile 中的 ADDCOPY 使用。注意不要把 //usr 作为 build context,否则构建会非常缓慢甚至失败。

第一个 Dockerfile(等价于上一节的 commit 效果):

dockerfile 复制代码
FROM ubuntu
RUN apt-get update && apt-get install -y vim
bash 复制代码
docker build -t ubuntu-with-vim-dockerfile .

构建时注意 RUN 的执行过程:Docker 会启动一个临时容器执行操作,并通过 commit 保存为新的镜像层 (输出中的 Running in xxx 就是临时容器)。用 docker history 可以看到新镜像比 ubuntu 多了一层(apt-get 创建的 111MB 层)。

4.3 Dockerfile 常用指令速查

指令 作用 格式
FROM 指定 base 镜像(第一条必须是 FROM FROM <image>:<tag>
MAINTAINER 注明镜像作者 MAINTAINER <name>
RUN 构建时运行命令(安装软件包) RUN <command>
COPY 从 build context 复制文件到镜像 COPY <src> <dest>
ADD 同 COPY,但 tar/zip 等归档文件会自动解压 ADD <src> <dest>
ENV 设置环境变量(可被后续指令使用) ENV <key> <value>
EXPOSE 声明容器中应用监听的端口 EXPOSE <port>
VOLUME 将目录声明为 volume VOLUME ["/data"]
WORKDIR 设置后续指令的工作目录(不存在会自动创建) WORKDIR /path
CMD 容器启动时执行的默认命令(可被 docker run 后参数替换,多个时最后一个生效) CMD command param
ENTRYPOINT 容器启动时执行的主命令(一定执行,CMD 或 run 参数作为其参数) ENTRYPOINT command param

一个综合示例:

dockerfile 复制代码
# my dockerfile
FROM busybox                 # 从 busybox 开始构建
MAINTAINER 6946630@qq.com    # 声明作者信息
WORKDIR /testdir             # 设置工作目录为 /testdir
RUN touch tmpfile1           # 创建 tmpfile1
COPY ["tmpfile2", "."]       # 复制 tmpfile2 到镜像
ADD ["passwd.tar.gz", "."]   # 复制并自动解压
ENV WELCOME "You are in my container,welcome!"

4.4 RUN / CMD / ENTRYPOINT 终极辨析

三句话记牢:

  • RUN:构建时执行命令,创建新的镜像层,常用于装软件;
  • CMD :容器启动后的默认命令,可被 docker run 后面的参数替换
  • ENTRYPOINT :容器启动时执行的主命令,一定会执行docker run 的参数会作为参数传给它。
指令 执行时机 核心作用 关键特性 Shell/Exec 格式注意点
RUN docker build 镜像构建阶段 执行命令,安装依赖、配置系统、生成新镜像层 每一条 RUN 都会新增一层镜像;容器运行时不会再执行 RUN多条 RUN 尽量合并,减少镜像层数 Shell:底层/bin/sh -c,解析环境变量Exec:直接调用程序,不走 shell,不自动解析$变量
CMD docker run 容器启动阶段 定义容器默认启动命令 可被docker run 镜像名 后面跟的指令直接完全替换多个 CMD 只生效最后一条 CMD 格式会作为 ENTRYPOINT 的参数;Exec 推荐使用
ENTRYPOINT docker run 容器启动阶段 定义容器固定入口主程序 不会被docker run后的命令覆盖;run 后的内容当作参数传给 ENTRYPOINT多个 ENTRYPOINT 只生效最后一条 一般推荐 Exec 格式,方便搭配 CMD 提供默认参数

两种写法格式(重要):

格式 写法 特点
Shell 格式 RUN apt-get install python3 底层调用 /bin/sh -c会解析环境变量
Exec 格式 RUN ["apt-get", "install", "python3"] 直接执行,不经过 shell,不会解析环境变量 (如 $name 不会被替换)
bash 复制代码
# Shell 格式:环境变量会被替换,输出 Hello,gqd
FROM busybox
ENV name gqd
ENTRYPOINT echo "Hello, $name"

# Exec 格式:环境变量不替换,输出 Hello, $name
FROM busybox
ENV name gqd
ENTRYPOINT ["/bin/echo", "Hello, $name"]

# 想用环境变量就用 sh -c 包一层
ENTRYPOINT ["/bin/sh", "-c", "echo Hello, $name"]
4.41 RUN

只在 build 构建镜像的时候执行,写在 Dockerfile 里,构建完就固化进镜像。

bash 复制代码
# Shell格式
RUN apt-get update && apt install -y nginx
# Exec格式
RUN ["apt-get","install","-y","nginx"]
4.42 CMD

CMD 的 3 种写法

  1. Shell 写法:CMD python app.py
  2. Exec 写法(推荐):CMD ["python","app.py"]
  3. 搭配 ENTRYPOINT 写法:CMD 只提供默认参数
bash 复制代码
ENTRYPOINT ["python","app.py"]
CMD ["--port","80"]
4.43 ENTRYPOINT
  • ENTRYPOINT 的 Exec 格式用于设置要执行的命令及其参数,同时可通过 CMD 提供额外的参数。
  • ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。
  • ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。
bash 复制代码
# Exec 格式用于设置要执行的命令及其参数,
[root@docker ~]# vim Dockerfile
FROM busybox
ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]

[root@docker ~]# docker build -t dockerfile6 .

[root@docker ~]# docker run -it dockerfile6
Hello world

[root@docker ~]# docker run -it dockerfile6 gqd
Hello gqd

# shell 格式
[root@docker ~]# vim Dockerfile
FROM busybox
ENTRYPOINT echo "Hello"
CMD ["world"]

[root@docker ~]# docker build -t dockerfile7 .

[root@docker ~]# docker run -it dockerfile7
Hello

[root@docker ~]# docker run -it dockerfile7 gqd
Hello

最佳实践 :应用/服务类镜像优先用 Exec 格式的 ENTRYPOINT 固定入口,用 CMD 提供可替换的默认参数 ;RUN 用来安装软件包(apt-get updateapt-get install 要放在同一个 RUN 中,避免用到过期的缓存层)。


五、镜像缓存特性与调试

5.1 缓存特性

Docker 会缓存已有镜像的镜像层 :构建或下载时,如果某层已存在,直接使用(Using cache),无需重新创建。

bash 复制代码
# 构建时加上 --no-cache 可强制不用缓存
docker build --no-cache -t ubuntu-with-vim-dockerfile .

关键规则:每一层都依赖下层,只要某一层发生变化,它上面所有层的缓存都会失效。 因此:

  • 修改或添加指令 → 后续层缓存失效;
  • 交换指令顺序(即使逻辑上镜像内容不变)→ Docker 也必须重建受影响的层;
  • docker pull 下载镜像时也会复用已有层(输出中显示 Already exists 的层就是复用的)。

实践启示 :把变动频繁的指令(如 COPY 代码)放在 Dockerfile 后面 ,把变动少的(如 RUN 装依赖)放在前面,可以最大化利用缓存、加快构建。

5.2 调试 Dockerfile

Dockerfile 执行失败时,前一条指令成功构建的镜像还在,可以基于它调试。经典案例:

dockerfile 复制代码
FROM busybox
RUN touch tmpfile
RUN /bin/bash -c "echo continue to build..."   # ← 失败:busybox 里没有 bash!
COPY testfile /
bash 复制代码
# 构建报错后,用失败指令之前的镜像层启动容器,手工执行定位
docker run -it busybox
# 发现 busybox 用的是 /bin/sh,没有 /bin/bash
# 修改为 /bin/sh 后重新构建即成功

六、镜像命名与 tag 最佳实践

6.1 镜像名称格式

bash 复制代码
[image name] = [repository]:[tag]

完整格式:[镜像仓库地址]/[Namespace]/[Repository]:[tag]

  1. Registry(仓库服务器地址):054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com 去哪里拉取镜像(华为云 SWR 镜像仓库域名)

  2. Namespace(命名空间):library 仓库内分组,隔离不同项目 / 用户,官方镜像默认 library

  3. Repository(repo,镜像仓库名):nginx 同一软件的镜像集合,同 repo 用 tag 区分版本

  4. Tag(标签):latest 版本标记,latest 只是标签名,不自动永远最新

bash 复制代码
054b8ac70e8010d90f2ac00ef29e6580.mirror.swr.myhuaweicloud.com/library/nginx:latest
└───────────────         仓库地址             ───────────────┘└namespace┘└repo┘└tag┘
  • docker build 未指定 tag 时,默认使用 latest
  • docker tag 可以给镜像打新标签:docker tag httpd httpd:v8.6

6.2 小心 latest 陷阱

latest 并没有特殊含义 ,只是"未指定 tag 时的默认值"。Docker Hub 上很多仓库把 latest 当作最新稳定版的别名,但这只是约定,不是强制规定 。生产环境建议明确指定 tag(如 httpd:2.3ubuntu:xenial),不要依赖 latest。

6.3 tag 版本方案(社区通用做法)

发布 v1.9.1 时打四个 tag:1.9.11.91latest;发布 v1.9.2 后把 1.91latest 挪过来;发布 v2.0.0 后新增 2.0.02.02 并把 latest 指向它:

bash 复制代码
docker tag myimage-v1.9.1 myimage:1
docker tag myimage-v1.9.1 myimage:1.9
docker tag myimage-v1.9.1 myimage:1.9.1
docker tag myimage-v1.9.1 myimage:latest

这样 myimage:1 始终指向 1 分支最新、myimage:1.9 指向 1.9.x 最新、myimage:latest 指向所有版本最新,具体版本随意选用。


七、镜像分发:Registry 实战

7.1 Docker Hub(公共 Registry)

bash 复制代码
# 1. 注册账号(hub.docker.com)
# 2. 登录
docker login
# 3. 镜像名必须包含用户名(官方镜像除外)
docker tag httpd:latest gaoqd/httpd:v1
# 4. 上传(只会上传本地新增的层,base 层已存在则跳过)
docker push gaoqd/httpd:v1
# 5. 其他 Host 即可下载使用
docker pull gaoqd/httpd:v1

7.2 华为云 SWR(国内公共/私有 Registry)

bash 复制代码
docker login -u <用户名> -p <密码> swr.cn-east-3.myhuaweicloud.com

# 镜像名格式:仓库地址/组织名/镜像名:版本
docker tag httpd:centos swr.cn-east-3.myhuaweicloud.com/gaoqiaodong/centos_httpd:v1
docker push swr.cn-east-3.myhuaweicloud.com/gaoqiaodong/centos_httpd:v1

# 删除本地镜像后再 pull 验证
docker pull swr.cn-east-3.myhuaweicloud.com/gaoqiaodong/centos_httpd:v1

7.3 搭建本地 Registry(一行命令)

bash 复制代码
# 启动 registry 容器(5000 是服务端口;-v 把镜像数据存到宿主机 /myregistry)
docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2

# 重命名镜像使其符合格式 [registry-host]:[port]/[username]/xxx
docker tag httpd:latest localhost:5000/httpd:v1

# 上传
docker push localhost:5000/httpd:v1

# 查看仓库里有哪些镜像
curl http://localhost:5000/v2/_catalog
# {"repositories":["httpd"]}

# 删除本地镜像后从自建仓库拉取
docker pull localhost:5000/httpd:v1

注意:用内网地址(如 192.168.137.99:1000)作私有仓库时,需要在 /etc/docker/daemon.json 配置 insecure-registries 并重启 Docker:

bash 复制代码
{
 "insecure-registries": ["192.168.137.99:1000"]
 }

7.4 企业级私有仓库 Harbor

Harbor 是 Docker Registry 的企业级封装:提供 Web UI、角色与用户权限管理、操作审计,还整合了 K8s 插件仓库。核心流程:

bash 复制代码
# 下载解压离线安装包
wget https://github.com/goharbor/harbor/releases/download/v2.9.1/harbor-offline-installer-v2.9.1.tgz
tar -xvf harbor-offline-installer-v2.9.1.tgz

# 部署到 /opt/harbor,导入镜像、修改配置(hostname/密码/注释 https)
mkdir /opt/harbor && mv harbor/* /opt/harbor/
cd /opt/harbor
docker load -i harbor.v2.9.1.tar.gz
cp -ar harbor.yml.tmpl harbor.yml && vim harbor.yml

# 安装(先 prepare 再 install,全部容器 healthy 即成功)
./prepare
./install.sh

# 使用:创建用户/项目 → 配置 insecure-registries → login → push/pull
docker login 192.168.108.30
docker tag nginx:latest 192.168.108.30/cloud/nginx:latest
docker push 192.168.108.30/cloud/nginx:latest
docker pull 192.168.108.30/cloud/nginx

# 卸载
docker compose down && rm -rf /data && rm -rf /opt/harbor

Harbor 常用功能:项目 (每个项目独立镜像地址)、用户管理 (不同项目不同维护人)、项目定额(限制仓库最大空间)。


八、综合案例:Dockerfile 实战三连

案例 1:制作带 SSH 的 CentOS 镜像(官方镜像默认不带 ssh)

dockerfile 复制代码
# centos.ssh.dockerfile
FROM centos:8.4.2105
MAINTAINER gaoqiaodong
# 替换 yum 源为阿里云 vault 源
RUN minorver=8.4.2105 && \
    sed -e "s|^mirrorlist=|#mirrorlist=|g" \
    -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.aliyun.com/centos-vault/$minorver|g" \
    -i.bak /etc/yum.repos.d/CentOS-*.repo
RUN yum install -y openssh-server
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key
RUN echo "root:huawei" | chpasswd
EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]
bash 复制代码
docker build -t centos:ssh -f centos.ssh.dockerfile .
docker run -d -p 2022:22 --name sshtest centos:ssh
ssh root@localhost -p 2022    # 登录成功

案例 2:自定义 httpd 镜像

dockerfile 复制代码
FROM centos:8.4.2105
MAINTAINER gaoqiaodong
RUN minorver=8.4.2105 && sed -e "s|^mirrorlist=|#mirrorlist=|g" \
    -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.aliyun.com/centos-vault/$minorver|g" \
    -i.bak /etc/yum.repos.d/CentOS-*.repo
RUN yum install -y httpd && yum clean all && rm -rf /var/cache/yum
COPY index.html /var/www/html/
EXPOSE 80
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]

案例 3:定制带 vim + ifconfig + JDK8 的 CentOS 镜像

dockerfile 复制代码
FROM centos:8.4.2105
MAINTAINER gaoqiaodong<6946630@qq.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 换 yum 源(同上)
RUN minorver=8.4.2105 && sed -e "s|^mirrorlist=|#mirrorlist=|g" \
    -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.aliyun.com/centos-vault/$minorver|g" \
    -i.bak /etc/yum.repos.d/CentOS-*.repo
RUN yum -y install vim
RUN yum -y install net-tools
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# 安装包 jdk-8u461-linux-x64.tar.gz 必须与 Dockerfile 在同一目录
ADD jdk-8u461-linux-x64.tar.gz /usr/local/java/
ENV JAVA_HOME /usr/local/java/jdk1.8.0_461
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD /bin/bash
bash 复制代码
docker build -t centosjava8:461 .
docker run -it centosjava8:461 /bin/bash
# pwd 输出 /usr/local(验证 WORKDIR),java -version 正常(验证 JDK)

九、镜像管理常用命令与 save/load

命令 作用
docker images 显示镜像列表
docker history 显示镜像构建历史(分层结构)
docker commit 从容器创建新镜像
docker build 从 Dockerfile 构建镜像
docker tag 给镜像打 tag
docker pull 从 Registry 下载镜像
docker push 上传镜像到 Registry
docker rmi 删除本地镜像(不会删除 Registry 中的镜像
docker search 命令行搜索 Docker Hub 镜像
docker save / docker load 镜像导出为 tar 文件 / 从 tar 导入(离线分发利器)

两个易错点:

  1. rmi 与多 tag :一个镜像对应多个 tag 时,删除其中一个 tag 只是删除标签,只有最后一个 tag 被删除时镜像才真正被删除
  2. save/load 离线迁移
bash 复制代码
# 导出(默认存 overlay2,直接拷贝文件系统很复杂,save 一步搞定)
docker save httpd -o httpd.tar
docker save httpd hello-world -o images.tar

# 导入(注意:本地已有同名镜像时会被覆盖)
docker rmi httpd:latest hello-world:latest
docker load -i httpd.tar

十、总结

本章要点回顾

  1. 最小镜像 hello-world 只有 13.3kB,FROM scratch 从零构建;
  2. base 镜像 = 精简 rootfs + 复用宿主 kernel,所以 CentOS 才 204MB;容器只能使用且不能修改宿主 kernel;
  3. 分层结构 :镜像由只读镜像层 + 可写容器层构成,分层带来共享资源的高效;
  4. Copy-on-Write:容器层只记录变化(whiteout 遮挡删除、复制修改),镜像层永不被修改;
  5. 两种构建方式docker commit(直观但不可审计、不推荐)vs Dockerfile(推荐,每条指令一层);
  6. 缓存特性 :层不变则缓存复用,指令顺序变化会破坏缓存;RUN 装依赖放前面,COPY 代码放后面
  7. 命名规范[仓库地址]/[namespace]/[repository]:[tag],慎用 latest,多 tag 版本方案更专业;
  8. 分发链路:Docker Hub / 华为云 SWR / 本地 registry:2 / 企业级 Harbor。
相关推荐
吴声子夜歌1 小时前
Shell编程——数组
linux·运维·shell
2401_891957311 小时前
简单了解多路转接select
运维·服务器
2302_1112 小时前
java依赖小结
java·运维·数据库
程序员-Benothing2 小时前
Shell 脚本条件判断与流程控制:if for while case 详解
linux·运维·服务器
IT摆渡者3 小时前
基于 Docker 部署 Nextcloud 私有云盘(外挂 Windows 共享)项目实践
运维·docker·容器
做运维的阿瑞3 小时前
Linux下DNS服务器搭建
linux·运维·服务器
团子股股东峥哥3 小时前
day37-RHEL-管理基本存储
linux·运维·服务器
天远API3 小时前
零信任架构实战:基于天远全能消金报告构建自动化消费分期网关
运维·人工智能·架构·自动化
Android系统攻城狮4 小时前
Linux Gstreamer深度解析之gst_audio_converter_reset调用流程与实战(二十一)
linux·运维·服务器·gstreamer音视频·音视频进阶