home..

다양한 네트워크 기능들

mont-kim Docker Container Pod Container Kubernetes Service

Endpoints와 Endpointslice

그리고 Readiness Probe

Readiness Probe 를 사용하여 헬스체크 실패 시 ‘Endpoints, EndpointSlice 어떤게 다른지’ 알아보자

Endpoint Slices - k8s_Docs

  • kube-proxy 가 매번 모든 endpoint 를 watch 해야 한다
apiVersion: discovery.k8s.io/v1beta1
kind: EndpointSlice
metadata:
  name: demo-slice-1
  labels:
    kubernetes.io/service-name: demo
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.0.0.1"
    conditions:
      ready: true

Untitled

kubectl get endpointslice
NAME                      ADDRESSTYPE   PORTS   ENDPOINTS
clusterip-service-l2n9q   IPv4          8080    10.244.2.7,10.244.2.8,10.244.1.5
+ 1 more...

수퍼마리오로 Readiness Probe 테스트

설정 및 Service(Nodeport) 설정 배포


  # 모니터링
watch kubectl get pod,svc,ep,endpointslice -owide
    
# 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mario
  labels:
    app: mario
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mario
  template:
    metadata:
      labels:
        app: mario
    spec:
      tolerations:
      - key: "node-role.kubernetes.io/control-plane"
        effect: "NoSchedule"
      nodeSelector:
        node-role.kubernetes.io/control-plane: ""
      containers:
      - name: mario
        image: pengbai/docker-supermario
        readinessProbe:
          exec:
            command:
            - cat
            - healthcheck
---
apiVersion: v1
kind: Service
metadata:
  name: mario
spec:
  ports:
    - name: mario-webport
      port: 80
      targetPort: 8080
      nodePort: 30001
  selector:
    app: mario
  type: NodePort
   externalTrafficPolicy: Local
EOF

# 모니터링
watch kubectl get pod,svc,ep,endpointslice -owide
    
# 상태 확인 : ‘Endpoints, EndpointSlice 출력 정보 차이’를 알아보자
kubectl describe svc
kubectl describe ep
kubectl describe endpointslice
  
# 파드에 파일 생성
kubectl exec -it deployments/mario -- touch healthcheck
kubectl exec -it deployments/mario -- ls -l
    
# 상태 확인
kubectl describe svc
kubectl describe ep
kubectl describe endpointslice
  
# 게임 접속
echo -e "Mario Game URL = http://localhost:30001"  # macOS
echo -e "Mario Game URL = http://Y.Y.Y.Y:30001"    # Windows
  
# 파드에 파일 삭제
kubectl exec -it deployments/mario -- rm healthcheck
kubectl exec -it deployments/mario -- ls
  
# 상태 확인
kubectl describe svc
kubectl describe ep
kubectl describe endpointslice
  
# 삭제
kubectl delete deploy,svc mario

서비스 EndpointSlice 등장 배경

scaling-kubernetes-networking-with-endpointslices

image.png

EndpointSlices는 Kubernetes 네트워크 확장성 문제를 해결하기 위해 등장했습니다.

기존 Endpoints API는 서비스에 연결된 모든 Pod의 IP와 포트를 하나의 리소스로 관리했는데, 이 방식은 수천 개의 Pod을 가진 대규모 클러스터에서 성능과 확장성의 한계를 드러냈습니다. 특히, 리소스 크기 제한으로 인해 kube-proxy가 전체 클러스터에 변경 사항을 전파하는 과정에서 네트워크 부하가 크게 증가합니다.

EndpointSlices는 이러한 문제를 해결하기 위해 서비스의 네트워크 엔드포인트 정보를 여러 작은 조각으로 나누어 관리합니다. 기본적으로 각 EndpointSlice는 최대 100개의 엔드포인트를 저장하며, Pod이 추가되거나 삭제될 때 전체 리소스 대신 해당 조각만 업데이트하면 됩니다. 이 방식은 네트워크 부하를 대폭 줄이고 성능을 개선하며, 서비스가 수십만 개의 엔드포인트로 확장될 수 있도록 지원합니다.

또한, EndpointSlices는 이중 스택 서비스(Dual-Stack Services)나 지리적 토폴로지 기반 라우팅(Topology Aware Routing) 같은 새로운 기능을 가능하게 하여, 서비스가 동일한 지역 내에서 효율적으로 라우팅될 수 있도록 도와줍니다.

EndpointSlice 등장 이후

scaling-node

