EMD Blog

Kubernetes 개요 본문

Kubernetes

Kubernetes 개요

EmaDam 2022. 9. 3. 10:52

쿠버네티스

쿠버네티스는 컨테이너 기반의 서비스를 관리하기 위한 오픈소스 플랫폼이다.

컨테이터를 사용하면 다음과 같은 장점을 가져갈 수 있다.

  • VM 보다 훨씬 가볍기 때문에 기민하게 애플리케이션을 배포할 수 있다.
  • 주기적으로 이미지를 빌드하고 빠르게 롤백 가능하다. (CI/CD)
  • 빌드/릴리스 시점에 애플리케이션 컨테이너 이미지를 생성하기 때문에 개발과 운영의 관심사가 분리된다.
  • OS 수준의 정보와 메트릭, Health Check 등을 확인 할 수 있다. (가시성)
  • 모든 환경에서 동일하게 구동된다. (일관성)
  • 다양한 OS, On-Premise, Public Cloud에서 구동된다. (이식성)
  • OS에서 애플리케이션을 실행 하는 수준으로 추상화 수준이 높아진다.
  • 마이크로서비스 배포 가능
  • 리소스를 격리하고 애플리케이션 성능을 예측 할 수 있다.
  • 자원을 효율적으로 사용 가능하다.

쿠버네티스를 사용하면 다음과 같은 기능을 사용 할 수 있다.

  • DNS 이름을 사용하거나 자체 IP 주소를 사용해 Container를 노출 할 수 있다.
  • 로그밸런서를 통해 트래픽을 분산하고 다양한 배포 패턴으로 배포를 안정적으로 할 수 있다.
  • 로컬, Public Cloud 등에 있는 스토리지들을 자동으로 장착 가능하다.
  • 쿠버네티스는 선언적 구성을 통해 컨테이너의 원하는 상태를 서술 할 수 있으며 속도도 설정할 수 있다.
  • 컨테이너가 필요로 하는 자원을 쿠버네티스에 요청하면 쿠버네티스는 그에 맞게 노드를 구성한다.
  • Health Check를 통한 자동복구 기능을 제공한다.
  • 민감한 정보를 쿠버네티스에서 관리해 스택 구성 시 시크릿을 노출하지 않고 배포 할 수 있다.
  • 쿠버네티스는 PaaS 수준의 다양한 기능을 제공하고 다른 솔루션들을 통합할 수 있다.

쿠버네티스 참고사항

  • 컨테이너에서 구동되는 어플리케이션이라면 쿠버네티스에서도 동작한다.
  • 소스 코드를 배포하거나 어플리케이션을 빌드하지 않는다. CI/CD를 사용해야 한다.
  • Mysql, Spark, Redis와 같은 어플리케이션 레벨의 서비스는 제공하지 않는다. 필요하면 해당 서비스가 포함된 컨테이너를 구동하면 된다.
  • 로깅, 모니터링, 알림 솔루션을 제공하지 않는다. 메커니즘만 제공하다.
  • 시스템 설정과 같은 기본 설정들은 선언적 API를 사용하면 된다.
  • 포괄적인 유지보수, 관리, 머신 설정, 자동 복구 시스템을 제공하지 않는다.
  • 단순히 워크플로우를 수행하는 것이 아닌 독립적이고 조합 가능한 제어 프로세스들로 구성되어 있다.

필수 용어들

문서를 읽기 전 먼저 알고있어야 할 내용들 정리

파드(Pod)

쿠버네티스에서 생서하고 관리할 수 있는 배포 가능한 가장 작은 컴퓨팅 단위이다. 그냥 설명 읽으면 조금 헷갈리는데 Docker에서 Docker Compose를 생각하면 된다. 사용자가 배포할 어플리케이션은 무조건 단일 컨테이너로 실행된다는 보장이 없다. 의존성이 있는 두 컨테이너가 함께 실행되어야 하는 어플리케이션이 있을 수도 있는데 이때 이 컨테이너들을 파드로 묶어버리면 된다.(참고로 쿠버네티스에서 파드는 최소 단위기 때문에 단일 컨테이너도 파드로 묶어야한다.)

