gitignore与dockerignore文件的用法
在日常开发里,.gitignore 和 .dockerignore 都是“排除文件”的配置,但它们服务的对象并不一样:
.gitignore影响 Git 是否追踪某些文件。.dockerignore影响 Docker 构建镜像时,哪些文件不会被发送到构建上下文。
看起来只是少提交几个文件、少复制几个文件,实际它们会直接影响仓库干净程度、镜像构建速度、镜像安全性,以及多人协作时的稳定性。
为什么需要 ignore 文件
一个项目目录中通常会同时存在源码、构建产物、依赖缓存、本地配置、日志、临时文件等内容。
例如:
project/
├── src/
├── node_modules/
├── dist/
├── .env
├── app.log
└── Dockerfile
其中 src/ 是应该进入版本库的源码,node_modules/ 可以通过包管理器重新安装,dist/ 可能是构建输出,.env 里可能包含数据库密码或访问密钥,app.log 是运行时日志。
如果不做排除,Git 仓库会变得臃肿,Docker 构建上下文也会变大,甚至可能把敏感信息打进镜像。
.gitignore 的作用
.gitignore 用来告诉 Git:哪些未被追踪的文件不需要出现在 git status 中,也不应该被 git add 默认加入暂存区。
常见适合写入 .gitignore 的内容有:
- 编译产物:
dist/、build/、target/ - 依赖目录:
node_modules/、vendor/ - 日志文件:
*.log - IDE 配置:
.idea/、.vscode/ - 系统文件:
.DS_Store、Thumbs.db - 本地环境变量:
.env、.env.local
一个常见的 .gitignore 可以这样写:
# dependencies
node_modules/
# build output
dist/
build/
# logs
*.log
npm-debug.log*
# local env
.env
.env.local
# system files
.DS_Store
Thumbs.db
# IDE
.idea/
.vscode/
这样做以后,新产生的这些文件不会干扰 git status,也不容易被误提交。
.dockerignore 的作用
.dockerignore 用来告诉 Docker:执行 docker build 时,哪些文件不需要发送给 Docker daemon 作为构建上下文。
这和 .gitignore 的目标不同。Git 关心的是版本控制,Docker 关心的是构建镜像时能看到哪些文件。
例如执行:
docker build -t my-app .
命令最后的 . 表示当前目录会作为构建上下文。Docker 会把这个目录下的文件打包发送给构建进程,然后 Dockerfile 里的 COPY、ADD 才能使用这些文件。
如果没有 .dockerignore,一些完全不需要进镜像的内容也会被发送过去,比如 .git/、node_modules/、测试报告、日志文件、密钥文件等。
一个常见的 .dockerignore 可以这样写:
.git
.gitignore
node_modules
npm-debug.log
dist
coverage
.env
.env.*
Dockerfile
docker-compose.yml
.DS_Store
README.md
注意,是否忽略 Dockerfile、README.md 取决于项目需求。它们不一定会进入镜像,但从“减少构建上下文”的角度看,如果构建阶段不需要它们,就可以排除。
基本匹配规则
.gitignore 和 .dockerignore 的语法非常接近,常见规则如下。
忽略文件
debug.log
表示忽略名为 debug.log 的文件。
忽略目录
logs/
结尾带 / 时,表示忽略目录。
使用通配符
*.log
*.tmp
* 可以匹配任意字符。上面的配置会忽略所有 .log 和 .tmp 后缀的文件。
指定根目录
/dist
开头带 / 时,表示从当前 ignore 文件所在目录的根位置开始匹配。
如果写成:
dist/
则可能匹配任意层级下名为 dist 的目录。
反向保留
*.log
!important.log
! 表示不要忽略。上面的规则表示忽略所有 .log 文件,但保留 important.log。
不过这里有一个容易踩坑的地方:如果父目录已经被忽略,里面的文件通常不能仅靠 ! 重新加入,需要先把目录本身保留下来。
例如:
logs/*
!logs/important.log
这表示忽略 logs/ 目录下的大部分文件,但保留 logs/important.log。
二者的关键区别
.gitignore 和 .dockerignore 不应该简单复制粘贴,因为它们的使用场景不同。
| 文件 | 作用对象 | 主要目的 |
|---|---|---|
.gitignore | Git 工作区 | 避免提交不需要进入版本库的文件 |
.dockerignore | Docker 构建上下文 | 减少构建上下文,避免无关文件影响镜像构建 |
例如 dist/ 是否应该忽略,就要看项目构建方式:
- 如果
dist/是 CI 或本地构建产物,通常应该写入.gitignore。 - 如果 Dockerfile 会在镜像里重新执行构建,
dist/通常也应该写入.dockerignore。 - 如果 Dockerfile 只负责把已经构建好的
dist/拷贝进 Nginx 镜像,那.dockerignore就不能忽略dist/。
再例如 node_modules/:
- 对 Git 来说,通常应该忽略。
- 对 Docker 来说,如果镜像里会执行
npm install,也应该忽略。 - 但如果某些离线构建场景必须复制本地依赖,就不能盲目忽略。
已经被 Git 追踪的文件怎么办
.gitignore 只影响“尚未被 Git 追踪”的文件。如果一个文件已经被提交过,后来再加入 .gitignore,Git 仍然会继续追踪它的变化。
这时需要从 Git 索引中移除,但保留本地文件:
git rm --cached .env
如果是目录:
git rm -r --cached dist/
然后提交这次变更:
git add .gitignore
git commit -m "Update gitignore rules"
这样文件会从版本库中移除,但本地文件仍然存在。
Docker 构建上下文的坑
.dockerignore 最容易被忽视的问题是:构建上下文过大。
例如一个项目里有几百 MB 的 node_modules/,如果没有写入 .dockerignore,每次 docker build 都可能要把它发送给 Docker。即使 Dockerfile 没有 COPY node_modules,这个传输动作也已经发生了。
另一个问题是敏感文件。
假设 .env 里有生产数据库密码,如果它进入了构建上下文,就存在被 COPY . . 打进镜像的风险。即使后续某一层删除了这个文件,也不代表它没有出现在镜像历史层中。
因此 .dockerignore 中通常应该明确排除:
.env
.env.*
*.pem
*.key
id_rsa
id_rsa.pub
Node 项目的示例
.gitignore:
node_modules/
dist/
coverage/
*.log
.env
.env.local
.DS_Store
.dockerignore:
.git
node_modules
coverage
*.log
.env
.env.*
.DS_Store
README.md
配合 Dockerfile:
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
CMD ["npm", "start"]
这里忽略本地 node_modules 是合理的,因为镜像会通过 npm ci 安装依赖。
Java 项目的示例
.gitignore:
target/
*.class
*.log
.idea/
.settings/
.classpath
.project
.dockerignore:
.git
target
*.log
.idea
README.md
如果 Dockerfile 负责在镜像里执行 Maven 构建,那么 target/ 可以忽略。
如果 Dockerfile 是下面这种只复制 jar 包的形式:
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY target/app.jar app.jar
CMD ["java", "-jar", "app.jar"]
那就不能在 .dockerignore 里忽略 target/,否则 COPY target/app.jar app.jar 会找不到文件。
排查 ignore 是否生效
Git 可以用 check-ignore 查看某个文件为什么被忽略:
git check-ignore -v .env
如果想看当前未追踪但没有被忽略的文件:
git status --short
Docker 侧可以通过观察构建输出中的上下文大小来判断 .dockerignore 是否明显生效:
docker build -t my-app .
如果看到发送的 build context 很大,就应该优先检查 .dockerignore。
也可以临时写一个 Dockerfile 来验证某个文件是否还在构建上下文里:
FROM alpine
WORKDIR /app
COPY . .
RUN find . -maxdepth 2 -type f | sort
如果某个文件已经被 .dockerignore 排除,COPY . . 后就不会出现在列表里。
实践建议
.gitignore 的核心原则是:凡是可以由源码、配置或依赖管理工具重新生成的内容,都尽量不要提交。
.dockerignore 的核心原则是:凡是 Docker 构建不需要的内容,都尽量不要进入构建上下文。
两者可以有相似规则,但不要机械保持一致。每次写 ignore 文件时,都可以问两个问题:
- 这个文件是否应该成为项目历史的一部分?
- 这个文件是否必须参与镜像构建?
第一个问题决定 .gitignore,第二个问题决定 .dockerignore。
总结
.gitignore 让 Git 仓库保持干净,.dockerignore 让 Docker 构建更快、更安全。
前者关注版本控制,后者关注构建上下文。它们语法接近,但职责不同。写好这两个文件以后,项目会少很多无意义的文件变更,镜像也更不容易混入本地缓存、日志和密钥。