Kubernetes 개발·운영 실전 2편 — Namespace & RBAC 설계
Kubernetes 멀티 팀·멀티 환경 운영을 위한 Namespace 분리 전략과 RBAC 설계 원칙. 최소 권한 원칙을 지키면서 개발자 생산성을 높이는 실전 구성.
Namespace와 RBAC를 먼저 설계해야 하는 이유
클러스터를 구성하면 기본적으로 default 네임스페이스만 존재합니다. 초기에는 여기에 다 올리는 경우가 많지만, 팀이 커지고 서비스가 늘어나면 이 구조는 반드시 문제를 일으킵니다.
- A팀이 잘못 실행한
kubectl delete pods --all이 B팀 서비스를 삭제 - 개발자가 프로덕션 Secret을 실수로 수정
- 어떤 팀의 워크로드가 클러스터 리소스를 독점
Namespace와 RBAC는 이 문제를 구조적으로 막는 첫 번째 방어선입니다.
1. Namespace 분리 전략
환경 기반 분리 (소규모 팀)
cluster
├── development
├── staging
└── production
apiVersion: v1
kind: Namespace
metadata:
name: development
labels:
environment: development
team: platform
---
apiVersion: v1
kind: Namespace
metadata:
name: staging
labels:
environment: staging
---
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
environment: production
팀 + 환경 기반 분리 (중대형 팀)
cluster
├── team-a-dev
├── team-a-prod
├── team-b-dev
├── team-b-prod
└── platform # 공통 인프라 (monitoring, ingress 등)
팀별로 네임스페이스를 분리하면 팀 간 리소스 격리와 독립 배포가 가능합니다.
서비스 기반 분리 (마이크로서비스)
cluster
├── auth-service
├── payment-service
├── notification-service
└── platform
서비스마다 별도 네임스페이스를 두면 NetworkPolicy로 서비스 간 통신을 명확히 제어할 수 있습니다.
2. ResourceQuota로 리소스 상한 설정
네임스페이스별 리소스 상한을 설정하지 않으면 특정 팀의 워크로드가 클러스터 전체를 잠식할 수 있습니다.
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-a-quota
namespace: team-a-dev
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "20"
services: "10"
persistentvolumeclaims: "5"
secrets: "20"
configmaps: "20"
LimitRange로 기본값 설정
Pod에 resources를 명시하지 않으면 무제한으로 실행됩니다. LimitRange로 기본값을 강제합니다.
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: team-a-dev
spec:
limits:
- type: Container
default:
cpu: "500m"
memory: 256Mi
defaultRequest:
cpu: "100m"
memory: 128Mi
max:
cpu: "2"
memory: 2Gi
3. RBAC 설계 원칙
RBAC는 누가(Subject) 무엇을(Verb) 어떤 리소스에(Resource) 할 수 있는지 정의합니다.
역할 계층 설계
| 역할 | 대상 | 권한 |
|---|---|---|
| cluster-admin | 플랫폼 팀 | 클러스터 전체 |
| namespace-admin | 팀 리드 | 특정 네임스페이스 전체 |
| developer | 개발자 | 읽기 + Pod 조작 + 로그 조회 |
| viewer | 외부 감사, 인턴 | 읽기 전용 |
developer Role 정의
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
namespace: team-a-dev
rules:
# 조회 권한
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "endpoints", "events"]
verbs: ["get", "list", "watch"]
# Pod 조작 (exec, 로그, 포트포워딩)
- apiGroups: [""]
resources: ["pods/exec", "pods/log", "pods/portforward"]
verbs: ["get", "create"]
# Deployment 조회 + 재시작
- apiGroups: ["apps"]
resources: ["deployments", "replicasets", "statefulsets"]
verbs: ["get", "list", "watch", "patch"]
# Secret은 목록만 — 내용 조회 금지
- apiGroups: [""]
resources: ["secrets"]
verbs: ["list"]
viewer Role 정의
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: viewer
namespace: team-a-dev
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["*"]
verbs: ["get", "list", "watch"]
RoleBinding으로 사용자에게 역할 부여
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: team-a-dev
subjects:
- kind: User
name: alice@company.com
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: team-a-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
4. ServiceAccount 최소 권한 설정
Pod가 Kubernetes API를 호출할 필요가 없다면 기본 ServiceAccount 토큰 마운트를 비활성화합니다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app
namespace: team-a-dev
automountServiceAccountToken: false # API 접근 불필요 시 비활성화
API 접근이 필요한 경우 전용 ServiceAccount를 만들고 최소 권한만 부여합니다.
# ArgoCD처럼 클러스터 리소스를 관리하는 컨트롤러용
apiVersion: v1
kind: ServiceAccount
metadata:
name: resource-watcher
namespace: platform
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: resource-watcher
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: resource-watcher
subjects:
- kind: ServiceAccount
name: resource-watcher
namespace: platform
roleRef:
kind: ClusterRole
name: resource-watcher
apiGroup: rbac.authorization.k8s.io
5. RBAC 검증
# 특정 사용자의 권한 확인
kubectl auth can-i get pods --namespace team-a-dev --as alice@company.com
kubectl auth can-i delete secrets --namespace production --as alice@company.com
# 현재 내 권한 전체 확인
kubectl auth can-i --list --namespace team-a-dev
# ServiceAccount 권한 확인
kubectl auth can-i get deployments \
--as system:serviceaccount:platform:resource-watcher
6. 실전 네임스페이스 초기화 스크립트
#!/bin/bash
# init-namespace.sh <team> <env>
TEAM=$1
ENV=$2
NS="${TEAM}-${ENV}"
kubectl create namespace $NS
kubectl label namespace $NS team=$TEAM environment=$ENV
kubectl apply -f - <<EOF
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota
namespace: $NS
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
pods: "30"
EOF
kubectl apply -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developer-binding
namespace: $NS
subjects:
- kind: Group
name: ${TEAM}-developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: developer
apiGroup: rbac.authorization.k8s.io
EOF
echo "Namespace $NS initialized."
정리
| 설계 요소 | 핵심 원칙 |
|---|---|
| Namespace | 팀 또는 환경 기준으로 분리, 기본값 정의 |
| ResourceQuota | 네임스페이스별 CPU/메모리/오브젝트 상한 |
| LimitRange | 리소스 미명시 Pod에 기본값 강제 |
| RBAC | 최소 권한, Secret 읽기 금지 기본 |
| ServiceAccount | API 접근 불필요 시 토큰 마운트 비활성화 |
다음 편에서는 워크로드 패턴 선택을 다룹니다. Deployment, StatefulSet, DaemonSet, Job/CronJob을 언제 쓰는지 기준을 정리합니다.