지금까지 모든 서비스가 각자 포트를 열고 HTTP로 통신했다. Grafana는 9300, Prometheus는 9090, 웹 앱은 8080. 포트를 외우고 다녀야 하고, 데이터는 평문으로 날아다닌다. 이번에 Nginx를 HTTPS 게이트웨이로 바꿔서, 443 포트 하나로 모든 서비스에 접근하고 나머지 포트는 방화벽에서 차단한다.
| 항목 | Before | After |
|---|---|---|
| 프론트엔드 | http://10.211.55.10:8080 | https://10.211.55.10/ |
| API | http://10.211.55.10:8080/api/ | https://10.211.55.10/api/ |
| Grafana | http://10.211.55.10:9300 | https://10.211.55.10/grafana/ |
| Prometheus | http://10.211.55.10:9090 | https://10.211.55.10/prometheus/ |
| SSL | 없음 | NEW TLS 1.2/1.3 자체서명 인증서 |
| HTTP 접속 시 | 그냥 응답 | HTTPS로 301 리다이렉트 |
| 보안 헤더 | 없음 | NEW HSTS, X-Frame-Options, XSS Protection |
| 외부 노출 포트 | 8080, 9090, 9300 | 80, 443만 (최소화) |
ports: 8080/tcp 3000/tcp 9090/tcp 9300/tcp
services: http ssh
ports: 443/tcp
rich rules:
rule family="ipv4" port port="9100" protocol="tcp" reject
rule family="ipv4" port port="8081" protocol="tcp" reject
Nginx는 웹 앱 스택(docker-lab_default)에 속하고, Grafana와 Prometheus는 모니터링 스택(monitoring_default)에 속한다. 서로 다른 Docker 네트워크라 기본적으로 통신이 안 된다. Nginx에 monitoring_default 네트워크를 external: true로 추가 연결해서 해결한다.
external: true로 해당 네트워크를 연결해야 한다. 이걸 모르면 Nginx → Grafana 프록시에서 "502 Bad Gateway"가 뜬다.HTTPS를 적용하려면 SSL/TLS 인증서가 필요하다. 인증서는 "이 서버가 진짜 이 서버가 맞다"를 증명하는 디지털 신분증이다. 보통 CA(인증기관)에서 발급받지만, 내부 서버나 개발 환경에서는 직접 서명한 인증서(Self-Signed)를 만들어 쓴다. CA 서명이 없어서 브라우저가 "안전하지 않음" 경고를 띄우지만, 암호화 자체는 CA 서명 인증서와 완전히 동일하게 동작한다.
-nodes 옵션이 중요한데, 이걸 안 쓰면 개인키에 비밀번호가 걸려서 Nginx가 시작할 때마다 비밀번호를 입력해야 한다. 서버 재부팅이나 컨테이너 재시작 시 자동 복구가 안 되기 때문에 운영 환경에서는 보통 -nodes를 사용한다.
$ sudo mkdir -p /opt/docker-lab/ssl $ sudo openssl req -x509 -nodes -days 365 \ -newkey rsa:2048 \ -keyout /opt/docker-lab/ssl/server.key \ -out /opt/docker-lab/ssl/server.crt \ -subj '/C=KR/ST=Seoul/L=Seoul/O=EndofLinux/CN=rocky.local'
| 옵션 | 설명 |
|---|---|
-x509 | 자체 서명 인증서 생성 (CSR 대신 바로 인증서) |
-nodes | 개인키 암호화 없음 (서버 자동 시작용) |
-days 365 | 유효기간 1년 |
-newkey rsa:2048 | 2048비트 RSA 키 새로 생성 |
-keyout | 개인키 저장 경로 |
-out | 인증서 저장 경로 |
-subj | 인증서 정보 (국가/지역/조직/도메인) |
$ sudo openssl x509 -in /opt/docker-lab/ssl/server.crt -noout -subject -dates subject=C=KR, ST=Seoul, L=Seoul, O=EndofLinux, CN=rocky.local notBefore=Mar 8 10:22:53 2026 GMT notAfter=Mar 8 10:22:53 2027 GMT
/opt/docker-lab/ssl/ ├── server.crt # 인증서 (공개키 포함) └── server.key # 개인키
| 항목 | 자체 서명 (Self-Signed) | Let's Encrypt |
|---|---|---|
| 비용 | 무료 | 무료 |
| 브라우저 신뢰 | ❌ 경고 표시 | ✅ 신뢰됨 |
| 발급 조건 | 없음 | 공인 도메인 + 외부 접근 필요 |
| 유효기간 | 자유 설정 | 90일 (자동 갱신) |
| 용도 | 내부/개발/테스트 | 프로덕션 |
이 파일은 /opt/docker-lab/nginx.conf이고, Nginx가 어떤 포트에서 듣고, 어떤 경로를 어디로 보낼지 정의하는 핵심 설정 파일이다. 컨테이너 내부 /etc/nginx/conf.d/default.conf에 마운트된다.
이번 변경의 핵심은 세 가지다. 첫째, HTTP 80번 서버 블록을 추가해서 모든 HTTP 요청을 HTTPS로 리다이렉트한다 — 사용자가 실수로 http://로 접속해도 자동으로 https://로 넘어가게 하는 것이다. 둘째, HTTPS 443번 서버 블록에 SSL 인증서와 보안 프로토콜을 설정한다. 셋째, 기존에 각자 포트를 열고 있던 Grafana(9300)와 Prometheus(9090)를 서브패스 프록시로 통합해서 443번 포트 하나로 접근할 수 있게 한다.
upstream backend { server app:8000; } server { listen 80; server_name _; location / { root /usr/share/nginx/html; index index.html; } location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
upstream backend { server app:8000; } # HTTP → HTTPS 리다이렉트 server { listen 80; server_name _; return 301 https://$host$request_uri; } # HTTPS 서버 server { listen 443 ssl; server_name _; # SSL 인증서 ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; # SSL 프로토콜 (TLS 1.0/1.1 비활성화) ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # 보안 헤더 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options SAMEORIGIN always; add_header X-XSS-Protection "1; mode=block" always; # 프론트엔드 location / { root /usr/share/nginx/html; index index.html; } # API 리버스 프록시 location /api/ { proxy_pass http://backend/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # Grafana 리버스 프록시 location /grafana/ { proxy_pass http://grafana:3000/grafana/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # WebSocket 지원 (Grafana Live용) proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # Prometheus 리버스 프록시 location /prometheus/ { proxy_pass http://prometheus:9090/prometheus/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
| 설정 | 값 | 설명 |
|---|---|---|
ssl_protocols | TLSv1.2 TLSv1.3 | TLS 1.0/1.1 비활성화 (취약) |
ssl_ciphers | ECDHE-* | 강력한 암호화 스위트만 허용 |
ssl_prefer_server_ciphers | on | 서버 측 암호화 우선 |
ssl_session_cache | shared:SSL:10m | SSL 세션 캐시 (성능 향상) |
ssl_session_timeout | 10m | 세션 재사용 타임아웃 |
| 헤더 | 값 | 설명 |
|---|---|---|
Strict-Transport-Security | max-age=31536000 | 1년간 HTTPS만 사용 강제 (HSTS) |
X-Content-Type-Options | nosniff | MIME 타입 스니핑 방지 |
X-Frame-Options | SAMEORIGIN | iframe 삽입 방지 (클릭재킹 방어) |
X-XSS-Protection | 1; mode=block | XSS 공격 감지 시 페이지 차단 |
| 헤더 | 설명 |
|---|---|
Host | 원본 호스트명 전달 |
X-Real-IP | 실제 클라이언트 IP 전달 |
X-Forwarded-For | 프록시 체인 IP 전달 |
X-Forwarded-Proto | 원본 프로토콜 (https) 전달 |
Upgrade / Connection | WebSocket 지원 (Grafana Live용) |
proxy_http_version 1.1과 Upgrade/Connection 헤더가 없으면 Live 기능이 동작하지 않는다.여기서 수정해야 하는 파일이 세 개다. 각 파일이 뭔지 먼저 짚고 넘어간다.
| 파일 | 경로 | 역할 |
|---|---|---|
docker-compose.yml (웹 앱) | /opt/docker-lab/ | Nginx + Backend 컨테이너 정의. 포트와 네트워크 설정 |
docker-compose.yml (모니터링) | /opt/monitoring/ | Prometheus + Grafana + Loki 등 8개 컨테이너 정의 |
prometheus.yml | /opt/monitoring/ | Prometheus가 메트릭을 어디서 수집할지 정의하는 스크래핑 설정 |
이 파일은 Nginx와 Backend 앱을 정의한다. 핵심 변경은 세 가지다: 포트를 8080에서 표준 80/443으로 변경, SSL 인증서 디렉토리를 :ro(읽기 전용)로 마운트, 그리고 모니터링 스택 네트워크에 연결하는 것이다. 네트워크 연결이 왜 필요한가 — Docker Compose는 스택별로 격리된 네트워크를 만들기 때문에, Nginx가 다른 스택에 있는 Grafana나 Prometheus에 접근하려면 external: true로 해당 네트워크를 명시적으로 연결해야 한다.
services: app: build: . container_name: backend-app networks: - default restart: unless-stopped nginx: image: nginx:latest container_name: nginx-proxy ports: - "80:80" # HTTP → HTTPS 리다이렉트용 - "443:443" # HTTPS 메인 포트 volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf - ./index.html:/usr/share/nginx/html/index.html - ./ssl:/etc/nginx/ssl:ro # SSL 인증서 (읽기 전용) depends_on: - app networks: - default - monitoring_default # Grafana/Prometheus 접근용 restart: unless-stopped networks: monitoring_default: external: true # 모니터링 스택 네트워크 연결
| 변경 | 설명 |
|---|---|
ports: 8080:80 → 80:80, 443:443 | 표준 HTTP/HTTPS 포트 사용 |
| NEW ssl 볼륨 | ./ssl:/etc/nginx/ssl:ro (읽기 전용) |
| NEW monitoring_default 네트워크 | Grafana/Prometheus 컨테이너 접근용 외부 네트워크 |
이 파일은 /opt/monitoring/docker-compose.yml이다. Grafana와 Prometheus에 서브패스 설정을 추가한다. 왜 필요한가 — Nginx가 /grafana/ 경로를 Grafana로 프록시 해줘도, Grafana 자체가 "나는 / 루트에서 서비스되고 있다"고 생각하면 CSS/JS 리소스를 /public/에서 찾으려고 한다. 실제로는 /grafana/public/이어야 하니까 404가 뜬다. Grafana에게 "너는 /grafana/ 경로에서 서비스되고 있다"를 알려줘야 하는 것이다. Prometheus도 마찬가지다.
# Prometheus — 서브패스 설정 추가 prometheus: command: - '--config.file=/etc/prometheus/prometheus.yml' - '--web.external-url=/prometheus/' - '--web.route-prefix=/prometheus/' # Grafana — 서브패스 설정 추가 grafana: environment: - GF_SECURITY_ADMIN_PASSWORD=EofGrafana2026! - GF_SERVER_ROOT_URL=https://10.211.55.10/grafana/ - GF_SERVER_SERVE_FROM_SUB_PATH=true
| 서비스 | 설정 | 설명 |
|---|---|---|
| Prometheus | --web.external-url=/prometheus/ | 외부 URL 경로 인식 |
| Prometheus | --web.route-prefix=/prometheus/ | 내부 라우팅 프리픽스 |
| Grafana | GF_SERVER_ROOT_URL | 서브패스 URL 설정 |
| Grafana | GF_SERVER_SERVE_FROM_SUB_PATH=true | 서브패스 모드 활성화 |
이 파일은 /opt/monitoring/prometheus.yml이고, Prometheus가 어떤 타겟에서 메트릭을 수집할지 정의하는 스크래핑 설정이다. 서브패스를 적용하면 Prometheus 자기 자신의 메트릭 엔드포인트도 경로가 바뀐다. 기본 /metrics가 /prometheus/metrics로 변경되기 때문에, 자체 수집 job의 metrics_path를 수정해야 한다. 안 하면 Prometheus가 자기 자신의 상태를 수집하지 못해서 해당 메트릭이 누락된다.
- job_name: 'prometheus' metrics_path: '/prometheus/metrics' # 서브패스 모드 메트릭 경로 static_configs: - targets: ['localhost:9090']
지금까지 Nginx가 HTTPS 게이트웨이 역할을 하도록 설정했다. 그런데 방화벽에서 기존 포트를 안 닫으면 의미가 없다. 누군가 http://10.211.55.10:9090으로 Prometheus에 직접 접근할 수 있으면, 리버스 프록시를 거치지 않고 SSL 없이 평문 통신이 가능하다. HTTPS로 통합한 의미가 사라진다. 따라서 80(리다이렉트용)과 443(HTTPS)만 열고 나머지는 전부 차단해야 한다.
# HTTPS 포트 개방 $ sudo firewall-cmd --permanent --add-port=443/tcp # HTTP 서비스 허용 (80 → 리다이렉트용) $ sudo firewall-cmd --permanent --add-service=http # 기존 포트 제거 $ sudo firewall-cmd --permanent --remove-port=8080/tcp $ sudo firewall-cmd --permanent --remove-port=9300/tcp $ sudo firewall-cmd --permanent --remove-port=9090/tcp # 적용 $ sudo firewall-cmd --reload
| 포트 | Before | After |
|---|---|---|
| 8080/tcp | 허용 (Nginx HTTP) | 제거 |
| 9090/tcp | 허용 (Prometheus) | 제거 |
| 9300/tcp | 허용 (Grafana) | 제거 |
| 80/tcp | 없음 | NEW HTTP → HTTPS 리다이렉트 |
| 443/tcp | 없음 | NEW HTTPS 전체 서비스 |
public (active) services: dhcpv6-client http ssh ports: 443/tcp rich rules: rule family="ipv4" port port="9100" protocol="tcp" reject rule family="ipv4" port port="8081" protocol="tcp" reject
# 모니터링 스택 재시작 $ cd /opt/monitoring && sudo docker compose up -d --force-recreate # 웹 앱 스택 재시작 $ cd /opt/docker-lab && sudo docker compose up -d --force-recreate
# HTTP → HTTPS 리다이렉트 $ curl -sk -o /dev/null -w '%{http_code} → %{redirect_url}' http://localhost 301 → https://localhost/ # HTTPS 프론트엔드 $ curl -sk -o /dev/null -w '%{http_code}' https://localhost 200 # API $ curl -sk https://localhost/api/ {"status": "ok", "message": "Hello from Backend!", "server": "Rocky Linux 9.7"} # Grafana (302 = 로그인 페이지 리다이렉트, 정상) $ curl -sk -o /dev/null -w '%{http_code}' https://localhost/grafana/ 302 # Prometheus (302 = graph 페이지 리다이렉트, 정상) $ curl -sk -o /dev/null -w '%{http_code}' https://localhost/prometheus/ 302 # SSL 인증서 확인 $ echo | openssl s_client -connect localhost:443 2>/dev/null | openssl x509 -noout -subject -issuer -dates subject=C=KR, ST=Seoul, L=Seoul, O=EndofLinux, CN=rocky.local issuer=C=KR, ST=Seoul, L=Seoul, O=EndofLinux, CN=rocky.local notBefore=Mar 8 10:22:53 2026 GMT notAfter=Mar 8 10:22:53 2027 GMT
/opt/docker-lab/ ├── docker-compose.yml # 웹 앱 서비스 정의 ├── Dockerfile # Python 백엔드 이미지 ├── app.py # Python API 서버 ├── nginx.conf # Nginx HTTPS + 리버스 프록시 설정 ├── index.html # 프론트엔드 페이지 └── ssl/ # SSL 인증서 ← NEW ├── server.crt # 인증서 (공개키) └── server.key # 개인키 /opt/monitoring/ ├── docker-compose.yml # 모니터링 스택 정의 ├── prometheus.yml # Prometheus 설정 ├── loki-config.yml # Loki 설정 └── promtail-config.yml # Promtail 설정
| Service | URL | 인증 |
|---|---|---|
| Frontend | https://10.211.55.10/ | - |
| Backend API | https://10.211.55.10/api/ | - |
| Grafana | https://10.211.55.10/grafana/ | admin / EofGrafana2026! |
| Prometheus | https://10.211.55.10/prometheus/ | - |
| Container | Image | Internal Port | 접근 경로 |
|---|---|---|---|
| nginx-proxy | nginx:latest | 80, 443 | / (HTTPS 엔트리포인트) |
| backend-app | docker-lab-app | 8000 | /api/ |
| grafana | grafana/grafana:11.4.0 | 3000 | /grafana/ |
| prometheus | prom/prometheus | 9090 | /prometheus/ |
| node-exporter | prom/node-exporter | 9100 | 내부 전용 |
| cadvisor | cadvisor/cadvisor | 8080 | 내부 전용 |
| loki | grafana/loki:3.4.2 | 3100 | 내부 전용 |
| promtail | grafana/promtail:3.4.2 | - | 내부 전용 |
| 환경 | 인증서 | 방법 |
|---|---|---|
| 개발/테스트 | 자체 서명 | openssl req -x509 |
| 프로덕션 | Let's Encrypt | certbot --nginx |
| 기업 | 상용 CA | DigiCert, Sectigo 등 구매 |
| 문제 | 원인 | 해결 |
|---|---|---|
| 브라우저 "안전하지 않음" 경고 | 자체 서명 인증서 (CA 미신뢰) | 고급 → 계속 진행 (정상 동작) |
| Grafana 404 | 서브패스 미설정 | GF_SERVER_SERVE_FROM_SUB_PATH=true |
| Prometheus 404 | 서브패스 미설정 | --web.external-url=/prometheus/ |
| Nginx → Grafana 연결 실패 | 네트워크 격리 | monitoring_default 외부 네트워크 연결 |
| HTTP 접속 시 페이지 안 뜸 | 리다이렉트 설정 누락 | return 301 https://$host$request_uri |
| 서브패스 적용 후 No data | Data Source URL 미수정 | URL에 /prometheus 서브패스 추가 |
HTTPS + 리버스 프록시 통합 작업을 전부 끝내고 Grafana에 접속했더니 대시보드에 No data가 떴다. 모든 패널이 비어 있었다. 서브패스를 적용할 때 빠뜨리기 쉬운 함정이 여기 있었다.
Grafana 대시보드 접속 자체는 https://10.211.55.10/grafana/로 정상 동작하지만, CPU·메모리·디스크 등 모든 메트릭 패널이 No data를 표시했다.
Prometheus에 --web.route-prefix=/prometheus/ 서브패스를 적용했으면, Prometheus의 API 경로가 /api/v1/query에서 /prometheus/api/v1/query로 바뀐다. 그런데 Grafana의 Prometheus Data Source URL은 기존 http://prometheus:9090 그대로 남아있었기 때문에, Grafana가 엉뚱한 경로로 메트릭을 요청하고 있었던 것이다.
| 항목 | Before (문제) | After (해결) |
|---|---|---|
| Data Source URL | http://prometheus:9090 | http://prometheus:9090/prometheus |
| 메트릭 경로 | /api/v1/query (404) | /prometheus/api/v1/query (200) |
먼저 서버 시간이 맞는지 확인한다. 이전에 시간 동기화 문제가 있었기 때문에 습관적으로 체크하는 것이다. 시간이 맞으면 Data Source URL을 확인한다.
# 1. 서버 시간 확인 $ date -u Sun Mar 8 10:38:34 UTC 2026 # → 정상 (Mac과 동일) # 2. Grafana Data Source URL 확인 $ curl -s -u 'admin:EofGrafana2026!' \ http://localhost:9300/grafana/api/datasources \ | python3 -c "import sys,json; [print(d['name'], d['url']) for d in json.load(sys.stdin)]" Loki http://loki:3100 Prometheus http://prometheus:9090 # ← /prometheus 누락 발견!
/prometheus가 빠져 있었다. Grafana가 http://prometheus:9090/api/v1/query로 요청하지만, Prometheus는 /prometheus/api/v1/query에서만 응답한다.Grafana API로 Data Source URL을 수정한다. 웹 UI에서도 할 수 있지만, 재현 가능한 명령어로 남겨두는 게 좋다.
# Data Source UID 확인 $ DS_UID=$(curl -s -u 'admin:EofGrafana2026!' \ http://localhost:9300/grafana/api/datasources \ | python3 -c "import sys,json; [print(d['uid']) for d in json.load(sys.stdin) if d['name']=='Prometheus']") # URL 업데이트 (서브패스 추가) $ curl -s -X PUT -u 'admin:EofGrafana2026!' \ -H 'Content-Type: application/json' \ http://localhost:9300/grafana/api/datasources/uid/$DS_UID \ -d '{ "name": "Prometheus", "type": "prometheus", "access": "proxy", "url": "http://prometheus:9090/prometheus", "isDefault": true }'
$ curl -s -u 'admin:EofGrafana2026!' \ 'http://localhost:9300/grafana/api/datasources/proxy/uid/efewvb6wfudq8e/api/v1/query?query=node_load1' \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['data']['result'][0]['value'][1])" 0.31 # → 정상 반환, 대시보드 데이터 복구 확인
서비스에 서브패스(sub-path)를 적용할 때는 서비스 자체 + 리버스 프록시 + 연동 클라이언트 세 곳을 모두 수정해야 한다. 하나라도 빠지면 장애가 발생한다.
# SSL 인증서 만료일 확인 $ openssl x509 -in /opt/docker-lab/ssl/server.crt -noout -dates # SSL 인증서 상세 정보 $ openssl x509 -in /opt/docker-lab/ssl/server.crt -noout -text # Nginx 설정 테스트 $ sudo docker exec nginx-proxy nginx -t # Nginx 리로드 (재시작 없이 설정 적용) $ sudo docker exec nginx-proxy nginx -s reload # HTTPS 연결 테스트 $ curl -sk https://10.211.55.10/ $ curl -sk https://10.211.55.10/api/ $ curl -sk https://10.211.55.10/grafana/ $ curl -sk https://10.211.55.10/prometheus/ # SSL 핸드셰이크 디버깅 $ openssl s_client -connect 10.211.55.10:443 # 인증서 갱신 (1년 후) $ sudo openssl req -x509 -nodes -days 365 \ -newkey rsa:2048 \ -keyout /opt/docker-lab/ssl/server.key \ -out /opt/docker-lab/ssl/server.crt \ -subj '/C=KR/ST=Seoul/L=Seoul/O=EndofLinux/CN=rocky.local' $ sudo docker exec nginx-proxy nginx -s reload
ssl_protocols TLSv1.2 TLSv1.3;으로 설정하면 취약한 프로토콜을 차단할 수 있다.GF_SERVER_ROOT_URL과 GF_SERVER_SERVE_FROM_SUB_PATH=true 두 가지를 모두 설정해야 한다. 하나만 설정하면 CSS/JS 리소스 경로가 꼬여서 로그인 화면은 뜨지만 대시보드가 깨진다. Nginx의 proxy_pass URL 끝에도 반드시 /grafana/를 붙여야 한다.docker-lab_default, Grafana는 monitoring_default 네트워크에 속하기 때문에 기본적으로 서로 통신이 불가능하다. Nginx의 docker-compose.yml에 monitoring_default 네트워크를 external: true로 추가하면 두 네트워크를 동시에 연결할 수 있다.certbot --nginx 명령어로 자동 발급과 Nginx 설정 수정이 가능하다. Docker 환경에서는 certbot 컨테이너를 추가하거나, nginx-proxy + letsencrypt-companion 이미지를 사용하는 방법이 일반적이다.http://prometheus:9090을 http://prometheus:9090/prometheus로 변경한다. 서브패스 적용 시 서비스 자체, 리버스 프록시, 연동 클라이언트(Data Source) 세 곳을 모두 수정해야 한다.