인셔셔RSS 관심 있는 블로그, 뉴스, 기술 정보를 효율적으로 추적하고 읽으세요
원문 읽기 InertiaRSS에서 열기

추천 피드

V
V2EX
博客园 - 叶小钗
Y
Y Combinator Blog
大猫的无限游戏
大猫的无限游戏
博客园 - 【当耐特】
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Docker
WordPress大学
WordPress大学
Blog — PlanetScale
Blog — PlanetScale
博客园 - Franky
G
Google Developers Blog
爱范儿
爱范儿
Google DeepMind News
Google DeepMind News
Stack Overflow Blog
Stack Overflow Blog
云风的 BLOG
云风的 BLOG
Engineering at Meta
Engineering at Meta
aimingoo的专栏
aimingoo的专栏
V
Visual Studio Blog
M
MIT News - Artificial intelligence
Hugging Face - Blog
Hugging Face - Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
나는 47,000줄의 Terraform 스파게티를 상속받았다 — 생산 환경을 태우지 않고 그것을 풀어놓는 방법은 여기 있습니다
S, Sanjay · 2026-05-22 · via DEV Community

Slack 메시지로 제 월요일이 망쳐졌다

"Hey, 이전 플랫폼 팀이 떠났어. 리포지토리를 여기에 두고. 행운을 빌어 🫡"

나는 Git 리포지토리를 바라보았다. 47,000 줄의 Terraform. 하나의 상태 파일. 모듈은 없었다. x 이름이 __JHSNS_SEG_3fec2966_5__, temp2, 그리고 제가 가장 좋아하는 것 - DO_NOT_TOUCH_ask_raj. __JHSNS_SEG_3fec2966_8__ Raj는 두 년 전에 회사를 떠났었다.

한 해 이상 Senior DevOps Engineer로 일해왔다면, 그런 것들을 상속받았을 거예요. 아마 47K 줄은 아니지만, main.tf 당신의 경력 선택에 대해 의문을 제기하는 문제를 열었습니다.

이것은 "Terraform 최선의 관행" 기사가 아닙니다. 그런 것들은 Terraform을 운영해야 했던 사람들에 의해 작성되지 않았습니다.terraform plan3,000개 자원 상태 파일에 2시에 있을 때 엔지니어링 부사장이 지켜보고 있었다.

이것은 생존 가이드입니다.


반패턴 #1: 모노리스 상태 파일 (또는 "직업 실패의 단일 지점")

나가서 발견한 것

# main.tf — 8,400 lines
# "Managed" networking, compute, databases, DNS, IAM, monitoring,
# and somehow... a CloudFront distribution for a marketing site
# that was decommissioned in 2023.

resource "aws_vpc" "main" { ... }
resource "aws_instance" "api_server_1" { ... }
resource "aws_instance" "api_server_2" { ... }
# ... 200 more instances ...
resource "aws_rds_instance" "prod_db" { ... }
resource "aws_iam_role" "god_mode" { ... }  # yes, really

전체 화면 모드로 입력하세요 전체 화면 모드 종료

하나의terraform apply촉촉했다모든 것이 네트워킹, 데이터베이스, 컴퓨트, DNS — 모두 1월의 크리스마스 조명처럼 뒤얽혀 있습니다. 보안 그룹 규칙에 오타 하나라도 있으면? 축하합니다, 당신의 plan이 847개의 자원을 평가해야 하는데, Terraform은 당신의 RDS 인스턴스를 교체해야 한다고 결정했습니다.

실제 위험

이건 단순히 엉망이 아니라 — 운영적으로 치명적입니다. 이것이 일어나는 것은 다음과 같습니다:

  • terraform plan14분을 소요합니다.
  • State 파일 잠금은 한 번에 한 사람만 작업할 수 있음을 의미합니다.
  • 오류의 폭발 반경 = 전체 인프라.
  • 새로운 팀 멤버들은 아무것도 건드리지 두려워합니다 (당연히 그럴 만합니다).

어떻게 해결했는지 (운영 중단 없이)

단계 1: State 수술 시작 vớiterraform state mv

# First, I mapped resource dependencies visually
terraform graph | dot -Tsvg > infra-dependency-map.svg

