docker镜像与dockerfile
一、docker镜像
1.什么是镜像
容器解决应用开发、测试和部署的问题,而镜像解决应用部署环境问题。镜像是一个只读的容器模板, 打包了应用程序和应用程序所依赖的文件系统以及启动容器的配置文件,是启动容器的基础。镜像所打 包的文件内容就是容器的系统运行环境——rootfs(根文件系统或根目录)。容器与镜像类似对象与类的关系。
2.docker镜像原理
1. 分层: Docker镜像采用分层的方式构建,每一个镜像都由一组镜像组合而成。每一个镜像层都可 以被需要的镜像所引用,实现了镜像之间共享镜像层的效果。这样的分层设计在镜像的上传与下载 过程当中有效的减少了镜像传输的大小,在传输过程当中本地或注册中心只需要存在一份底层的基 础镜像层即可,真正被保存和下载的内容是用户构建的镜像层。而在构建过程中镜像层通常会被缓 存以缩短构建过程。
2. 写时复制:底层镜像层在多个容器间共享,每个容器启动时不需要复制一份镜像文件,而是将所有 需要的镜像层以只读的方式挂载到一个挂载点,在只读层上再覆盖一层读写层。在容器运行过程中 产生的新文件将会写入到读写层,被修改过的底层文件会被复制到读写层并且进行修改,而老文件 则被隐藏。
3. 联合挂载:docker采用联合挂载技术,在同一个挂载点同时挂载多个文件系统,从而使得容器的根 目录看上去包含了各个镜像层的所有文件。如下图:
4. 内容寻址:根据镜像层内容计算校验和,生成一个内容哈希值,并使用该值来充当镜像层ID、索引 镜像层。内容寻址提高了镜像的安全性,在pull、push和load、save操作后检测数据的完整性。另 外基于内容哈希来索引镜像层,对于来自不同构建的镜像层,只要拥有相同的内容哈希值,就能被 不同的镜像所引用。
docker的镜像和容器的关系不是类和对象的关系而是相同的引用,以下是详细讲解:
一、“类与对象” 类比的局限性
1. 类与对象的本质特征
- 类(Class):是抽象模板,定义属性和方法,不占用实际资源;
- 对象(Object):是类的实例化结果,每个对象独立拥有数据空间,修改对象不会影响类或其他对象。
- 核心逻辑:对象是类的 “复制”,彼此隔离。
2. Docker 镜像与容器的实际机制
- 镜像(Image):是只读的文件系统快照,由多层只读层叠加而成(基于 UnionFS);
- 容器(Container):是镜像的运行实例,在镜像顶层添加一个可写层(
container layer
)。- 核心逻辑:容器并非镜像的 “复制”,而是共享镜像的底层只读层,仅添加独立的可写层。
二、“相同引用” 的本质:分层存储与共享机制
1. UnionFS 分层架构
Docker 使用 UnionFS(如 Overlay2)实现镜像和容器的分层存储:
- 镜像层(只读):多个镜像可共享底层基础层(如
ubuntu:latest
的文件系统),类似 Linux 的硬链接,多个引用指向同一物理存储;- 容器层(可写):每个容器在镜像顶层添加一个专属的可写层,用于存储运行时修改(如写入文件、环境变量变更)。
2. “相同引用” 的具体体现
- 底层共享:当多个容器基于同一镜像启动时,它们共享镜像的所有只读层(即底层文件系统的引用相同),而非各自复制一份镜像数据。例如:
bash
# 启动两个容器基于同一镜像 docker run -d ubuntu:22.04 sleep 3600 docker run -d ubuntu:22.04 sleep 3600
这两个容器的底层文件系统(如/bin/bash
、/etc
)实际指向同一份物理存储,仅各自的可写层独立。- 空间效率:这种共享机制大幅节省磁盘空间,避免镜像复制的冗余(类似 Linux 系统中多个进程共享同一可执行文件的内存映射)。
3. 与 “类与对象” 的关键差异
对比维度 类与对象关系 镜像与容器关系 数据隔离性 对象完全独立,无共享数据 容器共享镜像的只读层数据 修改影响 修改对象不影响类或其他对象 容器修改仅作用于自身可写层 资源占用 每个对象独立占用内存 多个容器共享底层镜像资源 创建逻辑 对象是类的 “实例化复制” 容器是镜像的 “引用 + 扩展”
3. docker镜像关键概念
1. registry:注册中心,用来保存docker镜像,其中包括镜像的层次结构和关于镜像的元数据。
2. repository:仓库,即由具有某个功能的Docker镜像的所有迭代版本构成的镜像组。
3. manifest:Docker镜像元数据文件,在pull、push、save和load中作为镜像结构和基础信息的描 述文件。在镜像被pull或者load到Docker宿主机时,manifest被转化为本地的镜像配置文件config
4. image:镜像,用来存储一组相关的元数据信息,主要包括镜像的架构(如amd64)、镜像默认配 置信息、构建镜像的容器配置信息、包含所有镜像层的rootfs。
5. layer:镜像层,是docker用来管理镜像的中间概念,镜像是由镜像层组成的,单个镜像层可以被 多个镜像和容器共享。
6. dockerfile:是一个镜像制作过程的定义,文档包含了镜像制作的所有命令和完整操作流程
1. Registry(注册中心)
定义与作用
Registry 是 Docker 生态中的镜像存储服务,负责集中保存 Docker 镜像的完整数据(包括镜像层、元数据、清单等)。它是镜像的「云端仓库」,支持镜像的上传(
push
)、下载(pull
)、删除等操作。核心特性
- 存储结构:以分层方式存储镜像数据,支持内容寻址(通过镜像层的哈希值索引),避免重复存储;
- 元数据管理:保存镜像的
manifest
(清单)、标签(tag)、版本信息等;- 多租户支持:可部署公共 Registry(如 Docker Hub)或私有 Registry(如 Harbor、AWS ECR)。
如何查看与使用?
公共 Registry(Docker Hub):
访问 hub.docker.com 可浏览官方仓库(如nginx
、ubuntu
)和用户仓库。例如,搜索nginx
会显示其所有标签(latest
、alpine
等)。私有 Registry 操作:
若部署了私有 Registry(如 Harbor),需先登录:docker login my-private-registry.com:5000 # 输入账号密码
上传镜像到私有 Registry:
docker tag nginx:alpine my-private-registry.com/my-team/nginx:alpine # 重命名镜像为私有仓库路径 docker push my-private-registry.com/my-team/nginx:alpine # 上传
2. Repository(仓库)
定义与作用
Repository(仓库)是 Registry 中的镜像逻辑分组单元,由同一功能镜像的所有迭代版本(不同标签)组成。例如,
nginx
仓库包含nginx:latest
、nginx:alpine
、nginx:1.25
等不同标签的镜像。核心特性
- 标签(Tag):仓库中的每个镜像版本通过标签区分(如
latest
是默认标签);- 命名规则:仓库名通常为
[用户/组织名]/[镜像名]
(如docker/library/nginx
是官方仓库,myuser/myapp
是个人仓库)。如何查看与使用?
查看仓库中的标签:
- 公共仓库:在 Docker Hub 页面直接查看标签列表(如 nginx 标签页);
- 私有仓库:通过命令行或 Registry 管理界面(如 Harbor 的 Web 界面)查看。
拉取仓库中的特定标签:
docker pull nginx:alpine # 拉取 nginx 仓库的 alpine 标签镜像
3. Manifest(清单)
定义与作用
Manifest 是镜像的元数据描述文件,JSON 格式,记录镜像的核心信息,包括:
- 镜像的配置文件(
config
)的哈希值;- 所有镜像层(
layers
)的哈希值列表(按堆叠顺序);- 镜像的架构(如
amd64
)、操作系统(如linux
)等元数据。核心作用
- 镜像传输的「地图」:当执行
docker pull
或docker push
时,首先传输 Manifest,通过其中的层哈希值确认需要下载 / 上传的层;- 本地镜像的「索引」:下载后,Manifest 会被转换为本地镜像的配置文件(
config.json
),记录镜像的运行时默认配置(如入口命令、环境变量)。如何查看与使用?
查看远程镜像的 Manifest(需镜像未开启内容信任):
docker manifest inspect nginx:alpine
输出示例(简化):
{"schemaVersion": 2,"mediaType": "application/vnd.docker.distribution.manifest.v2+json","config": {"mediaType": "application/vnd.docker.container.image.v1+json","size": 7023,"digest": "sha256:abc123..." # 镜像配置文件的哈希},"layers": [{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 2815,"digest": "sha256:def456..." # 第一层的哈希},{"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip","size": 1234,"digest": "sha256:ghi789..." # 第二层的哈希}] }
查看本地镜像的 Manifest(需通过
docker inspect
间接获取):docker inspect nginx:alpine | jq '.[0].RootFS' # 查看镜像层的哈希列表
4. Image(镜像)
定义与作用
Image(镜像)是 Docker 容器的只读模板,由以下部分组成:
- 配置信息(
config.json
):包括镜像的架构、默认命令(CMD
)、入口点(ENTRYPOINT
)、环境变量(ENV
)等;- 根文件系统(RootFS):由多个只读镜像层(
layer
)堆叠而成,定义容器的初始文件系统;- 元数据:如镜像创建时间、作者、标签等。
核心特性
- 不可变性:镜像一旦构建完成,内容不可修改(修改需生成新镜像);
- 分层结构:基于内容寻址的镜像层共享机制,节省存储(详见下文
layer
部分)。如何查看与使用?
查看本地镜像列表:
docker images # 显示所有本地镜像(仓库名、标签、镜像 ID、大小等)
查看镜像详细信息:
docker inspect nginx:alpine # 输出镜像的完整配置(JSON 格式)
关键字段:
Config
:镜像的默认运行配置(如Cmd
、Env
);RootFS.Layers
:镜像层的哈希列表(与 Manifest 中的layers
一致);Created
:镜像的创建时间。
5. Layer(镜像层)
定义与作用
Layer(镜像层)是镜像的最小存储单元,对应 Dockerfile 中一条指令(如
RUN
,COPY
)的文件系统变更。每个层是一个只读的tar
压缩包,包含该指令对文件系统的修改(新增、删除、修改的文件 / 目录)。核心特性
- 内容寻址:每个层的哈希值由其内容(文件数据 + 元数据)计算生成(SHA256),相同内容的层会被不同镜像共享;
- 堆叠顺序:层按 Dockerfile 指令顺序堆叠,上层覆盖下层的同名文件;
- 可写层隔离:容器运行时,会在镜像层顶部添加一个可写层(
container layer
),所有运行时修改仅作用于该层。如何查看与使用?
查看镜像的层历史:
docker history nginx:alpine
输出示例:
IMAGE CREATED CREATED BY SIZE COMMENT sha256:abc123 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B <missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80/tcp 0B <missing> 2 weeks ago /bin/sh -c apt-get update && apt-get install… 123MB ...
每行对应一个镜像层,
CREATED BY
显示该层对应的 Dockerfile 指令,SIZE
是层的大小。查看层的具体内容(需借助工具):
可通过docker image inspect
获取层的哈希,然后到 Docker 存储目录(如/var/lib/docker/overlay2
)查看层的实际文件:# 获取镜像层的哈希列表 docker inspect nginx:alpine | jq '.[0].RootFS.Layers' # 输出类似:["sha256:def456...", "sha256:ghi789..."]# 查看层的内容(需 root 权限) ls /var/lib/docker/overlay2/def456.../diff # 该目录是层的解压内容
6. Dockerfile
定义与作用
Dockerfile 是镜像构建的脚本文件,包含一系列指令(如
FROM
,RUN
,COPY
),定义了镜像的构建步骤和配置。Docker 通过解析 Dockerfile 逐行执行指令,生成镜像层,最终构建出完整镜像。核心指令与示例
常见 Dockerfile 指令:
FROM
:指定基础镜像(如FROM ubuntu:22.04
);RUN
:执行命令(如RUN apt-get update
);COPY
:复制文件到镜像(如COPY app/ /app/
);CMD
:定义容器默认启动命令(如CMD ["nginx", "-g", "daemon off;"]
);EXPOSE
:声明容器监听的端口(如EXPOSE 80
)。如何使用?
编写 Dockerfile(示例:构建一个简单的 Nginx 镜像):
# 基础镜像 FROM nginx:alpine# 复制自定义配置到镜像 COPY nginx.conf /etc/nginx/nginx.conf# 声明端口(仅文档作用) EXPOSE 80# 容器启动时执行的命令(继承自基础镜像的 CMD)
构建镜像:
docker build -t my-nginx:v1 . # -t 指定镜像标签,. 表示 Dockerfile 在当前目录
验证构建结果:
docker images | grep my-nginx # 查看新构建的镜像 docker history my-nginx:v1 # 查看镜像层是否包含 COPY 指令生成的层
总结:概念间的协同关系
- 构建阶段:用户编写
Dockerfile
,Docker 逐行执行指令生成layer
(镜像层),最终构建为image
(镜像);- 存储阶段:镜像的
manifest
(清单)描述其层结构和元数据,镜像与层被存储在registry
(注册中心)的repository
(仓库)中;- 分发与运行阶段:用户通过
docker pull
从 Registry 下载镜像(基于 Manifest 拉取层),启动容器时镜像的只读层与容器的可写层合并,形成运行环境。
4. 镜像管理命令
Docker 镜像的全生命周期管理(构建、查看、标记、导出 / 导入、删除)依赖一系列核心命令。以下按功能分类,详细讲解每个命令的作用、常用选项、操作示例及注意事项,帮助你高效管理镜像。
一、镜像构建:
docker build
功能
通过解析
Dockerfile
脚本,逐层构建镜像。支持分层缓存、参数传递、资源限制等高级特性,是镜像创建的标准方式(推荐替代docker commit
)。常用选项(标⭐为高频使用)
选项 说明 -t, --tag
⭐指定镜像的标签(格式: 仓库名:标签
,如myapp:v1
);可多次使用设置多个标签。-f, --file
⭐显式指定 Dockerfile
路径(默认当前目录的Dockerfile
)。--no-cache
⭐禁用构建缓存(强制重新构建所有层,用于解决缓存导致的镜像内容不同步)。 --build-arg
设置构建时变量(需在 Dockerfile
中用ARG
声明)。--pull
构建前尝试拉取基础镜像的最新版本(避免使用本地旧版本)。 --rm
构建成功后删除中间容器(默认 true
,节省磁盘空间)。--force-rm
无论构建是否成功,总是删除中间容器(避免中间容器残留)。 操作示例
假设当前目录有
Dockerfile
(内容如下):# 基础镜像 FROM nginx:alpine# 复制自定义配置到镜像 COPY nginx.conf /etc/nginx/nginx.conf# 声明端口(仅文档作用) EXPOSE 80# 容器启动命令(继承自基础镜像)
构建命令:
# 基本构建(标签为 my-nginx:v1,使用当前目录的 Dockerfile) docker build -t my-nginx:v1 .# 显式指定 Dockerfile 路径(假设文件在 ./docker/ 目录) docker build -t my-nginx:v1 -f ./docker/Dockerfile .# 禁用缓存并设置构建变量(假设 Dockerfile 中有 ARG VERSION) docker build -t my-nginx:v1 --no-cache --build-arg VERSION=1.25 .
注意事项
- 构建上下文(PATH):命令中的
.
表示构建上下文路径,Docker 会将该路径下的所有文件打包传给 Docker 守护进程(避免在上下文中放入大文件,否则会影响构建速度)。- 缓存机制:默认情况下,若某层的内容未变(哈希匹配),则直接复用缓存层,加速构建。若需强制更新某层(如
RUN apt-get update
),需用--no-cache
。- 中间容器:构建过程中会生成临时容器(用于执行
RUN
指令),默认构建成功后删除(--rm=true
),失败时保留(便于调试)。
二、镜像查看:
docker images
功能
列出本地已下载或构建的镜像,支持过滤、格式化输出,可查看镜像的标签、大小、创建时间等信息。
常用选项(标⭐为高频使用)
选项 说明 -a, --all
⭐显示所有镜像(包括中间层镜像,默认过滤中间层)。 --filter
⭐过滤镜像(如 dangling=true
查看悬空镜像,label=key=value
按标签过滤)。--format
自定义输出格式(使用 Go 模板,如 {{.Repository}}:{{.Tag}}
)。-q, --quiet
仅显示镜像 ID(用于脚本自动化)。 --digests
显示镜像的摘要(SHA256 哈希,用于验证镜像完整性)。 操作示例
# 查看所有本地镜像(含标签、大小等) docker images# 查看所有镜像(含中间层) docker images -a# 过滤悬空镜像(无标签、未被引用的镜像,通常是构建失败的残留) docker images --filter dangling=true# 仅显示镜像 ID docker images -q# 格式化输出(显示仓库:标签 及 大小) docker images --format "{{.Repository}}:{{.Tag}} \t {{.Size}}"
注意事项
- 悬空镜像(Dangling Image):指无标签(
<none>:<none>
)且未被其他镜像引用的镜像,通常由覆盖标签(如docker tag old-image new-tag
)或构建失败导致,可通过docker image prune
清理。- 中间层镜像:构建过程中生成的临时层(
docker images -a
可见),默认被过滤,可通过--no-cache
避免生成冗余中间层。
三、镜像删除:
docker rmi
功能
删除本地镜像。若镜像被容器引用(即使容器已停止),需先删除容器或强制删除镜像。
常用选项
选项 说明 -f, --force
强制删除镜像(即使被容器引用,需先停止容器)。 --no-prune
不删除该镜像的中间层(默认会删除未被其他镜像引用的中间层)。 操作示例
# 删除指定标签的镜像 docker rmi my-nginx:v1# 强制删除被容器引用的镜像(需先停止容器) docker stop my-container # 停止容器 docker rmi -f my-nginx:v1 # 强制删除镜像# 删除多个镜像(通过镜像 ID 或标签) docker rmi my-nginx:v1 nginx:alpine# 删除所有无标签的悬空镜像(等价于 docker image prune) docker rmi $(docker images -q --filter dangling=true)
注意事项
- 依赖关系:若镜像被其他镜像引用(如作为基础镜像),需先删除依赖它的镜像,否则无法删除。
- 容器关联:运行中的容器依赖的镜像无法删除(会报错
conflict: unable to remove repository reference
),需先停止并删除容器(docker rm
)。
四、镜像标记:
docker tag
功能
为本地镜像添加或修改标签,方便分类管理(如标记为测试版、生产版)或推送至不同仓库(如私有 Registry)。
操作示例
# 为镜像添加新标签(原标签保留) docker tag my-nginx:v1 my-nginx:latest # 同时存在 my-nginx:v1 和 my-nginx:latest# 标记镜像到私有 Registry(以便推送) docker tag my-nginx:v1 private-registry.com/my-team/my-nginx:v1# 覆盖已有标签(原标签指向的镜像变为悬空镜像) docker tag my-nginx:v1 my-nginx:old # 原 my-nginx:old 若存在,会变为 <none>:<none>
注意事项
- 标签的意义:标签是镜像的「别名」,不影响镜像本身(镜像的唯一标识是
IMAGE ID
)。- 推送准备:推送镜像到私有 Registry 前,需通过
docker tag
将镜像重命名为私有仓库路径:标签
(如private-registry.com/user/repo:tag
)。
五、镜像导出与导入:
docker save
/docker load
功能
docker save
:将本地镜像打包为tar
文件,用于离线传输或备份。docker load
:从tar
文件中导入镜像到本地(恢复备份或传输后的镜像)。操作示例
# 导出单个镜像为 tar 文件(包含镜像层和元数据) docker save -o my-nginx.tar my-nginx:v1# 导出多个镜像(用空格分隔) docker save -o all-images.tar my-nginx:v1 nginx:alpine# 导入 tar 文件到本地镜像库(会保留原标签) docker load -i my-nginx.tar
注意事项
- 文件大小:
tar
文件包含镜像的所有层,体积较大(与镜像大小一致)。- 跨平台兼容性:导出的镜像仅适用于相同架构(如
amd64
)的 Docker 环境,跨架构需重新构建。
六、归档文件创建镜像:
docker import
功能
从
tar
归档文件(如docker export
导出的容器文件系统)创建镜像。与docker save
不同,docker import
会丢失镜像的层信息,生成一个单层镜像。操作示例
# 从容器导出文件系统(得到容器的当前文件系统快照) docker export my-container > container.tar# 从归档文件创建镜像(无层信息,仅单层) docker import -c "CMD [\"nginx\", \"-g\", \"daemon off;\"]" container.tar my-nginx:imported
注意事项
- 适用场景:仅用于从容器文件系统快照恢复镜像(如迁移容器状态),不推荐替代
docker build
(无法利用分层缓存)。- 元数据丢失:导入的镜像会丢失原镜像的构建历史(
docker history
仅显示一层),且无法复用其他镜像的层。
七、容器创建镜像:
docker commit
功能
从运行中的容器创建新镜像(将容器的可写层保存为镜像层)。适用于快速保存容器的临时状态,但不推荐用于正式镜像构建(无法复现构建过程)。
常用选项
选项 说明 -a, --author
镜像作者(如 --author "Nick <nick@example.com>"
)。-m, --message
提交说明(类似 Git 的 commit -m
)。-p, --pause
提交时暂停容器(默认 true
,避免提交过程中容器状态变化)。操作示例
# 启动容器并修改内容(如修改 nginx 配置) docker run -it --name my-container nginx:alpine /bin/bash echo "server { listen 80; }" > /etc/nginx/nginx.conf # 修改配置 exit# 从容器提交新镜像 docker commit -a "Nick" -m "Custom nginx config" my-container my-nginx:committed
注意事项
- 不可复现性:
commit
生成的镜像无法通过Dockerfile
复现,后续修改需再次commit
,难以维护。- 镜像体积:每次
commit
会生成新层,可能导致镜像体积膨胀(推荐用docker build
分层构建)。
总结:命令使用场景建议
需求 推荐命令 原因 标准镜像构建 docker build
可复现、支持分层缓存、易于维护。 临时保存容器状态 docker commit
快速保存,但不推荐长期使用。 镜像离线传输 / 备份 docker save
+docker load
保留镜像层信息,支持恢复原镜像。 从容器文件系统创建镜像 docker import
仅用于迁移容器状态,无法复用分层机制。 镜像标签管理 docker tag
灵活标记,便于分类和推送。 镜像删除 docker rmi
清理冗余镜像,注意依赖关系。
5. 镜像仓库命令
Docker 镜像的全生命周期管理不仅包括本地构建与查看,还涉及远程仓库交互(登录、拉取、推送)、镜像分享(公共仓库 / 本地导出 / 私有仓库)等关键操作。以下结合命令详解、操作示例与最佳实践,完整梳理这一流程。
一、远程仓库认证:
docker login
与docker logout
核心作用
docker login
:登录到 Docker 镜像仓库(如 Docker Hub 或私有 Registry),获取上传 / 下载权限;docker logout
:退出当前登录的仓库,避免凭证泄露。命令格式与选项
# 登录(默认 Docker Hub) docker login [OPTIONS] [SERVER]# 登出(默认 Docker Hub) docker logout [OPTIONS] [SERVER]
常用选项:
-u, --username
:登录用户名;-p, --password
:登录密码(或 Token);- (注意:直接使用
-p
可能泄露密码,推荐交互式输入或使用--password-stdin
从标准输入读取)操作示例
# 登录 Docker Hub(交互式输入密码) docker login # 输入用户名后,输入密码(隐藏输入)# 非交互式登录(仅用于脚本,需谨慎保管密码) docker login -u "myuser" -p "mypassword"# 登录私有 Registry(如 localhost:5000) docker login http://localhost:5000 -u "testuser" -p "testpwd"# 退出 Docker Hub docker logout# 退出私有 Registry docker logout http://localhost:5000
注意事项
- 登录凭证默认存储在
~/.docker/config.json
(Linux/macOS)或%USERPROFILE%\.docker\config.json
(Windows),包含仓库地址和加密后的密码;- 若私有 Registry 使用自签名证书,需配置 Docker 信任该证书(或通过
--insecure-registry
跳过校验,见后文私有仓库部分)。
二、镜像远程操作:
docker pull
与docker push
核心作用
docker pull
:从远程仓库下载镜像到本地;docker push
:将本地镜像上传到远程仓库(需先登录)。命令格式与选项
# 拉取镜像(默认 Docker Hub) docker pull [OPTIONS] NAME[:TAG|@DIGEST]# 推送镜像(默认 Docker Hub) docker push [OPTIONS] NAME[:TAG]
常用选项:
-a, --all-tags
:拉取仓库中所有标签的镜像(如docker pull -a nginx
拉取nginx:latest
、nginx:alpine
等);--disable-content-trust
:跳过镜像内容校验(默认开启,确保镜像未被篡改);操作示例
# 拉取 Docker Hub 的 nginx:latest 镜像(默认标签为 latest) docker pull nginx# 拉取指定标签的镜像(如 nginx:alpine) docker pull nginx:alpine# 拉取指定摘要(Digest)的镜像(确保内容绝对一致) docker pull nginx@sha256:abc123... # 从 manifest 中获取摘要# 推送本地镜像到 Docker Hub(需先登录) docker push myuser/myapp:v1# 推送镜像到私有 Registry(需先打标签为私有仓库路径) docker tag myapp:v1 localhost:5000/myuser/myapp:v1 docker push localhost:5000/myuser/myapp:v1
注意事项
- 拉取镜像时,若本地已有相同哈希的镜像层,会直接复用(基于内容寻址),节省下载时间;
- 推送镜像时,仅上传本地不存在于远程仓库的镜像层(增量上传),提升效率;
- 若镜像被标记为多个标签(如
myapp:v1
和myapp:latest
),推送时需分别推送每个标签。
三、镜像搜索:
docker search
核心作用
在 Docker Hub 搜索公开镜像,支持过滤(如官方镜像、高星镜像),快速定位所需镜像。
命令格式与选项
docker search [OPTIONS] TERM
常用选项:
--automated
:仅显示自动构建的镜像(由 Dockerfile 自动构建,非手动commit
);--no-trunc
:显示完整的镜像描述(默认截断);-f, --filter
:过滤条件(如stars=10
表示收藏数≥10);操作示例
# 搜索名称包含 "nginx" 的镜像 docker search nginx# 搜索收藏数≥10 的 nginx 镜像 docker search -f stars=10 nginx# 仅显示自动构建的 nginx 镜像 docker search --automated nginx# 显示完整描述(不截断) docker search --no-trunc nginx
输出示例:
NAME DESCRIPTION STARS OFFICIAL AUTOMATED nginx Official build of Nginx. 20000 [OK] bitnami/nginx Bitnami nginx Docker Image 1500 [OK] ...
四、镜像分层与联合挂载实践(以 Nginx 为例)
Docker 镜像的分层机制是其轻量化的核心,通过
docker save
、解压镜像包和查看manifest
,可以直观理解镜像层的结构。操作步骤
运行 Nginx 容器:
docker run -d -p 81:80 --name mynginx1 nginx:latest
进入容器查看文件系统:
docker exec -it mynginx1 /bin/bash ls /etc/nginx # 查看 Nginx 配置文件(来自镜像层) exit
保存镜像为 tar 归档:
docker save -o nginx.tar nginx:latest
解压并查看镜像层:
mkdir nginx-layers && tar -xvf nginx.tar -C nginx-layers
解压后会得到多个文件:
manifest.json
:镜像的清单文件(记录层哈希、配置文件路径);config.json
:镜像的配置文件(记录镜像元数据);- 多个
sha256-xxx.tar
文件:每个文件对应一个镜像层(压缩的文件系统变更)。分析
manifest.json
(简化示例):[{"Config": "config.json","RepoTags": ["nginx:latest"],"Layers": ["sha256:layer1.tar", # 基础层(如 Alpine Linux)"sha256:layer2.tar", # Nginx 安装层"sha256:layer3.tar" # 其他配置层]} ]
对比
docker commit
生成的镜像:
- 运行容器并修改文件(如添加
test.txt
):docker exec mynginx1 touch /test.txt
- 提交容器为新镜像:
docker commit -m "Add test.txt" mynginx1 mynginx:v1
- 保存并解压
mynginx:v1
的镜像包,会发现manifest.json
中新增了一个层(对应test.txt
的变更),而底层的 Nginx 层与原镜像完全一致(哈希相同,证明层复用)。关键结论
- 镜像由多个只读层堆叠而成,相同内容的层会被不同镜像共享(节省存储);
docker commit
会生成新层(记录容器可写层的变更),但无法复现构建过程(不推荐用于生产)。
五、镜像分享的三种方式
方式 1:公共仓库(Docker Hub)分享
适用于公开分享镜像,需注册 Docker Hub 账号并创建仓库。
操作步骤:
创建 Docker Hub 仓库:
登录 hub.docker.com,点击Create Repository
,输入仓库名(如myuser/mynginx
)。本地镜像打标签:
docker tag mynginx:v1 myuser/mynginx:v1
推送镜像到 Docker Hub:
docker login # 登录 Docker Hub docker push myuser/mynginx:v1
拉取验证(任意设备):
docker pull myuser/mynginx:v1
方式 2:本地导出 / 导入(
docker save
+docker load
)适用于离线传输或备份镜像(无需网络)。
操作步骤:
导出镜像为 tar 文件:
docker save -o myimages.tar mynginx:v1 mysql:5.7 # 导出多个镜像
传输文件(通过 U 盘、局域网等)。
导入镜像到目标设备:
docker load -i myimages.tar # 导入后镜像标签和层信息完整保留
方式 3:私有注册中心分享
适用于企业内部私有镜像管理,需搭建私有 Registry 并配置认证。
搭建步骤(以本地 Registry 为例):
启动 Registry 容器(无认证版本):
docker run -d -p 5000:5000 --name registry --restart=always registry:2
验证 Registry 状态:
curl http://localhost:5000/v2/_catalog # 应返回 {"repositories":[]}(空仓库)
配置认证(可选):
使用htpasswd
生成密码文件(需安装apache2-utils
):sudo apt install apache2-utils # Linux htpasswd -Bbn testuser testpwd > /path/to/auth/htpasswd # 生成密码文件
启动带认证的 Registry:
docker run -d -p 5000:5000 --name registry --restart=always \-v /path/to/data:/var/lib/registry \ # 数据卷(持久化存储)-v /path/to/auth:/auth \ # 密码文件路径-e "REGISTRY_AUTH=htpasswd" \ # 启用 htpasswd 认证-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \registry:2
配置 Docker 信任私有 Registry(解决 HTTPS 校验问题):
- 编辑 Docker 服务配置(Linux):
sudo vim /lib/systemd/system/docker.service
- 在
ExecStart
行末尾添加--insecure-registry localhost:5000
(允许不安全连接):ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry localhost:5000
- 重启 Docker 服务:
sudo systemctl daemon-reload sudo systemctl restart docker
推送与拉取镜像:
# 登录私有 Registry docker login http://localhost:5000 -u testuser -p testpwd# 打标签 docker tag mynginx:v1 localhost:5000/myuser/mynginx:v1# 推送 docker push localhost:5000/myuser/mynginx:v1# 拉取(其他设备需配置相同的 --insecure-registry) docker pull localhost:5000/myuser/mynginx:v1
二、 dockerfile
1.dockerfile是什么
Dockerfile是一个创建镜像所有命令的文本文件, 包含了一条条指令和说明, 每条指令构建一层, 通过 docker build命令,根据Dockerfile的内容构建镜像,因此每一条指令的内容, 就是描述该层如何构建.有了 Dockefile, 就可以制定自己的docker镜像规则,只需要在Dockerfile上添加或者修改指令, 就可生成 docker 镜像。
2. dockerfile解决了什么问题
Dockerfile 包含了镜像制作的完整操作流程,其他开发者可以通过 Dockerfile 了解并复现制作过程 Dockerfile 中的每一条指令都会创建新的镜像层,这些镜像可以被 Docker Daemon 缓存。再次制作镜 像时,Docker 会尽量复用缓存的镜像层(using cache),而不是重新逐层构建,这样可以节省时间和 磁盘空间 Dockerfile 的操作流程可以通过docker image history [镜像名称]查询,方便开发者查看变更记录。
3. docker build 构建流程
一、Docker build 核心流程
第一步:准备上下文(Context)并处理文件
打包上下文文件
docker build
会将指定的上下文目录(context)中的文件打包,发送给 Docker 引擎(Docker daemon)。- 若上下文中存在
.dockerignore
文件,会按规则过滤掉不需要上传的文件(如日志、临时文件等)。例外规则
- 若
.dockerignore
文件中包含.dockerignore
或Dockerfile
本身,这两个文件不会被过滤,仍会被上传。镜像标签验证
- 若指定了镜像标签(
-t
参数),会先验证仓库名(repository)和标签(tag)的格式是否合法。第二步:发送构建请求至 Docker 引擎
- 通过 HTTP 请求向 Docker server 发送构建指令,请求中包含上下文信息(打包后的文件)。
第三步:Docker 引擎执行镜像构建
解压上下文文件
- 在临时目录中解压上下文文件,作为构建的基础环境。
解析 Dockerfile 指令
- 读取并解析
Dockerfile
中的每条指令,按指令类型(如RUN
、COPY
、ADD
等)分发到对应模块处理。逐层构建镜像层
- 对每条指令,创建临时容器并执行指令,然后将容器状态提交(commit)为一个新的镜像层。
- 例如:
RUN apt-get update
会生成一个包含更新操作的镜像层。合并镜像层
- 所有指令执行完毕后,合并所有镜像层,最后一次 commit 生成的镜像 ID 即为最终镜像 ID。
二、构建缓存机制(提升效率的关键)
1. 缓存默认行为
- Docker 会自动缓存已构建的镜像层,下次构建时若发现指令未变,直接复用缓存,无需重新执行。
- 可通过
--no-cache=true
参数禁用缓存(如docker build --no-cache=true -t myimage .
)。2. 缓存匹配规则
- 基础检查:遍历缓存中的镜像层,检查构建指令是否与当前指令完全一致(如
RUN
命令的参数)。- 特殊指令处理:
- 对于
ADD
、COPY
指令,除了指令本身,还会校验文件的校验和(checksum),若文件内容不同则缓存失效。- 忽略容器内文件检查:
- 例如
RUN apt-get update
执行后,容器内文件更新,但缓存策略不会校验这些文件,仅依赖指令本身和文件校验和。三、查看镜像构建历史
- 命令:
docker history <镜像ID或标签>
- 作用:显示镜像各层的构建指令、大小、时间等信息,便于调试和优化构建流程。
4. 关键字
指令 | 说明 |
---|---|
FROM | 设置镜像使用的基础镜像 |
MAINTAINER | 设置镜像的作者 |
RUN | 编译镜像时运行的脚本 |
CMD | 设置容器的启动命令 |
LABEL | 设置镜像标签 |
EXPOSE | 设置镜像暴露的端口 |
ENV | 设置容器的环境变量 |
ADD | 编译镜像时复制上下文中的文件到镜像中 |
COPY | 编译镜像时复制上下文中的文件到镜像中 |
ENTRYPOINT | 设置容器的入口程序 |
VOLUME | 设置容器的挂载卷 |
USER | 设置运行 RUN、CMD、ENTRYPOINT 的用户名 |
WORKDIR | 设置 RUN、CMD、ENTRYPOINT、COPY、ADD 指令的工作目录 |
ARG | 设置编译镜像时加入的参数 |
ONBUILD | 设置镜像的 ONBUILD 指令 |
STOPSIGNAL | 设置容器的退出信号量 |
5. dockerfile 实践(go)
一、基本语法实践案例
案例目标:使用 Golang 构建一个可执行程序并打包为镜像。
1. 案例准备
# 创建项目目录 mkdir example1 && cd example1
2. Dockerfile 内容
FROM golang:1.18 # 指定基础镜像 ENV env1=env1value env2=env2value # 设置环境变量 MAINTAINER nick # 镜像作者 LABEL hello=1.0.0 # 镜像标签 RUN git clone https://gitee.com/nickdemo/helloworld.git # 克隆代码 WORKDIR helloworld # 设置工作目录 RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct # 配置代理 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app . # 编译程序 EXPOSE 80 # 暴露端口 CMD ["./app", "--param1=p1", "--param2=p2"] # 容器启动命令
3. 构建与运行
# 构建镜像 docker build -t hello:1.0.0 -f Dockerfile . # 运行容器 docker run -p 80:80 -d --name hello hello:1.0.0
二、Docker Build 上下文实践
案例目标:理解上下文(Context)对构建的影响。
1. 素材准备
mkdir example2 && cd example2 # 下载 Nginx 源码和应用代码 curl https://nginx.org/download/nginx-1.21.6.tar.gz > nginx-1.21.6.tar.gz git clone https://gitee.com/nickdemo/helloworld
2. 无上下文优化的 Dockerfile(低效)
FROM golang:1.18 ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 RUN git clone https://gitee.com/nickdemo/helloworld.git # 从网络克隆,不使用本地上下文 WORKDIR helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app . EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
3. 优化后的上下文 Dockerfile(高效)
FROM golang:1.18 ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 COPY ./helloworld /go/src/helloworld # 从上下文复制本地代码 WORKDIR /go/src/helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app . EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
4. 实践操作
- 忽略文件测试:在
example2
中创建.dockerignore
,添加helloworld
,观察构建时是否跳过该目录。- 上下文路径测试:修改
COPY
路径为上下文外的路径(如../other
),观察构建错误。三、多阶段构建实践
案例目标:分离编译环境与运行环境,减小镜像体积。
1. 素材准备
mkdir example3 && cd example3 curl https://nginx.org/download/nginx-1.21.6.tar.gz > nginx-1.21.6.tar.gz git clone https://gitee.com/nickdemo/helloworld
2. 基础多阶段构建(无别名)
# 第一阶段:编译环境 FROM golang:1.18 ADD ./helloworld /go/src/helloworld/ WORKDIR /go/src/helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .# 第二阶段:运行环境(使用 Alpine 轻量级系统) FROM alpine:latest ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 WORKDIR /app/ COPY --from=0 /go/src/helloworld/app ./ # 从第一阶段复制可执行文件 EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
3. 带别名的多阶段构建(更清晰)
# 第一阶段:命名为 stage0 FROM golang:1.18 as stage0 ADD ./helloworld /go/src/helloworld/ WORKDIR /go/src/helloworld RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .# 第二阶段:运行环境 FROM alpine:latest ENV env1=env1value env2=env2value MAINTAINER nick LABEL hello=1.0.0 WORKDIR /app/ COPY --from=stage0 /go/src/helloworld/app ./ EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"]
4. 构建命令
docker build -t hello:1.0.0 -f Dockerfile .
四、ADD 与 COPY 指令对比实践
案例目标:理解
ADD
与COPY
的功能差异。1. 基础功能对比
FROM alpine:latest WORKDIR /soft/# COPY 仅复制文件(不支持解压/URL) COPY nginx-1.21.6.tar.gz ./copy/ # 复制本地文件 COPY srcDir/ ./destDir/ # 复制目录内容(不含目录本身)# ADD 支持解压和 URL ADD https://nginx.org/download/nginx-1.21.6.tar.gz ./add/ # 从 URL 下载(不解压) ADD nginx-1.21.6.tar.gz ./add/ # 解压本地压缩包
2. 多阶段构建中的应用
# 编译阶段 FROM golang:1.18 as stage0 COPY ./helloworld /go/src/helloworld/ # 推荐用 COPY 复制本地文件 WORKDIR /go/src/helloworld RUN go build -o app .# 运行阶段 FROM alpine:latest WORKDIR /app/ COPY --from=stage0 /go/src/helloworld/app ./
五、CMD 与 ENTRYPOINT 指令实践
案例目标:理解容器启动命令的不同配置方式。
1. CMD 指令案例
FROM alpine:latest WORKDIR /app COPY app ./ EXPOSE 80 CMD ["./app", "--param1=p1", "--param2=p2"] # 默认启动命令# 运行时覆盖 CMD: # docker run -d hello:1.0.0 ./app --param1=1 --param2=2
2. ENTRYPOINT 指令案例
FROM alpine:latest WORKDIR /app COPY app ./ EXPOSE 80 ENTRYPOINT ["./app"] # 固定入口程序 CMD ["--param1=p1", "--param2=p2"] # 为 ENTRYPOINT 提供默认参数# 运行时追加参数: # docker run -d hello:1.0.0 --param1=1 --param2=2 # 替换入口程序: # docker run --entrypoint sh hello:1.0.0
六、Build Arg 实践
案例目标:动态传递构建参数。
FROM golang:1.18 as s0 WORKDIR /go/src/app RUN go env -w GOPROXY=$http_proxy # 使用预定义参数FROM alpine:latest as s1 ARG wd=./app # 定义默认参数 ARG label=myapp # 镜像标签参数 ARG tag=1.0.0 # 镜像版本参数 RUN echo "工作目录: $wd, 标签: $label, 版本: $tag" WORKDIR $wd LABEL $label=$tag# 构建命令(传递参数): # docker build --build-arg http_proxy=https://proxy... --build-arg wd=/home/app --tag hello:1.0.0 .
七、Target 与 Cache-From 实践
案例目标:加速多阶段构建,复用缓存
FROM golang:1.18 as s0 ADD ./helloworld /go/src/helloworld/ WORKDIR /go/src/helloworld RUN go build -o app .FROM alpine:latest as s1 WORKDIR /app COPY --from=s0 /go/src/helloworld/app ./# 分步构建: # 1. 构建第一阶段并保存 docker build -t prehello:1.0.0 --target s0 . # 2. 从缓存构建第二阶段 docker build -t hello:1.0.0 --cache-from prehello:1.0.0 --target s1 .
八、ONBUILD 指令实践
案例目标:定义基础镜像的下游构建行为。
1. 定义基础镜像
FROM golang:1.18 ONBUILD ADD ./helloworld /go/src/helloworld/ # 下游构建时触发 ONBUILD WORKDIR /go/src/helloworld ONBUILD RUN go env -w GOPROXY=https://proxy.golang.com.cn,https://goproxy.cn,direct ONBUILD RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o app .# 构建基础镜像 docker build -t mygolang:1.0.0 .
2. 使用基础镜像构建应用
FROM mygolang:1.0.0 as s0 # 自动触发 ONBUILD 指令 FROM alpine:latest as s1 WORKDIR /app COPY --from=s0 /go/src/helloworld/app ./ EXPOSE 80 ENTRYPOINT ["./app"]# 构建应用镜像(无需重复编写编译步骤) docker build -t hello:1.0.0 .
6. dockerfile实践(c语言)
一、项目准备
1. 目录结构
创建项目目录
c-app-docker
,包含:
src/
:存放 C 源文件Dockerfile
:构建镜像的核心文件2. 编写 C 程序
在
src/
目录下创建main.c
,实现一个简单的 HTTP 服务(使用libmicrohttpd
库)// src/main.c #include <stdio.h> #include <microhttpd.h>#define PORT 8080static int handle_request(void *cls, struct MHD_Connection *connection,const char *url, const char *method,const char *version, const char *upload_data,size_t *upload_data_size, void **con_cls) {const char *response = "Hello, Docker C App!\n";struct MHD_Response *mhd_response;int ret;mhd_response = MHD_create_response_from_buffer(strlen(response), (void *)response, MHD_RESPMEM_PERSISTENT);ret = MHD_queue_response(connection, MHD_HTTP_OK, mhd_response);MHD_destroy_response(mhd_response);return ret; }int main() {struct MHD_Daemon *daemon;daemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, PORT, NULL, NULL,&handle_request, NULL, MHD_OPTION_END);if (NULL == daemon) {fprintf(stderr, "Failed to start MHD daemon\n");return 1;}printf("Server running on port %d...\n", PORT);getchar(); // 阻塞进程,避免退出MHD_stop_daemon(daemon);return 0; }
二、Dockerfile 编写(多阶段构建优化)
使用 多阶段构建 分离编译环境与运行环境,最终镜像仅保留运行所需的最小依赖,体积更小、更安全。
Dockerfile
内容:# ================================================ # 阶段1:编译环境(使用 GCC 编译 C 程序) # ================================================ FROM gcc:13 as builder# 设置工作目录 WORKDIR /app# 安装依赖库(libmicrohttpd 用于 HTTP 服务) RUN apt-get update && \apt-get install -y libmicrohttpd-dev && \rm -rf /var/lib/apt/lists/*# 复制 C 源文件到容器 COPY src/main.c ./# 编译 C 程序(生成可执行文件 app) RUN gcc -o app main.c -lmicrohttpd# ================================================ # 阶段2:运行环境(仅保留运行所需的最小依赖) # ================================================ FROM alpine:3.19# 安装运行时依赖(libmicrohttpd 库) RUN apk add --no-cache libmicrohttpd# 设置工作目录 WORKDIR /app# 从编译阶段复制可执行文件(仅保留最终程序) COPY --from=builder /app/app ./# 暴露服务端口(与 C 程序中定义的 PORT 一致) EXPOSE 8080# 启动命令:运行编译后的可执行文件 CMD ["./app"]
三、镜像构建与运行
1. 构建镜像
在项目根目录(
c-app-docker
)下执行构建命令:docker build -t c-http-server:1.0 .
说明:
-t c-http-server:1.0
:指定镜像名称为c-http-server
,标签为1.0
。- 多阶段构建会自动跳过中间阶段(仅保留最后一个
FROM
后的内容),最终镜像体积远小于编译环境。2. 运行容器
docker run -p 8080:8080 --name c-server -d c-http-server:1.0
参数说明:
-p 8080:8080
:将容器的 8080 端口映射到宿主机的 8080 端口。--name c-server
:指定容器名称为c-server
。-d
:后台运行容器。3. 验证服务
通过浏览器或命令行访问宿主机的 8080 端口:
curl http://localhost:8080
预期输出:
Hello, Docker C App!
四、关键步骤说明
1. 多阶段构建的优势
- 体积更小:编译阶段使用
gcc:13
(约 1.2GB),运行阶段使用alpine:3.19
(约 5MB),最终镜像仅包含运行所需的libmicrohttpd
库和可执行文件(总大小约 20MB)。- 更安全:运行环境不包含编译器、调试工具等冗余组件,减少攻击面。
2. 依赖管理
- 编译阶段:通过
apt-get
安装libmicrohttpd-dev
(包含头文件和编译库)。- 运行阶段:通过
apk add
安装libmicrohttpd
(仅运行时库,无开发文件)。3. 缓存优化
COPY src/main.c ./
放在RUN gcc
前,避免源文件修改时重复安装依赖(Docker 按指令顺序缓存,若COPY
后修改main.c
,仅RUN gcc
会重新执行)。五、扩展实践建议
1. 静态编译(可选)
若需完全独立的可执行文件(不依赖宿主机库),可在编译阶段添加
-static
参数# 阶段1 编译命令改为: RUN gcc -o app main.c -lmicrohttpd -static
注意:
libmicrohttpd
可能不支持静态编译,需根据实际库的特性调整。2. 健康检查
在
Dockerfile
中添加健康检查,确保服务正常运行:HEALTHCHECK --interval=5s --timeout=3s \CMD curl -f http://localhost:8080/ || exit 1
3. 环境变量配置
若需动态配置端口或参数,可使用
ENV
和ARG
指令:# 在阶段2添加: ARG PORT=8080 ENV APP_PORT=$PORT EXPOSE $APP_PORT CMD ["./app", "--port", "$APP_PORT"]
构建时指定参数
docker build -t c-http-server:1.0 --build-arg PORT=8090 .
0voice · GitHub