본문 바로가기

카테고리 없음

K8s service

지금의 목적은 무언가 할 수 있게끔 만들어둔 서버를 실행시키고, 이걸 안정적으로 잘 써먹는데 있습니다.

kubernetes를 쓰는 관점에선 우리가 원하는 상태를 유지하기만 한다면 서버 여럿이 죽었다가 새로이 생겨나는 건 별 일이 아닙니다.

서버 하나 띄우고 애지중지 입김 불어 모셔두는 방식이 아니니까 당연하지만,

한 시간 전에 제 요청을 처리해준 172.18.0.5 pod는 지금 없을 수도 있죠.

그럼 지금 새 요청을 하려면 저는 누굴 뭐라고 불러야 될까요?

 

K8s에서는 pod들을 논리적으로 묶어서 명찰을 붙여줍니다.

필요시 그 명찰의 이름을 불러주면 됩니다. 3조!

호출되면 그 안에서 누굴 어떻게 부를지는 이 조를 결성할 때 써둔 명세에 있습니다.

여기서의 그룹화와 그 명세가 K8s의 service 중 ClusterIP입니다.

더보기

이런게 없으면 매번 어느 pod가 살아있고, 어떤 ip를 갖고 있는지 확인하여 그 ip를 사용해서 불러야겠죠.

그 사이에 그 ip를 가진 pod가 사라졌을 수도 있겠습니다만...

apiVersion: v1
kind: Service
metadata:
  name: cluster-svc
spec:
  type: ClusterIP
  selector:
    app: cluster-pod
  ports:
    - name: cluster
      port: 8080
      targetPort: 80

위 명세를 통해서 service를 생성하면,

cluster-svc라는 이름으로 app: cluster-pod 라는 label을 가진 pod들을 (관념적으로) 묶습니다.

이 때 통신시에는 service에서는 8080, pod에선 80 port을 사용한다고 약속했습니다.

 

$ kubectl get svc cluster-svc
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
cluster-svc   ClusterIP   10.96.43.118   <none>        8080/TCP   94s

cluster 내부에서 이 service는 10.96.43.118를 자동으로 할당 받았습니다. 명세에 직접 지정할 수도 있습니다. (참고)

이 ip는 service에 할당하기로 약속된 ip 범위를 기반으로 합니다.

$ kubectl -n kube-system get pods -l component=kube-controller-manager -o yaml | grep service-cluster-ip-range

      - --service-cluster-ip-range=10.96.0.0/16

 

 

이 service에 연결된 endpoint를 확인해보면, (참고 : endpoint는 endpointSlice로 대체되어가는 추세)

$ kubectl get endpoints cluster-svc
NAME          ENDPOINTS                     AGE
cluster-svc   10.244.1.2:80,10.244.1.3:80   2m2s

이 ip들은 app: cluster-pod 라는 label을 가진 pod들의 ip이고, 80은 명세에서 지정했던 targetPort 입니다.

$ kubectl get pod -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP           NODE          NOMINATED NODE   READINESS GATES
cluster-pod-1   1/1     Running   0          46m   10.244.1.2   kind-worker   <none>           <none>
cluster-pod-2   1/1     Running   0          46m   10.244.1.3   kind-worker   <none>           <none>
netshoot-pod    1/1     Running   0          46m   10.244.1.4   kind-worker   <none>           <none>

$ kubectl get pod --show-labels
NAME            READY   STATUS    RESTARTS   AGE   LABELS
cluster-pod-1   1/1     Running   0          71m   app=cluster-pod
cluster-pod-2   1/1     Running   0          71m   app=cluster-pod
netshoot-pod    1/1     Running   0          71m   <none>

 

 

 

이제 ClusterIP 덕분에 pod 하나 하나는 알바 아니게 됐죠.

근데 ClusterIP에 할당된 cluster ip는 cluster 내부의 private ip라 외부에서 호출할 수 없습니다.

외부에서 호출할 수 있는 진입점은 다른 타입의 service인 nodePort를 이용하면 얻을 수 있습니다.

