Admission Webhook
개요
어드미션 컨트롤 도 웹훅을 설정하는 것이 가능하다.
어드미션 컨트롤에서의 유형과 마찬가지로, 여기에도 두 가지 유형이 존재한다.
- validating admission webhook
- mutating admission webhook
먼저 변형이 완료된 이후에 최종적으로 검증이 들어가게 될 것이다.
순서가 이러니, 최종 상태를 확인하는 웹훅은 반드시 validating 쪽에 넣어야 할 것이다.
사용하기 위해서는 admissionregistration.k8s.io/v1
api가 활성화돼있어야 한다.
설정 방법
어드미션 웹훅 서버 만들기
https://github.com/kubernetes/kubernetes/blob/release-1.21/test/images/agnhost/webhook/main.go
일단 요청을 받으면 그에 맞는 응답을 하는 서버를 만들어야 한다.
요청 설정
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"]
scope: "Namespaced"
clientConfig:
service:
namespace: "example-namespace"
name: "example-service"
caBundle: <CA_BUNDLE>
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 5
만약 웹훅 서버에 인증이 필요하다면 api서버에 --admission-control-config-file
에 파일을 넣어줘야 한다.
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionWebhook
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: WebhookAdmissionConfiguration
kubeConfigFile: "<path-to-kubeconfig-file>"
- name: MutatingAdmissionWebhook
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: WebhookAdmissionConfiguration
kubeConfigFile: "<path-to-kubeconfig-file>"
이런 파일을 넣으면 된다.
웹훅 설정
그래서 구체적으로 어떤 동작을 할지 로직을 써야겠지.
이를 위해서는 MutatingWebhookConfiguration
, ValidatinWebhookConfiguration
을 써주면 된다.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
...
webhooks:
- name: my-webhook.example.com
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: ["apps"]
apiVersions: ["v1", "v1beta1"]
resources: ["deployments", "replicasets"]
scope: "Namespaced"
...
이런 식으로 쓰면 되는데, 와일드카드를 사용하는 것도 가능하다.
라벨 셀렉터을 통해 요청을 매칭하는 것도 가능하다.
- name: my-webhook.example.com
objectSelector:
matchLabels:
foo: bar
namespaceSelector:
matchExpressions:
- key: runlevel
operator: NotIn
values: ["0","1"]
rules:
- operations: ["CREATE"]
apiGroups: ["*"]
apiVersions: ["*"]
resources: ["*"]
scope: "*"
이런 식으로 해주면, 라벨에 걸린 오브젝트들에 어떤 규칙들을 지정하는 것이 될 것이다.
네임스페이스에 대해서도 성립한다.
matchPolicy
한꺼번에 여러 api 그룹과 버전을 지정하고 싶다면?
matchPolicy를 이용하면된다.
여기에는 두 가지 값이 가능하다.
- Exact
- 정확하게 매칭돼야만 적용된다.
- Equivalent
- 다른 api 그룹과 버전이더라도 rule에 명시된 리소스라면 적용돼야 한다.
- 이때 적용되는 요청은 웹훅을 타고갈 때 rule에 명시된 대로 변경될 것이다.
대체로 후자가 추천되는데, 이후에 업데이트가 일어나 버전이 바뀌어도 웹훅이 적용될 것이기 때문이다.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
webhooks:
- name: my-webhook.example.com
matchPolicy: Equivalent
rules:
- operations: ["CREATE","UPDATE","DELETE"]
apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
scope: "Namespaced"
이런 식으로 하면 그럴 일이야 없겠지만.. 이후에 디플로이먼트의 api 그룹이 변경되더라도 웹훅이 적용될 것이다.
matchConditions
matchConditions:
- name: 'exclude-leases' # Each match condition must have a unique name
expression: '!(request.resource.group == "coordination.k8s.io" && request.resource.resource == "leases")' # Match non-lease resources.
- name: 'exclude-kubelet-requests'
expression: '!("system:nodes" in request.userInfo.groups)' # Match requests made by non-node users.
- name: 'rbac' # Skip RBAC requests, which are handled by the second webhook.
expression: 'request.resource.group != "rbac.authorization.k8s.io"'
이런 식으로 CEL 표현식을 사용하는 것도 가능하다.
이렇게 하면 HTTP 상에서의 상세한 필터링도 가능해질 것이다.
이 모든 조건을 충족해야만 웹훅이 날아간다.
여기에 특별한 변수들이 있다.
- object - 들어온 요청 오브젝트
- oldObject - 존재하던 오브젝트
- request - 위의 AdmissionReview에 담긴 내용들
- authorizer - CEL 인가자.
- authorizer.requestResource - 요청된 리소스에 대한 빠른 인가 체크
clientConfig
clientConfig:
url: "https://my-webhook.example.com:9443/my-webhook-path"
여기에 보낼 웹훅 주소를 넣는다.
clientConfig:
caBundle: <CA_BUNDLE>
service:
namespace: my-service-namespace
name: my-service-name
path: /my-path
port: 1234
클러스터 내부 서비스로 있는 거라면 이렇게 지정해도 된다.
sideEffects
webhooks:
- name: my-webhook.example.com
sideEffects: NoneOnDryRun
하나의 오브젝트에 대해서 변형을 진행하나 이게 다른 곳에서 영향을 미칠 수도 있다.
이에 대한 회복 로직을 잘 마련해줘야 한다.
웹훅은 한번 요청을 변경은 해도 해당 요청이 잘 수행될지에 대한 검토를 하지 않는다.
그래서 dry run으로 오는 요청에 대해 제대로 응답을 해야 한다.
dry run은 그냥 테스트만 하는 요청이기 때문에 실제 변형을 짆애하지 않게 해야 한다.
reinvocationPolicy
- name: my-webhook.example.com
reinvocationPolicy: IfNeeded
여러 mutating webhook이 동작하는 환경에서는 웹훅이 여러번 보내져야 하고, 이것들이 전부 적용되기 위해서는 이전 요청에 대한 응답이 제대로 다음 요청으로 보내져야 할 것이다.
이럴 때는 재호출 정책을 지정한다.
Never를 쓰면 한번만 호출이 일어나고, IfNeeded면 변형이 진행된 오브젝트가 다른 룰에 들어가야 할 때 재호출을 해준다.
주의점이 있는데, a와 b가 있는데 a로 인한 변형 때문에 b로 재호출이 일어나고, 다시 a로 호출이 가야한다면 이는 제대로 동작하지 않을 것이다.
또한 재호출을 최소화하기 위해 웹훅이 알아서 재정렬될 수도 있다.
전부제대로 됐는지 확인하려면 validatain webhhok 을 쓰자.
이래서 왠만해서 Idempotent해야 한다.
감사
api 서버에서는 웹훅으로 인해 어떤 상황이 일어났는지 이벤트로 남겨준다.
모범 사례
각 웹훅은 멱등하게 만드는 것이 좋다.
같은 요청이 몇 번을 들어와도 같은 응답을 내뱉게 만들어야 한다.
가령 파드 생성 요청에 현재 시간을 이름으로 가지는 사이드카 컨테이너를 넣는 것은 전혀 멱등하지 않다.
또 이미 사이드카가 있는데 같은 이름을 가진 사이드카를 또 넣는다던가 하는 식의 오류를 방지해야 한다.
관련 문서
이름 | noteType | created |
---|