Notice
Recent Posts
Recent Comments
Link
«   2025/12   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Archives
Today
Total
관리 메뉴

근묵자흑

Kubernetes Pattern: Ambassador 본문

k8s/kubernetes-pattern

Kubernetes Pattern: Ambassador

Luuuuu 2025. 12. 13. 19:26

Ambassador 패턴은 Kubernetes의 구조적 패턴(Structural Patterns) 중 하나로, 외부 서비스에 대한 접근을 프록시하고 복잡성을 숨기는 특수한 사이드카 컨테이너입니다. 이 패턴을 통해 메인 애플리케이션 컨테이너는 외부 의존성에 직접 접근하지 않고도 localhost를 통해 단순화된 인터페이스로 외부 서비스를 사용할 수 있습니다.

flowchart LR
    subgraph Pod["Pod"]
        direction LR
        Main["메인 애플리케이션<br/>컨테이너"]
        Ambassador["Ambassador<br/>컨테이너"]
        Main -->|"localhost:port"| Ambassador
    end

    Ambassador -->|"프록시"| External1["외부 서비스 A"]
    Ambassador -->|"프록시"| External2["외부 서비스 B"]
    Ambassador -->|"프록시"| External3["외부 서비스 C"]

    style Main fill:#4a90d9,stroke:#333,color:#fff
    style Ambassador fill:#e67e22,stroke:#333,color:#fff
    style External1 fill:#95a5a6,stroke:#333,color:#fff
    style External2 fill:#95a5a6,stroke:#333,color:#fff
    style External3 fill:#95a5a6,stroke:#333,color:#fff

Problem: 왜 Ambassador 패턴이 필요한가?

컨테이너화된 서비스는 고립되어 존재하지 않으며, 외부 서비스에 접근해야 하는 경우가 많습니다. 하지만 외부 서비스에 접근하는 것은 다음과 같은 어려움을 수반합니다.

외부 서비스 접근의 복잡성

flowchart TB
    subgraph Challenges["외부 서비스 접근 시 직면하는 문제들"]
        C1["동적으로 변하는 주소"]
        C2["클러스터 인스턴스의<br/>로드 밸런싱 필요"]
        C3["불안정한 프로토콜"]
        C4["복잡한 데이터 포맷"]
        C5["서비스 디스커버리<br/>라이브러리 필요"]
    end

    App["비즈니스 로직<br/>+<br/>외부 서비스 접근 로직"]

    C1 --> App
    C2 --> App
    C3 --> App
    C4 --> App
    C5 --> App

    App --> Problem["단일 책임 원칙 위반<br/>재사용성 저하<br/>환경별 설정 복잡"]

    style Problem fill:#e74c3c,stroke:#333,color:#fff
    style Challenges fill:#f8f9fa,stroke:#333

구체적인 문제 시나리오

시나리오 1: 환경별 캐시 서버 접근

개발 환경에서는 로컬 Redis를 사용하고, 프로덕션에서는 분산된 Redis 클러스터를 사용해야 합니다. 애플리케이션 코드가 이 복잡성을 직접 처리한다면, 환경마다 다른 클라이언트 설정과 연결 로직이 필요합니다.

flowchart LR
    subgraph Traditional["기존 방식의 문제"]
        AppCode["애플리케이션 코드"]
        AppCode --> DevConfig["개발 설정"]
        AppCode --> ProdConfig["프로덕션 설정"]
        DevConfig --> LocalRedis["로컬 Redis"]
        ProdConfig --> Cluster["Redis 클러스터"]
    end

    Problem["환경마다 다른 코드<br/>복잡한 조건문<br/>유지보수 어려움"]

    Traditional --> Problem

    style Problem fill:#e74c3c,stroke:#333,color:#fff

시나리오 2: 서비스 디스커버리

외부 서비스를 사용하려면 레지스트리에서 서비스를 조회하고 클라이언트 사이드 서비스 디스커버리를 수행해야 합니다. 이러한 라이브러리를 애플리케이션에 포함시키면 컨테이너가 무거워지고 종속성이 증가합니다.

시나리오 3: 불안정한 프로토콜 처리

