KANS3 - Kubernetes Service: ClusterIP, NodePort

서비스 오브젝트

Kubernetes에 배포된 POD에 접근하기 위해서는 서비스 오브젝트를 통해서 가능합니다. 서비스 오브젝트는 4가지 타입으로 ClusterIP, NodePort, LoadBlancer, ExternalName 유형이 있습니다. 서비스를 사용하는 이유는 백엔드에 배치된 POD는 기본적으로 휘발성이 있는 것으로 POD의 Sacle in/out, 노드의 변동에 따라 추가, 삭제, 재생성이 발생하여 IP가 재할당되어 항상 동일한 IP를 가지지 못한다는 점에 있습니다. 이러한 문제를 On-Prem에서 L4 장비를 사용하여 구성하듯이 K8s 내부에서도 서비스 오브젝트로 구성하여 POD - POD 간 통신 외부 - POD간 통신에서도 POD의 변동사항이 발생해도 서비스를 유지하는데 사용합니다. 서비스는 는 On-prem에서 L4 장비와 같이 백엔드 정보를 가지고 있어야하는데, 백엔드(POD IP) 정보는 Endpoint(혹은 EndpointSlice)로 POD의 IP를 서비스 오브젝트와 연결하여 사용한다.

ClusterIP

ClusterIP는 K8s 클러스터 내부에서 사용하는 Service 오브젝트의 유형입니다.

apiVersion: v1
kind: Service
metadata:
  name: sample-service
spec:
  port:
  - name: http
    port: 80
    protocol: tcp
    targetPort: 8080
  sessionAffinity: None
  selector:
    app: sample-service
  type: ClusterIP

이와 유사하게 샘플을 배포하고 테스트 해보면, POD의 수량에 따라 1/n으로 나누어져서 통신이 들어가는 것을 확인할수 있습니다.

for pod in $WEBPOD1 $WEBPOD2 $WEBPOD3; do kubectl exec -it net-pod -- curl -s $pod | grep Hostname; done
Hostname: webpod1
Hostname: webpod2
Hostname: webpod3

iptables 정책은 다음과 같이 추가됩니다.

PREROUTING → KUBE-SERVICES → KUBE-SVC-### → KUBE-SEP-#<파드1> ,  KUBE-SEP-#<파드2> ,  KUBE-SEP-#<파드3>

그리고 iptables에서 룰 확인시 다음과 같이 확인이 가능합니다.

root@myk8s-control-plane:~# iptables -v --numeric --table nat --list PREROUTING
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    # K8s Service 오브젝트와 관련된 룰
   95  5749 KUBE-SERVICES  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
    2   169 DOCKER_OUTPUT  0    --  *      *       0.0.0.0/0            192.168.65.254
