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 编排多容器应用!

🔗 权威参考:

相关推荐
阳光九叶草LXGZXJ5 小时前
达梦数据库-报错-15-列【XXX】长度超出定义
linux·运维·数据库·sql·学习
奔跑的大白啊5 小时前
多容器共享目录权限踩坑记
linux·ubuntu·共享目录·linux权限·docker 容器化部署
黄大刀6 小时前
DataPulse一款开箱即用的SCADA(四)-历史记录
运维·scada·动环
归秋1428 小时前
2026 企业 AI 办公工具选型指南:从评估框架到产品适配
大数据·运维·人工智能
Android系统攻城狮8 小时前
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
android·linux·运维·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
H.莓飛9 小时前
【C++】命名空间、缺省参数、函数重载与引用
linux·开发语言·c++·visual studio
91刘仁德9 小时前
IP协议详解:从IP协议头到网段划分、路由与NAT
linux·服务器·网络·网络协议·tcp/ip
硅基手札10 小时前
【Linux内核专栏 14】网络协议栈
linux·运维·网络协议
沫璃染墨12 小时前
《Linux工程实践篇(一):认识设计模式——从日志系统看策略模式》
linux·c++·安全·设计模式·策略模式
工作10年+,存储芯片行业12 小时前
Linux NVMe 中断排查与性能优化:CPU 亲和性
linux·运维·服务器·windows·性能优化·ssd·pcie