HTTP와 같은 불안정한 프로토콜로 서비스를 소비할 때, 서킷 브레이커 로직, 타임아웃 설정, 재시도 로직 등이 필요합니다. 이 모든 것을 애플리케이션에 구현하면 비즈니스 로직이 인프라 관심사와 뒤섞입니다.

핵심 문제: 관심사의 분리 실패

이상적으로 컨테이너는 단일 목적을 가지고 다른 컨텍스트에서 재사용 가능해야 합니다. 하지만 비즈니스 기능을 제공하면서 동시에 외부 서비스를 특수한 방식으로 소비하는 컨테이너는 두 가지 이상의 책임을 갖게 됩니다.


Ambassador 패턴의 해결 방식

Ambassador 패턴은 외부 서비스 접근의 복잡성을 숨기고, 메인 애플리케이션 컨테이너에게 localhost를 통한 단순화된 뷰를 제공합니다.

아키텍처 개요

flowchart LR
    subgraph DevEnv["개발 환경"]
        subgraph DevPod["Pod"]
            DevApp["애플리케이션"] -->|"localhost:6379"| DevAmb["Ambassador<br/>(Local Proxy)"]
        end
        DevAmb --> LocalRedis["로컬 Redis"]
    end

    subgraph ProdEnv["프로덕션 환경"]
        subgraph ProdPod["Pod"]
            ProdApp["애플리케이션"] -->|"localhost:6379"| ProdAmb["Ambassador<br/>(Cluster Proxy)"]
        end
        ProdAmb --> Redis1["Redis Shard 1"]
        ProdAmb --> Redis2["Redis Shard 2"]
        ProdAmb --> Redis3["Redis Shard 3"]
    end

    style DevAmb fill:#e67e22,stroke:#333,color:#fff
    style ProdAmb fill:#e67e22,stroke:#333,color:#fff
    style DevApp fill:#4a90d9,stroke:#333,color:#fff
    style ProdApp fill:#4a90d9,stroke:#333,color:#fff

위 다이어그램에서 볼 수 있듯이, 애플리케이션은 항상 localhost:6379로 연결하면 됩니다. Ambassador 컨테이너가 환경에 따라 적절한 백엔드로 프록시합니다.

로그 처리 Ambassador

가장 기본적인 Ambassador 패턴 예제를 살펴보겠습니다. REST 서비스가 생성한 데이터를 로깅할 때, Ambassador가 로그 데이터를 처리합니다.

# ambassador-log-example.yaml
apiVersion: v1
kind: Pod
metadata:
  name: random-generator
  labels:
    app: random-generator
spec:
  containers:
  # 메인 애플리케이션 컨테이너: 랜덤 숫자를 생성하는 REST 서비스
  - name: main
    image: k8spatterns/random-generator:1.0
    env:
    # Ambassador와 통신하기 위한 URL - localhost 사용
    - name: LOG_URL
      value: http://localhost:9009
    ports:
    - containerPort: 8080
      protocol: TCP

  # Ambassador 컨테이너: 로그 데이터를 받아 처리
  - name: ambassador
    image: k8spatterns/random-generator-log-ambassador

이 예제의 핵심 포인트는 다음과 같습니다:

1. localhost를 통한 통신: 메인 컨테이너는 http://localhost:9009로 로그 데이터를 전송합니다. 같은 Pod 내의 컨테이너들은 네트워크 네임스페이스를 공유하므로 localhost로 서로 통신할 수 있습니다.

2. 관심사의 분리: 메인 컨테이너는 로그 데이터가 어떻게 처리되는지 알 필요가 없습니다. Ambassador가 단순히 콘솔에 출력할 수도 있고, 전체 로깅 인프라로 전달할 수도 있습니다.

3. 컨테이너 교체 용이성: Ambassador 컨테이너를 교체해도 메인 컨테이너는 수정할 필요가 없습니다. Pod 정의만 재구성하면 됩니다.


사이드카 패턴 패밀리 비교

Ambassador 패턴을 이해하기 전에, 사이드카 패턴 패밀리를 비교해보면 각 패턴의 역할이 명확해집니다.

