-
Kubernetes에서 특권 포트 피하기: NodePort + iptables 포워딩 패턴실제 경험과 인사이트를 AI와 함께 정리한 글 2025. 10. 27. 14:14
작성일: 2025년 10월 27일
카테고리: Kubernetes, Security, Networking
난이도: 중급
TL;DR
- 문제: Kong Gateway를 80/443 포트에서 실행하려면 root 권한 필요 (보안 위험)
- 해결: NodePort(30080/30443) + iptables 포워딩으로 보안, 고가용성, 무중단 배포 모두 확보
- 핵심: Linux 1024 미만 포트는 root만 바인딩 가능하지만, iptables는 커널 레벨에서 포워딩
- 결과: Pod는 root 없이 실행, 다중 Pod로 HA 구성, Rolling Update 무중단, 외부에서는 80/443으로 접속
들어가며
imprun.dev는 Kubernetes 기반 서버리스 Cloud Function 플랫폼입니다. API Gateway로 Kong을 운영하면서, 표준 HTTP/HTTPS 포트(80/443)에서 서비스하되 보안을 유지해야 하는 과제에 직면했습니다.
우리가 마주한 질문:
- ❓ root 실행: Kong을 80/443 포트에서 실행하려면 root 권한이 필요한데, 보안은?
- ❓ hostNetwork: 노드 네트워크를 직접 사용하면? 고가용성은?
- ❓ MetalLB/Cloud LB: LoadBalancer 타입이 정석인데, 우리 환경에서 가능할까?
검증 과정:
root + 특권 포트 (80/443)
- ✅ 간단하지만 Pod Security Standards: Restricted 위반
- ❌ 컨테이너 탈출 시 노드 전체 장악 가능
hostNetwork + iptables
- ✅ non-root로 실행 가능
- ❌ 노드당 1개 Pod만 가능 → 고가용성 불가
- ❌ Rolling Update 시 다운타임 발생 → 서비스 중단
NodePort + iptables ← 최종 선택
- ✅ non-root 실행 (UID 1000, 비특권 포트 8000/8443)
- ✅ 다중 Pod 실행 (최소 2개, HPA로 자동 스케일)
- ✅ Rolling Update 무중단 배포
- ✅ 추가 인프라 불필요 (MetalLB/Cloud LB 비용 0원)
환경 제약사항
- ❌ Cloud LoadBalancer: 온프레미스 환경, 월 비용 발생
- ❌ MetalLB: 제한된 리소스 환경(3노드, 4 cores, 24GB), 추가 오버헤드 부담
- ✅ NodePort + iptables: 커널 레벨 동작, 리소스 오버헤드 없음
결론:
- ✅ 보안 강화 (Pod Security Standards: Restricted 준수)
- ✅ 고가용성 확보 (최소 2개 Pod, 자동 페일오버)
- ✅ 무중단 배포 (Rolling Update, 다운타임 0초)
- ✅ 운영 비용 절감 (추가 인프라 없음)
이 글은 imprun.dev 플랫폼 구축 경험을 바탕으로, 특권 포트 문제의 본질, hostNetwork의 치명적 한계, 그리고 NodePort + iptables가 제한된 환경에서 어떻게 보안/고가용성/무중단 배포를 동시에 달성했는지 상세히 공유합니다.
배경: 특권 포트의 딜레마
Linux의 특권 포트 제약
Unix/Linux 시스템에서는 1024 미만 포트를 "특권 포트(Privileged Ports)"라고 부릅니다.
# 일반 사용자로 80 포트 바인딩 시도 $ node server.js --port 80 Error: listen EACCES: permission denied 0.0.0.0:80 # root로 실행하면 성공 $ sudo node server.js --port 80 Server listening on port 80 ✅이유:
- 보안을 위해 well-known 포트는 root만 사용 가능하도록 제한
- HTTP(80), HTTPS(443), SSH(22), DNS(53) 등이 특권 포트
- 악의적인 프로세스가 중요 포트를 점유하는 것을 방지
Kubernetes에서의 문제
Kong Gateway를 80/443에서 실행하려면?
❌ 옵션 1: root로 Pod 실행
apiVersion: v1 kind: Pod spec: securityContext: runAsUser: 0 # root containers: - name: kong ports: - containerPort: 80 # 특권 포트 - containerPort: 443문제점:
- 🚨 보안 위험: Pod가 컨테이너를 탈출하면 노드 전체 장악
- 🚨 Pod Security Standards 위반: Restricted 정책 위반
- 🚨 공격 표면 확대: root 권한으로 실행되는 프로세스는 더 큰 피해 가능
- 🚨 Best Practice 위반: Kubernetes는 non-root 실행을 권장
❌ 옵션 2: CAP_NET_BIND_SERVICE 권한 부여
apiVersion: v1 kind: Pod spec: containers: - name: kong securityContext: capabilities: add: - NET_BIND_SERVICE # 특권 포트 바인딩 권한문제점:
- ⚠️ 여전히 권한 상승: 일반 사용자가 특권 포트 사용 가능
- ⚠️ Pod Security Standards: Baseline까지만 허용 (Restricted 불가)
- ⚠️ 제한적: 특권 포트만 해결, 다른 보안 위험은 남음
해결책: NodePort + iptables 포워딩 (hostNetwork 사용 안 함!)
아키텍처
graph TB Client[외부 클라이언트<br/>curl http://imprun.dev/] IPTables[iptables PREROUTING<br/>커널 레벨 NAT] NodePort[NodePort Service<br/>30080/30443] Pod1[Kong Pod #1<br/>8000/8443<br/>UID: 1000 non-root] Pod2[Kong Pod #2<br/>8000/8443<br/>UID: 1000 non-root] Client -->|80/443 요청| IPTables IPTables -->|80→30080<br/>443→30443| NodePort NodePort -->|LoadBalancing| Pod1 NodePort -->|LoadBalancing| Pod2 style Client stroke:#2563eb,stroke-width:3px style IPTables stroke:#ea580c,stroke-width:3px style NodePort stroke:#0891b2,stroke-width:2px style Pod1 stroke:#16a34a,stroke-width:2px style Pod2 stroke:#16a34a,stroke-width:2px핵심 포인트:
- ✅ iptables: 커널 레벨에서 포워딩 (root 권한 불필요)
- ✅ NodePort: Kubernetes Service로 다중 Pod 관리
- ✅ 다중 Pod: 고가용성 + 무중단 배포
- ✅ non-root: Pod는 UID 1000으로 실행
핵심 차이점: hostNetwork를 사용하지 않습니다!
왜 hostNetwork를 사용하지 않았나?
❌ hostNetwork의 치명적 단점
# hostNetwork: true 사용 시 deployment: hostNetwork: true replicas: 1 # 강제로 1개만 가능!문제점:
노드당 하나의 Pod만 실행 가능
- 같은 노드에 두 개의 Kong Pod 불가능 (포트 충돌)
- 고가용성(HA) 불가능
Rolling Update 불가능 → 서비스 장애!
sequenceDiagram participant K8s as Kubernetes participant OldPod as 기존 Pod<br/>(80/443 점유) participant NewPod as 새 Pod participant User as 사용자 Note over K8s,User: ❌ hostNetwork: Rolling Update 시나리오 K8s->>NewPod: 1. 새 Pod 생성 시도 NewPod--xOldPod: 2. 포트 충돌! (80/443 이미 사용 중) NewPod->>NewPod: 3. Pending 상태 Note over K8s: 4. 기존 Pod를 먼저 삭제해야 함 K8s->>OldPod: 5. 기존 Pod 삭제 User--xOldPod: ⚠️ 다운타임 시작! OldPod->>OldPod: 종료 중... K8s->>NewPod: 6. 새 Pod 시작 가능 NewPod->>NewPod: 시작 중... User--xNewPod: ⚠️ 여전히 연결 불가 NewPod->>NewPod: Ready User->>NewPod: ✅ 연결 가능 (다운타임 종료) Note over K8s,User: 결과: 10-30초 다운타임 발생!재시작 시 다운타임
- Kong 설정 변경, 버전 업그레이드 등
- 무조건 서비스 중단 발생
오토스케일링 불가능
- HPA로 트래픽에 따라 Pod 수 조절 불가
- 항상 단일 Pod 고정
결론: 프로덕션 환경에서 hostNetwork는 위험!
대신 선택한 방법: NodePort + iptables
# Kong Service (NodePort) apiVersion: v1 kind: Service metadata: name: kong-proxy namespace: kong-system spec: type: NodePort ports: - name: http port: 80 targetPort: 8000 nodePort: 30080 # 노드의 30080 포트로 노출 - name: https port: 443 targetPort: 8443 nodePort: 30443 # 노드의 30443 포트로 노출이점:
- ✅ Rolling Update 가능 (무중단 배포)
- ✅ 여러 Pod 실행 가능 (고가용성)
- ✅ 오토스케일링 가능 (HPA 사용)
- ✅ Kubernetes 네이티브 (Service 리소스 사용)
왜 이 방법인가?
✅ 보안
- Kong Pod는 non-root로 실행 (UID 1000)
- 특권 포트 바인딩 권한 불필요
- Pod Security Standards Restricted 준수 가능
- 컨테이너 탈출해도 일반 사용자 권한만 획득
✅ 고가용성
- 여러 Pod 동시 실행 가능 (hostNetwork는 불가능)
- Rolling Update 무중단 배포 (hostNetwork는 다운타임 필수)
- 오토스케일링 지원 (트래픽에 따라 자동 조절)
✅ 성능
- iptables는 커널 레벨에서 동작 (오버헤드 거의 없음)
- NAT는 네트워크 스택에서 처리
- NodePort 자체는 kube-proxy가 최적화
✅ 단순성
- Kong 설정 변경 불필요 (기본 포트 8000/8443 사용)
- 표준 Kubernetes Service 사용
- iptables 규칙만 추가
실전 구현
1단계: Kong Helm Values 설정
# kong-values.yaml proxy: enabled: true type: NodePort # NodePort 사용! http: enabled: true servicePort: 80 containerPort: 8000 # 비특권 포트! nodePort: 30080 # 노드 포트 지정 tls: enabled: true servicePort: 443 containerPort: 8443 # 비특권 포트! nodePort: 30443 # 노드 포트 지정 # hostNetwork 비활성화 (중요!) deployment: hostNetwork: false # Rolling Update 및 HA 지원 # 보안: non-root 실행 securityContext: runAsUser: 1000 # kong 사용자 runAsNonRoot: true fsGroup: 1000 # 고가용성 설정 autoscaling: enabled: true minReplicas: 2 # 최소 2개 Pod (HA) maxReplicas: 5 # 최대 5개 Pod targetCPUUtilizationPercentage: 75 # 환경 변수 env: # Kong 기본 포트 명시 proxy_listen: "0.0.0.0:8000, 0.0.0.0:8443 ssl"핵심:
type: NodePort- NodePort Service 사용nodePort: 30080/30443- 노드 포트 지정hostNetwork: false- hostNetwork 사용 안 함!minReplicas: 2- 고가용성을 위해 최소 2개 PodrunAsNonRoot: true- root 실행 방지
2단계: iptables 포워딩 규칙 설정
규칙 생성
#!/bin/bash # setup-port-forwarding.sh # HTTP 포워딩: 80 → 30080 (NodePort) iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 30080 # HTTPS 포워딩: 443 → 30443 (NodePort) iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 30443 # 규칙 저장 (재부팅 후에도 유지) iptables-save > /etc/iptables/rules.v4REDIRECT vs DNAT:
REDIRECT: 로컬 포트로 포워딩 (같은 호스트)DNAT: 다른 IP로 포워딩 (다른 호스트)- 우리 경우: REDIRECT 사용 (80→30080, 443→30443)
systemd 서비스로 자동화
# /etc/systemd/system/kong-port-forward.service [Unit] Description=Kong Port Forwarding (80→8000, 443→8443) After=network.target [Service] Type=oneshot RemainAfterExit=yes ExecStart=/usr/local/bin/setup-port-forwarding.sh ExecStop=/usr/local/bin/cleanup-port-forwarding.sh [Install] WantedBy=multi-user.target활성화:
systemctl daemon-reload systemctl enable kong-port-forward.service systemctl start kong-port-forward.service3단계: 검증
# 1. iptables 규칙 확인 iptables -t nat -L PREROUTING -n -v # Chain PREROUTING (policy ACCEPT) # target prot opt source destination # REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 redir ports 30080 # REDIRECT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 redir ports 30443 # 2. Kong Service (NodePort) 확인 kubectl get svc -n kong-system kong-kong-proxy # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) # kong-kong-proxy NodePort 10.96.123.45 <none> 80:30080/TCP,443:30443/TCP # 3. Kong Pod 확인 (2개 이상 실행 중!) kubectl get pod -n kong-system # NAME READY STATUS RESTARTS AGE # kong-kong-76fc56677-72xs7 2/2 Running 0 10m # kong-kong-76fc56677-8jk2l 2/2 Running 0 10m ✅ HA! # 4. Kong이 8000/8443 포트에서 리스닝 확인 kubectl exec -n kong-system kong-kong-76fc56677-72xs7 -c proxy -- \ netstat -tlnp | grep kong # tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 1/nginx # tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN 1/nginx # 5. 외부에서 80/443 포트 접속 테스트 curl -I http://<node-ip>/ # HTTP/1.1 404 Not Found ✅ (Kong 응답) curl -I https://<node-ip>/ # HTTP/2 404 ✅ (Kong TLS 응답) # 6. Pod가 non-root로 실행 중인지 확인 kubectl exec -n kong-system kong-kong-76fc56677-72xs7 -c proxy -- id # uid=1000(kong) gid=1000(kong) groups=1000(kong) ✅ # 7. Rolling Update 테스트 (무중단!) kubectl set image deployment/kong-kong -n kong-system \ proxy=kong:3.10 ingress-controller=kong/kubernetes-ingress-controller:3.10 # deployment.apps/kong-kong image updated # Pod가 하나씩 교체됨 (다운타임 없음!) kubectl rollout status deployment/kong-kong -n kong-system # Waiting for deployment "kong-kong" rollout to finish: 1 out of 2 new replicas... # Waiting for deployment "kong-kong" rollout to finish: 1 old replicas... # deployment "kong-kong" successfully rolled out ✅
다른 선택지와의 비교
옵션 비교표
방식 보안 고가용성 무중단배포 복잡도 권장도 root + 특권 포트 ❌ 매우 낮음 ✅ ✅ ⭐ 매우 쉬움 ❌ 비권장 CAP_NET_BIND_SERVICE ⚠️ 낮음 ✅ ✅ ⭐⭐ 쉬움 ⚠️ 제한적 hostNetwork + iptables ✅ 높음 ❌ 단일 Pod ❌ 다운타임 ⭐⭐ 쉬움 ❌ 비권장 NodePort + iptables ✅ 높음 ✅ 다중 Pod ✅ 무중단 ⭐⭐⭐ 보통 ✅ 권장 NodePort (단독) ✅ 높음 ✅ ✅ ⭐⭐ 쉬움 ⚠️ 30000+ 포트 LoadBalancer (MetalLB) ✅ 높음 ✅ ✅ ⭐⭐⭐⭐ 복잡 ✅ 권장 (프로덕션) LoadBalancer (클라우드) ✅ 높음 ✅ ✅ ⭐⭐ 쉬움 ✅ 권장 (클라우드) 왜 NodePort + iptables를 선택했나?
1. 보안 우선
# Pod Security Standards Compliance apiVersion: v1 kind: Pod metadata: labels: pod-security.kubernetes.io/enforce: restricted ✅ spec: securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: kong securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALLiptables 방식만 Restricted 정책을 완전히 준수 가능
2. 환경 제약사항
우리 환경:
❌ 클라우드 LoadBalancer 없음
- 온프레미스 환경 (클라우드 아님)
- 클라우드 LB는 가장 쉽지만 월 비용 발생 💰
❌ MetalLB 부적합
- 추가 리소스 필요 (L2 모드: ARP, BGP 모드: 라우터 설정)
- 열악한 노드 환경: 제한된 CPU/메모리
- MetalLB 자체 Pod 리소스 + 네트워크 오버헤드
- 3노드 클러스터에서 추가 부담
✅ NodePort + iptables는 즉시 사용 가능
- 추가 인프라 불필요
- 추가 리소스 소비 없음 (커널 레벨 동작)
- 설정 간단 (iptables 규칙 2개)
3. 고가용성 + 무중단 배포
hostNetwork 방식의 문제:
sequenceDiagram participant K8s as Kubernetes participant OldPod as 기존 Pod (80/443) participant NewPod as 새 Pod participant User as 사용자 Note over K8s,User: ❌ hostNetwork: 다운타임 발생 K8s->>NewPod: 새 Pod 생성 시도 NewPod--xOldPod: 포트 충돌! K8s->>OldPod: 기존 Pod 삭제 User--xOldPod: ⚠️ 다운타임! K8s->>NewPod: 새 Pod 시작 User--xNewPod: ⚠️ 여전히 불가 NewPod->>User: ✅ Ready (10-30초 후)NodePort 방식의 해결:
sequenceDiagram participant K8s as Kubernetes participant Pod1 as Pod #1 (기존) participant Pod2 as Pod #2 (기존) participant Pod3 as Pod #3 (신규) participant Service as NodePort Service participant User as 사용자 Note over K8s,User: ✅ NodePort: 무중단 Rolling Update User->>Service: 계속 요청 Service->>Pod1: 트래픽 분산 Service->>Pod2: 트래픽 분산 K8s->>Pod3: 1. 새 Pod 생성 (포트 충돌 없음!) Pod3->>Pod3: Ready Service->>Pod3: 트래픽 분산 시작 Note over Service: Pod1, Pod2, Pod3 모두 서빙 K8s->>Pod1: 2. 기존 Pod #1 삭제 Service->>Pod2: 트래픽 분산 Service->>Pod3: 트래픽 분산 Note over Service: Pod2, Pod3가 서빙 (다운타임 없음!) K8s->>Pod1: Pod #4 생성 (Pod #2 교체) Note over K8s,User: 결과: 다운타임 0초!장점:
- ✅ 다중 Pod 실행 (최소 2개로 HA 구성)
- ✅ Rolling Update (다운타임 0초)
- ✅ HPA 지원 (트래픽 급증 시 자동 스케일)
4. 성능
Client → 80 포트 요청 ↓ (iptables NAT, 커널 레벨) 30080 포트로 포워딩 ↓ (kube-proxy, iptables 또는 IPVS) Kong Service (LoadBalancing) ↓ Kong Pod (2개 이상)홉 수: 1-2 (iptables + kube-proxy)
비교:
- NodePort + iptables: 1-2 홉 (우리 선택)
- LoadBalancer: 1-2 홉 (동일, 하지만 비용 or 리소스)
- hostNetwork: 0 홉 (하지만 HA 불가능!)
Trade-off: 1-2 홉 추가되지만, HA와 무중단 배포 얻음
5. 단순성
필요한 것:
- NodePort Service (Helm values)
- iptables 규칙 2개 (80→30080, 443→30443)
- systemd 서비스 1개 (자동화)
불필요한 것:
- ❌ MetalLB 설치 및 설정
- ❌ 클라우드 LoadBalancer 비용
- ❌ 복잡한 네트워크 설정
상세 설명: iptables NAT 동작 원리
NAT (Network Address Translation) 테이블
# NAT 테이블의 체인 구조 iptables -t nat -L -n -v # Chain PREROUTING (패킷이 라우팅되기 전) # Chain INPUT (로컬 프로세스로 향하는 패킷) # Chain OUTPUT (로컬 프로세스에서 나가는 패킷) # Chain POSTROUTING (패킷이 라우팅된 후)우리가 사용하는 체인: PREROUTING
패킷 처리 흐름
1. 외부 클라이언트: curl http://node-ip/ ↓ 2. 패킷 도착: dst=node-ip:80 ↓ 3. PREROUTING 체인 진입 ↓ 4. iptables 규칙 매칭: -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000 ↓ 5. 목적지 포트 변경: dst=node-ip:8000 ↓ 6. 라우팅 결정: 로컬 프로세스 (Kong) ↓ 7. Kong (8000 포트 리스닝) 처리 ↓ 8. 응답: src=node-ip:80 (원래 포트로!)핵심: 클라이언트는 80 포트로 요청했다고 인식, 서버는 8000 포트에서 처리
REDIRECT vs DNAT 상세 비교
# REDIRECT: 로컬 포워딩 (우리가 사용) iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000 # 효과: 같은 호스트의 8000 포트로 포워딩 # DNAT: 원격 포워딩 iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.1.100:8000 # 효과: 다른 서버(10.0.1.100)의 8000 포트로 포워딩REDIRECT 장점:
- ✅ 간결한 문법 (포트만 지정)
- ✅ 로컬 최적화 (커널이 빠른 경로 사용)
- ✅ IP 변경 불필요 (호스트 IP 유지)
트러블슈팅
Case 1: iptables 규칙이 적용 안 됨
증상:
curl http://node-ip/ # Connection refused확인:
# 1. iptables 규칙 존재 여부 iptables -t nat -L PREROUTING -n # 규칙이 없음? # 2. 규칙 수동 추가 iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000 # 3. 테스트 curl http://node-ip/ # 성공!영구 적용:
# Ubuntu/Debian iptables-save > /etc/iptables/rules.v4 systemctl enable netfilter-persistent # CentOS/RHEL iptables-save > /etc/sysconfig/iptables systemctl enable iptablesCase 2: Kong이 8000 포트에서 리스닝 안 함
증상:
netstat -tlnp | grep 8000 # (아무것도 없음)원인:
- Kong 설정에서
proxy_listen잘못 설정 - Kong Pod가 시작 안 됨
해결:
# Kong Helm values env: proxy_listen: "0.0.0.0:8000, 0.0.0.0:8443 ssl" # 또는 KONG_PROXY_LISTEN: "0.0.0.0:8000, 0.0.0.0:8443 ssl"Case 3: hostNetwork 포트 충돌
증상:
kubectl get pod -n kong-system # NAME READY STATUS RESTARTS AGE # kong-kong-xxx 0/2 Pending 0 5m kubectl describe pod -n kong-system kong-kong-xxx # Events: # Warning FailedScheduling didn't have free ports for the requested pod ports원인:
- hostNetwork 사용 시 노드당 하나의 Pod만 가능
- 다른 프로세스가 8000/8443 포트 점유 중
해결:
# 1. 포트 점유 확인 netstat -tlnp | grep -E ':(8000|8443)' # tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 12345/some-process # 2. 점유 프로세스 종료 kill 12345 # 3. Pod 재시작 kubectl delete pod -n kong-system kong-kong-xxxCase 4: HTTPS가 작동 안 함
증상:
curl https://node-ip/ # curl: (35) error:1408F10B:SSL routines:ssl3_get_record:wrong version number원인:
- iptables 규칙에 HTTPS(443) 누락
- Kong이 8443에서 TLS 리스닝 안 함
해결:
# 1. HTTPS 포워딩 규칙 추가 iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443 # 2. Kong TLS 설정 확인 kubectl exec -n kong-system kong-kong-xxx -c proxy -- \ kong config | grep ssl # ssl_cert = /path/to/cert.pem # ssl_cert_key = /path/to/key.pem
운영 고려사항
모니터링
# 1. iptables 규칙 통계 watch -n 1 'iptables -t nat -L PREROUTING -n -v' # Chain PREROUTING (policy ACCEPT 12345 packets, 678K bytes) # pkts bytes target prot opt in out source destination # 5678 234K REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 # 2. Kong 연결 수 모니터링 netstat -an | grep :8000 | grep ESTABLISHED | wc -l # 3. Kong 메트릭 (Prometheus) curl http://localhost:8001/metrics # kong_http_requests_total # kong_latency_ms백업 및 복구
# 규칙 백업 iptables-save > /backup/iptables-$(date +%Y%m%d).rules # 규칙 복구 iptables-restore < /backup/iptables-20251027.rules # 모든 NAT 규칙 제거 (위험!) iptables -t nat -F PREROUTING # 재적용 iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8000 iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8443보안 강화
# 1. 특정 인터페이스만 허용 iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8000 # 2. 특정 소스 IP만 허용 iptables -t nat -A PREROUTING -s 10.0.0.0/8 -p tcp --dport 80 -j REDIRECT --to-port 8000 # 3. Rate limiting (연결 수 제한) iptables -A INPUT -p tcp --dport 8000 -m connlimit --connlimit-above 100 -j REJECT # 4. DDoS 방어 (SYN flood) iptables -A INPUT -p tcp --syn --dport 8000 -m limit --limit 10/s --limit-burst 20 -j ACCEPT iptables -A INPUT -p tcp --syn --dport 8000 -j DROP
향후 개선: MetalLB로 전환
현재 (NodePort + iptables) vs 미래 (MetalLB)
항목 NodePort + iptables MetalLB 설정 복잡도 낮음 (NodePort + iptables 2개 규칙) 중간 (MetalLB 설치 + IP 풀 설정) 고가용성 ✅ 가능 (다중 Pod) ✅ 가능 (다중 노드 LB) Rolling Update ✅ 무중단 ✅ 무중단 오토스케일링 ✅ 가능 (HPA) ✅ 가능 (HPA) Kubernetes 네이티브 ⚠️ 부분적 (NodePort + OS 레벨 iptables) ✅ 완전 (LoadBalancer) 포트 관리 ⚠️ iptables 수동 관리 필요 ✅ 자동 (LoadBalancer) 리소스 오버헤드 ✅ 없음 (커널 레벨) ⚠️ MetalLB Pod 리소스 필요 MetalLB 전환 계획
# 1. MetalLB 설치 kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.0/config/manifests/metallb-native.yaml # 2. IP 주소 풀 설정 apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: production namespace: metallb-system spec: addresses: - 192.168.1.100-192.168.1.110 # 3. L2 Advertisement apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: default namespace: metallb-system # 4. Kong Service를 LoadBalancer로 변경 (NodePort에서 전환) proxy: type: LoadBalancer externalTrafficPolicy: Local # 5. iptables 규칙 제거 (더 이상 불필요) iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 30080 iptables -t nat -D PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 30443 iptables-save > /etc/iptables/rules.v4전환 시기:
- ✅ Kubernetes 네이티브 관리 필요 시 (iptables 수동 관리 제거)
- ✅ 노드 환경 개선 후 (리소스 여유 확보)
- ✅ 다중 노드 로드밸런싱 필요 시 (현재는 단일 노드 iptables)
- ✅ 인프라 복잡도 감수 가능 시 (MetalLB 설치 및 운영)
현재 NodePort + iptables를 유지하는 이유:
- ✅ 이미 고가용성 확보 (다중 Pod)
- ✅ 이미 무중단 배포 가능 (Rolling Update)
- ✅ 이미 오토스케일링 가능 (HPA)
- ✅ 추가 리소스 불필요 (열악한 노드 환경)
마무리
핵심 요약
- 특권 포트 문제: Linux 1024 미만 포트는 root만 바인딩 가능
- 보안 우선: root 실행은 큰 보안 위험, non-root 필수
- NodePort + iptables: NodePort(30080/30443) + 커널 레벨 iptables 포워딩 (80→30080, 443→30443)
- 비교 우위: 보안(non-root) + 고가용성(다중 Pod) + 무중단 배포 + 단순성(2개 규칙)
- 실무 선택: 제한된 리소스 환경에서 추가 인프라(MetalLB/Cloud LB) 없이 즉시 사용 가능
언제 사용하나?
NodePort + iptables 포워딩 권장:
- ✅ 온프레미스 환경
- ✅ 제한된 리소스 환경 (MetalLB 부담)
- ✅ 추가 인프라 비용 회피 (Cloud LB 비용 X)
- ✅ 즉시 배포 필요 (추가 설치 불필요)
- ✅ 보안 중시 (non-root + Restricted Pod Security)
- ✅ 고가용성 필요 (다중 Pod 지원)
- ✅ 무중단 배포 필요 (Rolling Update)
MetalLB/LoadBalancer 권장:
- ✅ Kubernetes 네이티브 관리 선호 (iptables 수동 관리 제거)
- ✅ 다중 노드 로드밸런싱 필요
- ✅ 리소스 여유 있는 환경
- ✅ 인프라 복잡도 감수 가능
- ✅ 클라우드 환경 (Cloud LB 사용 가능)
NodePort 권장:
- ✅ 간단한 테스트
- ✅ 포트 범위 30000-32767 허용
- ✅ 임시 환경
실제 적용 결과
imprun.dev 환경:
- ✅ Kong non-root 실행 (UID 1000)
- ✅ Pod Security Standards Restricted 준수
- ✅ NodePort Service + iptables 2개 규칙으로 80/443 포워딩
- ✅ 고가용성 확보 (최소 2개 Pod, HPA로 자동 스케일)
- ✅ Rolling Update 무중단 배포 (Kong 업데이트 시 다운타임 0초)
- ✅ 추가 비용 0원 (MetalLB/Cloud LB 불필요)
- ✅ 추가 리소스 소비 없음 (커널 레벨 iptables)
- ✅ 성능 오버헤드 무시할 수준 (<1%)
운영 경험:
- 설정 시간: 15분 (NodePort 설정 + iptables 규칙 2개)
- 재부팅 테스트: 정상 (systemd 자동 적용)
- Rolling Update: 무중단 (Pod 교체 시 다운타임 없음)
- 트러블슈팅: 거의 없음
- 만족도: 매우 높음 😊
참고 자료
공식 문서
관련 글
실습 자료
태그: #Kubernetes #Security #iptables #Kong #Networking #DevOps
"보안과 고가용성을 모두 잡는 방법: NodePort + iptables 포워딩"
🤖 이 블로그는 실제 프로덕션 환경에서 NodePort + iptables 포워딩을 운영한 경험을 바탕으로 작성되었습니다.
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
Monaco Editor "TextModel got disposed" 에러 완벽 해결 가이드 (0) 2025.10.27 Kubernetes Gateway API 실전 가이드: Kong Ingress에서 표준 API로 전환하기 (1) 2025.10.27 Kubernetes Gateway API 실전 가이드: Kong Ingress에서 표준 API로 전환하기 (0) 2025.10.27 frontend/CLAUDE.md (0) 2025.10.27 Claude AI와 함께하는 프론트엔드 개발: imprun.dev의 CLAUDE.md 가이드 공개 (0) 2025.10.27