컨테이너는 기본적으로 외부와 격리된 네트워크 안에서 실행된다. 호스트 머신이나 외부 클라이언트가 컨테이너 서비스에 접근하려면 포트 바인딩을 명시적으로 선언해야 한다. Docker Compose는 ports와 expose 두 가지 키워드로 이 동작을 제어한다.
ports는 호스트 포트를 컨테이너 포트에 매핑해 외부 트래픽을 받는다.
services:
web:
image: nginx
ports:
- "8080:80"
"호스트포트:컨테이너포트" 형식. 위 예시는 호스트의 8080으로 들어온 요청을 컨테이너 내부 80 포트로 전달한다.
외부 클라이언트
│
▼
호스트 :8080
│ 포트 매핑
▼
컨테이너 :80 (nginx)
호스트 포트를 생략하면 Docker가 임의의 빈 포트를 자동 할당한다.
ports:
- "80" # 호스트 포트 자동 할당
한 서비스에 복수의 포트를 선언하거나, 연속된 범위를 한 번에 매핑할 수 있다.
services:
app:
image: myapp
ports:
- "3000:3000"
- "9229:9229" # Node.js 디버거
- "8000-8005:8000-8005" # 범위 바인딩
범위 바인딩은 호스트와 컨테이너의 포트 수가 같아야 한다.
expose는 호스트에는 포트를 열지 않고, 같은 Compose 네트워크 안의 다른 서비스에만 포트를 알린다.
services:
db:
image: postgres
expose:
- "5432"
호스트 머신 Compose 내부 네트워크
(접근 불가) ┌─────────────────────────┐
│ web ──────► db :5432 │
└─────────────────────────┘
expose는 실제로 포트를 여는 것이 아니라 문서화 역할에 가깝다. Compose 네트워크 내 서비스 간 통신은 expose 없이도 서비스 이름으로 접근 가능하다.
| 항목 | ports | expose |
|---|---|---|
| 호스트에서 접근 | 가능 | 불가 |
| 다른 서비스에서 접근 | 가능 | 가능 |
| 외부 노출 | O | X |
| 용도 | 외부 트래픽 수신 | 내부 서비스 간 통신 문서화 |
Compose는 같은 docker-compose.yml 안의 서비스들을 기본적으로 하나의 네트워크에 연결한다. api 서비스가 db 서비스에 접근할 때는 호스트 포트 매핑 없이 서비스 이름(db)과 컨테이너 포트만으로 충분하다.
services:
api:
image: myapi
environment:
DB_HOST: db
DB_PORT: 5432
db:
image: postgres
# ports 선언 없이도 api에서 접근 가능
ports를 불필요하게 열면 호스트 포트가 외부에 노출되어 보안 위협이 생긴다. 외부 접근이 필요한 서비스에만 ports를 선언하고, 내부 전용 서비스는 포트를 열지 않는 것이 원칙이다.
기본값은 TCP이며, UDP가 필요할 경우 명시한다.
ports:
- "5353:5353/udp"
- "8080:80/tcp"