13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] SRE

  • [Cloud] Datadog 비용 최적화 전략: 불필요한 지출 줄이기

    [Cloud] Datadog 비용 최적화 전략: 불필요한 지출 줄이기

    Datadog 비용 최적화 전략: 불필요한 지출을 줄이는 운영 방법론

    Datadog 비용 최적화는 모니터링을 약하게 만드는 일이 아닙니다. 장애를 설명하는 신호는 끝까지 남기고, 아무도 보지 않는 반복 신호는 수집 전이나 색인 단계에서 줄이는 작업에 가깝습니다. 이 선을 먼저 정해두면 비용도 줄고, 운영자가 불안해하지 않더라고요.

    Datadog 비용이 커지는 조직을 보면 공통점이 있습니다. 기능을 많이 써서라기보다 수집 정책의 기본값이 운영 정책처럼 굳어져 있습니다. Docker stdout은 전부 로그로 들어오고, Kubernetes의 짧게 뜨는 Job도 서비스처럼 취급되고, DEBUG 로그는 릴리스가 끝난 뒤에도 계속 색인됩니다.

    홈랩에서 Kubernetes와 VM을 섞어 굴릴 때도 비슷했습니다. 서비스 몇 개만 올렸는데 health check 로그, readiness probe 로그, 테스트 Job 출력, DEBUG 로그가 생각보다 빨리 쌓이더라고요. 그때 깨달은 건 단순했습니다. 비용을 줄이려면 UI에서 필터 몇 개 만지는 것보다 수집, 태깅, 색인, 보관, 알림의 경계를 먼저 정해야 합니다.

    Datadog 비용 최적화 전체 흐름을 보여주는 아키텍처 다이어그램

    Datadog 비용 최적화는 한 번의 정리 작업이 아니라 운영 루프입니다. 저는 보통 귀속 → 차단 → 샘플링 → 보관 → 검증 순서로 봅니다. 이 순서를 지키면 무엇을 줄였는지와 무엇을 아직 볼 수 있는지가 같이 남습니다.

    1. Datadog 비용 최적화는 제품명이 아니라 의사결정 단위로 보기

    Datadog 지출을 볼 때 “로그가 비싸다”, “APM이 비싸다”처럼 제품명으로만 보면 조치가 흐려집니다. 실무에서는 다음 질문으로 쪼개야 합니다.

    • 누가 만들었나: team, service, env 태그로 소유자를 찾을 수 있는가?
    • 어디에서 늘었나: host, container, custom metric, indexed log, ingested log, trace 중 어느 축인가?
    • 장애 대응에 쓰였나: 최근 인시던트에서 실제로 검색하거나 알림 조건에 사용했는가?
    • 줄이면 무엇을 잃나: 원인 분석 시간, 보안 감사, 규제 보관, SLO 계산에 영향이 있는가?

    제가 가장 먼저 확인하는 화면과 지표는 아래입니다.

    • Usage Attribution: 설정한 태그 키를 기준으로 사용량을 나눠 봅니다. 다만 모든 제품 사용량이 모든 태그 기준으로 완벽히 분해되지는 않습니다.
    • Estimated Usage Metrics: datadog.estimated_usage.* 메트릭으로 증가 추세를 봅니다. 실시간 추정치라 청구서와 1:1로 맞추는 용도보다는 변화 감지에 더 적합합니다.
    • Log Indexes: 어떤 로그가 검색 가능한 상태로 저장되는지, 어떤 exclusion filter를 통과하는지 확인합니다.
    • Retention: prod 오류 로그와 dev access log가 같은 기간 저장되고 있지 않은지 봅니다.

    비용 절감 작업에서 가장 위험한 말은 “일단 Agent 꺼보죠”입니다. Agent를 끄면 비용은 줄어든 것처럼 보이지만 장애 때 시스템이 침묵합니다. 먼저 소유권과 경로를 잡고, 그다음 수집량을 줄이는 편이 안전합니다.

    2. 태그 표준화: 비용 보고서가 아니라 운영 계약으로 다루기

    Datadog에서 태그는 검색 편의 기능을 넘어 비용 통제의 단위입니다. 태그가 흔들리면 Usage Attribution도 흔들리고, 팀별 비용 대화도 금방 흐려집니다. 저는 최소한 아래 4개를 기본 계약으로 둡니다.

    env:prod | env:staging | env:dev
    service:api | service:worker | service:nginx
    team:platform | team:billing | team:checkout
    cost_center:shared | cost_center:revenue | cost_center:internal

    중요한 건 태그 이름보다 태그가 붙는 위치입니다. 애플리케이션 로그에는 service가 붙었는데 컨테이너 메트릭에는 빠지고, APM trace에는 다른 서비스명이 들어가면 비용 분석이 갈라집니다. 가능하면 애플리케이션, 컨테이너 label, Helm values, Agent 설정에서 같은 네이밍을 유지하세요.

    Kubernetes에서는 Pod label과 Datadog 태그를 연결하는 규칙을 명확히 두는 편이 좋습니다. 예를 들어 Helm values에서 공통 태그를 관리하면 배포마다 태그가 빠지는 실수를 줄일 수 있습니다.

    # datadog-values.yaml 예시
    datadog:
      tags:
        - env:prod
        - team:platform
        - cost_center:shared
      logs:
        enabled: true
        containerCollectAll: false
      apm:
        portEnabled: true

    containerCollectAll: false는 모든 컨테이너 로그를 기본 수집하지 않겠다는 선택입니다. 이 값 하나로 비용이 마법처럼 줄지는 않지만, “로그를 보내려면 의도를 표시한다”는 문화를 만들 수 있습니다. 다만 초기 도입기나 장애가 잦은 시스템에서는 너무 빨리 닫으면 디버깅이 어려워집니다.

    3. Agent 단계에서 버릴 것과 Datadog 안에서 줄일 것 구분하기

    로그 비용 최적화의 첫 갈림길은 “Agent에서 보내지 않을 것인가, Datadog에는 보내되 색인하지 않을 것인가”입니다. 둘은 완전히 다른 결정입니다.

    선택지 언제 쓰나 장점 위험
    Agent 단계 제외 health check, readiness probe, 로컬 개발 Job처럼 분석 가치가 낮고 반복적인 로그 전송과 ingestion 자체를 줄입니다. 나중에 Datadog에서 복구할 수 없습니다.
    Index exclusion filter 들어오긴 해야 하지만 검색 저장은 줄이고 싶은 로그 수집 후 라우팅과 샘플링을 조정할 수 있습니다. ingestion은 계속 발생합니다.
    별도 Index 분리 보관 기간, 접근 권한, 검색 목적이 다른 로그 prod, audit, dev 로그의 보관 정책을 다르게 가져갑니다. 라우팅 쿼리와 순서를 문서화해야 합니다.
    애플리케이션 로그 레벨 수정 DEBUG/TRACE가 prod에서 계속 발생하는 경우 가장 근본적인 해결입니다. 릴리스 관찰에 필요한 신호까지 사라질 수 있습니다.

    제 기준은 간단합니다. 절대 다시 보지 않을 로그는 Agent에서 제외하고, 상황에 따라 필요할 수 있는 로그는 Datadog에 보낸 뒤 색인 정책으로 조절합니다. 보안, 감사, 결제, 인증 실패 로그는 비용만 보고 Agent 단계에서 버리면 안 됩니다.

    4. Datadog Agent 로그 수집을 실제로 줄이는 설정

    먼저 Agent가 무엇을 수집하는지 확인합니다. 비용 최적화 전에 이 명령어 결과를 남겨두면 변경 전후 비교가 쉽습니다.

    sudo datadog-agent status
    sudo datadog-agent configcheck
    sudo datadog-agent flare --local

    컨테이너로 Agent를 운영한다면 아래처럼 확인합니다.

    docker exec -it datadog-agent agent status
    docker exec -it datadog-agent agent configcheck

    NGINX access log에서 health check만 제외하려면 서비스별 conf에서 log_processing_rules를 씁니다. 패턴은 Go 정규식 문법을 따르므로, 운영 반영 전에 샘플 로그로 꼭 검증하세요.

    # /etc/datadog-agent/conf.d/nginx.d/conf.yaml
    logs:
      - type: file
        path: /var/log/nginx/access.log
        service: nginx
        source: nginx
        log_processing_rules:
          - type: exclude_at_match
            name: exclude_healthcheck
            pattern: 'GET /health(?:\s|\?)'

    GET /health처럼 단순 문자열만 쓰면 의도치 않은 경로까지 걸릴 수 있습니다. 비용 필터는 일부러 좁게 잡는 편이 낫습니다. 비용을 아끼려다 필요한 장애 로그를 버리면 복구 비용이 더 커지거든요.

    반대로 특정 로그만 보내고 싶다면 include_at_match를 쓸 수 있습니다. 다만 이 방식은 나머지를 모두 버리는 성격이라 dev나 특정 배치 Job처럼 목적이 좁은 곳에만 권합니다.

    # 특정 배치 Job에서 ERROR/WARN만 Datadog으로 전송
    logs:
      - type: file
        path: /var/log/batch/job.log
        service: settlement-batch
        source: java
        log_processing_rules:
          - type: include_at_match
            name: include_warn_error_only
            pattern: '"level":"(WARN|ERROR)"|\b(WARN|ERROR)\b'
    Datadog Agent 로그 필터링으로 옵저버빌리티 비용 절감하는 구성도

    Agent 단계 필터는 효과가 빠르지만 되돌릴 수 있는 데이터가 없습니다. 그래서 저는 health check, liveness probe, 성공한 정적 파일 요청처럼 장애 설명력이 낮고 재현 가능한 로그부터 제외합니다.

    5. Docker와 Kubernetes: 전체 수집보다 명시 수집을 기본값으로 만들기

    컨테이너 환경에서 비용이 흔들리는 이유는 서비스 수보다 수명 짧은 워크로드와 stdout 기본 수집 때문입니다. CronJob, migration Job, 임시 테스트 컨테이너가 모두 같은 규칙으로 수집되면 비용 그래프가 운영 트래픽과 무관하게 튑니다.

    Docker Compose에서는 Datadog Agent가 자기 자신이나 개발용 컨테이너 로그를 물고 들어가지 않게 제외 규칙을 둡니다. Compose의 environment list 문법에서는 값 전체를 불필요하게 따옴표로 감싸지 않는 편이 읽기도 좋습니다.

    services:
      datadog-agent:
        image: gcr.io/datadoghq/agent:latest
        environment:
          - DD_LOGS_ENABLED=true
          - DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL=true
          - DD_CONTAINER_EXCLUDE_LOGS=name:datadog-agent image:^local-dev-tool:.* name:^tmp-.*
          - DD_CONTAINER_EXCLUDE_METRICS=name:^tmp-.*
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock:ro
          - /var/lib/docker/containers:/var/lib/docker/containers:ro
          - /proc/:/host/proc/:ro
          - /sys/fs/cgroup/:/host/sys/fs/cgroup:ro

    DD_CONTAINER_EXCLUDE는 전역 제외에 가깝고, DD_CONTAINER_EXCLUDE_LOGS와 DD_CONTAINER_EXCLUDE_METRICS는 로그와 메트릭을 따로 다룰 때 씁니다. 저는 보통 로그부터 줄입니다. 메트릭은 장애 대응의 기본 뼈대라서, 임시 Job이 아닌 이상 너무 공격적으로 빼지 않습니다.

    Kubernetes에서는 namespace나 label 정책을 섞어야 합니다. 예를 들어 dev namespace 전체 로그를 줄이고, 꼭 필요한 Pod만 annotation으로 수집하는 방식이 관리하기 편합니다.

    # 로그를 수집할 Pod에만 명시적으로 Autodiscovery annotation 부여
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: api
      namespace: prod
    spec:
      template:
        metadata:
          labels:
            app: api
            tags.datadoghq.com/env: prod
            tags.datadoghq.com/service: api
            tags.datadoghq.com/team: checkout
          annotations:
            ad.datadoghq.com/api.logs: >-
              [{"source":"java","service":"api","log_processing_rules":[{"type":"exclude_at_match","name":"drop_health","pattern":"GET /health(?:\\s|\\?)"}]}]
        spec:
          containers:
            - name: api
              image: registry.example.com/api:stable

    여기서 흔한 실패 모드는 컨테이너 이름 불일치입니다. annotation의 ad.datadoghq.com/api.logs에서 api는 컨테이너 이름과 맞아야 합니다. Deployment 이름이나 Kubernetes Service 이름으로 착각하면 설정이 조용히 빗나갑니다.

    6. Log Index와 Exclusion Filter: 비용 최적화의 진짜 손잡이

    Datadog 로그는 ingestion과 indexing을 나눠 봐야 합니다. 들어온 로그 전체가 곧 검색 가능한 로그는 아닙니다. Index exclusion filter를 쓰면 Datadog에는 로그가 들어오지만, 특정 조건의 로그를 색인하지 않거나 샘플링할 수 있습니다.

    제가 자주 쓰는 판단은 이렇습니다.

    • 2xx access log: 전체 요청 추세는 메트릭으로 보고, 로그는 샘플링합니다.
    • 4xx log: 로그인, 결제, API gateway는 보존 쪽에 무게를 둡니다. 정적 리소스 404는 샘플링 후보입니다.
    • 5xx log: 기본 보존입니다. 줄이고 싶다면 먼저 중복 메시지와 애플리케이션 재시도 폭주를 고칩니다.
    • DEBUG/TRACE: prod 기본 색인은 피합니다. 릴리스 관찰 기간에는 만료 시간을 두고 켭니다.
    • audit/security: 비용 최적화의 첫 대상이 아닙니다. 보관 기간과 접근 권한을 별도 Index로 관리합니다.
    로그 유형 추천 정책 쓰면 좋은 도구 피해야 할 접근
    Health check, readiness probe Agent에서 제외 exclude_at_match, DD_CONTAINER_EXCLUDE_LOGS 검색 UI에서만 숨기고 ingestion을 계속 두기
    정상 2xx access log 샘플링 또는 낮은 보관 기간 Index exclusion filter, log-based metric 모든 2xx를 장기 색인
    오류 5xx, panic, exception 보존 별도 Index, Error Tracking, alert 비용 급증 원인이라는 이유만으로 일괄 제외
    개발/스테이징 로그 수집 범위 제한, 짧은 보관 namespace 정책, env 태그, index routing prod와 같은 Index/retention 적용
    보안/감사 로그 별도 정책으로 보호 전용 Index, RBAC, retention 정책 일반 access log와 같은 exclusion filter 공유

    Index filter에서 특히 조심할 점은 순서입니다. 넓은 라우팅 규칙이 앞에 있으면 뒤의 세밀한 규칙은 기대처럼 작동하지 않을 수 있습니다. prod 오류 로그는 먼저 분리하고, 제외 규칙은 더 좁게 두는 편이 안전합니다.

    7. Estimated Usage Metrics로 변화 감시하기

    비용 최적화는 적용보다 검증이 더 중요합니다. 저는 변경 전후에 아래 메트릭을 대시보드에 올려 둡니다.

    datadog.estimated_usage.logs.ingested_bytes
    datadog.estimated_usage.logs.ingested_events
    datadog.estimated_usage.logs.truncated_count
    datadog.estimated_usage.containers
    datadog.estimated_usage.hosts
    datadog.estimated_usage.metrics.custom

    최근 Metric Name Pricing을 쓰는 조직은 datadog.estimated_usage.metrics.custom 대신 datadog.estimated_usage.metrics.points.ingested, datadog.estimated_usage.metrics.points.indexed, datadog.estimated_usage.billable.metrics 같은 메트릭을 확인해야 할 수 있습니다. 계약과 과금 모델에 따라 보이는 지표가 달라질 수 있으니 Plan & Usage 화면과 함께 검증하세요.

    로그 사용량 메트릭은 service, status, datadog_index, datadog_is_excluded 같은 태그와 함께 볼 수 있습니다. 이 태그들이 비어 있거나 N/A로 보이면 라우팅이나 태깅이 기대대로 되지 않는다는 신호일 수 있습니다.

    모니터는 절대값보다 변화율에 먼저 둡니다. 계약과 트래픽이 조직마다 달라서 “몇 GB 이상이면 문제” 같은 숫자를 박아 넣는 건 오히려 위험합니다. 대신 운영 이벤트와 연결해서 봅니다.

    • 배포 직후 특정 service의 로그 이벤트가 급증했는가?
    • 주말이나 야간에도 env:dev 로그가 계속 색인되는가?
    • datadog_is_excluded:false 비율이 갑자기 늘었는가?
    • status:debug가 prod에서 계속 증가하는가?
    • truncated_count가 늘어 긴 로그가 잘리고 있지는 않은가?
    예시 모니터 쿼리 방향
    sum:datadog.estimated_usage.logs.ingested_events{service:api,env:prod,status:debug}.as_count()
    
    예시 대시보드 분해 기준
    sum:datadog.estimated_usage.logs.ingested_bytes{*} by {service,env,datadog_index,datadog_is_excluded}

    Estimated Usage Metrics는 청구 금액 계산기가 아니라 조기 경보 장치로 쓰는 게 맞습니다. 최종 비용은 계약, 제품별 과금 기준, 보관 정책에 따라 달라지므로 숫자를 단정하지 마세요.

    Datadog 사용량 대시보드에서 비용 최적화 결과를 확인하는 화면

    전후 비교는 총량보다 service, env, team, datadog_index별 변화로 보는 쪽이 실무적입니다. 총량만 보면 트래픽 증가와 낭비 증가를 구분하기 어렵습니다.

    8. 재현 가능한 시나리오: DEBUG 로그가 prod 비용을 밀어 올릴 때

    실제로 자주 보는 장면입니다. 신규 릴리스 후 service:api에서 DEBUG 로그가 켜진 채로 prod에 올라갑니다. 장애는 없는데 로그 색인량만 계속 늘고, 개발팀은 “문제 생기면 보려고 켜둔 것”이라고 말합니다. 이때 바로 DEBUG를 전부 버리면 또 다른 문제가 생깁니다.

    1. Log Explorer에서 env:prod service:api status:debug로 검색합니다.
    2. 상위 message pattern을 확인합니다. 같은 메시지가 반복되면 코드 경로를 찾습니다.
    3. trace_id, request_id, user_id 같은 고카디널리티 필드가 불필요하게 facet이나 태그로 승격됐는지 봅니다.
    4. 릴리스 관찰 목적이면 종료 시각을 정하고, 그 이후에는 INFO 이상으로 되돌립니다.
    5. 즉시 비용 방어가 필요하면 Index exclusion filter로 DEBUG 색인을 줄이되, ERROR/WARN과 특정 릴리스 태그는 보존합니다.
    6. 사후에는 배포 파이프라인에 prod 로그 레벨 검증을 넣습니다.

    애플리케이션 쪽에서 막을 수 있다면 그게 제일 좋습니다. 예를 들어 Kubernetes 배포에 로그 레벨 환경 변수를 명시하고, prod에서 DEBUG가 들어가지 않게 리뷰 규칙을 둡니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: api
    spec:
      template:
        metadata:
          labels:
            app.kubernetes.io/version: "1.2.3"
        spec:
          containers:
            - name: api
              image: registry.example.com/api:stable
              env:
                - name: LOG_LEVEL
                  value: INFO
                - name: DD_SERVICE
                  value: api
                - name: DD_ENV
                  value: prod
                - name: DD_VERSION
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.labels['app.kubernetes.io/version']

    운영에서 “DEBUG는 무조건 나쁘다”는 식으로 접근하면 팀이 우회합니다. 제가 선호하는 정책은 기본은 INFO, DEBUG는 티켓과 만료 시간을 가진 예외입니다. 이거 진짜 편하더라고요. 비용도 줄고, 필요한 디버그 관찰도 정당하게 할 수 있습니다.

    9. 커스텀 메트릭과 태그 카디널리티 관리

    Datadog 비용 최적화에서 로그만 보고 끝내면 절반만 본 겁니다. 커스텀 메트릭은 태그 조합이 늘어날수록 관리가 어려워집니다. 특히 user_id, request_id, session_id, pod_uid 같은 값이 태그로 들어가면 카디널리티가 급격히 커질 수 있습니다.

    제가 보는 기준은 단호합니다. 집계해서 의사결정하지 않을 값은 메트릭 태그로 올리지 않습니다. 로그 필드로 남기는 것과 메트릭 태그로 승격하는 것은 비용과 쿼리 성능 면에서 다른 선택입니다.

    필드 메트릭 태그 적합성 이유 대안
    env 높음 집계와 알림 분리에 계속 사용됩니다. 공통 태그로 유지
    service 높음 소유권과 장애 범위를 나누는 기본 축입니다. APM, 로그, 메트릭 모두 동일 명칭 사용
    team 중간~높음 비용 귀속과 운영 책임에 유용합니다. 조직 개편 시 매핑 관리
    user_id 낮음 고카디널리티가 되기 쉽습니다. 로그 필드 또는 샘플링 이벤트로 유지
    request_id 낮음 거의 모든 요청마다 값이 달라집니다. trace/log correlation에 사용

    StatsD나 DogStatsD를 쓰는 서비스라면 코드 리뷰에서 태그 추가를 가볍게 보지 마세요. 새 태그 하나가 대시보드 필터를 편하게 만들 수도 있지만, 반대로 비용과 쿼리 성능을 계속 압박할 수도 있습니다.

    # 피하고 싶은 예: request_id가 태그로 들어감
    statsd.increment('checkout.payment_attempt', tags=[
        'env:prod',
        'service:checkout',
        f'request_id:{request_id}'
    ])
    
    # 더 나은 예: 집계 가능한 축만 태그로 사용
    statsd.increment('checkout.payment_attempt', tags=[
        'env:prod',
        'service:checkout',
        f'payment_method:{payment_method}',
        f'result:{result}'
    ])

    10. APM과 Trace: 샘플링을 비용 절감 버튼처럼만 보지 않기

    APM 비용이 부담될 때 흔한 반응은 “샘플링 낮추자”입니다. 그런데 trace는 로그보다 더 조심해야 합니다. 낮은 샘플링은 비용을 줄이지만, 희귀한 장애 경로와 긴 꼬리 지연을 놓칠 수 있습니다.

    제가 쓰는 선택 기준은 이렇습니다.

    • 트래픽은 많고 정상 경로가 반복적: 샘플링을 낮추고 RED 지표는 메트릭으로 봅니다.
    • 오류율은 낮지만 비즈니스 영향이 큼: 오류 trace와 특정 service/resource는 더 오래 보존합니다.
    • 릴리스 직후: 일정 시간 샘플링을 넓히고, 안정화 뒤 기본값으로 되돌립니다.
    • 비용 급증: span tag에 고카디널리티 값이 붙었는지, 새 instrumentation이 과도한 span을 만들었는지 먼저 봅니다.

    즉 APM 최적화의 핵심은 “적게 남기기”가 아니라 희귀하지만 중요한 경로를 보존하고, 반복적인 정상 경로를 얇게 보는 것입니다.

    11. 트러블슈팅: 최적화 뒤 관측성이 깨지는 대표 원인

    비용 최적화 후 “로그가 안 보여요”가 나오면 대부분 아래 원인 중 하나입니다. 겉으로는 비슷해 보여도 근본 원인이 다르므로 확인 순서를 정해두는 게 좋습니다.

    증상 가능한 원인 확인 방법 조치
    특정 Pod 로그가 전혀 안 보임 container exclude 규칙 또는 annotation 이름 불일치 agent configcheck, Agent 로그, Pod container name 확인 annotation의 컨테이너명과 실제 container name을 맞춥니다.
    로그는 들어오는데 검색이 안 됨 Index routing 또는 exclusion filter에 걸림 datadog_index, datadog_is_excluded 태그 확인 Index 순서와 필터 쿼리를 좁힙니다.
    오류 로그까지 줄어듦 Agent exclude 패턴이 너무 넓음 변경된 regex와 샘플 로그 비교 ERROR/WARN 예외를 먼저 분리하거나 Agent 제외 대신 Index 정책으로 옮깁니다.
    비용은 줄었는데 대시보드가 비어 보임 로그 기반 메트릭 또는 facet 의존성 누락 대시보드 쿼리의 데이터 소스 확인 로그를 줄이기 전 필요한 메트릭을 별도로 생성합니다.
    변경했는데 효과가 없음 Agent 재시작 누락, DaemonSet 미롤아웃, 다른 설정 경로 우선 rollout status, agent status 롤아웃과 적용된 최종 설정을 확인합니다.

    Agent 설정을 바꾼 뒤에는 아래 순서로 확인합니다.

    sudo systemctl restart datadog-agent
    sudo datadog-agent configcheck
    sudo datadog-agent status
    sudo journalctl -u datadog-agent --since '10 minutes ago' --no-pager

    Kubernetes라면 DaemonSet 롤아웃과 Agent Pod 로그를 같이 봅니다.

    kubectl -n datadog rollout status daemonset/datadog-agent
    kubectl -n datadog get pods -l app=datadog-agent -o wide
    kubectl -n datadog logs daemonset/datadog-agent --tail=200

    12. 운영 체크리스트: 한 번 줄이고 끝내지 않기

    Datadog 비용은 한 번 정리해도 다시 자랍니다. 새 서비스, 새 로그 필드, 새 대시보드, 새 tracer가 계속 들어오기 때문입니다. 그래서 저는 월 1회 또는 큰 릴리스 뒤에 아래 체크리스트를 돌립니다.

    • env, service, team 태그가 Usage Attribution에 충분히 반영되는가?
    • 지난 7일 또는 지난 릴리스 이후 사용량이 급증한 service가 있는가?
    • prod에서 DEBUG/TRACE 로그가 계속 색인되는가?
    • dev/staging 로그가 prod와 같은 보관 정책을 쓰고 있지 않은가?
    • Index exclusion filter 순서가 문서와 일치하는가?
    • 커스텀 메트릭 태그에 고카디널리티 값이 새로 들어오지 않았는가?
    • APM trace 샘플링이 중요한 오류 경로를 놓치지 않는가?
    • 비용 절감 후 대시보드, SLO, 알림이 정상 동작하는가?
    Datadog 비용 최적화 체크리스트 요약 인포그래픽

    체크리스트는 문서로만 두면 잘 안 봅니다. 저는 대시보드에 “이번 달 사용량 증가 Top service”와 “prod DEBUG 로그”를 같이 올려둡니다. 팀이 자기 사용량을 직접 보게 만드는 편이 중앙 플랫폼팀이 계속 말하는 것보다 훨씬 오래 갑니다.

    FAQ: Datadog 비용 최적화에서 자주 막히는 지점

    로그를 줄이면 장애 대응이 느려지지 않나요?

    무작정 줄이면 느려집니다. 그래서 error, warn, security, audit 로그는 먼저 지키고, health check, 반복 2xx access log, prod DEBUG 같은 후보부터 줄입니다. 필요한 신호를 메트릭으로 승격한 뒤 로그 색인을 줄이는 방식도 좋습니다.

    가장 먼저 손볼 곳은 어디인가요?

    대부분은 로그 색인 정책과 컨테이너 로그 수집 범위부터 보는 게 빠릅니다. 그다음 커스텀 메트릭 카디널리티, APM sampling과 retention을 봅니다. host 수를 줄이는 건 인프라 구조와 연결되어 있어 단기 비용 최적화 카드로는 조심스럽습니다.

    Agent에서 제외하는 것과 Index에서 제외하는 것 중 무엇이 더 좋나요?

    다시 볼 일이 없는 반복 로그는 Agent에서 제외하세요. 나중에 검색할 가능성이 있거나 운영 정책이 바뀔 수 있는 로그는 Datadog에 보낸 뒤 Index exclusion filter나 retention으로 다루는 편이 안전합니다.

    Usage Attribution이 팀별로 깔끔하게 안 나뉩니다. 왜 그런가요?

    계측 단계에서 태그가 빠졌거나, 제품별 사용량이 모든 태그 기준으로 완벽히 분해되지 않을 수 있습니다. 그래서 비용 보고서를 만들기 전에 공통 태그가 로그, 메트릭, trace, 컨테이너에 일관되게 들어가는지 먼저 확인해야 합니다.

    마무리: 줄일 데이터와 지킬 신호를 분리하세요

    Datadog 비용 최적화의 핵심은 “덜 보기”가 아니라 장애를 설명하는 신호만 더 선명하게 남기는 것입니다. 운영 환경이라면 Usage Attribution + 태그 표준화 + Log Index 필터를 먼저 잡으세요. 개발/스테이징 비용이 문제라면 Agent 수집 범위와 컨테이너 제외 규칙부터 손보는 게 빠릅니다.

    이럴 땐 이렇게 가면 됩니다. health check와 반복 정상 로그는 Agent에서 제외하세요. prod 오류, 보안, 감사 로그는 보존하세요. 2xx access log는 샘플링하거나 짧게 보관하세요. DEBUG 로그는 기본 비활성화하고, 필요할 때만 만료 시간을 두고 켜세요. 커스텀 메트릭에는 집계 가능한 태그만 올리세요.

    마지막으로 변경 전후를 꼭 같은 대시보드로 보세요. 감으로 줄였다고 말하지 말고, service, env, team, datadog_index별 사용량 변화로 확인하는 겁니다. 관련 글로는 로그 레벨 운영 정책, Kubernetes 관측성 태그 표준화, 클라우드 모니터링 비용 관리 글을 함께 연결하면 독자가 다음 단계로 이동하기 좋습니다.

  • [k8s] Kubernetes Liveness Probe: Readiness·Startup 차이와 설정 가이드

    [k8s] Kubernetes Liveness Probe: Readiness·Startup 차이와 설정 가이드

    Kubernetes Liveness Probe: Readiness, Startup 차이와 운영 가이드

    Kubernetes Liveness Probe를 처음 만졌을 때 가장 크게 데인 지점은, 프로브를 “상태 확인”이 아니라 “불안하면 일단 재시작” 버튼처럼 썼다는 점이었습니다. 앱이 조금 늦게 뜨기만 해도 죽은 것으로 판정했고, 결과는 뻔했죠. Pod(파드, 쿠버네티스의 최소 배포 단위)는 부팅 도중 반복 재시작했고, 실제 버그보다 운영 설정이 더 큰 장애를 만들더라고요.

    운영에서 프로브는 단순 헬스 체크가 아닙니다. 저는 이걸 장애 감지 장치보다 복구 정책 스위치에 가깝게 봅니다. Kubernetes Liveness Probe를 실패시키면 kubelet이 해당 컨테이너를 다시 시작하고, Readiness Probe가 실패하면 Service 트래픽 대상에서 빠지며, Startup Probe는 느린 기동 구간을 보호합니다. 같은 probe라도 실패 뒤에 이어지는 동작이 완전히 다르기 때문에, 이 차이를 설계하지 않고 문법만 맞추면 장애가 길어집니다.

    이번 글에서는 홈랩과 실무에서 반복해서 검증했던 기준으로, 언제 Liveness를 써야 하는지, 언제 오히려 빼는 게 나은지, Readiness를 어디까지 엄격하게 볼지, Startup Probe를 어떤 식으로 계산해 붙일지를 실행 가능한 예시와 함께 정리해보겠습니다. 배포 전략이나 Service 동작 원리가 헷갈린다면 블로그의 관련 글도 함께 읽어보시면 흐름이 더 잘 잡힙니다.

    Kubernetes Liveness Probe를 포함한 프로브 전체 흐름 아키텍처 이미지

    liveness, readiness, startup probe가 각각 어떤 순간에 동작하고 서비스 트래픽에 어떤 영향을 주는지 보여주는 개요 다이어그램입니다.

    Kubernetes Liveness Probe가 왜 중요한가

    Liveness Probe(라이브니스 프로브, 생존 확인)는 “이 프로세스를 계속 살려둘 가치가 있는가”를 묻습니다. 핵심은 회복 불가능성입니다. 요청이 일시적으로 느린 상태, DB가 잠깐 흔들린 상태, 외부 API가 timeout 나는 상태는 대개 liveness의 대상이 아니거든요. 반대로 이벤트 루프가 멈췄다거나, 스레드 데드락으로 더 이상 요청을 처리할 수 없고 자체 회복도 기대하기 어렵다면 liveness를 실패시켜 재기동시키는 편이 낫습니다.

    Readiness Probe(레디니스 프로브, 요청 처리 준비 상태 확인)는 질문이 다릅니다. 지금 이 Pod로 트래픽을 보내도 되나?를 판단하죠. 살아는 있지만 아직 준비가 안 됐거나, 의존 서비스가 불안정해 새 요청을 받으면 실패율만 올릴 상황이라면 readiness를 false로 두고 엔드포인트에서 빠지는 쪽이 안전합니다.

    Startup Probe(스타트업 프로브, 초기 기동 확인)는 더 실무적입니다. 느리게 뜨는 앱에게 “운영 중 기준”을 너무 일찍 들이대지 않게 해 줍니다. JVM 앱, 대용량 캐시를 적재하는 API, 마이그레이션 이후에만 정상 동작하는 서비스는 startup probe가 없으면 정상 기동 중에도 liveness에 맞아 죽기 쉽습니다.

    현장에서 가장 자주 보는 오해는 이것입니다. “헬스 체크는 엄격할수록 좋다.” 실제 운영은 반대인 경우가 많습니다. 프로브는 엄격함보다 의미 분리가 더 중요합니다. Liveness는 재시작을 정당화할 수 있을 때만, Readiness는 트래픽 차단이 이득일 때, Startup은 초기화 변동 폭을 흡수할 때 써야 합니다.

    Kubernetes Liveness Probe와 Probe 3종 역할 구분표

    Probe 종류 실무 질문 실패 시 쿠버네티스 동작 넣어야 하는 경우 빼거나 약하게 둬야 하는 경우
    Liveness Probe 프로세스가 회복 불가능하게 멈췄는가 컨테이너 재시작 deadlock, event loop 정지, 내부 워커 hang 외부 의존성 장애에 자주 흔들리는 앱, 자체 hang 가능성이 낮은 단순 API
    Readiness Probe 지금 요청을 받아도 되는가 Service 엔드포인트에서 제외 DB 연결 전, 캐시 워밍업 중, 소비자 초기화 전, 배포 중 drain 필요 상태 판단에 무거운 쿼리나 외부 API 호출이 필요한 경우
    Startup Probe 초기 부팅이 아직 끝나지 않았는가 기동 구간 보호, 실패 지속 시 재시작 JVM, 모델 로딩, schema migration, 큰 플러그인 초기화 기동 시간이 매우 짧고 변동 폭이 작은 앱

    운영 판단을 더 거칠게 요약하면 아래처럼 보시면 됩니다.

    • Liveness: 계속 두면 더 나빠질 프로세스만 다시 띄웁니다.
    • Readiness: 살아 있어도 지금은 손님 받지 말자는 신호입니다.
    • Startup: 부팅 중인 앱을 운영 기준으로 성급하게 심판하지 않기 위한 완충 장치입니다.

    이 차이를 놓치면 증상이 묘해집니다. Pod는 Running인데 요청은 503이 쌓이거나, DB가 잠깐 느려졌을 뿐인데 liveness가 재시작 루프를 만들어 장애 반경을 넓혀 버리죠. 프로브 설계의 핵심은 “정상/비정상 판정”보다 실패 도메인을 어디서 끊을지에 있습니다.

    Kubernetes 헬스 체크 방식도 성격이 다릅니다

    Kubernetes에서는 보통 HTTP GET, TCP Socket, Exec 세 방식으로 검사합니다. 셋 다 쓸 수 있다고 해서 셋 다 좋은 건 아닙니다. 중요한 건 무엇을 검증하고 무엇을 버리는지를 알고 선택하는 겁니다.

    • HTTP GET: 가장 해석이 쉽습니다. <code>/live, /ready처럼 의미를 분리하기 좋고, 애플리케이션 레벨 판단이 가능합니다.
    • TCP Socket: 포트가 연결 가능한지만 확인합니다. 포트는 열려 있는데 실제 요청 처리는 불안정한 상황은 못 잡을 수 있습니다.
    • Exec: 컨테이너 안에서 명령을 실행하므로 유연하지만, 프로세스 생성 비용과 스크립트 실패 모드까지 같이 관리해야 합니다.

    웹 애플리케이션이라면 대체로 Readiness는 HTTP, Liveness는 아주 가볍고 보수적으로, Startup은 실제 기동 시간을 기준으로 별도 구성하는 쪽을 권합니다. 반대로 배치 워커처럼 HTTP 서버가 없고, 작업 큐 polling 상태나 pid 파일 확인이 더 직접적인 경우에는 exec probe가 더 맞을 수 있습니다.

    중요한 판단 하나만 더 짚으면, Liveness에서 외부 의존성을 확인하는 순간 프로브는 자가치유 장치가 아니라 장애 증폭 장치가 되기 쉽습니다. DB, Redis, Kafka, 외부 API는 readiness 쪽에서 다루고, liveness는 프로세스 내부 생존성만 보는 편이 운영 사고가 적었습니다.

    실전 구현: Deployment에 Liveness, Readiness, Startup Probe 넣기

    재현 가능한 예시로 가보겠습니다. 아래 YAML은 HTTP 엔드포인트를 분리한 애플리케이션 기준입니다. 데모용 구조지만 실무에서도 그대로 많이 씁니다. 포인트는 세 가지입니다. startup은 부팅 보호, readiness는 트래픽 허용 판단, liveness는 내부 고장 감지입니다.

    1. 애플리케이션에서 /live와 /ready를 분리합니다.
    2. /ready는 요청 처리에 필요한 최소 의존성이 준비됐을 때만 200을 반환하게 합니다.
    3. /live는 외부 의존성을 빼고, 프로세스가 hang 상태인지 중심으로 판단합니다.
    4. 기동 시간이 흔들리는 앱이면 startup probe로 운영 구간 판정을 늦춥니다.
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: probe-demo
    spec:
      replicas: 2
      selector:
        matchLabels:
          app: probe-demo
      template:
        metadata:
          labels:
            app: probe-demo
        spec:
          containers:
            - name: app
              image: your-registry/your-app:1.0.0
              ports:
                - containerPort: 8080
              startupProbe:
                httpGet:
                  path: /live
                  port: 8080
                periodSeconds: 5
                timeoutSeconds: 2
                failureThreshold: 24
              readinessProbe:
                httpGet:
                  path: /ready
                  port: 8080
                periodSeconds: 5
                timeoutSeconds: 2
                failureThreshold: 3
                successThreshold: 1
              livenessProbe:
                httpGet:
                  path: /live
                  port: 8080
                periodSeconds: 10
                timeoutSeconds: 2
                failureThreshold: 3

    여기서 많이 놓치는 계산이 있습니다. startupProbe의 허용 시간은 보통 failureThreshold * periodSeconds로 먼저 읽으면 편합니다. 위 예시는 5초마다 검사하고 24번까지 실패를 허용하니, 대략 120초까지는 기동 보호 구간이라고 해석할 수 있습니다. 앱이 어떤 날은 15초, 어떤 날은 80초 걸린다면 initialDelaySeconds 하나로 버티는 것보다 startup probe가 훨씬 안전합니다.

    startupProbe, readinessProbe, livenessProbe가 YAML 안에서 어떤 식으로 연결되고 순서대로 동작하는지 보여주는 구성 이미지입니다.

    적용 후에는 아래 순서로 보는 게 제일 빠릅니다. 배포, 상태 변화, 이벤트, 직전 로그를 묶어서 확인해야 원인이 잘 보입니다.

    kubectl apply -f probe-demo.yaml
    kubectl rollout status deployment/probe-demo
    kubectl get pods -l app=probe-demo -w
    kubectl describe pods -l app=probe-demo

    kubectl get pods -w는 READY와 RESTARTS 변화를 실시간으로 보기 좋고, kubectl describe pods -l app=probe-demo는 probe 실패 메시지와 kubelet 이벤트를 확인할 때 특히 유용합니다. 앱 로그만 보고 있으면 왜 재시작됐는지 놓치는 경우가 많습니다.

    Kubernetes Liveness Probe 파라미터는 이렇게 읽으시면 됩니다

    • initialDelaySeconds: 검사 시작 전 대기 시간입니다. 다만 느린 앱 보호를 이 값 하나로 해결하려 들면 운영 단계의 장애 감지가 늦어집니다.
    • periodSeconds: 검사 주기입니다. 너무 짧으면 노이즈와 부하가 늘고, 너무 길면 장애 감지가 늦습니다.
    • timeoutSeconds: 응답 대기 시간입니다. readiness가 자주 timeout 난다면 엔드포인트 자체가 무겁다는 신호일 수 있습니다.
    • failureThreshold: 연속 실패 허용 횟수입니다. 일시적 지연과 실제 장애를 구분하는 완충 장치로 보면 이해가 쉽습니다.
    • successThreshold: 연속 성공 횟수입니다. readiness의 flap을 줄일 때 의미가 있고, liveness와 startup에서는 1이어야 합니다.

    제 경험상 기동이 느린 앱에 initialDelaySeconds만 크게 주는 방식은 나중에 운영 감지를 둔하게 만듭니다. 시작 구간과 정상 운영 구간은 실패 의미가 다르기 때문에, startup probe로 분리하는 편이 더 깔끔하더라고요.

    헬스 엔드포인트는 이렇게 나누는 편이 덜 사고 납니다

    문법보다 더 중요한 건 애플리케이션이 무엇을 200으로 돌려주느냐입니다. 같은 Spring Boot나 FastAPI라도 엔드포인트 설계가 엉성하면 probe는 멀쩡해 보이는데 운영은 계속 흔들립니다. 저는 아래처럼 역할을 나누는 편을 선호합니다.

    엔드포인트 포함할 것 빼야 할 것 이유
    /live 메인 루프 정상 동작, 내부 큐 정지 여부, 프로세스 핵심 스레드 상태 DB ping, 외부 API 호출, 느린 파일 시스템 검사 재시작이 의미 있는 내부 고장만 잡아야 하기 때문
    /ready DB 연결 풀 준비, 필수 캐시 준비, 메시지 소비자 초기화 완료 비핵심 외부 연동, 과도한 상세 진단, 고비용 쿼리 트래픽 수용 가능 여부만 빠르게 판단해야 하기 때문

    예를 들어 주문 API가 있고, 요청 한 건을 처리하려면 DB 연결과 내부 캐시 로드가 필수라고 해보겠습니다. 이 경우 /ready는 DB pool이 usable인지, 필수 캐시가 준비됐는지만 보면 됩니다. 반면 분석용 외부 API가 잠깐 느린 건 새 주문 수락과 직접 관계가 없다면 readiness에 넣지 않는 편이 낫습니다. 모든 연동을 readiness에 다 넣으면 안전해 보일 수는 있어도, 실제로는 트래픽을 과하게 차단하게 됩니다.

    실무에서 많이 하는 실수 4가지

    여기서부터가 설정 문법보다 훨씬 중요합니다. 대부분의 장애는 YAML 오타보다 잘못된 운영 가정에서 나옵니다.

    1. Liveness Probe에 외부 의존성을 넣는 경우

    예를 들어 /healthz가 DB 질의를 수행하고, DB 응답이 늦으면 liveness가 실패하도록 만들면 어떻게 될까요. 앱 프로세스는 멀쩡한데 외부 의존성 장애가 컨테이너 재시작으로 번집니다. 재시작 직후 연결 폭증까지 붙으면 DB는 더 버거워지고, 결국 앱과 DB가 같이 흔들립니다. 이 패턴은 진짜 오래 사람을 괴롭히더라고요. 재시작이 복구가 아니라 증폭이 되는 겁니다.

    분리 기준은 단순합니다.

    • Liveness: 프로세스 내부 상태만 확인
    • Readiness: 요청 처리에 반드시 필요한 의존성만 확인

    2. 느린 앱에 Startup Probe 없이 Liveness만 거는 경우

    JVM, 대형 모델 로딩, 플러그인 초기화, schema migration이 있는 앱은 부팅 시간이 환경마다 다릅니다. 이때 liveness가 먼저 붙으면 정상 기동 중인 프로세스를 죽입니다. 증상은 CrashLoopBackOff로 보이는데, 실제 원인은 코드 버그보다 프로브 타이밍인 경우가 꽤 많습니다.

    권하는 방식은 먼저 실제 기동 로그를 보고, 포트 오픈 시점이 아니라 요청 처리 준비 완료 시점을 기준으로 startup budget을 잡는 겁니다. 앱이 포트를 열었다고 준비가 끝난 건 아니니까요.

    3. Readiness 엔드포인트를 너무 무겁게 만든 경우

    Readiness는 자주 호출됩니다. 여기에 비싼 SQL, 다중 외부 API 확인, 긴 디스크 검사까지 넣으면 체크 자체가 시스템 부하가 됩니다. 더 나쁜 경우는 readiness endpoint가 느려서 timeout 나고, 그 timeout 때문에 엔드포인트에서 빠졌다가 붙었다가를 반복하는 겁니다. 서비스는 살아 있는데 트래픽 경로만 흔들리죠.

    실무에서는 readiness를 빠르고, 결정적이며, 요청 처리 가능 여부만 말하는 엔드포인트로 두는 게 안정적입니다.

    4. probe 실패 로그를 앱 로그만으로 해석하는 경우

    Pod가 재시작하면 애플리케이션 로그부터 보는 경우가 많습니다. 그런데 probe 실패의 1차 단서는 대개 kubelet 이벤트에 있습니다. “왜 죽였는지”는 kubectl describe pod에 더 잘 남습니다. 재시작된 후 현재 컨테이너 로그만 보면 이미 중요한 구간이 사라졌을 수도 있고요.

    kubectl describe pod <pod-name>
    kubectl logs <pod-name> --previous
    kubectl get events --sort-by=.metadata.creationTimestamp

    --previous는 직전 컨테이너 로그를 보기에 특히 중요합니다. probe 때문에 재시작된 직후에는 현재 로그가 너무 깨끗해서 오히려 단서가 안 남는 경우가 많습니다.

    트러블슈팅: CrashLoopBackOff가 probe 때문인지 확인하는 방법

    재현 가능한 시나리오 하나를 잡아보겠습니다. 앱이 시작하면서 DB migration을 수행하고, HTTP 포트는 먼저 뜨지만 실제 요청 처리는 migration 완료 후에만 가능하다고 해보죠. 이때 liveness가 너무 빨리 붙으면, Kubernetes는 애플리케이션이 아직 기동 중인데도 불안정하다고 판단해 재시작시킬 수 있습니다. 문제는 재시작하면 migration이 다시 시작된다는 점입니다. 이 루프가 반복되면 코드가 아니라 프로브가 장애 원인입니다.

    1. Pod 상태 확인: kubectl get pods -w에서 RESTARTS가 계속 증가하는지 봅니다.
    2. 이벤트 확인: kubectl describe pod에서 Liveness probe failed, Readiness probe failed, Startup probe failed 메시지를 찾습니다.
    3. 직전 로그 확인: kubectl logs --previous로 종료 직전 애플리케이션 상태를 봅니다.
    4. 기동 순서 분리: 포트 오픈 시점, migration 완료 시점, ready 응답 가능 시점을 로그로 나눠 읽습니다.
    5. Startup Probe 추가 또는 조정: 준비 완료 전까지 liveness 간섭을 차단합니다.

    이때 숫자를 감으로 찍지 말고, 실제 이벤트와 로그 타임라인을 맞춰 보셔야 합니다. 앱의 “실제 준비 완료”와 Kubernetes가 “준비됐다고 믿는 시점”이 어긋날 때 문제가 생깁니다. 먼저 그 간격을 맞춘 뒤 threshold를 조정해야 덜 헤맵니다. 순서가 반대면 결국 숫자 놀음이 되더라고요.

    kubectl get pod <pod-name> -o wide
    kubectl describe pod <pod-name> | sed -n '/Events:/,$p'
    kubectl logs <pod-name> --previous --timestamps
    kubectl logs <pod-name> --timestamps

    여기서 볼 포인트는 단순합니다. 이벤트 시각과 앱 로그 시각을 맞춰서, probe 실패가 먼저였는지, 앱 내부 오류가 먼저였는지를 분리하는 겁니다. 순서를 잘못 읽으면 원인과 결과를 바꿔 잡게 됩니다.

    검증: 설정 후 무엇을 보고 정상이라고 판단할까

    설정을 넣는 것과 운영이 안정화되는 것은 별개입니다. 배포 직후에는 아래 네 가지를 같이 보는 편이 좋습니다. 하나만 보면 착시가 생깁니다.

    • 배포 직후 READY 변화: Running보다 READY가 중요합니다. Running인데 READY가 0/1이면 서비스는 사실상 트래픽을 못 받습니다.
    • 엔드포인트 반영: readiness 실패 시 실제로 Service 엔드포인트에서 빠지는지 확인해야 합니다.
    • RESTARTS 안정화: 정상 상태에 들어간 뒤 RESTARTS가 더 늘지 않아야 합니다.
    • 이벤트 노이즈 여부: 간헐적 probe failure가 계속 쌓인다면, 배포 피크나 노드 부하 때 실제 장애로 이어질 수 있습니다.
    kubectl rollout status deployment/probe-demo
    kubectl get pods -l app=probe-demo -o wide
    kubectl get endpoints
    kubectl describe deployment probe-demo

    검증 기준을 더 분명하게 두자면 이렇습니다. READY는 기대 복제 수만큼 올라와야 하고, RESTARTS는 안정 구간에서 멈춰야 하며, endpoints에는 준비된 Pod만 남아야 합니다. Pod가 떠 있다는 사실만으로 정상 판정을 내리면 실제 장애를 놓치기 쉽습니다.

    Kubernetes Liveness Probe 검증과 Pod 상태 확인 이미지

    Pod 상태, 이벤트 로그, readiness 반영 여부를 함께 보는 검증 흐름 이미지입니다.

    운영 팁: 앱 유형별로 probe를 다르게 잡아야 합니다

    앱 유형 추천 구성 이유 보수적으로 볼 포인트
    가벼운 웹 API Readiness + 보수적 Liveness 기동이 빠르고 요청 처리 여부 판단이 단순함 Liveness가 굳이 필요 없는 서비스도 적지 않음
    기동이 느린 JVM 앱 Startup + Readiness + Liveness 초기화 시간 보호와 운영 구간 분리가 필요함 initialDelaySeconds 하나로 해결하려 하지 않기
    배치 워커 Exec 또는 최소 Liveness HTTP 엔드포인트가 없을 수 있음 작업 큐 적체를 liveness 실패로 바로 연결하지 않기
    외부 의존성이 많은 앱 Readiness 강화, Liveness 단순화 장애 전파를 재시작 루프로 만들지 않기 위함 핵심 의존성만 readiness에 포함하기

    혹시 “애플리케이션은 분명 떠 있는데 로드밸런서 뒤에서만 실패하는” 경험이 있었다면, 대체로 readiness가 너무 느슨하거나 너무 무거운 경우가 많습니다. 카프카 소비자가 붙은 앱에서도 비슷한 장면이 자주 나옵니다. HTTP 포트는 빨리 떠도 소비자 그룹 조인이 끝나기 전엔 실제 처리 품질이 불안정할 수 있거든요. 그때 /ready를 소비자 초기화 완료 이후에만 true로 바꾸면 배포 중 실패율이 꽤 줄어듭니다. 이거 실무에서 진짜 편하더라고요.

    자주 묻는 질문

    Readiness만 있으면 Liveness는 없어도 될까요?

    가능합니다. 이건 의외로 많은 팀이 과하게 쓰는 부분입니다. 애플리케이션이 hang 상태에 빠질 가능성이 낮고, 장애 시 프로세스가 명확하게 종료되며, readiness만으로도 트래픽 차단이 충분하다면 liveness를 아예 두지 않는 선택도 실무적으로 타당합니다. Liveness는 필수 옵션이 아니라 재시작이 실제 복구 수단일 때만 가치가 있습니다.

    TCP probe면 충분하지 않나요?

    정말 단순한 서비스라면 됩니다. 하지만 대부분의 웹 애플리케이션은 “포트가 열려 있다”와 “요청을 정상 처리할 수 있다”가 다릅니다. 그래서 운영 해석 가능성을 높이려면 HTTP 기반 엔드포인트 분리가 더 낫습니다.

    헬스 체크 엔드포인트는 하나로 합쳐도 될까요?

    작게 시작할 때는 가능합니다. 다만 운영 기간이 길어질수록 역할 분리가 이깁니다. /live, /ready를 분리하면 장애 원인과 후속 조치가 선명해집니다. 최소한 readiness와 liveness는 분리하는 쪽을 권합니다.

    Kubernetes Liveness Probe 선택 기준 요약 인포그래픽

    앱 유형별로 어떤 probe 조합을 선택하면 좋은지 한눈에 보는 요약 이미지입니다.

    마무리: Kubernetes Liveness Probe는 이렇게 고르면 덜 흔들립니다

    Kubernetes Liveness Probe는 만능 안정화 버튼이 아닙니다. 잘못 걸면 재시작이 복구가 아니라 장애 확산 경로가 됩니다. 반대로 Readiness Probe를 제대로 설계하면 준비 안 된 Pod로 트래픽이 들어가는 문제를 깔끔하게 줄일 수 있고, Startup Probe는 느린 기동 서비스를 불필요한 CrashLoopBackOff에서 지켜줍니다.

    기준은 꽤 분명합니다. 가벼운 API라면 Readiness를 먼저 정확히 만들고, Liveness는 꼭 필요한 경우에만 보수적으로 추가하세요. 느리게 뜨는 앱이라면 Startup Probe를 분리하세요. 외부 의존성이 많은 서비스라면 Liveness는 단순하게 두고 Readiness에서 트래픽 차단을 설계하세요. 운영 안정성은 “많이 검사하는 것”보다 무엇을 실패시켰을 때 어떤 후속 조치가 일어나는지를 정확히 설계할 때 올라갑니다.