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
我的建议是:先停容器,再删镜像。养成好习惯。
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。你想想看,这差距多大。
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 实战经验总结
说了这么多,我总结几条铁律:
- 镜像要小——多用多阶段构建,少装无用包
- 标签要规范——别用latest上生产,指定版本号
- 定期清理——
docker image prune每周跑一次 - 离线部署用save/load——别用export/import,会丢东西
镜像管理看起来简单,但做不好,微服务架构就是沙上建塔。我见过一个团队,镜像管理混乱,导致线上回滚时找不到正确的版本,最后只能重新构建。嗯,那场面,挺尴尬的。
下一节,我们聊聊容器生命周期管理。到时候我会讲一个我踩过的坑——容器启动参数写错,导致服务三天没连上数据库。敬请期待。