빌드노트
Compose override에서 포트를 바꿨는데 기존 포트도 남는다
개발용 Compose 파일에서 호스트 포트를 바꿔도 기존 포트가 남는 이유를 config로 확인한다. ports의 고유 키를 기준으로 병합 결과를 읽고, !override로 교체하거나 포트를 한 파일에서만 정의하는 방법을 실제 입력과 비교한다.
개발용 Compose 파일에서 호스트 포트를 바꿨는데 최종 설정에 이전 포트까지 남을 수 있다. ports는 뒤 파일의 목록으로 통째로 교체되지 않는다. 호스트 포트가 다르면 별도 항목으로 병합된다. 새 포트만 남기려면 지원 버전에서 !override를 쓰거나, 공통 파일에서 포트를 빼고 환경별 파일 한 곳에서만 정의한다.
실행에 쓰는 파일 그대로 config를 확인한다
기존 프로젝트에서는 평소 사용하는 파일 목록과 순서를 유지한 채 하위 명령을 config로 바꾼다. 다음은 기본 파일이 compose.yaml, 개발 파일이 compose.dev.yaml인 경우다. 자신의 파일명으로 바꾸고, 원래 쓰던 --env-file, -p 같은 옵션도 유지한다. 첫 명령으로 버전을 확인하고, 둘째 명령으로 병합된 설정을 읽는다.
docker compose version
docker compose -f compose.yaml -f compose.dev.yaml config
config 공식 설명 (새 탭에서 열림)에 따르면 이 명령은 파일을 병합하고 변수를 치환하며 짧은 표기를 정규화한다. 출력의 해당 서비스 아래 ports를 본다. config -q는 설정을 출력하지 않으므로 이 비교에서는 -q를 빼야 한다. 명령이 실패하면 오류부터 해결하고, 출력이 없다는 이유로 포트가 제거됐다고 판단하지 않는다.
다른 이름의 개발 파일을 쓴다면 입력 파일도 명시해야 한다. 다중 파일 사용법 (새 탭에서 열림)의 기본 조합은 compose.yaml과 선택적인 compose.override.yaml이며, -f를 쓰면 나열한 순서대로 병합한다.
컨테이너 포트가 같아도 호스트 포트는 추가된다
작은 입력으로 비교하려면 Docker Compose CLI가 있는 환경의 별도 빈 폴더에 다음 두 파일을 저장한다. config만 실행하므로 이미지를 받거나 컨테이너를 기동할 필요는 없다. 먼저 compose.yaml에 호스트의 127.0.0.1:18080을 컨테이너 포트 80에 연결하는 선언을 둔다.
services:
web:
image: nginx:alpine
ports:
- "127.0.0.1:18080:80"
compose.dev.yaml에는 같은 서비스의 호스트 포트만 18081로 바꿔 적는다.
services:
web:
ports:
- "127.0.0.1:18081:80"
두 파일이 있는 폴더에서 앞의 명령을 실행했다. Windows 11, Docker CLI 29.8.0, Compose v5.5.1에서 종료 코드는 0이었다. services.web.ports에는 published: "18080"과 published: "18081"인 두 항목이 남았다. 둘 다 host_ip: 127.0.0.1, target: 80, protocol: tcp였다. 포트 표기 문서 (새 탭에서 열림)에서 published는 호스트 포트, target은 컨테이너 포트이며, 생략한 프로토콜의 기본값은 tcp다.
이 결과는 ports의 고유 키 규칙 (새 탭에서 열림)으로 설명된다. 식별 기준은 {ip, target, published, protocol} 네 값의 조합이다. 같은 키의 항목은 병합하고 다른 항목은 추가하므로, 컨테이너 포트가 같아도 published가 다르면 둘 다 남는다. 파일 순서를 뒤집은 대조 실행에서도 두 항목이 유지됐다.
둘째 파일에 같은 127.0.0.1:18080:80을 적으면 한 항목으로 합쳐졌다. 반면 ports: []를 적었을 때는 기존 18080이 남았다. 빈 목록만으로 앞 파일의 포트를 지울 수는 없었다.
새 포트만 남기는 두 가지 선택
Docker Compose 2.24.4 이상이면 !override로 목록 전체를 교체할 수 있다. 최소 버전은 공식 Replace value 설명 (새 탭에서 열림)에 명시돼 있다. 앞의 compose.dev.yaml을 다음 내용으로 바꾼 뒤, 처음 사용한 두 파일의 config 명령을 다시 실행한다.
services:
web:
ports: !override
- "127.0.0.1:18081:80"
직접 비교한 결과는 종료 코드 0이었고 ports에 published: "18081"인 항목 하나만 남았다. 18080은 사라졌다. 전체 목록을 교체하므로 계속 필요한 다른 포트도 이 목록에 함께 적는다. 실제 파일 조합에서 이전 포트가 다시 보인다면 교체 파일 뒤에 읽는 다른 파일이 포트를 추가하는지 확인한다.
태그를 해석하지 못하는 버전이라면 공통 파일에서 ports를 없애고 개발 파일에만 둔다. 위 예제에서는 compose.yaml의 ports 블록 전체를 제거해 services.web.image: nginx:alpine만 남기고, compose.dev.yaml은 태그 없는 원래 입력으로 되돌린다. 같은 두 파일을 합친 실행에서도 18081 한 항목만 남았다. 환경별 파일은 한 번에 하나를 선택해야 하며, 다른 입력 파일에 남아 있는 포트도 함께 점검한다.
포트 선언 자체를 없애려는 목적에는 ports: !reset []도 있다. Reset value 설명 (새 탭에서 열림)에서 태그 뒤의 값은 무시되므로 새 포트를 지정하는 용도로 쓰지 않는다. 별도 비교에서 이 설정을 기본 파일 뒤에 적용하자 services.web의 ports 키 자체가 사라졌다. 현재 문서에는 최소 버전 표기가 없다. compose-go 1.13.5의 도입 기록 (새 탭에서 열림)과 이를 채택한 Compose 2.18.0의 의존성 (새 탭에서 열림)을 연결하면 도입 경계를 2.18.0으로 추론할 수 있지만, 과거 바이너리는 이번에 실행하지 않았다. 사용 버전의 config에서 제거 결과를 확인해야 한다.
수정 뒤 원하는 포트 하나만 나오면 설정 병합 단계에서는 의도한 결과가 된 것이다. 이번 비교는 config 출력까지만 확인했다. 실제 컨테이너에 반영됐는지, 포트가 점유됐는지, 접속·방화벽 동작이 어떤지는 검증하지 않았다. 출력이 이미 원하는 포트 하나인데 접속이 안 된다면, 파일 병합과 실제 실행 상태를 구분해 조사한다.