사이드카 컨테이너

사이드카 컨테이너 (Sidecar Containers)

FEATURE STATE: Kubernetes v1.33 [stable](기본적으로 활성화)

사이드카 컨테이너는 같은 Pod 안에서 메인 애플리케이션 컨테이너와 함께 실행되는 보조 컨테이너예요. 이 컨테이너들은 로깅, 모니터링, 보안, 데이터 동기화 같은 추가 서비스나 기능을 제공해서 기본 앱 컨테이너의 기능을 향상시키거나 확장하는 데 사용돼요. 기본 애플리케이션 코드를 직접 바꾸지는 않아요.

보통 Pod에는 앱 컨테이너가 하나만 있어요. 예를 들어 로컬 웹서버가 필요한 웹 애플리케이션이 있다면, 로컬 웹서버가 사이드카이고 웹 애플리케이션 자체가 앱 컨테이너예요.

Kubernetes의 사이드카 컨테이너

Kubernetes는 사이드카 컨테이너를 init 컨테이너의 특수한 경우로 구현해요. 사이드카 컨테이너는 Pod 시작 후에도 계속 실행돼요. 이 문서에서는 Pod 시작 중에만 실행되는 컨테이너를 명확히 지칭하기 위해 *일반 init 컨테이너(regular init containers)*라는 용어를 사용해요.

클러스터에 SidecarContainers 기능 게이트가 활성화되어 있다면(Kubernetes v1.29부터 기본적으로 활성화), Pod의 initContainers 필드에 나열된 컨테이너에 대해 restartPolicy를 지정할 수 있어요. 이러한 재시작 가능한 사이드카 컨테이너는 같은 Pod 안의 다른 init 컨테이너와 메인 애플리케이션 컨테이너로부터 독립적이에요. 이 컨테이너들은 메인 애플리케이션 컨테이너와 다른 init 컨테이너에 영향을 주지 않고 시작, 중지, 재시작할 수 있어요.

또한 init이나 사이드카로 표시되지 않은 여러 컨테이너로 Pod를 실행할 수도 있어요. 이는 Pod 안의 컨테이너들이 Pod가 전반적으로 동작하는 데 필요하지만 어떤 컨테이너를 먼저 시작하거나 중지할지 제어할 필요가 없을 때 적합해요. 컨테이너 수준 restartPolicy 필드를 지원하지 않는 오래된 Kubernetes 버전을 지원해야 할 때도 이렇게 할 수 있어요.

예시 애플리케이션

두 개의 컨테이너가 있고 그중 하나가 사이드카인 Deployment 예시를 볼게요.

참고:

이 예시에서 사이드카 컨테이너는 의도적으로 restartPolicy: Always와 함께 initContainers 아래 정의돼 있어요. Kubernetes는 이런 컨테이너를 Pod 수명 동안 계속 실행되는 사이드카로 취급해요.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: alpine:latest
          command: ['sh', '-c', 'while true; do echo "logging" >> /opt/logs.txt; sleep 1; done']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          # Setting restartPolicy: Always makes this a sidecar container.
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      volumes:
        - name: data
          emptyDir: {}

사이드카 컨테이너와 Pod 수명 주기

init 컨테이너가 restartPolicyAlways로 설정된 상태로 생성되면, Pod의 전체 수명 동안 시작되어 계속 실행돼요. 이는 메인 애플리케이션 컨테이너로부터 분리된 지원 서비스를 실행하는 데 유용할 수 있어요.

이 init 컨테이너에 readinessProbe가 지정되면, 그 결과가 Pod의 ready 상태를 결정하는 데 사용돼요.

이 컨테이너들은 init 컨테이너로 정의되기 때문에 일반 init 컨테이너와 동일한 순서 및 순차적 보장을 누려요. 따라서 복잡한 Pod 초기화 흐름을 위해 사이드카 컨테이너와 일반 init 컨테이너를 섞을 수 있어요.

일반 init 컨테이너와 비교해, initContainers 안에 정의된 사이드카는 시작된 후에도 계속 실행돼요. Pod에 .spec.initContainers 항목이 둘 이상 있을 때 이 점이 중요해요. 사이드카 스타일의 init 컨테이너가 실행 중이 되면(kubelet이 해당 init 컨테이너의 started 상태를 true로 설정하면), kubelet은 정렬된 .spec.initContainers 목록에서 다음 init 컨테이너를 시작해요. 그 상태는 컨테이너에 실행 중인 프로세스가 있고 시작 프로브가 정의되지 않아 true가 되거나, startupProbe가 성공한 결과로 true가 돼요.

Pod 종료 시, kubelet은 메인 애플리케이션 컨테이너가 완전히 멈출 때까지 사이드카 컨테이너의 종료를 미뤄요. 그런 다음 사이드카 컨테이너는 Pod 스펙에 나타난 순서의 반대 순서로 종료돼요. 이 접근 방식은 사이드카가 더 이상 서비스가 필요하지 않을 때까지 계속 동작하면서 Pod 안의 다른 컨테이너를 지원하도록 보장해요.

사이드카 컨테이너를 사용한 Job

