Java 项目部署之 Docker工具快速入门: 使用 Docker 镜像命令练习

📅 发布时间:2026/10/12 4:52:20
Java 项目部署之 Docker工具快速入门: 使用 Docker 镜像命令练习
概述从 Docker Hub 拉一个 Redis 镜像导出成 tar删掉本地镜像再从 tar 灌回来。命令本身没难度坑都藏在细节里——怎么命名归档文件、save和重定向有什么区别、删镜像为什么会被容器拦住。纲要练习题目的六步目标与拆解每步的命令、预期输出与设计原因镜像命令速查表docker save/load与docker export/import的边界四个高频陷阱-o与、.tar.gz后缀、load 混用 import、删镜像前先删容器一条可复制的完整练习脚本练习要解决什么讲义给的题目是需求去 Docker Hub 搜索并拉取一个 Redis 镜像目标是六步顺序不能乱序号目标对应命令1去 Docker Hub 搜索 Redis 镜像浏览器搜索 /docker search redis2查看 Redis 镜像的名称和版本Docker Hub 的 Tags 页3拉取镜像docker pull redis4打包成 tar 归档docker save -o redis.tar redis:latest5删除本地镜像docker rmi redis:latest6从 tar 重新加载docker load -i redis.tar练习题里没有第 7 步但少了它整道题就没有验收标准——加载回来之后必须再docker images看一眼确认 REPOSITORY、TAG、IMAGE ID 都对得上。题目的真正考点不是记得住命令而是理解save/load这条链路里到底搬运了什么。课前资料目录长这样练习用到的归档文件以后会和其他素材放在一起day03-Docker/ ├── 讲义/ │ ├── Docker实用篇.md # 2.1.5 就是本文的练习题 │ ├── Docker实用篇.pptx │ └── assets/ └── 资料/ ├── Centos7安装Docker.md ├── Dockerfile ├── docker-compose ├── docker-demo.jar ├── hmy.cnf ├── jdk8.tar.gz ├── mysql.tar ├── nginx.conf ├── redis.tar.gz # 注意只有 22 字节不是 save 的产物 ├── assets/ └── cloud-demo/后面讲命名陷阱时会回到redis.tar.gz这个文件上。确认镜像名称镜像名是[repository]:[tag]两段式不写 tag 时默认就是latest。Redis 的官方镜像在 Docker Hub 上叫redis完整身份是docker.io/library/redis:latestlibrary是官方镜像的命名空间日常拉取时可以全省掉。不加latest而写具体版本是有意义的。docker pull redis:6.2.1拉的是写死的版本docker pull redis拉的是当下最新的。生产环境的镜像必须钉版本否则今天构建的和下个月重建的可能不是同一个东西。命令行也能搜但结果只是 Docker Hub 上前若干个仓库的简要列表看不出 tags正式确认版本还是去 Docker Hub 的 Tags 页面看。dockersearch redisNAME DESCRIPTION STARS OFFICIAL redis Redis is an open source key-value store that... 12000 [OK] bitnami/redis Bitnami Redis Docker Image ...OFFICIAL那列是[OK]的才是官方镜像library/redis排在第一行。拉取镜像dockerpull redis等价于docker pull redis:latest看到的输出是分层下载的过程Using default tag: latest latest: Pulling from library/redis a2abf6c4d29d: Pull complete c7a4e4382001: Pull complete 4044b9ba67c9: Pull complete Digest: sha256:b3f4a3db5e1c1a0d0f... Status: Downloaded newer image for redis:latest docker.io/library/redis:latestPulling from后面那一串就是镜像层layer。Docker 按层下载并做内容去重所以第二次拉同一个镜像会直接提示Image is up to date for redis:latest不会重新下载——层已经在本地缓存里。这也是后面docker save导出的东西一定会比单个 tar 包大的原因它把每一层都装进去了。查看本地镜像dockerimages当前 Docker 版本的列头是固定的五列REPOSITORY TAG IMAGE ID CREATED SIZE redis latest 7614ae9453d1 2 weeks ago 113MB nginx latest 605c77e624dd 3 weeks ago 141MB想只看某个镜像把名字当过滤参数传进去dockerimages redisIMAGE ID是这一列的短 ID。这条命令不只是看看有没有它还是第 6 步验收时的比对基准回灌之后 IMAGE ID 应该和删除前完全一致如果变了说明你灌进来的不是同一个镜像。导出为 tar 归档dockersave-oredis.tar redis:latest讲义原文这里就是redis.tar不带.gz。命令跑完没有回显靠ll或ls -lh确认ls-lhredis.tar-rw-r--r-- 1 root root 138M Feb 28 10:12 redis.tar-o是--output意思是让 Docker 自己把归档写到指定文件。不带-o时save会把 tar 流打到标准输出——这个默认行为后面会和 shell 重定向放在一起讲。删除本地镜像dockerrmi redis:latestUntagged: redis:latest Untagged: redissha256:b3f4a3db5e1c1a0d0f... Deleted: sha256:7614ae9453d1... Deleted: sha256:c7a4e4382001... ...注意两件事。第一删镜像用rmi不是rmdocker rm删的是容器名字差一个字母结果天差地别。第二输出是先把Untagged把 tag 摘掉再逐层Deleted顺序和拉取时正好相反。删完复查列表里就没有它了dockerimages redisREPOSITORY TAG IMAGE ID CREATED SIZE从归档回灌dockerload-iredis.tarLoaded image: redis:latest-i是--input从文件读。这一步之所以能还原是因为save出来的 tar 里有完整的manifest.json和全部层目录tag、镜像配置CMD、ENV、暴露的端口都在里面。load只是把档案解回本地镜像库不涉及任何网络请求所以离线机器上同样能跑通。如果只需要确认归档里有什么不用加载直接看 tar 的目录结构tar-tfredis.tar|head-n87614ae9453d1e1... 7614ae9453d1e1.../VERSION 7614ae9453d1e1.../json 7614ae9453d1e1.../layer.tar c7a4e4382001... ... manifest.json repositoriesmanifest.json就是元数据一堆十六进制目录名就是各层。复查与比对dockerimagesdockerimages-qredis回灌后redis:latest应该原样出现IMAGE ID 与删除前一致。整个往返过程可以画成这样docker pull redisdocker save -o redis.tardocker rmi redis:latestdocker load -i redis.tar确认恢复Docker Hublibrary/redis:latest本地镜像redis:latest磁盘归档 redis.tar未压缩 tar含全部分层 manifest.json本地已无该镜像docker images 复查IMAGE ID 与删除前一致图中save与load是两条方向相反的边中间夹着一次删除。删掉镜像不会动磁盘上的 tar所以这条链路本质上是把镜像从 Docker 的存储目录里挪到普通文件系统上再挪回去。命令速查表阶段命令作用常用参数搜索docker search redis在 Docker Hub 上按关键字找仓库--limit n、--filter is-officialtrue拉取docker pull redis:6.2.1从仓库拉镜像到本地不写 tag 即latest查看docker images列出本地镜像-q只输出 ID、-a含中间层、--filter过滤导出docker save -o redis.tar redis:latest把镜像含全部分层与元数据存成 tar-o/--output可一次跟多个镜像加载docker load -i redis.tar从 tar 恢复镜像保留 tag-i/--input、-q静默打标签docker tag redis:latest myredis:1.0给镜像加一个新引用源镜像、目标名删除docker rmi redis:latest删除本地镜像-f强制、可传 ID 或名称:tag清理docker image prune -a清理未被容器使用的镜像-a连无 tag 的一起清看帮助docker save --help查某个子命令的语法所有子命令通用docker save --help的输出值得存一份本机实际是这样的Usage: docker save [OPTIONS] IMAGE [IMAGE...] Save one or more images to a tar archive (streamed to STDOUT by default) Options: -o, --output string Write to a file, instead of STDOUT --platform string Save only the given platform variant. Formatted as os[/arch[/variant]] (e.g., linux/amd64)streamed to STDOUT by default这句是后面所有命名问题的根源。save/load 与 export/import 的边界这两组命令长得像作用对象完全不同。save/load面向镜像export/import面向容器维度docker save/docker loaddocker export/docker import操作对象镜像容器归档命令docker save -o a.tar IMAGEdocker export -o a.tar CONTAINER归档内容全部分层 manifest.json等元数据容器根文件系统的合并快照只有一层是否保留 tag保留load后 tag 原样恢复丢失import时必须自己指定名称:tag是否保留历史与配置保留CMD、ENTRYPOINT、ENV、构建历史全部丢失回灌命令docker load -i a.tardocker import a.tar myapp:1.0归档体积大等于所有层之和小只有一份合并后的文件系统典型用途离线机器之间搬运镜像导出容器现场做 rootfs、做瘦身基镜像一句话记save存的是镜像本身export存的是容器里的文件长什么样。练习里用的是save路径所以回灌之后redis:latest这个 tag 才会自动回来。两条链路不能交叉把export出来的快照丢给docker load会直接报归档格式错误。把save出来的 tar 丢给docker import要么报 tar 解析错误要么成功导入出一个没有入口命令的垃圾镜像docker run起来立刻退出。几个容易踩的坑docker save -o和重定向两种写法看起来等价实际不是dockersave-oredis.tar redis:latest# Docker 自己打开文件写入dockersave redis:latestredis.tar# shell 先创建文件再让 Docker 往标准输出写单纯的二选一都能用真正的坑是混着写dockersave-oa.tar redis:latestb.tar-o a.tar把归档写进了a.tar标准输出没有任何内容于是 shell 创建并清空的b.tar永远是 0 字节。这种命令不会报错等到别人拿着b.tar去docker load才炸。重定向还有第二个副作用shell 是先创建、截断目标文件再执行命令。如果镜像名敲错了docker save失败退出但redis.tar已经被清空了——你以为有个归档其实是个 0 字节文件而-o写法在镜像不存在时根本不会去动那个文件。所以凡是生成产物的场景优先用命令自带的-o/-i不要用重定向。.tar.gz后缀不等于真压缩docker save的输出永远是未压缩 tar。把文件命名成redis.tar.gz不会让它小一个字节dockersave-oredis.tar.gz redis:latest# 内容是 tar名字骗人ls-lhredis.tar.gz# 大小和 redis.tar 一模一样要真压缩得自己过一遍 gzipdockersave redis:latest|gzipredis.tar.gzdocker load能自动识别 gzip 归档所以上面这条压缩版load -i redis.tar.gz也能正常加载。问题是文件叫 .tar.gz 但内容是裸 tar这种情况用tar -xzf解压会直接报gzip: stdin: not in gzip format排查时容易被名字带偏。回到资料/目录里那个redis.tar.gz——它只有 22 字节显然不是任何一次save的产物就是个占位文件。判断一个归档是不是真货第一件事是ls -lh看体积Redis 镜像导出来是百兆级别不可能 22 字节。load和import不能混用load只认save生成的归档import只认文件系统快照。名字里都带个载入的意思参数格式却完全不同load用-i指定归档import直接把 tar 当位置参数还要额外给个名称:tag。记法是save 配 loadexport 配 import。删镜像前必须先删依赖它的容器练习里docker rmi一次就成功了是因为从头到尾只pull了镜像没run过容器。一旦镜像被容器引用过哪怕容器已经停止删除就会失败Error response from daemon: conflict: unable to delete 7614ae9453d1 (must be forced) - image is being used by stopped container 3f2b1c9d8e70Docker 拦这一下是故意的容器只是停止没有删除它的可写层还挂在原镜像上直接删镜像会让这个容器再也起不来。处理顺序是先清容器再清镜像dockerrm-f3f2b1c9d8e70# 先删容器dockerrmi redis:latest# 再删镜像要批量清场dockerrm-f$(dockerps-aq)# 删除所有容器含运行中的dockerrmi redis:latest用docker rmi -f硬删也能过但那是强制解引用容器会变成悬空状态除了掩盖问题没有别的好处。完整练习脚本把六步串成一个可以整段复制的脚本带 IMAGE ID 比对做自动验收#!/usr/bin/env bash# Docker 镜像命令练习Redis 镜像的拉取 / 导出 / 删除 / 回灌# 需要有 docker 权限非 root 用户确认已在 docker 组里否则每条命令前面加 sudoset-euopipefailIMAGEredis:latestTARredis.tarecho 0. 环境自检 dockerversion--format{{.Server.Version}}echo 1. 拉取镜像不写 tag 等价于${IMAGE}dockerpull redisecho 2. 查看本地镜像 dockerimagesdockerimages redisecho 3. 导出为 tar 归档 rm-f${TAR}dockersave-o${TAR}${IMAGE}ls-lh${TAR}# 确认归档里确实有 manifest.json 和各层目录tar-tf${TAR}|head-n8echo 4. 记录删除前的 IMAGE ID OLD_ID$(dockerimages-q${IMAGE})echobefore:${OLD_ID}echo 5. 删除本地镜像 # 先确认没有容器在引用这个镜像echo引用该镜像的容器应为空dockerps-a--filterancestor${IMAGE}--format{{.ID}} {{.Names}}dockerrmi${IMAGE}# 复查查不到任何记录即删除成功dockerimages${IMAGE}echo 6. 从 tar 重新加载 dockerload-i${TAR}echo 7. 复查并比对 IMAGE ID NEW_ID$(dockerimages-q${IMAGE})echoafter :${NEW_ID}if[-n${NEW_ID}][${OLD_ID}${NEW_ID}];thenecho通过IMAGE ID 与删除前一致镜像完整恢复elseecho失败镜像未恢复或 ID 不一致2exit1fi脚本里有两处是练习题原文没写、但实际必须做的记录删除前的 IMAGE ID、回灌后再比一次。少了这两步恢复成功只能靠肉眼感觉。官方文档docker save-o与--platform的用法docker load支持的归档格式与压缩类型docker exportdocker importRedis 官方镜像查看全部可用 tag讲义原文day03-Docker/讲义/Docker实用篇.md第 2.1.5 节总结练习的六步是一条完整闭环pull拿到镜像 →save落成 tar →rmi删本地 →load灌回来。归档文件放在普通文件系统上镜像放在 Docker 的存储目录里两边互不影响删了镜像不会动 tar。save搬运的是镜像本身带着全部分层和manifest.json所以load之后 tag 和 IMAGE ID 都能原样回来export搬运的是容器文件系统的合并快照元数据全丢必须配import且要自己补名字。归档一律用-o/-i别用重定向。-o a.tar b.tar这种写法会静默产出一个 0 字节的b.tar而且重定向会在命令执行前就先清空目标文件。save的输出是未压缩 tar叫.tar.gz不会变小。想真压一遍就docker save redis:latest | gzip redis.tar.gz。判断归档真伪先看体积别信后缀。docker rmi失败多半是被容器占着先docker rm -f清容器再删镜像不要习惯性加-f硬删。