이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Docker network와 port mapping은 Spring API 접근 경로를 어떻게 결정하는가?
  • localhost, container name, host port, container port를 어떻게 구분해야 하는가?
  • Nginx, firewall, Docker port mapping이 어긋날 때 어떤 장애 신호가 나타나는가?

개요

Docker network 문제는 서버는 떠 있는데 접속이 안 된다로 나타난다.

Spring container 안에서는 8080이 열려 있지만 host에는 publish되지 않았을 수 있다.

host에서는 8080이 열려 있지만 Nginx upstream은 다른 port를 바라볼 수 있다.

Compose 내부에서는 service name으로 접근해야 하는데 localhost를 써서 자기 자신을 바라볼 수도 있다.

Port Mapping 기본

Docker port mapping은 host port와 container port를 연결한다.

docker run -p 8080:8080 order-api:3f21a9c

왼쪽 8080은 host port다.

오른쪽 8080은 container 내부 port다.

Spring server.port=8080은 container 내부 port를 의미한다.

host에서 접근하려면 port가 publish되어 있어야 한다.

docker ps --format "table {{.Names}}\t{{.Ports}}"

EXPOSE와 Publish

Dockerfile의 EXPOSE 8080은 문서화에 가깝다.

외부 접근을 자동으로 열어주지 않는다.

실제 host port 공개는 -p 또는 Compose ports가 담당한다.

services:
  app:
    image: order-api:3f21a9c
    ports:
      - "8080:8080"

EXPOSE만 있고 ports가 없으면 host에서 접근할 수 없을 수 있다.

localhost의 함정

container 안에서 localhost는 container 자기 자신이다.

Spring app container에서 localhost:5432를 DB로 쓰면 같은 container 안의 5432를 찾는다.

Compose에서는 DB service name을 써야 한다.

spring:
  datasource:
    url: jdbc:postgresql://db:5432/order

host에서 접근할 때의 localhost와 container 내부의 localhost는 다르다.

이 차이를 모르면 DB 연결 실패나 외부 API 연결 실패를 잘못 해석한다.

Compose Network

Compose는 기본 network를 만들고 service name으로 DNS를 제공한다.

services:
  app:
    image: order-api:3f21a9c
    depends_on:
      - db
  db:
    image: postgres:16

app container는 db:5432로 DB에 접근할 수 있다.

하지만 depends_on은 DB가 ready 상태라는 뜻이 아니다.

DB process가 떠도 schema migration이나 connection readiness는 별도 확인이 필요하다.

Nginx와 Docker

Nginx가 host에서 실행되고 Spring이 container로 실행되면 upstream은 host port를 바라본다.

location / {
    proxy_pass http://127.0.0.1:8080;
}

Nginx도 container라면 같은 Docker network 안에서 service name을 바라볼 수 있다.

proxy_pass http://order-api:8080;

어느 쪽인지에 따라 upstream 주소가 달라진다.

Nginx 502가 발생하면 Spring container log에 요청이 들어왔는지 먼저 확인한다.

운영 체크리스트

  • Spring server.port와 container port가 일치하는가?
  • host port publish가 필요한 서비스에만 열려 있는가?
  • Nginx/LB upstream이 host port와 container network 중 어디를 바라보는가?
  • container 내부에서 DB나 Redis를 localhost로 바라보지 않는가?
  • Compose service name과 DNS가 문서화되어 있는가?
  • firewall과 security group이 host port를 허용하는가?
  • management port를 분리했다면 health check 경로가 맞는가?

장애 신호

  • container 내부 curl localhost:8080은 성공하지만 host curl localhost:8080은 실패한다.
  • host에서는 성공하지만 외부에서는 timeout이 난다.
  • Nginx 502가 늘고 Spring access log에는 요청이 없다.
  • Spring app이 DB를 localhost로 바라봐 connection refused가 난다.
  • docker ps Ports 항목이 비어 있다.
  • host port 충돌로 container 시작이 실패한다.
  • Compose 내부 service name을 해석하지 못한다.

안전한 완화 조치

network 문제는 안쪽에서 바깥쪽으로 확인한다.

  1. container 내부 Spring health 확인
  2. host에서 published port 확인
  3. Nginx upstream 확인
  4. firewall/security group 확인
  5. 외부 client 확인
docker exec order-api curl -fsS http://localhost:8080/actuator/health
curl -fsS http://127.0.0.1:8080/actuator/health
docker network inspect <network>

임시로 모든 port를 열기보다 어느 경로가 막혔는지 좁힌다.

인프라 담당자와 공유할 자료

증상: 외부 502, host curl 127.0.0.1:8080 실패
container: order-api Up, internal health OK
docker ps Ports: 127.0.0.1:18080->8080/tcp
Nginx upstream: 127.0.0.1:8080
요청: upstream port를 18080으로 맞출지 container publish를 수정할지 확인

port 문제는 Spring port, container port, host port, proxy port를 한 줄에 보여주는 것이 중요하다.

실전 팁

  • EXPOSEports를 혼동하지 않는다.
  • localhost가 어느 실행 범위 기준인지 항상 확인한다.
  • Nginx가 host에 있는지 container network 안에 있는지 먼저 본다.
  • port mapping은 release note나 compose 파일에 명시한다.
  • host port를 넓게 열기보다 proxy 뒤로 숨기는 구성을 우선 검토한다.

위험 신호!

  • Dockerfile에 EXPOSE가 있으니 외부 접근이 된다고 생각한다.
  • container 내부에서 DB URL을 localhost로 둔다.
  • docker ps의 Ports 항목을 보지 않고 Nginx부터 수정한다.
  • host firewall과 Docker publish port를 따로 관리하지 않는다.
  • management endpoint를 외부에 그대로 노출한다.

확인 질문

확인 질문

  • 8080:8080 port mapping에서 왼쪽과 오른쪽은 각각 무엇인가?
    • 왼쪽은 host port이고 오른쪽은 container 내부 port다.
  • container 안에서 localhost를 DB 주소로 쓰면 왜 문제가 될 수 있는가?
    • container 안의 localhost는 그 container 자신이므로 별도 DB container나 host DB를 바라보지 않는다.
  • Nginx 502에서 Docker 쪽으로 먼저 확인할 것은 무엇인가?
    • Spring container 내부 health, host port publish, Nginx upstream 주소와 port, Spring access log 도달 여부다.

참고 문서