# Then, split by domain boundaries
terraform state mv 'aws_vpc.main' -state-out=networking/terraform.tfstate
terraform state mv 'aws_subnet.public[0]' -state-out=networking/terraform.tfstate
terraform state mv 'aws_subnet.public[1]' -state-out=networking/terraform.tfstate

전체 화면 모드로 입력하세요 전체 화면 모드 종료

단계 2: 블래스트 반경으로 상태 경계 giới thiệu

나는 다섯 개의 상태 파일로 나뉘었습니다.주파수 변경그리고폭발 반경:

레이어 콘텐츠 주파수 변경 폭발 반경
foundation VPC, 서브넷, 경로 테이블 월별 심각
security IAM, KMS, 보안 그룹 주간 심각
data RDS, ElastiCache, S3 희귀 대단
compute ECS/EKS, ASGs, ALBs 일별 높음
edge CloudFront, Route53, WAF 주간 중간

3단계: 원격 상태 데이터 소스로 함께 연결합니다

# In compute/main.tf
data "terraform_remote_state" "networking" {
  backend = "s3"
  config = {
    bucket = "company-terraform-state"
    key    = "foundation/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_ecs_service" "api" {
  # Reference networking outputs safely
  network_configuration {
    subnets = data.terraform_remote_state.networking.outputs.private_subnet_ids
  }
}

전체 화면 모드 입력 전체 화면 모드 종료

결과: terraform plan 14분에서 45초로 줄었습니다. 팀 속도가 삼배로 증가했습니다. 2시에 알림을 받지 않습니다.


반패턴 #2: 사본 붙여넣기 제국 (또는 "집에서 모듈")

나가서 발견한 것

environments/
├── dev/
│   └── main.tf      # 1,200 lines
├── staging/
│   └── main.tf      # 1,200 lines (95% identical to dev)
├── prod/
│   └── main.tf      # 1,200 lines (90% identical... with 47 "hotfixes")
└── dr/
    └── main.tf      # 1,200 lines (copied from prod 8 months ago, never updated)

전체 화면 모드 전체 화면 모드 종료

동일한 인프라의 사본 4개와 미묘한 차이. 스테이징에는 보안 그룹 규칙이 있었는데, 프로덕션에는 없었음. DR은 3개의 서비스가 전혀 없었음. 누구도 어떤 차이가 의도된 것인지 알지 못함.

왜 이것이 경력 공학자들을 죽인다?

너는 못해diff이 문제를 해결하는 당신의 방법. 파일들은 의도적(프로덕션은 더 큰 인스턴스를 가짐)과 우발적(개발자가 버그를 수정했지만 전파를 깜빡했음)한 방식으로 분열되었습니다. 당신은진실의 출처가 없음.

실제로 작동하는 리팩토링 전략

모든 것을 한 번에 통합하려 하지 마세요. 나는 3 스프린트 동안 지속된 "큰 폭" 리팩토링이 실패하여 스테이징 환경을 1 주일 동안 망가뜨린 후에 어렵게 이를 배웠습니다.

대신, Strangler Fig 패턴을 사용하세요:

# modules/api-platform/main.tf
variable "environment" {
  type = string
  validation {
    condition     = contains(["dev", "staging", "prod", "dr"], var.environment)
    error_message = "Environment must be dev, staging, prod, or dr."
  }
}

variable "config" {
  type = object({
    instance_type    = string
    min_capacity     = number
    max_capacity     = number
    enable_waf       = bool
    multi_az         = bool
    backup_retention = number
  })
}

locals {
  # Environment-specific defaults that document WHY they differ
  env_config = {
    dev = {
      instance_type    = "t3.medium"
      min_capacity     = 1
      max_capacity     = 2
      enable_waf       = false
      multi_az         = false
      backup_retention = 1
    }
    prod = {
      instance_type    = "m5.xlarge"
      min_capacity     = 3
      max_capacity     = 20
      enable_waf       = true
      multi_az         = true
      backup_retention = 35
    }
  }
}

전체 화면 모드로 진입 전체 화면 모드로 나가기

핵심 통찰: 모든 환경 차이는코드에 의식적인 결정으로 문서화되었습니다가 아닌, 1,200줄의 파일에 우연한 분기로 숨겨져 있지 않습니다


반패턴 #3: terraform apply -auto-approve YOLO 파이프라인

.gitlab-ci.yml

deploy_prod:
  stage: deploy
  script:
    - terraform init
    - terraform apply -auto-approve  # 🚨 WHAT
  only:
    - main

에서 발견한 내용Enter fullscreen mode Exit fullscreen mode

계획 아티팩트 없음. 승인 게이트 없음. 차이 검토 없음. main으로 푸시 → 생산 환경에서 인프라 변경. 커밋 히스토리는 공포 이야기를 말해줌:

fix: revert the revert of the fix
fix: actually fix prod this time
fix: ok THIS one fixes it
revert: revert everything from today

전체 화면 모드로 진입 전체 화면 모드에서 나감

실제로 선임 엔지니어가 필요한 것은 무엇인가

# .github/workflows/terraform.yml
name: "Terraform"

on:
  pull_request:
    paths: ['infrastructure/**']
  push:
    branches: [main]
    paths: ['infrastructure/**']

jobs:
  plan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Terraform Plan
        id: plan
        run: |
          terraform init
          terraform plan -no-color -out=tfplan \
            -detailed-exitcode 2>&1 | tee plan_output.txt
        continue-on-error: true

      - name: Comment Plan on PR
        uses: actions/github-script@v7
        if: github.event_name == 'pull_request'
        with:
          script: |
            const fs = require('fs');
            const plan = fs.readFileSync('plan_output.txt', 'utf8');
            const truncated = plan.length > 60000 
              ? plan.substring(0, 60000) + '\n\n... truncated ...' 
              : plan;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `## Terraform Plan Output\n\`\`\`\n${truncated}\n\`\`\``
            });

      - name: Upload Plan Artifact
        uses: actions/upload-artifact@v4
        with:
          name: tfplan
          path: tfplan

  apply:
    needs: plan
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment: production  # Requires manual approval
    steps:
      - uses: actions/checkout@v4

      - name: Download Plan
        uses: actions/download-artifact@v4
        with:
          name: tfplan

      - name: Terraform Apply
        run: terraform apply tfplan  # Apply ONLY the reviewed plan

