오늘은 Admission Controllers 개념을 학습하고 kind 클러스터에서 직접 활성화/비활성화를 실습했다. kube-apiserver manifest를 수정해서 NamespaceAutoProvision을 켜고 끄면서 Mutating과 Validating의 차이를 체감했다.
RBAC이 "누가 뭘 할 수 있는지"를 제어한다면, Admission Controller는 요청 내용 자체가 괜찮은지를 검사한다. 인증/인가를 통과한 요청이 etcd에 저장되기 직전에 가로채는 구조다.
kubectl 요청 → Authentication → Authorization(RBAC) → Admission Controller → etcd 저장
Mutating — 요청을 수정해서 통과시킴 (namespace 자동 생성, 기본값 주입 등)
Validating — 수정 없이 허용/거절만 함
순서는 항상 Mutating 먼저, Validating 나중이다. 수정한 뒤에 검사해야 의미가 있기 때문이다.
Kubernetes에는 기본으로 내장된 Admission Controller들이 있고, 커스텀 규칙이 필요하면 Webhook 방식으로 직접 만들 수 있다.
| 종류 | 설명 |
|---|---|
| 내장 | Kubernetes 기본 탑재 (NamespaceLifecycle, LimitRanger 등) |
| 웹훅 | 내가 직접 만드는 커스텀 규칙 서버 |
kube-apiserver에 어떤 admission controller가 명시적으로 활성화되어 있는지 확인했다.
# kind 컨트롤플레인 컨테이너 진입
docker exec -it kind-control-plane bash
# manifests 디렉토리로 이동
cd /etc/kubernetes/manifests/
ls
# etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
cat kube-apiserver.yaml | grep enable-admission
# --enable-admission-plugins=NodeRestriction
현재 명시적으로 활성화된 건 NodeRestriction 하나였다. 명시 안 해도 기본으로 켜진 플러그인(NamespaceLifecycle, LimitRanger 등)은 따로 표시되지 않는다.
존재하지 않는 namespace에 Pod 배포를 시도했다.
kubectl run ghost-pod --image=nginx -n ghost-ns
Error from server (NotFound): namespaces "ghost-ns" not found
기본 상태에서는 namespace가 없으면 바로 거절된다. NamespaceLifecycle(Validating)이 막는 것이다.
NamespaceAutoProvision을 활성화하기 위해 컨테이너 안에서 manifest를 수정했다. vim이 없어서 sed를 사용했다.
# vim 없음
vim kube-apiserver.yaml
# bash: vim: command not found
# sed로 수정
sed -i 's/--enable-admission-plugins=NodeRestriction/--enable-admission-plugins=NodeRestriction,NamespaceAutoProvision/' \
/etc/kubernetes/manifests/kube-apiserver.yaml
# 확인
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep enable-admission
# --enable-admission-plugins=NodeRestriction,NamespaceAutoProvision

kube-apiserver가 재시작된 후 같은 명령을 다시 실행했다.
kubectl run ghost-pod --image=nginx -n ghost-ns
# pod/ghost-pod created
ghost-ns namespace가 존재하지 않았는데 Pod가 만들어졌다. NamespaceAutoProvision(Mutating)이 namespace를 자동으로 생성해준 것이다.
이번엔 반대로 NamespaceAutoProvision을 제거하고 같은 시도를 해봤다.
sed -i 's/NodeRestriction,NamespaceAutoProvision/NodeRestriction/' \
/etc/kubernetes/manifests/kube-apiserver.yaml
kubectl run test-pod --image=nginx -n deleted-ns
Error from server (NotFound): namespaces "deleted-ns" not found
Mutating(AutoProvision)이 없으니 Validating(NamespaceLifecycle)이 그대로 거절했다. Q2와 비교하면 어떤 Controller가 동작하느냐에 따라 결과가 완전히 달라진다는 걸 직접 체감할 수 있었다.
클러스터에 등록된 Validating/Mutating webhook이 있는지 확인했다.
kubectl get validatingwebhookconfigurations
# No resources found
kubectl get mutatingwebhookconfigurations
# No resources found
현재 kind 기본 설치라 등록된 webhook이 없다. 실무 클러스터에 Istio나 OPA/Gatekeeper를 설치하면 여기에 나타난다. 트러블슈팅 시 배포가 갑자기 거절될 때 이 명령으로 먼저 확인하는 게 첫 번째 체크포인트다.
| Mutating | Validating | |
|---|---|---|
| 역할 | 요청 수정 | 허용/거절 판단 |
| 수정 가능 | ✅ | ❌ |
| 실행 순서 | 1번째 | 2번째 |
| 예시 | NamespaceAutoProvision | NamespaceLifecycle |
Q. kube-apiserver에서 명시적으로 활성화된 admission controller를 확인하라.
→ /etc/kubernetes/manifests/kube-apiserver.yaml에서 --enable-admission-plugins 플래그 확인
Q. NamespaceAutoProvision을 활성화하고 존재하지 않는 namespace에 Pod 배포가 성공하는지 확인하라.
→ kube-apiserver manifest의 --enable-admission-plugins에 ,NamespaceAutoProvision 추가, vim 없으면 sed -i 사용
Q. NamespaceAutoProvision을 제거한 뒤 존재하지 않는 namespace에 Pod 배포 시 어떤 일이 발생하는지 확인하라.
→ Mutating 제거 시 Validating(NamespaceLifecycle)이 거절. Q2와 비교해서 Controller 유무에 따른 동작 차이 체감
Q. 클러스터에 등록된 Validating/Mutating webhook configuration을 조회하라.
→ kubectl get validatingwebhookconfigurations, kubectl get mutatingwebhookconfigurations