CloudNet 가시다님이 진행하는 Kubernetes Advanced Network Study 4주차 정리입니다.
1.6 sessionAffinity : ClientIP
: 클라이언트가 접속한 목적지(파드)에 고정적인 접속을 지원
: iptables 룰 타고 파드에 고정 접속!
▶ 설정 및 파드 접속 확인
- 클라이언트(TestPod) -> 서비스(ClusterIP) 접속 시 : 1개의 목적지(backend) 파드로 고정 접속됨
1.7 ClusterIP 통신 시 Hairpin NAT 필요 -> 접속하려는 클라이언트와 목적지(파드)가 동일 대상!
▶ Cluster IP에 묶은(엔드포인트) 파드에서 서비스(Cluster IP)로 접속 시, 목적지 파드가 자신(파드)으로 결정되었을 때 어떻게 해야 통신이 될까요?
1.8 서비스(ClusterIP) 부족한 점
- 클러스터 외부에서는 서비스(ClusterIP)로 접속이 불가능 -> NodePort 타입으로 외부에서 접속 가능
- IPtables는 파드에 대한 헬스체크 기능이 없어서 문제 있는 파드에 연결 가능 -> 서비스 사용, 파드에 Readiness Probe 설정으로 파드 문제 시 서비스의 엔드포인트에서 제거되게 해야한다!
- 서비스에 연동된 파드 갯수 퍼센트(%)로 랜덤 분산 방식, 세션어피니티 이외에 다른 분산 방식 불가능 -> IPVS 경우 다양한 분산 방식(알고리즘) 가능
- 목적지 파드 수가 있는 환경에서, 출발지 파드와 목적지 파드가 동일한 노드에 배치되어 있어도, 랜덤 분산으로 다른 노드에 목적지 파드로 연결 가능
1.9 심화
▶ Optimizing iptables mode performance
In iptables mode, kube-proxy creates a few iptables rules for every Service, and a few iptables rules for each endpoint IP address. In clusters with tens of thousands of Pods and Services, this means tens of thousands of iptables rules, and kube-proxy may take a long time to update the rules in the kernel when Services (or their EndpointSlices) change. You can adjust the syncing behavior of kube-proxy via options in the iptables section of the kube-proxy configuration file(which you specify via kube-proxy --config <path> )
minSyncPeriod
- The minSyncPeriod parameter sets the minimum duration between attempts to resynchronize iptables rules with the kernel. If it is 0s, then kube-proxy will always immediately synchronize the rules every time any Service or Endpoint changes. This works fine in very small clusters, but it results in a log of redundant work when lots of things change in a small time period. For example, if you have a Service backed by a Deployment with 100 pods, and you delete the Deployment, then with minSyncPeriod: 0s, kube-proxy would end up removing the Service's endpoints from the iptables rules one by one, for a total of 100 udpates. With a larger minSyncPeriod, multiple Pod deletion events would get aggregated together, so kube-proxy might instead end up making, say, 5 updates, each removing 20 endpoints, which will be much more efficient in terms of CPU, and result in the full set of changes being synchronized faster.
- The larger the value of minSyncPeriod, the more work that can be aggregated, but the downside is that each individual change may end up waiting up to the full minSyncPeriod before being processed, meaning that the iptables rules spend more time being out-of-sync with the current API server state.
- The default value of 1s should work well in most clusters, but in very large clusters it may be necessary to set it to a larger value. Especially, if kube-proxy's sync_proxy_rules_duration_seconds metric indicates an average time much larger than 1 second, then bumping up minSyncperiod may make updates more efficient.
Updating legacy minSyncPeriod configuration
- Older versions of kube-proxy updated all the rules for all Services on every sync; this led to performance issus (update lag) in large clulsters, and the recommended solution was to set a larger minSyncPeriod. Since Kubernetes v1.28, the iptables mode of kube-proxy uses a more minimal approach, only making updates where Services or Endpoint Slices hava actually changed.
- If you were previously overriding minSyncPeriod, you should try removing that override and letting kube-proxy use the default value (1s) or at least a smaller value than you were using before upgrading.
- If you are not runnning kube-proxy from Kubernetes 1.31, check the behavior and associated advice for the version that you are actually running.
syncPeriod
- The syncPeriod parameter conrols a handful of synchronization operations that are not directly related to changes in individual Services and EndpointSlices. In particular, it controls how quickly kube-proxy notices if an external component has interfered with kube-proxy's iptables rules. In large clusters, kube-proxy also only performs certain cleanup perations once every syncPeriod to avoid unnecessary work.
- For the most part, increasing syncPeriod is not expected to have much impact on performance, but in the past, it was sometimes useful to set it to a very large value (eg, 1h). This is no longer recommended, and is likely to hurt functionality more than it improves performance.
2. NodePort
2.1 통신 흐름
요약 : 외부 클라이언트가 '노드IP:NodePort' 접속 시 해당 노드의 iptables 룰에 의해서 SNAT/DNAT 되어 목적지 파드와 통신 후 리턴 트래픽은 최초 인입 노드를 경유해서 외부로 되돌아감
- 외부에서 클러스터의 '서비스(NodePort)'로 접근 가능 > 이후에는 Cluster IP 통신과 동일
- 모든 노드(마스터 포함)에 iptables rule이 설정되므로, 모든 노드에 Node Port로 접속 시 iptables rule에 의해서 분산 접속이 됨 (노드포트는 모든노드,마스터 포함에 Listen 된다)
- Node의 모든 Local IP(Local host Interface IP : loopback 포함) 사용 가능 & Local IP를 지정 가능
- 쿠버네티스 NodePort 할당 범위 기본(30000-32767) & 변경하기
2.2 실습 구성
▶ 목적지(backend) 디플로이먼트(Pod) 파일, 서비스(NodePort) 파일 생성
2.3 서비스(NodePort) 접속 확인
▶ 외부 클라이언트(mypc 컨테이너)에서 접속 테스트 & 서비스(NodePort) 부하분산 접속 확인
♣ 중요 서비스를 처리하는 경우 법적인 보안 요구사항으로, 최초 접속자(외부 클라이언트)의 IP를 수집해야합니다.
현재 상태에서는 노드의 IP로 SNAT 되어서 웹서버(파드)에서 수집을 할 수 없습니다.
2.4 IPTABLES 정책 확인
▶ iptables 정책 적용 순서 : PREROUTING -> KUBE-SERVICES -> KUBE-NODEPORTS -> KUBE-EXT -#(MARK) -> KUBE-SVC-# -> KUBE-SEP-# -> KUBE-POSTROUTING(MASQUERADE) <- KUBE-EXT-#(MARK) 규칙 과정 추가됨
- NodePort에 매칭 시 마킹 후 출발지 IP를 해당 노드에 있는 네트워크 인터페이스의 IP로 변환(SNAT)하여 목적지 파드로 전달
- 외부에서 쿠버네티스 NodePort 서비스를 통해서 접속 시에는 출발지 IP가 접속한 노드의 IP로 변환(SNAT) 되어서 목적지 파드로 접속하므로 클라이언트 IP가 보존되지 않습니다.
: -m nfacct --nfacct-name localhost_nps_accepted_pkts 추가 됨 : 패킷 flow 카운팅 - 카운트 이름 지정
2.5 externalTrafficPolicy 설정
externalTrafficPolicy : Local : NodePort로 접속 시 해당 노드에 배치된 파드로만 접속 됨, 이때 SNAT 되지 않아서 외부 클라이언트 IP가 보존됨
: 외부 클라이언틍서 파드에 배포된 NodePort를 알아야함
▶ 설정 및 파드 접속 확인
- 외부 클라이언트 -> 각각 노드 2,3 접속 시 각각 노드에 생성된 파드로만 접속됨!
- 웹 파드에서 접속자의 IP 정보 확인(logs)시 외부 클라이언트 IP가 그대로 확인됨
▶ iptables 정책 적용 확인 : PREROUTING -> KUBE-SERVICES -> KUBE-NODEPORTS -> KUBE-EXT-# -> KUBE_SVL-# -> KUBE-SEP-# <자신의 노드에 생성된 파드> <- KUBE-XLB 삭제되고, KUBE-EXT-#, KUBE-SVL-# 과정 추가됨
- 서비스에 연동된 파드이지만 다른 노드에 있는 파드의 경우 서비스 엔드포인트(SEP) 규칙이 없기 때문에, 해당 파드로 부하분산 되지 않습니다.
2.6 서비스(NodePort) 부족한 점
- 외부에서 노드의 IP와 포트로 직접 접속이 필요함 -> 내부망이 외부에 공개(라우팅 가능)되어 보안에 취약
=> LoadBalancer 서비스 타입으로 외부 공개 최소화 가능! - 클라이언트 IP 보존을 위해서, externalTrafficPolicy:local 사용 시 파드가 없는 노드 IP로 NodePort 접속 시 실패
=> LoadBalancer 서비스에서 헬스체크(Probe)로 대응 가능!
2.7 Readiness Probe + Endpoints, EndpointSlice
서비스 사용 시 Readiness Probe를 사용하여 헬스체크 실패 시 'Endpoints, EndpointSlice 출력 정보 차이'를 확인!
▶ Endpoint Slices
- 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
▶ 수퍼마리오 디플로이먼트에 Readiness Probe 설정 및 Service(NodePort) 설정 배포
2.8 Topology Aware Hint
: 트래픽이 발생한 존 내에서 네트워크 트래픽을 유지하도록 처리하는 메커니즘을 제공
: 클러스터의 파드간 동일한 존의 트래픽을 선호하는 것은 안전성, 성능(네트워크 지연 및 처리량) 혹은 비용 측면에 도움이 될 수 있습니다.
: 클라이언트가 엔드포인트를 어떻게 사용해야 하는지에 대한 제안을 포함시킴으로써 토폴로지 인지 라우팅을 가능하게 합니다.
: 이러한 접근은 엔드포인트슬라이스(EndpointSlice) 또는 엔드포인트(Endpoint) 오브젝트의 소비자가 이용할 수 있는 메타데이터를 추가하며, 이를 통해 해당 네트워크 엔드포인트의 트래픽이 근우너지에 더 가깝게 라우트될 수 있습니다.
* 동기
: 서비스의 엔드포인트를 계산할 때, 엔드포인트슬라이스 컨트롤러는 각 엔드포인트의 토폴리지를 고려하여, 엔드포인트가 특정 존에 할당되도록힌트 필드를 채운다. 그러면 kube-proxy와 같은 클러스터 구성 요소는 해당 힌트를 인식하고, 트래픽 라우팅 구성에 활용한다.
* 토폴로지 인지 힌트 사용하기
: service.kubernetes.io/topology-aware-hints 어노테이션을 auto로 설정하여 서비스에 대한 토폴로지 인지 힌트를 활성화 가능
2.9 파드 간 속도 측정
iperf3 : 서버 모드로 동작하는 단말과 클라이언트 모드로 동작하는 단말로 구성해서 최대 네트워크 대역폭 측정 - TCP, UDP, SCTP 지원
▶ 쿠버네티스 환경에서 속도 측정 테스트
- 배포 및 확인
1. TCP 5201, 측정시간 5초
2. UDP 사용, 역방향 모드 (-R)
3. TCP, 쌍방향 모드(-R)
4. TCP 다중 스트림(30개), -P(number of parallel client streams to run)
* iperf3 명령어?
: Server-Client 간 네트워크 처리량을 측정하는데 사용되는 툴!
: iperf3의 경우 5201 포트를 기본적으로 사용 (포트 허용 필요)
- 자주 사용하는 옵션 : -s(서버모드), -c(클라이언트모드), -t (시간), -p(포트변경시 포트지정)