Kubernetes 스타일의 init 컨테이너를 사용해 사이드카를 쓰는 Job을 정의하면, 각 Pod의 사이드카 컨테이너가 메인 컨테이너가 끝난 후에도 Job이 완료되는 것을 막지 않아요.

두 개의 컨테이너가 있고 그중 하나가 사이드카인 Job 예시를 볼게요.

apiVersion: batch/v1
kind: Job
metadata:
  name: myjob
spec:
  template:
    spec:
      containers:
        - name: myjob
          image: alpine:latest
          command: ['sh', '-c', 'echo "logging" > /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      initContainers:
        - name: logshipper
          image: alpine:latest
          # Setting restartPolicy: Always makes this a sidecar container.
          restartPolicy: Always
          command: ['sh', '-c', 'tail -F /opt/logs.txt']
          volumeMounts:
            - name: data
              mountPath: /opt
      restartPolicy: Never
      volumes:
        - name: data
          emptyDir: {}

애플리케이션 컨테이너와의 차이점

사이드카 컨테이너는 같은 pod에서 앱 컨테이너와 나란히 실행돼요. 하지만 기본 애플리케이션 로직을 실행하지는 않고, 대신 메인 애플리케이션에 지원 기능을 제공해요.

사이드카 컨테이너는 자신만의 독립적인 수명 주기를 가져요. 앱 컨테이너와 독립적으로 시작, 중지, 재시작할 수 있어요. 이 말은 기본 애플리케이션에 영향을 주지 않고 사이드카 컨테이너를 갱신, 확장, 유지보수할 수 있다는 뜻이에요.

사이드카 컨테이너는 기본 컨테이너와 같은 네트워크 및 스토리지 네임스페이스를 공유해요. 이렇게 함께 위치함으로써 밀접하게 상호작용하고 리소스를 공유할 수 있어요.

Kubernetes 관점에서 사이드카 컨테이너의 graceful 종료는 덜 중요해요. 다른 컨테이너들이 배정된 graceful 종료 시간을 모두 사용하면, 사이드카 컨테이너는 graceful하게 종료할 시간 전에 SIGTERM 신호를 받고 그 다음 SIGKILL 신호를 받아요. 그래서 사이드카 컨테이너에서 0과 다른 종료 코드(0은 성공적인 종료를 나타냄)는 Pod 종료 시 정상적인 것이며 일반적으로 외부 도구가 무시해야 해요.

init 컨테이너와의 차이점

사이드카 컨테이너는 메인 컨테이너와 나란히 실행되며 그 기능을 확장하고 추가 서비스를 제공해요.

사이드카 컨테이너는 메인 애플리케이션 컨테이너와 동시에 실행돼요. pod의 수명 주기 내내 활성 상태이며 메인 컨테이너와 독립적으로 시작하고 중지할 수 있어요. init 컨테이너와 달리, 사이드카 컨테이너는 수명 주기를 제어하는 프로브를 지원해요.

사이드카 컨테이너는 메인 애플리케이션 컨테이너와 직접 상호작용할 수 있어요. init 컨테이너처럼 항상 같은 네트워크를 공유하고, 선택적으로 볼륨(파일시스템)도 공유할 수 있기 때문이에요.

init 컨테이너는 메인 컨테이너가 시작되기 전에 멈추므로, init 컨테이너는 Pod 안의 앱 컨테이너와 메시지를 주고받을 수 없어요. 데이터 전달은 단방향뿐이에요(예를 들어 init 컨테이너는 emptyDir 볼륨 안에 정보를 넣을 수 있어요).

사이드카 컨테이너의 이미지를 변경해도 Pod가 재시작되지는 않지만, 컨테이너 재시작은 발생해요.

init, 사이드카, 앱 컨테이너의 실행 순서를 고려하면 리소스 사용에 대한 다음 규칙이 적용돼요.

  • 모든 init 컨테이너에 정의된 특정 리소스의 request 또는 limit 중 가장 높은 값이 유효 init request/limit가 돼요. 어떤 리소스에 리소스 limit가 지정되지 않으면 가장 높은 limit로 간주돼요.
  • Pod의 특정 리소스에 대한 유효 request/limitpod 오버헤드와 다음 중 더 높은 값의 합이에요.
    • 특정 리소스에 대한 모든 non-init 컨테이너(앱 및 사이드카 컨테이너)의 request/limit 합
    • 특정 리소스에 대한 유효 init request/limit
  • 스케줄링은 유효 request/limit를 기반으로 수행돼요. 즉 init 컨테이너가 Pod 수명 동안 사용하지 않는 초기화용 리소스를 예약할 수 있다는 뜻이에요.
  • Pod의 유효 QoS 계층은 모든 init, 사이드카, 앱 컨테이너에 대해 동일한 QoS(quality of service) 계층이에요. 쿼터와 limit는 유효 Pod request와 limit를 기반으로 적용돼요.

사이드카 컨테이너와 Linux cgroups

Linux에서 Pod 수준 제어 그룹(cgroups)에 대한 리소스 할당은 스케줄러와 동일하게 유효 Pod request와 limit를 기반으로 해요.

더 알아보기