🏗️ 아키텍처

Sidecar

Sidecar Pattern

메인 컨테이너 옆에 보조 컨테이너 배치. 서비스 메시의 기반.

📖 상세 설명

Sidecar Pattern은 메인 애플리케이션 컨테이너 옆에 보조 컨테이너를 배치하여 공통 기능을 분리하는 마이크로서비스 디자인 패턴입니다. 오토바이 옆에 붙은 사이드카처럼 메인 컨테이너와 생명주기를 함께하며, 네트워크 네임스페이스와 볼륨을 공유합니다.

핵심 개념:

  • 동일 Pod: Kubernetes에서 메인 컨테이너와 사이드카는 같은 Pod에 배치
  • localhost 통신: 같은 네트워크 네임스페이스를 공유하여 localhost로 통신
  • 볼륨 공유: emptyDir 등을 통해 파일 시스템 공유 가능
  • 독립 배포: 사이드카 업데이트 시 메인 앱 코드 변경 불필요

주요 활용 사례:

  • Service Mesh Proxy: Istio Envoy, Linkerd - 트래픽 라우팅, mTLS, 관측성
  • 로그 수집: Fluentd, Filebeat - 로그 파일을 중앙 로그 시스템으로 전송
  • 설정 동기화: Consul Template, Confd - 설정 변경 감지 및 적용
  • 시크릿 주입: Vault Agent - 시크릿을 안전하게 주입
  • 프록시: OAuth2 Proxy, Ambassador - 인증/인가 처리

Service Mesh에서의 역할:

Istio, Linkerd 같은 Service Mesh는 모든 서비스 Pod에 사이드카 프록시(Envoy 등)를 자동 주입합니다. 이 프록시가 모든 인바운드/아웃바운드 트래픽을 가로채서 mTLS 암호화, 로드 밸런싱, 서킷 브레이커, 분산 트레이싱을 제공합니다. 애플리케이션 코드 수정 없이 네트워크 레벨 기능을 투명하게 적용할 수 있습니다.

Kubernetes 1.28+ Sidecar Container: 기존에는 사이드카도 일반 컨테이너로 취급되어 시작/종료 순서 제어가 어려웠습니다. 1.28부터 정식 지원되는 Sidecar Container(initContainers + restartPolicy: Always)는 메인 컨테이너보다 먼저 시작하고 나중에 종료되어 더 안정적인 라이프사이클 관리가 가능합니다.

💻 코드 예제

로그 수집 Sidecar (Fluentd)

