ClusterIP 서비스에 externarlIPs 를 지정하면, 추가적인 IP 로도 ClusterIP 서비스로의 접근이 가능하게 된다. spec.type: ExternalIP 로 지정하는게 아닌, ClusterIP 로 지정하고, spec.externalIPs 를 지정한다. 단, externalIPs 에는 트래픽을 보내고자 하는 node 의 IP 를 지정해야 한다. externalIPs 를 지정하면, 쿠버네티스 클러스터 외부에서도 클러스터 내부 node IP 로 통신이 가능하며, 파드에 대한 요청도 분산되어 처리된다.
| 필드 | 설정값 |
| spec.externalIPs | 외부로 노출할 쿠버네티스 노드 IP 주소 |
| spec.ports[].port | ClusterIP 서비스에서 수신할 포트 번호 |
| spec.ports[].targetPort | 목적지 파드 포트 번호 |
서비스를 생성하기 위해서 먼저 node 의 IP 를 조회한다.
$ kubectl get nodes -o custom-columns='NAME:{.metadata.name},IP:{.status.addresses[?(@.type=="InternalIP")].address}'
NAME IP
desktop-control-plane 172.18.0.6,fc00:f853:ccd:e793::6
desktop-worker 172.18.0.4
desktop-worker2 172.18.0.3
아래 매니페스트를 사용하여 externalIPs 를 지정한 ClusterIP 서비스를 생성하자. externalIPs 에는 위에서 조회한 노드의 IP 중 클러스터 외부에서 접속 가능하게 할 IP를 기재한다.
apiVersion: v1
kind: Service
metadata:
name: sample-externalip
spec:
type: ClusterIP
externalIPs:
- 172.18.0.3 # node 의 IP
- 172.18.0.4 # node 의 IP
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
selector:
app: sample-app
생성한 서비스를 조회해보자
$ kubectl get services
# sample-externalip 서비스의 EXTERNAL-IP 에 지정한 node IP 들이 출력됨을 확인한다.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 98d
sample-externalip ClusterIP 10.96.37.106 172.18.0.3,172.18.0.4 8080/TCP 14s
$ kubectl describe service sample-externalip
Name: sample-externalip
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=sample-app
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.37.106
IPs: 10.96.37.106
External IPs: 172.18.0.3,172.18.0.4
Port: http-port 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.244.2.118:80,10.244.2.117:80,10.244.1.133:80
Session Affinity: None
External Traffic Policy: Cluster
Internal Traffic Policy: Cluster
Events: <none>
클러스터 내부 파드에서 dig 를 이용해서 클러스터 내부 DNS 에서 반환하는 서비스의 IP 주소를 확인해보면, 위에서 생성한 서비스의 IP 주소임을 확인할 수 있다.
$ kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-externalip.default.svc.cluster.local
...
;; QUESTION SECTION:
;sample-externalip.default.svc.cluster.local. IN A
;; ANSWER SECTION:
sample-externalip.default.svc.cluster.local. 30 IN A 10.96.37.106
...
pod "testpod" deleted
이렇게 서비스를 생성하면 지정한 노드의 iptables 규칙에 kube-proxy 가 아래와 같은 규칙을 추가한다.(일부만 표시)
...
# 파드 IP 가 직접 등록되어있다.
Chain KUBE-SEP-LNFMA4JXKHGYU5GA (1 references)
0 0 KUBE-MARK-MASQ 0 -- * * 10.244.2.118 0.0.0.0/0 /* default/sample-externalip:http-port */
Chain KUBE-SEP-PIHNOKMVC3JPXE5V (1 references)
0 0 KUBE-MARK-MASQ 0 -- * * 10.244.2.117 0.0.0.0/0 /* default/sample-externalip:http-port */
...
Chain KUBE-SERVICES (2 references)
...
# ClusterIP 서비스 IP/Port 와 설정된 ExternalIPs(즉, 노드 IP)가 등록되어있다.
0 0 KUBE-SVC-PCX4Y6UD55K35SPY 6 -- * * 0.0.0.0/0 10.96.37.106 /* default/sample-externalip:http-port cluster IP */ tcp dpt:8080
0 0 KUBE-EXT-PCX4Y6UD55K35SPY 6 -- * * 0.0.0.0/0 172.18.0.3 /* default/sample-externalip:http-port external IP */ tcp dpt:8080
0 0 KUBE-EXT-PCX4Y6UD55K35SPY 6 -- * * 0.0.0.0/0 172.18.0.4 /* default/sample-externalip:http-port external IP */ tcp dpt:8080
...
# 파드로 로드밸런싱하는 방식이 지정되어있다.
Chain KUBE-SVC-PCX4Y6UD55K35SPY (2 references)
0 0 KUBE-MARK-MASQ 6 -- * * !10.244.0.0/16 10.96.37.106 /* default/sample-externalip:http-port cluster IP */ tcp dpt:8080
0 0 KUBE-SEP-HNM32ADNEKRGXVEK 0 -- * * 0.0.0.0/0 0.0.0.0/0 /* default/sample-externalip:http-port -> 10.244.1.133:80 */ statistic mode random probability 0.33333333349
0 0 KUBE-SEP-PIHNOKMVC3JPXE5V 0 -- * * 0.0.0.0/0 0.0.0.0/0 /* default/sample-externalip:http-port -> 10.244.2.117:80 */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-LNFMA4JXKHGYU5GA 0 -- * * 0.0.0.0/0 0.0.0.0/0 /* default/sample-externalip:http-port -> 10.244.2.118:80 */
...
이 iptables 규칙은 해당 노드의 리눅스 커널이 "직접" 참조하며, 이를 바탕으로 패킷을 필터링하고 NAT 를 직접 수행한다. kube-proxy 파드가 NAT를 수행하는 것이 "아님"에 유의하자. kube-proxy 는 서비스 설정에 따라 노드에 iptables/IPVS 규칙을 생성할 뿐이다.
참고로, 각 노드에 실행중인 kube-proxy 파드는 아래 커맨드로 확인 가능하다.
# kube-proxy 파드 확인
$ kubectl get pods -n kube-system -o wide -l k8s-app=kube-proxy
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-proxy-6pn2g 1/1 Running 1 (52d ago) 126d 172.18.0.4 desktop-worker <none> <none>
kube-proxy-c8r4l 1/1 Running 1 (52d ago) 126d 172.18.0.3 desktop-worker2 <none> <none>
kube-proxy-l52jn 1/1 Running 0 126d 172.18.0.3 desktop-control-plane <none> <none>
참고로, 노드의 iptables 를 아래 커맨드로 조회할 수 있다.
# 특정 노드의 kube-proxy 파드에 접속
$ kubectl -n kube-system exec -it kube-proxy-xxxxx -- sh
# 그 안에서 iptables 조회
$ iptables -t nat -L -n -v | grep KUBE
노드에 해당 포트로 패킷이 인입되면 노드의 리눅스 커널에서 iptables 에 명시된 NAT 규칙에 따라 목적지 Pod 로 NAT 를 수행한다. ClusterIP 서비스가 런타임(패킷이 들어오는 시점)에 직접 NAT 를 수행하는 것이 아니라는 점에 유의해야 한다. ClusterIP는 로드밸런싱 로직을 정의할 뿐이다. 즉, 실제 NAT는 커널이 수행하지만, "어느 파드로 NAT할지 결정하는" 서비스 로직을 ClusterIP 설정을 기반으로 kube-proxy 가 미리 ClusterIP 체인 로직으로 만들어둔다.(iptables 모드. selector에 매칭되는 각 파드가 대상임.)
정리하면, 아래와 같다.
[1] 외부 클라이언트 → 172.18.0.3:8080
↓
[2] 노드 172.18.0.3의 kube-proxy가 생성한 iptables/IPVS 규칙이 매칭됨
(노드 리눅스 커널 네트워크 계층에서 수행됨)
↓
[3] 해당 규칙이 패킷을 ClusterIP의 externalIPs 로 NAT 수행
(노드 리눅스 커널 네트워크 계층에서 수행됨)
↓
[4] ClusterIP 규칙에 따라 파드 중 하나(예: 10.244.1.133:80)로 로드밸런싱
(노드 리눅스 커널 네트워크 계층에서 수행됨)
↓
[5] 파드 내 컨테이너 프로세스가 80포트에서 응답
단, 무조건 클러스터 외부에서 접속할 수 있는건 아니고, 그 노드에 도달할 수 있는 외부 네트워크(예: 같은 사설망. LAN, VPC 등)에 한해서 접속가능하다.
'Kubernetes' 카테고리의 다른 글
| [Kubernetes] 서비스에 할당 가능한 IP 대역 조회 (0) | 2025.10.19 |
|---|---|
| [Kubernetes] ClusterIP 서비스 이해 (0) | 2025.10.19 |
| [Kubernetes] 서비스 디스커버리와 DNS 설정 (1) | 2025.10.14 |
| [Kubernetes] Multi Port ClusterIP, 포트 이름을 사용한 참조 (0) | 2025.10.12 |
| [Kubernetes] 클러스터 네트워크 개괄 (0) | 2025.10.12 |