flowchart TB
    subgraph Sidecar["Sidecar 패턴"]
        direction LR
        S_Main["메인 컨테이너"] <-->|"기능 확장"| S_Side["사이드카 컨테이너"]
    end

    subgraph Adapter["Adapter 패턴"]
        direction RL
        A_External["외부 시스템<br/>(Prometheus 등)"] -->|"표준 형식 요청"| A_Adapter["Adapter<br/>컨테이너"]
        A_Adapter -->|"변환된 데이터"| A_Main["메인 컨테이너"]
    end

    subgraph Ambassador["Ambassador 패턴"]
        direction LR
        Amb_Main["메인 컨테이너"] -->|"localhost"| Amb_Proxy["Ambassador<br/>컨테이너"]
        Amb_Proxy -->|"프록시"| Amb_External["외부 서비스"]
    end

    style S_Side fill:#27ae60,stroke:#333,color:#fff
    style A_Adapter fill:#9b59b6,stroke:#333,color:#fff
    style Amb_Proxy fill:#e67e22,stroke:#333,color:#fff
패턴 역할 프록시 방향 주요 사용 사례
Sidecar 메인 컨테이너 기능 확장 양방향 협력 로그 수집, 설정 동기화
Adapter 이기종 시스템 출력 표준화 외부 → 내부 (Reverse Proxy) 메트릭 변환, 로그 포맷 변환
Ambassador 외부 서비스 접근 단순화 내부 → 외부 (Client Proxy) 서비스 디스커버리, 캐시 접근

Native Sidecar 컨테이너

Kubernetes 1.28에서 알파로 도입되고 1.29에서 베타로 기본 활성화된 Native Sidecar 컨테이너는 Ambassador 패턴의 구현을 개선합니다.

기존 방식의 문제점

기존에는 사이드카 컨테이너를 일반 컨테이너(spec.containers)로 정의했습니다. 이 방식에는 몇 가지 문제가 있었습니다.

sequenceDiagram
    participant IC as Init Container
    participant Main as 메인 컨테이너
    participant Sidecar as 사이드카<br/>(기존 방식)

    Note over IC,Sidecar: 기존 방식의 문제점

    IC->>IC: 완료될 때까지 실행
    IC-->>Main: Init 완료 후 시작
    Main->>Main: 시작
    Sidecar->>Sidecar: 동시 시작

    Note over Main,Sidecar: 문제 1: 사이드카가 준비되기 전에<br/>메인이 시작될 수 있음

    Main->>Main: 완료
    Sidecar->>Sidecar: 계속 실행 중...

    Note over Main,Sidecar: 문제 2: 메인이 종료되어도<br/>사이드카가 Pod 종료를 막음

Native Sidecar의 해결책

Native Sidecar는 initContainersrestartPolicy: Always를 설정하여 정의합니다.

# native-sidecar-ambassador.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-ambassador
spec:
  # Native Sidecar는 initContainers에 정의
  initContainers:
  # Ambassador 컨테이너를 Native Sidecar로 정의
  - name: ambassador-proxy
    image: envoyproxy/envoy:v1.28-latest
    restartPolicy: Always  # 이것이 Native Sidecar의 핵심
    ports:
    - containerPort: 10000
      name: proxy
    # startupProbe로 메인 컨테이너 시작 전 준비 완료 보장
    startupProbe:
      httpGet:
        path: /ready
        port: 9901
      initialDelaySeconds: 2
      periodSeconds: 5
      failureThreshold: 10

  # 메인 애플리케이션 컨테이너
  containers:
  - name: main-app
    image: my-application:1.0
    env:
    - name: EXTERNAL_SERVICE_URL
      value: http://localhost:10000

Native Sidecar의 라이프사이클

sequenceDiagram
    participant Init as 일반 Init Container
    participant Sidecar as Native Sidecar<br/>(Ambassador)
    participant Main as 메인 컨테이너

    Note over Init,Main: Native Sidecar 라이프사이클

    Init->>Init: 실행 및 완료
    Init-->>Sidecar: Init 완료 후 시작
    Sidecar->>Sidecar: 시작

    Note over Sidecar: startupProbe 통과 대기

    Sidecar-->>Main: Sidecar 준비 완료 후 시작
    Main->>Main: 실행

    Note over Sidecar,Main: 메인과 사이드카 동시 실행

    Main->>Main: 완료 (종료)

    Note over Sidecar: 메인 종료 후<br/>SIGTERM 수신

    Sidecar->>Sidecar: 정리 후 종료

    Note over Init,Main: Pod 종료 완료

