提示本文原创作品良心制作干货为主简洁清晰一看就会文章目录前言一、基础概念1.1 dockerfile是什么1.2 docker分层文件系统1.3 docker镜像原理1.4 容器一致性二、Dockerfile基础命令2.1 dockerfile 简单指令2.2 docker构建镜像语法三、Dockerfile实例3.1 单阶段构建前端项目3.2 单阶段构建后端项目3.3 多阶段构建前端项目3.4 多阶段构建后端项目四、Dockerfile优化4.1 使用更轻量的基础镜像4.2 合并 RUN 指令清理缓存文件4.3 使用 .dockerignore 排除无关文件4.4 利用多阶段构建4.5 合理使用 COPY/ADD优先用 COPY4.6 减少镜像层数合理排序指令4.7 清理不必要的依赖和文件4.8 使用非root用户运行容器4.9 总结前言随着容器化技术的普及Docker 已成为现代应用交付的核心基础设施。而 Dockerfile 作为构建容器镜像的 “代码化蓝图”将环境配置、依赖安装、应用部署等流程固化为可复用的文本彻底解决了 “在我机器上能跑” 的环境一致性难题。本文将从基础概念到最佳实践系统讲解 Dockerfile 的编写逻辑帮助你高效构建安全、轻量的容器镜像实现从代码到容器的无缝交付一、基础概念1.1 dockerfile是什么Dockerfile是一个由特定指令构成的纯文本文件是构建 Docker 镜像的标准化 “图纸”。它通过 FROM、RUN、COPY、CMD 等固定指令将操作系统、依赖、配置、代码等环境信息逐一定义让 Docker 按步骤自动构建出可直接运行的镜像实现环境可重复、可移植、可版本管理1.2 docker分层文件系统这张图展示了 docker 容器的分层文件系统结构从上到下分为三个核心部分可读写层rw作用这是容器运行时唯一可写入的层。所有在容器内产生的新文件、修改的文件都存储在这一层。当容器被删除时这一层的数据也会随之丢失这也是为什么需要数据卷Volume来实现持久化的原因Init层rowh作用这是一个特殊的只读层由 Docker 自动生成。它主要用于存放/etc/hosts、/etc/resolv.conf等容器运行时必需的配置文件。它被标记为rowh只读写时复制保证了基础镜像的完整性只读层rowh作用这些层来自基础镜像ubuntu:latest是完全只读的。它们包含了操作系统的核心文件、库和应用。当容器需要修改这些层中的文件时AUFS 会触发“写时复制”Copy-on-Write机制将该文件复制到最上层的可读写层中进行修改从而保证了基础镜像的不可变性1.3 docker镜像原理Dockerfile 采用分层构建机制每一条指令对应镜像的一个只读层。Docker 读取文件后按顺序逐层执行、缓存复用最后在顶层生成可读写层形成容器。构建过程基于 UnionFS 联合文件系统实现层与层之间的叠加、复用与共享保证镜像轻量化、构建高效化1.4 容器一致性容器一致性就是同一个镜像在任何机器上运行环境、行为、结果完全一样容器一致性的核心来源于镜像分层 隔离机制Dockerfile 将系统、依赖、配置、代码全部固化为不可变的镜像层保证构建内容完全一致。容器运行时通过 Linux Namespace 和 Cgroups 实现资源隔离使每个容器拥有独立的进程、网络、文件系统不受宿主机环境影响。因此无论在任何环境运行容器的行为都高度统一二、Dockerfile基础命令2.1 dockerfile 简单指令Dockerfile 通过一系列标准化指令定义镜像构建流程以下是最常用的基础指令每个指令配核心作用和极简示例指令核心作用简单示例FROM指定基础镜像必选镜像的“底层操作系统/环境”FROM ubuntu:22.04基于UbuntuRUN构建镜像时执行命令安装依赖、配置环境如apt/yum安装软件RUN apt update apt install nginx -yCOPY将宿主机文件/目录复制到镜像内仅本地文件不能跨机器COPY ./app.jar /usr/local/ADD增强版COPY支持解压压缩包、下载远程文件ADD https://xxx.tar.gz /tmp/WORKDIR设置容器运行/指令执行的工作目录后续指令默认在此目录操作WORKDIR /app后续指令在/app下ENV设置环境变量容器内可读取如配置端口、路径ENV PORT8080EXPOSE声明容器要暴露的端口仅声明不实际映射提示作用EXPOSE 8080CMD容器启动时执行的命令可被docker run参数覆盖一个Dockerfile仅最后一个生效CMD [java, -jar, app.jar]ENTRYPOINT容器启动的固定入口命令不可被覆盖需传参时配合CMDENTRYPOINT [nginx, -g, daemon off;]VOLUME声明数据卷指定容器内持久化目录避免数据丢失VOLUME [/data]LABEL为镜像添加元数据标签键值对形式用于分类、说明镜像信息如作者、版本LABEL authortestxxx.com version1.0USER指定后续指令执行的用户/用户组默认root切换非root用户提升安全性USER nginx切换为nginx用户核心补充说明RUN分两种写法RUN 命令shell格式、RUN [命令, 参数]exec格式推荐避免shell解析问题CMD和ENTRYPOINT区别CMD是“默认命令”可替换ENTRYPOINT是“固定入口”比如ENTRYPOINT [ping] CMD [baidu.com]运行容器时docker run 镜像 google.com会把CMD替换成google.com指令执行会生成镜像层尽量合并RUN命令如用连接减少镜像层数提升效率USER注意事项切换的用户需提前在镜像中创建可通过RUN useradd xxx创建否则会报错LABEL可多组合并写LABEL authortestxxx.com descriptionnginx镜像便于docker inspect查看镜像信息总结FROM是基础RUN做构建COPY/ADD传文件WORKDIR定路径CMD/ENTRYPOINT管启动EXPOSE/VOLUME处理网络和数据LABEL加元数据USER控权限指令按执行顺序编写层越少、逻辑越简洁镜像越轻量且安全2.2 docker构建镜像语法# 基础语法格式dockerbuild[可选参数]构建上下文路径/URL核心可选参数参数作用示例-t/--tag给镜像打标签格式名称:版本版本默认latest-t react-demo:v1-f/--file指定非默认名称/路径的Dockerfile默认找上下文里的Dockerfile-f ./docker/Dockerfile.prod--no-cache不使用构建缓存强制重新执行所有RUN指令解决缓存导致的依赖更新问题--no-cache# 在Dockerfile所在目录执行给镜像打标签react-demo:v1dockerbuild-treact-demo:v1..代表构建上下文是当前目录Docker会读取当前目录的Dockerfile构建后镜像标签为react-demo:v1# Dockerfile不在当前目录/名称不是Dockerfile时使用dockerbuild-treact-demo:prod-f./docker/Dockerfile.prod.-f指定使用./docker/目录下的Dockerfile.prod文件镜像标签为react-demo:prod 默认只认Dockerfile首字母大写非默认名称必须用-f指定# 不使用缓存重新安装所有依赖比如npm包更新时dockerbuild-treact-demo:v2 --no-cache.如果修改了RUNnpminstall但没生效加--no-cache重新构建总结构建镜像核心命令是docker build -t 镜像名:版本 上下文路径-t和.是最核心的两个部分-f用于指定自定义Dockerfile--no-cache用于强制刷新构建步骤构建上下文路径决定了COPY/ADD能读取的文件范围优先用.当前目录三、Dockerfile实例3.1 单阶段构建前端项目构建 React 前端项目的 Dockerfilerootdocker1:~# docker pull node:16.20.0 #下载基础镜像rootdocker1:~# mkdir -p /data/docker-images/web-react #创建存放镜像的目录rootdocker1:~# vim /data/docker-images/web-react/Dockerfile# 基础镜像FROM node:16.20.0# 全局配置npm淘宝源优先级最高避免被项目配置覆盖RUNnpmconfigsetregistry https://registry.npmmirror.comnpmconfigsetfetch-retry-maxtimeout60000npmconfigsetcache /tmp/npm-cache# 全局安装最新create-react-app避免旧版本问题RUNnpminstall-gcreate-react-applatest# 创建全新React项目无旧依赖、无配置污染RUN create-react-app my-react-app --use-npm# 切换到项目目录WORKDIR /my-react-app# 强制重新安装依赖确保用淘宝源RUNnpminstall--force# 声明端口EXPOSE3000# 启动项目CMD[npm,start]rootdocker1:~# cd /data/docker-images/web-reactrootdocker1:/data/docker-images/web-react# docker build -t react:v1 . #构建镜像rootdocker1:/data/docker-images/web-react# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZE react v1 56480e51c2f320seconds ago1.36GBnode16.20.0 c56ec223ce192years ago 909MB rootdocker1:/data/docker-images/web-react# docker run -itd --name react-app -p 3000:3000 react:v1浏览器访问ip:30003.2 单阶段构建后端项目基于阿里云的 JRE 镜像构建一个包含 Spring Cloud Eureka 服务的 Docker 镜像并运行这个镜像容器最终启动一个 Eureka 注册中心服务# 拉取镜像rootdocker1:~# docker pull registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data# 拉取项目rootdocker1:~# git clone https://gitee.com/luqing000000/java-test.gitrootdocker1:~# ls java-test/Dockerfile spring-cloud-eureka-0.0.1-SNAPSHOT.jar rootdocker1:~# cat java-test/Dockerfile# 基础镜像可以按需修改可以更改为公司自有镜像FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data# 切换工作目录WORKDIR /app# jar 包名称改成实际的名称本示例为 spring-cloud-eureka-0.0.1-SNAPSHOT.jarCOPY spring-cloud-eureka-0.0.1-SNAPSHOT.jar /app# 端口EXPOSE8761# 启动 Jar 包CMD[java,-jar,spring-cloud-eureka-0.0.1-SNAPSHOT.jar]rootdocker1:~#rootdocker1:~# cd java-test/rootdocker1:~/java-test# docker build -t java-test:v1 .rootdocker1:~/java-test# docker run -itd --name java-jar-test -p 8761:8761 java-test:v1rootdocker1:~/java-test# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 37e571b42cd7 java-test:v1java -jar spring-cl…12seconds ago Up8seconds0.0.0.0:8761-8761/tcp, :::8761-8761/tcp java-jar-test浏览器访问ip:87613.3 多阶段构建前端项目# Step 1拉取代码rootdocker1:~# mkdir /data/docker-images/webserver-vuerootdocker1:~# cd /data/docker-images/webserver-vuerootdocker1:~/webserver-vue# git clone https://gitee.com/luqing000000/docker-test.git# Step 2编写Dockecrfilerootdocker1:~/webserver-vue# cat DockerfileFROM registry.cn-hangzhou.aliyuncs.com/hujiaming/node:16.15.0 AS builder COPY ./ /app WORKDIR /app RUNnpminstall--registryhttp://registry.npmmirror.comnpmrun build FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/nginx:v1.24.0 RUNmkdir/app COPY--frombuilder /app/dist /app COPY nginx.conf /etc/nginx/nginx.conf# Step 3编写nginx配置文件rootdocker1:~/webserver-vue# cat nginx.confuser nginx;worker_processes1;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;events{worker_connections1024;}http{include /etc/nginx/mime.types;default_type application/octet-stream;log_format main$remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for;access_log /var/log/nginx/access.log main;sendfile on;keepalive_timeout65;server{listen80;server_name localhost;location /{root /app;index index.html;try_files$uri$uri/ /index.html;}error_page500502503504/50x.html;location/50x.html{root /usr/share/nginx/html;}}}# Step 4构建镜像rootdocker1:~/webserver-vue# docker build -t webserver:v1 .rootdocker1:~/webserver-vue# docker run -itd --name webserver-vue1 -p 82:80 webserver:v1浏览器访问ip:823.4 多阶段构建后端项目rootdocker1:~# git clone https://gitee.com/luqing000000/spring-boot-project.gitrootdocker1:~# cd spring-boot-project/rootdocker1:~/spring-boot-project# cat Dockerfile# 第一阶段构建阶段builder # 基于阿里云镜像源的 maven 3.5.3 镜像作为构建基础镜像包含Maven编译环境# AS builder 给这个阶段起别名方便后续阶段引用FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/maven:3.5.3 AS builder# 设置容器内的工作目录为 /app后续指令都基于此目录执行WORKDIR /app# 将宿主机当前目录Dockerfile所在目录的所有文件复制到容器的 /app 目录下COPY ./.# 执行Maven命令清理旧构建产物 → 编译打包 → 跳过单元测试加快构建速度# 最终会在 /app/target 目录生成可执行的jar包RUN mvn cleaninstall-DskipTests# 第二阶段运行阶段 # 基于阿里云镜像源的轻量JRE 8u211镜像仅包含Java运行环境无编译环境减小体积# 选择jre而非jdk因为运行jar包仅需运行时环境进一步精简镜像FROM registry.cn-hangzhou.aliyuncs.com/hujiaming/jre:8u211-data# 从第一阶段builder的 /app/target 目录复制打包好的jar包到当前阶段的工作目录默认/# 仅复制最终产物丢弃构建阶段的Maven环境、源码等大幅减小最终镜像体积COPY--frombuilder /app/target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar.# 声明容器暴露的端口为8761Eureka默认端口仅为声明作用实际映射需docker run -p指定EXPOSE8761# 容器启动时执行的命令通过java -jar运行Eureka服务的jar包CMD[java,-jar,spring-cloud-eureka-0.0.1-SNAPSHOT.jar]rootdocker1:~/spring-boot-project# docker build -t spring-boot:v1 .rootdocker1:~/spring-boot-project# docker run -itd --name spring-boot-test -p 8761:8761 spring-boot:v1浏览器访问ip:8761四、Dockerfile优化Dockerfile 的优化核心目标是减小镜像体积、加快构建速度、提升镜像安全性同时让构建过程更高效、可维护4.1 使用更轻量的基础镜像基础镜像是镜像体积的“大头”优先选择官方精简版镜像避免使用ubuntu/centos等完整发行版改用alpine基于 musl libc体积通常 10MB对于语言类镜像选择官方的 slim/alpine 变体如python:3.12-slim、node:20-alpine反例FROM ubuntu:22.04 RUN apt-get update apt-get install -y python3优化后FROM python:3.12-alpine # 体积从几百MB降至几十MB4.2 合并 RUN 指令清理缓存文件Docker 每层镜像都会保留修改过多RUN指令会增加层数和体积反例多层且未清理缓存FROM ubuntu:22.04 RUN apt-get update RUN apt-get install -y nginx优化后合并为一层即时清理FROM ubuntu:22.04 RUN apt-get update \ apt-get install -y nginx4.3 使用 .dockerignore 排除无关文件和.gitignore类似.dockerignore可以排除不需要加入构建上下文的文件如node_modules、.git、日志、测试文件减少构建上下文体积加快COPY/ADD速度示例 .dockerignore# 排除Node.js依赖 node_modules/ # 排除git相关 .git/ .gitignore # 排除日志和临时文件 *.log tmp/ # 排除测试文件 tests/4.4 利用多阶段构建多阶段构建可以将“构建环境”和“运行环境”分离只保留运行所需的文件大幅减小最终镜像体积尤其适合编译型语言Go、Java、C示例本文的3.3、3.4小节4.5 合理使用 COPY/ADD优先用 COPYCOPY仅用于文件/目录复制功能简单且明确优先使用ADD会自动解压压缩包、支持URL下载不推荐仅在需要解压时使用反例ADD https://example.com/package.tar.gz /app # 不推荐用ADD下载文件 COPY . . # 复制所有文件包含无关内容优化后# 下载文件用RUN wget/curl更可控 RUN wget https://example.com/package.tar.gz -O /app/package.tar.gz \ tar -xzf /app/package.tar.gz -C /app \ rm /app/package.tar.gz COPY main.go go.mod go.sum /app/ # 仅复制编译必需的文件4.6 减少镜像层数合理排序指令Dockerfile 中除FROM外每个指令RUN/COPY/ADD/ENV等都会创建一层层数越多镜像越臃肿合并无关的RUN指令见第2点把“变化频率低的指令”如安装依赖放在前面利用 Docker 构建缓存变化频率高的指令如复制业务代码放在后面避免缓存失效示例合理排序FROM python:3.12-alpine WORKDIR /app # 先复制依赖文件变化少利用缓存 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # --no-cache-dir 避免缓存pip包 # 再复制业务代码变化多仅代码修改时重新构建这层 COPY . . CMD [python, app.py]4.7 清理不必要的依赖和文件安装依赖时仅安装“运行时依赖”而非“构建时依赖”如编译用的gcc/make安装后删除移除临时文件、日志、包管理缓存如apt-get clean、pip cache purge示例清理构建依赖FROM alpine:3.19 RUN apk add --no-cache --virtual .build-deps gcc musl-dev \ # 安装运行时依赖 编译依赖 apk add --no-cache python3 \ # 编译安装某个包 pip install --no-cache-dir some-package \ # 删除构建依赖保留运行时依赖 apk del .build-deps4.8 使用非root用户运行容器默认以root用户运行容器有安全风险优化为创建普通用户FROM python:3.12-alpine WORKDIR /app # 创建普通用户并授权 RUN addgroup -S appgroup adduser -S appuser -G appgroup COPY --chownappuser:appgroup . . # 复制文件时指定所属用户 # 切换到普通用户 USER appuser CMD [python, app.py]4.9 总结Dockerfile 优化的核心要点可归纳为减体积用轻量基础镜像、多阶段构建、清理缓存/依赖、合并镜像层提速度合理排序指令利用缓存、.dockerignore减小构建上下文、使用 BuildKit保安全非root用户运行、清理敏感信息、仅保留必要依赖。通过以上优化镜像体积通常能减少 50% 以上构建速度提升 30%~80%同时镜像的安全性和可维护性也会显著提高注文中若有疏漏欢迎大家指正赐教。本文为100%原创转载请务必标注原创作者尊重劳动成果。求赞、求关注、求评论你的支持是我更新的最大动力评论区等你