Service (ClusterIP & NodePort)
4번째 시간을 정리 합니다. 시작하기 앞서서 이번에는 그래도 많이 사용해본 쿠버네티스의 Service를 다루는 시간이니 쉽겠다고 생각했습니다. 하지만 Iptable이 이렇게 많은 기능이 제공되는지 몰랐다. 네트워크의 세계는 넓다.
참고 자료로 이전에 정리한 kubernetes의 각 항목을 살펴보자. 글이 있습니다.
실습 환경 구성
- K8S : v1.22.6
- OS : Ubuntu 20.04.3
- CNI : Calico v3.21 - Direct mod
- Iptables proxy mode
# K8S 배포에 사용할 폴더(디렉터리)를 생성 후 사용해주세요
# vagrant 파일 다운로드
$ curl -O https://raw.githubusercontent.com/gasida/KANS/main/4/Vagrantfile
# 배포
$ vagrant up
# 배포 확인 : k8s-rtr, k8s-m, k8s-w1, k8s-w2, k8s-w3, k8s-pc
$ vagrant status
# 마스터노드 접속
$ vagrant ssh k8s-m
# 마스터노드에서 Calico 확인 (Direct mode)
$ calicoctl get ippool -o wide
Vagrantfile
# Base Image
BOX_IMAGE = "ubuntu/focal64"
BOX_VERSION = "20211026.0.0"
# max number of worker nodes
N = 3
Vagrant.configure("2") do |config|
#-----Router Node
config.vm.define "k8s-rtr" do |subconfig|
subconfig.vm.boot_timeout = 1800
subconfig.vm.box = BOX_IMAGE
subconfig.vm.box_version = BOX_VERSION
subconfig.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--groups", "/Service-Lab"]
v.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
v.name = "Service-k8s-rtr"
v.memory = 512
v.cpus = 1
v.linked_clone = true
end
subconfig.vm.hostname = "k8s-rtr"
subconfig.vm.synced_folder "./", "/vagrant", disabled: true
subconfig.vm.network "private_network", ip: "192.168.10.254"
subconfig.vm.network "private_network", ip: "192.168.20.254"
subconfig.vm.network "forwarded_port", guest: 22, host: 50000, auto_correct: true, id: "ssh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/linux_router.sh"
end
#-----Manager Node
config.vm.define "k8s-m" do |subconfig|
subconfig.vm.boot_timeout = 1800
subconfig.vm.box = BOX_IMAGE
subconfig.vm.box_version = BOX_VERSION
subconfig.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--groups", "/Service-Lab"]
v.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
v.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
v.name = "Service-k8s-m"
v.memory = 4048
v.cpus = 4
v.linked_clone = true
end
subconfig.vm.hostname = "k8s-m"
subconfig.vm.synced_folder "./", "/vagrant", disabled: true
subconfig.vm.network "private_network", ip: "192.168.10.10"
subconfig.vm.network "forwarded_port", guest: 22, host: 50010, auto_correct: true, id: "ssh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/init_cfg.sh", args: N
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/route1.sh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/master.sh"
end
#-----Worker Node Subnet1
(1..N).each do |i|
config.vm.define "k8s-w#{i}" do |subconfig|
subconfig.vm.boot_timeout = 1800
subconfig.vm.box = BOX_IMAGE
subconfig.vm.box_version = BOX_VERSION
subconfig.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--groups", "/Service-Lab"]
v.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
v.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
v.name = "Service-k8s-w#{i}"
v.memory = 1536
v.cpus = 2
v.linked_clone = true
end
subconfig.vm.hostname = "k8s-w#{i}"
subconfig.vm.synced_folder "./", "/vagrant", disabled: true
subconfig.vm.network "private_network", ip: "192.168.10.10#{i}"
subconfig.vm.network "forwarded_port", guest: 22, host: "5001#{i}", auto_correct: true, id: "ssh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/init_cfg.sh", args: N
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/route1.sh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/worker.sh"
end
end
#-----Client PC Subnet2
config.vm.define "k8s-pc" do |subconfig|
subconfig.vm.boot_timeout = 1800
subconfig.vm.box = BOX_IMAGE
subconfig.vm.box_version = BOX_VERSION
subconfig.vm.provider "virtualbox" do |v|
v.customize ["modifyvm", :id, "--groups", "/Service-Lab"]
v.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
v.name = "Service-k8s-pc"
v.memory = 512
v.cpus = 1
v.linked_clone = true
end
subconfig.vm.hostname = "k8s-pc"
subconfig.vm.synced_folder "./", "/vagrant", disabled: true
subconfig.vm.network "private_network", ip: "192.168.20.100"
subconfig.vm.network "forwarded_port", guest: 22, host: 50020, auto_correct: true, id: "ssh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/client_pc.sh"
subconfig.vm.provision "shell", path: "https://raw.githubusercontent.com/gasida/KANS/main/4/route2.sh"
end
end
클러스터 내부를 외부에 노출
1. 파드 생성 : K8S 클러스터 내부에서만 접속
2. 서비스(Cluster Type) 연결 : K8S 클러스터 내부에서만 접속
- 동알한 애플리케이션의 다수 파드 접속을 위하여 서비스를 통해 접속
3. 서비스(NodePort Type) 연결 : 외부 클라이언트가 서비스를 통해서 클러스터 내부의 파드로 접속
- 서비스(NodePort Type)의 일부 단점을 보안한 서비스(LoadBalancer Type)도 존재
4. 서비스 종류
ClusterIP
타입
NodePort
타입
LoadBalancer
타입
K8S 서비스 구성 모드
소개 - 링크 Ssup Finda 커피고래
- User space 프록시 모드
- Redirect, DNAT를 통해서 Service로 전송한 모든 요청 Packet은 kube-proxy로 전달 => 포트 하나 당 하나의 서비스가 연결
- 즉 유저스페이스(kube-proxy)에서 커널 스페이스로 변환이 필요하여 그 만큼 비용이 발생
- kube-proxy 프로세스가 문제 시 SPOF 발생!(대응이 어렵다!)
https://medium.com/finda-tech/kubernetes-네트워크-정리-fccd4fd0ae6
- Iptables 프록시 모드
- 1번 방식인 kube-proxy가 직접 proxy의 역할을 수행하지 않고 그 역할을 전부 netfilter에게 맡긴다.
- 이를 통해 service IP를 발견하고 그것을 실제 Pod로 전달하는 것은 모두 netfilter가 담당하게 되었고,
- kube-proxy는 단순히 netfilter의 규칙을 알맞게 수정하는 것을 담당할 뿐이다. 즉 kube-proxy 는 rule 만 관리하고 실제 데이터 트래픽 처리를 직접 하지 않는다.
- kube-proxy 가 데몬셋으로 동작하여, SPOF 발생 시 자동으로 다시 띄워 동작한다! 혹시 문제가 생겨도 룰은 이미 설정되어 있으니 데이터 통신은 된다.
- 물론 룰 추가 삭제는 안되지만.. 결론은 ‘User space’ 에 비해서 좀 더 안정적이고 성능 효율도 좋다!
https://medium.com/finda-tech/kubernetes-네트워크-정리-fccd4fd0ae6
- IPVS 프록시 모드
- IPVS Mode는 Linue Kernel에서 제공하는 L4 Load Balacner인 IPVS가 Service Proxy 역할을 수행하는 Mode이다.
- Packet Load Balancing 수행시 IPVS가 iptables보다 높은 성능을 보이기 때문에 IPVS Mode는 iptables Mode보다 높은 성능을 보여준다
- IPVS 프록시 모드는 iptables 모드와 유사한 넷필터 후크 기능을 기반으로 하지만, 해시 테이블을 기본 데이터 구조로 사용하고 커널 스페이스에서 동작한다.
- 이는 IPVS 모드의 kube-proxy는 iptables 모드의 kube-proxy보다 지연 시간이 짧은 트래픽을 리다이렉션하고, 프록시 규칙을 동기화할 때 성능이 훨씬 향상됨을 의미한다.
- 다른 프록시 모드와 비교했을 때, IPVS 모드는 높은 네트워크 트래픽 처리량도 지원한다.
https://kubernetes.io/ko/docs/concepts/services-networking/service/#proxy-mode-ipvs
- eBPF 모드 + XDP
- 기존 netfilter/iptables 기반 통신
- eBPF + XDP 네트워킹 모듈
[https://deview.kr/data/deview/session/attach/1000T3_송인주_Kubernetes 클러스터에서의 고성능&고가용성 NAT Networking 시스템.pdf](https://deview.kr/data/deview/session/attach/1000_T3%EC%86%A1%EC%9D%B8%EC%A3%BC_Kubernetes%20%ED%81%B4%EB%9F%AC%EC%8A%A4%ED%84%B0%EC%97%90%EC%84%9C%EC%9D%98%20%EA%B3%A0%EC%84%B1%EB%8A%A5&%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1%20NAT%20Networking%20%EC%8B%9C%EC%8A%A4%ED%85%9C.pdf)
사용 약자
- S.IP : Source IP , 출발지(소스) IP
- D.IP : Destination IP, 도착치(목적지) IP
- S.Port : Source Port , 출발지(소스) 포트
- D.Port : Destination Port , 도착지(목적지) 포트
- NAT : Network Address Translation , 네트워크 주소 변환
- SNAT : Source IP 를 NAT 처리, 일반적으로 출발지 IP를 변환
- DNAT : Destination IP 를 NAT 처리, 일반적으로 목적지 IP와 목적지 포트를 변환
ClusterIP
클라이언트(TestPod)가
CLUSTER-IP
접속 시 해당 노드의 iptables 룰(랜덤 분산)에 의해서 DNAT 처리가 되어 목적지(backend) 파드와 통신
iptables 분산룰(정책)은 모든 노드에 자동으로 설정됨
10.96.0.1 은 CLUSTER-IP 주소
- 클러스터 내부에서만
CLUSTER-IP
로 접근 가능 => 서비스에 DNS(도메인) 접속도 가능! 서비스(ClusterIP 타입) 생성
하게 되면, apiserver -> (kubelet) -> kube-proxy -> iptables 에rule
(룰)이생성
됨모드 노드
(마스터 포함)에iptables rule 이 설정
되므로, 파드에서 접속 시 해당 노드에 존재하는 iptables rule 에 의해서 분산 접속이 됨
실습
- 목적지(backend) 파드(Pod) 생성 : 3pod.yaml
$ cat <<EOT> 3pod.yaml
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
EOT
- 클라이언트(TestPod) 생성 : netpod.yaml
$ cat <<EOT> netpod.yaml
apiVersion: v1
kind: Pod
metadata:
name: net-pod
spec:
nodeName: k8s-m
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOT
- 서비스(ClusterIP) 생성 : svc-clusterip.yaml에서 spec.ports.port 와 spec.ports.targetPort 가 어떤 의미인지 이해 필요
$ cat <<EOT> svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-clusterip
spec:
ports:
- name: svc-webport
port: 9000
targetPort: 80
selector:
app: webpod
type: ClusterIP
EOT
- 생성 및 확인
# 생성
$ kubectl apply -f **3pod**.yaml,**netpod**.yaml,**svc-clusterip**.yaml
# 모니터링
$ watch -d 'calicoctl get wep;echo; kubectl get svc,ep svc-clusterip'
# 파드와 서비스 사용 네트워크 대역 정보 확인
$ kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
"--service-cluster-ip-range=10.96.0.0/12",
"--cluster-cidr=172.16.0.0/16",
# 확인
$ calicoctl get wep
WORKLOAD NODE NETWORKS INTERFACE
webpod1 k8s-w1 172.16.158.3/32 cali778ba511bf9
webpod2 k8s-w2 172.16.184.3/32 cali798bceab6f5
webpod3 k8s-w0 172.16.34.3/32 cali10220bc3a76
$ kubectl get svc svc-clusterip
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-clusterip ClusterIP 10.105.114.73 <none> 9000/TCP 13m
# spec.ports.**port** 와 spec.ports.**targetPort** 가 어떤 의미인지 꼭 이해하자!
$ kubectl describe svc svc-clusterip
# 서비스 생성 시 엔드포인트를 자동으로 생성, 물론 수동으로 설정 생성도 가능
$ kubectl get endpoints svc-clusterip
NAME ENDPOINTS AGE
svc-clusterip 172.16.158.1:80,172.16.184.1:80,172.16.34.1:80 13m
- 클라이언트(TestPod) Shell 에서 접속 테스트 & 서비스(ClusterIP) 부하분산 접속 확인
# 클라이언트(TestPod) Shell 실행
$ kubectl exec -it net-pod -- zsh
-------------------------------
# 목적지(backend) 파드(Pod) IP로 직접 curl 접속 테스트
## POD1=<webpod1 IP주소>
## POD2=<webpod2 IP주소>
$ POD1=172.16.158.1
$ POD2=172.16.184.1
$ POD3=172.16.24.0
$ curl -s $POD1
$ curl -s $POD2
$ curl -s $POD3
# 서비스(ClusterIP) 부하분산 접속 확인
## SVC1=<svc-clusterip 의 ClusterIP주소>
$ SVC1=10.98.149.178
$ curl -s $SVC1 --connect-timeout 1
$ curl -s $SVC1:9000
Hostname: webpod3
IP: 127.0.0.1
IP: 172.16.34.3 # 접속한 웹서버의 IP 정보
RemoteAddr: 172.16.116.5:60152 # 접근한 클라이언트의 IP 정보 >> 접속한 클라이언트 IP 가 웹 서버에 로그에서 확인 가능하다!
GET / HTTP/1.1
Host: 10.105.114.73:9000 # 최초(?) 접속한 'IP/도메인' 정보
User-Agent: curl/7.78.0
Accept: */*
$ curl -s $SVC1:9000 | grep Hostname
$ curl -s $SVC1:9000 | grep Hostname
$ for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr
$ for i in {1..1000}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr
# 혹은
$ for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 1 ; done
$ for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 0.1 ; done
$ for i in {1..10000}; do curl -s $SVC1:9000 | grep Hostname; sleep 0.01 ; done
-------------------------------
# (옵션) 워커노드에서 패킷 캡쳐 확인
$ tcpdump -i any tcp port 80 -nnq
## 각각 워커노드의 파드에 연결된 CALI 의 이름을 변수에 지정
$ CALI=cali778ba511bf9
$ CALI=cali798bceab6f5
$ CALI=cali798bceab6f5
## 각 워커노드에 cali 에서 패킷 캡쳐
$ tcpdump -i $CALI tcp port 80 -nnq
$ tcpdump -i $CALI tcp port 80 -w /tmp/svc1-1.pcap
## 각 워커노드에 enp0s8 에서 패킷 캡쳐
$ tcpdump -i enp0s8 tcp port 80 or proto 4 -nnq
$ tcpdump -i enp0s8 tcp port 80 -w /tmp/svc1-2.pcap
- 클라이언트(TestPod) => 서비스(ClusterIP) 접속 시 : 3개의 목적지(backend) 파드로 랜덤 부하 분산 접속됨
서비스(ClusterIP) 부족한 점
- 클러스터 외부에서는 서비스(ClusterIP)로 접속이 불가능 => NodePort 타입으로 외부에서 접속 가능!
- IPtables 는 파드에 대한
헬스체크 기능이 없어서
문제 있는 파드에 연결 가능 => 서비스 사용, 파드에 Readiness Probe 설정으로 파드 문제 시 서비스의 엔드포인트에서 제거되게 하자!
NodePort
요약 : 외부 클라이언트가 ‘노드IP:NodePort’ 접속 시 해당 노드의 iptables 룰에 의해서 SNAT/DNAT 되어 목적지 파드와 통신 후 리턴 트래픽은 최초 인입 노드를 경유해서 외부로 되돌아감
NodePort(노드포트)는 모든 노드(마스터 포함)에 Listen
외부 클라이언트의 출발지IP도 SNAT 되어서 목적지 파드에 도착함!, 물론 DNAT 동작 포함!
- 외부에서 클러스터의 ‘서비스(NodePort)’ 로 접근 가능 => 이후에는 Cluster IP 통신과 동일!
- 모드 노드(마스터 포함)에 iptables rule 이 설정되므로, 모든 노드에 NodePort 로 접속 시 iptables rule 에 의해서 분산 접속이 됨
- Node 의 모든 Loca IP(Local host Interface IP : loopback 포함) 사용 가능 & Local IP를 지정 가능
- 쿠버네티스 NodePort 할당 범위 기본(30000-32767) & 변경하기 - 링크
실습
- 목적지(backend) 디플로이먼트(Pod) 파일 생성 : echo-deploy.yaml
$ cat <<EOT> echo-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-echo
spec:
replicas: 3
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
EOT
- 서비스(NodePort) 파일 생성 : svc-nodeport.yaml
$ cat <<EOT> svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-nodeport
spec:
ports:
- name: svc-webport
port: 9000
targetPort: 8080
selector:
app: deploy-websrv
**type: NodePort**
EOT
- 생성 및 확인
# 생성
$ kubectl apply -f echo-deploy.yaml,svc-nodeport.yaml
# 모니터링
$ watch -d 'calicoctl get wep;echo; kubectl get svc,ep svc-nodeport'
# 확인
$ kubectl get deploy,pod -o wide
# 아래 30158은 서비스(NodePort) 정보!
$ kubectl get svc svc-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nodeport **NodePort** 10.111.1.238 <none> 9000:**30158**/TCP 3m3s
$ kubectl get endpoints svc-nodeport
NAME ENDPOINTS AGE
svc-nodeport 172.16.158.5:8080,172.16.184.3:8080,172.16.34.4:8080 4m18s
# **Port** , **TargetPort** , **NodePort** 각각의 차이점의 의미를 알자!
$ kubectl describe svc svc-nodeport
- 외부 클라이언트(k8s-pc)에서 접속 테스트 & 서비스(NodePort) 부하분산 접속 확인
# NodePort 확인
$ kubectl get service svc-nodeport -o jsonpath='{.spec.ports[0].nodePort}' ;echo
30353
# NodePort 를 변수에 지정
$ export NPORT=$(kubectl get service svc-nodeport -o jsonpath='{.spec.ports[0].nodePort}')
# 노드 포트 Listen 확인 : ss 옵션 -4(ipv4) -t(TCP) -l(Listen) -n(숫자로 출력) -p(프로세스)
# 모든 노드(마스터, 워커들)에서 노드 포트가 Listen(열림)됨!
$ ss -4tlnp | egrep "(Process|$NPORT)"
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
**LISTEN** 0 4096 0.0.0.0:**30466** 0.0.0.0:* users:(("**kube-proxy**",pid=8661,fd=10))
# 파드 로그 실시간 확인 (웹 파드에 접속자의 IP가 출력)
$ kubetail -l app=deploy-websrv -f
$ kubectl logs -l app=deploy-websrv -f
# 외부 클라이언트(k8s-pc)에서 실행
--------------------
# 노드의 IP와 NodePort를 변수에 지정
## NODE1=<노드1의 IP주소>
## NODE2=<노드2의 IP주소>
## NODE3=<노드3의 IP주소>
## NPORT=<서비스의 NodePort>
$ MASTER=192.168.10.10
$ NODE1=192.168.10.101
$ NODE2=192.168.10.102
$ NODE3=192.168.10.103
$ NPORT=30353
# 서비스(NodePort) 부하분산 접속 확인
$ curl -s $MASTER:$NPORT
$ curl -s $NODE1:$NPORT
$ curl -s $NODE2:$NPORT
$ curl -s $NODE3:$NPORT
$ for i in {1..100}; do curl -s $MASTER:$NPORT| grep Hostname; done | sort | uniq -c | sort -nr
$ for i in {1..100}; do curl -s $NODE1:$NPORT | grep Hostname; done | sort | uniq -c | sort -nr
$ for i in {1..100}; do curl -s $NODE2:$NPORT | grep Hostname; done | sort | uniq -c | sort -nr
$ for i in {1..100}; do curl -s $NODE3:$NPORT | grep Hostname; done | sort | uniq -c | sort -nr
## client_address 주소는 왜 이렇게 표기 될까요? 생각해 보셔요.
$ for i in {1..100}; do curl -s $MASTER:$NPORT| grep client_address; done | sort | uniq -c | sort -nr
$ for i in {1..100}; do curl -s $NODE1:$NPORT | grep client_address; done | sort | uniq -c | sort -nr
$ for i in {1..100}; do curl -s $NODE2:$NPORT | grep client_address; done | sort | uniq -c | sort -nr
$ for i in {1..100}; do curl -s $NODE3:$NPORT | grep client_address; done | sort | uniq -c | sort -nr
-------------------------------
# (옵션) NodePort 서비스는 ClusterIP 를 포함
# CLUSTER-IP:PORT 로 접속 가능! <- 마스터노드에서 아래 실행하자!
$ kubectl get svc svc-nodeport
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nodeport **NodePort** **10.111.1.238** <none> **9000**:30158/TCP 3m3s
curl **10.111.1.238:9000**
# (옵션) 노드에서 Network Connection
$ conntrack -E
$ conntrack -L --any-nat
# (옵션) 패킷 캡쳐 확인
$ NPORT=30158
$ tcpdump -i enp0s8 tcp port $NPORT or tcp port 8080 or proto 4 -nn
$ tcpdump -i enp0s8 tcp port $NPORT or tcp port 8080 or proto 4 -w /tmp/svc2.pcap
- 외부 클라이언트 => 서비스(NodePort) 접속 시 : 3개의 목적지(backend) 파드로 랜덤 부하 분산 접속됨
- 웹 파드에서 접속자의 IP 정보 확인(logs) 시 외부 클라이언트IP 가 아닌, 노드의 IP로 SNAT 되어서 확인됨
2.6 서비스(NodePort) 부족한 점
- 외부에서 노드의 IP와 포트로 직접 접속이 필요함 → 내부망이 외부에 공개(라우팅 가능)되어 보안에 취약함 => LoadBalancer 서비스 타입으로 외부 공개 최소화 가능!
- 클라이언트 IP 보존을 위해서,
externalTrafficPolicy: local
사용 시 파드가 없는 노드 IP로 NodePort 접속 시 실패 => LoadBalancer 서비스에서 헬스체크(Probe) 로 대응 가능!
참고 자료
- K8S Docs - Service
- [이성미 강사님] 따베쿠 - 서비스 실습
- [FINDA] K8S 네트워크 정리 - 링크
- [바름]님 블로그 - IPtables 개념 - 링크
- [환영]님 블로그 - NAT - 링크
- [커피고래] 서비스 네트워크 2 - 링크
- [ssup2] Linux Netfilter/iptables conntrack Service Proxy random-fully IPVS SLB ConnectResetIssue
- NodePort 할당 범위 변경하기 - 링크