[쿠버네티스 패턴] 17장 Adapter 패턴

bocopile·2025년 11월 27일

쿠버네티스 패턴

목록 보기
15/28
post-thumbnail

1. Adapter 패턴이란?

1) 한 줄 정의

Adapter 패턴은 여러 언어·프레임워크로 작성된 컨테이너 애플리케이션을

“외부에서 볼 때는 하나의 표준 인터페이스처럼” 보이게 만드는 패턴입니다.

2) 상세 설명

Adapter 패턴은 Sidecar 패턴의 한 종류이지만, 목적이 매우 명확합니다.

복잡하고 제각각인 애플리케이션 내부를 숨기고,

외부 시스템(모니터링, 로깅 등)이 소비할 수 있는 통일된 형식으로 바꿔 주는 것

즉, 컨테이너 안에서 어떤 언어를 사용하든, 어떤 방식으로 로그·메트릭을 남기든 상관없이

Adapter 컨테이너가 대신 데이터를 읽고, 변환하며, 표준 포맷으로 내보내는 역할을 합니다.


2. 왜 Adapter 패턴이 필요한가? (문제 상황)

1) 현대 시스템의 다양성

요즘 시스템 구조는 보통 다음과 같은 형태를 띱니다.

  • 팀마다 사용하는 언어가 다릅니다. (Java, Python, Node.js, Go, …)
  • 프레임워크, 라이브러리, 로깅/메트릭 방식도 제각각입니다.
  • 하지만 모니터링 툴은 하나의 통일된 인터페이스를 원합니다.
    • 예: Prometheus, Datadog, New Relic 등은 일정한 형식/프로토콜을 기대합니다.

2) 모니터링 관점의 문제

예를 들어 다음과 같은 상황이 있을 수 있습니다.

  • 어떤 서비스는 HTTP로 메트릭을 내보냅니다.
  • 어떤 서비스는 파일에만 로그를 남깁니다.
  • 어떤 서비스는 아예 메트릭을 내보내지 않기도 합니다.

이 상태에서 “전체 시스템을 하나의 통합된 모니터링 뷰로 보고 싶다”라는 요구가 들어오면,

  • 각 서비스가 모니터링 시스템에 맞는 SDK를 붙이고,
  • 포맷을 변경하며,
  • HTTP 엔드포인트까지 새로 열어야 합니다.

레거시 서비스가 많을수록 이는 사실상 불가능에 가까운 작업이 됩니다.

3) Adapter 패턴의 역할

이때 애플리케이션 코드를 직접 수정하지 않고 문제를 해결할 수 있는 방법이 바로 Adapter 패턴입니다.

  • 기존 애플리케이션은 그대로 둔 채,
  • 별도의 Adapter 컨테이너를 붙여서,
  • 외부에서 요구하는 통일된 모니터링 인터페이스를 제공하도록 만드는 방식입니다.

3. Adapter 패턴의 핵심 아이디어

Adapter 패턴의 핵심 아이디어는 매우 단순합니다.

각 Pod에 Adapter 컨테이너를 하나 더 붙이고,
이 컨테이너가 애플리케이션의 커스텀 데이터(로그, 메트릭 등)를 읽어서 외부 시스템이 이해할 수 있는 표준 포맷으로 변환해 줍니다.

1) 구성 요소

조금 더 구체적으로 구성 요소를 나눠 보면 다음과 같습니다.

  1. 애플리케이션 컨테이너
    • 각자 기존 방식대로 로그·메트릭을 남깁니다.
    • 예: 파일 /logs/random.log에 랜덤 숫자와 수행 시간을 기록.
  2. Adapter 컨테이너 (Sidecar)
    • 애플리케이션이 남긴 데이터를 공유 볼륨을 통해 읽습니다.
    • 로그/메트릭 포맷을 파싱하여 Prometheus 등 외부 도구가 이해하는 포맷으로 변환합니다.
    • 변환된 결과를 HTTP 엔드포인트로 제공합니다.
  3. 외부 모니터링 툴
    • 각 Pod의 Adapter 컨테이너로 HTTP 요청을 보내 메트릭을 수집합니다.
    • 모든 서비스가 같은 프로토콜과 포맷을 사용하므로, 시스템 전체가 균일하게 보입니다.

2) 정리

  • 내부: 각 서비스는 제각각 로그·메트릭을 남깁니다.
  • Pod 내부: Adapter 컨테이너가 이를 읽어 표준 포맷으로 변환합니다.
  • 외부: 모니터링 시스템은 어디서 오든 동일한 포맷의 메트릭만 바라보면 됩니다.

4. 랜덤 숫자 생성기 + Prometheus 예제

책에서는 랜덤 숫자 생성기 애플리케이션을 예시로 Adapter 패턴을 설명합니다.

1) 요구사항

애플리케이션은 랜덤 숫자를 생성하고, 해당 숫자를 생성하는 데 걸린 시간을 로그 파일에 남깁니다.