(이 하나의 진입점을 위해 위에서 만들었던 ClusterIP도 생성하고 nodePort도 생성할 필요는 없습니다)

apiVersion: v1
kind: Service
metadata:
  name: nodeport-svc
spec:
  type: NodePort
  selector:
    app: nodeport-deploy
  ports:
    - name: nodeport-svc
      port: 80          # 서비스 포트 (Cluster 내부에서 사용)
      targetPort: 80    # 실제 컨테이너 포트
      nodePort: 31001   # 외부에서 접근할 NodePort

외부에서는 nodePort로 지정된 31001번을 통해 들어오고, 결과적으로는 pod의 80번 (targerPort)로 요청이 들어갑니다.

여기서 지정할 수 있는 nodePort 범위는 기본값이 30000-32767로 설정되어 있습니다. (참고)

 

위 명세로 생성된 NodePort service는 ClusterIP와 마찬가지로 service-cluster-ip-range를 기반으로 한 cluster ip를 할당 받습니다.

pod들과의 매핑도 동일한 방식으로 확인할 수 있습니다.

$ kubectl get pod,svc
NAME                                   READY   STATUS    RESTARTS   AGE
pod/nodeport-deploy-59b68567d7-cqv64   1/1     Running   0          18m
pod/nodeport-deploy-59b68567d7-ghqk5   1/1     Running   0          18m

NAME                   TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/nodeport-svc   NodePort    10.96.146.167   <none>        80:31001/TCP   18m

$ kubectl get endpoints nodeport-svc
NAME           ENDPOINTS                     AGE
nodeport-svc   10.244.1.5:80,10.244.1.6:80   20m

$ kubectl get pod -o wide
NAME                               READY   STATUS    RESTARTS   AGE    IP           NODE          NOMINATED NODE   READINESS GATES
nodeport-deploy-59b68567d7-cqv64   1/1     Running   0          20m    10.244.1.6   kind-worker   <none>           <none>
nodeport-deploy-59b68567d7-ghqk5   1/1     Running   0          20m    10.244.1.5   kind-worker   <none>           <none>

$ kubectl get pod --show-labels
NAME                               READY   STATUS    RESTARTS   AGE    LABELS
nodeport-deploy-59b68567d7-cqv64   1/1     Running   0          20m    app=nodeport-deploy,pod-template-hash=59b68567d7
nodeport-deploy-59b68567d7-ghqk5   1/1     Running   0          20m    app=nodeport-deploy,pod-template-hash=59b68567d7

 

service의 부하 분산은 K8s 부하 분산에 의존합니다.

$ curl -s http://localhost:31001 | grep Hostname
Hostname: nodeport-deploy-59b68567d7-cqv64
$ curl -s http://localhost:31001 | grep Hostname
Hostname: nodeport-deploy-59b68567d7-ghqk5
$ http://localhost:31001 | grep Hostname
Hostname: nodeport-deploy-59b68567d7-cqv64

$ for i in {1..100}; do curl -s http://localhost:31001 | grep Hostname; done | sort | uniq -c | sort -nr
  52 Hostname: nodeport-deploy-59b68567d7-cqv64
  48 Hostname: nodeport-deploy-59b68567d7-ghqk5

 

kube-proxy mode가 기본값인 iptables면 요청을 랜덤한 pod로 보내는 방식으로 부하를 분산합니다.

kube-proxy mode가 IPVS면 균등 분산합니다. (round-robin)

$ kubectl describe configmap -n kube-system kube-proxy | grep mode
mode: iptables

 

K8s LoadBalancer 타입의 service를 생성하고 클라우드 서비스사에서 제공하는 LoadBalancing 서비스와 연결하여 관리할 수도 있습니다.

 

참고

https://medium.com/finda-tech/kubernetes-네트워크-정리-fccd4fd0ae6

https://kubernetes.io/docs/concepts/services-networking/service/

https://kimalarm.tistory.com/100

https://www.reddit.com/r/kubernetes/comments/18bv777/does_a_service_load_balance/?rdt=36384

https://mokpolar.tistory.com/65