전체 화면 모드로 진입 전체 화면 모드에서 나감

타협할 수 없는 규칙:

  1. PR에 계획이 생성되고 아티팩트로 첨부됩니다.
  2. 인간이 생산 적용 전에 차이점을 검토합니다.
  3. Apply는 사용합니다정확히리뷰된 계획(새로운 계획은 아님).
  4. 더욱production환경은 고급 엔지니어의 수동 승인이 필요합니다.

반패턴 #4: 비밀 정보 (The Ticking Compliance Bomb)

나는 발견한 것

resource "aws_db_instance" "prod" {
  engine               = "postgres"
  instance_class       = "db.r5.2xlarge"
  username             = "admin"
  password             = "Pr0d_P@ssw0rd_2022!"  # I wish I was joking
  publicly_accessible  = true                    # I really wish I was joking
}

전체 화면 모드 전체 화면 모드 종료

비밀번호는 .tf 파일에, 상태 파일에, 계획 출력에, Git 이력에 있었습니다. 네 곳에서 유출될 수 있는 곳입니다. 그리고publicly_accessible = true는 이 쓰레기 손담이 아이스크림 위에 올린 딸기였습니다.

The Fix (That Also Passes Audit)

# Use a data source to pull secrets at plan/apply time
data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "prod/rds/master-password"
}

resource "aws_db_instance" "prod" {
  engine              = "postgres"
  instance_class      = "db.r5.2xlarge"
  username            = "admin"
  password            = data.aws_secretsmanager_secret_version.db_password.secret_string
  publicly_accessible = false

  # Prevent Terraform from detecting password "drift"
  lifecycle {
    ignore_changes = [password]
  }
}

전체 화면 모드로 진입 전체 화면 모드를 벗어나기

하지만 그것만으로는 충분하지 않습니다. 주 state 파일 여전히 민감한 값 포함하고 있습니다. 완전한 해결책:

# backend.tf
terraform {
  backend "s3" {
    bucket         = "company-terraform-state"
    key            = "prod/data/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true                          # SSE-KMS encryption
    kms_key_id     = "arn:aws:kms:us-east-1:xxx:key/yyy"
    dynamodb_table = "terraform-state-lock"
  }
}

전체 화면 모드로 진입 전체 화면 모드 종료

더 엄격한 S3 버킷 정책, 접근 로깅, 그리고 절대 개발자에게 직접 상태 파일 접근을 허용하지 않기. 대신 terraform output을 사용하세요.


