개요
LoadBalancer 타입의 Service에 대한 소개를 가볍게 했는데, 보다 세부적인 유형을 탐색하고, MetalLB의 2가지 모드에 대해서 실습
LoadBalancer
개념 소개 글에서 LoadBalancer 타입의 Service를 소개할 때는 단순히 CSP에서 제공하는 타입이면서 NodePort와 ClusterIP를 포함하는 정도로만 소개했지만, 더 자세히 알아보면 NodePort 이상으로 알아야할 것들이 존재
k8s에서 LoadBalancer 타입의 Service를 생성한다고 해서 그 기능을 k8s가 직접 제공하진 않음
CSP에서 제공하는 LB 컴포넌트를 이용하거나 직접 그러한 컴포넌트를 구성해야만 정상적으로 이용 가능
계층별 구분
LB는 계층에 따라 NLB / ALB로 나뉘며, 각각 4계층 그리고 7계층에서 동작
NLB의 경우 4계층이기 때문에 TCP / UDP / TLS 등의 트래픽을 처리하고, ALB 보다 처리 속도가 빠름
ALB의 경우 7계층이기 때문에 HTTP / HTTPS / gRPC 등의 트래픽을 처리할 수 있고, 대표적으로 Ingress 등에서 사용되는 LB
제공자 구분
LoadBalancer 타입은 CSP와 온프레미스 2가지로 나눌 수 있고, 온프레미스는 다시 H/W LB와 S/W LB로 나눌 수 있음
CSP와 온프레미스의 H/W LB 경우 아래 2가지 방식을 지원하는데, 첫 번째는 클러스터의 특정 노드로 전달하고 나면 NodePort 방식으로 동작하고, 두 번째는 Pod IP를 동적으로 관리하는 LB Controller를 이용하여 Pod에 직접 트래픽을 보내는 식으로 동작
1.
LB가 특정 노드로 부하 분산하여 NodePort로 접근한 후, iptables로 Pod에 부하 분산하는 방식 (NodePort 접근 방식)
2.
LB가 직접 Pod로 부하 분산하는 방식 (Pod Direct 접근 방식)
S/W LB의 대표적인 솔루션은 MetalLB (BareMetal LoadBalancer)인데, 다른 방식과는 달리 별도로 트래픽을 받아주는 물리적인 노드가 있는 것은 아니기 때문에 클러스터 내부에서 LB 역할을 하는 Pod가 우선적으로 트래픽을 받아서 특정 노드로 부하 분산한 후, iptables로 Pod에 부하 분산하는 방식으로 동작
S/W LB에서 사용되는 그 외 솔루션으로는 OpenELB, PubeLB, kube-vip 등이 존재
동작 방식
앞 단 과정의 경우, 클라이언트는 노드의 IP 주소를 이용한 것이 아니라 VIP로 된 LB의 IP 주소로 접속하게 되고, LB로 접속 시 부하 분산을 통해 노드로 접속
VIP로 동작하기 때문에 LB 뒤의 실제 노드는 모르게 되고, 노드에 문제가 발생하더라도 외부로 공개된 VIP의 변경 없이 노드 교체가 가능
externalTrafficPolicy가 Local이 되더라도 LB를 이용하여 Pod가 존재하는 노드들로만 부하 분산이 가능하고, Local 설정에 따라 클라이언트 IP를 보존하는 것도 가능
노드 상에 Pod가 없는 경우 LB 단에서 헬스 체크를 통해 노드의 상태를 실패로 파악하고 트래픽을 전달하지 않도록 구성하는 것이 가능
LB에서 노드로의 dNAT 1회, 노드에서 Pod로의 dNAT 1회로 총 2회의 dNAT이 동작
한계
1.
Service 마다 LB를 생성하게 되므로 자원 측면에서 비효율적
2.
도메인 기반 라우팅이 어렵기 때문에 HTTP/HTTPS 등의 처리 아쉬움 존재
→ 인그레스를 통해 자원 효율화와 7계층 지원으로 한계점 극복 가능
MetalLB 개념
MetalLB는 BareMetal로 동작하는 LB를 의미하는데, k8s 상에서는 DaemonSet으로 Speaker Pod를 노드마다 생성하여 ExternalIP를 전파하는 식으로 동작
Speaker Pod는 호스트 네트워크 네임스페이스를 이용 ("NetworkMode": "host")
ExternalIP의 전파 방식으로는 Layer2 모드의 ARP (GARP) 혹은 BGP 모드의 BGP를 이용
Layer2 모드는 클라우드 환경에 부적합하며, BGP 모드의 경우 일부 CNI의 호환성 한계와 네트워크 부서의 협조 (네트워크의 깊은 이해)가 필요하다는 문제가 있음
실무에서 MetalLB를 이용한다면 BGP 모드 + externalTrafficPolicy: Local을 권장
노드 마다 존재하는 Speaker Pod들 중 특정 Service를 책임질 리더 Speaker Pod를 선출하게 되고, 리더 Speaker Pod가 존재하는 노드로만 트래픽이 인입되어 해당 노드에서 iptables로 부하 분산하여 Pod에 접속
Service를 담당하는 리더 Speaker Pod가 존재하는 노드로만 트래픽이 인입되므로 부하가 쏠리는 현상이 존재할 수 있음
ExternalIP의 전파는 ARP를 이용한다고 했는데 이 중에서도 DAD (Duplicated Address Detection)에 사용되는 GARP를 주로 이용하고, 노드의 MAC 주소 홍보를 리더 Speaker Pod가 해줌으로써 ExternalIP의 소유권을 리더 Speaker Pod가 있는 노드로 귀속하여 ExternalIP로 접속 시 해당 노드로 접속될 수 있도록 동작
GARP로 홍보된 내용에 따라 리더 Speaker Pod가 존재하는 노드로만 접속하게 되면서 해당 노드의 iptables를 이용한다고 했는데, externalTrafficPolicy가 Local이라면 해당 노드에 Pod가 없을 때 동작하지 않기 때문에 일반적으로는 externalTrafficPolicy를 Cluster로 이용하여 다른 노드에 Pod가 존재하는 경우에도 동작하도록 이용
즉, 클라이언트의 IP 보존이 필요 없으면서, (2계층에서 동작하기 때문에 동일 네트워크 상에서 동작한다는 한계가 있다보니) 소규모 환경일 때 Layer2 모드를 이용
FailOver 측면에서는 10 ~ 20초 정도 소요로 장애 상황이 꽤나 길게 이어짐
Layer2 모드와는 다르게 리더 Speaker Pod라는 개념은 없고, 리더 Speaker Pod가 수행하던 GARP 홍보 과정을 BGP로 대신하여 ExternalIP를 전파
단, BGP 업데이트를 받지는 않고 자신이 속한 대역의 네트워크만 홍보
Pod가 존재하는 노드로만 부하를 전달하는 것이 가능하기 때문에 externalTrafficPolicy를 Local로 두어 클라이언트 IP 수집도 자연스럽게 가능한데, 이 때 수반되는 부하 쏠림 현상은 라우터를 거칠 때의 ECMP 라우팅을 통해 노드 별로 적절히 부하를 분산하는 것이 가능
ECMP (Equal Cost Multi Path)
하나의 목적지로 패킷 라우팅을 수행하 때, 여러 개의 경로를 선택하는 라우팅 기법
즉, (ECMP 덕에 특정 노드가 트래픽을 받아낼 필요가 없을 뿐더러 Pod가 존재하는 노드로만 라우팅이 선정 가능하기 때문에) 클라이언트 IP 보존이 필요한 경우에 적합하고, (네트워크 대역 홍보가 이뤄지는 BGP를 이용하기 때문에) 대규모 환경일 때 BGP 모드를 이용
FailOver 측면에서는 Layer2 모드 보다는 조금 더 나은 부분이 있는데, 정상적인 종료 시 BGP Update의 Withdrawn Routes 정보를 통해 거의 무중단으로 네트워크 정보를 갱신 (비정상 종료 시 마찬가지로 회복이 느릴 수 있음)
실습 환경 준비
실습
MetalLB 구축
설치는 Kustomize, Helm, manifest 등의 방법을 지원
설치
# MetalLB 설치
# https://raw.githubusercontent.com/metallb/metallb/refs/heads/main/config/manifests/metallb-native-prometheus.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/refs/heads/main/config/manifests/metallb-native-prometheus.yaml
Shell
복사
# IPAddressPool 생성
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-ippool
namespace: metallb-system
spec:
addresses:
- 172.18.255.200-172.18.255.250
EOF
Shell
복사
# L2Advertisement 생성
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: my-l2-advertise
namespace: metallb-system
spec:
ipAddressPools:
- my-ippool
EOF
Shell
복사
# https://krew.sigs.k8s.io/docs/user-guide/setup/install/
# Krew 플러그인 이용은 위 링크의 krew를 검색하여 참고
# Krew 플러그인으로 Stern 설치
# Stern은 다중 Pod 및 컨테이너의 로그를 조회할 수 있도록 돕는 플러그인
kubectl krew update
kubectl krew search | grep stern
kubectl krew install stern
Shell
복사
CRD 리소스 조회
# MetalLB CRD 조회
kubectl get crd | grep metallb
Shell
복사
# IPAddressPool 리소스 상세 설명 조회
# MetaLB에서는 Service를 생성할 때 해당 Service가 사용할 ExternalIP를 IPAddressPool를 통해 동적으로 할당 가능
kubectl explain ipaddresspools.metallb.io
# 리소스 조회
kubectl get ipaddresspools -n metallb-system
Shell
복사
# L2Advertisement 리로스 상세 설명 조회
# 설정된 IPAddressPool을 기반으로 얻은 LoadBalancer의 IP 주소를 Layer2 모드로 홍보
kubectl explain l2advertisements.metallb.io
# 리소스 조회
kubectl get l2advertisements -n metallb-system
Shell
복사
# ServiceL2Statuses 리소스 상세 설명 조회
# MetalLB가 Layer2 모드로 동작할 때 실제 트래픽 상태를 조회할 수 있도록 돕는 리소스
kubectl explain servicel2statuses.metallb.io
# 리소스 조회 (Layer2 모드 실습 시 확인 가능)
kubectl get servicel2status -n metallb-system
Shell
복사
CRD 외의 리소스 조회
# MetalLB 관련 리소스 조회
kubectl get all,cm,secret,ep -n metallb-system
Shell
복사
MetalLB 관련 컨테이너 조회
# MetaLB 관련 컨테이너들을 모두 조회
# kube-rbac-proxy 컨테이너는 MetalLB 설치에 함께 구축된 Prometheus에서 사용할 Exporter
kubectl get po -n metallb-system -l app=metallb -o jsonpath="{range .items[*]}{.metadata.name}{':\n'}{range .spec.containers[*]}{' '}{.name}{' -> '}{.image}{'\n'}{end}{end}"
Shell
복사
Speaker Pod의 IP 조회
# MetalLB의 Speaker Pod의 IP 확인
# 네트워크 모드가 호스트 임에 따라 노드의 IP를 그대로 이용 (호스트 네트워크 네임스페이스 이용)
kubectl get po -n metallb-system -o wide
kubectl get no -o wide
Shell
복사
모니터링
# Stern을 이용한 로그 조회
kubectl stern -n metallb-system -l app=metallb
# 그 외 기본 기능으로 로그 조회
kubectl logs -f -n metallb-system -l app=metallb
Shell
복사
Layer2 모드
리소스 생성
# Pod 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: webpod1
labels:
app: webpod
spec:
nodeName: myk8s-worker
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod2
labels:
app: webpod
spec:
nodeName: myk8s-worker2
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
EOF
# Service 생성
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: svc1
spec:
ports:
- name: svc1-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc2
spec:
ports:
- name: svc2-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
name: svc3
spec:
ports:
- name: svc3-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
EOF
Shell
복사
Service 관련 조회
# Layer2 모드에서 리더 Speaker Pod가 홍보하는 GARP에 따라 스캔은 되었겠지만, ARP Scan을 한 번 더 수행
docker exec -it myk8s-control-plane arp-scan --interfac=eth0 --localnet
Shell
복사
# Service 및 Endpoints 조회
# Service 마다 ExternalIP가 할당
# LoadBalancer 타입의 Service는 allocateLoadBalancerNodePorts: true가 기본 값임에 따라 NodePort와 ClusterIP를 포함
# ExternalIP 접속 시 ports를 이용
# 노드 IP로 접속 시 nodePort를 이용
kubectl get svc,ep
Shell
복사
# Service를 describe 시 VIP 별 리더 Speaker Pod를 가진 노드 확인 가능
kubectl describe svc | grep Events: -A5
Shell
복사
# 특정 Service를 조회 했을 때 NodePorts를 이용함과 VIP를 확인 가능
kubectl get svc svc1 -o json | jq | grep -E "(allocateLoadBalancerNodePorts|VIP)" -B2 -A2
Shell
복사
# CRD인 servicel2status로 LB 상태를 확인
kubectl get servicel2status -n metallb-system
Shell
복사
# ExternalIP를 변수에 지정
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC2EXIP=$(kubectl get svc svc2 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC3EXIP=$(kubectl get svc svc3 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SVC1EXIP $SVC2EXIP $SVC3EXIP
# mypc라는 외부 노드에서 arping을 이용하여 Service의 VIP에 해당되는 MAC 주소 확인
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do docker exec -it mypc arping -I eth0 -f -c 1 $i; done
# mypc의 ARP 테이블을 확인하여 MAC 주소에 해당되는 실제 IP 주소들을 확인
docker exec -it mypc ip -c neigh | sort
# 실제 IP 주소에 해당되는 노드를 찾음
kubectl get no -o wide
Shell
복사
접속 확인
# ExternalIP를 변수에 지정
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC2EXIP=$(kubectl get svc svc2 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
SVC3EXIP=$(kubectl get svc svc3 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SVC1EXIP $SVC2EXIP $SVC3EXIP
# mypc라는 외부 노드에서 접속 확인
# Hostname을 통해 VIP로 접속되는 것을 확인 가능
# RemoteAddr을 통해 sNAT이 되는 것을 확인 가능
for i in $SVC1EXIP $SVC2EXIP $SVC3EXIP; do echo ">> Access Service External-IP : $i <<" ;docker exec -it mypc curl -s $i | egrep 'Hostname|RemoteAddr|Host:' ; echo ; done
# Service 별로 각 Pod에 적절히 부하 분산 접속됨을 확인
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC1EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC2EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
docker exec -it mypc zsh -c "for i in {1..100}; do curl -s $SVC3EXIP | grep Hostname; done | sort | uniq -c | sort -nr"
Shell
복사
iptables 확인
업데이트 이전에는 PREROUTING → KUBE-SERVICES → KUBE-FW-XXX → KUBE-SVC-XXX → KUBE-SEP-XXX → POSTROUTING → KUBE-POSTROUTING로 이뤄졌었고, 특이사항으로는 KUBE-FW-XXX에서 KUBE-MARK-MASQ와 KUBE-SVC-XXX로 전달하여 KUBE-POSTROUTING에서 sNAT이 처리될 수 있도록 했음
현재는 NodePort의 체인 형태인 PREROUTING → KUBE-SERVICES → KUBE-NODEPORTS → KUBE-EXT-XXX → KUBE-MARK-MASQ → KUBE-SVC-XXX → KUBE-SEP-XXX → POSTROUTING → KUBE-POSTROUTING 와 동일
FailOver 테스트
리더 Speaker Pod가 동작하고 있는 노드에 장애가 발생하게 되면 남은 Speaker Pod들은 해당 노드의 장애를 인지
이를 통해 리더 Speaker Pod를 다시 선출하게 되어 GARP로 VIP에 대한 MAC 주소가 갱신되도록 처리
Layer2 모드의 장애 복구에는 ARP 전파 시간 때문에 20초 ~ 1분의 장애 복구 시간이 소요될 수 있음
# 1번 Service의 VIP를 변수에 할당
SVC1EXIP=$(kubectl get svc svc1 -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo $SVC1EXIP
# 1번 Service의 리더 Speaker Pod가 존재하는 노드를 확인
docker exec -it mypc arping -I eth0 -f -c 1 $SVC1EXIP
docker exec -it mypc ip -c neigh | sort
kubectl get no -o wide
Shell
복사
# 좌측 터미널에서는 Service 1번으로 반복적인 접속 시도
docker exec -it mypc zsh -c "while true; do curl -s --connect-timeout 1 $SVC1EXIP | egrep 'Hostname|RemoteAddr'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
# 우측 터미널에서는 알아낸 노드 중지
# 금방 접속이 되는 것처럼 보여도 새로운 리더 Speaker Pod를 선출하기 전까지는 불안정하게 접속
# ARP 테이블과 노드 정보로 현재 리더 Speaker Pod를 갖고 있는 노드를 확인
docker stop myk8s-worker
docker exec -it mypc ip -c neigh | sort
kubectl get no -o wide
# 우측 터미널에서는 다시 노드를 재시작
# 노드를 복구함과 동시에 기존 리더 Speaker Pod가 리더로 복귀
# ARP 테이블과 노드 정보로 현재 리더 Speaker Pod를 갖고 있는 노드를 확인
docker start myk8s-worker
docker exec -it mypc ip -c neigh | sort
kubectl get no -o wide
Shell
복사
리소스 삭제
kubectl delete po,svc --all
Shell
복사
BGP 모드
별도 라우터 장비 구성하여 BGP 연동 되도록 설정하여야 실습 정상 진행 가능하고, 해당 상태에서 BGP 설정을 위한 ConfigMap 생성 시 자동으로 모드 전환
# 아래와 같이 리눅스 라우터에 BGP 설정이 되어 있음
router bgp 64513
bgp router-id 192.168.10.254
maximum-paths 4
network 10.1.1.0/24
neighbor 192.168.10.10 remote-as 64512
neighbor 192.168.10.101 remote-as 64512
neighbor 192.168.10.102 remote-as 64512
...
Shell
복사
서비스 관련 확인 & 접속 확인 & FailOver 테스트는 Layer2 모드와 거의 동일하여 스킵
FailOver는 비정상 종료 시 Layer2와 비슷하게 일정 시간이 소요될 수 있으나, 정상 종료 시 BGP Update 패킷으로 거의 즉각적인 네트워크 정보 갱신이 이뤄지면서 거의 무중단을 지원하여 Layer2 모드에 비해 조금 더 나은 부분이 존재
리소스 생성
# IPAddressPool을 기재한 ConfigMap 생성
cat <<EOF | kubectl replace --force -f -
apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
peers:
- peer-address: 192.168.10.254
peer-asn: 64513
my-asn: 64512
address-pools:
- name: default
protocol: bgp
avoid-buggy-ips: true
addresses:
- 172.20.1.0/24
EOF
Shell
복사
IPAddressPool을 여럿 이용한다면 peers 항목에 추가로 기재하고, BGP에서 기본으로 사용할 풀에 auto-assign: true를 기재한 후 나머지 풀은 auto-assign: false를 기재
Service를 생성할 때 등록된 IPAddressPool 중 특정 풀을 이용하고 싶다면, annotations에 사용하려는 풀을 기재
예시
Service 마다 LB를 생성했지만, 각 LB가 동일한 VIP가 필요하다면, annotations에 직접 지정도 가능 (DNS처럼 TCP, UDP의 Service를 생성 후 동일한 IP로 처리하려는 경우)
예시
# Pod 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
name: webpod1
labels:
app: webpod
spec:
#nodeName: k8s-w1
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod2
labels:
app: webpod
spec:
#nodeName: k8s-w2
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
---
apiVersion: v1
kind: Pod
metadata:
name: webpod3
labels:
app: webpod
spec:
#nodeName: k8s-w3
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
EOF
# Service 생성
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
name: svc1
spec:
ports:
- name: svc1-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
#externalTrafficPolicy: Local
---
apiVersion: v1
kind: Service
metadata:
name: svc2
spec:
ports:
- name: svc2-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
#externalTrafficPolicy: Local
---
apiVersion: v1
kind: Service
metadata:
name: svc3
spec:
ports:
- name: svc3-webport
port: 80
targetPort: 80
selector:
app: webpod
type: LoadBalancer
#externalTrafficPolicy: Local
EOF
Shell
복사
BGP 관련 조회
# TCP 179 포트로 BGP 정보 전파되는 것을 확인 가능 (VIP들은 전파되지만, 다른 BGP 정보들을 k8s 노드가 받진 않음)
ss -nlptu | egrep 'Netid|speaker'
# BGP 패킷 캡쳐
# tcpdump -i <노드NIC> -nnq tcp port 179
tcpdump -i enp0s8 -nnq tcp port 179
ngrep -tW byline -d enp0s8 '' 'tcp port 179'
# 3개 Service의 각 VIP에 대해서 k8s 노드로 ECMP 라우팅 정보 업데이트 확인 가능
ip -c route
# BGP 상세 정보 확인
show bgp ipv4 unicast summary
show ip bgp
show running-config
Shell
복사
BGP 상세 정보 확인 출력 예시
iptables 확인
업데이트 이전에는 PREROUTING → KUBE-SERVICES → KUBE-FW-XXX → KUBE-SVC-XXX → KUBE-SEP-XXX → POSTROUTING → KUBE-POSTROUTING로 이뤄졌었고, 특이사항으로는 KUBE-FW-XXX에서 KUBE-MARK-MASQ와 KUBE-SVC-XXX로 전달하여 KUBE-POSTROUTING에서 sNAT이 처리될 수 있도록 했음
현재는 NodePort의 체인 형태인 PREROUTING → KUBE-SERVICES → KUBE-NODEPORTS → KUBE-EXT-XXX → KUBE-MARK-MASQ → KUBE-SVC-XXX → KUBE-SEP-XXX → POSTROUTING → KUBE-POSTROUTING 와 동일
리소스 삭제
kubectl delete po,svc --all
Shell
복사
ExternalIP
ExternalIP 타입의 Service는 외부에서 특정 노드의 IP 주소로 들어온 트래픽을 ClusterIP를 이용하여 컨테이너로 보내는 형태로 통신하는 방식
NodePort와 거의 유사한데 차이로는 NodePort와 달리 1개 혹은 특정 노드들을 지정할 수 있다는 점이지만, 특별한 이유가 없으면 externalTrafficPolicy 등을 지정할 수 있는 NodePort를 이용하는 것을 권장
ExternalIP는 지정된 노드의 IP로만 전달되기 때문에 외부에서 ExternalIP로 접근하면 무조건 노드의 IP로 sNAT된 후 컨테이너로 전달되기 때문에 클라이언트의 IP 주소를 수집할 수 없고, 이에 따라 externalTrafficPolicy를 이용할 수 없음
리소스 생성
# Deployment와 ExternalIP가 지정된 ClusterIP 타입의 Service를 생성
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-echo
spec:
replicas: 2
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
terminationGracePeriodSeconds: 0
containers:
- name: ndks-websrv
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-externalip
spec:
type: ClusterIP
externalIPs:
- 192.168.10.101
- 192.168.10.102
ports:
- name: svc-webport
port: 9000
targetPort: 8080
selector:
app: deploy-websrv
EOF
Shell
복사
Service 관련 조회
# Service 조회
# 지정했던 ExternalIP가 존재함을 확인 가능
kubectl get svc svc-externalip
Shell
복사
# YAML 형태로 Service를 조회
# externalTrafficPolicy가 따로 존재하지 않는 것을 확인 가능
kubectl get svc svc-externalip -o yaml
Shell
복사
리소스 삭제
kubectl delete deploy deploy-echo; kubectl delete svc svc-externalip
Shell
복사
실습 환경 삭제
kind delete cluster --name myk8s
Shell
복사