스터디 목차
- 1주차
- 컨테이너 격리 - https://malwareanalysis.tistory.com/248
- 네트워크 네임스페이스 - https://malwareanalysis.tistory.com/249
- 2주차
- Flannel CNI: https://malwareanalysis.tistory.com/254
- pause 컨테이너: https://malwareanalysis.tistory.com/255
- 3주차
- calico 기본 통신과정: https://malwareanalysis.tistory.com/256
- ccalico 모드: https://malwareanalysis.tistory.com/264
- 4주차
- service와 kube-proxy iptables모드: https://malwareanalysis.tistory.com/265
- 5주차
- coredns 동작이해: https://malwareanalysis.tistory.com/267
- MetalLB L2동작 이해: https://malwareanalysis.tistory.com/271
- MetalLB BGP동작 이해: https://malwareanalysis.tistory.com/272
- 6주차
- Ingress 컨셉 이해: https://malwareanalysis.tistory.com/277
- Ingress 동작 이해: https://malwareanalysis.tistory.com/278
- Ingress https 적용: https://malwareanalysis.tistory.com/283
- 7주차
- Cilium CNI 등장배경: https://malwareanalysis.tistory.com/288
- Cilium CNI 살펴보기: https://malwareanalysis.tistory.com/289
- Cilium CNI pod 통신: https://malwareanalysis.tistory.com/290
- Cilium CNI service 통신: https://malwareanalysis.tistory.com/292
- 7주차 과제: https://malwareanalysis.tistory.com/294
안녕하세요. 이 글은 facebook 쿠버네티스 그룹에서 올라온 "쿠버네티스 네트워크 스터디" 4주차 내용을 정리했습니다.
스터디 모집글: https://www.facebook.com/groups/k8skr/posts/3202691746679143
1. 4주차 첫번째 주제
4주차 첫번째 주제는 쿠버네티스 서비스 리소스 동작모드와 iptables모드에서 clusterIP 동작흐름을 이해하는 시간을 갖었습니다.
2. Pod IP를 이용한 애플리케이션 통신의 문제점
쿠버네티스로 서비스하는 애플리케이션은 pod를 수동으로 삭제하고 다시 실행하거나 레플리카컨트롤러(ReplicaController)를 이용하여 pod가 죽더라도 다시 pod를 실행하는 상황이 자주 일어납니다. 이 때, pod가 재실행되면 CNI에 의해서 pod IP가 재할당될 수 있습니다. 재할당된 pod IP때문에 pod IP를 하드코딩한 애플리케이션은 더 이상 재실행된 pod와 통신이 되지 않습니다.
예를 들어, 백엔드<->프론트 pod가 통신되는 구조에서 백엔드 pod가 알 수없는 이유로 다시 재실행되었다고 가정해봅시다. 백엔드 pod의 IP가 재할당 되어 더이상 프론트는 백엔드 pod와 통신하지 못하고 서비스 장애가 발생합니다.
3. pod IP 재할당으로 통신이 되지 않는 문제 해결
쿠버네티스는 pod IP가 변경되어도 계속해서 통신할 수 있도록 pod 네트워크 레벨을 추상화하여 통신 문제를 해결했습니다. pod 네트워크로 패킷을 전달하는 프록시 개념을 도입했고 프록시를 구현한 쿠버네티스 리소스가 서비스입니다. 그리고 프록시가 동작할 수 있도록 각 노드 설정을 담당하는 쿠버네티스 컴퍼넌트가 kube-proxy입니다.
서비스는 pod 네트워크 패킷 전달 이외에 로드 밸런싱, 서비스 디스커버리(DNS)기능을 지원합니다. 공식문서에서 친절히 설명되어 있습니다.
로드 밸런싱 기능은 pod를 수동으로 여러개 띄우거나 replica controller을 이용하여 pod를 2개 이상 실행할 때 동작합니다.
4. 서비스 특징
4.1 Endpoints
서비스가가 패킷을 전달하고자 하는 pod집합을 endpoints라고 합니다.
endpoint 모니터가 상시 실행되어 pod 종료 또는 장애가 발생하면 endpoints에서 해당 pod를 제거합니다. 반대로, 새로운 pod가 생성되면 endpoints에 자동으로 포합시킵니다. 쿠버네티스 관리자가 일일이 endpoints를 관리하는 것이 아니라 쿠버네티스 자동으로 관리해줍니다!
endpoints에 pod를 속하게 하려면 selector과 라벨을 사용합니다.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
4.2 Ports와 TargetPorts
서비스가 가지고 있는 포트를 ports라고 부릅니다. 그리고 TargetPort와 연결된 endpoints 포트를 targetports라고 합니다. 아래 그림에서는 frontend pod가 백엔드 서비스 ports로 접속하면 pod ports로 패킷이 전달됩니다.
yaml파일에서는 targetPorts와 port이외에 프로토콜을 설정할 수 있습니다.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
4.3 서비스 타입
서비스 타입은 서비스에 접근하는 범위를 관리합니다. 서비스 타입은 총 3가지가 있습니다.
- ClusterIP: 쿠버네티스 클러스터에서만 서비스에 접근
- NodePort: 외부에서 노드를 통해서 서비스에 접근
- LoadBalancer: 외부 로드밸런서(예: 클라우드 공급자)를 통해서 서비스에 접근
5. kube-proxy
[챕터 4]에서는 서비스와 pod가 연결되는 큰 그림을 설명했습니다. [챕터 5]에서는 관계를 동작시키게 하는 kube-proxy에 대해 살펴보겠습니다.
5.1 서비스 가상IP
[챕터 4.2]에서 살펴본것 처럼 서비스는 포트를 가지고 가상IP를 가지게 됩니다. 공식문서(https://kubernetes.io/ko/docs/concepts/services-networking/service/#%EA%B0%80%EC%83%81-ip%EC%99%80-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%94%84%EB%A1%9D%EC%8B%9C)에 언급된 것처럼 kube-proxy가 서비스의 가상IP를 구현하게 됩니다. 포트 또한 kube-proxy가 관리하게 됩니다.
5.2 동작 모드
kube-proxy는 서비스 가상IP, 서비스 포트뿐만 아니라 서비스 네트워크 통신을 어떻게 할지 설정합니다. 설정방법 종류를 kube-proxy동작 모드라고 부르고 3가지가 있습니다. 이 글에서는 iptables동작 모드를 기준으로 설명합니다. 각 동작모드는 서비스 3가지 타입 통신방법(ClusterIP, NodePort, LoadBalancer)에 영향을 끼칩니다.
5.3 모든 노드에 서비스를 사용할 수 있는 이유
kube-proxy는 데몬셋으로 실해되어 모든 노드에 존재합니다. 모든 노드에 kube-proxy가 있기 때문에, pod 실행위치와 상관없이 모든 노드에서 서비스를 이용해서 pod로 접근할 수 있습니다.
6. iptables 모드와 ClusterIP 서비스 타입
6.1 kube-proxy iptables모드
kube-proxy는 동작 모드에 따라 서비스로 오고 가는 네트워크 패킷을 제어합니다. kube-proxy가 iptables모드로 동작하는 경우 리눅스 iptables를 이용하여 서비스로 오는 패킷과 나가는 패킷을 제어합니다. iptables모드일때, 서비스라는 쿠버네티스 리소스는 실체가 iptables이였던 것입니다.
공식문서(https://kubernetes.io/ko/docs/concepts/services-networking/service/#proxy-mode-iptables)에서 설명하는 것처럼 kube-proxy는 api server를 감시하여 pod가 삭제되거나 생성되면 iptables를 업데이트 합니다.
6.2 iptables 분석
kube-proxy는 iptables nat 테이블 규칙(rule)을 이용하여 서비스에 오는 패킷과 나가는 패킷의 주소를 변환합니다. 주소변환 규칙때문에 pod까지 패킷이 전달됩니다. 그 규칙이 조금 많아서... 한단계한단계 간단히 살펴볼려고 합니다.
먼저 테스트를 위한 nginx pod 3개와 서비스를 생성합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-test
labels:
app: nginx-test
spec:
replicas: 3
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-test
spec:
selector:
app: nginx-test
ports:
- port: 8888
targetPort: 80
- 첫번째 과정
iptables는 처음 들어오는 패킷은 PREROUTING 테이블 규칙을 만납니다. 서비스로 오는 패킷은 KUBE-SERVICES 규칙을 따르게 됩니다.
iptables -v --numeric --table nat --list PREROUTING
- 두번째 과정
KUBE-SERVICE 규칙에서 테스트로 올린 nginx-test서비스가 보입니다. IP타입인 ClusterIP도 화면에 표시됩니다.
iptables -t nat --list KUBE-SERVICES
- 세번째 과정
다음 규칙은 랜덤 확률로 SEP규칙을 따르게 됩니다. 랜덤확률이기 때문에 부하 분산이 아니지만 로드밸런싱 됩니다. 각 KUBE-SEP규칙은 도착치 주소를 POD IP로 주소변환(DNAT)합니다. 자세히보면 DNAT으로 되는 것을 확인할 수 있습니다.
- 네번째 과정
[1~3]과정은 pod를 선택하고 도착지 IP가 변경되는 과정이었습니다. 네번째 과정은 iptables을 어떻게 빠져나가는지 살펴봅니다.
ClusterIP서비스 타입은 iptables을 빠져나갈때 mark match ! 0x4000규칙에 매칭되어 호출한 체인으로 돌아갑(RETURN)니다. 아래 있는 MARK, MASQUERADE규칙을 실행하지 않습니다. 마치 프로그래밍에서 함수호출이 끝나는 것처럼요. 호출한 곳은 아마도 KUBE-SERVICE SEP에 해당하여 pod와 통신을 시작합니다.
iptables -v --numeric --table nat --list cali-POSTROUTING; echo ; iptables -v --numeric --table nat --list KUBE-POSTROUTING; echo ; iptables -v --numeric --table nat --list cali-nat-outgoing
7. SessionAffinity
7.1 개요
[챕터 6]에서 세번째 과정에서 설명한 것처럼 랜덤확률로 pod로 로드밸런싱됩니다.
애플리케이션 유형에 따라서 로드밸런싱을 최소화 하고 싶은 경우가 있습니다. 그럴때 사용하는 옵션이 sessionAffinity입니다. sessionAffinity옵션이 활성화되면 처음 접속한 pod에 계속 접속합니다.
7.2 활성화
sessionAffinity는 디폴트로 비활성화(None)되어 있습니다. sessionAffinity를 ClientIP로 변경하면 활성화됩니다. 아래 예제는 [챕터 6]에서 배포한 서비스를 대상으로 sessionAffinity를 활성화 했습니다.
kubectl get svc nginx-test -o yaml | sed -e "s/sessionAffinity: None/sessionAffinity: ClientIP/" | kubectl apply -f -
7.3 원리
kube-proxy iptables모드에서는 conntrack, rcheck, reap모듈을 사용합니다. conntrack모듈에 일정시간동안 연결정보를 저장합니다. 저장된 연결정보가 요청되면 처음 접속한 pod과 통신하게 합니다.
iptables -t nat -S | grep recent
conntrack이 저장한 세션정보는 conntrack명령어로 확인할 수 있습니다. --dst-nat인자로 pod IP로 필터링할 수 있습니다.
conntrack -L --dst-nat <pod IP>
참고자료
[1] [블로그] https://coffeewhale.com/k8s/network/2019/05/11/k8s-network-02/
[2] [쿠버네티스 공식문서] https://kubernetes.io/ko/docs/tutorials/services/source-ip/
[3] [쿠버네티스 공식문서] https://kubernetes.io/ko/docs/concepts/services-networking/service/
'연재 시리즈' 카테고리의 다른 글
쿠버네티스 네트워크 스터디 5주차 - coredns 동작이해 (2) | 2022.02.16 |
---|---|
쿠버네티스 네트워크 스터디 4주차 - 4주차과제 (0) | 2022.02.10 |
쿠버네티스 네트워크 스터디 3주차 2편: Calico 모드 (0) | 2022.02.05 |
[중간과제] github action과 argocd 연계 프로젝트를 마치며 (2) | 2022.02.03 |
쿠버네티스 네트워크 스터디 3주차 1편: Calico 기본 통신 과정 (0) | 2022.01.24 |