Native Sidecar의 주요 이점

시작 순서 보장: Native Sidecar는 항상 메인 컨테이너보다 먼저 시작됩니다. startupProbe를 사용하면 사이드카가 준비된 후에만 메인 컨테이너가 시작됩니다.

종료 순서 보장: Kubernetes 1.29부터 메인 컨테이너가 모두 종료된 후에야 사이드카가 SIGTERM을 받습니다. 사이드카는 정의된 역순으로 종료됩니다.

Job 워크로드 지원: 기존 방식에서는 Job의 메인 컨테이너가 완료되어도 사이드카가 계속 실행되어 Job이 완료되지 않는 문제가 있었습니다. Native Sidecar는 이 문제를 해결합니다.

리소스 계산 개선: Native Sidecar의 리소스 요청은 메인 컨테이너의 리소스 요청과 합산되어 더 정확한 스케줄링이 가능합니다.


실습: Ambassador 패턴 구현

실습 1: 기본 Ambassador 패턴 - 로그 프록시

가장 기본적인 Ambassador 패턴입니다. 메인 애플리케이션이 생성한 로그를 Ambassador가 처리합니다.

# manifests/test1-basic-log-proxy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: random-generator
  labels:
    app: random-generator
spec:
  containers:
  - name: main
    image: k8spatterns/random-generator:1.0
    env:
    - name: LOG_URL
      value: http://localhost:9009  # Ambassador로 로그 전송
    ports:
    - containerPort: 8080

  - name: ambassador
    image: k8spatterns/random-generator-log-ambassador
    # 포트 9009는 Pod 외부로 노출되지 않음

 

flowchart LR
    subgraph Pod["random-generator Pod"]
        Main["main container<br/>(랜덤 숫자 생성)"]
        Ambassador["ambassador container<br/>(로그 수집)"]
        Main -->|"localhost:9009<br/>(로그 데이터)"| Ambassador
    end

    Client["외부 클라이언트"] -->|"http://:8080<br/>(API 호출)"| Main
    Ambassador -->|"로그 전송"| LogSystem["로깅 시스템"]

    style Main fill:#4a90d9,stroke:#333,color:#fff
    style Ambassador fill:#e67e22,stroke:#333,color:#fff

 

실습 2: HTTP 프록시 Ambassador

Nginx를 사용하여 외부 HTTP 서비스를 프록시하는 Ambassador입니다.

# ConfigMap으로 Nginx 설정 외부화
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-ambassador-config
data:
  nginx.conf: |
    http {
        upstream external_service {
            server httpbin.org:80;
        }

        server {
            listen 8080;

            location /health {
                return 200 "Ambassador is healthy\n";
            }

            location / {
                proxy_pass http://external_service;
                proxy_connect_timeout 5s;
                proxy_read_timeout 10s;

                # 재시도 정책
                proxy_next_upstream error timeout http_502;
                proxy_next_upstream_tries 3;
            }
        }
    }
---
apiVersion: v1
kind: Pod
metadata:
  name: http-proxy-ambassador
spec:
  containers:
  - name: main
    image: curlimages/curl:8.5.0
    command:
    - sh
    - -c
    - |
      while true; do
        # Ambassador를 통해 외부 API 호출
        curl http://localhost:8080/get
        sleep 15
      done

  - name: ambassador
    image: nginx:1.25-alpine
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: nginx-config
      mountPath: /etc/nginx/nginx.conf
      subPath: nginx.conf

  volumes:
  - name: nginx-config
    configMap:
      name: nginx-ambassador-config
flowchart LR
    subgraph Pod["http-proxy-ambassador Pod"]
        Main["main container"]
        Nginx["nginx ambassador"]
        Main -->|"localhost:8080"| Nginx
    end

    Nginx -->|"프록시"| External["httpbin.org"]

    Note["타임아웃: 5s<br/>재시도: 3회<br/>서킷브레이커"]

    style Main fill:#4a90d9,stroke:#333,color:#fff
    style Nginx fill:#e67e22,stroke:#333,color:#fff
    style Note fill:#fff3cd,stroke:#333

