Docker镜像操作:构建、推送、拉取与优化
上一章我们掌握了容器的全流程操作,而容器的“源头”是镜像——没有镜像,就无法创建容器。本章将深入镜像的核心操作:如何拉取和管理镜像、如何用Dockerfile自定义构建镜像、如何将镜像推送到仓库分享,以及关键的镜像优化技巧(让你的镜像更小、更高效)。
学会这些,你不仅能“用别人的镜像”,还能“造自己的镜像”,真正实现“一次构建,到处运行”的Docker核心价值。
一、镜像拉取:从仓库“下载”现成的镜像 📥
大多数时候,我们不需要从零构建镜像,而是直接使用仓库中现成的镜像(比如Nginx、MySQL)。拉取镜像用docker pull命令,简单直接。
核心语法:
docker pull [仓库地址/]镜像名称[:版本标签]
- 仓库地址:默认是Docker Hub(可省略),国内仓库需指定(如
registry.cn-hangzhou.aliyuncs.com/library/nginx); - 版本标签:默认拉取
latest(最新版),生产环境建议指定具体版本(如nginx:1.25),避免自动更新导致兼容问题。
示例:
- 拉取Docker Hub的Nginx最新版:
docker pull nginx # 等价于 docker pull docker.io/library/nginx:latest
- 拉取指定版本的MySQL(5.7版本):
docker pull mysql:5.7
- 从阿里云仓库拉取Nginx(速度更快):
docker pull registry.cn-hangzhou.aliyuncs.com/library/nginx:latest
二、镜像管理:查看、删除与标签操作 📋
拉取镜像后,需要学会管理本地镜像(查看列表、删除无用镜像、给镜像打标签等)。
1. 查看本地镜像列表
用docker images(或docker image ls)命令:
docker images
输出结果包含:
REPOSITORY:镜像仓库名;TAG:版本标签(latest表示最新);IMAGE ID:镜像唯一ID(前3位即可区分);SIZE:镜像体积。
2. 删除本地镜像
用docker rmi(或docker image rm)命令,注意删除前需先删除依赖该镜像的所有容器(否则会报错)。
- 语法:
docker rmi 镜像ID/镜像名:标签
-
示例:
# 先删除依赖该镜像的容器(假设容器名为my-nginx) docker rm -f my-nginx # 再删除镜像 docker rmi nginx:latest # 或用镜像ID删除(更快捷,只需前3位) docker rmi 605c77e624dd -
批量删除无标签镜像(俗称“虚悬镜像”,标签为
<none>):
docker rmi $(docker images -f "dangling=true" -q)
3. 给镜像打标签(重命名)
镜像标签不仅是版本标识,还决定了推送仓库的路径(比如推送到个人仓库需用用户名/镜像名:标签格式)。用docker tag命令打标签:
- 语法:
docker tag 原镜像名:原标签 新镜像名:新标签
- 示例:
把本地的nginx:latest标记为“准备推送到个人Docker Hub仓库的mynginx:v1”:
此时docker tag nginx:latest 你的DockerHub用户名/mynginx:v1docker images会显示一个新镜像(实际与原镜像共享存储,体积不增加)。
三、Dockerfile:构建自定义镜像的“配方” 📝
如果现成的镜像不符合需求(比如需要预装特定工具、修改默认配置),就需要自定义构建镜像。而Dockerfile就是构建镜像的“配方文件”——用指令描述镜像的构建步骤。
1. Dockerfile基础语法(核心指令)
一个简单的Dockerfile由一系列指令组成,常用指令如下:
| 指令 | 作用 | 示例 |
|---|---|---|
FROM | 指定基础镜像(必须是第一条指令) | FROM nginx:1.25 |
RUN | 执行命令(构建时运行) | RUN apt-get update && apt-get install -y curl |
COPY | 复制本地文件到镜像中 | COPY ./index.html /usr/share/nginx/html/ |
ADD | 类似COPY,但支持URL和自动解压 | ADD https://xxx.tar.gz /app/ |
WORKDIR | 设置工作目录(后续命令的执行目录) | WORKDIR /app |
EXPOSE | 声明容器运行时监听的端口(仅声明) | EXPOSE 80 443 |
CMD | 容器启动时执行的命令(可被覆盖) | CMD ["nginx", "-g", "daemon off;"] |
ENTRYPOINT | 容器启动时执行的命令(不可被覆盖) | ENTRYPOINT ["echo", "Hello"] |
2. 编写第一个Dockerfile:自定义Nginx镜像
目标:基于官方Nginx镜像,替换默认首页为自定义的index.html。
步骤:
-
新建一个文件夹(如
mynginx),在文件夹内创建两个文件:Dockerfile(无后缀)index.html(自定义首页内容)
-
index.html内容:
<h1>Hello, This is My Custom Nginx!</h1>
Dockerfile内容:
# 基础镜像:官方Nginx 1.25版本
FROM nginx:1.25# 维护者信息(可选)
LABEL maintainer="yourname@example.com"# 复制本地index.html到容器的Nginx默认页面目录
COPY ./index.html /usr/share/nginx/html/index.html# 声明容器监听80端口(仅文档作用)
EXPOSE 80
四、构建镜像:用Dockerfile“烘焙”镜像 🔥
有了Dockerfile,就可以用docker build命令构建镜像了。
核心语法:
docker build -t 镜像名:标签 构建上下文路径
-t:指定镜像的名称和标签(必填);- 构建上下文路径:Dockerfile所在的目录(
.表示当前目录),构建时会将该目录下的所有文件发送给Docker引擎,所以不要在上下文路径放无关文件(可通过.dockerignore排除)。
示例:构建自定义Nginx镜像
在mynginx文件夹所在目录执行:
docker build -t my-custom-nginx:v1 .
- 执行过程:Docker会逐条执行Dockerfile中的指令,每一步生成一个镜像层,最后输出镜像ID表示构建成功。
- 验证:用
docker images查看,会看到my-custom-nginx:v1镜像。
运行自定义镜像验证效果:
docker run -d -p 8080:80 --name test-custom-nginx my-custom-nginx:v1
访问localhost:8080,会看到自定义的“Hello, This is My Custom Nginx!”页面,说明构建成功。
五、推送镜像:把自己的镜像分享到仓库 📤
构建好的镜像默认只存在本地,若要分享给团队或在其他机器使用,需要推送到仓库(公有仓库如Docker Hub,或私有仓库)。
步骤1:登录仓库
- 登录Docker Hub:
docker login # 输入Docker Hub用户名和密码
- 登录私有仓库(如阿里云):
docker login registry.cn-hangzhou.aliyuncs.com # 输入仓库账号密码
步骤2:给镜像打符合仓库要求的标签
仓库要求镜像标签格式为仓库地址/用户名/镜像名:版本(Docker Hub可省略仓库地址)。
示例:准备推送到Docker Hub的个人仓库:
docker tag my-custom-nginx:v1 你的DockerHub用户名/my-custom-nginx:v1
步骤3:推送镜像
docker push 你的DockerHub用户名/my-custom-nginx:v1
- 推送成功后,在Docker Hub的个人仓库中可看到该镜像,其他人通过
docker pull 你的用户名/my-custom-nginx:v1即可拉取。
六、镜像优化:让你的镜像更小、更安全 🚀
镜像体积过大会导致拉取慢、存储占用高,甚至隐藏安全风险。掌握以下优化技巧,让你的镜像“瘦身”又安全。
技巧1:使用Alpine基础镜像(体积骤减)
Alpine是一个轻量级Linux发行版,基础镜像仅5-10MB(相比Ubuntu的100+MB、CentOS的200+MB)。
示例:用Alpine版Nginx替代官方版:
FROM nginx:1.25-alpine # 体积约20MB,而nginx:1.25约180MB
COPY ./index.html /usr/share/nginx/html/
技巧2:多阶段构建(分离构建和运行环境)
对于编译型语言(如Java、Go),构建过程需要编译器和依赖,但运行时不需要。多阶段构建可只保留运行时环境,大幅减小体积。
示例:构建Java应用(Spring Boot):
# 第一阶段:编译代码(用包含JDK的镜像)
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests # 编译打包得到jar包# 第二阶段:运行环境(仅保留JRE)
FROM openjdk:17-jre-slim
WORKDIR /app
# 从第一阶段复制jar包到当前镜像
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
- 效果:最终镜像仅包含JRE和jar包,体积比单阶段构建小70%以上。
技巧3:合并RUN命令,清理缓存
每个RUN指令会创建一个镜像层,合并命令可减少层数;同时清理安装缓存(如apt-get clean)。
反例(差):
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim # 多层+未清理缓存
正例(优):
RUN apt-get update && \apt-get install -y curl vim && \apt-get clean && \ # 清理APT缓存rm -rf /var/lib/apt/lists/* # 删除缓存文件
技巧4:使用.dockerignore文件
排除构建上下文路径中不需要的文件(如node_modules、.git),减少发送给Docker引擎的文件量,加速构建。
在Dockerfile同级目录创建.dockerignore:
node_modules
.git
*.log
七、避坑指南:镜像操作常见问题 ❌→✅
-
构建镜像时报“COPY failed: no such file or directory”
→ 原因:COPY的源文件路径错误,或文件不在构建上下文目录中。
→ 解决:确保文件存在,且路径相对于Dockerfile所在目录(上下文路径)。 -
删除镜像时报“image is referenced in multiple repositories”
→ 原因:一个镜像被打了多个标签(如nginx:latest和mynginx:v1指向同一镜像)。
→ 解决:先删除所有标签对应的镜像名,或用镜像ID删除。 -
推送镜像时报“denied: requested access to the resource is denied”
→ 原因:未登录仓库,或标签格式不符合仓库要求(如推送到Docker Hub需用用户名/镜像名)。
→ 解决:先docker login,再按仓库要求打标签。
八、总结:镜像操作核心命令与流程 📌
| 操作 | 命令 |
|---|---|
| 拉取镜像 | docker pull 镜像名:标签 |
| 查看镜像列表 | docker images |
| 删除镜像 | docker rmi 镜像ID/镜像名:标签 |
| 给镜像打标签 | docker tag 原镜像 新镜像:标签 |
| 构建镜像 | docker build -t 镜像名:标签 . |
| 推送镜像 | docker push 镜像名:标签 |
镜像作为Docker的“静态模板”,是容器运行的基础。下一章我们将学习如何让容器中的数据持久化(避免容器删除后数据丢失),这是部署数据库等应用的关键——毕竟,谁也不想重启容器后数据全没了~
小练习:用Dockerfile构建一个自定义Python镜像(基于python:3.11-alpine),预装requests库,然后推送到你的仓库。
