Skip to content

✍️ 필사 모드: Platform Engineering 완전 가이드 2025: Internal Developer Platform, Backstage, Golden Path

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

목차

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 도구

┌──────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
│              │ BackstagePortHumanitecKratix├──────────────┼──────────────┼──────────────┼──────────────┼──────────────┤
│ 유형          │ 오픈소스      │ SaaS/자체호스팅│ SaaS         │ 오픈소스      │
│ 개발사        │ Spotify/CNCFPortHumanitecSyntasso│ 핵심 강점     │ 확장성, 커뮤니티│ 빠른 설정    │ 스코어링 엔진│ 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가지를 설명하세요.

정답:

  1. 접근 방식: DevOps는 각 팀에 DevOps 엔지니어를 파견하여 지원하는 반면, Platform Engineering은 셀프서비스 플랫폼을 구축하여 개발자가 직접 인프라를 관리할 수 있게 합니다.

  2. 확장성: DevOps 엔지니어는 개인의 역량에 의존하므로 조직이 커지면 병목이 되지만, Platform Engineering은 플랫폼 자체가 확장 가능하여 개발자 수 증가에 비례하여 확장됩니다.

  3. 표준화: DevOps는 팀마다 다른 도구와 프로세스를 사용할 수 있지만, Platform Engineering은 Golden Path를 통해 조직 전체의 표준화된 개발 경로를 제공합니다. 지식이 개인이 아닌 플랫폼에 내재화됩니다.

Q2. Backstage의 핵심 구성요소 3가지와 각각의 역할을 설명하세요.

정답:

  1. Software Catalog: 조직의 모든 소프트웨어 자산(서비스, API, 라이브러리, 인프라)을 추적하는 중앙 카탈로그. 서비스 소유자, 의존성, 상태를 한눈에 파악할 수 있습니다.

  2. Software Templates: 새 프로젝트를 표준화된 방식으로 생성하는 템플릿 시스템. GitHub 저장소 생성, CI/CD 설정, Backstage 등록까지 자동화합니다.

  3. TechDocs: docs-as-code 방식의 기술 문서 시스템. 코드 저장소 내의 마크다운 파일을 Backstage에서 렌더링하여 서비스별 기술 문서를 중앙에서 탐색할 수 있습니다.

Q3. Golden Path의 핵심 원칙 3가지를 설명하고, Golden Path가 아닌 "강제 표준"과의 차이를 설명하세요.

정답:

핵심 원칙:

  1. 제안이지 강제가 아님: Golden Path를 따르면 가장 쉬운 길이지만, 벗어날 자유가 있습니다. 다만 벗어나면 플랫폼 팀의 지원을 받기 어렵습니다.

  2. 즉시 프로덕션 레디: Golden Path로 생성된 서비스는 CI/CD, 모니터링, 보안 스캔이 기본 포함되어 Day 1부터 프로덕션 배포가 가능합니다.

  3. 지속적 진화: 고정된 표준이 아니라, 개발자 피드백과 기술 발전에 따라 지속적으로 업데이트됩니다.

강제 표준과의 차이: 강제 표준은 위반 시 차단하거나 처벌하는 반면, Golden Path는 따르면 보상(쉬운 인프라, 자동 모니터링, 빠른 지원)이 있는 인센티브 기반 접근입니다.

Q4. DORA 메트릭의 4가지 지표와 각각의 "Elite" 수준을 설명하세요.

정답:

  1. 배포 빈도 (Deployment Frequency): 프로덕션에 얼마나 자주 배포하는가. Elite: 하루 여러 번 (주문형 배포).

  2. 변경 리드타임 (Lead Time for Changes): 코드 커밋부터 프로덕션 배포까지의 시간. Elite: 1시간 미만.

  3. 변경 실패율 (Change Failure Rate): 배포 후 장애, 롤백, 핫픽스가 필요한 비율. Elite: 0-15%.

  4. 서비스 복구 시간 (Time to Restore Service): 장애 발생부터 서비스 복구까지의 시간. Elite: 1시간 미만.

이 4가지 지표는 소프트웨어 딜리버리 성능을 측정하는 산업 표준이며, 높은 성능 조직일수록 비즈니스 성과도 좋다는 연구 결과가 있습니다.

Q5. Platform as a Product 원칙에서 "개발자가 고객"이라는 개념을 실천하기 위한 방법 4가지를 설명하세요.

정답:

  1. 정기적인 사용자 인터뷰: 개발자들과 1:1 또는 그룹 인터뷰를 수행하여 Pain Point와 요구사항을 파악합니다. 실제 사용 패턴을 관찰하는 사용성 테스트도 포함됩니다.

  2. NPS(Net Promoter Score) 측정: 분기별로 플랫폼 만족도 설문을 실시하여 정량적으로 개발자 경험을 추적합니다. 추세 변화를 모니터링하고 개선 영역을 식별합니다.

  3. 제품 로드맵과 릴리스 노트: 플랫폼의 향후 계획을 투명하게 공유하고, 변경 사항을 릴리스 노트로 정리하여 개발자들이 새 기능을 활용할 수 있게 합니다.

  4. 전담 지원 채널과 SLA: Slack 채널, 오피스 아워 등 전담 지원 채널을 운영하고, 응답 시간 SLA를 설정하여 개발자들이 안정적으로 플랫폼을 사용할 수 있게 합니다.


13. 참고 자료

  1. Backstage.io - https://backstage.io/
  2. CNCF Platforms White Paper - CNCF TAG App Delivery
  3. Team Topologies - Matthew Skelton & Manuel Pais
  4. Platform Engineering on Kubernetes - Manning Publications
  5. DORA Metrics - https://dora.dev/
  6. SPACE Framework - Microsoft Research
  7. Crossplane Documentation - https://crossplane.io/
  8. ArgoCD Documentation - https://argo-cd.readthedocs.io/
  9. Karpenter Documentation - https://karpenter.sh/
  10. Port - https://www.getport.io/
  11. Humanitec - https://humanitec.com/
  12. Kratix - https://kratix.io/
  13. OPA Gatekeeper - https://open-policy-agent.github.io/gatekeeper/
  14. Kyverno - https://kyverno.io/

마무리

Platform Engineering은 DevOps의 자연스러운 진화입니다. 개발자의 인지 부하를 줄이고, 셀프서비스를 통해 생산성을 높이며, Golden Path로 조직 전체의 품질과 일관성을 향상시킵니다.

성공적인 Platform Engineering의 핵심은 **"제품처럼 플랫폼을 운영하는 것"**입니다. 개발자를 고객으로 대하고, 피드백을 수집하고, 지속적으로 개선하세요. 가장 좋은 플랫폼은 개발자가 자연스럽게 사용하고 싶어하는 플랫폼입니다.

현재 단락 (1/1115)

DevOps는 개발과 운영의 벽을 허물었지만, 새로운 문제를 만들었습니다. "You build it, you run it" 원칙이 확산되면서 개발자들의 **인지 부하(Cogniti...

작성 글자: 0원문 글자: 26,467작성 단락: 0/1115