2、Docker镜像管理:镜像的获取(pull)、查看(images)、删除(rmi)、构建(build)、导出导入(save/load)、标签管理(tag)

镜像管理,说白了就是Docker世界的「物资调度」。你想想看,没有镜像,容器就是空中楼阁。我刚开始接触Docker时,觉得镜像不就是个文件嘛,后来踩了不少坑才明白——镜像管理做不好,整个微服务体系都会跟着遭殃。

这一节,我把镜像的六个核心操作掰开揉碎讲给你听。嗯,都是实战中每天都要用的东西。

2.1 镜像获取:docker pull

获取镜像,最直接的方式就是pull。我个人习惯用官方镜像,但有时候也得从私有仓库拉。

# 从Docker Hub拉取最新版Nginx
docker pull nginx:latest

# 拉取指定版本
docker pull nginx:1.21.6

# 从私有仓库拉取
docker pull registry.example.com/myapp:v1.0

这里有个坑,我曾经遇到过——不加标签默认拉取latest。但latest不代表最新,它只是个标签,维护者可能几个月没更新。所以,我建议你生产环境永远指定具体版本号。

我的习惯:拉镜像前先用 docker search nginx 看看有哪些版本,或者直接去Docker Hub官网查。别盲目拉latest。

2.2 镜像查看:docker images

拉完镜像,总得看看本地有什么货吧?docker images 就是干这个的。

# 列出所有本地镜像
docker images

# 更详细的信息
docker images --no-trunc

# 只看某个镜像
docker images nginx

输出结果长这样:

REPOSITORY TAG IMAGE ID CREATED SIZE
nginx 1.21.6 605c77e624dd 2 weeks ago 142MB
nginx latest 605c77e624dd 2 weeks ago 142MB

注意看,同一个IMAGE ID可能对应多个标签。这说明它们指向同一个镜像层,只是名字不同。我刚开始没搞懂这个,以为占了双倍空间,其实不是。

2.3 镜像删除:docker rmi

镜像多了,磁盘就报警。删除镜像,用 docker rmi

# 删除指定镜像(通过标签)
docker rmi nginx:1.21.6

# 删除指定镜像(通过ID)
docker rmi 605c77e624dd

# 强制删除(镜像被容器引用时)
docker rmi -f nginx:latest

# 删除所有未使用的镜像
docker image prune -a
注意:如果镜像正在被容器使用,直接rmi会报错。我曾经手滑删了一个正在运行的镜像,结果容器还在跑,但镜像没了。嗯,容器其实用的是镜像层缓存,删了镜像不影响运行中的容器,但你再想启动新容器就麻烦了。

我的建议是:先停容器,再删镜像。养成好习惯。

2.4 镜像构建:docker build

这才是重头戏。构建镜像,就是把你写的代码、配置、依赖打包成一个可交付的单元。

# 基本构建
docker build -t myapp:v1.0 .

# 指定Dockerfile路径
docker build -f ./docker/Dockerfile.prod -t myapp:prod .

# 带构建参数
docker build --build-arg VERSION=1.0 -t myapp:v1.0 .

构建的核心是Dockerfile。我见过太多人把Dockerfile写得又臭又长。记住一个原则:每一层都要精简

举个例子,我优化过的一个Node.js应用Dockerfile:

FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM node:16-alpine
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]

为什么用多阶段构建?因为最终镜像只保留运行所需的东西,构建工具全扔了。我有个项目,优化前镜像1.2GB,优化后只有180MB。你想想看,这差距多大。

避坑指南:我曾经把整个项目目录COPY进去,结果node_modules也进去了,镜像直接炸了。后来加了.dockerignore文件,世界清净了。

2.5 镜像导出导入:docker save / docker load

有时候网络不通,或者要离线部署,就得用save和load。说白了就是打包和解包。

# 导出镜像为tar文件
docker save -o nginx.tar nginx:1.21.6

# 也可以导出多个
docker save -o myimages.tar nginx:1.21.6 redis:7.0

# 导入镜像
docker load -i nginx.tar

这里有个细节:save保存的是镜像的完整元数据和所有层,而export/import只能保存容器的文件系统。我刚开始搞混过,结果导出的东西死活跑不起来。

操作 作用对象 保留历史 适用场景
docker save 镜像 镜像迁移、离线部署
docker export 容器 容器快照、调试

我的经验是:生产环境迁移,永远用save/load。export/import会丢失层信息,镜像体积虽然小了,但没法增量更新。

2.6 标签管理:docker tag

标签,就是镜像的别名。同一个镜像可以有多个标签,方便版本管理。

# 给镜像打标签
docker tag nginx:1.21.6 myregistry.com/nginx:1.21.6

# 打多个标签
docker tag nginx:1.21.6 myregistry.com/nginx:latest
docker tag nginx:1.21.6 myregistry.com/nginx:stable

标签命名有讲究。我见过有人用 v1.0-final-真的不改了 这种标签,嗯,看着就头疼。

我的标签规范:

  • 语义化版本:v1.2.3、v2.0.0-rc1
  • 环境标识:latest、stable、dev
  • 构建信息:v1.2.3-build.20240301
小技巧:推送镜像到仓库前,先打上仓库地址的标签。比如 docker tag myapp:v1.0 registry.example.com/myapp:v1.0,然后再push。这样不会搞混本地和远程的镜像。

2.7 实战经验总结

说了这么多,我总结几条铁律:

  1. 镜像要小——多用多阶段构建,少装无用包
  2. 标签要规范——别用latest上生产,指定版本号
  3. 定期清理——docker image prune 每周跑一次
  4. 离线部署用save/load——别用export/import,会丢东西

镜像管理看起来简单,但做不好,微服务架构就是沙上建塔。我见过一个团队,镜像管理混乱,导致线上回滚时找不到正确的版本,最后只能重新构建。嗯,那场面,挺尴尬的。

下一节,我们聊聊容器生命周期管理。到时候我会讲一个我踩过的坑——容器启动参数写错,导致服务三天没连上数据库。敬请期待。