쿠버네티스 내부 이해 #1 (마스터 2탄)

snooby·2024년 11월 1일
0

🐳 kubernetes

목록 보기
53/63

저번 내용의 API 서버를 간략히 정리하고 이어가볼까요?

결국 클러스터의 모든 구성요소는 API 서버를 통해 etcd와 통신하고 API 서버는 오브젝트의 유효성을 검증하고 etcd에 저장합니다.
그리고 그 결과를 클라이언트에게 반환합니다.

요 API 서버가 리소스 변경을 클라이언트에서 어떻게 통보하는 걸까요?

API 서버가 클라이언트에게 결과 통보하는 방법

API 서버는 클러스터 구성요소간 통신 병목 역할 외에 아무것도 하지 않습니다.

예를 글어 레플리카셋 리소스를 만들때 파드를 만들지도 않고 서비스의 엔드포인트도 관리하지 않습니다.
( 이것들은 컨트롤러 매니저의 컨트롤러들이 하는 겁니다. )

이 컨트롤러에게 무엇을 해야하는지 알려주는 역할을 하느냐? 그것도 안합니다.
단지, 컨트롤러와 다른 구성 요소가 배포된 리소스의 변경 사항을 관찰할 수 있도록만 하는게 API 서버의 역할입니다.

쉽게 말해, 나는 서로 통신하고 정보 공유할 수 있게 해주는 전화기 같은 존재 지 이래라 저래라 하지는 않아요~

컨트롤러 플레인 구성 요소는 리소스가 CRUD 될 때 통보를 받을 수 있도록 요청할 수 있습니다.
이렇게하면 구성 요소가 클러스터 메타데이터 변경에 대응해 필요한 모든 작업을 수행할 수 있습니다.

스케줄러 이해하기

스케줄러는 앞서 말한 API 서버의 리소스 감시 시스템을 통해 새로 생성될 파드를 기다리고 있다가 할당된 노드가 없는 새로운 파드를 노드에 할당하기만 합니다.

스케줄러는 선택된 노드에 파드를 실행하도록 지시하는 게 아니고 단지 API 서버로 파드 정의를 갱신합니다.

(파드 너 어디에 떠! 가 아니고!! 파드가 어디에 떳습니다. API 서버님 etcd에 업데이트 좀 해주십쇼)

API 서버는 앞서 말한 감시 메커니즘을 통해 변경사항을 etcd에 저장하고 kubelet에게 파드가 스케줄린된 것을 통보합니다.
대상 노드 (파드가 스케줄링된)의 kubelet은 파드가 해당 노드에 스케줄링된 것을 확인하자마자 파드의 컨테이너를 생성하고 실행합니다.

(kubelet 관점) : 오 스케줄표(etcd) 보니깐 내 노드에 pod가 스케줄링 되었네 pod 만들어드려야지

이렇게만 보면 스케줄링 프로세스는 어디에 뜰지를 etcd에 작성하기만 하는 것 같아보이는데… 파드가 뜨기 적합한 노드를 선택하는 작업이 그리 단순하지 않습니다.
물론 무작위로 노드 선택하는 건 쉽겠지만…
당연히 그리 간단하게 하진 않겟죠?

기본 스케줄링 알고리즘

파드가 뜰 노드를 셀렉하는 스케줄링 방법은 2파트로 나뉜다.

1. 수용 가능한 노드 찾기

모든 노드 중 파드를 스케줄링할 수 있는 노드 목록을 필터링하는 겁니다.

파드를 수용할 수 있는 노드를 찾기 위해, 스케줄러는 미리 설정된 조건함수 목록에 각 노드를 전달합니다.
Ex) 노드가 하드웨어 리소스에 대한 파드 요청을 충족시킬 수 있는가? 노드에 리소스는 충분한가? Taints, affinity 등
이러한 모든 검사를 통과해야 노드가 파드를 수용할 수 있다고 판단합니다.

2. 파드에 가장 적합한 노드 선택

가령, 2개의 노드가 지금 파드가 뜨기 적합하다고 해도.. 1개는 지금 파드가 10개 떠있고 다른 1개의 노드는 텅텅 노는 아이라면 후자의 노드에 스케줄 하는게 맞겠죠?

