앞서
이 게시글은 subicura 님의 인프런 유료 강의, "초보를 위한 쿠버네티스 안내서"를 듣고 정리하기 위해 작성했습니다.
아키텍처
사람을 시켜 컨테이너를 하나를 띄우려면?
1. 컨테이너 한개 생성 해달라는 요청을 받음
2. 그 요청을 어딘가에 적어둠
3. 서버의 현재 상태가 방금 적어둔 ( 컨테이너 1개 생성 ) 상태와 같은지 체크
4. 컨테이너 하나 있어야 하는데 없네? 만들기
이 개념이 굉장히 중요하다.
Desired State
1. Observe (상태 체크) : 현재 상태 == 원하는 상태 인지 체크
2. Diff ( 차이점 발견 ) : 현재 상태 != 원하는 상태 이므로
3. Act ( 조치 ) : 현재 상태 -> 원하는 상태
쿠버네티스는 이 3가지를 반복함.
다시 위의 예시로 돌아가서, 이번에는 서버를 2대로 증설하라는 요청을 받았다!
그렇게 서버를 새로 하나 띄웠다. 이 때 새로운 컨테이너는 어디에 띄워야 할까?
당연히 아무 것도 띄워져 있지 않은 2번 서버에 띄워야 할 것이다.
이 때, 어느 서버에 띄워줄 지 결정하는 사람을 Scheduler 라 하자.
컨테이너나 네트워크, 노드 등을 체크해주는 사람을 Controller 라 하자.
즉, 컨트롤러의 갯수에 따라 Desired State 가 굉장히 많아질 수 있다.
다시 나눠보면, 쿠버네티스의 영역은 두 가지가 생긴다.
실제로 컨테이너들이 띄워져있는 Node 부분과, Node 를 관리하기 위해 움직이는 Master 영역으로 나뉜다.
중간에서 Scheduler, Controller 들에게 명령을 해줬던 사람은 API Server 라는 용어로 부르고, API Server 가 명령을 기억해해두기 위해 적어두던 데이터베이스를 etcd 라고 한다.
etcd
- 모든 상태와 데이터를 저장
- 분산 시스템으로 구성하여 안전성을 높임 (고가용성, 보통 3대 정도 띄움 )
- 가볍고 빠르면서 정확하게 설계
- Key(directory) - Value 형태로 데이터 저장
- TTL, watch 같은 부가 기능 제공.
- 백업은 필수!
API Server
- 상태를 바꾸거나 조회
- etcd 와 유일하게 통신하는 모듈
- REST API 형태로 제공
- 권한을 체크하여 적절한 권한이 없을 경우 요청을 차단
- 관리자 요청 뿐만 아니라 다양한 내부 모듈과 통신
- 요청이 굉장히 많음. 수평으로 확장되도록 디자인
Scheduler
- 새로 생성된 Pod ( 지금은 컨테이너라고 생각하세요) 를 감지하고 실행할 노드를 선택 ( 어떤 노드에 어떤 컨테이너를? )
- 노드의 현재 상태와 Pod의 요구사항을 체크
- 노드에 라벨을 부여 ( 그래픽이 필요한 경우, gpu-enabled 라벨이 되어있는 노드에 배포한다던가..)
Controller
- 논리적으로 다양한 컨트롤러가 존재 (복제 컨트롤러, 노드 컨트롤러, 엔드포인트 컨트롤러...)
- 끊임 없이 상태를 체크하고 Desired State를 유지
- 복잡성을 낮추기 위해 하나의 프로세스로 실행
조회 흐름
- Controller 는 etcd 와 직접 통신하지 않고 API Server 에게 요청한다.
- API Server는 해당 Controller 가 조회 권한이 있는지 체크
- 권한이 있다면 etcd 에 정보 조회함.
- etcd 가 Desired State가 변경되었다고 API Server에 알려줌
- API Server는 Controller 에게 변경되었다고 알려줌
- Controller 는 Desired State 로 리소스 변경
- Contoller 는 변경 후에 API Server 에 변경 사항 전달
- API Server 는 얘가 정보를 변경할 권한이 있는지 체크하고,
- 권한이 있다면 etcd 에 정보 갱신을 기록함.
즉, Scheduler, Controller 들 모두가 API Server 와의 통신을 하고 있다.
이제 Node 부분을 살펴보자
크게 Proxy 와 Kubelet 이라는 컴포넌트가 있다. 마찬가지로 API Server 와 통신함.
Proxy 는 iptables, ipvs 방식을 사용하고, Kubelet 이 Pod 와 직접 통신한다.
Kubelet
- 각 노드에서 실행
- Pod을 실행/중지하고 상태를 체크
- CRI (Container Runtime Interface)
- 컨테이너를 직접 쓰는게 아니라 Pod 이라는 것으로 감싸서 사용함.
Proxy
- 네트워크 프록시 같이 부하 분산 역할
- 성능상의 이유로 별도의 프록시 프로그램 대신 iptables 또는 IPVS를 사용 ( 설정 파일만 관리 )
그럼 용어를 토대로 다시 예제로 돌아가볼까요?
- 관리자가 API Server 에게 Pod 를 띄워달라고 요청함
- API Server 는 etcd 에게 "Pod 을 생성하라는 요청이 들어왔다!" 라고 적어둔다.
- Controller 는 "새 Pod 가 확인 되어야 하나??" 를 계속 API Server 에게 체크 중이다.
- 헐, 요청이 들어왔네! Pod 할당 요청을 API Server에게 한다.
- API Server 는 다시 받아서 etcd 의 상태를 "Pod 할당 요청" 으로 바꾼다.
- Scheduler 또한 계속 API Server 에게 할당할 Pod 가 있는지 체크 중이다.
- 헐 있네! 새 노드에 할당하겠다고 API Server 에 요청함.
- API Server 는 etcd 에 "새로운 Pod 를 특정 노드에 할당하는데, 아직 미실행 상태다!" 를 기록함.
- Node 단의 Kubelet 은 "어, 내 담당 노드에 할당된 Pod 중에서 아직 미실행 된 게 있네?"를 체크함.
- 정보를 가져와서 Pod 생성하고, API Server 에 Pod 생성했다고 알려줌
- API Server 는 etcd 에 "새 Pod를 특정 노드에 할당해서 실행중이다" 를 기록함.
Addons
CNI (네트워크), DNS(도메인, 서비스 디스커버리), 대시보드(시각화) 등의 컴포넌트가 있다.
오브젝트
지금까지 쿠버네티스가 어떻게 내부적으로 동작하는 지 알아봤다. 이제 각 오브젝트를 알아보자.
Pod
- 가장 작은 배포 단위 ( 컨테이너를 감싼다 )
- 각 Pod 마다 고유 IP 를 받는다.
- 하나의 Pod 에 여러 개의 컨테이너가 들어갈 수 있다. 각 컨테이너가 localhost 의 폴더를 공유할 수도 있고 포트로 네트워크를 공유할 수도 있다.
ReplicaSet
- 여러 개의 Pod 를 관리
- 새로운 Pod 는 Pod Template 을 보고 하나 더 띄워줌.
- 원하는 Pod의 수(Replicas) 에 맞춰 Pod 를 관리
Delpoyment
- 배포 버전을 관리
- 무중단으로 배포하기 위해서, 순간적으로 ReplicaSet 을 2개로 만들고, 각 replicas 를 한 쪽 컨테이너는 줄이고, 한 쪽 컨테이너는 늘이면서 자연스럽게 update 시킴.
이 외에도 다양한 Workload 가 있다.
- Daemon Set : 모든 Node 에 꼭 하나씩만 떠있길 원하는 Pod 을 만들고 싶을 때(모니터링, 로그)
- Staeful Sets : 순서대로 Pod을 실행하고 싶거나, 같은 볼륨을 재활용하고 싶을 때
- Job : 한 번 실행하고 죽는 Pod
Service - Cluster IP
- 3개의 Pod 이 있을 때, ClusterIP 로 묶여 있으면 ClusterIP 로 요청을 보내면 3개 중에 하나로 보내서 수행함.
- 이렇게 하는 이유는, Pod 가 업데이트될 때 기존 Pod 이 유지되지 않고 죽었다가 새로 만들어진다.
- 즉 IP 가 유동적이기 때문에, 고유 IP 를 갖는 Service 의 ClusterIP 로 요청을 보내는 것.
- web 에서 redis 가 있는 Pod 로 요청을 보낸다면, redis 가 속해있는 ClusterIP 로 요청을 보냄.
Service - NodePort
- Cluster IP 는 내부 통신만 가능하다. 외부 브라우저에서는 접근이 안 된다.
- 외부에서 접근하기 위해서, Cluster IP 를 NodePort 로 감싸준다. (30000-32767 포트)
- 노드01 과 노드02 가 있다면, 노드01 로 직접 요청해서 Pod 에 접근할 수도 있고, 노드02에서 노드01 내부의 ClusterIP 에 요청해서 접근할 수도 있다. 즉 둘 중 어느 노드에 접근해도 같은 Pod 에 접근할 수 있게 되는 것
Service - LoadBalancer
- 노드01 로 도메인을 연결해놨는데 노드01이 갑자기 죽어버린다면? 노드02로 붙어야 하는데,,
- 직접 연결해뒀다면 접속이 안될 것이다.
- 이 때 사용자는 LoadBalancer 에게 요청을 한다. 그 요청이 NodePort 로 가고, ClusterIp 로 가고, Pod 로 가는 방식
Ingress
- 도메인 또는 경로 별 라우팅
- Nginx, HAProxy, ALB ...
일반적인 구성
- Pod 를 직접 띄우는 일은 잘 없다. Deployment 를 띄우면 자동으로 ReplicaSet 을 생성하고, 얘는 Pod 를 생성하고..
- 외부 노출을 위해 Service (ClusterIp) 를 붙이고, 얘는 NodePort 에 붙이고, LoadBalancer 에 붙이고..
- Ingress 에 연결해 도메인을 연결하면, 사용자가 www.어디어디 로 들어왔을 때 요청이 간다.
그 외 기본 오브젝트
- Volume ( Storage )
- Namespace - 논리적인 리소스 구분
- ConfigMap/Secret - 설정
- ServiceAccount - 권한 계정
- Role/ClusterRole - 권한 설정
API 호출
원하는 상태(Desired State) 를 다양한 오브젝트(Object) 로 정의(Spec) 하고 API 서버에 yaml 형식으로 전달
'DevOps > Kubernetes' 카테고리의 다른 글
초보를 위한 Kubernetes 안내서 (1) : 소개 (0) | 2021.06.22 |
---|
최근댓글