⭐ 가시다(gasida) 님이 진행하는 KANS(**K**ubernetes **A**dvanced **N**etworking **S**tudy)3기 실습 게시글입니다.
게시글 상 소스코드, 사진에서 ****굵게**** '''코드쉘''' 에 대한 부분이 들어가있을수도 있습니다.
⭐코드에서 실습 시 사용하는 변수가 들어갈 수 있습니다. 아래의 정보를 사용하고있습니다.
#route53 Domain: jjongguet.com
#AWS Secret Key: jjongkey
#Aws .pem Key: jjong-key.pem
Kubernetes 의 Pod 를외부로 노출하기 위한 방법
- 파드 생성 : K8S 클러스터 내부에서만 접속
2. 서비스(Cluster Type) 연결 : K8S 클러스터 내부에서만 접속
- 동일한 애플리케이션의 다수의 파드의 접속을 용이하게 하기 위한 서비스에 접속
- 고정 접속(호출) 방법을 제공 : 흔히 말하는 ‘고정 VirtualIP’ 와 ‘Domain주소’ 생성
3. 서비스(NodePort Type) 연결 : 외부 클라이언트가 서비스를 통해서 클러스터 내부의 파드로 접속
- 서비스(NodePort Type)의 일부 단점을 보완한 서비스(LoadBalancer Type) 도 있습니다!
서비스 종류
ClusterIP
타입
이번스터디의 핵심: iptables 에 의해 트래픽이 변경된다. 라는 게 핵심
NodePort
타입
LoadBalancer
타입
Service(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 에 의해서 분산 접속이 됨
3pod.yaml
**cat <<EOT> 3pod.yaml**
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
---
apiVersion: v1
kind: Pod
metadata:
name: **webpod3**
labels:
app: webpod
spec:
**nodeName: myk8s-worker3**
containers:
- name: container
image: traefik/whoami
terminationGracePeriodSeconds: 0
**EOT**
netpod.yaml
**cat <<EOT> netpod.yaml**
apiVersion: v1
kind: Pod
metadata:
name: **net-pod**
spec:
**nodeName: myk8s-control-plane**
containers:
- name: netshoot-pod
image: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
**EOT**
svc-clusterip.yaml
**cat <<EOT> svc-clusterip.yaml**
apiVersion: v1
kind: **Service**
metadata:
name: svc-clusterip
spec:
ports:
- name: svc-webport
port: 9000 # 서비스 IP 에 접속 시 사용하는 포트 port 를 의미
targetPort: 80 # 타킷 targetPort 는 서비스를 통해서 목적지 파드로 접속 시 해당 파드로 접속하는 포트를 의미
selector:
app: webpod # 셀렉터 아래 app:webpod 레이블이 설정되어 있는 파드들은 해당 서비스에 연동됨
type: **ClusterIP** # 서비스 타입
**EOT**
생성 및 확인
# 모니터링
watch -d 'kubectl get pod -owide ;echo; kubectl get svc,ep svc-clusterip'
# 생성
kubectl apply -f **3pod**.yaml,**netpod**.yaml,**svc-clusterip**.yaml
# 파드와 서비스 사용 네트워크 대역 정보 확인
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
# 확인
kubectl get pod -owide
kubectl get svc svc-clusterip
# spec.ports.**port** 와 spec.ports.**targetPort** 가 어떤 의미인지 꼭 이해하자!
kubectl describe svc svc-clusterip
# 서비스 생성 시 엔드포인트를 자동으로 생성, 물론 수동으로 설정 생성도 가능
kubectl get endpoints svc-clusterip
kubectl get endpointslices -l kubernetes.io/service-name=svc-clusterip
요약
⇒ spec.ports.port 는 service 가 받는 port
번호
⇒ spec.ports.targetPort 는 service 에 연결된 pod 가 받는 port
번호
Service(ClsuterIP) 접속확인
# webpod 파드의 IP 를 출력
kubectl get pod -l app=webpod -o jsonpath="{.items[*].status.podIP}"
# webpod 파드의 IP를 변수에 지정
WEBPOD1=$(kubectl get pod webpod1 -o jsonpath={.status.podIP})
WEBPOD2=$(kubectl get pod webpod2 -o jsonpath={.status.podIP})
WEBPOD3=$(kubectl get pod webpod3 -o jsonpath={.status.podIP})
echo $WEBPOD1 $WEBPOD2 $WEBPOD3
# net-pod 파드에서 webpod 파드의 IP로 직접 curl 로 반복 접속
for pod in $WEBPOD1 $WEBPOD2 $WEBPOD3; do kubectl exec -it net-pod -- curl -s $pod; done
for pod in $WEBPOD1 $WEBPOD2 $WEBPOD3; do kubectl exec -it net-pod -- curl -s $pod | grep Hostname; done
for pod in $WEBPOD1 $WEBPOD2 $WEBPOD3; do kubectl exec -it net-pod -- curl -s $pod | grep Host; done
for pod in $WEBPOD1 $WEBPOD2 $WEBPOD3; do kubectl exec -it net-pod -- curl -s $pod | egrep 'Host|RemoteAddr'; done
# 서비스 IP 변수 지정 : svc-clusterip 의 ClusterIP주소
SVC1=$(kubectl get svc svc-clusterip -o jsonpath={.spec.clusterIP})
echo $SVC1
# 위 서비스 생성 시 kube-proxy 에 의해서 iptables 규칙이 모든 노드에 추가됨
docker exec -it myk8s-control-plane **iptables -t nat -S | grep $SVC1**
**for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i iptables -t nat -S | grep $SVC1; echo; done**
*-A KUBE-SERVICES -**d 10.200.1.52**/32 -p tcp -m comment --comment "default/svc-clusterip:svc-webport cluster IP" -m tcp **--dport 9000** -j KUBE-SVC-KBDEBIL6IU6WL7RF*
## (참고) ss 툴로 tcp listen 정보에는 없음 , 별도 /32 host 라우팅 추가 없음 -> 즉, iptables rule 에 의해서 처리됨을 확인
docker exec -it myk8s-control-plane ss -tnlp
docker exec -it myk8s-control-plane ip -c route
# TCP 80,9000 포트별 접속 확인 : 출력 정보 의미 확인
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000 | grep Hostname
kubectl exec -it net-pod -- curl -s --connect-timeout 1 $SVC1:9000 | grep Hostname
# 서비스(ClusterIP) 부하분산 접속 확인
## for 문을 이용하여 SVC1 IP 로 100번 접속을 시도 후 출력되는 내용 중 반복되는 내용의 갯수 출력
## 반복해서 실행을 해보면, SVC1 IP로 curl 접속 시 3개의 파드로 대략 33% 정도로 부하분산 접속됨을 확인
kubectl exec -it net-pod -- zsh -c "for i in {1..10}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
kubectl exec -it net-pod -- zsh -c "for i in {1..1000}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
혹은
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 1; done"
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 0.1; done"
kubectl exec -it net-pod -- zsh -c "for i in {1..10000}; do curl -s $SVC1:9000 | grep Hostname; sleep 0.01; done"
# conntrack 확인
docker exec -it myk8s-control-plane bash
----------------------------------------
conntrack -h
conntrack -E
conntrack -C
conntrack -S
conntrack -L --src 10.10.0.6 # net-pod IP
conntrack -L --dst $SVC1 # service ClusterIP
exit
----------------------------------------
# (참고) Link layer 에서 동작하는 ebtables
ebtables -L
⇒ Service(ClusterIP) 로 접근할때에는 iptablers 에 의해 영향을 받으며, 이때는 랜덤 부하분산 접속이 진행된다.
IPTABLES 정책 확인
iptables 정책 적용 순서
: PREROUTING → KUBE-SERVICES → KUBE-SVC-### → KUBE-SEP-#<파드1> , KUBE-SEP-#<파드2> , KUBE-SEP-#<파드3>
결론 : 내부에서 클러스터 IP로 접속 시, PREROUTE(nat) 에서 DNAT(3개 파드) 되고, POSTROUTE(nat) 에서 SNAT 되지 않고 나간다!
# 컨트롤플레인에서 확인 : 너무 복잡해서 리턴 트래픽에 대해서는 상세히 분석 정리하지 않습니다.
**docker exec -it myk8s-control-plane bash**
----------------------------------------
# iptables 확인
iptables -t filter -S
**iptables -t nat -S**
iptables -t nat -S | wc -l
iptables -t mangle -S
# iptables 상세 확인 - 매칭 패킷 카운트, 인터페이스 정보 등 포함
iptables -nvL -t filter
**iptables -nvL -t nat**
iptables -nvL -t mangle
# rule 갯수 확인
iptables -nvL -t filter | wc -l
iptables -nvL -t nat | wc -l
# 규칙 패킷 바이트 카운트 초기화
iptables -t filter --zero; iptables -t nat --zero; iptables -t mangle --zero
# 정책 확인 : 아래 정책 내용은 핵심적인 룰(rule)만 표시했습니다!
**iptables -t nat -nvL**
**iptables -v --numeric --table nat --list PREROUTING** | column -t
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
778 46758 **KUBE-SERVICES** all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
**iptables -v --numeric --table nat --list KUBE-SERVICES** | column
# 바로 아래 룰(rule)에 의해서 서비스(ClusterIP)를 인지하고 처리를 합니다
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
92 5520 **KUBE-SVC-KBDEBIL6IU6WL7RF** tcp -- * * 0.0.0.0/0 **10.105.114.73** /* default/svc-clusterip:svc-webport cluster IP */ **tcp dpt:9000**
iptables -v --numeric --table nat --list KUBE-SVC-KBDEBIL6IU6WL7RF | column
watch -d 'iptables -v --numeric --table nat --list KUBE-SVC-KBDEBIL6IU6WL7RF'
SVC1=$(kubectl get svc svc-clusterip -o jsonpath={.spec.clusterIP})
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; sleep 1; done"
# SVC-### 에서 **랜덤 확률**(대략 33%)로 SEP(Service EndPoint)인 각각 파드 IP로 DNAT 됩니다!
## 첫번째 룰에 일치 확률은 33% 이고, 매칭되지 않을 경우 아래 2개 남을때는 룰 일치 확률은 50%가 됩니다. 이것도 매칭되지 않으면 마지막 룰로 100% 일치됩니다
Chain KUBE-SVC-KBDEBIL6IU6WL7RF (1 references)
pkts bytes target prot opt in out source destination
38 2280 KUBE-**SEP-6TM74ZFOWZXXYQW6** all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/svc-clusterip:svc-webport */ statistic mode random probability **0.33333333349**
29 1740 KUBE-**SEP-354QUAZJTL5AR6RR** all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/svc-clusterip:svc-webport */ statistic mode random probability **0.50000000000**
25 1500 KUBE-**SEP-PY4VJNJPBUZ3ATEL** all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/svc-clusterip:svc-webport */
iptables -v --numeric --table nat --list KUBE-SEP-<각자 값 입력>
Chain KUBE-SEP-6TM74ZFOWZXXYQW6 (1 references)
pkts bytes target prot opt in out source destination
38 2280 **DNAT** tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/svc-clusterip:svc-webport */ **tcp to:172.16.158.3:80**
iptables -v --numeric --table nat --list KUBE-SEP-354QUAZJTL5AR6RR | column -t
Chain KUBE-SEP-6TM74ZFOWZXXYQW6 (1 references)
pkts bytes target prot opt in out source destination
29 1500 **DNAT** tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/svc-clusterip:svc-webport */ **tcp to:172.16.184.3:80**
iptables -v --numeric --table nat --list KUBE-SEP-PY4VJNJPBUZ3ATEL | column -t
Chain KUBE-SEP-6TM74ZFOWZXXYQW6 (1 references)
pkts bytes target prot opt in out source destination
25 1740 **DNAT** tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/svc-clusterip:svc-webport */ **tcp to:172.16.34.3:80**
iptables -t nat --zero
iptables -v --numeric --table nat --list POSTROUTING | column; echo ; iptables -v --numeric --table nat --list KUBE-POSTROUTING | column
watch -d 'iptables -v --numeric --table nat --list POSTROUTING; echo ; iptables -v --numeric --table nat --list KUBE-POSTROUTING'
# POSTROUTE(nat) : 0x4000(2진수로 0100 0000 0000 0000, 10진수 16384) 마킹 되어 있지 않으니 RETURN 되고 그냥 빠져나가서 SNAT 되지 않는다!
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
572 35232 **RETURN** all -- * * 0.0.0.0/0 0.0.0.0/0 **mark match ! 0x4000/0x4000**
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK xor 0x4000
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */ random-fully
**iptables -t nat -S | grep 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
**...
exit**
----------------------------------------
# 위 서비스 생성 시 kube-proxy 에 의해서 iptables 규칙이 모든 노드에 추가됨을 한번 더 확이
docker exec -it **myk8s-control-plane** iptables -v --numeric --table nat --list **KUBE-SVC-KBDEBIL6IU6WL7RF**
...
**for i in control-plane worker worker2 worker3; do echo ">> node myk8s-$i <<"; docker exec -it myk8s-$i** iptables -v --numeric --table nat --list **KUBE-SVC-KBDEBIL6IU6WL7RF; echo; done
...**
Pod 1개 장애 발생 시 동작
기초상태
Pod 모니터링
# 터미널1 >> ENDPOINTS 변화를 잘 확인해보자!
watch -d 'kubectl get pod -owide;echo; kubectl get svc,ep svc-clusterip;echo; kubectl get endpointslices -l kubernetes.io/service-name=svc-clusterip'
# 터미널2
SVC1=$(kubectl get svc svc-clusterip -o jsonpath={.spec.clusterIP})
kubectl exec -it net-pod -- zsh -c "while true; do curl -s --connect-timeout 1 $SVC1:9000 | egrep 'Hostname|IP: 10'; date '+%Y-%m-%d %H:%M:%S' ; echo ; sleep 1; done"
혹은
kubectl exec -it net-pod -- zsh -c "for i in {1..100}; do curl -s $SVC1:9000 | grep Hostname; done | sort | uniq -c | sort -nr"
Pod삭제 후 확인
# (방안1) 파드3번 삭제 >> 서비스의 엔드포인트가 어떻게 변경되는지 확인 하자!, 지속적인 curl 접속 결과 확인!, for 문 실행 시 결과 확인!, 절체 시간(순단) 확인!
kubectl delete pod webpod3
# (방안1) 결과 확인 후 다시 파드 3번 생성 >> **서비스 디스커버리**!
kubectl apply -f 3pod.yaml
---------------------------------
# (방안2) 파드3번에 레이블 삭제
kubectl get pod --show-labels
## 레이블(라벨)의 키값 바로 뒤에 하이픈(-) 입력 시 해당 레이블 삭제됨! >> 레이블과 셀렉터는 쿠버네티스 환경에서 매우 많이 활용된다!
kubectl label pod webpod3 app-
kubectl get pod --show-labels
# (방안2) 결과 확인 후 파드3번에 다시 레이블 생성
kubectl label pod webpod3 app=webpod
클러스터 외부에서 서비스로 접근가능한지 확인
docker ps
docker exec -it **mypc** ping -c 1 172.18.0.2
docker exec -it **mypc** curl <SVC1_IP>:9000
docker exec -it **mypc** curl <Pod IP>:80
'외부활동' 카테고리의 다른 글
[OSSCA] LoxiLB 1주차: yaml, swagger, cmd, api (0) | 2024.09.28 |
---|---|
[KANS3] Pod & Pause container (1) | 2024.09.07 |
[KANS3] Kubernetes, kind (0) | 2024.09.07 |
[KANS3] 도커 없이 컨테이너 만들기 (5) | 2024.09.01 |
[KANS3] Docker (2) | 2024.09.01 |