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 테스트를 진행해보자

좋은 웹페이지 즐겨찾기