쿠버네티스 컨트롤러 패턴

Jeongmin Yeo (Ethan)·2021년 3월 18일
3

Kubernetes

목록 보기
3/4
post-thumbnail

쿠버네티스 패턴 중 하나인 컨트롤러 패턴에 대해 학습합니다.


컨트롤러(Controller) 패턴은 쿠버네티스 세트를 능동적으로 모니터링하고 요청한 상태로 유지 관리한다.

쿠버네티스 중심부는 선언된 오브젝트 상태와 함께 현재 어플리케이션 상태를 정기적으로 모니터링하고 reconcile하는 컨트롤러가 컬렉션으로 구성되어있다.

문제

쿠버네티스는 많은 기능을 기본으로 제공해주는 플랫폼이다.

하지만 이런 오케스트레이션 플랫폼이면서도 모든 어플리케이션을 어떻게 명령해서 돌아가는지는 다루지 않는다.

다행스럽게도 쿠버네티스는 다행스럽게도 변경이나 중단 없이 쿠버네티스 빌딩 블록 위에서 어플리케이션 확장 기능을 제공한다.

변경이나 중단 없이 어떻게 쿠버네티스를 확장시키는지 의문이 생길 것이다.

설계상 쿠버네티스는 선언적인 자원 중심 API를 기반으로 한다.

선언적(declarative)이란 말은 명령적(imperative)와는 접근 방식이 다르다. 선언적 접근 방식은 쿠버네티스에게 어떻게 작동해야 하는지를 알려주지 않고 원하는 상태를 선언해놓는다.

예로 디폴로이먼트를 확장하는 경우 쿠버네티스에서 새로운 파드 생성을 명령하지 않는다. 대신 쿠버네티스 api를 통해 디폴로이먼트 자원의 replicas 속성에 원하는 상태를 기술해놓는다.

그렇다면 새로운 파드는 어떻게 만들까? 이것은 컨트롤러에 의해 내부적으로 수행된다.

원하는 자원 상태가 변경될 때마다 쿠버네티스는 이벤트를 생성해 이해관계가 있는 리스너들에게 브로드캐스트 한다.

이러한 리스너는 새로운 자원을 생성 수정 삭제하는데 반응해서 동작하고 이를 통해 나온 이벤트는 또 다른 이벤트가 생성된다.

또 그럼 이런 이벤트는 다시 컨트롤러에 의해 선택되어 작업을 수행한다.

이러한 전체 절차를 State Reconciliation이라고 한다. 대상 상태가 현재 상태와 다르다면 컨트롤러가 다시 요청해서 원하는 상태로 바꾸는 걸 Reconile 이라는 작업을 한다.

이 관점에서 보면 쿠버네티스는 상태 관리자다. 쿠버네티스 컴포넌트 인스턴스 요청 상태를 선언하고 그 상태로 유지할려고 노력하며 변경사항이 있을 경우 해당 상태로 변경할려고 시도한다.

그렇다면 쿠버네티스 코드를 수정하지 않고 어떻게 이 Reconciliation 작업을 수행할 수 있는 걸까?

해결책

쿠버네티스에는 레플리카세트(ReplicaSet), 데몬세트(DemonSet), 스테이트풀세트(StatefulSet), 디플로이먼트(Deployment), 서비스(Service) 같은 표준 쿠버네티스 자원을 관리하는 내부 컨트롤러가 존재한다.

이러한 컨트롤러는 컨트롤러 관리자에 의해 실행되며 마스터 노드에 존재한다.

이런 컨트롤러는 서로 독립적으로 존재하며 무한 루프를 톨며 자원을 모니터링하고 변경 사항이 있다면 Reconciliation 작업을 수행한다.

하지만 이렇게 바로 사용 가능한 컨트롤러 외에도 쿠버네티스 이벤트 중심 아키텍처를 통해 기본적으로 여러 사용자 정의 컨트롤러를 플러그인(Plug in)으로 사용할 수 있다.

사용자 정의 컨트롤러는 내부 컨트롤러와 동일한 방식으로 동작 상태 변경 이벤트에 별도의 기능을 추가할 수 있다. 즉 컨트롤러의 특징은 이벤트에 반응해 빠르게 처리한다는 특징이다.

Reconciliation의 절차는 다음과 같다.

  • 관측(observe)

    • 관측하고 있는 자원이 변경될 때 쿠버네티스가 배포하는 이벤트를 주시해서 실제 상태를 찾는다.
  • 분석(analyze)

    • 실제 상태와 요청한 상태의 차이를 알아낸다.
  • 실행(act)

    • 실제 상태가 원하는 요청한 상태로 되도록 작업을 수행한다.

예를들어 레플리카세트 컨트롤러는 레플리카 세트 자원을 모니터링 하고 있다. 이때 원하는 요청이 들어오면 현재 실행 상태와 원하는 상태의 차이를 알아내고 쿠버네티스 API 서버에 해당하는 작업을 제출한다. 그럼 쿠버네티스 API 서버는 노드 컴포넌트(kubelet, kubeproxy)에게 요청해서 그 작업을 수행하도록 한다.

정리하면 컨트롤러는 원하는 요청을 받기 위해서 이벤트 리스너로 자신을 등록시켜 놓는다. 그리고 요청이 들어오면 해당 작업을 분석해서 쿠버네티스 API에게 작업을 제출한다.