반패턴 #5: 200줄의 중첩 블록으로 이루어진 "신의 자원"

나가 발견한 것

resource "aws_ecs_task_definition" "api" {
  family                   = "api"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = 1024
  memory                   = 2048
  execution_role_arn       = aws_iam_role.ecs_execution.arn
  task_role_arn            = aws_iam_role.ecs_task.arn

  container_definitions = jsonencode([
    {
      name  = "api"
      image = "company/api:latest"  # 🚨 LATEST TAG IN PROD
      portMappings = [{ containerPort = 8080 }]
      environment = [
        { name = "DB_HOST", value = "prod-db.cluster-xxx.us-east-1.rds.amazonaws.com" },
        { name = "DB_NAME", value = "production" },
        { name = "REDIS_URL", value = "prod-redis.xxx.cache.amazonaws.com:6379" },
        # ... 45 more environment variables hardcoded here ...
      ]
      logConfiguration = {
        logDriver = "awslogs"
        options = {
          "awslogs-group"         = "/ecs/api"
          "awslogs-region"        = "us-east-1"
          "awslogs-stream-prefix" = "api"
        }
      }
      # ... 80 more lines of health checks, mount points, ulimits ...
    }
  ])
}

전체 화면 모드 입력 전체 화면 모드 종료

문제가 복잡해지고 있습니다:

  • 환경 변수는 고정 코드화되어 있습니다(SSM/Secrets Manager에서 소스로부터 가져오지 않음).
  • latest 태그는 배포가 재현할 수 없음을 의미합니다.
  • jsonencode 볼륨은 PR 리뷰에서 테스트되거나 비교할 수 없습니다.
  • 환경 변수의 한 변경 사항이 전체 작업 정의 재설정을 트리거합니다.

리팩토링된 버전

# Use templatefile for complex JSON — it's testable and readable
resource "aws_ecs_task_definition" "api" {
  family                   = "api-${var.environment}"
  network_mode             = "awsvpc"
  requires_compatibilities = ["FARGATE"]
  cpu                      = var.task_cpu
  memory                   = var.task_memory
  execution_role_arn       = aws_iam_role.ecs_execution.arn
  task_role_arn            = aws_iam_role.ecs_task.arn

  container_definitions = templatefile("${path.module}/templates/api-container.json.tpl", {
    image_tag     = var.image_tag  # Pinned, passed from CI/CD
    environment   = var.environment
    db_host       = data.aws_ssm_parameter.db_host.value
    redis_url     = data.aws_ssm_parameter.redis_url.value
    log_group     = aws_cloudwatch_log_group.api.name
    aws_region    = data.aws_region.current.name
  })
}

전체 화면 모드로 전환 종료 전체 화면 모드


리팩토링 플레이북 (이번 월요일에 해보세요)

이 케이스를 세 달 동안 뒤엉킨 후, 이것이 작동하는 순서는 다음과 같습니다:

1주차: 트라이게이션 및 보호

# 1. Enable state file encryption and locking NOW
# 2. Add branch protection — no direct pushes to main
# 3. Run terraform plan and SAVE the output as your baseline
terraform plan -no-color > baseline_plan_$(date +%Y%m%d).txt

# 4. Enable detailed audit logging on your state bucket

전체 화면 모드로 전환 종료 전체 화면 모드

2-4주차: 모노리스 분리

# Use terraform state list to inventory everything
terraform state list > all_resources.txt
wc -l all_resources.txt  # Mine had 2,847 resources

# Group by service domain
grep "aws_vpc\|aws_subnet\|aws_route" all_resources.txt > networking.txt
grep "aws_iam\|aws_kms" all_resources.txt > security.txt
grep "aws_rds\|aws_elasticache\|aws_s3" all_resources.txt > data.txt
grep "aws_ecs\|aws_alb\|aws_autoscaling" all_resources.txt > compute.txt

전체 화면 모드 입력 전체 화면 모드 종료

주 5-8: 모듈화를 점진적으로 수행

한 번에 하나의 서비스를모듈로 이동시킨다. 각 이동 후:

  1. terraform plan를 실행한다 — 이는변경 사항이 없음을 보여야 한다.
  2. 계획에 변경 사항이 표시되면 버그가 있습니다. 계속하기 전에 수정하세요.
  3. 다른 경험 많은 엔지니어로부터 PR 검토를 받으세요.
  4. 적용하고 24시간 동안 모니터링하세요.

