1. 개요

Admission Webhook은 API 서버가 객체를 etcd 에 저장하기 직전에 호출하는 외부 HTTP 서버다. 쿠버네티스가 “이거 저장해도 되나? 고칠 데 있나?” 하고 남에게 물어보는 지점이다.

쿠버네티스 코드를 고치지 않고 조직 규칙을 집행할 수 있는 자리이며, 사이드카 자동 주입도 여기서 일어난다.

kubectlapplyAPI 서버인증·인가누구·자격Mutating고친다스키마 검증내장Validating통과 / 거부만etcd내 웹훅 서버내 웹훅 서버고치는 쪽이 먼저, 검사하는 쪽이 나중 — 최종 결과물을 봐야 하기 때문

2. 두 종류

MutatingValidating
할 수 있는 일객체를 고친다통과 / 거부만
순서먼저나중
쓰임사이드카 주입, 기본값 채우기정책 위반 차단

순서가 이렇게 정해진 이유는 단순하다. 고치는 쪽이 나중에 오면 검사가 무의미해진다. 검사를 통과한 뒤에 누가 내용을 바꿔버리면 검사한 의미가 없다. 그래서 다 고쳐놓고 최종 결과물을 검사한다.

공식 문서도 최종 상태를 봐야 하는 웹훅은 반드시 validating 을 쓰라고 못 박는다 — mutating 단계에서 본 객체는 그 뒤에 또 바뀔 수 있기 때문이다.

3. AdmissionReview

API 서버와 웹훅은 AdmissionReview 라는 JSON 을 주고받는다.

요청 — “이 객체를 이 사용자가 이렇게 만들려고 한다”

응답 — 거부하려면 이렇게

{ "allowed": false, "status": { "message": "이미지는 사내 레지스트리만 허용" } }

고치려면 이렇게

{
  "allowed": true,
  "patch": "W3sib3AiOiJhZGQiLCJwYXRoIjoi...",
  "patchType": "JSONPatch"
}

patch 는 base64 로 인코딩된 JSON Patch 다. “객체를 통째로 돌려주는” 게 아니라 “어디를 어떻게 바꿔라"만 보낸다.

[{ "op": "add", "path": "/spec/containers/-", "value": { "name": "vault-agent", ... } }]

사이드카 주입의 실체가 이것이다. 내가 낸 YAML 에 없던 컨테이너가 이 패치로 끼어든다.

4. failurePolicy

웹훅 서버가 죽으면 어떻게 되는가. 이 한 필드가 정한다.

값웹훅이 응답하지 않으면결과
Fail (기본)요청을 거부안전하지만, 웹훅이 죽으면 배포가 전부 막힌다
Ignore요청을 통과계속 돌아가지만, 정책이 조용히 무시된다

어느 쪽도 공짜가 아니다.

보안 정책 웹훅   → Fail   막혀서 못 뜨는 게, 검사 없이 뜨는 것보다 낫다
편의 기능 웹훅   → Ignore 사이드카 못 붙는다고 배포까지 막을 이유는 없다

타임아웃 기본값은 10초이고, 공식 문서는 짧게 잡으라고 권한다. Fail + 긴 타임아웃 조합이면 웹훅 하나가 느려질 때 클러스터 전체 배포가 그만큼 지연된다.

5. 순환 의존과 교착

공식 문서가 가장 강하게 경고하는 지점이다.

Admission webhooks are essentially part of the cluster control-plane. You should write and deploy them with great caution.

웹훅은 컨트롤 플레인의 일부다. 잘못 만들면 클러스터가 스스로를 잠근다.

전형적인 교착
  웹훅이 "모든 Pod 를 검사한다" 로 설정됨  (namespaceSelector 없이)
        ↓
  웹훅 서버 자신도 Pod 다
        ↓
  웹훅 Pod 를 띄우려면 → 웹훅에게 물어봐야 함 → 그 웹훅이 아직 안 떴음
        ↓
  영원히 못 뜬다 (failurePolicy: Fail 이면 완전 정지)

그래서 실무에서는 kube-system 과 웹훅 자신의 네임스페이스를 검사 대상에서 제외한다. 공식 문서의 표현으로는 자기가 의존하는 컴포넌트의 요청을 가로채지 말라는 것이다.

6. reinvocationPolicy

웹훅이 여러 개일 때 생기는 문제. A 웹훅이 먼저 돌고, 그 뒤 B 웹훅이 객체를 고치면 A 는 자기가 본 것과 다른 최종 객체를 갖게 된다.

값동작
Never (기본)한 번만 호출
IfNeeded다른 웹훅이 객체를 고쳤으면 다시 호출

IfNeeded 는 편해 보이지만 웹훅이 여러 번 실행되어도 결과가 같아야(멱등) 안전하다. 사이드카 주입 웹훅이 이걸 안 지키면 컨테이너가 두 개 붙는다.

7. 등록

kubectl apply 로 등록하는 리소스다. 재시작이 필요 없어서 문서 제목이 Dynamic Admission Control 이다.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: pod-policy.example.com
webhooks:
- name: pod-policy.example.com
  rules:                          # 어떤 요청을 가로챌지
  - apiGroups:   [""]
    apiVersions: ["v1"]
    operations:  ["CREATE"]
    resources:   ["pods"]
  clientConfig:                   # 어디로 물어볼지
    service:
      namespace: example-namespace
      name: example-service
    caBundle: <CA_BUNDLE>         # HTTPS 필수
  admissionReviewVersions: ["v1"]
  sideEffects: None
  timeoutSeconds: 5
  failurePolicy: Fail

직접 서버를 짜는 대신 OPA Gatekeeper 나 Kyverno 로 정책만 선언하는 방식이 일반적이다. 웹훅 서버 구현·TLS 인증서 관리·교착 회피를 대신 해준다.

8. 관련 문서

  • apiVersion — admissionregistration.k8s.io/v1 그룹 → apiVersion
  • Sidecar — 주입되는 대상 → Sidecar
  • CRD — 웹훅이 검증·변형하는 사용자 정의 리소스

9. 출처

두 종류의 순서, 응답 필드, failurePolicy, 교착 경고는 공식 문서 “Dynamic Admission Control” 에 근거한다.

“Mutating admission webhooks are invoked first, and can modify objects sent to the API server to enforce custom defaults. After all object modifications are complete, and after the incoming object is validated by the API server, validating admission webhooks are invoked.”

“Admission webhooks are essentially part of the cluster control-plane. You should write and deploy them with great caution.”

“Default timeout for a webhook call is 10 seconds. You can set the timeout and it is encouraged to use a short timeout for webhooks.”