쿠버네티스 아키텍처 - 1

노재원·2023년 4월 23일
0

Kubernetes

목록 보기
8/8

쿠버네티스 인 액션 책 내용 요약 + 제 생각을 녹여냈습니다.

쿠버네티스란

컨테이너화 된 애플리케이션들을 쉽게 배포하고 관리할 수 있게 해주는 소프트웨어 시스템

특징

  1. 애플리케이션 자동 배포 및 쉬운 운영

  2. 리눅스 컨테이너 기술 기반이기 때문에 애플리케이션들이 서로 격리되어 영향을 미치지 않음. 그래서 하나의 클러스터에 완전 다른 성격의 애플리케이션들을 띄울 수가 있다.

  3. 수천 대의 컴퓨터를 하나의 컴퓨터로 추상화 할 수 있으며, 이렇게 엮인 클러스터에 애플리케이션을 쉽게 배포하고 실행시킬 수 있다.

  4. 인프라를 추상화하여 개발, 배포, 관리를 단순화한다.

    개발자는 특정 인프라와 관련된 서비스를 따로 구현할 필요없이, 쿠버네티스에 맡기고 애플리케이션의 핵심 기능 구현에만 집중한다.

구조

쿠버네티스의 기본 구성 요소와 각 역할을 살펴보자. 노드 역할에 따라 필요한 구성요소가 다르다.

  1. Master Node (Control Plane)
    • 전체 쿠버네티스 시스템을 제어하고 관리한다
    • 구성요소
      1. API 서버
        • 관리자, 컨트롤 플레인, 그리고 워커 노드들과 통신한다
        • 구성요소끼리 통신할 때도 항상 API 서버를 통한다
      2. etcd
        • 현재 클러스터의 구성과 상태 정보들을 저장하는 고가용성 데이터베이스
        • Key-Value 저장소 제공
      3. 컨트롤러 매니저
        • 리소스 복제본 관리, 워커 노드 추적, 노드 장애 처리와 같은 클러스터 단의 기능을 수행
      4. 스케줄러
        • 적절한 워커 노드를 선정하여 애플리케이션을 해당 노드에 배포
      5. kubelet
        • 위의 구성요소들을 static pod로써 띄우기 위한 역할
        • 마스터 노드의 상태 보고
      6. Container Runtime
        • docker, cri-o와 같은 컨테이너 런타임
        • 마스터 노드에도 구성요소들이 파드로 뜨기 때문에 반드시 필요
  2. Worker Node
    • 실제 애플리케이션을 배포하고 실행한다.
    • 구성요소
      1. Container Runtime
        • docker, cri-o와 같은 애플리케이션을 띄우는 컨테이너를 실행시킬 런타임
      2. kubelet
        • 컨트롤 플레인의 API 서버와 통신하고, 노드의 상태를 관리하는 프로세스
      3. kube-proxy
        • 애플리케이션 간의 트래픽을 로드 밸런싱하는 등의 네트워크 관리를 담당
        • 모든 워커 노드에 하나씩 배포 됨 (DaemonSet)

아키텍처 Deep Dive

이제부터 구성 요소들 하나씩 자세히 살펴보자.

etcd

  • 클러스터에 생성된 모든 오브젝트들의 정보들을 영구적으로 가지기 위한 목적
  • 키-값 저장소
  • 오직 API 서버와만 통신하며, 이렇게 함으로써 저장소 메커니즘 추상화, 유효성 검사, 낙관적 잠금 시스템, 권한 제한 등의 이점이 있다.
    • 낙관적 잠금이란?
      데이터를 잠궈서 여러 클라이언트가 동시에 데이터를 업데이트하려 할 때 첫 번째 시도만 적용되는 시스템이다. 데이터의 버전 정보가 있어서, 데이터를 업데이트하면 데이터의 버전이 증가되며, 업데이트할 때 etcd에 저장되어 있는 버전정보와 내가 수정하려는 데이터의 버전 정보가 맞지 않는다면 수정 요청이 거부된다.
  • 고가용성 보장
    고가용성을 위해 여러 etcd 인스턴스를 띄우는데, RAFT 합의 알고리즘을 통해 각 인스턴스는 대다수가 동의한 현재 상태 혹은, 이전 상태를 가지고 있다.
    합의 알고리즘은 전체 인스턴스에 과반수가 존재하면 새로운 상태로 전환하는 방식이다. 만약 인스턴스가 3개 있을 때 네트워크 오류 등으로 인해서 1개 인스턴스가 다운되도, 나머지 2개가 과반수이기 때문에 남은 2개 인스턴스는 새로운 상태로 전환된다. 그러다가 다운된 인스턴스가 복귀되서 다시 클러스터에 들어오면 2개 인스턴스의 상태를 따라간다.
    이러한 이유로 보통 etcd 인스턴스는 홀수로 간다. 만약 인스턴스를 2개로 하면, 둘 중에 하나만 죽어도 과반수가 안되기 때문에 새로운 상태로 업데이트하지 못해, 오히려 한 개일 때보다 장애 발생률이 더 높다.