정말 좋아하는 게시글인 커피고래님의 블로그, 그리고 OpenAI 조직의 쿠버네티스 7500대 운영기의 게시글입니다.

운영 서버의 수가 증가하면서, API 서버에 큰 부담을 주는 요인 중 하나는 Endpoint 리소스를 Watch하는 것이었습니다. 클러스터에 있는 모든 노드에서 동작하는 서비스들, 예를 들어 kubelet이나 node-exporter 같은 서비스들은, 노드에 변경사항이 발생할 때마다 클러스터 관점에서 이를 Watch하게 됩니다.

특히 각 노드가 kube-proxy를 통해 kubelet 서비스를 감시(watching)하고 있었기 때문에, 노드가 추가되거나 삭제될 때마다 이러한 Watch 이벤트가 발생했습니다.

결과적으로, 응답에 필요한 네트워크 대역폭과 요청 수는 노드 수의 제곱(N^2)만큼 증가하게 되었고, 가끔은 1GB/s 이상의 대역폭이 요구되기도 했습니다.

Kubernetes 1.17 버전에서 도입된 EndpointSlices 덕분에 이 부하는 1000배 가까이 감소했습니다.

image.png

문서를 자세히 읽다보니 사용했던 exporter중에서 iptables라는 exporter가 보이네요. 조직에서 관리하는 iptable 메트릭이 필요하다면 검토해볼만한 메트릭으로 보입니다.

https://github.com/madron/iptables-exporter

Topology Aware Hint

TopologyAwareHint

Kubernetes 클러스터 내에서 네트워크 트래픽이 발생한 영역(zone) 내에서 처리되도록 라우팅을 조정하는 기능입니다. 이를 통해 네트워크 성능(지연 시간, 처리량)을 개선하거나 비용을 절감할 수 있습니다

< 1.27 에서는 Topology Aware Routing이라는 이름을 사용했습니다.

동작 방식

EndpointSlice 컨트롤러는 각 영역에 할당할 엔드포인트의 비율을 계산해 힌트를 추가합니다. 이 비율은 해당 영역에서 사용할 수 있는 CPU 코어 수를 기준으로 합니다. kube-proxy는 이 힌트를 기반으로 트래픽을 라우팅하며, 필요할 때 다른 영역으로 트래픽을 분산시켜 부하를 조정할 수 있습니다.

Safeguards(보호장치): 이 기능은 엔드포인트의 수가 충분하지 않거나, 특정 조건을 충족하지 못하면 기본 클러스터 전체 라우팅으로 되돌아갑니다. 이를 통해 잘못된 힌트로 인한 과부하나 성능 저하를 방지합니다.

Constraints(제약사항):

Topology Aware Routing은 트래픽이 특정 영역에 집중되거나 노드의 리소스가 균등하지 않으면 제대로 작동하지 않을 수 있습니다. 또한, 자동 확장(autoscaling)과 함께 사용할 때 예상치 못한 문제들이 발생할 수 있습니다.

클러스터가 여러 영역(멀티존)으로 분할된 경우에 유용합니다. 서비스에 연결된 Pod들의 위치 정보를 기반으로, 트래픽이 가능한 한 같은 영역 내의 엔드포인트로 라우팅되도록 도와줍니다. 이를 위해 EndpointSlice 컨트롤러는 각 엔드포인트의 토폴로지 정보를 참고하여 힌트 필드를 채워 각 엔드포인트를 특정 영역에 할당합니다. kube-proxy는 이러한 힌트를 읽어 트래픽을 가까운 엔드포인트로 라우팅합니다.

Cross-AZ_통신_비용_절감

Topology Aware Routing은 EndpointSlice API를 기반으로 동작합니다. EndpointSlice는 서비스에 연결된 Pod의 엔드포인트 정보를 여러 조각으로 나눠 관리하는 API로, 각 조각에는 Pod의 IP, 포트, 토폴로지 정보 등이 포함됩니다. Topology Aware Routing은 이 EndpointSlice에 포함된 “힌트” 정보를 활용해, 트래픽을 발생한 영역 내에서 처리하도록 라우팅 방향을 조정합니다.

EndpointSlices는 각 Pod의 위치 정보를 제공하고, Topology Aware Routing은 이 정보를 기반으로 같은 영역 내에서 트래픽이 처리되도록 돕는 역할 해주기때문에 멀티존 환경에서 네트워크 성능을 최적화합니다.

테스트를 위한 디플로이먼트와 서비스 배포