컨트롤러

앞에서 보았듯 API 서버는 리소스를 etcd에 저장하고 변경사항을 클라이언트에 통보하는 역할을 하고, 스케줄러는 파드에 노드만 할당합니다.

그럼 API 서버로 배포된 리소스에 지정된 대로 시스템을 원하는 상태로 만드는 건 누가할까요?
이를 컨트롤러 매니저 (마스터 노드) 안에서 실행되는 컨트롤러에 의해 수행됩니다.

현재는 다양한 조정작업을 수행하는 여러 컨트롤러가 하나의 컨트롤러 매니저 프로세스에서 실행됩니다.

  • 레플리카셋, 데몬셋, 잡 컨트롤러
  • 디플로이먼트 컨트롤러
  • 스테이트풀셋 컨트롤러
  • 엔드포인트 컨트롤러
  • 퍼시스턴트볼륨 컨트롤러
    등..

각 컨트롤러의 기능은 너무 직관적인 이름으로 느낌이 오죠…
보시다시피 생성할 수 있는 거의 모든 리소스의 컨트롤러가 있는 것을 알 수 있습니다.

리소스 : 클러스터에 어떤것을 실행해야하는 지가 기술된 요소
컨트롤러 : 리소스를 배포함에 따라 실제 작업을 수행하는 활성화된 쿠버네티스 구성 요소

컨트롤러의 역할과 동작 방식

컨트롤러는 다양한 작업을 수행하지만 모두 API 서버에서 리소스가 변경되는 것을 감시하고 각 변경 작업을 수행합니다.

관제탑을 상시 관찰하여 변경값이 있다고 알림 오면 수행하는 역할!

일반적으로 컨트롤러는 조정 루프를 실행해, 실제 상태를 원하는 상태로 조정하고 새로운 상태를 리소스의 status 섹션에 기록합니다.
컨트롤러는 API 서버의 감시 메커니즘을 이용해 변경 사항을 통보받지만 모든 이벤트를 놓치지 않고 받는다는 것을 보장하지 않기 때문에, 장기적으로 목록을 가져오는 작업을 수행해 누락된 이벤트는 없는지 확인해야합니다.

의심하고 또 의심해!

컨트롤러들은 당연하겠지만 서로 직접 대화하지 않습니다.

모든 대화와 소통은 API 서버를 거친다!

컨트롤러는 심지어 다른 컨트롤러의 존재도 kubelet의 존재 마저도 모릅니다.
아주 철저한 사회에요~

각 컨트롤러는 API 서버에 연결하고 감시 메커니즘을 통해 컨트롤러가 담당하는 리소스 유형에서 변경이 발생하면 통보해줄 것을 요청합니다.

API 서버를 통해서만 소통할 수 있고 etcd의 값을 들을 수 있는데 API 말 잘 들어야지 모~

kubelet이 하는 일

쿠버네티스 컨트롤 플레인의 일부로써 마스터 노드에서 실행되는 다른 컨트롤러와 달리, kubelet과 서비스 프록시는 실제 파드 컨테이너가 실행되고 있는 워커 노드에서 실행됩니다.

왜 컨트롤 플레인의 일부인데 이 녀석은 워커 노드에 있을까..?

간단히 말하면, Kubelet은 워커노드에서 실행하는 모든 것을 담당하는 구성요소입니다.

kubelet 의 작업

  1. Kubelet이 실행중인 노드를 노드 리소스로 만들어 API 에 등록하기
  2. API 서버를 지속적으로 모니터링해 해당 노드에 파드가 스케줄링되면, 파드의 컨테이너를 시작하기
    ( 설정된 컨테이너 런타임 ex. 도커 에 지정된 컨테이너 이미지로 컨테이너를 실행하도록 지시함)
  3. 실행 중인 컨테이너를 계속 모니터링하면서 상태, 이벤트, 리소스 사용량을 API 서버에 보고
  4. API 서버에서 파드가 삭제되면 컨테이너를 정지하고 파드가 종료된 것을 서버에 통보

3번의 역할이 컨테이너의 liveness probe를 실행하는 구성요소이기도 합니다.
그래서 계속 컨테이너 상태를 모니터링해서 프로브가 실패하면 컨테이너를 다시 시작합니다.