동일한 애플리케이션 코드로 개발/프로덕션 환경에서 다른 백엔드를 사용합니다.

개발 환경: 로컬 Redis

# manifests/test4-env-backend-dev.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-dev-env
  labels:
    env: development
spec:
  containers:
  - name: main
    image: redis:7-alpine
    command:
    - sh
    - -c
    - |
      # 애플리케이션 코드 (환경 무관)
      while true; do
        redis-cli -h localhost -p 6379 SET "key" "value"
        redis-cli -h localhost -p 6379 GET "key"
        sleep 10
      done

  - name: ambassador
    image: redis:7-alpine
    command: ["redis-server", "--port", "6379"]

프로덕션 환경: HAProxy + Redis 클러스터

# manifests/test4-env-backend-prod.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: haproxy-ambassador-config
data:
  haproxy.cfg: |
    frontend redis_frontend
        bind *:6379
        default_backend redis_backend

    backend redis_backend
        balance roundrobin
        server redis1 redis-backend-1:6379 check
        server redis2 redis-backend-2:6379 check
---
apiVersion: v1
kind: Pod
metadata:
  name: app-prod-env
  labels:
    env: production
spec:
  containers:
  - name: main
    image: redis:7-alpine
    command:
    - sh
    - -c
    - |
      # 동일한 애플리케이션 코드
      while true; do
        redis-cli -h localhost -p 6379 SET "key" "value"
        redis-cli -h localhost -p 6379 GET "key"
        sleep 10
      done

  - name: ambassador
    image: haproxy:2.9-alpine
    volumeMounts:
    - name: haproxy-config
      mountPath: /usr/local/etc/haproxy/haproxy.cfg
      subPath: haproxy.cfg

  volumes:
  - name: haproxy-config
    configMap:
      name: haproxy-ambassador-config
flowchart TB
    subgraph Dev["개발 환경"]
        DevApp["main<br/>(애플리케이션)"]
        DevAmb["ambassador<br/>(로컬 Redis)"]
        DevApp -->|"localhost:6379"| DevAmb
    end

    subgraph Prod["프로덕션 환경"]
        ProdApp["main<br/>(동일한 코드)"]
        ProdAmb["ambassador<br/>(HAProxy)"]
        ProdApp -->|"localhost:6379"| ProdAmb

        ProdAmb --> Redis1["Redis 1"]
        ProdAmb --> Redis2["Redis 2"]
    end

    Code["동일한 애플리케이션 코드<br/>환경 차이 없음"]

    Code -.->|"개발"| DevApp
    Code -.->|"프로덕션"| ProdApp

    style DevAmb fill:#e67e22,stroke:#333,color:#fff
    style ProdAmb fill:#e67e22,stroke:#333,color:#fff
    style Code fill:#2ecc71,stroke:#333,color:#fff

핵심 포인트:

  • 메인 컨테이너 코드가 두 환경에서 동일
  • Ambassador만 교체하여 환경별 백엔드 전환
  • 관심사의 분리 달성

실습 4: Native Sidecar를 사용한 시작 순서 보장

Kubernetes 1.28+ 에서 Native Sidecar 기능을 사용합니다.

# manifests/test6-native-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: native-sidecar-test
spec:
  initContainers:
  # 1. 일반 init container
  - name: init-setup
    image: busybox:1.36
    command:
    - sh
    - -c
    - |
      echo "[INIT] Running one-time initialization..."
      sleep 2
      echo "[INIT] Initialization complete!"

  # 2. Native Sidecar (restartPolicy: Always)
  - name: ambassador-sidecar
    image: hashicorp/http-echo:1.0
    restartPolicy: Always  # 핵심
    args:
    - "-listen=:8080"
    - "-text=Native Sidecar Ambassador is running"
    ports:
    - containerPort: 8080
    # startupProbe: 메인은 이것이 성공할 때까지 시작 안 됨
    startupProbe:
      httpGet:
        path: /
        port: 8080
      initialDelaySeconds: 2
      periodSeconds: 2
      failureThreshold: 5

  containers:
  - name: main-app
    image: curlimages/curl:8.5.0
    command:
    - sh
    - -c
    - |
      echo "[MAIN] Main application starting..."
      echo "[MAIN] Native Sidecar should already be running!"

      # Ambassador가 이미 준비되어 있어야 함
      if curl -s http://localhost:8080; then
        echo "[MAIN] SUCCESS: Ambassador was ready before main started!"
      fi

      # 계속 실행
      for i in $(seq 1 10); do
        echo "[MAIN] Request #$i to Ambassador..."
        curl -s http://localhost:8080
        sleep 5
      done

      echo "[MAIN] Exiting..."