# 현재 노드 AZ 배포 확인
kubectl get node --label-columns=topology.kubernetes.io/zone
NAME                                               STATUS   ROLES    AGE   VERSION                ZONE
ip-192-168-1-225.ap-northeast-2.compute.internal   Ready    <none>   70m   v1.24.11-eks-a59e1f0   ap-northeast-2a
ip-192-168-2-248.ap-northeast-2.compute.internal   Ready    <none>   70m   v1.24.11-eks-a59e1f0   ap-northeast-2b
ip-192-168-3-228.ap-northeast-2.compute.internal   Ready    <none>   70m   v1.24.11-eks-a59e1f0   ap-northeast-2c

# 테스트를 위한 디플로이먼트와 서비스 배포

cat <<EOF | kubectl create -f -
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: websrv
        image: registry.k8s.io/echoserver:1.5
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  ports:
    - name: svc-webport
      port: 8080
      targetPort: 80
  selector:
    app: deploy-websrv
  type: ClusterIP
EOF

# 확인
kubectl get svc,ep svc-clusterip
kubectl get endpointslices -l kubernetes.io/service-name=svc-clusterip -o yaml | yh

# 접속 테스트를 수행할 클라이언트 파드 배포
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: netshoot-pod
spec:
  containers:
  - name: netshoot-pod
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF 

# 확인
kubectl get pod -owide

테스트 파드(netshoot-pod)에서 ClusterIP 접속 시 부하분산 확인 : AZ(zone) 상관없이 랜덤 확률 부하분산 동작

# 디플로이먼트 파드가 배포된 AZ(zone) 확인
kubectl get pod -l app=deploy-websrv -owide

# 테스트 파드(netshoot-pod)에서 ClusterIP 접속 시 부하분산 확인
kubectl exec -it netshoot-pod -- curl svc-clusterip | grep Hostname
Hostname: deploy-echo-7f67d598dc-h9vst

kubectl exec -it netshoot-pod -- curl svc-clusterip | grep Hostname
Hostname: deploy-echo-7f67d598dc-45trg

# 100번 반복 접속 : 3개의 파드로 AZ(zone) 상관없이 랜덤 확률 부하분산 동작
kubectl exec -it netshoot-pod -- zsh -c "for i in {1..100}; do curl -s svc-clusterip | grep Hostname; done | sort | uniq -c | sort -nr"
  35 Hostname: deploy-echo-7f67d598dc-45trg
  33 Hostname: deploy-echo-7f67d598dc-hg995
  32 Hostname: deploy-echo-7f67d598dc-h9vst

Topology Aware Hint 설정 후 ClusterIP 접속 시 부하분산 확인

  • 테스트 파드(netshoot-pod)에서 같은 AZ(zone)의 목적지 파드로만 접속
# Topology Aware Hint 설정 : 서비스에 annotate에 아래처럼 추가
kubectl annotate service svc-clusterip "service.kubernetes.io/topology-aware-hints=auto"
# 100번 반복 접속 : 테스트 파드(netshoot-pod)와 같은 AZ(zone)의 목적지 파드로만 접속
kubectl exec -it netshoot-pod -- zsh -c "for i in {1..100}; do curl -s svc-clusterip | grep Hostname; done | sort | uniq -c | sort -nr"
  100 Hostname: deploy-echo-7f67d598dc-45trg
    
# endpointslices 확인 시, 기존에 없던 hints 가 추가되어 있음 >> 참고로 describe로는 hints 정보가 출력되지 않음
kubectl get endpointslices -l kubernetes.io/service-name=svc-clusterip -o yaml | yh
...

(참고) Topology Aware Hint 설정 제거

kubectl annotate service svc-clusterip "service.kubernetes.io/topology-aware-hints -"

실습 리소스 삭제:

kubectl delete deploy deploy-echo; kubectl delete svc svc-clusterip

파드 토폴로지 분배 topologySpreadConstraints

# 디플로이먼트 배포
cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-echo
spec:
  replicas: 6
  selector:
    matchLabels:
      app: deploy-websrv
  template:
    metadata:
      labels:
        app: deploy-websrv
    spec:
      terminationGracePeriodSeconds: 0
      containers:
      - name: websrv
        image: registry.k8s.io/echoserver:1.5
        ports:
        - containerPort: 8080
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: "topology.kubernetes.io/zone"
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: deploy-websrv
EOF

# 파드 토폴로지 분배 확인 : AZ별 2개씩 파드 배포 확인
kubectl get pod -owide
© 2024 mont kim   •  Powered by Soopr   •  Theme  Moonwalk