이 수행 시간을 Prometheus로 모니터링하고 싶습니다.

하지만

  • 현재 로그 형식은 Prometheus가 이해하는 형식이 아닙니다.
  • HTTP /metrics 같은 엔드포인트도 없습니다.

2) 설계 개요

위 요구사항을 만족하기 위해 다음과 같은 구조로 설계합니다.

  1. 메인 컨테이너 (random-generator)
    • 환경 변수 LOG_FILE=/logs/random.log를 사용합니다.
    • 랜덤 숫자를 생성할 때마다 /logs/random.log에 “생성된 숫자 + 걸린 시간(ms)” 형식으로 로그를 남깁니다.
    • /logs 디렉터리는 emptyDir 볼륨으로 정의합니다.
  2. Adapter 컨테이너 (prometheus-adapter)
    • 동일하게 환경 변수 LOG_FILE=/logs/random.log를 사용합니다.
    • /logs 디렉터리를 같은 emptyDir 볼륨으로 마운트합니다.
    • 작은 HTTP 서버를 띄워 /metrics 요청이 들어오면
      • random.log를 읽고,
      • 로그 내용을 파싱해 Prometheus 포맷으로 변환한 뒤,
      • 변환된 내용을 HTTP 응답으로 반환합니다.
  3. Prometheus 서버
    • prometheus-adapter가 제공하는 HTTP 엔드포인트(예: :8000/metrics)를
      타깃으로 스크래핑하도록 설정합니다.
    • 애플리케이션은 Prometheus를 전혀 몰라도 되고,
      Adapter가 대신 Prometheus 친화적인 인터페이스를 제공해 줍니다.

3) Deployment YAML 개념 구조

설계를 간단히 YAML로 표현하면 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: random-generator
spec:
  replicas: 1
  selector:
    matchLabels:
      app: random-generator
  template:
    metadata:
      labels:
        app: random-generator
    spec:
      volumes:
        - name: log-volume
          emptyDir: {}
      containers:
        # 메인 애플리케이션 컨테이너
        - name: random-generator
          image: your-registry/random-generator:1.0
          env:
            - name: LOG_FILE
              value: /logs/random.log
          volumeMounts:
            - name: log-volume
              mountPath: /logs

        # Prometheus Adapter 컨테이너
        - name: prometheus-adapter
          image: your-registry/prometheus-adapter:1.0
          env:
            - name: LOG_FILE
              value: /logs/random.log
            - name: PORT
              value: "8000"
          ports:
            - containerPort: 8000
              name: metrics
          volumeMounts:
            - name: log-volume
              mountPath: /logs

5. Adapter 패턴의 또 다른 활용 – 로그 정규화

로그 수집/정규화 영역에서도 Adapter 패턴은 매우 유용하게 활용될 수 있습니다.

1) 문제 상황

다음과 같은 환경을 생각해 볼 수 있습니다.

각 컨테이너가 로그를 제각각 남깁니다.

어떤 곳은 JSON 형식, 어떤 곳은 단순 텍스트,어떤 곳은 타임스탬프 형식조차 제각각입니다.

이때 중앙 로그 수집기(예: ELK, Loki, Splunk 등)는통일된 형식의 로그를 받을수록 처리와 분석이 쉬워집니다.

2) Adapter 기반 로그 정규화 흐름

이런 상황에서 Pod 안에 로그 Adapter 컨테이너를 하나 더 두고 다음과 같이 동작시킵니다.

  1. 각 애플리케이션이 남기는 다양한 형식의 로그를 읽습니다.
  2. 공통 스키마(예: 공통 JSON 구조)로 정규화합니다.
  3. Self-Awareness 패턴(Pod의 메타데이터를 알고 있음)을 활용하여,
    • Pod 이름, Namespace, 버전, Node 정보 등의 컨텍스트 정보를 추가합니다.
  4. 최종적으로, 중앙 로그 시스템(ELK, Loki 등)이 읽기 좋은 형태로 전송합니다.

이렇게 구성하면,

  • 애플리케이션은 “그냥 로그만 잘 쓰는 것”에 집중할 수 있고,
  • 로그 포맷, 컨텍스트, 전송 방식 등은 Adapter가 전담하게 됩니다.

6. Sidecar 패턴과의 관계

Adapter 패턴은 사실 Sidecar 패턴의 특수한 형태라고 볼 수 있습니다.

1) 공통점

두 패턴 모두 다음과 같은 공통점을 가집니다.

  • Pod 안에 보조 컨테이너를 함께 배치한다는 점
  • 메인 애플리케이션과 오랫동안 함께 붙어 다니면서 기능을 확장·보완한다는 점

2) 차이점 (역할/목적)

