☘ ANONI Chat - NGINX(feat. Kibana오류와 HTTPS 적용하기)
Kibana에 접속시, UI가 안보이고 오류로그가 JSON 형태로만 보이는 오류가 발생.
찾아보니 elasticsearch 7.11+ 버전에서 발생하는 문제라고 한다.
우선 임시방편으로 docker-compose.yml을 수정할 필요가 있다.
라이센스를 basic으로 명시해줘야하고 보안 설정을 false로 해야한다.
보안 설정을 해제하는 이유는 보안 설정을 하면 HTTPS 사용이 강제되어서 HTTP로는 접근이 불가능하기 때문이다.
현재는 환경 구축 단계이고 HTTPS를 적용하지 않은 상태이기 때문에 나중에 HTTPS 설정을 하고 보안 설정을 다시 할 예정이다.
docker-compose.yml 변경 사항
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:7.11.1
environment:
- discovery.type=single-node
- xpack.security.enabled=false # 보안 기능 비활성화
- xpack.license.self_generated.type=basic # 기본 라이센스 사용
ports:
- "9200:9200"
networks:
- elk
volumes:
- esdata:/usr/share/elasticsearch/data
kibana:
image: docker.elastic.co/kibana/kibana:7.11.1
ports:
- "5601:5601"
networks:
- elk
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
- server.host=0.0.0.0
- xpack.security.enabled=false # Kibana 보안 비활성화
- xpack.license.self_generated.type=basic
정방향 프록시는:
리버스 프록시는:
[ 클라이언트 (브라우저) ]
⇩ HTTPS 요청 (443)
┌──────────────────────┐
│ NGINX 서버 │
│ - SSL 인증서 보유 │
│ - HTTPS 처리 │
│ - 요청을 Spring에 전달 │
└─────────┬────────────┘
⇩ HTTP (80)
[ Spring Boot 서버 ]
/api
, /admin
등)HTTPS등 인증서를 Spring에서 직접처리해도 되지만,
NGINX에서 프록시 + 인증서 처리를 담당하는 것이 확장성/유지보수 측면에서 유리하다.
SSL 인증서 발급 자동화 도구
구성 요소 | 설명 |
---|---|
NGINX | 리버스 프록시, 웹 서버 |
certbot | Let's Encrypt 무료 인증서 발급 클라이언트 |
.conf 파일 | 직접 작성해야 함 (예: server { listen 443; ... } ) |
cron/script | 인증서 자동 갱신 후 NGINX 재로드 수동 설정 필요 |
장점
단점
구성 요소 | 설명 |
---|---|
Nginx Proxy Manager 컨테이너 | 내부적으로 NGINX + certbot + 관리 인터페이스 내장 |
Web UI | 웹 브라우저로 리버스 프록시/도메인 설정/HTTPS 적용 가능 |
자동 인증서 발급 | Let's Encrypt 인증서 자동 신청/갱신 내장 |
Docker 기반 구성 | 한 줄로 실행 가능 docker-compose 스택 제공 |
장점
단점
→ 로드 벨런싱등 고급 설정은 추후 k8s를 통해 구현될 예정
기존 docker-compose.yml 파일에 nginx와 certbot을 추가한다.
# Nginx 리버스 프록시
nginx:
image: nginx:alpine
container_name: nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
depends_on:
- spring
networks:
- elk # Spring과 같은 네트워크에 연결
command: ["nginx", "-g", "daemon off;"]
# Certbot SSL 인증서 관리
certbot:
image: certbot/certbot
container_name: certbot
restart: unless-stopped
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
- /var/run/docker.sock:/var/run/docker.sock
entrypoint: "/bin/sh"
command: > # ssl 인증서 갱신시에만 nginx 설정 reload하는 명령어
-c "
apk add --no-cache docker-cli;
while :; do
echo 'Checking for certificate renewal...';
certbot renew --deploy-hook 'docker exec nginx nginx -s reload';
sleep 12h;
done
"
동일한 docker-compose.yml 파일 내의 spring의 port 변경
Spring:
# Spring의 기존 포트 설정
ports: - "80:8080" # 호스트포트:컨테이너포트
Spring:
# Spring의 변경된 포트 설정
expose: - "8080" # 컨테이너포트만
즉, ports
는 외부 → 컨테이너 접근을 위해 사용하고, expose
는 컨테이너 간 통신을 위해 사용
nginx:
# + nginx의 포트 설정
ports:
- "80:80"
- "443:443"
docker run --rm -it \
--name certbot \
-v $(pwd)/certbot/conf:/etc/letsencrypt \
-v $(pwd)/certbot/www:/var/www/certbot \
certbot/certbot \
certonly --webroot \
--webroot-path /var/www/certbot \
--email jsi50069@gmail.com \
--agree-tos \
--no-eff-email \
-d anonichat.world \
-d www.anonichat.world
--rm
: 컨테이너 종료 시 자동 삭제 (임시용 실행)-it
: interactive 모드-v ...:/etc/letsencrypt
: 인증서 저장 디렉토리 (호스트에 영구 저장)-v ...:/var/www/certbot
: 인증 도메인 소유 검증에 사용하는 웹 루트 경로certonly --webroot
: 웹 루트 방식으로 인증서만 발급 (nginx가 .well-known 요청을 받아줘야 함)--email
: 만료 알림용 이메일--agree-tos
: 이용약관 동의-d
: 도메인 이름으로 ssl 인증서를 받음docker-compose exec certbot ls -la /etc/letsencrypt/live/anonichat.world/
docker-compose up -d certbot
./nginx/conf.d:/etc/nginx/conf.d
docker-compose의 해당되는 디렉토리에 생성upstream spring-backend {
#Nginx가 내부 네트워크에서 Spring 컨테이너의 8080포트로 요청을 전달
server spring:8080;
}
# HTTP 요청은 기본적으로 **HTTPS로 리다이렉트**
# 단, `/well-known/acme-challenge/`는 Certbot 검증을 위해 예외 허용 (정적 루트 연결)
# 이때 certbot이 바인딩한 경로와 nginx `root` 경로가 정확히 일치해야 인증 가능
server {
listen 80;
server_name anonichat.world www.anonichat.world;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS 서버
server {
listen 443 ssl;
http2 on;
server_name anonichat.world www.anonichat.world;
server_tokens off;
ssl_certificate /etc/letsencrypt/live/anonichat.world/fullchain.pem; #인증서 체인 파일
ssl_certificate_key /etc/letsencrypt/live/anonichat.world/privkey.pem; # 개인 키 파일
ssl_protocols TLSv1.2 TLSv1.3; # 지원한 TLS 버전
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; # 강력한 암호화 스위트만 허용
ssl_prefer_server_ciphers off; # 재사용 가능한 세션, 캐시 설정
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # HTTPS 강제
add_header X-Frame-Options DENY always; # XSS 및 MIME 스니핑 방지
add_header X-Content-Type-Options nosniff always;
# proxy_pass 설정
location / {
proxy_pass http://spring-backend; # 내부 컨테이너로 요청 포워딩
# 클라이언트의 정보(IP, 프로토콜 등) Spring서버에 전달
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;
proxy_set_header X-Forwarded-Host $server_name;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # WebSocket 같은 프로토콜 호환성 확보
}
}
docker-compose exec nginx nginx -t
# 명령어 결과
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
docker-compose exec nginx nginx -s reload
curl -I http://anonichat.world
# 명령어 결과
HTTP/1.1 301 Moved Permanently
**Server**: nginx
**Date**: Thu, 12 Jun 2025 16:24:54 GMT
**Content-Type**: text/html
**Content-Length**: 162
**Connection**: keep-alive
**Location**: https://anonichat.world/