喵星之旅-狂奔的兔子-通过已有的 Docker 容器创建新镜像

创建新镜像

首先,确认要基于哪个容器创建镜像,通过 docker ps -a 命令查看所有容器(包括已停止的):

1
docker ps -a

记录目标容器的 CONTAINER ID(如 abc123456789)或 NAMES(如 my-ubuntu-container).

使用 docker commit 创建新镜像
基本语法:

1
docker commit [选项] <容器ID或名称> <新镜像名称[:标签]>

基于容器 my-ubuntu-container 创建一个名为 my-custom-ubuntu、标签为 v1.0 的新镜像:

1
docker commit my-ubuntu-container my-custom-ubuntu:v1.0

验证新镜像是否创建成功

使用 docker images 命令查看本地镜像列表

docker commit 不会提交外挂到外部主机的数据(即通过 -v 挂载的主机目录或数据卷中的内容),仅会提交容器自身可写层(容器内部未挂载区域)的文件变更。

核心原理:挂载数据与容器可写层的隔离性

Docker 中,通过 -v 挂载的外部数据(主机目录、数据卷)与容器的文件系统是隔离的:

容器可写层:容器运行时产生的新文件、修改的文件(未在挂载目录下)会保存在容器自身的可写层,这部分内容会被 docker commit 捕获并打包到新镜像中。
挂载的外部数据:通过 -v 主机路径:容器路径 挂载的外部数据,本质是 “主机文件系统的映射”,并不属于容器可写层的一部分,因此 docker commit 会完全忽略这部分内容。

是否需要关闭容器

容器是否处于运行状态,对通过 docker commit 创建镜像的最终结果(镜像内容)无本质影响,但会影响提交过程的稳定性和数据一致性.

关键原理:Docker Commit 捕获的是 “文件系统层”,而非 “运行状态”

Docker 容器的文件系统基于分层存储,docker commit 的本质是:将容器在基础镜像之上的 “增量文件变更层”(即 /var/lib/docker/overlay2 中的容器层)打包为新镜像。

无论容器是否运行,只要变更已被持久化到容器的文件系统(如 apt install 安装软件、echo “config” > file 写入文件),就会被包含在新镜像中;而仅存在于内存中的数据(如 echo “temp” 未写入文件、数据库未提交的事务、进程内存缓存),不会被捕获,与容器是否运行无关。

实操建议:优先选择 “停止容器后提交”

为避免因运行时数据写入导致的镜像损坏,建议遵循以下最佳实践:

停止容器前完成数据持久化
若容器运行中做了关键变更(如修改配置文件、生成数据文件),先确保变更已保存(如执行 sync 命令强制刷盘、关闭正在写入的进程),再停止容器。
停止容器后提交
通过 docker stop <容器名/ID> 停止容器,待容器状态变为 Exited 后,再执行 docker commit,此时文件系统无写入操作,镜像内容更稳定。

跳过容器中的大文件或文件夹

可以跳过容器中的大文件或文件夹,避免其被打包进新镜像(减少镜像体积、加快构建速度)。核心通过 docker commit 的 –exclude 参数 实现,也可结合容器内的临时目录特性辅助过滤,以下是具体方法、原理和实操示例:

使用 docker commit –exclude 显式排除

docker commit 支持通过 –exclude <路径> 参数,指定不需要包含在新镜像中的文件或文件夹路径(路径需基于容器内的绝对路径),可多次使用该参数排除多个目标。

1
2
3
4
5
docker commit \
--exclude <容器内绝对路径1> \
--exclude <容器内绝对路径2> \
<容器名/ID> \
<新镜像名:标签>

关键注意事项

路径必须是 “容器内的绝对路径”:不能使用主机路径(如 /var/lib/docker/...),需先通过 docker exec <容器ID> ls <路径> 确认容器内的目标路径存在。
支持文件夹和文件:既可以排除单个大文件(如 /data/logs/big.log),也可以排除整个大文件夹(如 /data/backup)。
排除是 “最终层过滤”:--exclude 会在打包容器增量层时,直接跳过指定路径的文件,这些文件不会进入新镜像(而非打包后删除,因此能真正减少镜像体积)。

实操示例:排除大文件 / 文件夹

假设我们有一个 centos 容器,容器内有两个需要排除的大目标:

大日志文件:/var/log/app/big-log-10G.log(10GB)
大备份文件夹:/data/backup(20GB)

步骤 1:确认容器内目标路径

先通过 docker exec 验证路径存在(避免因路径错误导致排除失败):

1
2
docker exec <容器ID> ls -lh /var/log/app/big-log-10G.log  # 确认大文件
docker exec <容器ID> ls -lh /data/backup # 确认大文件夹

步骤 2:停止容器(可选但推荐,确保数据稳定)

1
docker stop <容器ID>

步骤 3:带排除参数提交镜像

1
2
3
4
5
docker commit \
--exclude /var/log/app/big-log-10G.log \ # 排除单个大文件
--exclude /data/backup \ # 排除整个大文件夹
<容器ID> \
my-centos:without-big-files # 新镜像名

常见问题与解决方案

  1. 排除后镜像体积未减少?

    原因 1:路径错误(如使用相对路径、主机路径)。
    解决:通过 docker exec <容器ID> pwd 确认容器内路径,确保 –exclude 后是绝对路径。
    原因 2:目标文件在 “基础镜像层” 而非 “容器增量层”。
    docker commit 仅能排除容器运行中新增 / 修改的文件(增量层),若大文件来自基础镜像(如基础镜像自带的大软件),–exclude 无法排除(需通过 Dockerfile 的 RUN rm 清理基础镜像层)。

  2. 排除文件夹时,子目录未被排除?

    原因:–exclude 是 “精确匹配路径”,但排除文件夹时会自动包含其所有子目录(无需单独排除子目录)。
    例:–exclude /data/backup 会排除 /data/backup 及其下所有文件 / 子文件夹,无需再加 –exclude /data/backup/subdir。

  3. 运行中的容器排除时,大文件仍在写入?

    风险:若大文件正在被容器进程写入(如日志滚动),即使 –exclude,也可能因文件句柄未释放导致排除不完全。
    解决:先停止容器(docker stop),再提交(停止后无写入操作,排除更彻底)。

文章目录
  1. 创建新镜像
  2. 是否需要关闭容器
  3. 跳过容器中的大文件或文件夹
    1. 使用 docker commit –exclude 显式排除
    2. 实操示例:排除大文件 / 文件夹
    3. 常见问题与解决方案
|