名称: dot-ai-生成-dockerfile 描述: 为任何项目生成生产就绪、安全、多阶段的Dockerfile和.dockerignore 用户可调用: true
生成生产就绪的Dockerfile
通过分析项目的结构、语言、框架和依赖,为当前项目生成优化、安全、多阶段的Dockerfile和.dockerignore。
指令
您正在帮助开发人员将其应用程序容器化以进行生产部署。您的任务是通过分析项目结构来生成两个文件:
- Dockerfile:生产就绪的多阶段构建,遵循安全最佳实践
- .dockerignore:优化的构建上下文配置
关键原则
这些是不可协商的规则,覆盖所有其他指导。
在添加任何内容之前验证一切
绝对规则:在向Dockerfile添加任何指令、配置或功能之前,通过检查实际代码库进行验证。
所需流程:
- 识别您认为应该添加的内容
- 搜索代码库以验证其存在或实际需要
- 仅当验证后添加 – 如果无法在代码中找到证据,则不添加
- 不确定时询问用户 – 如果无法从代码库分析推断,询问用户而不是猜测
从不假设。始终验证。不确定时询问。仅基于证据的Dockerfile。
彻底性优先于速度:浅薄的分析导致损坏的Dockerfile。在生成任何内容之前:
- 阅读实际的源文件,不仅仅是文件名或目录列表
- 如果需要,使用不同查询多次搜索模式
- 通过其导入和依赖跟踪应用程序入口点
- 不要停在第一个搜索结果 – 彻底调查
- 如果分析感觉快速,您可能遗漏了什么
生成正确的Dockerfile花更长时间远优于快速但损坏的Dockerfile。提前花时间。
多架构支持
要求:确保所有Dockerfile指令支持多种架构(amd64、arm64等)。
应用于:
- 基础镜像选择:使用多架构官方镜像
- 二进制下载:动态检测架构,从不硬编码(amd64、x86_64等)
- 系统包安装:使用包管理器(自动处理架构)
- 构建命令:确保跨平台兼容性
Dockerfile必须在不同CPU架构上无需修改即可成功构建。
从不添加HEALTHCHECK
绝对禁止:在任何情况下都不添加HEALTHCHECK指令。
原因:
- 健康端点是应用程序特定的,无法从代码库分析验证
- 添加未验证的健康检查将导致容器被错误标记为不健康
- 如果应用程序有健康端点,用户会添加自己的HEALTHCHECK
如果添加HEALTHCHECK,您违反了“验证一切”原则。
最佳实践参考
这些是生成Dockerfile时考虑的最佳实践。仅在项目相关时应用它们 – 并非每个实践都适用于每种情况:
- 包管理器标志取决于使用哪个包管理器(apt-get vs apk vs 其他)
- 语言特定指导仅适用于该语言
- “验证一切”原则覆盖所有:如果实践不适合项目,跳过它
在生成期间使用此部分作为指导和验证参考。
安全
| 实践 | 描述 |
|---|---|
| 非根用户 | 创建并运行为专用用户(UID 10001+),从不运行为root |
| 固定镜像版本 | 使用特定标签如node:20-alpine,从不使用:latest |
| 官方镜像 | 优先使用Docker官方镜像或可信源的已验证发布者 |
| 镜像中无秘密 | 从不嵌入凭据、API密钥或密码在Dockerfile或ENV指令中 |
| 无sudo | 不在容器中使用sudo;当需要root访问时明确切换USER |
| 最小包 | 仅安装应用程序实际所需的包 |
| –no-install-recommends | 与apt-get一起使用此标志以防止安装可选包 |
| COPY优先于ADD | 始终使用COPY,除非专门需要ADD的tar提取功能;从不使用ADD与URLs |
| 无调试工具 | 避免在生成镜像中安装curl、wget、vim、netcat,除非应用程序需要 |
| 在同一层清理 | 在与安装相同的RUN命令中移除包管理器缓存 |
| 可执行文件由root拥有 | 应用程序二进制文件应由root拥有但由非根用户执行 |
镜像选择
| 实践 | 描述 |
|---|---|
| 最小基础镜像 | 优先使用alpine、slim、distroless或scratch,而不是完整发行版镜像 |
| 多阶段构建 | 始终将构建依赖与运行时分离;构建阶段 → 运行时阶段 |
| 匹配语言需求 | 编译语言 → distroless/scratch;解释语言 → 带运行时的slim/alpine |
| 从项目派生版本 | 从项目文件获取语言版本(package.json engines、go.mod等) |
构建优化
| 实践 | 描述 |
|---|---|
| 层缓存 | 在源代码之前复制依赖清单(package.json、go.mod) |
| 组合RUN命令 | 使用&&链相关命令以减少层并启用清理 |
| 明确COPY | 从不使用COPY . .;明确复制仅需要的文件和目录 |
| 按更改频率排序 | 首先放置稳定指令(基础镜像、依赖),最后放置易变指令(源代码) |
| 仅生产依赖 | 仅安装生产依赖,而不是devDependencies |
可维护性
| 实践 | 描述 |
|---|---|
| 排序参数 | 按字母顺序排列多行包列表,以便于维护和审查 |
| 使用WORKDIR | 始终使用WORKDIR更改目录,从不使用RUN cd |
| CMD的执行形式 | 使用JSON数组格式:CMD ["可执行文件", "arg1"]以正确处理信号 |
| 注释非明显决策 | 解释为何做出某些选择,而不是命令做什么 |
| OCI标签(可选) | 添加元数据标签用于镜像管理(org.opencontainers.image.*) |
流程
步骤0:检查现有Dockerfile
在生成任何内容之前,检查项目是否已有Dockerfile。
- 在项目根目录查找
Dockerfile(也检查变体如Dockerfile.prod) - 如果找到,阅读并存储其内容以供步骤2使用
- 类似地,检查
.dockerignore,如果存在则阅读它
这确定步骤2将生成新文件还是改进现有文件。
步骤1:分析项目结构
通过探索而非模式匹配来识别项目特征。
这些是分析目标,非查找表。以下示例是说明性的 – 对遇到的任何语言、框架或工具链应用相同的分析方法。
-
语言检测:探索项目以识别其编程语言。
- 查找依赖清单文件(例如
package.json、go.mod、requirements.txt、Cargo.toml、Gemfile、composer.json、mix.exs、build.sbt等) - 检查源文件扩展名
- 阅读清单内容以理解生态系统
- 原则:每种语言都有某种形式的依赖声明 – 找到并阅读它
- 查找依赖清单文件(例如
-
版本检测:找到所需语言/运行时版本。
- 在清单文件中搜索版本约束或引擎要求
- 查找版本文件(例如
.node-version、.python-version、.ruby-version、.tool-versions) - 检查CI配置文件,这些文件通常指定版本
- 如果项目指定版本 → 使用该确切版本
- 如果未指定版本 → 在线搜索该语言/运行时的当前LTS/稳定版本
- 原则:如果指定,使用项目所需版本,否则查找当前推荐版本 – 从不猜测
-
框架检测:从依赖和项目结构识别框架。
- 阅读清单文件中的依赖列表
- 查找框架特定配置文件
- 检查项目结构以查找框架约定
- 原则:框架留下指纹 – 配置文件、目录结构、依赖
-
应用程序类型:通过检查入口点和配置确定应用程序类型。
- Web服务器/API:查找HTTP服务器设置、路由定义、端口绑定
- CLI工具:查找参数解析、命令定义、bin条目
- Worker/后台作业:查找队列消费者、计划任务
- 静态站点:查找构建输出配置,无服务器代码
- 原则:入口点及其导入揭示应用程序目的
-
端口检测:在源代码和配置文件中搜索端口配置。
- 查找环境变量使用(例如
PORT、HTTP_PORT) - 搜索服务器初始化中的硬编码端口号
- 检查配置文件的端口设置
- 仅当找到具体证据时才添加EXPOSE
- 查找环境变量使用(例如
-
构建要求:识别项目如何构建。
- 阅读清单文件以获取构建脚本/命令
- 识别构建工具(可能是语言标准或第三方)
- 确定构建输出(编译后的二进制、转译代码、打包资产)
- 原则:每个需要构建的项目都有构建指令 – 找到它们
-
系统依赖:关键步骤 – 缺失运行时二进制会导致静默失败。
- 搜索代码库以查找执行外部命令或二进制的代码
- 常见模式:shell执行、子进程调用、exec函数、系统调用
- 对于每个找到的二进制,验证在运行时需要(不仅仅是构建时)
- 考虑应用程序实际做什么 – 需要CLI工具、数据库客户端、图像处理器吗?
- 当不确定某物是否为运行时依赖时,询问用户
-
环境变量检测:关键步骤 – 缺失环境变量导致运行时失败。
- 搜索代码库以查找环境变量访问(每种语言都有读取环境变量的方式)
- 查找
.env.example、.env.sample或类似文件,这些文件记录所需变量 - 检查配置和启动代码以查找环境变量使用
- 确定哪些变量是必需的(无默认值,应用程序失败)与可选的(有默认值)
- 对于必需变量,在Dockerfile中设置合理默认值
- 原则:如果代码读取环境变量,容器可能需要配置它
步骤2:生成或改进Dockerfile
如果无现有Dockerfile → 使用以下模式生成新的多阶段Dockerfile。
如果找到现有Dockerfile → 根据以下最佳实践和检查清单分析它,然后改进:
- 根据检查清单评估 – 检查构建器和运行时检查清单中的每个项目
- 识别问题 – 安全问题(运行为root、:latest标签)、缺失优化(无多阶段、COPY . .)、可维护性问题
- 保留有意定制 – 解释决策的注释、自定义配置、环境特定设置
- 编辑以修复问题 – 应用最佳实践,同时保持已正确的现有结构
- 解释更改 – 当呈现改进的Dockerfile时,简要说明更改了什么及原因
使用以下模式进行检查清单进行生成和验证。
以下示例显示结构模式,非复制粘贴模板。 根据项目使用的任何语言、包管理器和构建工具调整模式。
阶段1:构建器
# 构建阶段 - 为此语言使用带构建工具的镜像
FROM <语言镜像>:<版本>-<变体> AS builder
WORKDIR /app
# 模式:首先复制依赖清单以进行层缓存
# 示例:package.json、go.mod、requirements.txt、Gemfile、Cargo.toml、pom.xml
COPY <依赖清单文件> ./
# 模式:安装依赖,在同一层清理缓存
# 使用项目使用的任何包管理器
RUN <安装依赖命令> && \
<清理缓存命令>
# 模式:仅复制构建所需的源文件
# 从不使用“COPY . .” – 明确需要什么
COPY <源目录> ./
COPY <构建所需的配置文件> ./
# 模式:运行项目的构建命令
RUN <构建命令>
构建器阶段检查清单:
- [ ] 命名阶段(
AS builder) - [ ] 基础镜像适合语言(带构建工具)
- [ ] 版本从项目文件派生(非假设)
- [ ] 依赖清单在源代码之前复制
- [ ] 依赖安装并在同一RUN中清理缓存
- [ ] 仅复制所需文件(从不
COPY . .) - [ ] 构建命令匹配项目实际使用
阶段2:运行时
# 运行时阶段 - 使用适合语言的最小镜像
# 编译语言:考虑distroless、scratch或alpine
# 解释语言:使用带运行时的slim或alpine变体
FROM <最小运行时镜像>:<版本>
WORKDIR /app
# 模式:创建非根用户(语法因基础镜像而异)
# Alpine使用addgroup/adduser,Debian使用groupadd/useradd
RUN <创建组命令> && \
<创建用户命令>
# 模式:仅从构建器复制运行时工件
# 复制内容取决于语言:
# - 编译:仅二进制
# - 解释:构建输出 + 运行时依赖 + 最小配置
COPY --from=builder <构建输出> ./
COPY --from=builder <运行时依赖> ./
# 模式:设置所有权给非根用户
RUN chown -R <用户>:<组> /app
# 在暴露端口或设置CMD之前切换到非根用户
USER <非根用户>
# 仅在分析中验证端口时
EXPOSE <端口>
# 模式:使用执行形式以正确处理信号
# 命令取决于此应用程序如何运行
CMD ["<可执行文件>", "<参数>"]
运行时阶段检查清单:
- [ ] 最小基础镜像(适当时为alpine/slim/distroless/scratch)
- [ ] 创建非根用户(UID 10001+)
- [ ] 仅从构建器复制运行时工件
- [ ] 无源代码、测试、构建工具或开发依赖
- [ ] 设置适当所有权
- [ ] USER指令在CMD之前
- [ ] 仅在分析中验证端口时EXPOSE
- [ ] CMD以执行形式(JSON数组)
系统包安装模式
当需要系统包时,使用适合基础镜像的包管理器。原则始终相同:仅安装所需内容并在同一层清理缓存。
常见示例(调整为基础镜像的包管理器):
# apt-get(Debian、Ubuntu)
RUN apt-get update && \
apt-get install -y --no-install-recommends \
package1 \
package2 && \
rm -rf /var/lib/apt/lists/*
# apk(Alpine)
RUN apk add --no-cache \
package1 \
package2
# yum/dnf(RHEL、Fedora、CentOS)
RUN yum install -y \
package1 \
package2 && \
yum clean all && \
rm -rf /var/cache/yum
包安装检查清单:
- [ ] 为基础镜像使用正确的包管理器
- [ ] 使用标志跳过可选/推荐包(如可用)
- [ ] 包按字母排序以便维护
- [ ] 在同一RUN命令中清理缓存
- [ ] 仅应用程序实际所需的包
步骤3:创建或改进.dockerignore
如果无现有.dockerignore → 基于Dockerfile生成最小化版本。
如果找到现有.dockerignore → 根据Dockerfile的COPY命令审查它:
- 移除冗余排除(Dockerfile未复制的目录)
- 添加缺失的安全排除(复制的目录内的秘密)
- 保持最小化(约10-15行)
基于Dockerfile生成最小化的.dockerignore文件。
由于Dockerfile使用明确COPY命令(非COPY . .),.dockerignore目的有限:
- 安全 – 排除可能存在于被复制目录内的秘密模式
- 性能 – 排除减慢构建上下文传输的大目录
流程
- 审查Dockerfile的COPY命令 – 它复制哪些目录?
- 识别这些目录内的安全风险(可能意外存在的秘密文件)
- 识别项目中减慢上下文传输的大目录(>1MB)
- 仅排除这些项
什么不要排除
不要排除Dockerfile未复制的目录!
如果Dockerfile未复制目录,在.dockerignore中排除它是无意义的冗余。
目标大小
约10-15行最大。 如果.dockerignore超过20行,可能添加了不必要的排除。
步骤4:构建、测试和迭代
目的:在呈现给用户之前验证Dockerfile工作。Dockerfile在验证前未完成。
4.1 构建
构建镜像以验证Dockerfile语法和指令正确:
docker build -t [项目名称]-validation .
- 如果构建成功 → 继续运行
- 如果构建失败 → 分析错误,修复Dockerfile,重试
4.2 运行
启动容器以验证应用程序运行:
docker run -d --name [项目名称]-test [项目名称]-validation
sleep 5 # 允许启动时间
检查容器状态:
docker inspect --format='{{.State.Status}}' [项目名称]-test
docker inspect --format='{{.State.ExitCode}}' [项目名称]-test
预期行为取决于应用程序类型(在步骤1中确定):
- 服务(Web服务器、API、Worker):容器应仍在运行
- CLI工具 / 单次命令:容器应退出并代码0
如果容器崩溃或意外退出 → 继续日志分析以理解原因。
4.3 日志分析
捕获并分析容器日志:
docker logs [项目名称]-test 2>&1
使用步骤1中对项目的了解分析日志。 您知道:
- 这是什么语言和框架
- 应用程序应该做什么
- 它需要什么依赖
- 此类应用程序成功启动的样子
使用此上下文确定日志是否指示:
- 应用程序正确启动,或
- 有问题(错误、崩溃、缺失依赖、权限问题等)
如果日志指示问题 → 识别根本原因,修复Dockerfile或.dockerignore,重试。
4.4 链接(如可用)
如果安装了hadolint,运行它以捕获Dockerfile最佳实践问题:
hadolint Dockerfile
- 如果未安装hadolint → 跳过此检查
- 如果hadolint报告问题 → 评估每个问题,适当修复,重试
- 某些hadolint警告可能是有意的(基于项目上下文使用判断)
4.5 安全扫描(如可用)
如果安装了trivy,扫描构建的镜像以查找漏洞:
trivy image --severity HIGH,CRITICAL [项目名称]-validation
- 如果未安装trivy → 跳过此检查
- 如果trivy报告基础镜像中的HIGH/CRITICAL漏洞 → 考虑不同基础镜像版本或变体是否会有帮助
- 如果漏洞在应用程序依赖中 → 为用户记下但不要阻止(依赖更新超出Dockerfile范围)
4.6 迭代
如果任何验证步骤失败:
- 分析 特定错误消息或行为
- 识别根本原因 – 常见问题包括:
- 缺失文件 → 不正确的COPY命令或过度攻击的.dockerignore
- 缺失依赖 → 未安装系统包
- 权限被拒绝 → 所有权或USER指令问题
- 模块未找到 → 构建步骤不完整或错误文件复制
- Hadolint警告 → Dockerfile最佳实践问题
- 修复 适当文件(Dockerfile或.dockerignore)
- 重试 从步骤4.1
最多5次迭代。 如果5次尝试后仍失败:
- 停止并向用户呈现当前状态
- 解释什么失败以及尝试了什么修复
- 请求指导
4.7 清理
始终在验证后清理,无论成功与否:
docker stop [项目名称]-test 2>/dev/null || true
docker rm [项目名称]-test 2>/dev/null || true
docker rmi [项目名称]-validation 2>/dev/null || true
仅在以下情况下继续向用户呈现Dockerfile:
- 所有验证步骤通过,和
- 清理完成
输出格式
对于新Dockerfile(无现有文件)
向用户呈现两个文件:
- Dockerfile 带有清晰注释解释每个部分
- .dockerignore 带有组织部分
生成后提供:
- 简要解释设计选择(基础镜像、构建阶段、安全措施)
- 构建命令:
docker build -t [项目名称] . - 运行命令:
docker run -p [端口]:[端口] [项目名称] - 镜像大小预期
对于改进的Dockerfile(找到现有文件)
呈现改进的文件并附带更改摘要:
- Dockerfile – 改进版本
- 更改内容 – 简要列表说明更改了什么及原因:
- 安全修复(例如,“添加非根用户 – 之前运行为root”)
- 优化改进(例如,“添加多阶段构建以减少镜像大小”)
- 最佳实践更新(例如,“更改CMD为执行形式以进行信号处理”)
- 保留内容 – 注意保留的有意定制
- .dockerignore – 如需要更改的改进版本
对于两种情况
推荐后续步骤(Dockerfile已验证):
- 集成到CI/CD管道
- 提交到版本控制
成功标准
Dockerfile检查清单
- [ ] 成功构建无错误
- [ ] 使用多阶段构建(构建器 → 运行时)
- [ ] 运行为非根用户(UID 10001+)
- [ ] 使用固定版本标签(无
:latest) - [ ] 使用最小基础镜像(alpine/slim/distroless)
- [ ] 在源代码之前复制依赖清单(层缓存)
- [ ] 使用明确COPY(无
COPY . .) - [ ] 使用
&&组合RUN命令 - [ ] 在同一层清理包管理器缓存
- [ ] 使用
--no-install-recommends(如果使用apt-get) - [ ] 使用执行形式用于CMD(
["可执行文件", "参数"]) - [ ] 无调试工具除非需要
- [ ] 无秘密或凭据嵌入
.dockerignore检查清单
- [ ] 最小大小(约10-15行)
- [ ] 排除复制目录内的秘密
- [ ] 排除大不必要的目录
- [ ] 不排除Dockerfile未复制的目录
验证检查清单(步骤4)
- [ ] 镜像构建成功
- [ ] 容器启动无崩溃
- [ ] 日志显示无指示应用程序失败的错误
- [ ] Hadolint通过(如安装)
- [ ] Trivy显示无关键基础镜像漏洞(如安装)
- [ ] 测试容器和镜像已清理
在所有验证检查通过前,不向用户呈现Dockerfile。
示例工作流
新Dockerfile(无现有文件)
- 检查:“未找到现有Dockerfile。将生成新的。”
- 探索:“让我查找依赖清单… 找到
<清单文件>。阅读它以理解生态系统。” - 识别:“这是一个
<语言>项目使用<框架/工具>。清单指示版本<X>。” - 跟踪:“入口点是
<文件>。通过导入跟踪以理解运行时需求。” - 结构:“多阶段构建:构建器阶段需要
<构建工具>,运行时阶段仅需要<运行时工件>。” - 依赖:“搜索外部二进制使用… 找到
<二进制>。这需要在运行时镜像中。” - 生成:“创建Dockerfile和.dockerignore。根据最佳实践检查清单检查。”
- 构建与测试:“构建镜像… 运行容器… 检查日志…”
- 迭代(如需要):“构建失败由于缺失包。添加到Dockerfile并重试…”
- 清理与呈现:“验证通过。移除测试工件。这是您的Dockerfile。”
改进现有Dockerfile
- 检查:“找到现有Dockerfile。阅读它以分析…”
- 分析项目:与上述相同的探索以理解Dockerfile应该做什么。
- 评估:“检查现有Dockerfile与最佳实践检查清单…”
- “❌ 运行为root – 无USER指令”
- “❌ 使用:latest标签而非固定版本”
- “✅ 多阶段构建已就位”
- “✅ 依赖清单首先复制”
- 保留:“保持自定义ENV变量和特定端口配置 – 这些似乎是有意的。”
- 改进:“添加非根用户,固定镜像版本以匹配项目需求。”
- 构建与测试:“构建改进的镜像… 运行容器… 检查日志…”
- 迭代(如需要):“容器崩溃 – 日志显示权限错误。修复所有权并重试…”
- 清理与呈现:“验证通过。移除测试工件。以下是改进内容。”
关键心态:调查实际项目而非匹配模板。每个项目独特。在验证前不呈现。