KANS 스터디 4주차 - Service(Cluster IP & NodePort) - 2편(실습 및 과제)
개요
4주차에서 학습한 Cluster IP & NodePort중에서 NodePort를 활용한 서비스 외부 접속을 테스트 해보려한다.
이때, 샘플 애플리케이션에서 Readiness Probe 테스트를 중점적으로 해 볼 예정이다.
📢 참고 : 1편 실습 내용은 복습이 부족하여 주중에 다시 업로드 예정!
실습1
가장 기본적으로 Nginx 파드를 배포하고 NodePort로 접속해보는 실습을 해보려한다.
YAML 작성 배포
Deployments, SVC(NodePort) 명세 작성해보자. 이때, 필자는 nodePort: 3000
로 지정하였지만 직접 할당하지 않을 경우에는 30000~32767 사이 대역에서 랜덤으로 할당된다.
- Nginx Deployments 배포
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
- Nginx SVC 배포
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30000
type: NodePort
배포 및 접속확인
- 배포된 Pod, SVC, EP 확인
k get pods,svc,ep -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-deployment-66b6c48dd5-ghb6m 1/1 Running 0 4m18s 172.16.24.3 k8s-w3 <none> <none>
pod/nginx-deployment-66b6c48dd5-mwppm 1/1 Running 0 4m18s 172.16.184.2 k8s-w2 <none> <none>
pod/nginx-deployment-66b6c48dd5-r2lvw 1/1 Running 0 4m18s 172.16.158.2 k8s-w1 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d1h <none>
service/nginx-svc NodePort 10.104.129.47 <none> 80:30000/TCP 4m19s app=nginx
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.10.10:6443 5d1h
endpoints/nginx-svc 172.16.158.2:80,172.16.184.2:80,172.16.24.3:80 4m18s
- 접속 확인
브라우저에서 NodePort를 통해 정상적으로 접속되는 것을 확인해보았다.
실습2
이번에는 파드에 환경변수를 지정하고 배치된 노드의 정보를 출력하는 실습을 해보자
YAML 작성
환경변수를 사용하여 각 파드가 배치된 노드 및 파드의 정보를 index.html
에 넣도록 하는 YAML을 작성해 보았다.
💡 참고 : initContainer와 emptyDir을 사용하여 초기 정보를 index.html에 저장
위 그림과 비슷한 형태로 initContainer가 생성될 때 파드의 환경변수(노드명,파드명,파드IP)를 index.html파일에 저장하고 이를 emptyDir 볼륨으로 공유하는 형태이다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
initContainers:
- name: init-myservice
image: busybox:1.28
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
- name: MY_POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: MY_POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
command:
- /bin/sh
- -c
- |
echo "POD NAME : $MY_POD_NAME" >> /usr/share/nginx/html/index.html;
echo "POD IP : $MY_POD_IP" >> /usr/share/nginx/html/index.html;
echo "NODE NAME : $MY_NODE_NAME" >> /usr/share/nginx/html/index.html;
volumeMounts:
- name: nodeName-vol
mountPath: /usr/share/nginx/html
containers:
- name: nginx
image: nginx:1.14.2
volumeMounts:
- name: nodeName-vol
mountPath: /usr/share/nginx/html
ports:
- containerPort: 80
volumes:
- name: nodeName-vol
emptyDir: {}
배포 및 접속확인
- 배포된 Pod, SVC, EP 확인
k get pods,svc,ep -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx-deployment-5bcd9c849d-c7g8k 1/1 Running 0 79s 172.16.24.10 k8s-w3 <none> <none>
pod/nginx-deployment-5bcd9c849d-lvrn4 1/1 Running 0 85s 172.16.158.9 k8s-w1 <none> <none>
pod/nginx-deployment-5bcd9c849d-tdrz6 1/1 Running 0 82s 172.16.184.10 k8s-w2 <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d2h <none>
service/nginx-svc NodePort 10.110.114.237 <none> 80:30000/TCP 39m app=nginx
NAME ENDPOINTS AGE
endpoints/kubernetes 192.168.10.10:6443 5d2h
endpoints/nginx-svc 172.16.158.9:80,172.16.184.10:80,172.16.24.10:80 39m
- Cluster IP로 curl 테스트
root@k8s-m:~# curl 10.110.114.237
POD NAME : nginx-deployment-5bcd9c849d-c7g8k
POD IP : 172.16.24.10
NODE NAME : k8s-w3
curl 10.110.114.237
POD NAME : nginx-deployment-5bcd9c849d-lvrn4
POD IP : 172.16.158.9
NODE NAME : k8s-w1
curl 10.110.114.237
POD NAME : nginx-deployment-5bcd9c849d-tdrz6
POD IP : 172.16.184.10
NODE NAME : k8s-w2
- NodePort로 curl 테스트
curl 192.168.10.10:30000
POD NAME : nginx-deployment-5bcd9c849d-lvrn4
POD IP : 172.16.158.9
NODE NAME : k8s-w1
curl 192.168.10.10:30000
POD NAME : nginx-deployment-5bcd9c849d-tdrz6
POD IP : 172.16.184.10
NODE NAME : k8s-w2
curl 192.168.10.10:30000
POD NAME : nginx-deployment-5bcd9c849d-c7g8k
POD IP : 172.16.24.10
NODE NAME : k8s-w3
정상적으로 각 파드들이 적절하게 노드별로 스케쥴링 된 것을 확인할 수 있었다.
실습3
이전 실습까지는 마스터 노드IP를 통해 NodePort에 접속을 테스트하였다. 이번에는 각 노드별로 NodePort 호출 시 어떻게 동작하는지, 그리고 client_address
가 어떻게 남는지 확인해보자.
📢 그림 및 이미지 출처 : 가시다님
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
---
apiVersion: v1
kind: Service
metadata:
name: svc-nodeport
spec:
ports:
- name: svc-webport
port: 9000
targetPort: 8080
selector:
app: deploy-websrv
type: NodePort
접속 테스트
- 변수 설정
# 노드의 IP와 NodePort를 변수에 지정
## MASTER=<마스터의 IP주소>
## 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=30813
- 서비스(NodePort) 부하분산 접속 확인
for i in {1..100}; do curl -s $MASTER:$NPORT| grep Hostname; done | sort | uniq -c | sort -nr
36 Hostname: deploy-echo-65f76f6d64-78zrh
32 Hostname: deploy-echo-65f76f6d64-wrhtr
32 Hostname: deploy-echo-65f76f6d64-mmckl
for i in {1..100}; do curl -s $NODE1:$NPORT | grep Hostname; done | sort | uniq -c | sort -nr
38 Hostname: deploy-echo-65f76f6d64-mmckl
31 Hostname: deploy-echo-65f76f6d64-wrhtr
31 Hostname: deploy-echo-65f76f6d64-78zrh
for i in {1..100}; do curl -s $NODE2:$NPORT | grep Hostname; done | sort | uniq -c | sort -nr
44 Hostname: deploy-echo-65f76f6d64-wrhtr
36 Hostname: deploy-echo-65f76f6d64-78zrh
20 Hostname: deploy-echo-65f76f6d64-mmckl
for i in {1..100}; do curl -s $NODE3:$NPORT | grep Hostname; done | sort | uniq -c | sort -nr
35 Hostname: deploy-echo-65f76f6d64-wrhtr
35 Hostname: deploy-echo-65f76f6d64-mmckl
30 Hostname: deploy-echo-65f76f6d64-78zrh
앞서 실습에서 확인한 것 처럼 모두 동일하진 않지만 어느정도 비슷한 비율로 분산되어 접속된 것으로 확인된다.
- client_address 확인
for i in {1..100}; do curl -s $MASTER:$NPORT| grep client_address; done | sort | uniq -c | sort -nr
100 client_address=192.168.10.10
for i in {1..100}; do curl -s $NODE1:$NPORT | grep client_address; done | sort | uniq -c | sort -nr
67 client_address=192.168.10.101
33 client_address=10.0.2.15
for i in {1..100}; do curl -s $NODE2:$NPORT | grep client_address; done | sort | uniq -c | sort -nr
65 client_address=192.168.10.102
35 client_address=10.0.2.15
for i in {1..100}; do curl -s $NODE3:$NPORT | grep client_address; done | sort | uniq -c | sort -nr
68 client_address=192.168.10.103
32 client_address=10.0.2.15
여기서 확인할 내용은 바로 client_address
가 동일하지 않고 Node의 IP 주소로 출력되고 있다는 것이다. 그 이유는 노드의 IP로 SNAT되어 접속되기 때문이다.
기본 설정은 externalTrafficPolicy: Cluster
이므로 externalTrafficPolicy: Local
로 변경하고 client_address가
를 확인해보자
Node1:NodePort
접속시 Node1에 생성된 파드(Pod1)로만 접속됨
- Node3에 파드가 없을 경우에 접속 시 연결 실패됨!
- 외부 클라이언트의 IP 주소(아래 출발지IP: 50.1.1.1)가 노드의 IP로 SNAT 되지 않고 서비스(backend) 파드까지 전달됨!
이제 기존 설정을 변경하여 client_address
가 유지되도록 설정을 변경해보자
# 기본 정보 확인
kubectl get svc svc-nodeport -o json | grep 'TrafficPolicy"'
externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster
# 기존 통신 연결 정보(conntrack) 제거 후 아래 실습 진행하자!
(모든 노드에서)
conntrack -F
conntrack v1.4.5 (conntrack-tools): connection tracking table has been emptied.
# externalTrafficPolicy: local 설정 변경
kubectl get svc svc-nodeport -o yaml | sed -e "s/externalTrafficPolicy: Cluster/externalTrafficPolicy: Local/" | kubectl apply -f -
kubectl get svc svc-nodeport -o json | grep 'TrafficPolicy"'
"externalTrafficPolicy": "Local",
"internalTrafficPolicy": "Cluster",
- client_address 테스트
# Node1 Curl 테스트
curl -s --connect-timeout 1 $NODE1:$NPORT
Hostname: deploy-echo-65f76f6d64-mmckl
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.0 - lua: 10008
Request Information:
client_address=192.168.10.10
method=GET
real path=/
query=
request_version=1.1
request_uri=http://192.168.10.101:8080/
Request Headers:
accept=*/*
host=192.168.10.101:30813
user-agent=curl/7.68.0
Request Body:
-no body in request-
for i in {1..100}; do curl -s --connect-timeout 1 $NODE1:$NPORT | egrep 'Hostname|client_address'; done | sort | uniq -c | sort -nr
100 Hostname: deploy-echo-65f76f6d64-mmckl
100 client_address=192.168.10.10
# Node2 Curl 테스트
curl -s --connect-timeout 1 $NODE2:$NPORT
Hostname: deploy-echo-65f76f6d64-wrhtr
Pod Information:
-no pod information available-
Server values:
server_version=nginx: 1.13.0 - lua: 10008
Request Information:
client_address=192.168.10.10
method=GET
real path=/
query=
request_version=1.1
request_uri=http://192.168.10.102:8080/
Request Headers:
accept=*/*
host=192.168.10.102:30813
user-agent=curl/7.68.0
Request Body:
-no body in request-
for i in {1..100}; do curl -s --connect-timeout 1 $NODE2:$NPORT | egrep 'Hostname|client_address'; done | sort | uniq -c | sort -nr
100 Hostname: deploy-echo-65f76f6d64-wrhtr
100 client_address=192.168.10.10
externalTrafficPolicy: Local
로 변경 후 client_address
주소가 master
노드의 IP 주소인 192.168.10.10로 일정하게 찍히는 것으로 확인이 된다.
- 웹 브라우저 접속 확인
웹 브라우저에서 확인해보니 PC에 할당된 IP 주소인 192.168.10.1이 client_address
로 확인된다.
실습4
이번에는 Readiness Probe 테스트를 진행해보자
Author And Source
이 문제에 관하여(KANS 스터디 4주차 - Service(Cluster IP & NodePort) - 2편(실습 및 과제)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hyungwook/KANS-스터디-4주차-ServiceCluster-IP-NodePort-2편과제저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)