# ========================================== # 선택 파일 — 리버스 프록시가 다른 서버에 있을 때 (mail/README.md "리버스 프록시가 다른 서버에 있을 때") # # 기본(docker-compose.yml 단독)은 외부 리버스 프록시가 같은 호스트에서 external proxy 네트워크로 # -frontend:8080 에 붙는다. 이 파일은 프록시가 다른 서버(예: 같은 VPC 의 Nginx Proxy Manager)에 있어 # 사설망으로 이 서버의 frontend 에 붙는 구성으로 바꾼다: # - frontend 를 proxy 네트워크에서 뺀다 (이 서버에는 그 네트워크가 없다). edge 로만 backend 에 닿는 것은 그대로다 # - frontend 의 8080 을 이 서버의 사설 IP 에만 게시한다 — FRONTEND_PRIVATE_BIND = <사설 IPv4>:<포트> # frontend 의 환경 · nginx 설정(X-Forwarded-For 이어 붙이기 · X-Request-Id · 실시간 알림)은 바꾸지 않는다. # # 쓰는 법 — 루트 .env.production 에 두 줄 (리눅스는 COMPOSE_FILE 을 : 로 잇는다): # COMPOSE_FILE=docker-compose.yml:docker-compose.remote-proxy.yml # FRONTEND_PRIVATE_BIND=10.0.1.12:8080 # compose 가 --env-file 의 COMPOSE_FILE 을 읽으므로 문서의 운영 명령(sudo docker compose --env-file .env.production …)이 # 그대로 두 파일을 읽는다 (-f 를 붙이지 않는다 — 하나라도 빠뜨리면 그 명령만 기본 구성으로 돈다). # # ⚠️ 게시 주소는 바깥을 막는 장치가 아니다. NCP 처럼 공인 IP 를 사설 IP 로 NAT 하는 곳에서는 사설 IP 에 게시한 포트에 # 공인 IP 로도 닿는다 — 8080 은 방화벽(NCP 는 ACG)이 프록시 서버의 사설 IP 에서만 받게 한다. # ========================================== services: frontend: # 다른 서버의 프록시가 붙는 자리 — 이 서버의 사설 IPv4 에만 (0.0.0.0 · 빈 값으로 열지 않는다). # 값이 없으면 compose 가 멈추고, <사설 IPv4>:<포트> 꼴이 아니면 아래 frontend-bind-check 가 frontend 를 띄우지 않는다 # (값이 포트뿐이면 "8080:8080" 이 되어 모든 주소에 열리기 때문이다) ports: !override - "${FRONTEND_PRIVATE_BIND:?FRONTEND_PRIVATE_BIND 가 비어 있습니다 — 이 서버의 사설 IPv4 와 포트 (예 10.0.1.12:8080) · mail/README.md 의 리버스 프록시가 다른 서버에 있을 때}:8080" # 이 서버에는 외부 프록시의 proxy 네트워크가 없다 — edge(frontend ↔ backend)만 남긴다. # app-network(메일 엔진의 신뢰 대역)에는 기본 구성처럼 붙이지 않는다 networks: !override - edge depends_on: frontend-bind-check: condition: service_completed_successfully # ---------------------------------------- # 게시 주소 확인 — frontend 보다 먼저 한 번 돌고 끝난다 (docker compose ps -a 에 Exited (0)) # # compose 의 ${…:?} 는 빈 값만 막는다. 포트뿐인 값 · 0.0.0.0 · 공인 IP · IPv6 는 여기서 막는다 — 실패하면 up 이 # "frontend-bind-check didn't complete successfully" 로 멈추고 frontend 는 뜨지 않는다 (까닭: logs frontend-bind-check). # 셸만 쓴다 — 이미 받는 DB 이미지를 빌려 새 이미지 · 버전 고정 자리를 늘리지 않는다. 네트워크 없이 · 비-root · 읽기 전용. # 받는 값: RFC 1918 사설 IPv4(10/8 · 172.16/12 · 192.168/16, 앞자리 0 없이) + ":" + 포트 1~65535(앞자리 0 없이) # ---------------------------------------- frontend-bind-check: container_name: ${PROJECT_NAME}-frontend-bind-check image: ${DB_IMAGE} user: "65534:65534" network_mode: none read_only: true cap_drop: - ALL restart: "no" # 로그 보존 — docker-compose.yml 의 logging-default 와 같은 값 (한 번 돌고 끝나는 컨테이너라 몇 줄뿐이다) logging: driver: json-file options: max-size: "10m" max-file: "5" compress: "true" environment: - FRONTEND_PRIVATE_BIND=${FRONTEND_PRIVATE_BIND:-} entrypoint: - /bin/sh - -c - | v=$$FRONTEND_PRIVATE_BIND o='(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])' p='([1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])' case $$v in *[!0-9.:]*) ;; *) if printf '%s\n' "$$v" | grep -Eqx "(10(\.$$o){3}|172\.(1[6-9]|2[0-9]|3[01])(\.$$o){2}|192\.168(\.$$o){2}):$$p"; then echo "frontend-bind-check: frontend 를 $$v 에만 게시한다 (사설 IPv4)" exit 0 fi ;; esac echo "frontend-bind-check: FRONTEND_PRIVATE_BIND=\"$$v\" 는 쓸 수 없어 frontend 를 띄우지 않습니다 — <이 서버의 사설 IPv4>:<포트> 만 받습니다 (10.x · 172.16~31.x · 192.168.x, 예 10.0.1.12:8080). 포트뿐인 값 · 0.0.0.0 · 공인 IP · IPv6 는 받지 않습니다 (mail/README.md \"리버스 프록시가 다른 서버에 있을 때\")" >&2 exit 1 networks: # base 의 external proxy 네트워크를 지운다 — 이 서버에 없는 네트워크를 찾다 up 이 멈추지 않게 proxy: !reset null