sequenceDiagram
    participant K8s as Kubernetes
    participant Init as init-setup
    participant Amb as ambassador-sidecar<br/>(Native Sidecar)
    participant Main as main-app

    Note over K8s,Main: Native Sidecar 시작 순서

    K8s->>Init: 1. Init container 시작
    Init->>Init: 초기화 작업 수행
    Init-->>K8s: 완료

    K8s->>Amb: 2. Native Sidecar 시작
    Amb->>Amb: 시작 중...
    Amb->>Amb: startupProbe 실행

    Note over Amb: Probe 실패... 재시도
    Note over Amb: Probe 성공

    Amb-->>K8s: 준비 완료

    K8s->>Main: 3. 메인 컨테이너 시작
    Main->>Amb: 즉시 요청 가능
    Amb-->>Main: 응답

    Note over Main: 작업 완료
    Main-->>K8s: 종료

    K8s->>Amb: SIGTERM
    Amb->>Amb: 정리
    Amb-->>K8s: 종료

 

 

실무적인 관점에서 활용 방안 

1. Service Mesh와의 관계

Ambassador 패턴은 Service Mesh의 기초가 됩니다. Istio의 Envoy sidecar가 대표적인 예입니다.

flowchart TB
    subgraph ServiceMesh["Service Mesh Architecture"]
        subgraph PodA["Pod A"]
            AppA["Application A"]
            EnvoyA["Envoy Sidecar<br/>(Ambassador)"]
            AppA <--> EnvoyA
        end

        subgraph PodB["Pod B"]
            AppB["Application B"]
            EnvoyB["Envoy Sidecar<br/>(Ambassador)"]
            AppB <--> EnvoyB
        end

        EnvoyA <-->|"mTLS<br/>로드밸런싱<br/>재시도"| EnvoyB

        ControlPlane["Control Plane<br/>(Istiod)"]
        ControlPlane -.->|"설정<br/>정책"| EnvoyA
        ControlPlane -.->|"설정<br/>정책"| EnvoyB
    end

    style EnvoyA fill:#e67e22,stroke:#333,color:#fff
    style EnvoyB fill:#e67e22,stroke:#333,color:#fff
    style ControlPlane fill:#9b59b6,stroke:#333,color:#fff

Service Mesh가 제공하는 Ambassador 기능:

  • mTLS를 통한 서비스 간 암호화
  • 자동 재시도 및 서킷 브레이커
  • 트래픽 라우팅 및 로드밸런싱
  • 메트릭 수집 및 분산 추적
  • 정책 적용 (rate limiting, auth 등)

2. 레거시 애플리케이션 현대화

Ambassador 패턴은 수정하기 어려운 레거시 애플리케이션에 현대적인 네트워킹 기능을 추가할 때 유용합니다.

기능 Ambassador를 통한 추가 이점
TLS 종료 Nginx/Envoy Ambassador 애플리케이션 코드 변경 불필요
모니터링 Prometheus exporter sidecar 기존 앱에 메트릭 추가
로깅 Fluentd/Fluent Bit sidecar 중앙화된 로그 수집
서킷 브레이커 Envoy circuit breaker 복원력 향상
인증/인가 OAuth2 proxy 보안 강화
flowchart LR
    subgraph Legacy["레거시 애플리케이션 Pod"]
        LegacyApp["레거시 앱<br/>(수정 불가)"]

        subgraph Ambassadors["Ambassador 컨테이너들"]
            TLS["TLS<br/>Ambassador"]
            Metrics["Metrics<br/>Ambassador"]
            Auth["Auth<br/>Ambassador"]
        end

        LegacyApp <--> TLS
        LegacyApp <--> Metrics
        LegacyApp <--> Auth
    end

    External["외부<br/>클라이언트"] -->|"HTTPS"| TLS
    Prometheus["Prometheus"] -->|"수집"| Metrics
    AuthServer["인증<br/>서버"] <--> Auth

    style LegacyApp fill:#95a5a6,stroke:#333,color:#fff
    style TLS fill:#e67e22,stroke:#333,color:#fff
    style Metrics fill:#e67e22,stroke:#333,color:#fff
    style Auth fill:#e67e22,stroke:#333,color:#fff

