Files
project-infra/docs/standards/infra/STYLE.md
T

12 KiB
Raw Blame History

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 규칙

  1. 모든 워크로드(Deployment / StatefulSet / DaemonSet / Job / CronJob)에는 위 6개 app.kubernetes.io/* 라벨 + example.com/environment + example.com/owner-team필수다.
  2. app.kubernetes.io/environment 라벨은 사용 금지. 공식 well-known set 에 없으며, 환경 라벨은 조직 namespace 아래에 둔다.
  3. Selector (spec.selector.matchLabels)에는 app.kubernetes.io/nameapp.kubernetes.io/instance 사용한다. 이유: selector 는 immutable 이고, version / component 이외 라벨은 릴리즈마다 바뀌기 때문에 selector 에 포함하면 rollout 이 막힌다.
  4. 라벨 값은 DNS-1123 subdomain 또는 label 규칙을 따른다: 소문자 알파벳, 숫자, -, ., 최대 63자. 공백/대문자/언더스코어 금지.
  5. 라벨은 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

  1. 기본 스키마: <env>-<domain>-<service> — 예: prod-auth-keycloak, staging-billing-api.
  2. 단일 서비스 네임스페이스에 여러 컴포넌트가 있으면 서비스 이름까지만 사용한다: prod-auth 네임스페이스 안에 Keycloak, PostgreSQL, Flyway Job 이 공존.
  3. default 네임스페이스는 금지. kube-* 는 Kubernetes 예약.
  4. 클러스터 공통 플랫폼 컴포넌트는 별도 접두어: 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 규칙

  1. targetPort 는 number 대신 이름 참조를 권장: targetPort: http. 이유: 컨테이너가 바인드 포트를 바꿔도 Service 쪽 조정이 필요 없다.
  2. metrics 포트는 외부 노출 금지. ClusterIP 만 쓰며 NetworkPolicy 로 Prometheus 네임스페이스에서만 ingress 허용.
  3. 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)

  1. prod 환경: <registry>/<path>@sha256:<digest> 형태 digest pin 필수. 뮤터블 태그(:1, :latest, :main) 금지.
  2. staging: digest 권장, 최소 semver tag(:1.24.0) 허용. 절대 :latest 금지.
  3. lab / dev: semver tag 허용, :latest 지양 (로컬 / 노드 cache invalidation 이슈). lab 은 폐기 가능한 단일 클러스터 실험 환경에만 사용한다.
  4. imagePullPolicy:
    • digest 사용 시 IfNotPresent (이미지 콘텐츠는 immutable)
    • 뮤터블 태그 사용 시 Always
  5. 레지스트리: 조직 내부 미러가 우선한다. 예: registry.example.com/<ns>/<app>. Docker Hub 직접 pull 금지 (rate limit + 공급망 리스크).
  6. SHA256 digest 로 pin 한 이미지는 CI 파이프라인에서 cosign 서명 검증(선택)과 imagePullSecrets digest 검증에 연결한다.
containers:
- name: app
  image: registry.example.com/auth-platform/auth-server@sha256:9f0b2c4d8e7a1b3c5d7e9f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1d3e5f7a9b1c
  imagePullPolicy: IfNotPresent

5. 리소스 (Resources)

5.1 필수 필드

  1. 모든 컨테이너는 resources.requests.cpu, resources.requests.memory, resources.limits.memory반드시 설정한다.
  2. resources.limits.cpu선택이다. 레이턴시 민감 워크로드에만 설정한다. 이유: CFS throttling 으로 인한 p99 tail-latency 악화를 회피하려는 Tim Hockin / Google SRE 가이던스.

5.2 QoS 클래스

  1. Guaranteed — latency-critical (Keycloak, Vault, Postgres 등): requests == limits, CPU limit 도 설정.
  2. Burstable — 일반 stateless 앱: requests < limits 또는 CPU limit 생략.
  3. BestEffort금지. requests/limits 를 생략한 워크로드는 PR 에서 블록.

5.3 기본 가이드라인 (1000-서비스 스케일 기준 출발점)

워크로드 CPU req Memory req / limit
일반 stateless API 100m 128256Mi
무거운 JVM (Keycloak, Elasticsearch) 500m1 12Gi (req == limit)
배경 worker 250m 512Mi1Gi
전환성(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 기타

  1. PDB: 1.27+ 에서 spec.unhealthyPodEvictionPolicy: AlwaysAllow 필수. 기본값 IfHealthyBudget 은 노드 drain 중 복구 불가능한 Pod 가 evict 되지 못해 업그레이드가 멈추는 원인이 된다.
  2. Job / CronJob:
    • spec.ttlSecondsAfterFinished: 86400 (24h) 기본. 민감 로그가 남는 경우 3600 (1h).
    • spec.backoffLimit 명시 (기본 6). 크리티컬 마이그레이션(Flyway)은 0 또는 1 로 줄여 재시도 폭주 방지.
    • CronJob 은 spec.concurrencyPolicy: Forbid 를 기본값으로 둔다(중복 실행 금지).
  3. 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. 문서 내 예시 규약

  1. 모든 YAML 예시는 ```yaml 펜스로 감싼다. 다른 언어 펜스 금지.
  2. 한 파일에 여러 리소스가 등장하면 --- separator 를 명시적으로 추가한다.
  3. 예시는 원칙적으로 kubectl apply -f 로 바로 적용 가능한 완전체여야 한다. 지면상 생략할 때는 주석으로 표기: # ... (full spec omitted for brevity).
  4. 나쁜 예시(안티패턴)는 반드시 ## 나쁜 예시, ## ❌, 또는 ## bad example 헤더 아래에 둔다. CI 검증 스크립트가 이 헤더 규약으로 나쁜 예시 블록을 제외한다. 헤더 없이 안티패턴을 노출하면 검증기가 정당한 예시로 오인해 lint 규칙 위반을 일으킨다.
  5. 네임스페이스, 이미지 레지스트리, 도메인 이름은 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 이 임계치를 넘으면 리뷰어가 수정 또는 예외 주석을 요구한다.