API 서버

클러스터 상태를 조회하고 변경하는 REST API CRUD를 제공하고 클러스터의 상태는 etcd에 저장한다. API 서버에서 요청에 대한 유효성 검사도 하고 낙관적 잠금도 처리하고 있다.
보통 우리는 kubectl로 API 서버와 통신하여 클러스터를 조회하고 업데이트한다. 상태를 업데이트할 때 API 서버에서 무슨 일이 일어나는지 좀 더 자세하게 알아보자.

API 서버 동작 과정

  1. 인증
    API 서버에 구성된 여러 인증 플러그인을 차례로 실행시켜 요청을 보낸 클라이언트를 인증하는 과정을 거친다. 클라이언트의 인증서나 HTTP 헤더를 통해 인증하며, 클라리언트의 사용자 이름, 그룹 등의 정보를 추출한다. 그리고 이 데이터는 다음 단계인 인가에서 사용된다.
  2. 인가
    인가는 사용자가 요청한 행동을 취할 권한이 있는지 확인하는 절차이다. 권한이 있다면 다음 단계로 넘어간다.
  3. 어드미션 컨트롤
    리소스를 수정, 생성, 삭제 하는 행동을 한다면 어드미션 컨트롤로 요청이 보내진다. 요청온 리소스의 누락된 부분을 기본값으로 채워주거나 강제로 재정의하거나, 요청을 거부할 수도 있다.
  4. etcd에 저장
    모든 어드미션 컨트롤을 통과하면 유효성 검사를 하고 etcd에 상태를 저장한다.

기타 기능

  • 리소스 변경사항에 대해 감시하고 있는 클라이언트들에게 새로운 상태의 리소스를 전달

스케쥴러

파드를 띄울 때 가장 적합한 노드를 선택하는 역할을 한다. 적합한 노드를 고를 땐 크게 3단계를 거친다. 1단계는 우선 수용 가능한 노드 목록부터 뽑는다. taint나 리소스 부족 등으로 파드를 받을 수 없는 노드들을 우선 제외시킨다. 그 다음 2단계는, 수용 가능한 노드 중에서 가장 적합한 노드를 고른다. 3단계는 만약 2단계 결과로 여러 노드가 여전히 남아있다면 Round-Robin으로 노드 하나를 선택한다.
쿠버네티스의 여러 기법으로 관리자가 좀 더 세세하게 스케쥴링 되도록 유도할 수 있다. Affinity와 AntiAffinity 설정을 통해 파드를 고르게, 혹은 집약적으로 스케쥴링 되도록 할 수 있다.
최종 노드가 선택되면 API 서버에 요청을 보내 파드 상태를 업데이트한다. API 서버는 특정 리소스를 감시하는 클라이언트들에게 통보하는 기능이 있어서, 파드를 감시하는 kubelet에게 파드의 새로운 상태를 통보한다(아마 kubelet은 파드만 감시하고 있진 않을 것이다). 대상 노드의 kubelet은 이를 확인하고 Container Runtime과 통신하여 파드 컨테이너를 생성 및 실행시킨다.

컨트롤러 매니저

API 서버로부터 리소스의 상태를 통보받고, 실제 상태가 통보받은 상태(원하는 상태)가 되도록 하고 그 상태를 유지시키는 역할을 한다. 컨트롤러 매니저 프로세스에는 Deployment Controller, StatefulSet Controller 등의 여러 컨트롤러를 포함하고 있다. 각 컨트롤러들은 각자 자기가 맡은 리소스를 감시하고 있으며, API 서버로부터 통보를 받으면 각 컨트롤러들이 실제 상태를 원하는 상태로 바꾸고 새로운 상태를 리소스의 status를 업데이트한다.
API 서버의 통보 기능을 통해 리소스의 변경 사항에 대해 통보 받지만, 놓칠 수도 있으므로 주기적으로 누락된 상태 변화가 없는지 목록을 가져오기도 한다.
컨트롤러들이 실제 상태를 원하는 상태로 변경할 때, 예를 들어 Deployment의 원하는 replica가 3인데 실제 replica는 2일 때 3으로 만들기 위해 파드 하나를 더 생성하려고 할 것이다. 마치 컨트롤러가 직접 파드를 만드는 것처럼 설명됐지만 사실은 그렇지 않다. 컨트롤러는 새로 만들 파드의 메니페스트를 만들어 API서버에 보내고, 스케쥴러와 kubelet을 통해 해당 파드가 생성되는 것이다. 즉 컨트롤러도 직접 액션을 취하지 않고 API 서버를 거쳐서 액션을 한다.

나머지 구성 요소는 다음 글에 이어서 보자.

0개의 댓글