# logging-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-logging
spec:
  containers:
    # 메인 애플리케이션
    - name: app
      image: my-app:latest
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
      resources:
        limits:
          memory: "512Mi"
          cpu: "500m"

    # Sidecar: 로그 수집 (Fluentd)
    - name: log-collector
      image: fluent/fluentd:v1.16
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
          readOnly: true
        - name: fluentd-config
          mountPath: /fluentd/etc
      resources:
        limits:
          memory: "128Mi"
          cpu: "100m"

  volumes:
    - name: logs
      emptyDir: {}
    - name: fluentd-config
      configMap:
        name: fluentd-config

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluent.conf: |
    <source>
      @type tail
      path /var/log/app/*.log
      pos_file /var/log/app/fluentd.pos
      tag app.logs
      <parse>
        @type json
      </parse>
    </source>

    <match app.logs>
      @type elasticsearch
      host elasticsearch.logging.svc
      port 9200
      index_name app-logs
    </match>

Vault Agent Sidecar (시크릿 주입)

# vault-sidecar.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-vault
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "my-app-role"
    vault.hashicorp.com/agent-inject-secret-db: "secret/data/db/config"
    vault.hashicorp.com/agent-inject-template-db: |
      {{ with secret "secret/data/db/config" -}}
      export DB_HOST="{{ .Data.data.host }}"
      export DB_USER="{{ .Data.data.username }}"
      export DB_PASS="{{ .Data.data.password }}"
      {{- end }}
spec:
  serviceAccountName: my-app-sa
  containers:
    - name: app
      image: my-app:latest
      command: ["/bin/sh", "-c"]
      args:
        - source /vault/secrets/db && ./start.sh
      volumeMounts:
        - name: vault-secrets
          mountPath: /vault/secrets
  # Vault Injector가 자동으로 Sidecar 추가

---
# Kubernetes 1.28+ Native Sidecar Container
apiVersion: v1
kind: Pod
metadata:
  name: app-with-native-sidecar
spec:
  initContainers:
    # Native Sidecar: 메인보다 먼저 시작, 나중에 종료
    - name: vault-agent
      image: hashicorp/vault:1.15
      restartPolicy: Always  # 이게 핵심! Sidecar로 동작
      args:
        - agent
        - -config=/etc/vault/agent.hcl
      volumeMounts:
        - name: secrets
          mountPath: /vault/secrets
        - name: vault-config
          mountPath: /etc/vault

  containers:
    - name: app
      image: my-app:latest
      volumeMounts:
        - name: secrets
          mountPath: /vault/secrets
          readOnly: true

  volumes:
    - name: secrets
      emptyDir:
        medium: Memory  # RAM에 저장 (보안)
    - name: vault-config
      configMap:
        name: vault-agent-config

Istio Envoy Sidecar (자동 주입)

# Namespace에 Istio injection 활성화
kubectl label namespace my-app istio-injection=enabled

# Deployment 배포 시 Envoy Sidecar 자동 주입
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-service
  namespace: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-service
  template:
    metadata:
      labels:
        app: my-service
      # 특정 Pod에서 Sidecar 비활성화
      # annotations:
      #   sidecar.istio.io/inject: "false"
    spec:
      containers:
        - name: app
          image: my-service:latest
          ports:
            - containerPort: 8080

# 배포 후 Pod 확인 - Envoy Sidecar가 자동 추가됨
# kubectl get pods -n my-app
# NAME                          READY   STATUS    RESTARTS
# my-service-5d4f6c7b8-abc12   2/2     Running   0
#                               ^^^
#                               app + istio-proxy (Envoy)

# Istio VirtualService로 트래픽 제어
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: my-service
spec:
  hosts:
    - my-service
  http:
    - route:
        - destination:
            host: my-service
            subset: v1
          weight: 90
        - destination:
            host: my-service
            subset: v2
          weight: 10  # Canary: 10% 트래픽을 v2로

🗣️ 실무 대화 예시

아키텍처 리뷰

시니어: "서비스마다 로깅, 트레이싱 코드가 중복이네. 언어도 다르고..."

개발자: "Sidecar 패턴으로 분리하면 어떨까요? Fluentd Sidecar로 로그 수집하고, Jaeger Agent Sidecar로 트레이스 전송하면 앱 코드에서 인프라 로직을 뺄 수 있어요."

시니어: "리소스 오버헤드는?"

개발자: "Sidecar 하나당 메모리 50-100MB 정도요. 대신 각 앱에서 SDK 초기화 오버헤드가 사라지고, 버전 업그레이드도 Sidecar만 바꾸면 되니까 운영이 편해져요."

기술 면접

면접관: "Istio에서 Sidecar Proxy가 하는 일을 설명해주세요."

지원자: "Envoy Sidecar가 모든 인바운드/아웃바운드 트래픽을 가로챕니다. iptables 규칙으로 트래픽을 Envoy로 리다이렉트하고요. mTLS로 서비스 간 통신을 암호화하고, 로드밸런싱, 서킷브레이커, 재시도 정책을 적용합니다. 또한 모든 요청의 메트릭과 트레이스를 수집해서 관측성을 제공해요."

면접관: "Sidecar 없는 Service Mesh는요?"

지원자: "Cilium 같은 eBPF 기반 Service Mesh가 있어요. 커널 레벨에서 트래픽을 처리해서 Sidecar 오버헤드가 없죠. 다만 eBPF 지원 커널이 필요하고, L7 기능이 Envoy보다 제한적일 수 있어요."

트러블슈팅

운영팀: "Pod가 Ready인데 앱 컨테이너에서 외부 API 호출이 안 돼요."

개발자: "Istio Sidecar 때문일 수 있어요. Envoy가 아직 xDS 설정을 못 받아서 트래픽 라우팅을 못하는 거예요. holdApplicationUntilProxyStarts: true 설정하면 Envoy 준비될 때까지 앱 컨테이너 시작을 지연시킬 수 있어요."

운영팀: "아, Sidecar가 먼저 준비되어야 하는구나."

⚠️ 주의사항

🔗 관련 용어

Service Mesh Istio Envoy Ambassador Pattern Init Container mTLS

📚 더 배우기