Docker学习路径——8、Dockerfile

Dockerfile 从入门到精通:构建可复用、高效、安全的容器镜像

在《四、制作/更改镜像》中,我们通过 docker commit 手动创建镜像,但这种方式存在不可重复、难维护、镜像臃肿 等致命缺陷。Dockerfile 的出现彻底改变了这一局面------它用声明式脚本定义镜像构建过程,成为现代 DevOps 的基石。

为什么必须用 Dockerfile

  • 版本控制:Dockerfile 可纳入 Git 管理
  • 自动化构建:CI/CD 流水线直接调用
  • 分层缓存:加速重复构建
  • 最小化镜像:精准控制每一层内容

一、Dockerfile 核心原理

构建流程深度解析

Docker 构建过程本质是逐层叠加

  1. 启动临时容器 :基于 FROM 镜像
  2. 执行指令 :如 RUN apt-get install
  3. 提交新层 :类似 docker commit
  4. 清理临时容器:仅保留镜像层
  5. 循环执行:直到所有指令完成

💡 关键认知

每条指令生成一个只读层,最终镜像是所有层的叠加。删除文件不会减小镜像体积(因历史层仍存在)!


二、核心指令详解(附最佳实践)

指令 作用 最佳实践
FROM 基础镜像 优先选 alpine(<5MB)或官方 slim 镜像
RUN 安装软件 合并命令:RUN apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/*
COPY 复制文件 优于 ADD(避免自动解压陷阱)
ENV 环境变量 集中管理配置(如 JAVA_HOME
EXPOSE 声明端口 仅文档作用,运行时仍需 -p 映射
WORKDIR 工作目录 替代 RUN cd ...(避免路径混乱)
CMD 默认命令 可被 docker run 覆盖
ENTRYPOINT 入口点 CMD 组合实现参数传递

⚠️ ADD vs COPY

  • ADD 会自动解压 tar 文件(可能非预期行为)
  • ADD 支持 URL 下载(但建议用 RUN wget
    → 99% 场景用 COPY

三、实战案例:构建 Centos + Java 镜像

需求分析

  • 基础系统:CentOS
  • 必备工具:vim, ifconfig(net-tools)
  • 运行环境:JDK 8
  • 痛点:官方 CentOS 镜像无 yum 源(国内网络慢)

优化版 Dockerfile

dockerfile 复制代码
# 使用精简基础镜像
FROM 192.168.100.50:5000/centos:7

# 维护者信息(已弃用,建议用 LABEL)
LABEL maintainer="xiani<123@qq.com>"

# 设置工作目录
WORKDIR /usr/local

# 复制 JDK(比 ADD 更明确)
COPY jdk-8u121-linux-x64.tar.gz /usr/local/

# 解压并清理(关键!减少镜像体积)
RUN tar -zxf jdk-8u121-linux-x64.tar.gz && \
    rm -f jdk-8u121-linux-x64.tar.gz

# 配置环境变量
ENV JAVA_HOME=/usr/local/jdk1.8.0_121 \
    JRE_HOME=/usr/local/jdk1.8.0_121/jre \
    CLASSPATH=/usr/local/jdk1.8.0_121/lib/dt.jar:/usr/local/jdk1.8.0_121/lib/tools.jar:/usr/local/jdk1.8.0_121/jre/lib \
    PATH=/usr/local/jdk1.8.0_121/bin:$PATH

# 安装必要工具(若网络允许)
# RUN yum install -y vim net-tools && yum clean all

# 声明端口(文档作用)
EXPOSE 80

# 默认命令
CMD ["/bin/bash"]

🔧 关键优化点

  1. 合并 RUN 指令:避免中间层残留 tar 包
  2. 显式 COPY:清晰表达意图
  3. 多行 ENV:提升可读性

构建与验证

bash 复制代码
# 构建镜像(注意末尾的 .)
docker build -t centosjava:1.0 .

# 验证 JDK
docker run --rm centosjava:1.0 java -version
# 输出:java version "1.8.0_121"

# 验证环境变量
docker run --rm centosjava:1.0 env | grep JAVA_HOME
# 输出:JAVA_HOME=/usr/local/jdk1.8.0_121

📊 镜像体积对比

  • 未清理 tar 包:601MB
  • 清理后:580MB(节省 21MB)

四、虚悬镜像(Dangling Images)深度解析

什么是虚悬镜像?

  • 定义 :没有仓库名和标签(<none>:<none>)的镜像
  • 产生原因
    1. 重新构建同名镜像(旧镜像失去引用)
    2. 构建中途失败(残留中间层)
    3. 手动删除镜像标签

识别与清理

bash 复制代码
# 查看虚悬镜像
docker images -f "dangling=true"

# 安全清理(仅删除虚悬镜像)
docker image prune

# 强制清理(包括停止的容器、构建缓存)
docker system prune -a

💡 生产建议

在 CI/CD 脚本末尾添加 docker image prune -f,避免磁盘占满


五、Dockerfile 高级技巧

1. 多阶段构建(Multi-stage Build)

场景:编译型语言(Go/Java)应用

dockerfile 复制代码
# 第一阶段:编译
FROM maven:3.8 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests

# 第二阶段:运行
FROM openjdk:8-jre-slim
COPY --from=builder /app/target/app.jar /app.jar
CMD ["java", "-jar", "/app.jar"]

效果:镜像体积从 500MB → 120MB!


2. 构建参数(ARG)

动态传入构建参数:

dockerfile 复制代码
ARG JDK_VERSION=8u121
COPY jdk-${JDK_VERSION}-linux-x64.tar.gz /usr/local/

构建时指定:

bash 复制代码
docker build --build-arg JDK_VERSION=8u292 -t myapp .

3. .dockerignore 文件

排除无关文件(类似 .gitignore):

gitignore 复制代码
.git
*.log
target/

避免将敏感文件或大文件(如日志)复制到镜像


六、安全加固指南

1. 非 root 用户运行

dockerfile 复制代码
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

2. 最小化攻击面

  • 不安装 SSH 服务
  • 删除 shell(RUN rm /bin/sh
  • 使用 distroless 镜像(仅含应用和 runtime)

3. 扫描漏洞

bash 复制代码
# 官方扫描工具
docker scan centosjava:1.0

# 第三方工具(Trivy)
trivy image centosjava:1.0

七、常见陷阱与解决方案

问题 原因 解决方案
构建缓慢 每次都重装软件 合并 RUN 指令,利用缓存
镜像过大 未清理缓存文件 apt-get clean && rm -rf /var/lib/apt/lists/*
中文乱码 缺少 locale RUN localedef -i zh_CN -f UTF-8 zh_CN.UTF-8
时区错误 默认 UTC RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

八、总结:Dockerfile 黄金法则

  1. 基础镜像最小化:Alpine > Slim > Full
  2. 指令合并:减少镜像层数
  3. 及时清理:删除临时文件、缓存
  4. 非 root 运行:提升安全性
  5. 多阶段构建:分离构建与运行环境

🚀 行动清单

  • 将现有 docker commit 镜像重写为 Dockerfile
  • 为团队制定 Dockerfile 规范
  • 在 CI 中集成镜像扫描

掌握 Dockerfile,你就拥有了打造标准化、轻量化、安全化 容器镜像的核心能力。下一步,我们将探索如何用 docker-compose 编排多容器应用!

🔗 权威参考

相关推荐
2601_955256472 小时前
服务器日志管理最佳实践:logrotate配置详解、云日志服务对比与Docker日志限制方案
运维·服务器·docker
切糕师学AI2 小时前
Docker Compose 完全指南:从入门到实践
运维·docker·容器
叶子上的考拉2 小时前
解决远程连接服务器反应较慢问题
linux·运维·服务器
JackSparrow4142 小时前
彻底理解Java NIO(一)C语言实现 单进程+多进程+多线程 阻塞式I/O 服务器详解
java·linux·c语言·网络·后端·tcp/ip·nio
lazybird742 小时前
vmware装的ubuntu22.04, 在vmware中将磁盘由40G调整为50G后,ubuntu中还需要进行调整
linux·运维·服务器
Beiwen_2 小时前
在 Linux 服务器上配置 LaTeX Workshop 并解决 `ifsym.sty` 和 `llncs.cls` 错误
linux·服务器
小江的记录本2 小时前
【微服务与云原生架构】Serverless架构、FaaS/BaaS、核心原理、优缺点
java·后端·微服务·云原生·架构·系统架构·serverless
IMPYLH2 小时前
Linux 的 stat 命令
linux·运维·服务器·bash
error:(2 小时前
Linux系统Claude Code安装指南:绕过官方curl 403错误的解决方案
linux·运维·服务器