자세한 내용은 나중에 추가로 정리

노드

위에서 컨테이너들을 묶어 파드로 만든다고 했다. 파드는 실행중인 상태가 아닌 명세서같은 파일인데 이것을 실행시켜야 하나의 어플리케이션이 된다. 그럼 실행은 어디서 시킬까? 컨테이너가 실행되어야 할 서버가 필요한데 이 공간이 노드이다. 노드는 VM일수도 있고 물리적인 머신일수도 있다. 중요한 것은 노드에서 파드를 실행시킨다.

자세한 내용은 나중에 추가로 정리

클러스터

노드들이 여러개(한개라도) 모아두면 그것을 클러스터라고 한다. 정확하게는 클러스터를 만들면 그 안에 노드를 구성할 수 있다. 즉, 클러스터는 노드들의 집합이다. 클러스터는 최소 한 개의 워커노드를 가진다.

컨트롤 플레인

클러스터에 노드들이 여러개 있고 노드안에 파드가 존재한다. 이 상태에서 파드를 실행시킨다고 하면 어떻게 실행을 시켜야할까. 당연하겠지만 사람이 노드에 접속해서 컨테이너를 실행시키지는 않을 것이다. 쿠버네티스에서는 노드들과 파드들의 관리를 컨트롤 플레인을 사용해 관리한다.

이 컨트롤 플레인은 여러 컴포넌트로 구성이 되어 있으며, 클러스터에 대한 스케줄링이나 새로운 파드를 실행시키는 등의 전반적인 결정과 이벤트를 트리거 한다.

이 컨트롤 플레인 컴포넌트들은 클러스터 내 어떤 노드에서든지 동작하지만 보통은 별도의 노드에서 실행 시킨다. 이 컨트롤 플레인이 실행되는 별도의 노드를 마스터 노드라고 부른다.

컨트롤러

쿠버네티스의 컨트롤러는 클러스터의 상태를 관찰하면서 클러스터의 상태를 의도한 상태에 가깝도록 리소스들을 생성, 변경 요청(API 서버로)하는 컨트롤 루프이다.

쿠버네티스 오브젝트

오브젝트는 어떤한 의도를 담은 하나의 레코드를 말한다. 아마 문서나 다른 블로그 같은 것들을 찾아봐도 설명들이 모호하게 느껴질 수 있는데 오브젝트는 정확히 무언가 하나를 지칭하는 말은 아니기 때문이다. 아래 문서를 보면 쿠버네티스에서 생성가능한 모든 오브젝트 spec(오브젝트 명세)을 확인 할 수 있는데 진짜 굉장히 많다.

https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#controllerrevision-v1-apps

결국에는 자주 사용되는 몇 가지만 미리 숙지하고 나머지는 필요에 따라서 찾아써야한다는 뜻이다.

쿠버네티스는 수많은 작업들을 자동화해주고 관리해주지만 결국 무엇을 자동화하고 무엇을 관리하는지는 사람이 지정해줘야 한다. 이런 것들을 오브젝트 spec을 작성해서 제출하면 쿠버네티스는 현재 클러스터의 상태를 spec에 맞게 유지하려고 노력한다. 간단하게 예를 들면 파드도 오브젝트인데 파드를 어떤 노드에 어떤 방식으로 실행시키고 이런 내용들을 spec에 맞게 작성해서 제출하면 쿠버네티스는 작성한 내용에 맞게 파드를 유지할 것이다.

자세한 내용은 나중에 추가로 정리

네임스페이스

