✔️ 쿠버네티스(Kubernetes)
k8s는 Google이 개발했다.
2010년대에는 VM을 사용하는 회사가 대다수였다.
구글은 2009년부터 컨테이너라는 개념을 사용하고 있었고 구글의 내부 서비스는 컨테이너 위에서 작동하도록 되어있었다.
내부에 Borg 시스템을 만들었고 이것을 오픈 소스화 한 것이 k8s이다.
Borg는 C++ 이지만 k8s는 Go로 만들어졌다.
Borg 시스템을 단순히 오픈 소스화 한 것이 아니라 Go로 다시 만들었다.
✔️ what is k8s ?
- 서비스 디스커버리와 로드 밸런싱 쿠버네티스는 DNS 이름을 사용하거나 자체 IP 주소를 사용하여 컨테이너를 노출할 수 있다. 컨테이너에 대한 트래픽이 많으면, 쿠버네티스는 네트워크 트래픽을 로드밸런싱하고 배포하여 배포가 안정적으로 이루어질 수 있다.
- 스토리지 오케스트레이션 쿠버네티스를 사용하면 로컬 저장소, 공용 클라우드 공급자 등과 같이 원하는 저장소 시스템을 자동으로 탑재 할 수 있다.
- 자동화된 롤아웃과 롤백 쿠버네티스를 사용하여 배포된 컨테이너의 원하는 상태를 서술할 수 있으며 현재 상태를 원하는 상태로 설정한 속도에 따라 변경할 수 있다. 예를 들어 쿠버네티스를 자동화해서 배포용 새 컨테이너를 만들고, 기존 컨테이너를 제거하고, 모든 리소스를 새 컨테이너에 적용할 수 있다.
- 자동화된 빈 패킹(bin packing) 컨테이너화된 작업을 실행하는데 사용할 수 있는 쿠버네티스 클러스터 노드를 제공한다. 각 컨테이너가 필요로 하는 CPU와 메모리(RAM)를 쿠버네티스에게 지시한다. 쿠버네티스는 컨테이너를 노드에 맞추어서 리소스를 가장 잘 사용할 수 있도록 해준다.
- 자동화된 복구(self-healing) 쿠버네티스는 실패한 컨테이너를 다시 시작하고, 컨테이너를 교체하며, '사용자 정의 상태 검사'에 응답하지 않는 컨테이너를 죽이고, 서비스 준비가 끝날 때까지 그러한 과정을 클라이언트에 보여주지 않는다.
- 시크릿과 구성 관리 쿠버네티스를 사용하면 암호, OAuth 토큰 및 SSH 키와 같은 중요한 정보를 저장하고 관리 할 수 있다. 컨테이너 이미지를 재구성하지 않고 스택 구성에 시크릿을 노출하지 않고도 시크릿 및 애플리케이션 구성을 배포 및 업데이트 할 수 있다.
쿠버네티스는:
- 지원하는 애플리케이션의 유형을 제약하지 않는다. 쿠버네티스는 상태 유지가 필요 없는(stateless) 워크로드, 상태 유지가 필요한(stateful) 워크로드, 데이터 처리를 위한 워크로드를 포함해서 극단적으로 다양한 워크로드를 지원하는 것을 목표로 한다. 애플리케이션이 컨테이너에서 구동될 수 있다면, 쿠버네티스에서도 잘 동작할 것이다.
- 소스 코드를 배포하지 않으며 애플리케이션을 빌드하지 않는다. 지속적인 통합과 전달과 배포, 곧 CI/CD 워크플로우는 조직 문화와 취향에 따를 뿐만 아니라 기술적인 요구사항으로 결정된다.
- 애플리케이션 레벨의 서비스를 제공하지 않는다. 애플리케이션 레벨의 서비스에는 미들웨어(예, 메시지 버스), 데이터 처리 프레임워크(예, Spark), 데이터베이스(예, MySQL), 캐시 또는 클러스터 스토리지 시스템(예, Ceph) 등이 있다. 이런 컴포넌트는 쿠버네티스 상에서 구동될 수 있고, 쿠버네티스 상에서 구동 중인 애플리케이션이 Open Service Broker 와 같은 이식 가능한 메커니즘을 통해 접근할 수도 있다.
- 로깅, 모니터링 또는 경보 솔루션을 포함하지 않는다. 개념 증명을 위한 일부 통합이나, 메트릭을 수집하고 노출하는 메커니즘을 제공한다.
- 기본 설정 언어/시스템(예, Jsonnet)을 제공하거나 요구하지 않는다. 선언적 명세의 임의적인 형식을 목적으로 하는 선언적 API를 제공한다.
- 포괄적인 머신 설정, 유지보수, 관리, 자동 복구 시스템을 제공하거나 채택하지 않는다.
- 추가로, 쿠버네티스는 단순한 오케스트레이션 시스템이 아니다. 사실, 쿠버네티스는 오케스트레이션의 필요성을 없애준다. 오케스트레이션의 기술적인 정의는 A를 먼저 한 다음, B를 하고, C를 하는 것과 같이 정의된 워크플로우를 수행하는 것이다. 반면에, 쿠버네티스는 독립적이고 조합 가능한 제어 프로세스들로 구성되어 있다. 이 프로세스는 지속적으로 현재 상태를 입력받은 의도한 상태로 나아가도록 한다. A에서 C로 어떻게 갔는지는 상관이 없다. 중앙화된 제어도 필요치 않다. 이로써 시스템이 보다 더 사용하기 쉬워지고, 강력해지며, 견고하고, 회복력을 갖추게 되며, 확장 가능해진다.
✔️ k8s 아키텍처
클러스터링이란 물리적으로 또는 논리적으로 여러개의 리소스를 하나로 묶어서 사용하는 개념이다.
Docker에는 클러스터 기능이 없다.
VM이던 BM이던 상관없이 여러 시스템이 있고 Docker가 설치되어 있으면 각각 따로 관리되어야 한다.
이러한 Stand-alone 방식(각각의 독립적인 시스템)은 관리하기 매우 힘들다.
이런 시스템이 수십, 수백대가 되면 어떤 시스템에 접속해야 하는지 결정하는 것 조차도 힘들것이다.
클러스터의 핵심은 Control Plane와 Node Plane이다.
여러대의 VM은 컨트롤 플레인, 노드 플레인 역할로 나뉜다.
Node란 컨테이너를 실행해주는 것이며 컨테이너 런타임이 설치되어 있어야 한다.
Docker를 중심으로 구글 등 컨테이너 기술에 관심있는 여러 집단들이 한데 모여 Open Container Initiative, 이하 OCI라는 프로젝트를 시작하여 컨테이너에 관한 표준을 정하는 일들을 시작하게 됩니다.
그래서 Docker에서는 OCI 표준을 준수하는 containerd라는 Container Runtime을 만들고, Kubernetes에서는 OCI 표준을 준수하는 이미지들을 실행할 수 있는 Container Runtime Interface, 이하 CRI 스펙을 버전 1.5부터 제공함으로써 Docker 버전과 무관하게 OCI 표준을 준수하기만 하면 어떤 컨테이너 이미지도 Kubernetes에서 실행가능한 환경이 만들어지게 되었습니다.
요약하자면 컨테이너를 빌드하고, 실행하고 거기에 네트워크, 스토리지, CLI까지 제공해주는 Docker Engine이라는 패키지가 있었는데, 이게 하나의 패키지로 묶여있다보니 여러 불편함들이 생겨났고 이를 해소하기 위해 여러 사람들이 모여 OCI라는 Container Runtime 표준을 만들고 이 표준대로 각자 Container Runtime을 만들기 시작했는데 Docker에서 만든 Container Runtime이 바로 containerd (https://containerd.io/)라는 이야기입니다.
참고로 containerd의 d는 daemon의 d입니다.
한편, Red Hat, Intel, SUSE, Hyper, IBM 쪽에서도 OCI 표준에 따라 Kubernetes 전용 Container Runtime을 만들었는데 이것이 CRI-O (https://cri-o.io/)입니다.
containerd와 CRI-O 이 두가지 Container Runtime이 현재 가장 널리 사용되고 있으며 containerd는 Docker Engine에 기본으로 탑재되어 있어서 지금도 Docker를 사용한다면 내부적으로 사용되는 Container Runtime은 containerd를 사용하게 됩니다.
참고로 'docker build' 커맨드로 생성되는 이미지들 역시 OCI Image Spec을 준수하기 때문에 별도의 작업없이 containerd로 실행시킬 수 있습니다.
노드가 실제로 컨테이너를 실행하는 것이며 노드의 개수가 컨트롤 플레인 수보다 많다.
컨트롤 플레인은 노드들을 관리해주는 것이다. 즉, 클러스터를 관리해주는 것이다.
컨트롤 플레인은 많아야 하는 것은 아니지만 컨트롤 플레인에서 장애가 나면 아무것도 관리할 수 없다.
컨트롤 플레인이 하나라면 SPoF가 되는 것이다.
같은 컨트롤 플레인이 여러대 있는 것은 이런 SPoF를 피하기 위함이며 일반적으로 3대 이상 두는 것이 원칙이다.
그렇다면 노드를 몇대를 할것인가? 궁금할 것이다.
노드의 개수에 정답은 없다. 실행할 App의 개수에 따라 달려있다.
이런 클러스터링 방식은 절대 짝수개로 설정하지 않는다.
✔️ 각 구성요소의 명칭
컨트롤 플레인은 원래 마스터로 노드는 워커로 불렸었다. 또한 워커의 또 전신이 미니언즈이다.
미니언즈는 일꾼이고 항상 마스터를 쫓는다.
인종 차별 문제와 상표권 문제로 인해 미니언즈 → Worker로 이름을 변경했다.
요즘은 노드와 워커라는 명칭이 혼용되고 있으나, 노드대신 워커를 사용하는 편이 조금 더 명확하다.
✔️ 관리형 Kubernetes 서비스들
실제로 각 클라우드 밴더의 관리형 k8s 서비스들(AWS - EKS, Azure - AKS, Google - GKE)은 컨트롤 플레인을 관리해주는 것이다. 컨트롤 플레인을 추상화시켜 없앤다.
관리형이 아닌 설치형을 쓰면(EC2나 on-prem에 구성하는 방식) 우리가 컨트롤 플레인을 구성하고 직접 관리해야 한다.
요즘 관리형 서비스에서는 노드까지도 추상화시켜 없애기도 한다.
✔️ Split Brain
클러스터는 기본적으로 절대 짝수개로 설정하지 않는다.
클러스터 내부의 구성요소인 VM들이 네트워크를 통해 연결되어 있을 것이고 네트워크 통신을 통해 같은 상태인지를 체크할 것이다.
그런데 여기서 문제가 발생한다.
네트워크가 split(끊어지게)되어 1대가 죽고 3대만 통신 가능한 상태가 되면 정책을 시행해야한다.
클러스터를 구성하기 위해 가장 많이 사용하는 정책은 과반수 정책인데(== 정족수)
4개에서 1개가 문제가 발생하면 나머지 3대만을 작동시킨다.
이때 ❗ 최악의 시나리오를 생각해보자
4대의 VM이 작동중일때 2대에 장애가 발생한다면(동수 발생) 어떤것을 죽이고 어떤 것을 살릴 것인가 ?
이러한 5:5 Split Brain 발생 시 클러스터에 문제가 생긴다.
만약 장애가 발생한 VM이 여전히 가동 중이라면 외부 App은 어디서 실행되어야 하는지 갈피를 잡지 못한다.
ex) 데이터를 쓰기 작업했던 App이 가동을 멈춘 2개의 VM이라면 나머지 2대에서는 읽기 작업을 해도 소용없다.
따라서 클러스터는 절대로 짝수개로 설정하지 않는다.
홀수개로 설정하면 절대 5:5 Split Brain은 발생할 수 없다.
✔️ Control Plane
컨트롤 플레인을 여러개 구성하고 하나로 묶어줄 때 이또한 클러스터라고 한다.
정확히는 컨트롤 플레인의 클러스터는 etcd
라는 것의 클러스터이다.
모든 화살표는 모두 API를 거친다. 이를 kube API server라고 한다.
모든 k8s 통신은 API 서버를 거치며 절대 서로간에 직접적으로 통신하지 않는다.
kubectl는 핵심 명령어이자 클라이언트 명령어이다. 클러스터 전체가 서버이기 때문에 kubectl 명령은 항상 API 서버에게 통신을 건다.
k8s 에서 API 서버를 통해서 통신하지 않는 서비스는 없다.
그래서 kube API server가 죽어버리면 끝난다. 가장 중요한 API 서버이다.
✔️ c-m : Controller Manager
컨트롤 플레인을 정확히는 k8s 클러스터 전체를 컨트롤한다. 쿠버네티스의 가장 핵심이다.
다음 기능을 가진다.
- 노드 컨트롤러: 노드가 다운되었을 때 통지와 대응에 관한 책임을 가진다.
- 레플리케이션 컨트롤러: 시스템의 모든 레플리케이션 컨트롤러 오브젝트에 대해 알맞은 수의 파드들을 유지시켜 주는 책임을 가진다.
- 엔드포인트 컨트롤러: 엔드포인트 오브젝트를 채운다(즉, 서비스와 파드를 연결시킨다.)
- 서비스 어카운트 & 토큰 컨트롤러: 새로운 네임스페이스에 대한 기본 계정과 API 접근 토큰을 생성한다.
노드 컨트롤러는 워커 노드이다. 워커 노드가 3대가 있는데 한대 죽을 수도 새로운 노드가 한대 더 추가될 수도 있는데 그럴때 c-m이 관리한다.
쿠버네티스의 핵심은 컨테이너의 복제이다.
쿠버네티스에서는 컨테이너를 직접 컨트롤하지 않는다 Pod
라는 것을 컨트롤한다.
Pod가 곧 컨테이너를 의미하는 것은 아니다. Pod들의 복제를 제어하는 것이 레플리케이션 컨트롤러이다.
엔드포인트는 서비스와 파드를 연결시켜준다.
쿠버네티스에서의 서비스는 네트워크이다. 조금 더 정확히는 로드밸런서이다.
엔드포인트는 파드에 네트워크를 제공해주는 것이 엔드포인트 컨트롤러이다.
서비스 어카운트 & 토큰 컨트롤러는 인증을 관리한다.
✔️ c-c-m : Cloud Controller Manager
실제로 쿠버네티스가 클라우드(EKS, AKS, GKE)에 있으면 노드는 VM이며 노드간의 통신은 클라우드와 연결시켜야 한다.
그 때 사용하는 것이 c-c-m이다.
우리가 클라우드의 서비스를 쓰더라도 해당 구성을 볼 수 없으며 직접 컨트롤 플레인을 관리할 수 없다.
Scheduler : Pod라고 하는 것 즉, 컨테이너를 3개의 노드중 어디에 배치해야 할 것이냐? 를 결정한다.
운영체제 kernel에서 가장 중요한 것은 스케줄러이다.
우리가 실행시킨 App이 리소스를 얼마만큼 쓰게 할 것인지 스케줄링하는 것이다.
정확하게는 Pod를 어디에 어떻게 배치시킬 것인지를 담당하는 것이 스케줄러이다.
etcd (etc daemon) : key value storage(=database) (저장소)
리눅스에서 etc 디렉토리의 역할은 운영체제, App의 설정과 관련된 디렉토리이다.
데이터베이스도 스토리지이다. 데이터를 저장하는 방식을 key value 형태로 저장하는 것이 etcd이다.
쿠버네티스에서 항상 강조하는 것은 Desired State 선언적이라는 것이다.
EC2 인스턴스를 어떻게 만들어야 하는지를 선언하는 것이다.
쿠버네티스에서는 모든 것들을 선언(Desired)한다. yaml 방식으로 한다.
우리가 만들고자 하는 Pod를 어떻게 구성할 것인지를 yaml을 사용해 선언하며 etcd에 저장한다.
따라서 etcd가 장애가 발생하면 아무것도 설정할 수 없다.
inspect에서 JSON 형태로 데이터가 나오는데 그런 정보들이 다 여기 저장된다.
etcd의 핵심은 클러스터를 구성하는 것이다. FT, HA를 이루고 SPoF를 제거한다.
다른 구성요소들은 stateless이므로 장애가 나면 다시 실행하면 되지만 etcd는 stateful 하므로 데이터가 없어지면 아무것도 하지 못한다.
etcd는 key value 형태로 저장하기 위해 사용한다.
✔️ Node
실질적으로 컨테이너를 운영해주는 시스템이 노드이다. (워커 노드라고 한다.)
CRI - 컨테이너 런타임 인터페이스
즉, 여러대의 Docker가 설치되어 있는 것이다.
✔️ kubelet
이 Docker는 실질적으로 컨테이너를 만들어서 관리하는데 k8s는 컨테이너를 직접 관리하지 않고 Pod라는 객체를 통해서 컨테이너를 관리하는데 kubelet이 컨테이너 런타임들을 관리해주는 에이전트이다.
컨테이너를 만들어달라는 요청을 API에게 하면 API는 kubelet에게 알리고 kubelet은 Docker에게 요청을 다시 전달한다.
✔️ kube-proxy
Docker에서 ss - tnlp 명령을 실행하면 docker proxy가 있었다.
docker proxy는 포트 포워딩만을 담당하는 것이 아니라 네트워크의 정책을 담당한다.
src nat을 어떻게 할 것인지 dst를 어떻게 할 것인지 iptables의 정책을 세팅해주는 것이 docker proxy였다.
kube-proxy도 마찬가지로 네트워크 정책을 담당하는 역할을 한다.
즉, 워커 노드에는 kube proxy(네트워크 담당, Container Network Interface) kubelet(컨테이너 담당, Container Runtime Interface)이 존재한다.
여기서 컨트롤 플레인의 구성 요소들 c-m, c-c-m 등등도 다 컨테이너이다.
이 컨테이너를 관리하기 위해서 kubelet과 kube-proxy는 컨트롤 플레인에도 존재한다. (아키텍처에 표시되어 있지 않음)
✔️ 애드온
추가 구성요소이다.
쿠버네티스에는 기본적으로 쿠버네티스가 할 수 없는 기능들이 있다. 그런 기능을 하기 위해서 애드온을 붙인다.
애드온에는 주요 애드온들이 있다.
✔️ DNS
클러스터는 클러스터 DNS를 갖추어야 한다고 되어있는데 서비스 디스커버리를 한다는 것이다.
서비스 디스커버리는 docker compose에서 컨테이너의 이름으로 서비스를 연결하는 것과 같은 개념이다.
즉, 어떤 App이 어떤 App을 찾는 것이다.
서비스를 디스커버리 하는 가장 일반적인 형태가 DNS이다.
이 DNS는 kube-dns라고 하고 클러스터 내에서 사용한다.
k8s 클러스터 내부에서 컨테이너가 다른 컨테이너를 찾을 때 사용하는 것이 DNS이다.
coreDNS - 쿠버네티스 내에 DNS를 구현하기 위해 사용하는 오픈 소스이다.
DNS는 애드온이긴 하지만 반드시 써야하는 중요한 애드온이다.
✔️ 웹 UI (대시보드)
웹 UI는 일반적으로 사용하지 않는다.
모니터링은 프로메테우스를 사용하는 것이 더 좋기 때문에 보통 사용하지 않는다.
✔️ 컨테이너 리소스 모니터링
원래 힙스터라는 모니터링 솔루션이 있었는데 프로젝트가 끝났고 프로메테우스를 사용하고 있다.
✔️ 클러스터-레벨 로깅
ElasticSearch 라는 오픈 소스가 있는데 이걸 이용해서 로깅 기능을 사용한다.
Docker에서 기본적으로 Json 형태로 로그가 남는데 컨테이너가 지워지면 로그도 지워지므로 별도로 저장해야 한다.
이것이 클러스터 레벨의 로깅이다.
쿠버네티스에서는 이것을 ElasticSearch 로 구성하고 ELK로 구현한다.
'DevOps > Kubernetes' 카테고리의 다른 글
[Kubernetes] Worker Node 추가 구성하기 (0) | 2022.05.16 |
---|---|
[Kubernetes] kubeadm을 사용한 K8s 설치 및 K8s 클러스터 생성 (0) | 2022.05.16 |
[Kubernetes] Pod 생성 과정 (0) | 2022.03.28 |
[Kubernetes] Object - Volume이란 (3) (0) | 2022.03.28 |
[Kubernetes] Object - Volume이란 (2) (0) | 2022.03.28 |
영차영차 성장 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!