什么是 Docker 镜像?
Docker 镜像是 只读的容器模板,包含启动容器所需的一切资源:
Docker镜像的主要特点
分层结构(像摞积木)
镜像由多个 只读层(Layer) 堆叠而成,每层对应一次构建操作(如安装软件、复制文件)。
好处:
当使用docker commit提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被被更新过的文件。分层达到了在不同镜像之间共享镜像层的效果。
写时复制(Copy-on-Write,COW)
容器运行时,在镜像层上方添加 可读写层。
内容寻址(按 “内容指纹” 管理)
联合挂载(像拼拼图)
通过联合文件系统(如 Overlay2)将所有层 “拼” 成一个完整文件系统,用户看不到分层,只看到最终结果。
联合挂载就像 “把多个文件夹叠在一起,形成一个虚拟的、包含所有文件的新文件夹”,上层文件会覆盖下层,但原始文件不会被修改。Docker 利用这种技术实现了镜像的分层存储和容器的高效运行。
关键概念对比(新手必知)
概念 | 说明 |
---|---|
镜像(Image) | 只读模板,用于创建容器(类比“安装包”)。 |
容器(Container) | 镜像的运行实例(类比“正在运行的软件”),可读写。 |
仓库(Repository) | 镜像的“仓库”,用于存储镜像的不同版本(如 Docker Hub 上的 nginx 仓库)。 |
注册中心(Registry) | 存放仓库的服务(如 Docker Hub 是公共注册中心,企业可搭建私有注册中心)。 |
Dockerfile | 构建镜像的“菜谱”,用简单指令描述镜像的制作步骤(如拉取基础镜像、安装软件)。 |
dockerfile是啥
想象你要做一道菜,你需要一个菜谱告诉你用什么食材(基础镜像),按什么顺序加调料(执行命令),最后怎么装盘(容器启动命令)。Dockerfile 就是 Docker 镜像的 “菜谱”。
dockerfile解决了什么问题
Dockerfile 中的每⼀条指令都会创建新的镜像层,这些镜像可以被 Docker Daemon 缓存。再次制作镜像时,Docker 会尽量复⽤缓存的镜像层(using cache),⽽不是重新逐层构建,这样可以节省时间和磁盘空间。Dockerfile 的操作流程可以通过docker image history [镜像名称]查询
docker build 构建流程
上下文准备与过滤
构建上下文是指执行 docker build 命令时指定的路径(默认是当前目录 .),包含所有需要打包进镜像的文件。
示例:将当前目录作为上下文构建镜像
docker build -t myapp:v1 .
关键点:
.dockerignore 文件:用于排除不需要的文件(类似 .gitignore),提高传输效率。
HTTP 请求与镜像构建
分层构建与缓存机制
Docker 采用 分层构建 和 缓存复用 机制来加速镜像构建:
dockerfile
FROM ubuntu:20.04 # 层1:基础系统
RUN apt-get update # 层2:更新软件源
RUN apt-get install -y nginx # 层3:安装 Nginx
COPY . /app # 层4:复制应用代码
CMD ["nginx", "-g", "daemon off;"] # 层5:启动命令
构建过程:
每执行一条指令,创建一个临时容器
执行指令并提交变更,生成新的镜像层
删除临时容器,保留镜像层.
RUN apt-get install -y python3 # 缓存1
RUN apt-get install python3 -y # 缓存2(参数顺序不同)
COPY app.py /app/ # 如果 app.py 内容变化,缓存失效
建议:合理安排指令顺序:
不变的指令(如 FROM、RUN apt-get install)放在前面 频繁变更的指令(如 COPY .)放在后面
以下是对 Dockerfile 中常用指令的详细解析,以表格形式对比说明其功能、语法和使用场景,帮助快速理解和记忆:
Dockerfile 常用指令速查表
指令 | 功能 | 语法 | 核心作用 | 注意事项 |
---|---|---|---|---|
FROM | 指定基础镜像(必须为第一条指令) | FROM <镜像名>[:标签] |
基于现有镜像构建新镜像,如 FROM ubuntu:20.04 |
- 基础镜像可以是公共仓库(如 Docker Hub)或私有仓库 - 支持 scratch 空镜像 |
MAINTAINER | 设置镜像作者信息(已过时,推荐用 LABEL ) |
MAINTAINER <作者名或邮箱> |
标注镜像维护者信息 | - Docker 官方已不推荐使用 - 建议用 LABEL maintainer="xxx" 替代 |
RUN | 在镜像构建阶段执行命令(可创建新镜像层) | RUN <命令> 或 RUN ["executable", "param"] |
安装依赖、编译代码等,如 RUN apt-get install -y python3 |
- 每条 RUN 生成一个镜像层- 建议合并多条命令减少层数(如 && 连接) |
CMD | 设置容器启动时的默认命令(一个 Dockerfile 只能有一条 CMD ) |
CMD ["executable","param1"] 或 CMD command param |
定义容器启动时执行的命令,如 CMD ["nginx", "-g", "daemon off;"] |
- 若 ENTRYPOINT 存在,CMD 作为参数传递- 可被 docker run 命令覆盖 |
LABEL | 为镜像添加元数据标签(键值对) | LABEL |
标注镜像版本、用途等信息,如 LABEL version="1.0.0" |
- 支持多个标签 - 可通过 docker inspect 查看 |
EXPOSE | 声明容器运行时监听的端口(需配合 docker run -p 映射端口) |
EXPOSE <端口> [<端口>...] |
提示用户容器使用的端口,如 EXPOSE 80 443 |
- 仅声明端口,不实际开启防火墙 - 需在运行时用 -p 或 -P 映射 |
ENV | 设置环境变量(构建时和运行时均有效) | ENV 或 ENV |
定义环境变量,如 ENV APP_ENV=production |
- 可在 RUN 指令中引用(如 RUN echo $APP_ENV )- 容器内可通过 $key 访问 |
ADD | 复制文件或目录从上下文到镜像中(支持远程 URL 和压缩包自动解压) | ADD <源路径> <目标路径> |
复制文件到镜像,如 ADD app.tar.gz /app/ |
- 远程 URL 会下载文件 - 压缩包(tar、gz 等)会自动解压 - 不推荐用于远程文件 |
COPY | 复制文件或目录从上下文到镜像中(仅支持本地文件) | COPY <源路径> <目标路径> |
更安全的文件复制方式,如 COPY requirements.txt /app/ |
- 源路径必须在构建上下文中 - 支持 --chown 参数修改权限 |
ENTRYPOINT | 设置容器的入口程序(可让容器以进程形式运行,CMD 作为参数) |
ENTRYPOINT ["executable","param1"] 或 ENTRYPOINT command param |
定义容器启动时的主程序,如 ENTRYPOINT ["/app/main"] |
- 通常配合 CMD 使用- 不会被 docker run 命令覆盖,参数会追加到后面 |
VOLUME | 定义容器的数据卷(用于持久化存储或共享数据) | VOLUME <路径> 或 VOLUME ["路径1", "路径2"] |
声明数据卷挂载点,如 VOLUME /data |
- 数据卷会绕过联合文件系统,直接存储在宿主机 - 需在运行时用 -v 绑定 |
USER | 设置后续指令(RUN 、CMD 、ENTRYPOINT )执行时的用户 |
USER <用户名或UID> |
切换执行用户,如 USER app |
- 需确保用户存在于镜像中 - 若用户不存在,会在构建时报错 |
WORKDIR | 设置后续指令的工作目录(相当于 cd 目录 ) |
WORKDIR <目录路径> |
切换工作目录,如 WORKDIR /app |
- 可多次切换 - 路径支持环境变量(如 WORKDIR $APP_DIR ) |
ARG | 定义构建时的参数(不存储在镜像中,仅在构建阶段使用) | ARG <参数名>[=<默认值>] |
传递构建参数,如 ARG VERSION=1.0 |
- 可通过 docker build --build-arg <参数>=值 传入- 不可在容器运行时访问 |
ONBUILD | 设置子镜像构建时触发的指令(当前镜像被其他镜像继承时执行) | ONBUILD <指令> |
定义“钩子”指令,如 ONBUILD RUN apt-get update |
- 仅在其他镜像 FROM 当前镜像时生效- 慎用,可能导致构建逻辑复杂 |
STOPSIGNAL | 设置容器接收退出信号的机制(如 SIGTERM 、SIGKILL ) |
STOPSIGNAL <信号量> |
自定义容器停止信号,如 STOPSIGNAL SIGINT |
- 需与宿主机信号机制兼容 |
关键指令对比与最佳实践
指令 | 执行时机 | 用途 | 典型场景 |
---|---|---|---|
RUN | 构建阶段执行 | 安装依赖、修改文件系统等(生成镜像层) | RUN apt-get install -y nginx |
CMD | 容器启动阶段执行 | 定义默认命令(可被 docker run 覆盖) |
CMD ["nginx", "-g", "daemon off;"] |
ENTRYPOINT | 容器启动阶段执行 | 定义入口程序(参数可通过 CMD 或 docker run 传递) |
ENTRYPOINT ["/app/main"] 配合 CMD ["--config=prod"] |
2. ADD vs COPY
指令 | 源支持 | 自动解压 | 网络请求 | 安全性 | 推荐场景 |
---|---|---|---|---|---|
ADD | 本地文件、远程URL、压缩包 | 是 | 是 | 较低(远程文件风险) | 复制压缩包并自动解压 |
COPY | 本地文件 | 否 | 否 | 较高 | 复制本地配置文件、代码 |
指令 | 作用范围 | 是否存储到镜像 | 访问时机 | 用途 |
---|---|---|---|---|
ENV | 构建阶段和运行阶段 | 是 | 构建时和运行时 | 定义环境变量(如时区、版本) |
ARG | 仅构建阶段 | 否 | 仅限构建时 | 传递临时参数(如 Git 哈希) |
指令使用原则
分层最小化:
RUN
命令(如 RUN apt-get update && install -y ...
),减少镜像层数。缓存最大化:
FROM
、RUN 安装依赖
)放在前面,利用缓存。COPY .
)放在后面,减少缓存失效范围。安全性优先:
COPY
替代 ADD
复制本地文件,避免远程 URL 风险。USER app
),降低安全漏洞影响。清晰可维护:
# 安装 Web 服务器
),说明每条指令的目的。LABEL
标注镜像元数据,便于团队协作和检索。实战内容运行一个nginx镜像
docker build -t my-nginx:v1 .
这个命令用于构建 Docker 镜像,下面详细解析其各个部分的含义和作用:
核心参数
参数 | 作用 |
---|---|
-t |
指定镜像的名称(tag),格式为 名称:版本号 。若不指定版本,默认为 latest 。 |
-f |
指定 Dockerfile 的路径。若省略,默认使用当前目录下的 Dockerfile 。 |
. |
构建上下文(build context)的路径,表示将该目录下的所有文件发送给 Docker 引擎用于构建。 |
docker内容
FROM nginx:1.23-alpine
LABEL maintainer="[email protected]" version="1.0.0" description="Custom Nginx with custom config and page"
# 3. 清理默认配置(删除官方默认的 nginx.conf)
RUN rm -rf /etc/nginx/conf.d/default.conf && \
rm -rf /etc/nginx/nginx.conf
# 4. 将自定义配置复制到镜像中
COPY nginx.conf /etc/nginx/nginx.conf
# 5. 将自定义网页内容复制到镜像的 HTML 目录
COPY html/ /usr/share/nginx/html/
# 在 Dockerfile 中通过 EXPOSE 80 声明容器内服务监听 80 端口
EXPOSE 80
# 7. 定义容器启动命令(保持与官方镜像一致)
CMD ["nginx", "-g", "daemon off;"]
第一句 而言 建议你先拉取镜像到本地
docker pull nginx:1.23-alpine
第二句而言
# 旧写法(已过时) MAINTAINER Your Name
3 4 5句
COPY 指令会覆盖目标路径下的同名文件
docker run -it --rm my-nginx:v1 sh
你可以通过辨别文件是否改变
第七 运行命令
docker run -d -p 80:80 my-nginx-image
前面一个80是宿主机的端口,第二个80是容器端口
这个CMD是这个nginx的启动命令,当你运行容器的时候,自动启动
Docker 17.05版本以后,新增了Dockerfile多阶段构建。所谓多阶段构建,实际上是允许⼀个
Dockerfile 中出现多个 FROM 指令。这样做有什么意义呢?
多个 FROM 指令的意义
多个 FROM 指令并不是为了⽣成多根的层关系,最后⽣成的镜像,仍以最后⼀条 FROM 为准,之
前的 FROM 会被抛弃,那么之前的FROM ⼜有什么意义呢?
每⼀条 FROM 指令都是⼀个构建阶段,多条 FROM 就是多阶段构建,虽然最后⽣成的镜像只能是
最后⼀个阶段的结果,但是,能够将前置阶段中的⽂件拷⻉到后边的阶段中,这就是多阶段构建的
最⼤意义。
一、为什么需要数据卷?先看容器的 “天生缺陷”
假设你用 Docker 运行了一个网站容器:
容器里的文件藏得深:容器里的文件被 “封装” 在镜像的多层只读文件里,加上最上层的读写层,想直接在电脑上找到它们?难如大海捞针。
二、数据卷是什么?给容器加个 “外挂硬盘”
数据卷(Volume)是 Docker 提供的一种独立于容器生命周期的存储机制,可以理解为:
三、数据卷的核心功能:解决三大痛点
创建数据卷
docker volume create --name vol_simple
指定容器数据卷
//创建⼀个指定名字的volume ,并挂载到容器中的/data⽬录
docker run -d -v vol_simple:/data ubuntu
指定宿主机数据卷
docker run -d -v $HOME/data:/data ubuntu /bin/bash
设置权限
docker run -it -v $HOME/data:/data:ro -v /data1 -v /data2 ubuntu
/bin/bash
ro表示只读,rw表示读写,默认为rw
同时挂载多个数据卷
docker run -it -v $HOME/data:/data:ro -v /data1 -v /data2 ubuntu /bin/bash
通过dockerfile指定数据卷挂载
//dockerfile 添加指令
VOLUME /data
// 指定多个数据卷的挂载点
VOLUME ["/data1","/data2"]
没有设置宿主机数据卷
在 Docker 中,使用 VOLUME /data 声明的数据卷确实没有显式命名,但 Docker 会自动为其生成一个唯一的匿名卷(Anonymous Volume)
docker inspect <容器ID> | grep -A 10 Mounts
网桥
在⼀台未经特殊⽹络配置的Ubuntu机器上安装完Docker之后,在宿主机上通过ifconfig命令可以看
到多了⼀块名为docker0的⽹卡。该⽹卡即是docker0⽹桥,⽹桥在这⾥的作⽤相当于交换机,为连接在其上的设备转发数据帧。⽹桥上的veth⽹卡设备相当于交换机上的端⼝,可以将多个容器或虚拟机连接其上,这些端⼝⼯作在⼆层,所以是不需要配置IP信息的。
CNN
CNM(Container Network Model)译为容器⽹络模型,定义了构建容器虚拟化⽹络的模型。该模
型主要包含三个核⼼组件 沙盒、端点、⽹络
*一、沙盒(Sandbox)容器的 “通信工具箱”
类比:相当于每个容器的 “私人房间”,里面放着这个容器的 “通信设备”(网络配置)。
比如:房间里有 “电话号码本”(DNS 设置)、“地图”(路由表)、“窗户”(网络接口,比如网卡)。
每个房间(沙盒)可以有多个 “窗户”(端点),通过不同的 “窗户” 连接到不同的 “街道”(网络)。
作用:管理容器自己的网络设置,比如分配 IP 地址、设置访问规则。不同容器的 “房间” 互相隔离,确保网络配置不会混乱。
二、端点(Endpoint):容器的 “网络接口”
三、网络(Network):容器的 “通信街道”
libnetwork
ibnetwork 实现了CNM 的Docker⽹络组件库。libnetwork内置了⼏种⽹络驱动。
以下是 Docker 网络管理命令的实战演示,结合具体场景帮助你理解如何操作:
假设你有两个容器:web-app
(前端)和 api-server
(后端),需要让它们通过自定义网络通信。
my-microservices
)docker network create my-microservices
docker network ls
# 输出中应包含 my-microservices 网络
docker run -d --name web-app \
--network my-microservices \ # 指定加入 my-microservices 网络
-p 8080:80 \
nginx:alpine
docker run -d --name api-server --network my-microservices -p 3000:3000 python:3.9-alpine python -m http.server 3000
docker exec -it web-app sh
curl http://api-server:3000 # 容器名自动解析为 IP
# 输出:Hello from API
二、动态连接/断开容器与网络(实战场景:临时组网)
假设你有一个已运行的容器 legacy-app
,需要临时加入 my-microservices
网络与其他容器通信。
1. 查看当前容器连接的网络
docker inspect legacy-app | grep -A 10 "Networks"
2. 将容器连接到新网络
docker network connect my-microservices legacy-app
3. 验证连接结果
docker exec -it legacy-app sh
curl http://api-server:3000 # 现在可以访问同一网络的 api-server
4. 断开容器与网络
docker network disconnect my-microservices legacy-app
curl http://api-server:3000 # 再次访问会失败,已断开网络连接
三、查看网络详细信息(实战场景:排查网络问题)
1. 查看 my-microservices
网络的详细配置
docker network inspect my-microservices
关键输出:
[
{
"Name": "my-microservices",
"Id": "xxx",
"Driver": "bridge", // 网络类型为桥接
"IPAM": { // IP 地址管理
"Config": [
{
"Subnet": "172.18.0.0/16" // 子网段,容器IP从这里分配
}
]
},
"Containers": { // 连接到该网络的容器列表
"api-server": {
"IPv4Address": "172.18.0.2/16"
},
"web-app": {
"IPv4Address": "172.18.0.3/16"
}
}
}
]
2. 分析容器 IP 地址
172.18.0.0/16
子网,可通过 IP 直接通信:docker exec -it web-app sh
curl http://172.18.0.2:3000 # 用 IP 访问 api-server
四、清理无用网络(实战场景:释放磁盘空间)
1. 查看所有网络
docker network ls
old-network
。2. 删除指定网络
docker network rm old-network
3. 批量删除所有未使用的网络
docker network prune
y
确认删除。常用命令速查表
场景 | 命令 | 说明 |
---|---|---|
创建桥接网络 | docker network create my-bridge |
默认类型为 bridge |
创建 overlay 网络 | docker network create --driver overlay my-overlay |
用于跨主机集群通信 |
查看容器网络连接 | `docker inspect <容器名> | grep Networks` |
动态连接网络 | docker network connect my-network <容器名> |
将运行中的容器加入网络 |
清理无用网络 | docker network prune |
删除所有未被容器使用的网络 |
删除指定网络 | docker network rm my-network |
删除单个网络(需先断开所有容器) |
通过以上实战操作,你可以灵活管理 Docker 容器的网络连接,实现容器间通信、跨主机组网和资源清理等功能。