컨트롤러는 컨트롤 플레인(Control Plane)에 포함되어 있고 복잡한 애플리케이션 수명주기를 관리하게 가능하도록 하는 표준 매커니즘이 되었다.

그 결과 오퍼레이터(Operator)라는 더욱 정교한 차세대 컨트롤러가 탄생하기도 했다.

발전적인 측면과 복잡성의 관점에서 Reconciliation 컴포넌트는 다음과 같은 두 그룹으로 분류할 수 있다.

  • 컨트롤러

    • 표준 쿠버네티스 자원을 모니터링 하고 작동하는 간단한 Reconciliation 프로세스로 컨트롤러는 플랫폼 동작을 향상시키고 새로운 플랫폼 기능을 추가할 수 있다.
  • 오퍼레이터

    • 오퍼레이터 패턴의 핵심인 CustomResourceDefinition(CRD)와 연동하는 정교한 Reconciliation 프로세스로 일반적으로 복잡한 애플리케이션 도메인 로직을 캡슗화 하고 전체 애플리케이션 수명주기를 관리한다.

여기서는 비교적 간단한 개념인 컨트롤러를 먼저 다루고 다음에 CRD를 소개하고 오퍼레이터 패턴을 알아보겠다.

여러개의 컨트롤러가 동일한 자원에 동시에 접근하는 걸 막기위해 컨트롤러는 싱글톤 패턴을 사용한다.

컨트롤러는 디폴로이먼트로 배포되지만 쿠버네티스는 자원 객체가 변경될 떄마다 동시성 문제를 막기위해서 Optimistic locking을 사용하기 때문에 하나의 레플리카만 사용한다.

이 말은 컨트롤러는 백그라운드에서 영구적으로 실행되는 애플리케이션이란 망이다.

쿠버네티스 자체가 Go로 작성되었고 쿠버네티스 컨트롤러도 대부분 Go로 작성되었지만 쿠버네티스 API에 요청을 보내는 컨트롤러는 어떠한 언어로 만들던지 상관없다.

컨트롤러는 자원을 정의하는 모든 필드를 모니터링하고 처리할 수 있지만 메타데이터와 컨피그맵이 이러한 컨트롤러의 목적에는 적합하다.

다음은 컨트롤러가 모니터링하는 자원을 정의하는 위치를 선택할 때 고려해야할 사항이다.

  • 레이블(Label)

    • 자원 메타데이터의 일부인 레이블은 컨트롤러에서 watch할 수 있으며 백엔드 데이터베이스에 인덱싱되어 쿼리로 효율적으로 검색할 수 있다. 셀렉터 등의 기능이 필요할 때 레이블을 사용하면 좋다.
  • 에노테이션(Annotation)

    • 애노테이션은 레이블 대신으로 쓸 수 있는 대안으로 만약 레이블 값이 구문 제한으로 벗어난다면 에노테이션이 적합할 수 있다. 애노테이션은 인덱싱되지 않으므로 컨트롤러 쿼리에서 키로 사용되지않는 비식별정보에 에노테이션을 사용한다. 애노테이션을 사용해도 쿠버네티스 성능에 부정적인 영향은 없다.
  • 컨피그맵

    • 컨트롤러에 레이블이나 에노테이션에 잘 맞지 않는 추가 정보가 필요한 경우 컨피그맵을 사용해 대상 상태를 저장하는게 가능하다. 이러한 컨피그맵은 컨트롤러에서 watch하고 읽어들인다.

    • CRD를 사용해서 대상 상태 명세를 설계하는게 훨씬 더 적합하고 쉽지만 클러스터 권한이 높아야한다. 그러한 권한이 없다면 컨피그맵이 더 적합하다.

다음은 컨트롤러 패턴을 샘플 구현으로 공부할 수 있는 예제 컨트롤러다.

  • jenkins-x/exposecontroller

    • 이 컨트롤러는 서비스를 줏시해서 메타데이터에서 expose라는 에노테이션을 발견한다면 컨트롤러는 서비스를 외부 접근을 위해 ingress 객체를 자동으로 노출한다.
  • fabric8/configmapcontroller

    • 컨피그맵 객체의 변경을 주시하고 연관된 디플로이먼트의 롤링 업데이트를 수행하는 컨트롤러다.

    • 컨피그맵을 볼 수 없고 동적으로 새로운 설정으로 업데이트할 수 없는 애플리케이션에 이 컨트롤러를 사용할 수 있다. 특히 파드가 컨피그맵을 환경변수로 사용할 수 없는 경우에 재시작하지 않고 애플리케이션을 자체 업데이트 할 수 없는 경우에 적합하다.


정리

컨트롤러는 관심 객체의 요청한 상태에 실제 상태를 알기 위해 관심 객체를 모니터링하는 능동적인 Reconiliation 프로세스다.

그런 다음 현재 상태를 요청한 상태와 비슷하게 변경하깅 위해 지시를 보낸다.

쿠버네티스는 이 매커니즘을 내부 컨트롤러에서 사용하며 상룡자 정의 컨트롤러에서 재사용할 수 있다.

다음에느 이 컨트롤러 패턴을 기반으로해 유연한 운영 설정 방법을 제공하는 오퍼레이터 패턴에 대해 알아보겠다.

profile
좋은 습관을 가지고 싶은 평범한 개발자입니다.

0개의 댓글