이름 그대로 클러스터 내에서 리소스를 그룹핑 할 때 사용한다. 이 네임스페이스는 다양한 용도로 사용할 수 있는데 그 중 몇 가지를 살펴보면 다음과 같다.

  • 여러 사용자 간에 클러스터 자원 분배 → 네임스페이스 별로 자원을 제한 할 수 있는데 네임스페이스에 속해 있는 리소스를 관리하는 팀이 과하게 자원을 사용하지 않도록 할 수 있다.
  • 환경 분리 → 각 팀이 필요한 리소스만 관리할 수 있도록 분리하거나 dev, prod 와 같이 개발, 운영 환경을 분리 할 수도 있다.
  • 네임스페이스 단위로 스코핑 할 수 있는 오브젝트도 존재한다. → 디플로이먼트, 서비스 등

문서에 나와있는 몇 가지 사용 예시를 들었는데 네임스페이스는 그냥 필요에 따라서 적절하게 사용하기만 하면된다. 저 상황들과 사용예시는 말 그대로 예시일 뿐이다.

자세한 내용은 나중에 추가로 정리

컨트롤 플레인

컨트롤 플레인은 다양한 컴포넌트로 구성되어 있다.

kube-apiserver

https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

https://kubernetes.io/ko/docs/concepts/overview/kubernetes-api/

쿠버네티스는 API를 통해 사용자, 클러스터의 다른 부분, 외부 컴포넌트가 통신한다. 이 API를 노출 시키는 컴포넌트가 kube-apiserver이다. 문서를 보면 컨트롤 플레인의 프론트엔드라고 표현하고 있는데 이유는 컨트롤 플레인의 모든 동작은 이 API의 요청에 의해 동작하기 때문이다. 이 API 서버는 HTTP 기반으로 통신한다.

etcd

https://tech.kakao.com/2021/12/20/kubernetes-etcd/

https://etcd.io/docs/v3.6/faq/

클러스터의 모든 데이터를 저장하는 키-값 저장소이다. 여기에는 노드나 파드의 상태 정보들도 포함되어 있기 때문에 이 데이터베이스가 유실되면 모든 리소스가 미아가 된다. 그렇기 때문에 반드시 백업 계획을 가지고 있어야 한다.

kube-scheduler

이 컴포넌트는 노드가 배정되지 않은 파드를 감지해 노드를 배정해준다.

스케줄링에는 리소스에 대한 개별 및 총체적 요구 사항, 하드웨어/소프트웨어/정책적 제약, 어피니티(affinity) 및 안티-어피니티(anti-affinity) 명세, 데이터 지역성, 워크로드-간 간섭, 데드라인 등 다양한 요소들이 고려된다.

kube-controller-manager

컨트롤러 프로세스를 실행하는 컨트롤 플레인 컴포넌트이다. 컨트롤러는 아래와 같은 종류가 있다.

노드 컨트롤러: 노드가 다운되었을 때 통지와 대응

레플리케이션 컨트롤러: 지정된 수의 파드들을 유지

엔드포인트 컨트롤러: 서비스와 파드를 연결

서비스 어카운트 & 토큰 컨트롤러: 새로운 네임스페이스에 대한 기본 계정과 API 접근 토큰 생성

cloud-controller-manager

이름 그대로 Cloud 전용 컨트롤러만 실행시키는 컴포넌트이다. Cloud에 클러스터를 구성 할 경우 Cloud의 경우 각 Cloud Provider의 API를 사용해서 상호작용을 해야하기 때문에 이 컴포넌트가 필요하다. 이 컴포넌트에 포함되어 있는 컨트롤러는 아래와 같은 종류가 있다.

노드 컨트롤러 : Cloud 내 노드 관리

라우트 컨트롤러 : Cloud 내 컨테이너 간 라우트 구성

서비스 : Cloud에 Load Balancer 관리

노드 컴포넌트

kubelet