주 9-12: 파이프라인을 강화하세요

  • terraform validatetflint를 CI에 추가하세요.
  • 보안 스캔을 위해 checkov 또는 tfsec을 추가하세요.
  • 이상 탐지를 구현합니다 (차이를 알리는 일정된 계획).
  • 비용 추정을 infracost과 함께 추가합니다.

이상 탐지 크론 작업은 우리를 구한 것

이것은 아무도 이야기하지 않는 일입니다. 완벽한 리팩토링 이후에도 이상이 발생합니다 콘솔에서 누군가 클릭했습니다. 자동 수정 도구가 변경을 수행합니다. Lambda가 보안 그룹을 수정합니다.

# .github/workflows/drift-detection.yml
name: "Drift Detection"

on:
  schedule:
    - cron: '0 6 * * 1-5'  # Every weekday at 6 AM

jobs:
  detect-drift:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        layer: [foundation, security, data, compute, edge]
    steps:
      - uses: actions/checkout@v4

      - name: Terraform Plan (Drift Check)
        id: plan
        working-directory: infrastructure/${{ matrix.layer }}
        run: |
          terraform init
          terraform plan -detailed-exitcode -no-color > plan.txt 2>&1
          echo "exitcode=$?" >> $GITHUB_OUTPUT
        continue-on-error: true

      - name: Alert on Drift
        if: steps.plan.outputs.exitcode == '2'
        run: |
          # Exit code 2 = changes detected (drift!)
          curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -H 'Content-type: application/json' \
            -d "{\"text\":\"🚨 Drift detected in *${{ matrix.layer }}* layer. Check the plan output.\"}"

전체 화면 모드 입력 전체 화면 모드 종료

첫 주에만 3건의 미승인 콘솔 변경을 포착했습니다.


최고 엔지니어가 엉망으로 물려받은 후의 마지막 충언

  1. 모든 것을 한 번에 리팩토링하지 마세요. 문제를 만들고 신뢰도를 잃게 될 거예요.

  2. 고치기 전에 발견한 사항을 문서화하세요. 공포스러운 부분을 스크린샷하세요. 사후 분석과 성과 평가에 필요할 거예요.

  3. 시작하기 전에 리더십의 승인을 받으세요. "기술 부채를 해결하기 위해 3번의 스프린트가 필요합니다"는 어려운 판매입니다. "현재 설정은 인프라 변경이 사고를 유발할 확률이 40%라는 것"은 예산을 승인받습니다.

  4. 각각의 terraform state mv은 별도의, 검토된 PR이어야 합니다. 기술적으로 필요하기 때문이 아니라, 단계 50의 37단계에서 문제가 발생했을 때 깨끗한 git 이력을 이분법적으로 분석하고 싶기 때문입니다.

  5. 목표는 완벽한 Terraform이 아닙니다. 목표는 팀이 2시에 안전하게 운영할 수 있는 Terraform입니다.주니어 엔지니어가 실행할 수 없다면terraform plan두려움 없이, 당신의 리팩토링은 끝나지 않았습니다.


스크롤러용 TL;DR

반패턴 수정하세요 우선순위
몬로빗 상태 파일 폭발 반경으로 나누고 주파수 변경 P0
복사-붙여넣기 환경 모듈 + 환경 구성 P1
-auto-approve CI에서 계획 아티팩트 + 수동 승인 게이트 P0
상태/코드에 있는 비밀 비밀 관리자 + 암호화된 상태 + ignore_changes P0
JSON으로 직접 포함된 God 자원 templatefile + SSM 파라미터 P2
이상치 감지 없음 예약된plan알림과 함께 P1

Terraform 코드베이스를 보고 빈 공간에 "누가 이걸 만들었지?"라고 속삭였다면 — 당신만이 아니에요. 우리 모두 그런 경험이 있었어요. 좋은 소식은? 수정할 수 있어요. 한 번에 한 번의 상태 이동으로.


이것이 유용하신가요? 더 많은 테스트를 통과한 DevOps 콘텐츠를 위해 저를 따르세요. 저는 실제 생산 환경에서 일어나는 일에 대해 씁니다 — 문서에서의 행복한 경로가 아닙니다.