Security - Custom Controller & Operator

2026. 1. 2. 21:14·CKA

이전 글에서 CRD(CustomResourceDefinition) 를 만들어 Ticket 같은 커스텀 리소스를 Kubernetes API에 등록하면,

  • kubectl apply/get/delete 로 리소스 생성/조회/삭제
  • 리소스 데이터는 etcd에 저장

까지는 가능하다고 했습니다.

하지만 여기서 중요한 질문이 남습니다.

“Ticket 리소스를 만들면, 실제로 항공권 예약 API(booking API)를 호출해서 예약/변경/취소를 자동으로 수행하게 하려면?”

이 “실제 행동”은 Kubernetes가 CRD만으로 해주지 않습니다.
그 역할을 하는 것이 바로 커스텀 컨트롤러(Custom Controller) 입니다.


1) 컨트롤러란 무엇인가

컨트롤러는 한마디로:

  • Kubernetes 클러스터(정확히는 API Server의 오브젝트 상태)를 지속적으로 관찰(watch)
  • 특정 리소스(여기서는 Ticket)의 생성/변경/삭제 이벤트를 받아
  • “원하는 상태(Desired State)”를 “현재 상태(Actual State)”로 맞추도록 조정(reconcile) 하는 프로세스입니다.

Deployment를 만들면 Pod가 자동으로 맞춰지는 것도 같은 원리입니다.

  • Deployment 오브젝트는 etcd에 저장될 뿐이고
  • 실제로 ReplicaSet/Pod를 생성해 상태를 맞추는 건 Deployment Controller 입니다.

Ticket도 똑같습니다.

  • Ticket 오브젝트는 etcd에 저장될 뿐이고
  • 실제로 외부 예약 API를 호출해 예약/변경/취소를 수행하는 건 Ticket Controller 입니다.

2) “그럼 Python으로도 컨트롤러 만들 수 있지 않나?”

가능합니다. 예를 들어 Python으로:

  • Kubernetes API를 주기적으로 조회(polling)하고
  • 변경을 감지해서
  • 외부 API를 호출하는 방식

으로도 만들 수 있습니다.

다만 강의에서 말한 것처럼, 이런 방식은 실무에서 곧 어려워집니다.

  • 이벤트 기반 처리/재시도/중복처리 방지(멱등성)
  • 캐시(cache)와 큐(queue) 설계
  • 리소스 변경이 폭주할 때의 rate limiting
  • 워치(watch) 끊김 처리, 리스트-워치(list-watch) 패턴 등

그래서 Kubernetes 생태계에서는 보통 Go 기반 라이브러리(client-go / controller-runtime) 를 사용해 컨트롤러를 만드는 게 표준에 가깝습니다.


3) Go로 컨트롤러를 만들면 뭐가 편해지나 (핵심만)

Go 기반 컨트롤러는 보통 아래 구조로 동작합니다.

  1. Informer(Shared Informer)
    • API Server를 계속 polling하지 않고
    • list + watch 기반으로 이벤트를 받고
    • 로컬 캐시를 유지
  2. Workqueue
    • 이벤트를 바로 처리하지 않고 큐에 넣어
    • 순서/재시도/Rate limit을 관리
  3. Reconcile Loop
    • “현재 Ticket의 상태를 읽고 → 필요한 외부 동작 수행 → status 업데이트”를 반복
    • 컨트롤러는 “한 번만 실행”이 아니라 계속 루프를 돌며 상태를 맞춥니다.

컨트롤러 구현에서 가장 중요한 실무 원칙은 딱 2개입니다.

  • 멱등성(idempotency): 같은 이벤트가 여러 번 와도 결과가 깨지지 않게
  • status로 상태를 기록: spec은 사용자의 의도, status는 컨트롤러의 결과/진행상태

4) 커스텀 컨트롤러 개발 시작 방법 (강의 흐름 그대로)

강의에서는 “샘플 컨트롤러(sample-controller) 레포지토리”를 예로 듭니다. 흐름은 다음과 같습니다.

4.1 준비물

  • Go 설치
  • GitHub에서 sample-controller clone
git clone <sample-controller-repo-url>
cd sample-controller

4.2 코드 커스터마이징

  • controller.go 같은 파일에서
  • Ticket 리소스 생성/변경/삭제를 감지했을 때
  • 외부 예약 API를 호출하도록 커스터마이징합니다.

강의는 여기서 “코드 디테일”을 깊게 다루진 않고, 큰 흐름을 이해하는 데 집중합니다.

4.3 로컬에서 실행(개발 단계)

로컬에서 컨트롤러를 실행할 수도 있습니다. 이때 컨트롤러가 클러스터에 인증하기 위해 kubeconfig가 필요합니다.

# 예시: kubeconfig 지정
export KUBECONFIG=~/.kube/config

# 실행(레포 구조에 따라 go run ./... 또는 go run main.go 등)
go run .

이렇게 실행해두고, 다른 터미널에서 Ticket CR을 만들면:

kubectl apply -f my-ticket.yaml

컨트롤러가 Ticket 이벤트를 감지하고 외부 예약 API 호출 로직을 수행하는 형태입니다.


5) 컨트롤러를 “클러스터 안에서” 배포하기 (실무에서 대부분 이렇게 함)

매번 로컬에서 go run 하기보다는, 컨트롤러를 Docker 이미지로 패키징한 뒤 Deployment로 띄우는 방식이 일반적입니다.

5.1 컨트롤러 이미지 빌드/푸시(개념)

docker build -t myrepo/ticket-controller:0.1 .
docker push myrepo/ticket-controller:0.1

5.2 컨트롤러는 어떤 인증으로 API Server에 붙나?

