Photo by Spencer Davis on Unsplash

Kubernetes CRD와 Operator에 대해서

최근에 Kubernetes를 통해서 프로젝트를 진행했었다. Kubernetes의 기본적인 사용법과 어떻게 쓰는지는 시간이 지나면서 자연스레 익혀지지만 Kubernetes의 전체적인 철학이나 어떻게 Kubernetes가 동작하는지 원리에 대해서 개념이 머리에 명확하지 않은 것 같아서 시간을 내어 따로 정리해보려고 한다.

그 중에서도 CRD와 Operator 위주로 정리해보려고 하는데 이 두 개의 개념이 Kubernetes의 전반적인 개념과 동작 원리를 가장 잘 보여준다고 생각하기 때문이다.

Custom Resources

Kubernetes에서는 resource라는 개념이 있는데 공식문서에 따르면 resource는

Kubernetes Object를 모아놓은 Kubernetes API의 엔드포인트

라고 한다.

사실 정의에서부터 확실히 모르는 개념이 있었기 때문에 이해하기가 쉽지 않았다. 그래서 먼저 Kubernetes Object와 Kubernetes API에 대해서 먼저 정리해보려고 한다.

Kubernetes Objects

Kubernetes Objects는 공식문서에

persistent entities in the Kubernetes system

으로 표현되는데 Kubernetes는 이 object를 이용해서 다음과 같은 정보를 나타낼 수 있다.

  • application에 현재 붙어 있는 node들
  • application이 현재 이용할 수 있는 resource들
  • application이 어떻게 동작할지에 대한 policy: 간단한 예로 application이 어떻게 재시작할지 어떤 방식으로 업데이트할 것인지 fault-tolerance에 대한 정책들이 있을 수 있다.

Kubernetes의 독특한 점 중 하나인데 Kubernetes의 object는 java에서 말하는 object와는 느낌이 다르다. java에서는 object를 생성하게되면 그 순간 존재하는 것이지만 Kubernetes에서 object를 생성한다고 했을 때는 그것을 바로 생성하는 것은 아니다.

만약 사용자가 kubectl 을 통해서 pod을 1개 생성하는 요청을 보내면 Kubernetes system에서는 pod 하나가 존재하는 “상태”를 Kubernetes에 기록해놓는다. 그리고 Kubernetes 시스템은 그 상태를 맞추기 위해서 현재 상태와 사용자가 ‘원하는’ 상태를 비교하게 된다. 예를 들어 pod 한 개를 만들라는 상태를 kubernetes system에 기록해놓았는데 실제 클러스터에 pod 하나가 떠있지 않다면 Kubernetes system은 사용자가 ‘원하는’ 상태를 맞추기 위해서 실제로 pod을 클러스터에 생성할 것이다.

Object Spec and Status

모든 Kubernetes Object는 공통적으로 두 가지 필드를 가지고 있는데 specstatus가 그것이다.

  • Spec은 해당 object가 어떤 상태를 가질지에 대한 명세를 가지고 있다.
  • Status는 실제로 클러스터에 떠 있는 object가 어떤 상태를 가지고 있는지에 대한 정보를 담고 있는데 이것은 Kubernetes System에 의해서 바뀐다.

Kubernetes API

이와 같이 Kubernetes Object와 관련해서 CRUD 작업을 하기 위해서는 Kubernetes API를 통해서 해야한다. kubectl 은 사용자가 Kubernetes Object를 생성하고 싶다는 요청을 받으면 사용자 대신에 자신이 직접 Kubernetes API에 필요한 요청을 날리고 그 결과 클러스터에 해당 object가 생성되는 것이다.

kubectl 대신에 Kubernetes API 클라이언트 라이브러리를 통해서 프로그래밍적으로도 사용자가 직접 요청을 보낼 수 있다.

Custom Resources

이제 다시 정의로 넘어와보면

resource는 API object를 모아놓은 Kubernetes API의 엔드포인트

kubernetes에서 기본적으로 제공되는 pod resource를 생각해보았을 때 pod resource를 통해서 사용자는 pod과 관련해서 CRUD 를 수행할 수 있을 것이고 pod이라는 것이 어떤 내용을 담고 있는지 알 수 있을 것이다. 좀 더 추상적으로 정리하면 pod resource는 pod과 관련된 Kubernetes의 operation + pod에 대한 정의라고 볼 수 있을 거 같다.

custom resource는 Kubernetes에서 기본적으로 제공하는 resource들 이외에 사용자가 자신의 필요에 따라서 새로 정의한 resource를 말하는데 다르게 말하면 Kubernetes API를 확장하는 작업이라고 볼 수 있다. 이 부분과 관련해서는 Kubernetes에서 aggregation layer라는 개념이 있는데 이 부분은 직접 읽어보는 것이 나을 것 같아서 링크만 첨부한다.

한 번 custom resource가 특정 클러스터에 설치되면 다른 권한이 있는 유저들은 custom resource의 object를 kubectl 을 통해서 생성할 수 있다. 이것은 해당 resource에 대해서 REST API가 aggregation layer를 통해서 Kubernetes API가 확장되었기 때문에 가능한 것이라고 볼 수 있다.

Custom Controllers