하지만 역할과 목적 측면에서는 차이가 분명합니다.

  • Sidecar 패턴
    • 주 목적: “애플리케이션을 가까이에서 도와주는 모든 부가 기능”
      (로그 수집, 설정 리로드, 동기화, 에이전트 실행 등)
  • Adapter 패턴
    • 주 목적: “복잡하고 제각각인 내부를 숨기고외부에 표준화된 인터페이스를 제공하는 것
    • 일종의 리버스 프록시 역할을 하며,
      외부에서 보면 시스템이 매우 일관된 형태로 보이게 만듭니다.

정리하면, Sidecar가 “옆에서 도와주는 친구”라면, Adapter는 “외부 세계와의 인터페이스를 대신 맡는 통역가”에 가깝습니다.


7. 언제 Adapter 패턴을 쓰면 좋은가?

다음과 같은 상황이라면 Adapter 패턴 적용을 적극적으로 고려해 볼 수 있습니다.

  1. 여러 언어/프레임워크가 섞인 시스템을 하나의 모니터링 솔루션에서 보고 싶을 때
  2. 애플리케이션 코드를 직접 수정하기 어렵거나, 모니터링·로깅 코드를 서비스 비즈니스 로직에 섞고 싶지 않을 때
  3. 표준 인터페이스(예: Prometheus 포맷, 특정 로그 스키마)를모든 서비스에 강제로 적용하기 어려운 환경일 때
  4. 기존 레거시 시스템이 내보내는 데이터를 새로운 플랫폼에서 받아야 하는 상황 (마이그레이션, 점진적 전환 등)

이러한 경우 Adapter 컨테이너 하나만 추가해도, 레거시 코드를 수정하지 않고도 표준화된 인터페이스를 확보할 수 있고전체 시스템에 대한 모니터링/로깅/통합 뷰를 훨씬 쉽게 구성할 수 있습니다.


8. v1.34 버전 기준으로 직접적으로 연관되어 있는 부분들

쿠버네티스 v1.34 기준으로 보았을 때, Adapter 패턴과 직접적으로 맞닿아 있는 기능들을

“Adapter/Sidecar를 어떻게 설계·운영할 것인가”라는 관점에서 정리해 보겠습니다.

8.1 Sidecar 컨테이너 모델을 전제로 한 설계

  • Sidecar 컨테이너 동작 모델이 안정화되면서, Adapter 컨테이너를 “사이드카”라는 정식 개념 위에 올려 설계할 수 있습니다.
  • 예를 들어 다음과 같은 아이디어가 가능합니다.
    • Adapter가 준비되기 전까지 앱 컨테이너를 시작하지 않도록 하여,메트릭/로그 수집 준비가 끝난 이후에만 트래픽을 받도록 구성합니다.
    • Adapter 컨테이너에 별도의 헬스 체크/프로브를 붙여모니터링 파이프라인 자체의 상태를 Pod 레벨에서 바로 확인할 수 있도록 합니다.

8.2 컨테이너 재시작 규칙과 Adapter

v1.34에서는 컨테이너 재시작 동작을 좀 더 세밀하게 제어할 수 있어, Adapter와 메인 컨테이너의 관계를 다음과 같이 설계할 수 있습니다.

  • “Adapter가 죽으면 Pod 전체를 재시작한다.”
    → 메트릭/로그 수집이 깨진 상태로 애플리케이션이 계속 동작하는 상황을 방지합니다.
  • “Adapter는 재시작 시도 횟수를 N번까지만 허용한다.”
    → CrashLoop로 노드에 부담을 주는 상황을 방지합니다.
  • “메인 애플리케이션이 정상 종료되면 Adapter도 자연스럽게 종료되도록 한다.”
    → Job/배치 워크로드에서 보다 깔끔한 종료 플로우를 만들 수 있습니다.

결국 “Adapter 컨테이너가 죽었을 때, 시스템 입장에서 무엇을 의미하는가?”를 먼저 정의하고,

그 의미에 맞게 재시작 전략을 함께 설계하는 것이 중요합니다.

8.3 CEL 기반 Mutating Admission Policy와 Adapter 자동 주입

v1.34에서는 CEL(공통 표현 언어) 기반 Mutating Admission Policy가 도입되어,

외부 웹훅 없이도 API 서버 내부에서 변이(Mutation) 로직을 적용할 수 있습니다.

Adapter 패턴 관점에서 보면 다음과 같은 활용이 가능합니다.

  • 특정 네임스페이스/라벨/애노테이션 조건을 만족하는 Pod에 대해
    • Adapter 관련 라벨·애노테이션을 강제 적용하거나,
    • 로그 디렉터리, 환경 변수 등 공통 설정을 자동으로 주입하도록
      정책을 선언할 수 있습니다.
  • 완전한 의미의 “컨테이너 자동 주입”은 여전히 Webhook 방식이 현실적인 경우가 많지만,
    CEL Policy를 이용해 Adapter 대상 Pod를 태깅하고 공통 설정을 강제하는 용도로 활용할 수 있습니다.

9. 참고 출처

profile
DevOps Engineer

0개의 댓글