# K8s Service 오브젝트와 관련된 룰 확인
root@myk8s-control-plane:~# iptables -v --numeric --table nat --list KUBE-SERVICES
Chain KUBE-SERVICES (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 KUBE-SVC-NPX46M4PTMTKRN6Y  6    --  *      *       0.0.0.0/0            10.200.1.1           /* default/kubernetes:https cluster IP */ tcp dpt:443
    0     0 KUBE-SVC-ERIFXISQEP7F7OF4  6    --  *      *       0.0.0.0/0            10.200.1.10          /* kube-system/kube-dns:dns-tcp cluster IP */ tcp dpt:53
    0     0 KUBE-SVC-JD5MR3NA4I4DYORP  6    --  *      *       0.0.0.0/0            10.200.1.10          /* kube-system/kube-dns:metrics cluster IP */ tcp dpt:9153
    0     0 KUBE-SVC-TCOU7JCQXEZGVUNU  17   --  *      *       0.0.0.0/0            10.200.1.10          /* kube-system/kube-dns:dns cluster IP */ udp dpt:53
    # 신규로 생성한 svc-webport 서비스에 관한 룰
    0     0 KUBE-SVC-KBDEBIL6IU6WL7RF  6    --  *      *       0.0.0.0/0            10.200.1.61          /* default/svc-clusterip:svc-webport cluster IP */ tcp dpt:9000
 2213  133K KUBE-NODEPORTS  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
# 신규로 생성한 svc-webport 서비스에 관한 룰 확인
root@myk8s-control-plane:~# iptables -v --numeric --table nat --list KUBE-SVC-KBDEBIL6IU6WL7RF
Chain KUBE-SVC-KBDEBIL6IU6WL7RF (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 KUBE-MARK-MASQ  6    --  *      *      !10.10.0.0/16         10.200.1.61          /* default/svc-clusterip:svc-webport cluster IP */ tcp dpt:9000
    0     0 KUBE-SEP-TBW2IYJKUCAC7GB3  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/svc-clusterip:svc-webport -> 10.10.1.2:80 */ recent: CHECK seconds: 10800 reap name: KUBE-SEP-TBW2IYJKUCAC7GB3 side: source mask: 255.255.255.255
    0     0 KUBE-SEP-DOIEFYKPESCDTYCH  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/svc-clusterip:svc-webport -> 10.10.2.2:80 */ recent: CHECK seconds: 10800 reap name: KUBE-SEP-DOIEFYKPESCDTYCH side: source mask: 255.255.255.255
    0     0 KUBE-SEP-2TLZC6QOUTI37HEJ  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/svc-clusterip:svc-webport -> 10.10.3.2:80 */ recent: CHECK seconds: 10800 reap name: KUBE-SEP-2TLZC6QOUTI37HEJ side: source mask: 255.255.255.255
    0     0 KUBE-SEP-TBW2IYJKUCAC7GB3  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/svc-clusterip:svc-webport -> 10.10.1.2:80 */ statistic mode random probability 0.33333333349
    0     0 KUBE-SEP-DOIEFYKPESCDTYCH  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/svc-clusterip:svc-webport -> 10.10.2.2:80 */ statistic mode random probability 0.50000000000
    0     0 KUBE-SEP-2TLZC6QOUTI37HEJ  0    --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/svc-clusterip:svc-webport -> 10.10.3.2:80 */

명세의 type 부분에 ClusterIP를 기재함으로써 해당 서비스 오브젝트의 세부 유형은 ClusterIP로 지정됩니다. 이렇게 배포가 되면, 다른 POD에서는 자동으로 부여된 FQDN으로도 호출이 가능하며 FQDN은 다음과 같이 부여됩니다.

[서비스 이름].[네임스페이스].svc.cluster.local

그렇다면 yaml은 다음과 같이 다른 POD에서 호출이 가능합니다.

http://smaple-service.default.svc.cluster.local

이렇게 호출하는 것은 네임스페이스가 다른 경우에 해당하는 것이고, 같은 네임스페이스 내부에서는 다음과 같이 호출이 가능합니다.

http://sample-service

sessionAffinity: ClientIP

세션어피니티는 서비스를 통해 POD로의 연결시도시 백엔드 POD의 수량에 따라 부하분산되어 접근하게 되는데, 만약 L4에서의 sticky session처럼 처음 접근한 POD로 지속적인 접근을 하게 하기 위해서 해당 옵션을 사용하게 됩니다.

서비스 오브젝트 명세에 sessionAffinity: ClientIP 입력시 iptables에도 변화가 발생하는데 그 변화는 resent 모듈을 사용하는 것을 확인할수 있습니다.

iptables -t nat -S | grep recent
-A KUBE-SEP-2TLZC6QOUTI37HEJ -p tcp -m comment --comment "default/svc-clusterip:svc-webport" -m recent --set --name KUBE-SEP-2TLZC6QOUTI37HEJ --mask 255.255.255.255 --rsource -m tcp -j DNAT --to-destination 10.10.3.2:80
-A KUBE-SEP-DOIEFYKPESCDTYCH -p tcp -m comment --comment "default/svc-clusterip:svc-webport" -m recent --set --name KUBE-SEP-DOIEFYKPESCDTYCH --mask 255.255.255.255 --rsource -m tcp -j DNAT --to-destination 10.10.2.2:80
-A KUBE-SEP-TBW2IYJKUCAC7GB3 -p tcp -m comment --comment "default/svc-clusterip:svc-webport" -m recent --set --name KUBE-SEP-TBW2IYJKUCAC7GB3 --mask 255.255.255.255 --rsource -m tcp -j DNAT --to-destination 10.10.1.2:80
-A KUBE-SVC-KBDEBIL6IU6WL7RF -m comment --comment "default/svc-clusterip:svc-webport -> 10.10.1.2:80" -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-TBW2IYJKUCAC7GB3 --mask 255.255.255.255 --rsource -j KUBE-SEP-TBW2IYJKUCAC7GB3
-A KUBE-SVC-KBDEBIL6IU6WL7RF -m comment --comment "default/svc-clusterip:svc-webport -> 10.10.2.2:80" -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-DOIEFYKPESCDTYCH --mask 255.255.255.255 --rsource -j KUBE-SEP-DOIEFYKPESCDTYCH
-A KUBE-SVC-KBDEBIL6IU6WL7RF -m comment --comment "default/svc-clusterip:svc-webport -> 10.10.3.2:80" -m recent --rcheck --seconds 10800 --reap --name KUBE-SEP-2TLZC6QOUTI37HEJ --mask 255.255.255.255 --rsource -j KUBE-SEP-2TLZC6QOUTI37HEJ

sessionAffinity: ClusterIP 설정시 recent 모듈를 통해서 clientip를 기반으로 처음 접속한 POD로 다음 연결시에도 연결을하게 됩니다.

ClusterIP의 부족한점

ClusterIP 유형은 K8s 외부에서 접근이 불가능한 유형으로, 외부 접근은 NodePort나 Loadbalancer를 이용해야합니다. 또한, L4장비와는 다르게 이러한 통신을 구현하는데 사용하는 iptables는 healthcheck가 없는 부분도 감안해야 합니다. 다만, readiness/liveness 프로브를 사용하는 방법으로 보완을 해볼수있습니다.

NodePort

NodePort 부터는 실질적인 외부와의 통신이 가능한 유형으로 NodePort를 지정하게 되면 지정된 Port 번호로 전체 노드에서 해당 POD로 연결되도록 구성이 됩니다.

apiVersion: v1
kind: Service
metadata:
  name: sample-service
spec:
  port:
  - name: http
    port: 80
    targetPort: 8080
  selector:
    app: sample-service
  type: NodePort

위와 같은 명세가 API서버에 전달되면 서비스 오브젝트를 NodePort 유형으로 생성하게 되며, sepc 하위에 명시된 port 정보를 통해 모든 노드에서 80 포트를 오픈하게 됩니다. 노드가 3대라면, 3대 모두에 대해 IP:80 으로 접근시 selector에 지정된 app: sample-service로 레이블이 지정된 파드로 트래픽을 전달하게 됩니다. 결국 ClusterIP와의 차이점으로는 외부와의 통신 가능여부의 차이로 볼수가 있습니다.

iptables 규칙도 ClusterIP와는 다르게 KUBE-EXT-#(MARK) 부분이 추가되어 다음과 같이 순서가 변경됩니다.

PREROUTING → KUBE-SERVICES → KUBE-NODEPORTS → KUBE-EXT-#(MARK) → KUBE-SVC-# → KUBE-SEP-#  ⇒ KUBE-POSTROUTING (MASQUERADE)
root@myk8s-control-plane:~# iptables -t nat -S | grep PREROUTING
-P PREROUTING ACCEPT
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -d 192.168.65.254/32 -j DOCKER_OUTPUT
root@myk8s-control-plane:~# iptables -t nat -S | grep KUBE-SERVICES
-N KUBE-SERVICES
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A KUBE-SERVICES -d 10.200.1.10/32 -p tcp -m comment --comment "kube-system/kube-dns:metrics cluster IP" -m tcp --dport 9153 -j KUBE-SVC-JD5MR3NA4I4DYORP
-A KUBE-SERVICES -d 10.200.1.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-SVC-TCOU7JCQXEZGVUNU
# svc-nodeport 서비스 오브젝트 룰
-A KUBE-SERVICES -d 10.200.1.160/32 -p tcp -m comment --comment "default/svc-nodeport:svc-webport cluster IP" -m tcp --dport 9000 -j KUBE-SVC-VTR7MTHHNMFZ3OFS
-A KUBE-SERVICES -d 10.200.1.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y
-A KUBE-SERVICES -d 10.200.1.10/32 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp cluster IP" -m tcp --dport 53 -j KUBE-SVC-ERIFXISQEP7F7OF4
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
# svc-nodeport 서비스 오브젝트 룰 관련한 포트 확인 및 체크
root@myk8s-control-plane:~# iptables -t nat -S | grep KUBE-NODEPORTS | grep $NPORT
-A KUBE-NODEPORTS -d 127.0.0.0/8 -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp --dport 31436 -m nfacct --nfacct-name  localhost_nps_accepted_pkts -j KUBE-EXT-VTR7MTHHNMFZ3OFS
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp --dport 31436 -j KUBE-EXT-VTR7MTHHNMFZ3OFS
# NodePort에 대한 룰 확인, MARK-MASQ
root@myk8s-control-plane:~# iptables -t nat -S | grep "A KUBE-EXT-VTR7MTHHNMFZ3OFS"
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -m comment --comment "masquerade traffic for default/svc-nodeport:svc-webport external destinations" -j KUBE-MARK-MASQ
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -j KUBE-SVC-VTR7MTHHNMFZ3OFS
# 세부 룰 확인
root@myk8s-control-plane:~# iptables -t nat -S | grep "A KUBE-SVC-VTR7MTHHNMFZ3OFS -"
-A KUBE-SVC-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.1.3:8080" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-XEXGJWEWSC2GPNPZ
-A KUBE-SVC-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.2.3:8080" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-2AEFSWYPQGZTCWEI
-A KUBE-SVC-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.3.3:8080" -j KUBE-SEP-4FWIZRNI676EMNVA
# 트래픽 return시 룰 확인
root@myk8s-control-plane:~# iptables -t nat -S | grep "A KUBE-POSTROUTING"
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully

externalTrafficPolicy: Local

NodePort에서 externalTrafficPolicy 설정을 통해 트래픽에 대한 제어가 가능합니다. 특히 externalTrafficPolicy: Local 설정시 접근하는 해당노드에 배치된 POD로만 접근이 가능하며, 다른 노드에 배치된 POD는 접근이 불가합니다.

iptables에도 변화가 발생하는데 기존과는 다르게 KUBE-XLB가 제거되고, KUBE-EXT, KUBE-SVL이 추가되게 됩니다.

PREROUTING → KUBE-SERVICES → KUBE-NODEPORTS → KUBE-EXT-# → KUBE-SVL-# → KUBE-SEP-#<자신의 노드에 생성된 파드>
root@myk8s-worker2:/# iptables -t nat -S | grep 30240
-A KUBE-NODEPORTS -d 127.0.0.0/8 -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp --dport 30240 -m nfacct --nfacct-name  localhost_nps_accepted_pkts -j KUBE-EXT-VTR7MTHHNMFZ3OFS
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp --dport 30240 -j KUBE-EXT-VTR7MTHHNMFZ3OFS
root@myk8s-worker2:/# iptables -t nat -S | grep 'A KUBE-EXT-VTR7MTHHNMFZ3OFS'
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -s 10.10.0.0/16 -m comment --comment "pod traffic for default/svc-nodeport:svc-webport external destinations" -j KUBE-SVC-VTR7MTHHNMFZ3OFS
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -m comment --comment "masquerade LOCAL traffic for default/svc-nodeport:svc-webport external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -m comment --comment "route LOCAL traffic for default/svc-nodeport:svc-webport external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-VTR7MTHHNMFZ3OFS
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -j KUBE-SVL-VTR7MTHHNMFZ3OFS
root@myk8s-worker2:/# iptables -t nat -S | grep 'A KUBE-SVL-VTR7MTHHNMFZ3OFS'
-A KUBE-SVL-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.3.3:8080" -j KUBE-SEP-4FWIZRNI676EMNVA
root@myk8s-worker2:/# iptables -t nat -S | grep 'A KUBE-SEP-4FWIZRNI676EMNVA'
-A KUBE-SEP-4FWIZRNI676EMNVA -s 10.10.3.3/32 -m comment --comment "default/svc-nodeport:svc-webport" -j KUBE-MARK-MASQ
-A KUBE-SEP-4FWIZRNI676EMNVA -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp -j DNAT --to-destination 10.10.3.3:8080

NodePort + externalTrafficPolicy: Local 설정시 위의 룰은 POD가 배치된 노드에만 생성이 되는것을 확인할수 있습니다.

❯ for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i iptables -t nat -S | grep 'A KUBE-SVL-VTR7MTHHNMFZ3OFS'; echo; done

>> node myk8s-control-plane <<

>> node myk8s-worker <<

>> node myk8s-worker2 <<
-A KUBE-SVL-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.3.3:8080" -j KUBE-SEP-4FWIZRNI676EMNVA

>> node myk8s-worker3 <<
-A KUBE-SVL-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.2.3:8080" -j KUBE-SEP-2AEFSWYPQGZTCWEI

그래서 POD가 없는 노드에서 상세 확인시 KUBE-FIREWALL에 의해 차단되는 것을 확인할수가 있습니다.

root@myk8s-worker:/# iptables -t nat -S | grep 30240
-A KUBE-NODEPORTS -d 127.0.0.0/8 -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp --dport 30240 -m nfacct --nfacct-name  localhost_nps_accepted_pkts -j KUBE-EXT-VTR7MTHHNMFZ3OFS
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/svc-nodeport:svc-webport" -m tcp --dport 30240 -j KUBE-EXT-VTR7MTHHNMFZ3OFS
root@myk8s-worker:/# iptables -t nat -S | grep 'A KUBE-EXT-VTR7MTHHNMFZ3OFS'
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -s 10.10.0.0/16 -m comment --comment "pod traffic for default/svc-nodeport:svc-webport external destinations" -j KUBE-SVC-VTR7MTHHNMFZ3OFS
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -m comment --comment "masquerade LOCAL traffic for default/svc-nodeport:svc-webport external destinations" -m addrtype --src-type LOCAL -j KUBE-MARK-MASQ
-A KUBE-EXT-VTR7MTHHNMFZ3OFS -m comment --comment "route LOCAL traffic for default/svc-nodeport:svc-webport external destinations" -m addrtype --src-type LOCAL -j KUBE-SVC-VTR7MTHHNMFZ3OFS
root@myk8s-worker:/# iptables -t nat -S | grep 'A KUBE-SVC-VTR7MTHHNMFZ3OFS'
-A KUBE-SVC-VTR7MTHHNMFZ3OFS ! -s 10.10.0.0/16 -d 10.200.1.132/32 -p tcp -m comment --comment "default/svc-nodeport:svc-webport cluster IP" -m tcp --dport 9000 -j KUBE-MARK-MASQ
-A KUBE-SVC-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.2.3:8080" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-2AEFSWYPQGZTCWEI
-A KUBE-SVC-VTR7MTHHNMFZ3OFS -m comment --comment "default/svc-nodeport:svc-webport -> 10.10.3.3:8080" -j KUBE-SEP-4FWIZRNI676EMNVA
root@myk8s-worker:/# iptables -t filter -S | grep KUBE-FIREWALL
-N KUBE-FIREWALL
-A INPUT -j KUBE-FIREWALL
-A OUTPUT -j KUBE-FIREWALL
-A KUBE-FIREWALL ! -s 127.0.0.0/8 -d 127.0.0.0/8 -m comment --comment "block incoming localnet connections" -m conntrack ! --ctstate RELATED,ESTABLISHED,DNAT -j DROP

NodePort의 부족한점

외부에 노출되면 안되는 Node의 내부 IP가 노출이 되어 보안에 취약하게 됩니다. 그렇기 때문에 노출되는 부분을 최소화할수 있도록 LoadBalancer를 이용하게 됩니다.

번외

Service 오브젝트와 관련하여 실제 플랫폼을 구성하고 운영중인 방식

재직중인 회사에 구성한 데이터플랫폼은 서비스와 파이프라인영역으로 나누어 K8s기반의 서비스가 구동중에 있습니다. 파이프라인 부분은 추후 OSS를 이용한 내재화 예정으로 아직은 PaaS를 사용하고 있고, 데이터서비스 영역은 하나의 클러스터에서 네임스페이스로 구분하여 외부/내부를 사용하고 있습니다.

데이터플랫폼은 Google Cloud에서 GKE(Google Kubernetes Engine)의 Standard(dataplane v1 - calico)로 운영중에 있습니다. 해당 환경에서 서비스 오브젝트를 사용하는 방식은 조금 다르게 사용하게 되는데요. 아무래도, 클라우드 사업자를 통해서 사용하는 것이다보니 GKE내 Ingress API를 통해 Google Cloud내 Load Balancer를 구성 및 사용하고 있습니다.

클라이언트에서 API 혹은 데이터서비스까지의 접근을 도식화하면 다음과 같습니다.

Client - Load Balancer(Google Cloud - Load Balancer/L4, Ingress(GKE) 연동) - Kong SVC(Cluster IP) -  Kong API Gateway(ns: kong) - Kong POD - API SVC(ns: data-srv, ClusterIP) - API POD

이러한 방식을 사용해서 K8s 기준에서 외부로 노출되는 IP는 External(Public IP)와 Internal(Private IP) 2개로 제한되어있습니다. 그리고, GKE는 Ingress API를 통해서 Google Cloud의 Load Balancer에 대한 컨트롤이 가능한데요.

Load Balancer는 Application Load Balancer로 구성하지만, 실제로는 모든 URI Path를 Kong API Gateway로 넘어가도록 설정하여 SSL 처리 및 L4 역할로만 사용하도록 되어있습니다. 즉, 외부의 Load Balancer는 SSL/L4 만 담당하고, 실제 트래픽의 URI Path에 대한 라우팅(L7)은 Kong API Gateway가 전담하고 있습니다.

Kong API Gateway가 L7 역할이다보니 URI Path 호출 발생시 API 혹은 데이터 서비스 즉, 목적지까지 데이터를 전달해야하는데요. 이를 위해서 URI Path 라우팅에 등록시 K8s의 ClusterIP 유형 등록시 생성되는 FQDN을 사용하고 있습니다.

[서비스 이름].[네임스페이스].svc.cluster.local

이거와는 별개로 스터디에서 다루지 않은 ExternalName도 같이 사용하고 있습니다. 내부에서는 API 진입점을 만들어두었기 때문에, URI Path로 라우팅이 되게 되는데요. 문제는 아직 컨테이너로 구성되지 못하는 서비스의 경우 VM이나 기타 다른 CSP에 위치한 곳을 호출할수도 있어야 합니다. 이때 ExternalName 유형의 서비스를 구성해서 사용하는데요. 이 경우에도 기존과 크게 다르지는 않습니다. 다만, ExternalName 유형은 Kong API Gateway가 배치된 네임스페이스에 생성하고, 연결해서 사용합니다.

이렇게하면 앞에서 의도한 것처럼 K8s의 외부로 노출되는 Endpoint IP는 2개로 유지할수 있게 됩니다.