OPA Gatekeeper란?
OPA(Open Policy Agent) Gatekeeper는 Kubernetes 클러스터에 정책을 코드로 정의하고 강제하는 Admission Controller다. “특정 레이블이 없는 Pod는 배포 불가”, “latest 태그 이미지 사용 금지” 같은 운영 규칙을 Rego 언어로 작성하고, API 서버가 리소스를 생성·수정할 때 자동으로 검증한다.
Gatekeeper는 OPA를 Kubernetes-native하게 감싼 것으로, ConstraintTemplate과 Constraint CRD를 통해 정책을 선언적으로 관리한다. Pod Security Policy(PSP)가 deprecated된 후, Gatekeeper는 사실상의 표준 정책 엔진이 되었다.
Gatekeeper 설치
# Helm으로 설치
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm repo update
helm install gatekeeper gatekeeper/gatekeeper
--namespace gatekeeper-system
--create-namespace
--set replicas=3
--set audit.replicas=1
--set audit.interval=60
# 설치 확인
kubectl get pods -n gatekeeper-system
# NAME READY STATUS
# gatekeeper-audit-7c8d9f5b6-xxxxx 1/1 Running
# gatekeeper-controller-manager-xxx-xxxxx 1/1 Running
# gatekeeper-controller-manager-xxx-yyyyy 1/1 Running
# gatekeeper-controller-manager-xxx-zzzzz 1/1 Running
Controller Manager는 Validating Admission Webhook으로 동작하며, Audit 컨트롤러는 기존 리소스가 정책을 위반하는지 주기적으로 스캔한다.
핵심 개념: ConstraintTemplate + Constraint
Gatekeeper의 정책은 두 계층으로 나뉜다.
| 구성 요소 | 역할 | 비유 |
|---|---|---|
| ConstraintTemplate | 정책 로직 정의 (Rego) | 클래스 (틀) |
| Constraint | 정책 적용 범위·파라미터 설정 | 인스턴스 (적용) |
하나의 ConstraintTemplate으로 여러 Constraint를 만들 수 있다. 예를 들어 “필수 레이블 검사” 템플릿으로 “app 레이블 필수”, “team 레이블 필수” 등 여러 정책을 생성한다.
실전 1: 필수 레이블 강제
# ConstraintTemplate: 필수 레이블 검사 로직
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8srequiredlabels
spec:
crd:
spec:
names:
kind: K8sRequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("필수 레이블 누락: %v", [missing])
}
---
# Constraint: 모든 Namespace에 app, team 레이블 필수
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: ns-must-have-labels
spec:
enforcementAction: deny # deny | dryrun | warn
match:
kinds:
- apiGroups: [""]
kinds: ["Namespace"]
parameters:
labels:
- "app"
- "team"
# 테스트: 레이블 없이 Namespace 생성 시도
$ kubectl create namespace test-no-labels
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh" denied the request:
[ns-must-have-labels] 필수 레이블 누락: {"app", "team"}
# 레이블 포함 시 성공
$ kubectl create namespace test-ok --dry-run=server -o yaml
-l app=myapp,team=backend
namespace/test-ok created (server dry run)
실전 2: latest 태그 이미지 금지
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdisallowedtags
spec:
crd:
spec:
names:
kind: K8sDisallowedTags
validation:
openAPIV3Schema:
type: object
properties:
tags:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdisallowedtags
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
tag := _get_tag(container.image)
forbidden := {t | t := input.parameters.tags[_]}
forbidden[tag]
msg := sprintf("컨테이너 '%v'에 금지된 태그 '%v' 사용: %v",
[container.name, tag, container.image])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not contains(container.image, ":")
msg := sprintf("컨테이너 '%v'에 태그 미지정 (latest 암시): %v",
[container.name, container.image])
}
_get_tag(image) = tag {
parts := split(image, ":")
tag := parts[count(parts) - 1]
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sDisallowedTags
metadata:
name: no-latest-tag
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment", "StatefulSet", "DaemonSet"]
namespaces:
- "production"
- "staging"
excludedNamespaces:
- "kube-system"
parameters:
tags: ["latest"]
match 필드로 정책이 적용될 리소스 종류와 네임스페이스를 세밀하게 제어할 수 있다. excludedNamespaces로 시스템 네임스페이스는 제외한다.
실전 3: 리소스 Requests/Limits 강제
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8scontainerlimits
spec:
crd:
spec:
names:
kind: K8sContainerLimits
validation:
openAPIV3Schema:
type: object
properties:
requireRequests:
type: boolean
requireLimits:
type: boolean
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8scontainerlimits
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
input.parameters.requireRequests
not container.resources.requests
msg := sprintf("컨테이너 '%v'에 resources.requests 미설정", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
input.parameters.requireLimits
not container.resources.limits
msg := sprintf("컨테이너 '%v'에 resources.limits 미설정", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
input.parameters.requireRequests
container.resources.requests
not container.resources.requests.cpu
msg := sprintf("컨테이너 '%v'에 CPU requests 미설정", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
input.parameters.requireRequests
container.resources.requests
not container.resources.requests.memory
msg := sprintf("컨테이너 '%v'에 Memory requests 미설정", [container.name])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sContainerLimits
metadata:
name: must-have-resource-limits
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
parameters:
requireRequests: true
requireLimits: true
리소스 관련 정책은 K8s ResourceQuota·LimitRange 운영 글의 LimitRange와 함께 사용하면 더 강력한 리소스 거버넌스를 구축할 수 있다.
enforcementAction: 단계적 적용
| 모드 | 동작 | 사용 시점 |
|---|---|---|
dryrun |
위반 기록만, 차단 안 함 | 정책 도입 초기, 영향 파악 |
warn |
경고 메시지 반환, 차단 안 함 | 팀에 인지시키는 전환 기간 |
deny |
요청 거부 | 프로덕션 강제 적용 |
# 권장 적용 순서
# 1단계: dryrun으로 기존 위반 사항 파악
spec:
enforcementAction: dryrun
# Audit 결과 확인
kubectl get k8srequiredlabels ns-must-have-labels -o yaml
# status:
# totalViolations: 5
# violations:
# - name: default
# message: "필수 레이블 누락: {"app", "team"}"
# 2단계: warn으로 전환 (팀에 알림)
spec:
enforcementAction: warn
# 3단계: 충분한 전환 기간 후 deny 적용
spec:
enforcementAction: deny
Gatekeeper Library 활용
OPA 커뮤니티에서 제공하는 gatekeeper-library에는 실무에서 자주 쓰는 ConstraintTemplate이 이미 준비되어 있다.
# gatekeeper-library 설치
git clone https://github.com/open-policy-agent/gatekeeper-library.git
cd gatekeeper-library
# 주요 템플릿 예시
library/
├── general/
│ ├── allowedrepos/ # 허용된 컨테이너 레지스트리만 사용
│ ├── containerlimits/ # 리소스 제한 필수
│ ├── disallowedtags/ # 특정 태그 금지
│ ├── requiredlabels/ # 필수 레이블
│ └── uniqueserviceselector/ # Service selector 중복 방지
├── pod-security-policy/
│ ├── host-network-ports/ # hostNetwork 사용 금지
│ ├── privileged-containers/ # 특권 컨테이너 금지
│ ├── read-only-root-fs/ # 읽기 전용 루트 파일시스템
│ └── volumes/ # 허용 볼륨 타입 제한
# 필요한 템플릿만 적용
kubectl apply -f library/general/requiredlabels/template.yaml
kubectl apply -f library/pod-security-policy/privileged-containers/template.yaml
Mutation: 리소스 자동 수정
Gatekeeper 3.10+부터 Mutation 기능으로 리소스를 자동 수정할 수 있다. 검증(Validation)만이 아니라 기본값 주입도 가능하다.
# Assign: 기본 리소스 limits 자동 주입
apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
name: default-memory-limit
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
location: "spec.containers[name:*].resources.limits.memory"
parameters:
assign:
value: "512Mi"
pathTests:
- subPath: "spec.containers[name:*].resources.limits.memory"
condition: MustNotExist # 이미 설정된 경우 덮어쓰지 않음
---
# AssignMetadata: 기본 레이블 자동 추가
apiVersion: mutations.gatekeeper.sh/v1
kind: AssignMetadata
metadata:
name: add-env-label
spec:
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
namespaces: ["production"]
location: "metadata.labels.env"
parameters:
assign:
value: "production"
pathTests의 MustNotExist 조건은 이미 값이 설정된 리소스를 덮어쓰지 않도록 보호한다.
Audit와 모니터링
# 전체 위반 사항 조회
kubectl get constraints -o wide
# 특정 Constraint의 위반 상세
kubectl get k8sdisallowedtags no-latest-tag -o json |
jq '.status.violations[]'
# Prometheus 메트릭 연동
# Gatekeeper는 기본적으로 /metrics 엔드포인트 제공
# 주요 메트릭:
# - gatekeeper_violations{constraint_kind, constraint_name}
# - gatekeeper_request_duration_seconds
# - gatekeeper_constraint_template_status
# Grafana 대시보드 설정
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: gatekeeper-metrics
namespace: gatekeeper-system
spec:
selector:
matchLabels:
gatekeeper.sh/system: "yes"
endpoints:
- port: metrics
interval: 30s
Prometheus + Grafana 연동에 대한 자세한 설정은 K8s Prometheus 모니터링 운영 글을 참고하자.
CI/CD 파이프라인 통합
# GitHub Actions에서 배포 전 정책 검증
name: Policy Check
on: [pull_request]
jobs:
gatekeeper-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# gator CLI로 로컬 검증 (클러스터 불필요)
- name: Install gator
run: |
curl -L https://github.com/open-policy-agent/gatekeeper/releases/download/v3.16.0/gator-v3.16.0-linux-amd64.tar.gz | tar xz
sudo mv gator /usr/local/bin/
# ConstraintTemplate + Constraint + 테스트 리소스 검증
- name: Verify policies
run: |
gator verify ./policies/...
# 특정 매니페스트가 정책을 통과하는지 테스트
- name: Test manifests against policies
run: |
gator test
--filename=./policies/
--filename=./k8s/manifests/
# gator test용 테스트 스위트 작성
# policies/tests/required-labels/suite.yaml
apiVersion: test.gatekeeper.sh/v1alpha1
kind: Suite
tests:
- name: "레이블 없는 Pod는 거부"
template: ../../templates/required-labels.yaml
constraint: ../../constraints/must-have-app-label.yaml
cases:
- name: "app 레이블 있으면 통과"
object: allowed-pod.yaml
assertions:
- violations: "no"
- name: "app 레이블 없으면 거부"
object: denied-pod.yaml
assertions:
- violations: "yes"
message: "필수 레이블 누락"
gator CLI를 사용하면 클러스터 없이 로컬에서 정책을 테스트할 수 있어, PR 단계에서 정책 위반을 사전에 잡을 수 있다.
운영 팁과 주의사항
# 1. Webhook 장애 대비: failurePolicy 설정
# Gatekeeper가 다운되면 모든 배포가 차단될 수 있음
# Helm values에서 설정:
controllerManager:
webhook:
failurePolicy: Ignore # Fail(기본) → Ignore로 변경 시 장애 시 허용
# 2. 시스템 네임스페이스 제외 (필수!)
# Config 리소스로 글로벌 제외 설정
apiVersion: config.gatekeeper.sh/v1alpha1
kind: Config
metadata:
name: config
namespace: gatekeeper-system
spec:
match:
- excludedNamespaces:
- "kube-system"
- "gatekeeper-system"
- "cert-manager"
processes: ["*"]
# 3. Constraint 상태 확인 자동화
kubectl get constraints -A -o custom-columns=
NAME:.metadata.name,
ACTION:.spec.enforcementAction,
VIOLATIONS:.status.totalViolations
마무리
OPA Gatekeeper는 Kubernetes 클러스터의 정책 관리를 자동화하고 표준화하는 핵심 도구다. ConstraintTemplate으로 재사용 가능한 정책 로직을 정의하고, Constraint로 적용 범위를 제어하며, Mutation으로 기본값까지 자동 주입할 수 있다. dryrun → warn → deny 순서로 단계적 적용하고, gator CLI로 CI/CD 파이프라인에 통합하면, 인적 실수 없이 일관된 보안·운영 정책을 강제할 수 있다.