Kubernetes controller는 기본적으로 유저가 ‘원하는’ 상태를 읽어서 현재 상태와 비교해 ‘원하는’ 상태로 클러스터의 싱크를 맞춰주는 역할을 한다.

Custom Controller는 어느 resource와도 같이 사용될 수 있지만 custom resource와 같이 사용될 때 가장 강력하다. Custom resource와 custom controller를 같이 사용하는 패턴을 Operator Pattern이라고 하는데 Operator pattern은 custom resource에 대해서 사용자가 원하는 상태를 유지하도록 만들어 준다.

Operator Pattern

Operator Pattern은 현실 세계에서 관리자(operator)의 컨셉을 따온 시스템이다. 현실 세계에서 관리자는 자신이 담당하고 있는 공정이나 서비스의 자세한 부분들을 속속히 알고 있고 자신이 알고 있는 지식에 따라서 각 공정이나 서비스가 어떻게 운영되어야하고 관리되야하는지를 명확하게 알고 있는 사람이다.

Kubernetes operator도 이와 비슷하다. Operator는 자신의 custom resource들을 관리할 때 application domain을 이용해서 resource들 각각이 어떻게 동작해야하고, 어떻게 배포되야하고, 문제에 대해서 어떻게 반응해야하는지 등에 대해서 정의하고 핸들링할 수 있다. 다시 말하면 operator는 application-specific한 controller라고 볼 수 있다.

operator 또다른 강점은 application-specific하기 때문에 resource에 대해서 application domain을 이용해서 부가적인 동작을 하도록 만들 수도 있다. 예시는 아래에서 소개하겠다.

operator에 대한 구체적인 예로는 다음과 같은 상황이 있을 수 있다.

  • SampleDB라는 custom resource를 사용자가 만들어서 클러스터에 생성하였다.
  • 사용자가 SampleDB object를 Kubernetes API를 통해서 클러스터에 생성했다. 그 결과 사용자가 원하는 상태가 업데이트 되었다. (예: 클러스터 내에 SampleDB application 1개가 떠있어라.)
  • SampleDB operator는 Kubernetes control plane에 쿼리하여 SampleDB가 어떤 상태로 클러스터에 존재할지를 확인한다.
  • SampleDB operator는 현재 상태와 원하는 상태를 비교해서 만약 다르다면 원하는 상태로 가기 위한 동작을 취할 것이다. 원하는 상태로 가기 위한 동작으로는 다음과 같은 것들이 있을 수 있다.
  • 현재 상태에 SampleDB가 존재하지 않아서 새로운 SampleDB application을 클러스터 내에 배포할 수 있다. 이 때 SampleDB resource를 구성하는 PersistentVolumeClaim, 실제 application을 띄우기 위한 StatefulSet, 그리고 배포 초기에 설정한 initial configuration 작업을 돌릴 Job object들을 각각 만들 것이다.
  • 현재 상태에 원하는 상태보다 SampleDB가 더 많이 존재해서 SampleDB 하나를 지울 수 있다. 이 때 operator는 삭제할 SampleDB resource의 스내배샷을 찍고 StatefulSet, Volume 등 SampleDB와 관련된 resource들을 삭제할 것이다.
  • operator가 application domain을 이용해서 부가적인 동작을 하도록 코드를 추가 할 수 있다. 예를 들면 주기적으로 SampleDB를 백업하도록 하거나 특정 SampleDB의 버전이 예전 것이라면 최신 버전이 되도록 할 수도 있다.

Kubernetes Control Plane

Kubernetes system과 사용자의 cluster 사이에서 어떤 방식으로 통신하는지에 대해서는 Kubernetes Control Plane이 정의하고 있다. Kubernetes Control Plane에는 다양한 컴포넌트들이 존재하는데 그 중엔 대표적으로 Kubernetes Master와 kubelet 프로세스가 있다.

Control Plane은 클러스터 내부에 존재하는 모든 Kubernetes Object들의 기록들을 관리하고 있으며 사용자가 ‘원하는’ 상태를 etcd에 저장하고 있다. 그 기록들을 통해 object 상태를 관리하기 위해서 control loop를 돌리고 있다. Control loop는 클러스터 내부 상태에 변화가 생길 때마다 그것을 감지하고 만약에 사용자가 원하는 상태와 현재 상태가 일치하지 않으면 그것을 맞추기 위해서 동작한다.

예를 들어 사용자가 Kubernetes API를 통해서 Deployment를 생성할 때 사용자가 원하는 Deployment state를 같이 제출한다. Kubernetes Control Plane은 이것을 기록해서 보관하고 있고 이 상태를 맞추기 위해서 Deployment spec에 제시한 application을 클러스터 내부에 배포하게 된다. 이처럼 Control Plane은 클러스터 내부에 변화가 생길 때마다 다시 사용자가 원하는 형태로 만드려고 한다.

Reference

  • https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#understanding-kubernetes-objects
  • https://kubernetes.io/docs/reference/using-api/api-overview/
  • https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/
  • https://coreos.com/blog/introducing-operators.html
  • https://kubernetes.io/docs/concepts/#kubernetes-control-plane
  • https://kubernetes.io/docs/concepts/extend-kubernetes/operator/
  • https://admiralty.io/blog/kubernetes-custom-resource-controller-and-operator-development-tools/
  • https://medium.com/faun/writing-your-first-kubernetes-operator-8f3df4453234