Oh MyUtils

Docker Run to Compose - Docker命令转换为Compose YAML 在线工具

将docker run命令转换为docker-compose.yml格式。即时解析端口、卷、环境变量和30多个Docker标志 — 100%客户端处理,您的数据永远不会离开浏览器。

常见问题

什么是Docker Run to Compose转换器?

Docker Run to Compose转换器是一个将docker run CLI命令转换为docker-compose.yml(Docker Compose)YAML配置文件的工具。当您使用docker run -d --name nginx -p 80:80 nginx:latest这样的命令运行容器时,转换器会生成包含services、ports、volumes等指令的等效Compose YAML。这很有用,因为Compose文件可以进行版本控制、可重现,并且可以管理多容器应用程序。

如何使用这个Docker Run to Compose转换器?

将您的docker run命令粘贴到输入区域(可以包含或不包含docker run前缀)。转换会在您输入时自动实时进行。在输出区域查看生成的docker-compose.yml YAML。根据需要调整选项:选择Compose版本(Latest、v3.x或v2.x)和缩进(2或4个空格)。点击复制按钮将YAML复制到剪贴板,或点击下载将其保存为docker-compose.yml文件。

我的数据安全吗?会发送到服务器吗?

您的数据100%安全,永远不会离开您的浏览器。所有解析和转换完全在您设备上运行的客户端JavaScript中进行。没有Docker命令、环境变量、密码、API密钥或任何其他数据会被传输到任何服务器。您可以通过断开互联网来验证——初始页面加载后,该工具完全离线工作。

支持哪些docker run标志?

此转换器支持30多个常用的Docker run标志,包括:端口映射(-p)、卷(-v、--mount)、环境变量(-e、--env-file)、容器名称(--name)、重启策略(--restart)、网络(--network)、工作目录(-w)、用户(-u)、主机名(-h)、入口点(--entrypoint)、标签(-l)、Linux能力(--cap-add、--cap-drop)、特权模式(--privileged)、设备(--device)、日志(--log-driver、--log-opt)、资源限制(--memory、--cpus)、健康检查等。没有直接Compose对应的标志(如--rm)会显示警告。

Docker Compose版本(v2、v3、latest)有什么区别?

Docker Compose经历了多个规范版本的演进。版本2.x引入了services、networks和volumes作为顶级键,使用mem_limit等键进行资源约束。版本3.x为Docker Swarm兼容性而设计,将资源限制移至deploy.resources下。Latest(Common Specification)是省略version字段并使用最新语法的现代格式。对于大多数新项目,请使用Latest。

可以粘贴带反斜杠的多行docker run命令吗?

可以,转换器完全支持带反斜杠(\)换行的多行命令,这在从shell脚本、文档或README文件复制命令时很常见。工具会在转换前自动将其规范化为单个命令。只需原样粘贴多行命令即可。

为什么转换后的输出与预期不同?

常见原因包括:(1) 标志顺序——Compose YAML有标准的键顺序,可能与命令中的标志顺序不同。(2) 隐式默认值——-d(分离模式)等标志在Compose中是默认的,可能不会显式出现。(3) 版本差异——资源限制(--memory、--cpus)在v2和v3 Compose格式中表示方式不同。(4) 不支持的标志——--rm等标志在Compose中没有直接等效项,会以警告形式显示。

代码示例

// Docker Run to Docker Compose converter
// Parses docker run command and generates docker-compose.yml YAML

function parseDockerRun(command) {
  const normalized = command.replace(/\\\s*\n/g, ' ').trim();
  const stripped = normalized.replace(/^docker\s+run\s+/, '');
  const tokens = tokenize(stripped);

  const options = {
    image: '', name: '', ports: [], volumes: [],
    envVars: [], network: '', restart: '',
    workdir: '', hostname: '', command: [],
    detach: false, privileged: false,
  };

  let i = 0;
  while (i < tokens.length) {
    const token = tokens[i];
    if (token === '-p' || token === '--publish') {
      const val = tokens[++i];
      const parts = val.split(':');
      options.ports.push({ host: parts[0], container: parts.slice(1).join(':') });
    } else if (token === '-v' || token === '--volume') {
      const val = tokens[++i];
      const parts = val.split(':');
      options.volumes.push({ host: parts[0], container: parts[1], mode: parts[2] || '' });
    } else if (token === '-e' || token === '--env') {
      const val = tokens[++i];
      const eq = val.indexOf('=');
      options.envVars.push({ key: val.substring(0, eq), value: val.substring(eq + 1) });
    } else if (token === '--name') { options.name = tokens[++i]; }
    else if (token === '--restart') { options.restart = tokens[++i]; }
    else if (token === '--network') { options.network = tokens[++i]; }
    else if (token === '-d') { options.detach = true; }
    else if (!token.startsWith('-')) {
      options.image = token;
      options.command = tokens.slice(i + 1);
      break;
    }
    i++;
  }
  return options;
}

function tokenize(input) {
  const tokens = [];
  let current = '', inSQ = false, inDQ = false;
  for (const char of input) {
    if (char === "'" && !inDQ) { inSQ = !inSQ; }
    else if (char === '"' && !inSQ) { inDQ = !inDQ; }
    else if (char === ' ' && !inSQ && !inDQ) {
      if (current) { tokens.push(current); current = ''; }
    } else { current += char; }
  }
  if (current) tokens.push(current);
  return tokens;
}

function generateComposeYaml(options, indent = 2) {
  const pad = ' '.repeat(indent);
  const p2 = pad.repeat(2), p3 = pad.repeat(3);
  const name = options.name || options.image.split('/').pop().split(':')[0] || 'app';
  let yaml = `services:\n${pad}${name}:\n${p2}image: ${options.image}\n`;
  if (options.name) yaml += `${p2}container_name: ${options.name}\n`;
  if (options.restart) yaml += `${p2}restart: ${options.restart}\n`;
  if (options.ports.length > 0) {
    yaml += `${p2}ports:\n`;
    options.ports.forEach(p => yaml += `${p3}- "${p.host}:${p.container}"\n`);
  }
  if (options.volumes.length > 0) {
    yaml += `${p2}volumes:\n`;
    options.volumes.forEach(v => {
      const mode = v.mode ? `:${v.mode}` : '';
      yaml += `${p3}- ${v.host}:${v.container}${mode}\n`;
    });
  }
  if (options.envVars.length > 0) {
    yaml += `${p2}environment:\n`;
    options.envVars.forEach(e => yaml += `${p3}- ${e.key}=${e.value}\n`);
  }
  if (options.network) {
    yaml += `${p2}networks:\n${p3}- ${options.network}\n`;
  }
  return yaml;
}

// Example usage
const cmd = 'docker run -d --name nginx -p 80:80 -v ./html:/usr/share/nginx/html nginx:latest';
const opts = parseDockerRun(cmd);
console.log(generateComposeYaml(opts));

相关工具