클러스터 내부 Pod로 실행하면 컨트롤러는 kubeconfig 대신 보통 ServiceAccount 토큰(in-cluster config) 으로 API Server에 인증합니다.

즉, 컨트롤러 배포에는 거의 항상 다음이 같이 따라옵니다.

  • ServiceAccount
  • (Cluster)Role / (Cluster)RoleBinding (컨트롤러가 무엇을 읽고/쓰는지)
  • Deployment

예시(개념용, 최소 형태):

apiVersion: v1
kind: ServiceAccount
metadata:
  name: ticket-controller-sa
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: ticket-controller-role
rules:
- apiGroups: ["flights.com"]
  resources: ["tickets"]
  verbs: ["get", "list", "watch", "update", "patch"]
- apiGroups: ["flights.com"]
  resources: ["tickets/status"]
  verbs: ["update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: ticket-controller-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: ticket-controller-role
subjects:
- kind: ServiceAccount
  name: ticket-controller-sa
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ticket-controller
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ticket-controller
  template:
    metadata:
      labels:
        app: ticket-controller
    spec:
      serviceAccountName: ticket-controller-sa
      containers:
      - name: controller
        image: myrepo/ticket-controller:0.1

포인트: 컨트롤러는 보통 tickets/status 업데이트도 해야 하므로 status 권한을 별도로 포함하는 경우가 많습니다.


6) Operator Framework: CRD + Controller를 “하나의 설치 단위”로 패키징

여기까지 보면 커스텀 리소스를 쓰려면 보통 이렇게 해야 합니다.

  1. CRD 적용
  2. CR 생성
  3. 컨트롤러 배포(Deployment, RBAC, SA 등)

즉, CRD와 컨트롤러가 별개의 엔티티이고 설치/업데이트도 따로 관리해야 합니다.

6.1 Operator가 하는 일(핵심)

Operator는 보통 다음을 “한 패키지”처럼 제공합니다.

  • CRD
  • Controller(Deployment)
  • RBAC/ServiceAccount
  • 설치/업그레이드 매니페스트(버전 관리)

그래서 “Flight Operator”를 설치하면:

  • CRD가 설치되고
  • 컨트롤러가 같이 배포되며
  • 사용자는 CR만 만들면 되도록 구성되는 방향입니다.

6.2 Operator는 단순 설치를 넘어 “운영 작업”까지 자동화

강의에서 예로 든 etcd operator 같은 경우(개념적으로):

  • 클러스터 설치/스케일링/업그레이드
  • 백업 생성
  • 복구(restore)
  • 장애 감지 후 조치

처럼 사람이 하던 운영 절차를 코드로 자동화하는 방향이 Operator의 목표입니다.


7) OperatorHub / OLM(Operator Lifecycle Manager) 흐름(강의 수준 요약)

강의에서 말하는 설치 흐름은 대략 이 패턴입니다.

  1. OLM 설치
  2. Operator 설치(구독/설치 지침 따라)
  3. CR 생성하면 Operator가 알아서 애플리케이션을 구성

여기서 중요한 메시지는:

  • Operator는 “CRD+Controller”를 묶어 배포/업데이트/수명주기 관리까지 쉽게 해주는 생태계라는 점입니다.
  • 다만 강의에서도 말하듯, Operator 자체는 깊게 들어가면 별도의 코스가 필요할 정도로 주제가 커집니다.

8) 시험 관점 메모(강의 톤 유지)

강의에서는 보통:

  • CRD 작성/조회/다루는 문제는 나올 수 있다
  • 커스텀 컨트롤러를 직접 코딩하라는 문제는 보통 기대하지 않는다

라는 뉘앙스를 줍니다.

실무에서는 “컨트롤러/오퍼레이터를 만들고 운영”하는 일이 중요하지만, 자격시험에서는 대개 CRD 리소스 모델 이해 쪽이 더 중심이 됩니다.


9) 정리

  • CRD: Kubernetes에 “새 리소스 타입”을 등록 → 저장/조회/삭제 가능
  • Custom Controller: 그 리소스의 변화를 감시하고 실제 행동(외부 API 호출 등)을 수행
  • 컨트롤러는 보통 Go(client-go/controller-runtime)로 작성하는 게 표준에 가깝고, watch/queue/cache 기반으로 동작
  • Operator: CRD + Controller + RBAC + 배포 매니페스트를 “하나의 설치 단위”로 패키징하고, 운영 자동화까지 확장하는 개념

'CKA' 카테고리의 다른 글

Storage - Docker의 Storage  (0) 2026.01.03
Security - 정리(2)  (1) 2026.01.02
Security - Custom Resource Definition  (0) 2026.01.02
Security - Context & 네임스페이스 전환을 빠르게 하는 도구  (0) 2026.01.02
Security - NetworkPolicy  (0) 2026.01.02
'CKA' 카테고리의 다른 글
  • Storage - Docker의 Storage
  • Security - 정리(2)
  • Security - Custom Resource Definition
  • Security - Context & 네임스페이스 전환을 빠르게 하는 도구
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (243)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (119)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      typequery
      고급
      @args
      @discriminatorcolumn
      양방향 맵핑
      JPQL
      스레드
      조회 성능 최적화
      프록시 팩토리
      @discriminatorvalue
      log trace
      hibernate5module
      Thread
      jdk 동적 프록시
      gesingleresult
      페치 조인
      자바
      cglib
      김영한
      requset scope
      락
      단방향 맵핑
      jpq
      빈 후처리기
      @within
      Target
      WAS
      프록시
      reentarantlock
      버퍼
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    Security - Custom Controller & Operator
    상단으로

    티스토리툴바