노드 내에서 파드가 spec에 맞게 잘 동작하는 것을 확인하려면 그것을 감시하는 Agent가 필요하다. Kubelet이 그 역할을 한다. 당연하지만 Spec을 기반으로 확인하기 때문에 Kubernetes를 통해서 생성되지 않은 Container는 무시한다.

kube-proxy

각 노드 간(정확히는 Container 간)의 통신을 위해서는 노드가 트래픽을 받아서 노드 내 Container로 잘 전달해주어야 한다. 때문에 각 노드에는 proxy 서버가 필요한데 이때 사용되는 proxy가 kube-proxy다. 실제로 노드 내 파드를 노출시켜주는 역할을 하기 떄문에 서비스 개념의 구현체라고 할 수 있다.

컨테이너 런타임

노드에서는 컨테이너가 실행되기 때문에 컨테이너를 띄울 수 있는 환경이 필요하다.

쿠버네티스에서는 containerd, CRI-O와 같은 컨테이너 런타임 및 모든 쿠버네티스 컨테이너 런타임 인터페이스 구현체를 지원한다.

Docker는 어디있나요..? 라는 생각을 할 수 있는데 아래 글을 읽어보면 도움이 된다.

https://kr.linkedin.com/pulse/containerd는-무엇이고-왜-중요할까-sean-lee

http://www.opennaru.com/kubernetes/open-container-initiative/

https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md

Container는 Docker만 있는 것이 아니기 때문에 Kubernetes에서는 각 런타임(Docker같은)별로 고수준의 인터페이스를 구현해 kubelet과 통합해야했다. 대충 어렵다는 뜻인데 구현 자체도 제각각이니 힘들겠지만 각 런타임별로 업데이트가 있을 경우 각 런타임별로 모두 대응해야했기 때문에 유지관리도 힘들었다. 꼭 이런 문제(K8s)만이 아니더라도 표준화에 대한 문제는 항상 생길 수 밖에 없기 때문에 Open Container Initiative(OCI)라는 오픈소스 단체를 설립(Linux Foundation 산하)해 표준을 잡기 시작한다. 그리고 Kubernetes에서는 저 OCI 표준만 만족하면 어떤 컨테이너 이미지도 실행할 수 있도록 CRI를 제공하기 시작했다. 즉, 결론은 containerd, CRI-O등은 OCI 표준을 지키는 CRI의 구현체이다.

애드온

쿠버네티스는 기본적으로 모든 것을 제공하고 있지 않다. 그 대신 필요하다면 애드온을 통해 기능을 확장할 수가 있다. 애드온으로 확장하는 기능은 클러스터 단위의 기능이기 때문에 기본적으로 kube-system 네임스페이스에 속해있다. 대표적인 기능들은 아래와 같은 것들이 있다.

  • DNS (기본 활성화 되어있음.)
  • Web 기반 UI 대시보드
  • 컨테이너 리소스 모니터링
  • 클러스터 단위 로깅

이 외에도 다양한 애드온이 있으며 아래 경로에서 확인할 수 있다.

https://kubernetes.io/ko/docs/concepts/cluster-administration/addons/

쿠버네티스 API

쿠버네티스 API를 사용하면 쿠버네티스의 오브젝트들을 query 하거나 조작 할 수 있다.

그냥 REST로 호출해서 사용해도 되지만 아래와 같이 CLI 도구를 통해서도 조작할 수 있다. CLI는 내부적으로 API를 호출한다.

  • kubectl
  • kubeadm

애플리케이션에서 API를 사용하고 싶다면 아래 라이브러리를 사용하면 된다.

OpenAPI V2

쿠버네티스 API 서버는 /openapi/v2 엔드포인트를 사용하며 통합된 OpenAPI v2 스펙을 제공하고 있다. 요청 헤더는 아래와 같이 지정할 수 있다.

  • Accept-Encoding (Optional)
    • gzip
  • Accept
    • application/com.github.proto-openapi.spec.v2@v1.0+protobuf
    • application/json (default)
      • (= application/json)

