- Published on
Platform Engineering 완전 가이드 2025: Internal Developer Platform, Backstage, Golden Path
- Authors

- Name
- Youngju Kim
- @fjvbn20031
목차
1. Platform Engineering이란?
1.1 DevOps의 진화와 Platform Engineering
DevOps는 개발과 운영의 벽을 허물었지만, 새로운 문제를 만들었습니다. "You build it, you run it" 원칙이 확산되면서 개발자들의 **인지 부하(Cognitive Load)**가 급격히 증가했습니다.
개발자의 인지 부하 변화:
2010년대 초반: 현재:
┌───────────────┐ ┌───────────────┐
│ 비즈니스 로직 │ │ 비즈니스 로직 │
│ │ ├───────────────┤
│ │ │ CI/CD 파이프라인│
│ │ ├───────────────┤
│ │ │ 인프라 관리 │
│ │ ├───────────────┤
│ │ │ 모니터링/관찰 │
│ │ ├───────────────┤
│ │ │ 보안/컴플라이언스│
│ │ ├───────────────┤
│ │ │ 쿠버네티스 │
└───────────────┘ └───────────────┘
Gartner는 2026년까지 대규모 소프트웨어 엔지니어링 조직의 **80%**가 Platform Engineering 팀을 설립할 것으로 예측했습니다.
1.2 Platform Engineering의 정의
Platform Engineering은 셀프서비스 기능을 갖춘 **Internal Developer Platform(IDP)**을 설계하고 구축하는 분야입니다. 개발자가 인프라의 복잡성을 직접 다루지 않고도, 필요한 리소스를 프로비저닝하고 애플리케이션을 배포할 수 있게 합니다.
핵심 목표:
- 개발자 인지 부하 감소: 인프라 복잡성 추상화
- 셀프서비스: 티켓 없이 개발자가 직접 리소스 프로비저닝
- 표준화: Golden Path를 통한 모범 사례 제공
- 가드레일: 보안과 컴플라이언스를 자동으로 보장
- 개발자 경험(DX) 향상: 개발자 생산성과 만족도 증가
1.3 Platform Team vs DevOps Team
# Team Topologies 기반 비교
devops_team:
역할: "개발과 운영의 다리 역할"
접근법: "각 팀에 DevOps 엔지니어 파견"
문제점:
- "DevOps 엔지니어가 병목"
- "팀마다 다른 도구와 프로세스"
- "지식이 개인에 편중"
- "반복적인 인프라 작업"
platform_team:
역할: "내부 플랫폼 제품 개발 및 운영"
접근법: "셀프서비스 플랫폼 제공"
장점:
- "개발자 자율성 증대"
- "표준화된 도구와 프로세스"
- "지식이 플랫폼에 내재화"
- "자동화된 가드레일"
team_topologies_mapping:
stream_aligned_team: "비즈니스 기능 개발 팀 (개발자)"
platform_team: "IDP를 구축하고 운영하는 팀"
enabling_team: "새로운 기술 도입을 돕는 팀"
complicated_subsystem_team: "복잡한 하위 시스템 전문가 팀"
2. Internal Developer Platform(IDP) 아키텍처
2.1 IDP의 5계층 구조
┌─────────────────────────────────────────────────────┐
│ Developer Portal Layer │
│ (Backstage, Port, Humanitec Score UI) │
├─────────────────────────────────────────────────────┤
│ Integration & Delivery Layer │
│ (CI/CD: GitHub Actions, ArgoCD, Tekton) │
├─────────────────────────────────────────────────────┤
│ Security & Compliance Layer │
│ (OPA, Kyverno, Vault, Policy-as-Code) │
├─────────────────────────────────────────────────────┤
│ Resource Management Layer │
│ (Terraform, Crossplane, Pulumi, Helm) │
├─────────────────────────────────────────────────────┤
│ Infrastructure Layer │
│ (AWS, GCP, Azure, Kubernetes) │
└─────────────────────────────────────────────────────┘
2.2 IDP 핵심 구성요소
# IDP 핵심 구성요소
service_catalog:
설명: "모든 서비스, API, 인프라의 중앙 카탈로그"
기능:
- "서비스 소유자 및 의존성 추적"
- "API 문서 자동 생성"
- "서비스 성숙도 스코어카드"
도구: "Backstage Software Catalog, Port"
software_templates:
설명: "새 프로젝트를 표준화된 방식으로 생성"
기능:
- "마이크로서비스 스캐폴딩"
- "CI/CD 파이프라인 자동 설정"
- "모니터링/로깅 기본 포함"
도구: "Backstage Software Templates, Cookiecutter"
self_service_infrastructure:
설명: "개발자가 직접 인프라 프로비저닝"
기능:
- "데이터베이스 생성"
- "캐시 클러스터 프로비저닝"
- "메시지 큐 설정"
도구: "Crossplane, Terraform modules, Pulumi"
documentation:
설명: "코드 저장소와 연동된 기술 문서"
기능:
- "마크다운 기반 문서"
- "API 문서 자동화"
- "아키텍처 다이어그램"
도구: "Backstage TechDocs, ReadTheDocs"
developer_portal:
설명: "모든 것을 하나로 묶는 통합 UI"
기능:
- "서비스 카탈로그 탐색"
- "템플릿 실행"
- "문서 검색"
- "비용 확인"
도구: "Backstage, Port, Cortex"
3. Backstage 심층 분석
3.1 Backstage란?
Backstage는 Spotify가 만들고 CNCF에 기증한 오픈소스 개발자 포털 프레임워크입니다. 2020년에 오픈소스화되었으며, 현재 CNCF Incubating 프로젝트입니다.
Backstage 핵심 기능:
┌─────────────────────────────────────────────────────┐
│ Backstage │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Software │ │ Software │ │ TechDocs │ │
│ │ Catalog │ │ Templates │ │ │ │
│ │ │ │ │ │ 코드 기반 │ │
│ │ 서비스 목록 │ │ 프로젝트 생성│ │ 기술 문서 │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Kubernetes │ │ Search │ │ Plugins │ │
│ │ Plugin │ │ │ │ Ecosystem │ │
│ │ │ │ 전체 검색 │ │ │ │
│ │ K8s 통합 │ │ │ │ 100+ 플러그인│ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────┘
3.2 Software Catalog
Software Catalog는 Backstage의 핵심으로, 조직의 모든 소프트웨어 자산을 추적합니다.
# catalog-info.yaml - 서비스 등록 파일
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: user-service
description: "사용자 관리 마이크로서비스"
annotations:
github.com/project-slug: "myorg/user-service"
backstage.io/techdocs-ref: "dir:."
pagerduty.com/service-id: "PXXXXXX"
grafana/dashboard-selector: "user-service"
sonarqube.org/project-key: "myorg_user-service"
tags:
- java
- spring-boot
- user-management
links:
- url: "https://grafana.internal/d/user-service"
title: "Grafana Dashboard"
icon: dashboard
- url: "https://user-service.internal/swagger-ui"
title: "API Docs"
icon: docs
spec:
type: service
lifecycle: production
owner: team-backend
system: user-platform
providesApis:
- user-api
consumesApis:
- auth-api
- notification-api
dependsOn:
- resource:default/user-database
- resource:default/redis-cache
---
apiVersion: backstage.io/v1alpha1
kind: API
metadata:
name: user-api
description: "사용자 관리 REST API"
spec:
type: openapi
lifecycle: production
owner: team-backend
system: user-platform
definition:
$text: ./api/openapi.yaml
---
apiVersion: backstage.io/v1alpha1
kind: Resource
metadata:
name: user-database
description: "사용자 서비스 PostgreSQL 데이터베이스"
spec:
type: database
owner: team-backend
system: user-platform
3.3 Software Templates
Software Templates을 사용하면 새 프로젝트를 표준화된 방식으로 빠르게 생성할 수 있습니다.
# template.yaml - 마이크로서비스 생성 템플릿
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: spring-boot-microservice
title: "Spring Boot Microservice"
description: "표준 Spring Boot 마이크로서비스 프로젝트 생성"
tags:
- java
- spring-boot
- recommended
spec:
owner: team-platform
type: service
parameters:
- title: "서비스 기본 정보"
required:
- serviceName
- owner
- description
properties:
serviceName:
title: "서비스 이름"
type: string
description: "kebab-case 형식 (예: user-service)"
pattern: "^[a-z][a-z0-9-]*$"
owner:
title: "소유 팀"
type: string
description: "서비스를 소유할 팀"
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group
description:
title: "서비스 설명"
type: string
javaVersion:
title: "Java 버전"
type: string
enum: ["17", "21"]
default: "21"
- title: "인프라 설정"
properties:
database:
title: "데이터베이스"
type: string
enum: ["postgresql", "mysql", "none"]
default: "postgresql"
cache:
title: "캐시"
type: string
enum: ["redis", "none"]
default: "redis"
messageQueue:
title: "메시지 큐"
type: string
enum: ["kafka", "rabbitmq", "sqs", "none"]
default: "none"
- title: "배포 설정"
properties:
namespace:
title: "Kubernetes Namespace"
type: string
default: "default"
replicas:
title: "초기 레플리카 수"
type: number
default: 2
steps:
- id: fetch-template
name: "템플릿 코드 가져오기"
action: fetch:template
input:
url: "./skeleton"
values:
serviceName: "${{ parameters.serviceName }}"
owner: "${{ parameters.owner }}"
description: "${{ parameters.description }}"
javaVersion: "${{ parameters.javaVersion }}"
database: "${{ parameters.database }}"
cache: "${{ parameters.cache }}"
messageQueue: "${{ parameters.messageQueue }}"
- id: publish
name: "GitHub 저장소 생성"
action: publish:github
input:
allowedHosts: ["github.com"]
repoUrl: "github.com?owner=myorg&repo=${{ parameters.serviceName }}"
description: "${{ parameters.description }}"
defaultBranch: main
protectDefaultBranch: true
repoVisibility: internal
- id: register
name: "Backstage 카탈로그에 등록"
action: catalog:register
input:
repoContentsUrl: "${{ steps.publish.output.repoContentsUrl }}"
catalogInfoPath: "/catalog-info.yaml"
- id: create-argocd-app
name: "ArgoCD 애플리케이션 생성"
action: argocd:create-resources
input:
appName: "${{ parameters.serviceName }}"
argoInstance: "main"
namespace: "${{ parameters.namespace }}"
repoUrl: "${{ steps.publish.output.remoteUrl }}"
path: "k8s/overlays/development"
output:
links:
- title: "GitHub 저장소"
url: "${{ steps.publish.output.remoteUrl }}"
- title: "Backstage 카탈로그"
icon: catalog
entityRef: "${{ steps.register.output.entityRef }}"
3.4 TechDocs
TechDocs는 docs-as-code 방식으로 기술 문서를 관리합니다.
# mkdocs.yml - TechDocs 설정
site_name: "User Service"
site_description: "사용자 관리 마이크로서비스 기술 문서"
nav:
- Home: index.md
- Architecture:
- Overview: architecture/overview.md
- Data Model: architecture/data-model.md
- API Design: architecture/api-design.md
- Development:
- Getting Started: development/getting-started.md
- Local Setup: development/local-setup.md
- Testing Guide: development/testing.md
- Operations:
- Deployment: operations/deployment.md
- Monitoring: operations/monitoring.md
- Runbook: operations/runbook.md
- ADR:
- ADR-001 Database Choice: adr/001-database-choice.md
- ADR-002 Cache Strategy: adr/002-cache-strategy.md
plugins:
- techdocs-core
markdown_extensions:
- admonition
- codehilite
- pymdownx.superfences
- pymdownx.tabbed
3.5 Backstage 플러그인 생태계
# 인기 Backstage 플러그인
kubernetes_plugin:
기능: "K8s 클러스터의 서비스 상태 확인"
활용: "Pod 상태, 로그, 이벤트를 Backstage에서 직접 확인"
github_actions_plugin:
기능: "CI/CD 파이프라인 상태 표시"
활용: "빌드/배포 현황을 서비스 페이지에서 확인"
pagerduty_plugin:
기능: "온콜 일정 및 인시던트 연동"
활용: "서비스 소유자의 온콜 상태 표시"
cost_insights_plugin:
기능: "클라우드 비용 분석"
활용: "서비스별 비용 추적 및 트렌드 분석"
tech_radar_plugin:
기능: "기술 레이더 시각화"
활용: "조직의 기술 채택 상태 관리"
sonarqube_plugin:
기능: "코드 품질 메트릭 표시"
활용: "서비스별 코드 품질 스코어 확인"
grafana_plugin:
기능: "Grafana 대시보드 임베딩"
활용: "서비스 모니터링 대시보드를 Backstage에서 확인"
4. Golden Path 설계
4.1 Golden Path란?
Golden Path(Golden Road 또는 Paved Road라고도 함)는 조직이 권장하는 표준화된 개발 경로입니다. 개발자가 새 서비스를 만들 때 "어떤 언어를 쓸지, CI/CD는 어떻게 설정할지, 모니터링은 뭘 쓸지" 고민하지 않도록, 잘 정비된 길을 제공합니다.
Golden Path의 핵심 원칙:
1. 선택이 아닌 제안 (Suggestion, not Mandate)
├── 강제하지 않지만 따르면 가장 쉬운 길
├── 벗어날 수 있지만, 벗어나면 직접 지원
└── "비포장 도로도 갈 수 있지만, 포장 도로가 빠르다"
2. 즉시 프로덕션 레디 (Production-ready from Day 1)
├── CI/CD 파이프라인 기본 포함
├── 모니터링/로깅/알림 기본 설정
├── 보안 스캔 자동화
└── 헬스체크 엔드포인트
3. 지속적 진화 (Continuously Evolving)
├── 개발자 피드백 반영
├── 새로운 기술/도구 통합
└── 정기적인 업데이트
4.2 Golden Path 예시: 새 마이크로서비스
# Golden Path: 마이크로서비스 생성부터 프로덕션 배포까지
golden_path_microservice:
step_1_scaffolding:
도구: "Backstage Software Template"
결과:
- "GitHub 저장소 생성"
- "프로젝트 구조 (src, tests, k8s, docs)"
- "Dockerfile, docker-compose.yml"
- "CI/CD 파이프라인 (.github/workflows)"
- "catalog-info.yaml (Backstage 등록)"
- "mkdocs.yml (TechDocs)"
step_2_development:
도구: "표준 개발 환경"
결과:
- "devcontainer 설정"
- "pre-commit hooks (lint, format, security)"
- "통합 테스트 프레임워크"
- "로컬 개발 환경 (docker-compose)"
step_3_ci_cd:
도구: "GitHub Actions + ArgoCD"
결과:
- "PR시: 빌드, 테스트, 보안 스캔, 코드 리뷰"
- "merge시: Docker 이미지 빌드, 레지스트리 푸시"
- "ArgoCD 자동 동기화 (dev -> staging -> prod)"
- "Canary 또는 Blue-Green 배포"
step_4_observability:
도구: "OpenTelemetry + Grafana Stack"
결과:
- "메트릭: Prometheus + Grafana"
- "로그: Loki + Grafana"
- "트레이싱: Tempo + Grafana"
- "알림: Grafana Alerting -> Slack/PagerDuty"
step_5_security:
도구: "자동화된 보안 가드레일"
결과:
- "SAST: SonarQube"
- "DAST: OWASP ZAP"
- "SCA: Dependabot, Snyk"
- "이미지 스캔: Trivy"
- "Policy-as-Code: OPA/Kyverno"
4.3 표준 CI/CD 파이프라인
# .github/workflows/golden-path-ci.yml
# Golden Path 표준 CI 파이프라인
name: Golden Path CI
on:
pull_request:
branches: [main]
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: "${{ github.repository }}"
jobs:
lint-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'
cache: 'gradle'
- name: Lint
run: ./gradlew spotlessCheck
- name: Unit Tests
run: ./gradlew test
- name: Integration Tests
run: ./gradlew integrationTest
- name: Code Coverage
run: ./gradlew jacocoTestReport
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v2
env:
SONAR_TOKEN: "${{ secrets.SONAR_TOKEN }}"
- name: Dependency Check
uses: dependency-check/Dependency-Check_Action@main
with:
project: "${{ github.repository }}"
path: '.'
format: 'HTML'
build-and-push:
needs: [lint-and-test, security-scan]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: "${{ env.REGISTRY }}"
username: "${{ github.actor }}"
password: "${{ secrets.GITHUB_TOKEN }}"
- name: Build and Push
uses: docker/build-push-action@v5
with:
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Scan Image
uses: aquasecurity/trivy-action@master
with:
image-ref: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}"
format: 'table'
exit-code: '1'
severity: 'CRITICAL,HIGH'
5. 셀프서비스 인프라
5.1 Crossplane을 이용한 인프라 프로비저닝
Crossplane은 Kubernetes API를 확장하여 클라우드 리소스를 K8s 매니페스트로 관리할 수 있게 합니다.
# Crossplane Composition: RDS PostgreSQL
# crossplane-composition.yaml
apiVersion: apiextensions.crossplane.io/v1
kind: Composition
metadata:
name: postgresql-aws
labels:
provider: aws
database: postgresql
spec:
compositeTypeRef:
apiVersion: database.platform.io/v1alpha1
kind: PostgreSQLInstance
resources:
- name: rds-instance
base:
apiVersion: rds.aws.crossplane.io/v1alpha1
kind: DBInstance
spec:
forProvider:
engine: postgres
engineVersion: "15"
dbInstanceClass: db.t3.medium
allocatedStorage: 20
masterUsername: admin
skipFinalSnapshot: true
publiclyAccessible: false
vpcSecurityGroupIDRefs:
- name: rds-security-group
dbSubnetGroupNameRef:
name: rds-subnet-group
providerConfigRef:
name: aws-provider
patches:
- fromFieldPath: "spec.parameters.storageGB"
toFieldPath: "spec.forProvider.allocatedStorage"
- fromFieldPath: "spec.parameters.instanceClass"
toFieldPath: "spec.forProvider.dbInstanceClass"
---
# 개발자가 요청하는 리소스 (Claim)
apiVersion: database.platform.io/v1alpha1
kind: PostgreSQLInstance
metadata:
name: user-db
namespace: backend
spec:
parameters:
storageGB: 50
instanceClass: db.t3.medium
compositionSelector:
matchLabels:
provider: aws
database: postgresql
5.2 Terraform 모듈 기반 셀프서비스
# modules/microservice-infra/main.tf
# 마이크로서비스 인프라 표준 모듈
variable "service_name" {
type = string
description = "마이크로서비스 이름"
}
variable "team" {
type = string
description = "소유 팀"
}
variable "environment" {
type = string
description = "환경 (dev, staging, prod)"
}
variable "enable_database" {
type = bool
default = false
}
variable "enable_cache" {
type = bool
default = false
}
variable "enable_queue" {
type = bool
default = false
}
# EKS Namespace
resource "kubernetes_namespace" "service" {
metadata {
name = var.service_name
labels = {
team = var.team
environment = var.environment
managed-by = "terraform"
}
}
}
# PostgreSQL (선택)
module "database" {
count = var.enable_database ? 1 : 0
source = "../rds-postgresql"
name = "${var.service_name}-db"
environment = var.environment
instance_class = var.environment == "prod" ? "db.r6g.large" : "db.t3.medium"
allocated_storage = var.environment == "prod" ? 100 : 20
multi_az = var.environment == "prod" ? true : false
tags = {
Service = var.service_name
Team = var.team
Environment = var.environment
}
}
# Redis Cache (선택)
module "cache" {
count = var.enable_cache ? 1 : 0
source = "../elasticache-redis"
name = "${var.service_name}-cache"
environment = var.environment
node_type = var.environment == "prod" ? "cache.r6g.large" : "cache.t3.medium"
num_cache_nodes = var.environment == "prod" ? 3 : 1
tags = {
Service = var.service_name
Team = var.team
Environment = var.environment
}
}
# SQS Queue (선택)
module "queue" {
count = var.enable_queue ? 1 : 0
source = "../sqs"
name = "${var.service_name}-queue"
environment = var.environment
tags = {
Service = var.service_name
Team = var.team
Environment = var.environment
}
}
# 모니터링 대시보드 자동 생성
module "monitoring" {
source = "../grafana-dashboard"
service_name = var.service_name
namespace = kubernetes_namespace.service.metadata[0].name
enable_database_metrics = var.enable_database
enable_cache_metrics = var.enable_cache
enable_queue_metrics = var.enable_queue
}
output "namespace" {
value = kubernetes_namespace.service.metadata[0].name
}
output "database_endpoint" {
value = var.enable_database ? module.database[0].endpoint : null
sensitive = true
}
output "cache_endpoint" {
value = var.enable_cache ? module.cache[0].endpoint : null
sensitive = true
}
5.3 Pulumi Automation API
# Pulumi Automation API를 이용한 셀프서비스 인프라
# pulumi_self_service.py
import pulumi
from pulumi import automation as auto
import pulumi_aws as aws
import pulumi_kubernetes as k8s
import json
def create_microservice_infra(
service_name: str,
team: str,
environment: str,
config: dict
):
"""마이크로서비스 인프라 프로비저닝"""
def pulumi_program():
# Kubernetes Namespace
ns = k8s.core.v1.Namespace(
f"{service_name}-ns",
metadata=k8s.meta.v1.ObjectMetaArgs(
name=service_name,
labels={
"team": team,
"environment": environment,
"managed-by": "pulumi-automation"
}
)
)
# Database (optional)
if config.get("database"):
db = aws.rds.Instance(
f"{service_name}-db",
engine="postgres",
engine_version="15",
instance_class=(
"db.r6g.large" if environment == "prod"
else "db.t3.medium"
),
allocated_storage=config.get("database_storage_gb", 20),
db_name=service_name.replace("-", "_"),
username="admin",
skip_final_snapshot=True,
tags={
"Service": service_name,
"Team": team,
"Environment": environment
}
)
pulumi.export("database_endpoint", db.endpoint)
# Redis Cache (optional)
if config.get("cache"):
cache = aws.elasticache.Cluster(
f"{service_name}-cache",
engine="redis",
node_type=(
"cache.r6g.large" if environment == "prod"
else "cache.t3.medium"
),
num_cache_nodes=1,
tags={
"Service": service_name,
"Team": team,
"Environment": environment
}
)
pulumi.export("cache_endpoint", cache.cache_nodes[0].address)
pulumi.export("namespace", ns.metadata.name)
# Stack 생성 또는 선택
stack_name = f"{service_name}-{environment}"
project_name = "microservice-infra"
stack = auto.create_or_select_stack(
stack_name=stack_name,
project_name=project_name,
program=pulumi_program
)
# 설정 적용
stack.set_config("aws:region", auto.ConfigValue(value="ap-northeast-2"))
# 배포 실행
result = stack.up(on_output=print)
return {
"stack_name": stack_name,
"outputs": result.outputs,
"summary": result.summary
}
6. Developer Experience 측정
6.1 DORA Metrics
# DORA 메트릭 (DevOps Research and Assessment)
dora_metrics:
deployment_frequency:
설명: "배포 빈도 - 얼마나 자주 프로덕션에 배포하는가"
elite: "하루 여러 번"
high: "일주일에 한 번 ~ 하루에 한 번"
medium: "한 달에 한 번 ~ 일주일에 한 번"
low: "한 달에 한 번 미만"
lead_time_for_changes:
설명: "변경 리드타임 - 커밋부터 프로덕션 배포까지의 시간"
elite: "1시간 미만"
high: "1일 ~ 1주일"
medium: "1주일 ~ 1개월"
low: "1개월 이상"
change_failure_rate:
설명: "변경 실패율 - 배포 후 장애/롤백 비율"
elite: "0-15%"
high: "16-30%"
medium: "16-30%"
low: "31% 이상"
time_to_restore:
설명: "복구 시간 - 장애 발생부터 서비스 복구까지의 시간"
elite: "1시간 미만"
high: "1일 미만"
medium: "1일 ~ 1주일"
low: "1주일 이상"
6.2 SPACE Framework
# SPACE Framework for Developer Productivity
space_framework:
S_satisfaction:
설명: "개발자 만족도와 웰빙"
측정:
- "분기별 개발자 만족도 설문 (NPS)"
- "번아웃 위험 평가"
- "도구/플랫폼 만족도 점수"
예시_질문:
- "내부 도구에 대해 얼마나 만족하십니까? (1-10)"
- "새 서비스를 시작하는 것이 얼마나 쉽습니까?"
P_performance:
설명: "코드와 시스템의 성과"
측정:
- "코드 리뷰 품질 점수"
- "서비스 안정성 (SLO 달성률)"
- "고객 영향 인시던트 수"
A_activity:
설명: "개발 활동의 양적 측정"
측정:
- "PR 수와 크기"
- "배포 빈도"
- "코드 리뷰 참여도"
주의: "활동량만으로 생산성을 판단하지 말 것"
C_communication:
설명: "팀 간 협업과 소통의 효과성"
측정:
- "PR 리뷰 응답 시간"
- "문서 최신화율"
- "크로스팀 협업 빈도"
E_efficiency:
설명: "개발 프로세스의 효율성"
측정:
- "빌드 시간"
- "테스트 실행 시간"
- "환경 프로비저닝 시간"
- "온보딩 시간 (첫 PR까지)"
6.3 Platform 효과 측정 대시보드
# Platform 효과 측정 스크립트
# platform_metrics.py
from datetime import datetime, timedelta
import statistics
def calculate_platform_metrics(data):
"""플랫폼 효과 핵심 지표 계산"""
metrics = {}
# 1. 서비스 생성 시간
service_creation_times = data.get('service_creation_times', [])
if service_creation_times:
metrics['avg_service_creation_minutes'] = round(
statistics.mean(service_creation_times), 1
)
metrics['target_service_creation'] = 15 # 목표: 15분
# 2. 온보딩 시간 (첫 커밋까지)
onboarding_hours = data.get('onboarding_hours', [])
if onboarding_hours:
metrics['avg_onboarding_hours'] = round(
statistics.mean(onboarding_hours), 1
)
metrics['target_onboarding_hours'] = 4 # 목표: 4시간
# 3. 셀프서비스 비율
total_requests = data.get('total_infra_requests', 0)
self_service_requests = data.get('self_service_requests', 0)
if total_requests > 0:
metrics['self_service_rate'] = round(
(self_service_requests / total_requests) * 100, 1
)
metrics['target_self_service_rate'] = 80 # 목표: 80%
# 4. Golden Path 채택률
total_services = data.get('total_services', 0)
golden_path_services = data.get('golden_path_services', 0)
if total_services > 0:
metrics['golden_path_adoption_rate'] = round(
(golden_path_services / total_services) * 100, 1
)
# 5. 배포 빈도 (DORA)
deployments_per_day = data.get('daily_deployments', [])
if deployments_per_day:
metrics['avg_deployments_per_day'] = round(
statistics.mean(deployments_per_day), 1
)
# 6. 리드 타임 (커밋 -> 프로덕션)
lead_times_hours = data.get('lead_times_hours', [])
if lead_times_hours:
metrics['median_lead_time_hours'] = round(
statistics.median(lead_times_hours), 1
)
# 7. 플랫폼 NPS
nps_scores = data.get('nps_scores', [])
if nps_scores:
promoters = len([s for s in nps_scores if s >= 9])
detractors = len([s for s in nps_scores if s <= 6])
total = len(nps_scores)
metrics['platform_nps'] = round(
((promoters - detractors) / total) * 100
)
return metrics
7. Platform as a Product
7.1 제품 관리 원칙 적용
# Platform as a Product 원칙
principles:
developer_is_customer:
설명: "개발자가 플랫폼의 고객"
실천:
- "정기적인 사용자 인터뷰"
- "NPS 측정"
- "사용성 테스트"
- "피드백 루프"
product_roadmap:
설명: "플랫폼도 제품 로드맵을 가져야 함"
실천:
- "분기별 로드맵 공유"
- "우선순위 기반 기능 개발"
- "릴리스 노트 공개"
- "변경 사항 공지"
sla_and_support:
설명: "내부 SLA와 지원 체계"
실천:
- "플랫폼 가용성 SLA (예: 99.9%)"
- "응답 시간 SLA (예: 30분)"
- "전담 지원 채널 (Slack)"
- "정기적인 오피스 아워"
marketing:
설명: "내부 마케팅으로 채택 촉진"
실천:
- "쇼케이스 세션"
- "사용 사례 공유"
- "챔피언 프로그램"
- "내부 블로그/뉴스레터"
7.2 플랫폼 팀 조직 구조
플랫폼 팀 구조 (규모: 8-15명):
Platform Product Manager (1명)
├── 제품 비전과 로드맵
├── 개발자 인터뷰와 피드백 관리
└── 우선순위 결정
Platform Architect (1명)
├── 기술 전략과 아키텍처 결정
├── 기술 부채 관리
└── 외부 기술 트렌드 모니터링
Infrastructure Engineers (3-4명)
├── IDP 코어 인프라 구축
├── Kubernetes 클러스터 관리
├── CI/CD 파이프라인 개발
└── 보안 가드레일 구현
Developer Experience Engineers (2-3명)
├── Backstage 개발 및 플러그인
├── Golden Path 템플릿 개발
├── CLI 도구 개발
└── 문서 및 튜토리얼
Site Reliability Engineers (2명)
├── 플랫폼 안정성 보장
├── 인시던트 대응
├── 용량 계획
└── 성능 최적화
8. IDP 도구 비교
8.1 주요 IDP 도구
┌──────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
│ │ Backstage │ Port │ Humanitec │ Kratix │
├──────────────┼──────────────┼──────────────┼──────────────┼──────────────┤
│ 유형 │ 오픈소스 │ SaaS/자체호스팅│ SaaS │ 오픈소스 │
│ 개발사 │ Spotify/CNCF │ Port │ Humanitec │ Syntasso │
│ 핵심 강점 │ 확장성, 커뮤니티│ 빠른 설정 │ 스코어링 엔진│ K8s 네이티브 │
│ 플러그인 │ 100+ │ 내장 │ 내장 │ K8s CRD │
│ 셀프서비스 │ 템플릿 기반 │ 액션 기반 │ 스코어 기반 │ Promise 기반 │
│ 학습 곡선 │ 높음 │ 중간 │ 낮음 │ 높음 │
│ 커스터마이징 │ 매우 높음 │ 높음 │ 중간 │ 높음 │
│ 커뮤니티 │ 매우 활발 │ 성장 중 │ 작음 │ 성장 중 │
│ 적합 규모 │ 50+ 엔지니어 │ 20+ 엔지니어 │ 20+ 엔지니어 │ K8s 전문 팀 │
└──────────────┴──────────────┴──────────────┴──────────────┴──────────────┘
9. GitOps와 Platform Engineering
9.1 ArgoCD + Backstage 통합
# ArgoCD Application 정의
# argocd-application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: user-service
namespace: argocd
labels:
team: backend
managed-by: backstage
annotations:
backstage.io/component: "component:default/user-service"
spec:
project: default
source:
repoURL: "https://github.com/myorg/user-service"
targetRevision: HEAD
path: k8s/overlays/production
destination:
server: "https://kubernetes.default.svc"
namespace: user-service
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
9.2 ApplicationSet으로 멀티 환경 관리
# ArgoCD ApplicationSet - 환경별 자동 배포
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: user-service-environments
namespace: argocd
spec:
generators:
- list:
elements:
- environment: development
cluster: dev-cluster
namespace: user-service-dev
autoSync: "true"
- environment: staging
cluster: staging-cluster
namespace: user-service-staging
autoSync: "true"
- environment: production
cluster: prod-cluster
namespace: user-service-prod
autoSync: "false"
template:
metadata:
name: "user-service-{{ environment }}"
spec:
project: default
source:
repoURL: "https://github.com/myorg/user-service"
targetRevision: HEAD
path: "k8s/overlays/{{ environment }}"
destination:
server: "https://{{ cluster }}.internal:6443"
namespace: "{{ namespace }}"
syncPolicy:
automated:
prune: true
selfHeal: true
10. 보안 가드레일
10.1 Policy-as-Code: OPA/Gatekeeper
# Gatekeeper ConstraintTemplate: 필수 레이블 검증
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: requiredlabels
spec:
crd:
spec:
names:
kind: RequiredLabels
validation:
openAPIV3Schema:
type: object
properties:
labels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package requiredlabels
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("Missing required labels: %v", [missing])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: RequiredLabels
metadata:
name: require-team-label
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds: ["Deployment"]
namespaces:
- "backend"
- "frontend"
- "data"
parameters:
labels:
- "team"
- "environment"
- "service"
10.2 Kyverno 정책
# Kyverno 정책: 이미지 소스 제한
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
annotations:
policies.kyverno.io/title: "이미지 레지스트리 제한"
policies.kyverno.io/description: "승인된 레지스트리의 이미지만 허용"
spec:
validationFailureAction: Enforce
rules:
- name: validate-image-registry
match:
any:
- resources:
kinds:
- Pod
validate:
message: "승인된 레지스트리의 이미지만 사용할 수 있습니다."
pattern:
spec:
containers:
- image: "ghcr.io/myorg/* | 123456789.dkr.ecr.*.amazonaws.com/*"
---
# Kyverno 정책: 리소스 요청/제한 필수
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: Enforce
rules:
- name: validate-resources
match:
any:
- resources:
kinds:
- Pod
validate:
message: "모든 컨테이너에 CPU/메모리 requests와 limits를 설정해야 합니다."
pattern:
spec:
containers:
- resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
cpu: "?*"
11. 사례 연구
11.1 Spotify의 Platform Engineering
spotify_platform:
배경:
- "2000+ 엔지니어, 수천 개의 마이크로서비스"
- "각 팀이 독립적으로 기술 선택 -> 기술 분산"
- "서비스 발견과 소유자 추적이 어려움"
해결책:
- "Backstage 개발 및 오픈소스화 (2020)"
- "서비스 카탈로그로 모든 서비스 중앙 관리"
- "Golden Path로 표준화된 개발 경로 제공"
- "TechDocs로 코드 기반 문서화"
성과:
- "새 서비스 생성 시간: 수일 -> 수분"
- "온보딩 시간 대폭 단축"
- "서비스 발견 시간 최소화"
- "기술 부채 가시성 확보"
11.2 규모별 IDP 도입 전략
# 조직 규모별 IDP 도입 전략
small_org: # 20-50 엔지니어
phase_1:
- "표준 CI/CD 파이프라인 구축"
- "서비스 카탈로그 (간단한 Wiki 또는 스프레드시트)"
- "Terraform 모듈 표준화"
phase_2:
- "Backstage 도입 (Software Catalog)"
- "1-2개 Golden Path 템플릿"
- "기본 셀프서비스 (DB, 캐시)"
도입_기간: "3-6개월"
medium_org: # 50-200 엔지니어
phase_1:
- "Backstage 풀스택 도입"
- "3-5개 Golden Path 템플릿"
- "Crossplane 또는 Terraform 기반 셀프서비스"
- "DORA 메트릭 측정"
phase_2:
- "보안 가드레일 (OPA/Kyverno)"
- "비용 가시성 통합"
- "개발자 만족도 설문"
- "플러그인 생태계 확장"
도입_기간: "6-12개월"
large_org: # 200+ 엔지니어
phase_1:
- "전담 플랫폼 팀 구성 (8-15명)"
- "Platform as a Product 운영 모델"
- "종합적인 IDP 아키텍처 설계"
phase_2:
- "멀티 클러스터/멀티 클라우드 지원"
- "고급 보안 및 컴플라이언스 자동화"
- "FinOps 통합"
- "AI/ML 플랫폼 통합"
도입_기간: "12-18개월"
12. 퀴즈
Q1. Platform Engineering이 DevOps와 다른 점 3가지를 설명하세요.
정답:
-
접근 방식: DevOps는 각 팀에 DevOps 엔지니어를 파견하여 지원하는 반면, Platform Engineering은 셀프서비스 플랫폼을 구축하여 개발자가 직접 인프라를 관리할 수 있게 합니다.
-
확장성: DevOps 엔지니어는 개인의 역량에 의존하므로 조직이 커지면 병목이 되지만, Platform Engineering은 플랫폼 자체가 확장 가능하여 개발자 수 증가에 비례하여 확장됩니다.
-
표준화: DevOps는 팀마다 다른 도구와 프로세스를 사용할 수 있지만, Platform Engineering은 Golden Path를 통해 조직 전체의 표준화된 개발 경로를 제공합니다. 지식이 개인이 아닌 플랫폼에 내재화됩니다.
Q2. Backstage의 핵심 구성요소 3가지와 각각의 역할을 설명하세요.
정답:
-
Software Catalog: 조직의 모든 소프트웨어 자산(서비스, API, 라이브러리, 인프라)을 추적하는 중앙 카탈로그. 서비스 소유자, 의존성, 상태를 한눈에 파악할 수 있습니다.
-
Software Templates: 새 프로젝트를 표준화된 방식으로 생성하는 템플릿 시스템. GitHub 저장소 생성, CI/CD 설정, Backstage 등록까지 자동화합니다.
-
TechDocs: docs-as-code 방식의 기술 문서 시스템. 코드 저장소 내의 마크다운 파일을 Backstage에서 렌더링하여 서비스별 기술 문서를 중앙에서 탐색할 수 있습니다.
Q3. Golden Path의 핵심 원칙 3가지를 설명하고, Golden Path가 아닌 "강제 표준"과의 차이를 설명하세요.
정답:
핵심 원칙:
-
제안이지 강제가 아님: Golden Path를 따르면 가장 쉬운 길이지만, 벗어날 자유가 있습니다. 다만 벗어나면 플랫폼 팀의 지원을 받기 어렵습니다.
-
즉시 프로덕션 레디: Golden Path로 생성된 서비스는 CI/CD, 모니터링, 보안 스캔이 기본 포함되어 Day 1부터 프로덕션 배포가 가능합니다.
-
지속적 진화: 고정된 표준이 아니라, 개발자 피드백과 기술 발전에 따라 지속적으로 업데이트됩니다.
강제 표준과의 차이: 강제 표준은 위반 시 차단하거나 처벌하는 반면, Golden Path는 따르면 보상(쉬운 인프라, 자동 모니터링, 빠른 지원)이 있는 인센티브 기반 접근입니다.
Q4. DORA 메트릭의 4가지 지표와 각각의 "Elite" 수준을 설명하세요.
정답:
-
배포 빈도 (Deployment Frequency): 프로덕션에 얼마나 자주 배포하는가. Elite: 하루 여러 번 (주문형 배포).
-
변경 리드타임 (Lead Time for Changes): 코드 커밋부터 프로덕션 배포까지의 시간. Elite: 1시간 미만.
-
변경 실패율 (Change Failure Rate): 배포 후 장애, 롤백, 핫픽스가 필요한 비율. Elite: 0-15%.
-
서비스 복구 시간 (Time to Restore Service): 장애 발생부터 서비스 복구까지의 시간. Elite: 1시간 미만.
이 4가지 지표는 소프트웨어 딜리버리 성능을 측정하는 산업 표준이며, 높은 성능 조직일수록 비즈니스 성과도 좋다는 연구 결과가 있습니다.
Q5. Platform as a Product 원칙에서 "개발자가 고객"이라는 개념을 실천하기 위한 방법 4가지를 설명하세요.
정답:
-
정기적인 사용자 인터뷰: 개발자들과 1:1 또는 그룹 인터뷰를 수행하여 Pain Point와 요구사항을 파악합니다. 실제 사용 패턴을 관찰하는 사용성 테스트도 포함됩니다.
-
NPS(Net Promoter Score) 측정: 분기별로 플랫폼 만족도 설문을 실시하여 정량적으로 개발자 경험을 추적합니다. 추세 변화를 모니터링하고 개선 영역을 식별합니다.
-
제품 로드맵과 릴리스 노트: 플랫폼의 향후 계획을 투명하게 공유하고, 변경 사항을 릴리스 노트로 정리하여 개발자들이 새 기능을 활용할 수 있게 합니다.
-
전담 지원 채널과 SLA: Slack 채널, 오피스 아워 등 전담 지원 채널을 운영하고, 응답 시간 SLA를 설정하여 개발자들이 안정적으로 플랫폼을 사용할 수 있게 합니다.
13. 참고 자료
- Backstage.io - https://backstage.io/
- CNCF Platforms White Paper - CNCF TAG App Delivery
- Team Topologies - Matthew Skelton & Manuel Pais
- Platform Engineering on Kubernetes - Manning Publications
- DORA Metrics - https://dora.dev/
- SPACE Framework - Microsoft Research
- Crossplane Documentation - https://crossplane.io/
- ArgoCD Documentation - https://argo-cd.readthedocs.io/
- Karpenter Documentation - https://karpenter.sh/
- Port - https://www.getport.io/
- Humanitec - https://humanitec.com/
- Kratix - https://kratix.io/
- OPA Gatekeeper - https://open-policy-agent.github.io/gatekeeper/
- Kyverno - https://kyverno.io/
마무리
Platform Engineering은 DevOps의 자연스러운 진화입니다. 개발자의 인지 부하를 줄이고, 셀프서비스를 통해 생산성을 높이며, Golden Path로 조직 전체의 품질과 일관성을 향상시킵니다.
성공적인 Platform Engineering의 핵심은 **"제품처럼 플랫폼을 운영하는 것"**입니다. 개발자를 고객으로 대하고, 피드백을 수집하고, 지속적으로 개선하세요. 가장 좋은 플랫폼은 개발자가 자연스럽게 사용하고 싶어하는 플랫폼입니다.