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 -v ./html:/usr/share/nginx/html 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), 캐파빌리티 (--cap-add, --cap-drop), 프리빌리지 모드 (--privileged), 디바이스 (--device), 로깅 (--log-driver, --log-opt), 리소스 제한 (--memory, --cpus), 헬스 체크 (--health-cmd, --health-interval, --health-timeout, --health-retries), DNS (--dns), 추가 호스트 (--add-host), tmpfs (--tmpfs) 등이 포함됩니다. 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 명령어를 붙여넣을 수 있나요?

네, 변환기는 셸 스크립트, 문서, README 파일에서 복사할 때 흔한 백슬래시 (\) 줄 연속이 포함된 여러 줄 명령어를 완전히 지원합니다. 도구가 변환 전에 이를 자동으로 단일 명령어로 정규화합니다. 여러 줄 명령어를 그대로 붙여넣기만 하면 됩니다.

변환된 출력이 예상과 다르게 보이는 이유는 무엇인가요?

일반적인 이유: (1) 플래그 순서 — Compose YAML은 명령어의 플래그 순서와 다를 수 있는 표준 키 순서가 있습니다. (2) 암묵적 기본값 — -d (분리 모드) 같은 플래그는 Compose에서 기본값이므로 명시적으로 나타나지 않을 수 있습니다. (3) 버전 차이 — 리소스 제한 (--memory, --cpus)은 v2와 v3 Compose 형식에서 다르게 표현됩니다. (4) 복잡한 플래그 — --mount처럼 여러 하위 옵션이 있는 플래그는 긴 형식의 볼륨 구문으로 확장될 수 있습니다. (5) 미지원 플래그 — --rm 같은 일부 Docker run 플래그는 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));

관련 도구