사이드카 컨테이너
개요
기본적으로 사이드카 컨테이너는 사이드카 패턴을 컨테이너 형태로 만든 것을 말한다.
공식 문서에서는 사이드카 컨테이너라고 페이지를 아예 하나 빼서 설명하지만, 아예 sidecarContainer
이런 식으로 별도의 스펙이 만들어진 것은 아니라는 점을 유념할 필요가 있다.
사이드카 컨테이너는 메인 컨테이너들의 옆에 꼭 달라붙어 로깅, 프록시 등의 보조적인 기능을 수행하는 컨테이너를 일컫는 표현일 뿐이다.
1.28 버전 이후로부터 쿠버네티스에서는 자체적으로 사이드카 구현 방식을 제공하고 있다.
초기화 컨테이너에 restartPolicy
를 Always로 걸어서 만드는 방식으로..
이 문서에서 설명하는 사이드카 컨테이너는 바로 이 쿠버네티스 네이티브 사이드카 컨테이너이다.
엄밀한 구조로 보자면 초기화 컨테이너의 한 종류이며, 이 글에서 언급할 초기화 컨테이너는 사이드카 컨테이너가 아닌 초기화 컨테이너를 한정하여 표현한다.
기술에 대한 상세한 설명을 해준 동영상이 있다.[1]
기능
일단 사이드카 패턴 자체는, 비즈니스 로직을 담당하는 프로세스와 별개로 보조적인 기능을 하는 프로세스를 둘 때 사용한다.
대표적으로 다음의 기능들을 위해 패턴을 활용할 수 있겠다.
- 관측 가능성
- 로깅, 메트릭, 트레이싱 등의 기능을 비즈니스 로직으로부터 분리하여 제공
- 헬스체크
- 컨테이너 프로브보다 훨씬 복잡한 헬스체크 로직을 넣거나, 실제 트래픽 기반으로 상태를 확인할 때
- 동적 설정
- 클러스터 수준에서 어플리케이션 설정을 하면서 이것이 동적으로 컨테이너에 반영하게 하고 싶을 때.
- 앱 프로세스가 반드시 재시작해야 설정이 적용된다면 일찌감치 config-reloader라 해서 앱 프로세스를 재시작하는 별도의 프로세스를 구축해두는 것이 가능하다.
위 모든 기능들을 앱 로직 자체에 대한 수정 없이 구축할 수 있도록 별도의 프로세스를 두는 게 바로 사이드카 패턴의 주된 목적이다.
문서에서는 WAS 서버의 프론트인 웹서버를 사이드카로 두는 예시를 드나, 웹 개발자 입장에서는 그다지 와닿는 예시는 아닐 듯하다.
웹 서버나 was 서버나 핵심 로직을 품고 있는 경우도 많고, 애초에 현대의 개발 방식은 프론트와 백을 구분하기 때문이다.
앱 컨테이너 상위의 라이프사이클
사이드카 패턴을 왜 쓰는지는 알겠고, 그래서 쿠버네티스 네이티브 사이드카 컨테이너는 왜 쓰는가?
파드에는 여러 개의 컨테이너를 둘 수 있으니, 그냥 따로 컨테이너를 하나 더 설정하여 위의 기능들을 충족하는 컨테이너를 두는 것이 가능하다.
사이드카 컨테이너의 가장 큰 장점은, 사이드카 컨테이너가 파드의 전체 생애 주기와 함께 한다는 것에 있다.
일반적인 방식으로 컨테이너를 설정하는 방식에서 발생할 수 있는 상황을 짚어보자.
- 메인 앱 로직을 담당하는 컨테이너보다 늦게 실행될 수 있다.
- 결국 모든 컨테이너가 실행은 되겠지만, 뭐가 먼저 시작될지는 쿠버 조상님도 모른다.
- 여차하면 앱이 실행되고 한참 뒤에야 로깅이 가능할 수도 있다는 것이다..
- 파드 종료 시 앱 컨테이너보다 먼저 종료될 수 있다.
- 앱 컨테이너가 종료되는 과정에도 사이드카 프로세스가 동작해야 할 수 있는데, 일반 컨테이너 방식으로는 이걸 보장하기 어렵다.
- 컨테이너의 프로브가 파드 전체에 영향을 준다.
- 앱 로직 컨테이너는 멀쩡한데 사이드카로 쓰는 컨테이너의 readiness에 문제가 생긴다고 해보자.
- 그럼 앱 자체는 정상 작동하는데도 해당 파드로 트래픽이 오지 않게 되는 불상사가 일어날 수 있다.
사이드카 컨테이너는 완벽하게 파드와 라이프사이클을 함께 하며, 앱 컨테이너에 영향이 가지 않는 것이 보장된다!
일단 초기화 컨테이너로서 앱 컨테이너보다 먼저 실행되는 것이 완벽하게 보장된다.
나름 초기화 컨테이너이기 때문에 사이드카 컨테이너가 종료되는 불상사가 있다면 앱 컨테이너는 절대 시작되지 않는다.
또한 파드가 성공이나 실패를 통해 종료된다 해도 사이드카 컨테이너는 메인 컨테이너가 종료되기 이전까지는 무조건 기능한다.
여기에 사이드카 컨테이너의 프로브는 절대 파드 전체에 영향을 주지 않는다!
근데 그럴 거면 뭐하러 레디네스 설정함?
이것이 사이드카 컨테이너를 사용하는 핵심 이유 되시겠다.
사이드카로 데이터 플레인을 구성하는 이스티오에서도 이 네이티브 사이드카 컨테이너를 이용한 설정 방법을 제공해주고 있다.
사용 방법
단순하게 초기화 컨테이너 필드에 restartPolicy
를 Always
로 설정하면 사이드카 컨테이너가 된다.
파드 속 컨테이너의 장애#재시작 정책(restartPolicy)은 본디 파드 스펙에 명시를 해야 하지만, 초기화 컨테이너 하위 스펙으로도 명시할 수 있고, 이때 이게 적용된다는 것이다.
어떤 식으로 사용할 수 있는지 예시를 보자.
디플로이먼트
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
restartPolicy: Always
command: ['sh', '-c', 'tail -F /opt/logs.txt']
volumeMounts:
- name: data
mountPath: /opt
volumes:
- name: data
emptyDir: {}
Deployment 내 템플릿에 적용한 예시.
/opt라는 공간에 앱 컨테이너는 로그를 넣고, 사이드카 컨테이너에서 해당 로그를 실시간으로 출력한다.
잡
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
restartPolicy: Always
command: ['sh', '-c', 'tail -F /opt/logs.txt']
volumeMounts:
- name: data
mountPath: /opt
restartPolicy: Never
volumes:
- name: data
emptyDir: {}
사이드카 컨테이너의 진미가 드러나는 예시.
잡은 종료가 보장되는 앱 컨테이너를 두고 사용하는 리소스인데 여기에 위처럼 로깅 설정을 한다면!
위처럼 로깅 기능을 하는 컨테이너랑 상관 없이 잡이 종료되도록 보장하는 것이 중요한데 이럴 때 사이드카 컨테이너가 매우 유용하다!
이 덕분에, 파드의 생애 주기 자체에 영향을 조금도 주지 않으면서 생애 주기를 함께 하는 컨테이너를 만들 수 있게 되는 것이다.
참고
- 문서