关于 generate-dockerfile
面向开发者的容器化技能,通过分析项目生成或改进生产用 Dockerfile 和 .dockerignore。它要求依据代码库验证配置,并关注多阶段构建、安全与多架构支持。
主要功能
- 01基于代码库分析项目
- 02生成或改进 Dockerfile
- 03创建优化的 .dockerignore
- 04多阶段构建指导
- 05安全与多架构检查
适用场景
- 01为生产部署构建应用容器
- 02检查现有 Dockerfile 的安全性与构建实践
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ vtmocanu/ skills --skill generate-dockerfile安装后在 Claude Code 中输入/generate-dockerfile确认已加载。
详细介绍
移植自 vfarcic/dot-ai
shared-prompts/generate-dockerfile.md(MIT,版权所有 (c) 2025 Viktor Farcic)。原作者:Viktor Farcic。
生成可用于生产环境的 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、apk 或其他)
- 特定语言的指导仅适用于对应语言
- “核实所有内容”原则优先于一切:如果某项实践不适合项目,就跳过
生成时可将本节作为指导,并在验证时作为参考。
安全性
| 实践 | 说明 |
|---|---|
| 非 root 用户 | 创建专用用户(UID 10001+)并以该用户运行,绝不以 root 身份运行 |
| 固定镜像版本 | 使用具体标签,例如 node:20-alpine,绝不使用 :latest |
| 官方镜像 | 优先选择来自可信来源的 Docker Official Images 或 Verified Publishers |
| 镜像中不包含密钥 | 绝不在 Dockerfile 或 ENV 指令中嵌入凭据、API 密钥或密码 |
| 不使用 sudo | 不要在容器中使用 sudo;需要 root 权限时,显式切换 USER |
| 最少安装软件包 | 仅安装应用实际需要的软件包 |
| --no-install-recommends | 使用此标志搭配 apt-get,避免安装可选软件包 |
| 优先使用 COPY 而非 ADD | 除非确实需要 ADD 的 tar 解压功能,否则始终使用 COPY;绝不将 ADD 用于 URL |
| 不安装调试工具 | 除非应用需要,否则避免在生产镜像中安装 curl、wget、vim、netcat |
| 在同一层中清理 | 在安装软件包的同一条 RUN 命令中清除软件包管理器缓存 |
| 可执行文件归 root 所有 | 应用二进制文件应归 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 使用 exec 格式 | 使用 JSON 数组格式:CMD ["executable", "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 → 根据以下最佳实践和检查清单进行分析,然后改进:
- 对照检查清单评估 - 检查 Builder 和 Runtime 检查清单中的每一项
- 找出问题 - 安全问题(以 root 身份运行、
:latest标签)、缺少优化(未使用多阶段构建、COPY . .)、可维护性问题 - 保留有意的自定义设置 - 解释决策的注释、自定义配置、特定环境的设置
- 编辑以修复问题 - 应用最佳实践,同时保留已有的正确结构
- 说明更改 - 展示改进后的 Dockerfile 时,简要说明更改内容及原因
生成和验证时都使用以下模式和检查清单。
以下示例展示的是结构模式,而非可直接复制粘贴的模板。 根据项目使用的语言、包管理器和构建工具调整模式。
阶段 1:Builder
# Build stage - use an image with build tools for this language
FROM <language-image>:<version>-<variant> AS builder
WORKDIR /app
# PATTERN: Copy dependency manifests FIRST for layer caching
# Examples: package.json, go.mod, requirements.txt, Gemfile, Cargo.toml, pom.xml
COPY <dependency-manifest-files> ./
# PATTERN: Install dependencies, clean cache in same layer
# Use whatever package manager the project uses
RUN <install-dependencies-command> && \
<clean-cache-command>
# PATTERN: Copy only the source files needed for build
# Never use "COPY . ." - be explicit about what's needed
COPY <source-directories> ./
COPY <config-files-needed-for-build> ./
# PATTERN: Run the project's build command
RUN <build-command>
Builder 阶段检查清单:
- 命名阶段(
AS builder) - 基础镜像适用于该语言(包含构建工具)
- 版本根据项目文件确定(而非假设)
- 在源代码之前复制依赖清单
- 在同一个 RUN 中安装依赖并清理缓存
- 只复制必需文件(绝不使用
COPY . .) - 构建命令与项目实际使用的命令一致
阶段 2:Runtime
# Runtime stage - use minimal image appropriate for the language
# Compiled languages: consider distroless, scratch, or alpine
# Interpreted languages: use slim or alpine variant with runtime only
FROM <minimal-runtime-image>:<version>
WORKDIR /app
# PATTERN: Create non-root user (syntax varies by base image)
# Alpine uses addgroup/adduser, Debian uses groupadd/useradd
RUN <create-group-command> && \
<create-user-command>
# PATTERN: Copy ONLY runtime artifacts from builder
# What you copy depends on the language:
# - Compiled: just the binary
# - Interpreted: built output + runtime dependencies + minimal config
COPY --from=builder <build-outputs> ./
COPY --from=builder <runtime-dependencies> ./
# PATTERN: Set ownership to non-root user
RUN chown -R <user>:<group> /app
# Switch to non-root user BEFORE exposing ports or setting CMD
USER <non-root-user>
# Only if port was verified during analysis
EXPOSE <port>
# PATTERN: Use exec form for proper signal handling
# The command depends on how this application runs
CMD ["<executable>", "<args>"]
Runtime 阶段检查清单:
- 最小化基础镜像(根据需要选择 alpine/slim/distroless/scratch)
- 创建非 root 用户(UID 10001+)
- 只从 builder 复制运行时所需的产物
- 不包含源代码、测试、构建工具或开发依赖
- 设置正确的所有权
- 在 CMD 之前设置 USER 指令
- 仅在分析中确认过端口时才使用 EXPOSE
- CMD 使用 exec 格式(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 [project-name]-validation .
- 如果构建成功 → 继续运行
- 如果构建失败 → 分析错误,修复 Dockerfile,然后重试
4.2 运行
启动容器,以验证应用是否运行:
docker run -d --name [project-name]-test [project-name]-validation
sleep 5 # Allow startup time
检查容器状态:
docker inspect --format='{{.State.Status}}' [project-name]-test
docker inspect --format='{{.State.ExitCode}}' [project-name]-test
预期行为取决于应用类型(在第 1 步中确定):
- 服务(Web 服务器、API、工作进程):容器应继续运行
- CLI 工具 / 一次性命令:容器应已退出,退出代码为 0
如果容器崩溃或意外退出 → 继续分析日志,了解原因。
4.3 日志分析
收集并分析容器日志:
docker logs [project-name]-test 2>&1
根据你在第 1 步中了解的项目情况分析日志。 你知道:
- 项目使用的语言和框架
- 应用应执行的任务
- 应用所需的依赖项
- 这类应用成功启动时的表现
根据这些背景判断日志是否表明:
- 应用已正确启动,或者
- 存在问题(错误、崩溃、缺少依赖项、权限问题等)
如果日志表明存在问题 → 找出根本原因,修复 Dockerfile 或 .dockerignore,然后重试。
4.4 Lint 检查(如可用)
如果已安装 hadolint,运行它以发现 Dockerfile 最佳实践方面的问题:
hadolint Dockerfile
- 如果未安装 hadolint → 跳过此检查
- 如果 hadolint 报告问题 → 评估每个问题,酌情修复,然后重试
- 某些 hadolint 警告可能是有意为之(请结合项目背景自行判断)
4.5 安全扫描(如可用)
如果已安装 trivy,扫描已构建的镜像以查找漏洞:
trivy image --severity HIGH,CRITICAL [project-name]-validation
- 如果未安装 trivy → 跳过此检查
- 如果 trivy 在基础镜像中报告 HIGH/CRITICAL 漏洞 → 考虑更换基础镜像版本或变体是否有帮助
- 如果漏洞存在于应用依赖项中 → 告知用户,但不要因此阻止交付(更新依赖项不属于 Dockerfile 的范围)
4.6 迭代
如果任何验证步骤失败:
- 分析具体的错误消息或行为
- 找出根本原因——常见问题包括:
- 缺少文件 → COPY 命令不正确,或 .dockerignore 规则过于宽泛
- 缺少依赖项 → 未安装系统软件包
- 权限被拒绝 → 所有权或 USER 指令有问题
- 找不到模块 → 构建步骤未完成,或复制了错误的文件
- Hadolint 警告 → Dockerfile 最佳实践方面的问题
- 修复相应文件(Dockerfile 或 .dockerignore)
- 从第 4.1 步开始重试
最多迭代 5 次。 如果尝试 5 次后仍然失败:
- 停止并向用户展示当前状态
- 说明失败之处以及已尝试的修复方法
- 请求用户指导
4.7 清理
验证后务必清理,无论验证是否成功:
docker stop [project-name]-test 2>/dev/null || true
docker rm [project-name]-test 2>/dev/null || true
docker rmi [project-name]-validation 2>/dev/null || true
只有在以下条件满足后,才能继续向用户展示 Dockerfile:
- 所有验证步骤均已通过,并且
- 已完成清理
输出格式
对于新 Dockerfile(不存在现有文件)
向用户提供这两个文件:
- Dockerfile,包含清晰的注释,说明各个部分
- .dockerignore,包含分类清晰的部分
生成后,请提供:
- 对设计选择的简要说明(基础镜像、构建阶段、安全措施)
- 构建命令:
docker build -t [project-name] . - 运行命令:
docker run -p [port]:[port] [project-name] - 预期镜像大小
改进 Dockerfile(已找到现有文件)
展示改进后的文件,并附上更改摘要:
- Dockerfile - 改进后的版本
- 所做更改 - 简要列出更改内容及原因:
- 安全修复(例如:“添加非 root 用户——原先以 root 身份运行”)
- 优化改进(例如:“添加多阶段构建以减小镜像体积”)
- 最佳实践更新(例如:“将 CMD 改为 exec 形式,以便处理信号”)
- 保留内容 - 说明保留的自定义内容
- .dockerignore - 如需更改,则提供改进后的版本
两种情况均适用
建议的后续步骤(Dockerfile 已通过验证):
- 集成到 CI/CD 流水线
- 提交到版本控制系统
成功标准
Dockerfile 检查清单
- 构建成功且无错误
- 使用多阶段构建(builder → runtime)
- 以非 root 用户运行(UID 10001+)
- 使用固定版本标签(不使用
:latest) - 使用精简基础镜像(alpine/slim/distroless)
- 在复制源代码前先复制依赖清单(利用层缓存)
- 使用显式 COPY(不使用
COPY . .) - 使用
&&合并 RUN 命令 - 在同一层清理软件包管理器缓存
- 使用
--no-install-recommends(如果使用 apt-get) - 使用 exec 形式的 CMD(
["executable", "arg"]) - 除非确有需要,否则不包含调试工具
- 不嵌入密钥或凭据
.dockerignore 检查清单
- 文件大小精简(约 10-15 行)
- 排除已复制目录中的密钥
- 排除体积较大的非必需目录
- 不排除 Dockerfile 未复制的目录
验证检查清单(步骤 4)
- 镜像构建成功
- 容器启动后未崩溃
- 日志中没有表明应用程序故障的错误
- Hadolint 检查通过(如果已安装)
- Trivy 未发现基础镜像中的严重漏洞(如果已安装)
- 测试容器和镜像已清理
在所有验证检查均通过之前,不要向用户展示 Dockerfile。
工作流示例
新建 Dockerfile(不存在现有文件)
- 检查:“未找到现有 Dockerfile。将生成一个新文件。”
- 探索:“让我查找依赖清单……找到了
<manifest-file>。正在读取以了解所用技术生态。” - 识别:“这是一个使用
<framework/tool>的<language>项目。依赖清单表明版本为<X>。” - 追踪:“入口点是
<file>。正在追踪导入项,以了解运行时需求。” - 规划结构:“多阶段构建:builder 阶段需要
<build-tools>,runtime 阶段只需要<runtime-artifacts>。” - 依赖项:“正在搜索外部二进制文件的使用情况……找到了
<binary>。需要将其加入 runtime 镜像。” - 生成:“创建 Dockerfile 和 .dockerignore。根据最佳实践检查清单进行检查。”
- 构建与测试:“正在构建镜像……正在运行容器……正在检查日志……”
- 迭代(如有需要):“构建因缺少软件包而失败。正在将其添加到 Dockerfile 并重试……”
- 清理并展示:“验证通过。正在移除测试产物。这是你的 Dockerfile。”
改进现有 Dockerfile
- 检查:“发现现有 Dockerfile。正在读取并分析……”
- 分析项目:与上文相同,探索项目以了解 Dockerfile 应实现的功能。
- 评估:“正在根据最佳实践清单检查现有 Dockerfile……”
- “❌ 以 root 身份运行——没有 USER 指令”
- “❌ 使用 :latest 标签,而不是固定版本”
- “✅ 已采用多阶段构建”
- “✅ 优先复制了依赖清单”
- 保留:“保留自定义 ENV 变量和特定端口配置——这些设置看起来是有意为之。”
- 改进:“添加非 root 用户,并固定映像版本以匹配项目要求。”
- 构建并测试:“正在构建改进后的映像……运行容器……检查日志……”
- 迭代(如有需要):“容器崩溃——日志显示权限错误。正在修复所有权并重试……”
- 清理并展示:“验证通过。正在移除测试产物。以下是改进内容。”
核心思路:调查实际项目,而不是套用模板。每个项目都各不相同。验证通过后再展示。