3. 데이터베이스 연결 관리

Cloud SQL Proxy나 AWS RDS Proxy와 같은 데이터베이스 프록시를 Ambassador로 활용할 수 있습니다.

# Cloud SQL Proxy as Ambassador
apiVersion: v1
kind: Pod
metadata:
  name: app-with-cloudsql
spec:
  containers:
  - name: app
    image: my-app:1.0
    env:
    - name: DATABASE_URL
      value: "postgresql://user:pass@localhost:5432/dbname"

  - name: cloud-sql-proxy
    image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.8.0
    args:
    - "--port=5432"
    - "project:region:instance"
    - "--credentials-file=/secrets/service_account.json"
    volumeMounts:
    - name: sa-credentials
      mountPath: /secrets

이점:

  • 애플리케이션은 localhost로 DB 접근
  • Cloud SQL Proxy가 IAM 인증 처리
  • SSL/TLS 암호화 자동 적용
  • 연결 풀링 및 관리

4. Knative에서의 Ambassador 활용

Knative Serving의 queue-proxy는 Ambassador 패턴의 실제 구현입니다.

flowchart LR
    subgraph KnativeService["Knative Service Pod"]
        direction LR
        UserContainer["사용자<br/>컨테이너"]
        QueueProxy["Queue Proxy<br/>(Ambassador)"]
        UserContainer <-->|"localhost"| QueueProxy
    end

    Activator["Activator"] -->|"요청"| QueueProxy
    QueueProxy -->|"동시성 메트릭"| Autoscaler["Autoscaler"]

    QueueProxy -->|"버퍼링<br/>제한 적용"| UserContainer

    style QueueProxy fill:#e67e22,stroke:#333,color:#fff
    style UserContainer fill:#4a90d9,stroke:#333,color:#fff

Queue Proxy의 역할:

  • 요청 버퍼링 (애플리케이션 과부하 방지)
  • 메트릭 수집 (동시 요청 수 등)
  • Concurrency limit 강제 적용
  • 헬스체크 및 라이프사이클 관리

5. 프로덕션 고려사항

항목 권장 사항 이유
리소스 모니터링 Prometheus + Grafana Ambassador 오버헤드 추적
로깅 Structured logging 디버깅 용이성
버전 관리 명시적 이미지 태그 재현 가능성
설정 외부화 ConfigMap/Secret 설정 변경 시 재배포 불필요
네트워크 정책 NetworkPolicy 적용 보안 강화
메트릭 수집 Sidecar exporter 관찰성 향상

정리

요구사항 권장 패턴 이유
외부 서비스 접근 단순화 Ambassador 클라이언트 사이드 프록시
메트릭/로그 포맷 변환 Adapter 서버 사이드 프록시
설정 파일 동기화 Sidecar 양방향 협력
Service Mesh 구축 Ambassador (Envoy) 표준 패턴
레거시 앱 현대화 Ambassador 코드 변경 불필요

 

Ambassador 패턴은 외부 서비스 접근을 단순화하고 관심사를 분리하는 구조적 패턴입니다.

  • localhost 기반 통신의 단순함
  • 환경 독립적인 애플리케이션 코드
  • Native Sidecar의 시작 순서 보장
  • 프로덕션 환경을 위한 복원력과 보안

 

'k8s > kubernetes-pattern' 카테고리의 다른 글

Kubernetes Observability(2025)  (0) 2025.12.06
Kubernetes Pattern: Adapter  (0) 2025.11.29
Kubernetes Pattern: Sidecar  (4) 2025.11.22
Kubernetes Pattern: Init Conatiner  (2) 2025.11.15
Kubernetes Pattern: Self Awareness  (0) 2025.11.08