首页
/ docker compose alpha generate 详解:从已有容器反向生成 Compose 文件的实验性命令

docker compose alpha generate 详解:从已有容器反向生成 Compose 文件的实验性命令

2026-09-05 22:36:58作者:裘旻烁

docker compose alpha generate 是 docker/compose 提供的一个实验性(EXPERIMENTAL)子命令,它的核心能力是"逆向工程":把一台主机上已经存在的 Docker 容器(包括已停止的)作为输入,反向生成一份标准 Compose 项目模型,并以 YAML 或 JSON 形式打印到标准输出。读完本文,你将理解该命令的完整参数、它对容器配置的实际提取范围(镜像、环境变量、健康检查、端口、卷、网络、secrets)、服务命名与副本(Scale)推断规则,以及它在"遗留应用迁移到 Compose"这一场景下的落地方式。

命令定位与实验性状态

generate 并不挂在 docker compose 的主命令树顶层,而是注册在隐藏的 alpha 实验性命令组下。从 alpha 命令组定义 可以看到,alphaCommand 被标记为 experimentalCLIHidden: true,其下仅挂接了 vizpublishgenerate 三个子命令:

cmd.AddCommand(
    vizCommand(p, dockerCli, backendOptions),
    publishCommand(p, dockerCli, backendOptions),
    generateCommand(p, dockerCli, backendOptions),
)

因此调用入口是 docker compose alpha generate [OPTIONS] [CONTAINERS...]。由于属于 alpha 命令,命令入口 在执行时会先向 stderr 打印一条提示:

generate command is EXPERIMENTAL

这意味着它的接口与行为可能随版本变化,生产流水线中建议先验证输出再使用。

基本用法

命令要求至少指定一个容器作为输入参数(可以是容器名,也可以是容器 ID):

# 为单个容器生成 Compose 文件
docker compose alpha generate my-app

# 同时纳入多个容器
docker compose alpha generate my-app redis

# 指定项目名并输出 JSON
docker compose alpha generate --name my-stack --format json my-app redis

runGenerate 的入口逻辑很直接:容器列表为空时直接返回 at least one container must be specified 错误;否则构建 Compose 后端服务,调用 backend.Generate,最后根据 --format 将得到的项目模型序列化为 project.MarshalJSON()project.MarshalYAML() 并打印到 stdout。输出只走标准输出,因此可以方便地重定向或管道:

docker compose alpha generate my-app > compose.generated.yaml

完整参数说明

根据 官方参考文档cobra 命令定义,该命令的完整参数如下:

参数 类型 默认值 说明
--dry-run bool false 以 dry run 模式执行命令(全局持久标志,定义于 compose.go
--format string yaml 输出格式,取值 yamljson;其他取值会在 格式分支 中触发 unsupported format %q 错误
--name string 写入生成 Compose 文件的项目名(name 字段)
--project-dir string 项目使用的目录(Compose 全局项目选项之一)

其中 --dry-run--project-dirdocker compose 根命令上声明的持久标志,被 alpha generate 继承;--name--format 则是 generateOptions 自己声明的专属参数。--name 最终会进入 GenerateOptionsProjectName 字段:

type GenerateOptions struct {
    // ProjectName to set in the Compose file
    ProjectName string
    // Containers passed in the command line to be used as reference for service definition
    Containers []string
}

实现原理:容器如何变成 Compose 模型

整个"反向生成"逻辑集中在 pkg/compose/generate.go,可以分为容器收集、服务归并、配置抽取三个阶段。

1. 容器收集:按名称与 ID 双重过滤

Generate 会发起两次 ContainerList 请求:

  • 第一次按 name 过滤,匹配用户传入的容器名;
  • 第二次按 id 过滤,允许用户直接传入容器 ID(或 ID 前缀)。

两次请求都设置了 All: true,即已停止的容器同样可以参与生成。两次结果按 ID 去重合并后,如果最终一个容器都没找到,则报错 no container(s) found with the following name(s): ...

2. 服务命名与副本数推断

createProjectFromContainers 决定每个容器映射到哪个 Compose 服务:

  • 如果容器带有 com.docker.compose.service 标签(ServiceLabel),说明它本身就是由某个 Compose 应用创建的,此时直接复用该历史服务名,实现"同一 Compose 应用多次部署后仍能还原出一致的服务定义";
  • 否则回退到 getCanonicalContainerName 对容器名做规范化,作为新服务名;
  • 凡是归到同一服务名的容器,其 Scale 计数都会累加(service.Scale = increment(service.Scale))。换句话说,如果你手工起了 3 个同名前缀的副本容器,生成的 Compose 文件会自动体现 scale: 3 的语义。

每个服务初始只写入 NameImage(容器当前镜像引用)和 Labels,其余字段在随后的 inspect 阶段补充。

3. 配置抽取:实际能恢复哪些字段

对每个容器执行 ContainerInspect 后,extractComposeConfiguration 负责把引擎侧的容器配置翻译成 Compose 模型:

Compose 字段 数据来源 说明
environment inspect.Config.Env 通过 types.NewMappingWithEquals 转换为 key/value 映射
healthcheck inspect.Config.Healthcheck 逐项映射 testtimeoutintervalstart_periodstart_intervalretries(见 toComposeHealthCheck),零值项会被省略
ports inspect.HostConfig.PortBindings 生成 target/published/protocol/host_ip 形式的短语法端口映射
volumes inspect.Mounts 区分命名卷与 bind mount,详见下文
networks inspect.NetworkSettings.Networks 记录网络别名(aliases),并 inspect 网络本身以恢复 internal 标记

卷与 secrets 的识别toComposeVolumes 完成,规则值得注意:

  • mount.TypeVolume(命名卷):写入顶层 volumes 定义;只有当驱动不是 local 时才显式声明 driverlocal 驱动保持模型简洁;
  • mount.TypeBind(bind mount):直接映射为 Compose 的 bind 挂载;
  • 特殊场景:如果 bind 挂载的目标路径以 /run/secrets 开头,会被识别为 Compose secret——secret 名取目标路径的最后一个元素,file 字段取挂载源路径(并剥掉 /host_mnt 前缀),从而把通过 docker secret 机制注入的文件恢复为 secrets 声明。

网络toComposeNetwork 处理:对容器连接的每个网络调用 NetworkInspect,若能 inspect 成功则记录 internal: true 标记;同时把容器在该网络上的 aliases 写入服务的 networks.<name>.aliases

标签清洗:生成的服务标签会经过 cleanDockerPreviousLabels 过滤,剔除所有 com.docker.compose. 前缀和 desktop.docker.io 前缀的标签。这样做的目的是避免把旧 Compose 部署留下的状态标签(旧服务名、项目名等)重新写回新生成的 Compose 文件,造成二次部署时的命名冲突。

生成结果的形态

综合以上逻辑,输出是一个完整的 Compose 项目模型(types.Project),包含 name(当指定 --name 时)、servicesvolumesnetworkssecrets 四大块。以同时生成一个带命名卷、端口映射的 web 容器和一个带网络别名的 app 容器为例,yaml 输出在结构上类似:

name: my-stack
services:
  web:
    image: <容器当前镜像>
    ports:
      - target: 80
        published: "8080"
        protocol: tcp
    volumes:
      - type: volume
        source: web_data
        target: /data
  app:
    image: <容器当前镜像>
    networks:
      backend:
        aliases:
          - app-1
networks:
  backend:
volumes:
  web_data:

(以上为基于源码抽取逻辑整理的输出结构示例,实际字段以真实容器状态为准。)切换到 --format json 时,同样的模型会以 JSON 形式打印,便于程序化处理。

适用场景与能力边界

典型场景:接管遗留系统。当一台服务器上存在一批"只有容器、没有 Compose 文件"的历史应用时(早期用裸 docker run 部署、或旧 Compose 文件已丢失),alpha generate 可以快速产出一份可审查、可版本化的基线,再人工补全 builddepends_on、启动命令等字段后,即可用 docker compose up 重新纳管。

需要留意的边界(从源码结构看):

  • extractComposeConfiguration 只覆盖环境变量、健康检查、挂载、网络、端口绑定这几类字段,不会恢复容器的启动命令、entrypoint、depends_on、重启策略、资源限制等未列入抽取范围的配置,生成结果应视为"骨架"而非"完整还原";
  • 镜像字段取自容器当前的 Image 引用,若容器由本地构建镜像启动且标签为 <none> 或未打 tag,生成文件中会出现无法拉取的引用,需要人工改为 build 定义;
  • 输入容器不存在时命令会快速失败,且必须显式给出容器列表,不支持"导出全部容器";
  • 命令输出到 stdout 且带有 EXPERIMENTAL 提示写到 stderr,脚本化使用时注意分离两个流。

小结

docker compose alpha generate 以极小的参数面(--name--format,外加继承的 --dry-run--project-dir)提供了一条从"容器实例"回到"声明式配置"的逆向通道:名称/ID 双路收集容器、按 Compose 服务标签或规范名归并服务并推断副本数、逐项抽取镜像/环境/健康检查/端口/卷/网络/secrets、清洗历史标签后输出 YAML 或 JSON。理解 cmd/compose/generate.go 的命令层与 pkg/compose/generate.go 的实现层这两处源码,就能准确判断它在你的迁移流程中"能给出什么、缺什么需要人工补",是遗留 Docker 应用 Compose 化改造的实用起点。

登录后查看全文
热门项目推荐
相关项目推荐