상황 개요
Kubernetes 환경을 구축하면서 게이트웨이 서버에서 내부 서비스들에 접근하려고 했는데, 서비스명 기반 라우팅이 작동하지 않는 문제가 발생했습니다. NodePort를 활용한 포트 기반 접근으로 해결한 과정을 공유합니다.
환경 정보
- 플랫폼: Kubernetes
- 구성: 게이트웨이 서버 + Kubernetes 클러스터
- 목표: 게이트웨이에서 클러스터 내부 서비스로 트래픽 라우팅
문제 상황
Kubernetes 클러스터 구축 후 게이트웨이 서버에서 내부 서비스들을 서비스명으로 접근할 수 없었습니다.
# 게이트웨이에서 시도했던 것들
curl http://my-service:8080 # 실패
curl http://my-service.default:8080 # 실패
nslookup my-service # 실패
하지만 클러스터 내부 Pod들끼리는 서비스명 기반 통신이 정상적으로 작동했습니다.
원인 분석
문제의 핵심
게이트웨이 서버가 Kubernetes 클러스터 외부에 위치해서 클러스터 내부 DNS(CoreDNS)를 사용할 수 없었던 것이 원인이었습니다.
진단 과정
1단계: 서비스명 해결 테스트
# 게이트웨이에서
nslookup my-service # 실패
dig my-service.default.svc.cluster.local # 실패
# 클러스터 내부 Pod에서
kubectl exec -it pod-name -- nslookup my-service # 성공
2단계: 서비스 상태 확인
# 서비스 목록 및 타입 확인
kubectl get svc
kubectl get svc my-service -o yaml
# 엔드포인트 확인
kubectl get endpoints my-service
3단계: 현재 서비스 타입 확인
kubectl describe svc my-service
# Type: ClusterIP (클러스터 내부에서만 접근 가능)
4단계: NodePort로 변경 후 테스트
# 게이트웨이에서 노드IP + NodePort로 접근
curl http://192.168.1.101:30080 # 성공!
해결 방법
Kubernetes의 NodePort 서비스 타입을 활용해 클러스터 외부에서 포트를 통해 직접 접근할 수 있도록 설정했습니다.
서비스 타입 변경
기존 ClusterIP 서비스
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP # 클러스터 내부에서만 접근 가능
ports:
- port: 8080
targetPort: 8080
selector:
app: my-app
NodePort로 변경
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort # 노드 포트를 통해 외부 접근 가능
ports:
- port: 8080
targetPort: 8080
nodePort: 30080 # 모든 노드의 이 포트로 접근 가능
selector:
app: my-app
여러 서비스 NodePort 설정 예시
# API 서비스
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
type: NodePort
ports:
- port: 8080
targetPort: 8080
nodePort: 30081
selector:
app: api-app
---
# Frontend 서비스
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30082
selector:
app: frontend-app
게이트웨이 Nginx 설정
# /etc/nginx/sites-available/k8s-services
upstream api_backend {
server 192.168.1.101:30081; # 노드1 IP + NodePort
server 192.168.1.102:30081; # 노드2 IP + NodePort
server 192.168.1.103:30081; # 노드3 IP + NodePort
least_conn;
}
upstream frontend_backend {
server 192.168.1.101:30082;
server 192.168.1.102:30082;
server 192.168.1.103:30082;
least_conn;
}
# API 라우팅
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://api_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
}
}
# Frontend 라우팅
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://frontend_backend;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
작동 원리
기존 시도했던 방식 (실패)
게이트웨이 → 서비스명(my-service) → ❌ DNS 해결 실패
실제 해결 방식 (성공)
게이트웨이 → Nginx → 노드IP:NodePort → kube-proxy → Pod
- 게이트웨이: 클라이언트 요청 수신
- Nginx: upstream 설정에 따라 로드밸런싱
- NodePort: 모든 노드에서 동일한 포트로 서비스 접근 가능
- kube-proxy: 노드 내에서 해당 Pod으로 트래픽 전달
- Pod: 실제 애플리케이션에서 요청 처리
kubectl을 활용한 운영
서비스 상태 모니터링
# 서비스 목록과 NodePort 확인
kubectl get svc -o wide
# 특정 서비스의 엔드포인트 확인
kubectl get endpoints my-service
# 서비스 상세 정보
kubectl describe svc my-service
동적 NodePort 정보 수집
#!/bin/bash
# get-nodeports.sh
# NodePort 정보를 가져와서 Nginx upstream 설정 생성
SERVICES=("api-service" "frontend-service")
NODES=$(kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}')
for svc in "${SERVICES[@]}"; do
NODEPORT=$(kubectl get svc $svc -o jsonpath='{.spec.ports[0].nodePort}')
echo "upstream ${svc//-/_}_backend {"
for node in $NODES; do
echo " server $node:$NODEPORT;"
done
echo " least_conn;"
echo "}"
echo
done
헬스체크 스크립트
#!/bin/bash
# health-check.sh
# NodePort 서비스 헬스체크
check_service() {
local service=$1
local nodeport=$2
local nodes=$(kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="InternalIP")].address}')
for node in $nodes; do
if curl -f -s http://$node:$nodeport/health > /dev/null; then
echo "✓ $service on $node:$nodeport is healthy"
else
echo "✗ $service on $node:$nodeport is unhealthy"
fi
done
}
check_service "api-service" "30081"
check_service "frontend-service" "30082"
추가 고려사항
보안 측면
# NodePort 범위 확인 (기본: 30000-32767)
kubectl cluster-info dump | grep service-node-port-range
# 특정 노드에서만 접근하도록 방화벽 설정
ufw allow from 192.168.1.0/24 to any port 30080:30090
LoadBalancer 타입 대안 (클라우드 환경)
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: LoadBalancer # 클라우드 LB 자동 프로비저닝
ports:
- port: 80
targetPort: 8080
selector:
app: my-app
Ingress Controller 활용 방안
# 추후 개선 시 고려할 수 있는 Ingress 설정
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
장점과 한계
NodePort 방식의 장점
- 간단한 설정: 추가 컴포넌트 없이 외부 접근 가능
- 안정성: 클러스터 DNS 의존성 제거
- 투명성: 기존 게이트웨이 설정 재활용 가능
- 디버깅: 문제 발생 시 원인 파악이 쉬움
한계점
- 포트 관리: NodePort 범위 내에서 포트 할당 필요
- 보안: 모든 노드에서 포트가 열림
- 확장성: 노드 추가/제거 시 설정 업데이트 필요
결론
Kubernetes 환경에서 게이트웨이가 클러스터 외부에 있을 때 서비스명 기반 접근이 불가능한 문제를 NodePort 서비스 타입으로 해결할 수 있었습니다.
핵심 포인트
- 클러스터 경계 이해: 내부 DNS vs 외부 접근의 차이
- 서비스 타입 선택: ClusterIP → NodePort 변경
- 포트 기반 라우팅: Nginx upstream을 통한 로드밸런싱
- 운영 관리: kubectl을 활용한 모니터링과 자동화
처음에는 서비스명이 왜 안 되나 싶었는데, Kubernetes의 네트워크 구조를 이해하고 나니 NodePort가 이런 상황에 딱 맞는 해결책이었네요.
'TIL' 카테고리의 다른 글
| Nginx로 소켓 연결하기 / Upgrade가 필요한 이유 (0) | 2025.09.24 |
|---|---|
| L4 장비 l3 장비 이거 뭘까? 가볍게 정리! (0) | 2025.09.21 |
| Nginx 설정 팁 (0) | 2025.09.11 |
| Socket Admin UI 페이지 보는법 (0) | 2025.09.11 |
| Socket.IO에서 JWT 만료 시 아예 연결을 차단하는 방법 (0) | 2025.09.05 |