OPA 번들 서버 + GitOps로 구현하는 Kubernetes Policy-as-Code 실전 가이드
Kubernetes 클러스터를 운영하다 보면 "어떤 컨테이너 이미지는 허용하고 어떤 건 차단해야 한다", "모든 Pod에는 반드시 리소스 제한을 설정해야 한다"와 같은 정책 요구사항이 점점 많아집니다. 문제는 이런 정책이 애플리케이션 코드 곳곳에 흩어지거나, 담당자의 머릿속에만 존재하거나, 혹은 수동 리뷰에만 의존하는 경우가 많다는 점입니다. 이 글에서는 OPA(Open Policy Agent) 번들 서버를 Kubernetes에 배포하고, GitOps 워크플로로 Rego 정책을 자동 배포하는 방법을 단계별로 살펴봅니다.
Policy-as-Code는 정책을 YAML이나 문서가 아닌 Git 리포지토리에서 코드로 관리한다는 뜻입니다. GitOps와 결합하면 정책 변경도 코드 PR처럼 리뷰하고, CI에서 자동 검증하고, 클러스터에 무중단으로 반영할 수 있습니다. 전체 파이프라인의 흐름을 먼저 파악해두면 이후 설명이 훨씬 자연스럽게 연결됩니다.
Git push → GitHub Actions (opa test → opa build → s3 cp)
→ S3 번들 서버 → OPA 폴링(10~30초 주기) → 클러스터 정책 실시간 반영이 글을 통해 OPA 번들의 동작 원리, 번들 서버 구축 방법, GitHub Actions 기반 파이프라인 구성, 그리고 Gatekeeper와 OPAL까지 실제 운영에 바로 적용할 수 있는 패턴을 익힐 수 있습니다.
핵심 개념
OPA와 Rego: 정책을 코드로 표현하기
OPA는 CNCF 졸업 프로젝트로, 마이크로서비스·Kubernetes·CI/CD 파이프라인 등 다양한 환경에서 정책을 통합 관리하는 범용 정책 엔진입니다. 정책은 Rego라는 선언형 언어로 작성하며, 애플리케이션 코드와 정책 로직을 완전히 분리합니다.
아래는 Kubernetes Admission Webhook과 함께 동작하는 OPA에서 latest 태그 이미지 사용을 금지하는 Rego 정책 예시입니다.
# policies/k8s/no_latest_tag.rego
# 이 정책은 kube-mgmt 패턴의 OPA Admission Webhook용입니다.
# input.request 구조체를 통해 Kubernetes 요청 원문에 접근합니다.
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
# [_]는 Rego의 와일드카드 이터레이션 문법입니다 — 배열의 모든 요소를 순회합니다.
container := input.request.object.spec.containers[_]
endswith(container.image, ":latest")
msg := sprintf("컨테이너 '%v'는 latest 태그를 사용할 수 없습니다.", [container.name])
}Rego란? Datalog에서 파생된 선언형 질의 언어입니다. "어떻게 판단하는지(How)"가 아닌 "무엇이 참인지(What)"를 기술하는 방식으로, 익숙해지면 복잡한 정책도 간결하게 표현할 수 있습니다. Rego Playground(https://play.openpolicyagent.org)에서%EC%97%90%EC%84%9C) 브라우저 환경으로 직접 작성해볼 수 있습니다.
번들(Bundle): 정책을 패키징하는 단위
번들은 Rego 정책 파일과 정적 데이터(data.json 포함)를 하나의 압축 아카이브(.tar.gz)로 묶은 단위입니다. OPA 인스턴스는 HTTP(S) 번들 서버를 주기적으로 폴링하거나 Long Polling으로 구독하여 최신 번들을 가져오고, 파드 재시작 없이 정책을 실시간으로 갱신합니다.
| 유형 | 설명 | 적합한 상황 |
|---|---|---|
| Snapshot Bundle | 전체 정책·데이터 상태 포함. 수신 시 기존 캐시를 완전 교체 | 정책 수가 적거나 전체 갱신이 필요한 경우 |
| Delta Bundle | 변경분(JSON Patch)만 포함 | 대규모 데이터셋에서 네트워크·처리 비용 절감이 필요한 경우 |
번들은 opa build 명령어로 생성합니다.
# policies/ 디렉터리의 모든 Rego 파일과 data.json을 번들로 패키징합니다.
# -b(--bundle) 플래그는 .rego 파일뿐 아니라 data.json 등 정적 데이터도 함께 포함합니다.
opa build -b policies/ -o bundle.tar.gzGitOps: Git을 정책의 단일 진실 소스로
GitOps는 Git 리포지토리를 단일 진실 소스(Single Source of Truth)로 삼아 PR → CI 검증 → 자동 배포 사이클로 인프라와 설정을 관리하는 운영 모델입니다. 클러스터 상태를 Git과 자동으로 맞춰주는 ArgoCD(CD 도구)와 결합하면, Rego 정책을 Git에 저장하는 것만으로 다음이 자연스럽게 가능해집니다.
- 모든 정책 변경에 대한 변경 이력 및 승인자 추적
- PR 단계에서의 정책 문법·로직 자동 검증
- 문제 발생 시
git revert를 통한 즉각적인 롤백
실전 적용
네 가지 예시를 상황에 따라 선택해 활용할 수 있습니다.
| 상황 | 추천 예시 |
|---|---|
| Kubernetes 리소스 생성·수정을 즉시 차단해야 할 때 | 예시 2 (Gatekeeper + ArgoCD) |
| 마이크로서비스·Terraform 등 다양한 시스템에 OPA를 적용할 때 | 예시 1 (GitHub Actions + S3 번들) |
| PR 단계에서 manifest를 사전 검증하고 싶을 때 | 예시 3 (Conftest CI) |
| 번들 폴링 지연(수초~수십초)이 허용되지 않을 때 | 예시 4 (OPAL 실시간 푸시) |
예시 1: GitHub Actions + S3 번들 서버 파이프라인
정책 리포지토리에 PR이 병합되면 GitHub Actions가 번들을 빌드하여 S3에 업로드하고, 클러스터 내 OPA 인스턴스(Standalone Deployment로 실행)가 이를 폴링하여 정책을 갱신하는 가장 보편적인 패턴입니다.
리포지토리 구조 예시:
opa-policies/
├── policies/
│ ├── k8s/
│ │ ├── no_latest_tag.rego
│ │ └── require_resource_limits.rego
│ └── rbac/
│ └── role_binding_rules.rego
├── tests/
│ └── k8s_test.rego
└── .github/workflows/
└── deploy-bundle.ymlGitHub Actions 워크플로:
# .github/workflows/deploy-bundle.yml
name: Build and Deploy OPA Bundle
on:
push:
branches: [main]
paths: ['policies/**']
jobs:
test-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup OPA
uses: open-policy-agent/setup-opa@v2
with:
# 프로덕션 환경에서는 재현성을 위해 'latest' 대신 특정 버전을 고정하는 것을 권장합니다.
# 예: version: "0.68.0"
version: latest
- name: Run policy tests
run: opa test policies/ tests/ -v
- name: Build OPA bundle
run: opa build -b policies/ -o bundle.tar.gz
- name: Upload to S3
run: aws s3 cp bundle.tar.gz s3://my-opa-bundles/latest/bundle.tar.gz
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}| 단계 | 설명 |
|---|---|
opa test |
Rego 정책의 단위 테스트 실행. 실패 시 배포가 차단됩니다. |
opa build -b |
지정 디렉터리의 .rego 파일과 data.json을 번들로 패키징합니다. |
aws s3 cp |
번들을 S3에 업로드하여 번들 서버로 활용합니다. |
Kubernetes 내 OPA 설정(ConfigMap):
OPA는 아래 ConfigMap을 마운트한 Standalone Deployment로 실행됩니다. 프로덕션 환경에서는 서비스 어카운트와 네트워크 정책을 함께 구성하고, 번들 서버 엔드포인트를 반드시 HTTPS로 설정하는 것을 권장합니다.
# opa-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: opa-config
data:
config.yaml: |
services:
bundle-server:
url: https://s3.amazonaws.com/my-opa-bundles
# 프로덕션에서는 allow_insecure_tls를 기본값(false)으로 유지하세요.
bundles:
main:
service: bundle-server
resource: latest/bundle.tar.gz
polling:
min_delay_seconds: 10
max_delay_seconds: 30예시 2: OPA Gatekeeper + ArgoCD (Kubernetes 어드미션 컨트롤)
Kubernetes 리소스 생성·수정을 실시간으로 차단해야 하는 경우에 적합합니다. Gatekeeper는 ConstraintTemplate(Rego 정책 정의)과 Constraint(적용 파라미터)의 2계층 구조를 사용하며, 예시 1과 달리 input.review 구조체를 통해 Kubernetes 요청에 접근합니다.
# gatekeeper/templates/no-latest-tag.yaml
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8snolatestimage
annotations:
argocd.argoproj.io/sync-wave: "1" # Constraint보다 먼저 배포
spec:
crd:
spec:
names:
kind: K8sNoLatestImage
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
# Gatekeeper용 Rego: 패키지명 컨벤션과 input.review 구조가
# kube-mgmt 패턴(예시 1, input.request)과 다름에 주의하세요.
package k8snolatestimage
violation[{"msg": msg}] {
# [_]는 배열의 모든 요소를 순회하는 Rego 와일드카드 문법입니다.
container := input.review.object.spec.containers[_]
endswith(container.image, ":latest")
msg := sprintf("latest 태그 사용 불가: %v", [container.image])
}# gatekeeper/constraints/no-latest-tag-constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sNoLatestImage
metadata:
name: no-latest-image
annotations:
argocd.argoproj.io/sync-wave: "2" # Template 이후 배포
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]# ArgoCD Application — 정책 리포지토리를 감시
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: opa-policies
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/opa-policies
path: gatekeeper/
targetRevision: HEAD
destination:
server: https://kubernetes.default.svc
namespace: gatekeeper-system
syncPolicy:
automated:
prune: true
selfHeal: trueSync Wave란? ArgoCD에서 리소스 배포 순서를 제어하는 기능입니다.
argocd.argoproj.io/sync-wave어노테이션에 낮은 숫자를 가진 리소스가 먼저 배포됩니다. Gatekeeper에서ConstraintTemplate이 반드시Constraint보다 먼저 생성되어야 하므로, 이 순서 보장이 핵심입니다.
예시 3: Conftest를 이용한 CI 단계 사전 검증
번들이 클러스터에 반영되기 전, PR 단계에서 Kubernetes manifest를 정책으로 미리 검증하는 패턴입니다. 정책 위반 시 머지 자체를 차단합니다.
# .github/workflows/pr-check.yml
name: Policy Validation
on:
pull_request:
paths: ['k8s/**']
jobs:
conftest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# setup-opa와 일관되게 공식 액션을 활용하는 방식을 권장합니다.
- name: Setup Conftest
uses: open-policy-agent/setup-conftest@v0
with:
conftest: "0.50.0"
- name: Validate K8s manifests against policies
# conftest는 Kubernetes 네임스페이스 개념을 다루지 않으므로
# --all-namespaces 플래그 없이 디렉터리와 정책 경로만 지정합니다.
run: conftest test k8s/ --policy policies/예시 4: OPAL로 실시간 정책 푸시
번들 폴링의 지연(수초~수십초)이 허용되지 않는 환경에서는 OPAL을 활용할 수 있습니다. OPAL 서버가 Git 변경을 감지하면 웹소켓을 통해 OPAL 클라이언트(OPA와 함께 사이드카 또는 별도 Deployment로 실행)에 즉시 정책을 푸시합니다.
# OPAL 서버 및 클라이언트를 Helm으로 배포합니다.
# opal-server: Git 리포지토리를 감시하고 변경을 감지합니다.
# opal-client: OPA 인스턴스와 같은 네임스페이스에 배포되어 정책을 수신합니다.
helm repo add opal https://permitio.github.io/opal-helm-chart
helm install opal-server opal/opal-server \
--set opal_server.policy_repo_url=https://github.com/org/opa-policies \
--set opal_server.policy_repo_main_branch=main
helm install opal-client opal/opal-client \
--set opal_client.server_url=ws://opal-server:7002/ws장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 버전 관리·감사 추적 | 모든 정책 변경이 Git 커밋으로 기록되어 변경 이력과 승인자 추적이 용이합니다. |
| 무중단 정책 갱신 | 번들 폴링 또는 OPAL 푸시로 OPA 파드 재시작 없이 정책이 실시간 갱신됩니다. |
| 관심사 분리 | 애플리케이션 코드에서 정책 로직을 완전히 분리하여 독립적인 배포·테스트가 가능합니다. |
| 멀티 클러스터 일관성 | 번들 서버 하나로 수십~수백 개의 클러스터에 동일한 정책을 일관되게 배포할 수 있습니다. |
| 유연한 배포 백엔드 | HTTP 서버(Nginx), 오브젝트 스토리지(S3/GCS), OCI 레지스트리 등 다양한 번들 서버 옵션을 지원합니다. |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| 번들 폴링 지연 | 기본 폴링 방식에서는 번들 업데이트 후 OPA 반영까지 수초~수십초 지연이 발생합니다. | OPAL 또는 Delta 번들 + Long Polling을 도입할 수 있습니다. |
| 번들 서버 운영 오버헤드 | 번들 서버 자체의 가용성 관리, TLS 인증, 접근 제어가 필요합니다. | S3나 OCI 레지스트리를 번들 서버로 활용하면 관리 부담이 줄어듭니다. |
| Rego 학습 곡선 | Datalog에서 파생된 Rego 문법은 명령형 언어에 익숙한 개발자에게 직관적이지 않을 수 있습니다. | Rego Playground와 VS Code OPA Extension을 활용하면 진입 장벽이 낮아집니다. |
| ArgoCD Sync Wave 누락 | Gatekeeper 사용 시 ConstraintTemplate이 Constraint보다 먼저 배포되어야 합니다. | argocd.argoproj.io/sync-wave 어노테이션으로 배포 순서를 명시적으로 지정할 수 있습니다. |
OCI 레지스트리 번들 서버란?
ORAS(OCI Registry As Storage)도구를 이용해 OPA 번들을 컨테이너 이미지처럼 Harbor, GHCR, ECR 등 OCI 레지스트리에 Push하고 OPA가 직접 Pull하는 패턴입니다. 기존 컨테이너 인프라를 재활용할 수 있어 추가 서버 운영 없이 번들을 배포할 수 있습니다.
실무에서 가장 흔한 실수
1. 정책 테스트 없이 배포
opa test를 CI 파이프라인에 포함하지 않으면 문법 오류나 논리적 결함이 있는 정책이 프로덕션에 반영될 수 있습니다. 빌드 전 단계에 테스트를 포함하여 정책 품질을 자동으로 보장하는 것이 중요합니다.
2. Gatekeeper와 번들 서버 패턴의 혼용
Kubernetes 어드미션 컨트롤만 필요하면 Gatekeeper가 단순하고, 마이크로서비스나 Terraform 등 여러 시스템에 OPA를 적용해야 한다면 번들 서버 패턴이 적합합니다. 두 패턴의 목적과 범위를 먼저 명확히 정의하는 것이 중요합니다.
3. 번들 서버 TLS 미설정
번들 서버와 OPA 인스턴스 간의 통신에 TLS를 적용하지 않으면 정책 파일이 평문으로 전송됩니다. 프로덕션 환경에서는 반드시 HTTPS 엔드포인트를 사용하고, OPA 설정에서 allow_insecure_tls를 기본값(false)으로 유지하는 것을 권장합니다.
4. CI에서 OPA 버전을 latest로 고정
setup-opa@v2에 version: latest를 사용하면 OPA 버전이 바뀔 때마다 CI 결과가 달라질 수 있어 재현성이 떨어집니다. 프로덕션 파이프라인에서는 version: "0.68.0"과 같이 특정 버전을 명시하는 것을 권장합니다.
마치며
OPA 번들 서버와 GitOps를 결합하면 정책을 코드처럼 리뷰·검증·배포하는 체계를 갖출 수 있습니다. CNCF가 공개한 OPA 도입 사례에 따르면, 수십 개의 클러스터에 정책을 수동으로 배포하던 팀이 번들 서버 + GitOps 패턴으로 전환한 뒤 정책 배포 시간과 설정 불일치 인시던트를 크게 줄였다고 보고합니다. 처음부터 복잡한 OPAL 구성을 도입할 필요는 없으며, 작은 단위부터 시작해 점진적으로 확장해나가는 방식이 훨씬 현실적입니다.
지금 바로 시작해볼 수 있는 3단계:
- Rego 정책 작성 및 테스트: Rego Playground에서 간단한 정책을 작성해보고, 로컬에서
opa test ./policies/ ./tests/ -v명령어로 단위 테스트를 실행해볼 수 있습니다. - GitHub Actions 파이프라인 구성: 위 예시의
deploy-bundle.yml을 참고하여opa test → opa build → s3 cp세 단계로 이루어진 기본 파이프라인을 구성해볼 수 있습니다.setup-opaGitHub Action을 활용하면 OPA 설치 과정이 간소화됩니다. - Kubernetes에 OPA 배포 및 번들 연결: OPA를 Standalone Deployment로 배포하고 위 ConfigMap 예시를 참고하여 S3 번들 서버 폴링 설정을 적용해볼 수 있습니다. 막히는 지점이 있다면 OPA 공식 Kubernetes 배포 가이드에서 단계별 Quickstart를 참고하면 됩니다.
다음 글: OPA Gatekeeper의
ConstraintTemplate을 실전 보안 정책(이미지 서명 검증, 네임스페이스 레이블 강제, 특권 컨테이너 차단)으로 구성하고, Conftest와 통합하는 고급 패턴을 다룹니다.
참고 자료
- OPA 공식 문서 - Bundles | openpolicyagent.org
- OPA 공식 문서 - Deploying OPA on Kubernetes | openpolicyagent.org
- OPA 공식 문서 - Using OPA in CI/CD Pipelines | openpolicyagent.org
- Rego Playground | play.openpolicyagent.org
- CNCF 블로그 - Open Policy Agent: Best Practices for a Secure Deployment (2025) | cncf.io
- OPAL 공식 문서 - Helm Chart for Kubernetes | docs.opal.ac
- ORAS - Bundle, test and deploy Gatekeeper policies as OCI image | oras.land
- GitOps Security: Enforcing Policy as Code in Flux and ArgoCD | policyascode.dev
- Conftest 공식 문서 | conftest.dev
- GitHub - open-policy-agent/awesome-opa | github.com