liveness probe: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/

kubelet은 비유하자면, 각 파드 장이고 API server 그 위 대표 느낌?
각 파드로 파병된 kubelet들이 잘 현 상황 모니터링하고 위로 보고!
위에서 내려온 지시사항 잘 체크해서 수행하는 느낌

서비스 프록시

Kubelet 외에도, 모든 워커 노드는 클라이언트가 쿠버네티스 API로 정의한 서비스에 연결할 수 있도록 해주는 kube-proxy도 같이 실행합니다.

Kube-proxy는 서비스의 IP와 포트로 들어온 접속을 서비스를 지원하는 파드 중 하나와 연결시켜줍니다.

너와 나의 연결 고리 : kube-proxy

만일 서비스가 둘 이상의 파드에서 지원되는 경우에는 파드간의 로드밸런싱까지 해줍니다.

설명까지 듣고 나니, kube-proxy도 참 이름이 기능처럼 직관적이죠.
단순 proxy를 넘어 현재는 iptables 규칙만 사용해 프록시 서버를 거치지 않고 패킷을 무작위로 선택한 백엔드 파트로 전달합니다.

이러한 모드를 iptables 프록시 모드라고 부릅니다.

사진 속 두 방식의 가장 큰 차이점은 패킷이 kube-proxy를 통과해 사용자 공간에서 처리되는가 아니면 커널에서 처리되는가 입니다. (성능 차이가 큼)

또 다른 차이점은 userspace 프록시 모드는 진짜 라운드 로빈 방식을 사용해 파드간에 연결이 균형을 이루지만, iptables 프록시 모드는 파드를 무작위로 선택하기 때문에 그렇지 않다는 겁니다.

이를 좀 더 깊이 이해하기 위해서는 네트워크, 서비스 동작 방식 이해가 필요합니다.
이를 먼저 이해하고 다시 이야기 해봅시다.

DNS 서버 동작 방식

클러스터의 모든 파드는 기본적으로 클러스터 내부 DNS 서버를 사용하도록 설정되어 있습니다.
이를 사용해 파드는 서비스를 이름으로 쉽게 찾을 수 있고 헤드리스 서비스 파드인 경우에는 해당 파드의 IP 주소를 조회할 수 있습니다.

DNS 서버 파드는 kube-dns 서비스로 노출되므로, 해당 파드를 다른 파드와 마찬가지로 클러스터 안에서 이동할 수 있습니다.
해당 서비스 IP 주소는 클러스터에 배포된 모든 컨테이너가 가지고 있는 /etc/resolv.conf 파일 안에 nameserver로 지정되어 있습니다.

Kube-dns 파드는 API 서버 감시 메커니즘을 이용해 서비스와 엔드포인트 변화를 관찰하고 모든 변화를 DNS 레코드에 갱신합니다.
( DNS 도 리소스 변경과 현황은 API 서버를 사용한다는 말씀)

이로 클라이언트가 거의 항상 최신 DNS 정보를 얻을 수 있습니다.
간혹, 서비스 혹은 엔드포인트 리소스가 갱신되는 시간과 DNS 파드가 통보를 받는 시간 사이에는 DNS 레코드가 유효하지 않을 수 있기 때문에 최신이 아닌 경우도 존재합니다.

인그레스 컨트롤러

인그레스 컨트롤러는 리버스 프로시 서버 (nginx) 를 실행하고 클러스터에 정의된 인그레스, 서비스, 엔드포인트 리소스 설정을 유지합니다.
컨트롤러는 이러한 리소스를 감시하고 변경이 일어날 때마다 프록시 서버 설정을 변경합니다.

인그레스 리소스의 정의는 서비스를 가리키지만, 인그레스 컨트롤러는 트래픽을 서비스의 IP로 보내지 않고 서비스의 파드로 직접 전달합니다.

외부에서 접속한 클라이언트가 인그레스 컨트롤러로 연결할 때 IP를 보존하는데 영향을 주기 때문에, 특정 사용 사례에서는 서비스를 선호하도록 만듭니다.

profile
DevOps 🐥

0개의 댓글