[K8S] Kubernetes 동작원리
Docker는 컨테이너 기술의 혁명을 가져왔다.
리눅스 기술( cgroups, namespace)을 활용하여 리소스를 독립적으로 격리하고 할당이 가능해지자, 컨테이너(Container)라는 개념이 등장하였다. 하나의 컴퓨터 안의 여러 개의 컨테이너가 만들어졌고 모두 독립적으로 활동하였다. 이렇게 되자, 수많은 컨테이너를 관리하는 기술이 필요해졌는데, 그것이 바로 쿠버네티스(Kubernetes)이다.
쿠버네티스는 여러 대의 컴퓨터(노드)에 분산되어 있는 수많은 컨테이너를 관리한다. 컨테이너가 죽으면 자가복구(livenessProbe)를 시키고, 트래픽이 증가하면 자동화된 확장(AutoScaling)을 보이고 App의 버전이 올라가면 롤링 업그레이드(RollingUpdate)를 통해 순차적으로 재기동에 들어간다.
이처럼 여러 대의 노드에 분산되어 있는 컨테이너를 제어하려면 중앙에 컨트롤타워가 필요한데,
그것이 컨트롤 플레인(Control-Plane)이다.
쿠버네티스 API 서버는 쿠버네티스의 핵심이다.
외부에서 kubectl 명령이 들어오거나 k8s 클러스터 내부 컴포넌트들 사이의 통신은 API 서버를 거치어 일어난다.
Pod 생성하기 ( Scheduler )
API서버는 'etcd'에서 각 노드의 상태 데이터를 조회한다. etcd는 key-value 구조의 DB이다. 컨트롤 플레인은 각 노드를 제어해야 하므로, 클러스터에 몇 개의 노드가 있는지, 어떤 파드가 어느 노드에서 동작하고 있는지, 어떤 노드가 어떤 상태인지 등의 데이터를 저장하고 있다. 데이터가 있으니 데이터로 '정보'를 만들어야 한다.
API 서버는 상태 데이터를 '스케줄러'에게 전달한다. 스케줄러는 각 노드의 상태 데이터를 토대로 어떤 노드에 POD를 생성하면 좋을지 판단한다. 그리고 결과값을 API 서버에게 넘긴다. API서버는 스케줄러가 결정한 노드에게 POD 생성명령을 내린다. 컨트롤 플레인은 명령만 내릴 뿐, 노드 안의 실질적인 작업까지 담당하지는 않는다. 노드 안의 작업은 'kubelet'이 담당한다. kubelet은 명령을 토대로 POD를 생성한다.
이처럼 kubectl 명령부터 클러스터 내부 컴포넌트 사이의 통신까지 API 서버를 중심으로 이루어 진다.
Pod 관리하기 ( Controller )
컨트롤 플레인의 '컨트롤러'는 시스템 상태를 조절하는 컨트롤 루프이다.
방온도를 체크하는 모니터링 시스템이 방온도를 주기적으로 체크하다가, 설정된 온도 이하로 떨어지면 보일러를 작동시키는 원리와 같다고 보면된다. 특정 Pod 3개가 동작하도록 설정하였는데, 1개가 종료된다면 루프를 돌며 체크하던 컨트롤러는 API 서버에게 Pod 생성을 요청한다. 그러면 API 서버는 etcd의 상태데이터를 스케줄러에게 넘기고 적당한 노드를 찾은 뒤, kublet에게 Pod 생성을 명령한다.
이러한 '컨트롤러'는 어떤 Pod를 관리하느냐에 따라 다양하게 존재한다.
Stateless Pod 관리는 ReplicaSet, Deployment,
Stateful Pod 관리는 StatefulSet,
전체노드에 실행되는 Pod 관리는 Daemon Set,
실행과 종료가 정해진 배치성 Pod인 경우 Job,
Job을 시간을 기준으로 관리한다면 CronJob이 있다.
서비스 디스커버리 ( CoreDNS )
동일한 App이 배포된 여러 개의 Pod는 Controller(Deployment)로 묶여 관리된다.
그렇다면 다른 Pod와 통신을 하려면 어떻게 해야 할까?
우선, Pod를 네트워크에 노출시켜야 한다. Service는 Controller로 묶인 Pod 집합을 네트워크에 노출시켜 외부에서 접근이 가능하도록 만든다. 외부를 어디까지 하느냐에 따라, ClusterIP, NodePort, LoadBalancer 타입으로 나뉜다. 자세한 내용은 아래 링크를 참고하면 된다.
서비스가 생성되면 ip가 부여된다. 서비스에 접근하고 싶다면 ip로 접근하면 된다. 그러나 ip는 고정되어 있지 않다. ip는 언제든 변경될 수 있으므로, 변하지 않은 고정된 이름이 필요하다.
CoreDNS는 서비스 이름(도메인명)과 ip를 매핑하여 관리하는 쿠버네티스의 DNS 서버이다. 1.21 버전까지는 kube-dns를 사용하였지만 1.29버전부터는 CoreDNS를 사용한다. 서비스의 호환을 위해, CoreDNS의 서비스 이름은 kube-dns를 유지한다.
예를 들어,
Spring App Pod에서 NodeJS App Pod에 접근하려고 한다. Spring App Pod는 NodeJS App Pod의 ip를 모르지만 CoreDNS의 ip는 알고 있다. 그 이유는 모든 Pod는 최초 생성시, /etc/resolv.conf 파일에 DNS 서비스의 ip를 디폴트로 설정하기 때문이다.
Spring App Pod가 NodeJS 서비스에 접근하려고 한다. NodeJS의 도메인명은 nodejs[호스트명].default[네임스페이스명].svc.cluster.local 이다. 이와 같은 도메인명을 FQDN(Full Quealified Domain Name)이라 부른다.
쿠버네티스는 파드와 서비스를 위한 DNS 레코드를 생성한다. 사용자는 IP 주소 대신에 일관된 도메인명을 통해서 서비스에 접속할 수 있다. 동일한 네임스페이스라면 호스트명만으로도 DNS검색이 가능하다. 다른 네임스페이스라면 FQDN 방식의 도메인명으로 검색해야 한다. /etc/resolv.conf 파일 안의 저장된 DNS 서비스 ip로 도메인 검색을 하면 CordDNS로부터 ip를 전달받을 수 있다.
이렇게 쿠버네티스가 동작하는 가장 핵심적인 원리 3가지를 다루어 보았다.
1) 파드 생성
2) 파드 관리
3) 서비스 디스커버리
쿠버네티스 동작의 핵심을 이루는 주요 컴포넌트인 API 서버, Scheduler, Controller, etcd, CoreDNS도 POD로 동작하고 서비스를 노출시켜 트래픽을 받는 방식으로 동작한다.
참고자료
https://kubernetes.io/ko/docs/concepts/services-networking/dns-pod-service/
https://tech.kakao.com/2021/12/20/kubernetes-etcd/
https://avinetworks.com/glossary/kubernetes-service-discovery/
https://happycloud-lee.tistory.com/247
https://kubernetes.io/ko/docs/concepts/scheduling-eviction/scheduler-perf-tuning/
https://real-dongsoo7.tistory.com/134
https://velog.io/@koo8624/Kubernetes-CoreDNS
https://malwareanalysis.tistory.com/267
https://www.youtube.com/watch?v=Iue9TC13vPQ