12 KiB
STYLE.md — 인프라 문서 공용 규약 (Single Source of Truth)
이 문서는 docs/standards/infra/** 와 docs/examples/infra/** 에 등장하는 모든 라벨, 네이밍, 포트, 이미지, 리소스 관례의 정규(normative) 정의다. 다른 모든 문서의 YAML 조각은 예시이며, 여기 규약과 충돌할 경우 이 문서가 우선한다. AI 에이전트가 매니페스트를 생성할 때 관례가 문서 간 표류하는 것을 방지하려는 목적이다.
1. 라벨 (Labels)
1.1 Kubernetes well-known labels (app.kubernetes.io/*)
공식 well-known set. 이 namespace 아래에는 아래 6개 외에 임의 키를 추가하지 않는다.
| 키 | 의미 | 예시 |
|---|---|---|
app.kubernetes.io/name |
애플리케이션 이름 | auth-server |
app.kubernetes.io/instance |
인스턴스 (환경/리전 포함 가능) | auth-server-prod, auth-server |
app.kubernetes.io/version |
semver 또는 release tag | 1.24.0 |
app.kubernetes.io/component |
역할 | api, worker, migration, database |
app.kubernetes.io/part-of |
상위 시스템 | auth-platform |
app.kubernetes.io/managed-by |
배포 도구 | kustomize, argocd, helm |
1.2 조직 커스텀 라벨 (example.com/*)
example.com/ namespace 는 문서 전용 플레이스홀더다. 실제 조직은 자사 도메인(e.g., acme.corp/)으로 치환한다.
| 키 | 허용 값 |
|---|---|
example.com/environment |
lab | dev | staging | prod |
example.com/owner-team |
팀 slug (e.g., auth-platform, sre) |
example.com/cost-center |
회계 코스트 센터 ID |
example.com/data-classification |
public | internal | confidential | restricted |
example.com/tier |
0 (critical) | 1 | 2 | 3 (best-effort) |
1.3 규칙
- 모든 워크로드(Deployment / StatefulSet / DaemonSet / Job / CronJob)에는 위 6개
app.kubernetes.io/*라벨 +example.com/environment+example.com/owner-team이 필수다. app.kubernetes.io/environment라벨은 사용 금지. 공식 well-known set 에 없으며, 환경 라벨은 조직 namespace 아래에 둔다.- Selector (
spec.selector.matchLabels)에는app.kubernetes.io/name과app.kubernetes.io/instance만 사용한다. 이유: selector 는 immutable 이고,version/component이외 라벨은 릴리즈마다 바뀌기 때문에 selector 에 포함하면 rollout 이 막힌다. - 라벨 값은 DNS-1123 subdomain 또는 label 규칙을 따른다: 소문자 알파벳, 숫자,
-,., 최대 63자. 공백/대문자/언더스코어 금지. - 라벨은 metadata 의 최상위
labels:와 Pod template 의spec.template.metadata.labels:에 동일하게 복제한다(선택자 일치 보장).
metadata:
name: auth-server
labels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-prod
app.kubernetes.io/version: "1.24.0"
app.kubernetes.io/component: api
app.kubernetes.io/part-of: auth-platform
app.kubernetes.io/managed-by: kustomize
example.com/environment: prod
example.com/owner-team: auth-platform
spec:
selector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-prod
2. 네이밍 (Naming)
2.1 Namespace
- 기본 스키마:
<env>-<domain>-<service>— 예:prod-auth-keycloak,staging-billing-api. - 단일 서비스 네임스페이스에 여러 컴포넌트가 있으면 서비스 이름까지만 사용한다:
prod-auth네임스페이스 안에 Keycloak, PostgreSQL, Flyway Job 이 공존. default네임스페이스는 금지.kube-*는 Kubernetes 예약.- 클러스터 공통 플랫폼 컴포넌트는 별도 접두어:
platform-vault,platform-cert-manager,platform-monitoring.
2.2 리소스 이름
케밥-케이스, 소문자. Service / ServiceAccount / Secret / ConfigMap 이름은 관련 워크로드 이름을 접두어로 공유한다.
| 리소스 | 규약 | 예시 |
|---|---|---|
| Deployment / StatefulSet | <app> |
auth-server |
| Service (ClusterIP) | <app> (Deployment와 동일) |
auth-server |
| Headless Service (StatefulSet peer 통신용) | <app>-headless (옆에 일반 ClusterIP <app> 병행) |
keycloak-headless, keycloak |
| ServiceAccount | <app>-sa |
auth-server-sa |
| Secret (앱 소유) | <app>-<purpose> |
auth-server-db, auth-server-oidc |
| ConfigMap | <app>-<purpose> |
auth-server-config, auth-server-runtime |
| PDB | <app>-pdb |
auth-server-pdb |
| HPA | <app>-hpa |
auth-server-hpa |
| NetworkPolicy | <app>-<direction>-<peer> |
auth-server-egress-db, auth-server-ingress-traefik |
| Job (일회성) | <app>-<action>-<timestamp-or-version> |
auth-server-migrate-1-24-0 |
| CronJob | <app>-<action> |
auth-server-session-cleanup |
3. 포트 (Ports)
3.1 이름 규약
모든 containerPort / servicePort 에는 name 필드가 필수다. 아래 이름은 예약어로 취급한다.
| name | 용도 | 관행 포트 |
|---|---|---|
http |
HTTP 앱 트래픽 | 8080 |
https |
HTTPS 직접 종료 | 8443 |
grpc |
gRPC | 9090 또는 앱별 지정 |
metrics |
Prometheus scrape | 9090 (kube-prometheus 관행). 컴포넌트가 이미 9090 을 쓰면 9100 |
health |
별도 헬스/관리 포트 | Keycloak Quarkus 관리 포트 9000 등 |
admin |
관리 UI | 컴포넌트별 |
cluster |
내부 peer / 레플리케이션 | Vault 8201, Postgres 5432, etcd 2380 |
3.2 규칙
targetPort는 number 대신 이름 참조를 권장:targetPort: http. 이유: 컨테이너가 바인드 포트를 바꿔도 Service 쪽 조정이 필요 없다.metrics포트는 외부 노출 금지. ClusterIP 만 쓰며 NetworkPolicy 로 Prometheus 네임스페이스에서만 ingress 허용.health,admin포트는 Ingress 에 붙이지 않는다. NetworkPolicy 로 접근 대역을 제한한다.
Container ports 스탠자 (Deployment/Pod spec 내부):
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
- name: health
containerPort: 9000
protocol: TCP
Service 정의 (targetPort 는 이름 참조):
apiVersion: v1
kind: Service
metadata:
name: auth-server
spec:
selector:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-prod
ports:
- name: http
port: 80
targetPort: http
4. 이미지 (Images)
- prod 환경:
<registry>/<path>@sha256:<digest>형태 digest pin 필수. 뮤터블 태그(:1,:latest,:main) 금지. - staging: digest 권장, 최소 semver tag(
:1.24.0) 허용. 절대:latest금지. - lab / dev: semver tag 허용,
:latest지양 (로컬 / 노드 cache invalidation 이슈).lab은 폐기 가능한 단일 클러스터 실험 환경에만 사용한다. imagePullPolicy:- digest 사용 시
IfNotPresent(이미지 콘텐츠는 immutable) - 뮤터블 태그 사용 시
Always
- digest 사용 시
- 레지스트리: 조직 내부 미러가 우선한다. 예:
registry.example.com/<ns>/<app>. Docker Hub 직접 pull 금지 (rate limit + 공급망 리스크). - SHA256 digest 로 pin 한 이미지는 CI 파이프라인에서 cosign 서명 검증(선택)과
imagePullSecretsdigest 검증에 연결한다.
containers:
- name: app
image: registry.example.com/auth-platform/auth-server@sha256:9f0b2c4d8e7a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1d3e5f7a9b1c
imagePullPolicy: IfNotPresent
5. 리소스 (Resources)
5.1 필수 필드
- 모든 컨테이너는
resources.requests.cpu,resources.requests.memory,resources.limits.memory를 반드시 설정한다. resources.limits.cpu는 선택이다. 레이턴시 민감 워크로드에만 설정한다. 이유: CFS throttling 으로 인한 p99 tail-latency 악화를 회피하려는 Tim Hockin / Google SRE 가이던스.
5.2 QoS 클래스
Guaranteed— latency-critical (Keycloak, Vault, Postgres 등):requests == limits, CPU limit 도 설정.Burstable— 일반 stateless 앱:requests < limits또는 CPU limit 생략.BestEffort— 금지. requests/limits 를 생략한 워크로드는 PR 에서 블록.
5.3 기본 가이드라인 (1000-서비스 스케일 기준 출발점)
| 워크로드 | CPU req | Memory req / limit |
|---|---|---|
| 일반 stateless API | 100m | 128–256Mi |
| 무거운 JVM (Keycloak, Elasticsearch) | 500m–1 | 1–2Gi (req == limit) |
| 배경 worker | 250m | 512Mi–1Gi |
| 전환성(transient) Job (Flyway) | 100m | 128Mi |
실제 값은 부하 테스트 / VPA 권고 결과로 조정한다.
6. 보안 기준선 (Security baselines — 모든 Pod)
아래 블록은 모든 Pod 의 최소 baseline 이다. 이걸 내린 설정은 security-hardening.md 의 예외 절차를 거쳐야 한다.
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001 # 앱별 고정 UID, 루트(0) 금지
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry.example.com/auth-platform/auth-server@sha256:...
imagePullPolicy: IfNotPresent
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
memory: 256Mi
7. PDB, Job, Deployment 기타
- PDB: 1.27+ 에서
spec.unhealthyPodEvictionPolicy: AlwaysAllow필수. 기본값IfHealthyBudget은 노드 drain 중 복구 불가능한 Pod 가 evict 되지 못해 업그레이드가 멈추는 원인이 된다. - Job / CronJob:
spec.ttlSecondsAfterFinished: 86400(24h) 기본. 민감 로그가 남는 경우3600(1h).spec.backoffLimit명시 (기본 6). 크리티컬 마이그레이션(Flyway)은0또는1로 줄여 재시도 폭주 방지.- CronJob 은
spec.concurrencyPolicy: Forbid를 기본값으로 둔다(중복 실행 금지).
- Deployment:
spec.revisionHistoryLimit: 5(기본 10 은 너무 많음 — etcd 부하).spec.progressDeadlineSeconds: 600명시.spec.strategy.rollingUpdate.maxUnavailable: 0+maxSurge: 25%가 안전한 기본.
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: auth-server-pdb
spec:
minAvailable: 2
unhealthyPodEvictionPolicy: AlwaysAllow
selector:
matchLabels:
app.kubernetes.io/name: auth-server
app.kubernetes.io/instance: auth-server-prod
8. 문서 내 예시 규약
- 모든 YAML 예시는
```yaml펜스로 감싼다. 다른 언어 펜스 금지. - 한 파일에 여러 리소스가 등장하면
---separator 를 명시적으로 추가한다. - 예시는 원칙적으로
kubectl apply -f로 바로 적용 가능한 완전체여야 한다. 지면상 생략할 때는 주석으로 표기:# ... (full spec omitted for brevity). - 나쁜 예시(안티패턴)는 반드시
## 나쁜 예시,## ❌, 또는## bad example헤더 아래에 둔다. CI 검증 스크립트가 이 헤더 규약으로 나쁜 예시 블록을 제외한다. 헤더 없이 안티패턴을 노출하면 검증기가 정당한 예시로 오인해 lint 규칙 위반을 일으킨다. - 네임스페이스, 이미지 레지스트리, 도메인 이름은
example.com,registry.example.com플레이스홀더를 사용한다. 실제 조직 도메인은 overlays 에서만 등장한다.
검증 (Validation)
이 문서의 규약은 CI 에서 기계 검증된다.
- 실행:
scripts/ci/validate-docs.sh - Lint 설정 위치:
.kube-linter.yaml(repo root) - 목표 스코어:
- syntax 에러: 0
- schema 에러: 0
- lint warning: ≤ 5
syntax 또는 schema 에러가 있으면 PR 은 머지 불가. lint warning 이 임계치를 넘으면 리뷰어가 수정 또는 예외 주석을 요구한다.