응답 포맷을 보면 protobuf가 있는 것을 볼 수 있는데 쿠버네티스에서는 클러스터 내부 통신에 gRPC를 사용할 수 있다. (protobuf는 gRPC 통신시 사용될 수 있는 이진 메세지 형식) gRPC에 대한 내용은 아래 문서를 참고.

https://docs.microsoft.com/ko-kr/aspnet/core/grpc/comparison?view=aspnetcore-6.0

OpenAPI V3

OpenAPI 버전 3이 출시되면서 Kubernetes도 OpenAPI V3를 지원한다. 그런데 V3가 최근에 출시돼서 그런지 아직 알파 기능이다. 이 기능은 기본적으로 비활성화되어 있으며 사용하려면 기능 게이트를 사용해 기능을 활설화 시켜야 한다. 이 기능을 활성화 시키면 쿠버네티스 API 서버는 V3 스펙에 맞춰서 다음과 같은 엔드포인트로 제공하게 된다.

  • /openapi/v3/apis/<group>/<version>

요청헤더는 다음과 같다.

  • Accept-Encoding (Optional)
    • gzip
  • Accept
    • application/com.github.proto-openapi.spec.v3@v1.0+protobuf
    • application/json (default)
      • (= application/json)

참고로 /openapi/v3로 요청하게 되면 사용 가능한 모든 group/version 목록을 json 형태로 반환한다.

지속성

쿠버네티스 오브젝트의 상태는 직렬화해서 etcd에 저장한다.

API 그룹과 버전 규칙

쿠버네티스의 API는 여러 그룹으로 나눌 수 있다. 확장성을 위해서 용도에 따라 나눈 것인데 각 그룹별로 아래와 같이 엔드포인트를 가진다. (참고로 그룹 단위로 API를 활성화/비활성화 가능하다.)

  • /apis/$GROUP_NAME/$VERSION

그런데 저 엔드포인트 규칙을 따르지 않는 그룹도 존재한다. 바로 핵심 API 그룹인데 이 API는 쿠버네티스 내 핵심적인 리소스(node, pods, service 등)를 다루는데 사용되는 만큼 아래와 같이 훨씬 간소화된 엔드 포인트를 가지고 있다.

  • /api/v1

핵심 API 그룹을 제외한 나머지 그룹들은 무엇이 있는지 확인해보고 싶다면 아래 경로로 가서 확인해보면 된다.

버저닝은 API Level에서 수행된다. 이 말은 /apis/batch/v1 라는 API를 호출하고 있을 때 이 API의 새로운 버전이 release되었다고 해서 /apis/batch/v1의 response body 값이 변경되지 않는다는 것을 뜻한다. 만약 새로운 버전의 API가 release된다면 /apis/batch/v2와 같이 새로운 엔드포인트로 release된다. 그렇기 때문에 쿠버네티스 API는 API의 업데이트와 상관없이 일관성있는 response를 제공하며 각 버전에 대한 접근을 직접 제어 할 수 있다.

또한 한 API는 같은 버전과 상관없이 동일한 리소스를 관리한다. 예를 들면 v1 버전으로 생성한 오브젝트가 있다면 이 오브젝트는 v2로도 읽기, 수정, 삭제가 가능하다.

API 변경사항

쿠버네티스 API는 새로운 버전이 release되더라도 기존 클라이언트와의 호환성을 깨지 않기 위해 장기간 호환성을 유지하는 것을 목표로 하고 있다. 새로운 API가 생성되거나 변경사항이 생겼을 때, 리소스 또는 필드가 생성/삭제 될 수 있다. 생성은 상관이 없지만 삭제의 경우 많은 사용자들이 영향을 받을 수 있기 때문에 Kubernestes 지원 중단 정책을 따라서 진행한다.

alpha 버전의 경우 호환성이 깨질 수 있기 때문에 항상 Release 정보를 확인해야 한다.