Multi Container
파드와 컨테이너의 통신
- 파드는 하나 이상의 컨테이너가 공유하는 네트워크 및 파일 시스템을 제공하는 가상 환경이다. 각각의 컨테이너는 별도의 환경 변수와 자신만의 프로세스를 가지며, 서로 다른 기술 스택으로 구성된 별개의 이미지를 사용할 수 있는 독립된 단위다. 반면 파드는 한 노드상에서 동작하는 하나의 단위다. 따라서 파드에 속한 컨테이너는 모두 같은 노드에서 동작한다.
파드안 컨테이너간의 데이터 공유 (Volume)
- 같은 파드 안에 있는 컨테이너는 네트워크를 공유한다. 따라서 모든 컨테이너가 같은 IP 주소(파드의 IP 주소)를 갖는다. 파드 속 컨테이너는 외부 트래픽을 받을 수 있지만 각기 다른 포트를 주시해야 한다. 그리고 같은 파드 속 컨테이너 간 통신에는 localhost 주소를 사용한다.
- 컨테이너는 자신만의 파일 시스템을 갖니만 파드에서 제공하는 볼륨을 마운트할 수 있으며, 이 볼륨을 공유하는 방식으로 컨테이너끼리 정보를 교환할 수 있다.
# 한 파드안에 두개의 컨테이너를 띄우는 디플로이먼트 정의
spec:
containers:
- name: sleep
image: container-image1
volumeMounts:
- name: data
mountPath: /data-rw
- name: file-reader
image: container-image1
volumeMounts:
- name: data
mountPath: /data-ro
readOnly: true
volumes:
# 파드에 생성 공유된 볼륨을 통해 두 컨테이너 간의 데이터 공유가 가능해진다.
# 볼륨 마운트시 데이터 접근 권한을 통해 파일의 읽기, 쓰기 등의 제한을 걸수 있다.
- name: data
emptyDir: {}파드안 컨테이너간의 네트워크 공유
- 네트워크는 컨테이너마다 각기 다른 포트를 나누어 쓰는 형태로 공유된다. 이런 기능은 애플리케이션이 백그라운드로 돌아가며 진행 사항을 외부에 알리는 기능이 없는 상황에 유용하다. 파드 속 다른 컨테이너가 REST API를 통해 애플리케이션 컨테이너의 상태를 보고하는 형태가 된다.
# 한 파드 안에 네트워크를 통한 통신
spec:
containers:
- name: app-1
image: container-image
- name: app-2
image: container-image
ports:
- containerPort: 8080
# app-1 컨테이너에서 wget 명령어를 통한 동일 네트워크의 8080 포트를 사용하는 app-2 컨테이너로 네트워크 통신 테스트
# kubectl exec deploy/[deploy-name] -c app-1 -- wget -q -0 -- localhost:8080파드는 클러스터상의 IP 주소를 갖고 있다. 파드 속 컨테이너가 어떤 포트를 주시하고 있다면, 다른 파드가 이 포트로 접근할 수 있다. 트래픽을 파드의 특정 포트로 전달하는 서비스를 만들면 이 포트를 주시하는 컨테이너가 요청을 전달 받는다.
kubectl expose -f template.yaml --type=LoadBalancer --port=8020 --target-port=8080
파드는 애플리케이션을 구성하는 한 단위다. 파드는 애플리케이션의 단일 컴포넌트에 대응해야 한다. 애플리케이션 컨테이너를 지원하는 컨테이너를 파드에 추가할 수는 있지만, 한 파드 안에 서로 다른 애플리케이션을 함께 넣어서는 안된다. 이런 구성을 취하면 각각의 컴포넌트를 독립적으로 업데이트 또는 스케일링하거나 관리할 수 없게 된다.
초기화 컨테이너를 이용한 애플리케이션의 시작
- 모든 컨테이너가 병렬로 실행되는 기본 형태의 멀티컨테이너 파드는 파드의 모든 컨테이너가 동시에 실행되며 모든 컨테이너의 상태가 Ready가 되어야 파드 역시 준비된 것으로 최급된다.
사이드카 패턴이란, 보조 컨테이너가 메인(애플리케이션) 컨테이너를 지원하는 구도를 일컫는다.- 또한 메인(애플리케이션) 컨테이너보다 보조 컨테이너를 먼저 실행하여 애플리케이션 실행 준비를 하는 경우도 있는데 이런 추가 컨테이너를
초기화 컨테이너라고 한다. - 초기화 컨테이너는 파드 안에 여러개 정의할 수 있으며, 파드 정의에 기재된 순서대로 실행된다. 또한 각각의 초기화 컨테이너는 정해진 목표를 달성해야 다음 초기화 컨테이너를 실행한다. 그리고 모든 초기화 컨테이너가 목표를 달성해야 애플리케이션 컨테이너나 사이드카 컨테이너를 실행한다.
# 디플로이먼트의 template 필드에 정의된 파드
spec:
# 초기화 컨테이너는 배열 형태로 기재되며 나열된 순서대로 실행
initContainers:
- name: init-html
image: container-image
command: ['sh', '-c', "echo '<!DOCTYPE html...' > /data/index.html"]
# command: ['git', 'clone', ...]
volumeMounts:
- name: data
mountPath: /data초기화 컨테이너를 활용한 애플리케이션 컨테이너 설정 파일 구성
- 초기화 컨테이너를 활용하면 애플리케이션 컨테이너가 컨피그맵이나 환경 변수를 직접 사용하지 않더라도 컨피그맵과 환경 변수에 설정된 설정값에 따라 애플리케이션 동작이 변화한다.
# 초기화 컨테이너를 통해 애플리케이션 컨테이너의 설정파일을 구성하는 Deployment.yaml
spec:
initContainers:
- name: init-config
image: init-container-image
command: [...환경변수와 컨피그 맵을 통해 전달 받은 데이터를 통합하여 'config-dir'경로에 저장 명령 작성]
env:
- name: APP_ENVIRONMENT
value: TEST
volumeMounts:
- name: config-map
mountPath: /config-in
# 환경 설정과 컨피그맵의 데이터를 통합하여 저장하는 경로
# 애플리케이션 컨테이너에서 해당 경로의 설정파일을 기반으로 애플리케이션을 실행한다.
- name: config-dir
mountPath: /config-out
containers:
- name: application-container
image: application-container-image
volumeMounts:
- name: config-dir
mountPath: /config
readOnly: true
volumes:
- name: config-map
configMap:
name: configMap-name
- name: config-dir
emptyDir: {}- 초기화 컨테이너에서 컨피그맵, 비밀값, 환경 변수 등의 볼륨 마운트에서 설정을 읽은 후 환경 변수의 설정값을 병합하여 구성한 설정을 공디렉토리 볼륨 마운트에 파일로 기록한다.
- 애플리케이션 컨테이너는 설정 파일이 기록된 공 디렉토리 볼륨을 설정 파일 경로에 마운트 한다. 초기화 컨테이너가 기록한 설정 파일이 이미지에 포함된 설정 파일을 가린다.
- 컨테이너는 환경 변수를 공유하지 않는다. 애플리케이션 컨테이너는 초기화 컨테이너에 지정된 설정값을 볼 수 없다.
어댑터 컨테이너를 이용한 일관성 있는 애플리케이션 관리
- 어댑터 컨테이너는 애플리케이션과 컨테이너 플랫폼 사이를 중재하는 어댑터 역할로 기존 애플리케이션 컨테이너의 출력이나 인터페이스를 수정하여 다른 시스템의 요구사항에 맞게 적응 시켜주는 역할을 하는 보조 컨테이너로
사이드카 패턴의 한 종류로 메인 컨테이너와 동일한 파드에서 실행된다. - 핵심은 기존 애플리케이션의 코드를 전혀 변경하지 않고도 연동 표준을 맞출 수 있다는 점이다.
외부와의 통신을 추상화하기: 앰배서더 컨테이너
앰배서더 컨테이너는 애플리케이션과 외부와의 통신을 제어하고 단순화하는 역할을 한다. 애플리케이션이 localhost 주소로 네트워크 요청을 전달하면 이 요청을 앰배서더 컨테이너가 받아 처리하는 형태이다.서비스 메시 아키텍처는 마이크로 서비스 아키텍처(MSA)에서 서비스 간의 통신을 처기하기 위해 만들어진전용 인프라 계층이다. 애플리케이션 코드 변경 없이 서비스 간의 통신을 안정적이고, 빠르고, 안전하게 만들고, 통신 상태를 쉽게 파악할 수 있도록 지원한다.- 가장 큰 특징은 애플리케이션의 비즈니스 로직과 통신 관련 기능(네트워크 로직)을 완전히 분리하는 것이다.
앰배서더 컨테이너는 파드 외부로 나가는 HTTP 요청의 프록시 역할을 한다. 또한 앰배서더 컨테이너를 네트워크 전송 계층에 끼워넣으면 어떤한 유형의 트래픽이라도 처리할 수 있다. 데이터베이스 앰배서더 컨테이너는 읽기 쿼리 전용인 데이터 베이스 복제본으로 보내고, 쓰기 쿼리는 마스터 데이터 베이스로 보낼 수 도 있다.
애플리케이션 로직을 수정하지 않고 성능과 스케일리 모두를 달성할 수 있다.
파드 환경 이해하기
파드는 하나 이상의 컨테이너를 감싸는 경계다. 마찬가지로 컨테이너는 하나 이상의 프로세스를 감싸는 경계이다. 파드는 오버헤드 없이 가상화 계층을 추가하므로 유연하고 효율이 뛰어나다. 다만 이런 유연성의 대가는 복잡도의 상승이다. 따라서 멀티컨테이너 파드를 사용할 때는 아키텍처와 파드 구성에 주의 해야 한다.
파드의 생성 단계에서는 사이드카 컨테이너 (초기화 / 어댑터 / 앰배서더 컨테이너) 역시 애플리케이션 컨테이너와 동일하게 취급한다. 파드는 모든 컨테이너의 상태가 Running이 되지 못해 준비 상태로 넘어가지 못하며, 디플로이먼트는 멀티컨테이너 파드의 초기화 컨테이너가 무사히 종료되고 파드 컨테이너가 모두 Running 상태가 되기를 기다린다.
- 초기화 컨테이너를 가진 파드가 대체될 때 새 파드는 초기화 컨테이너를 모두 실행한다. 따라서 초기화 로직은 반복적으로 실행 할 수 있는 것이어야 한다.
- 초기화 컨테이너의 이미지를 변경하면 파드 자체를 재시작한다.
초기화 컨테이너는 다시 한번 실행되며, 애플리케이션 컨테이너도 모두 교체된다. - 파드 정의에서 애플리케이션 컨테이너의 이미지를 변경하면 애플리케이션 컨테이너가 대체된다.
초기화 컨테이너는 재시작하지 않는다. - 애플리케이션 컨테이너가 종료되면 파드가 애플리케이션 컨테이너를 재생성한다. 대체 컨테이너가 준비될 때까지 파드는 완전한 동작 상태가 아니게 되며, 서비스에서 트래픽을 전달받지 못한다.
shareProcessNamespace: true
- 파드는 단일한 컴퓨팅 환경으로, 파드 속 컨테이너는 네트워크 주소와 파일 시스템의 일부를 공유할 수 있다. 하지만 컨테이너 경계를 넘어 서로의 프로세스에는 접근할 수 없다. 하지만 간혹 프로세스 간 통신이나 애플리케이션 프로세스의 지표를 수집할 때 처럼 사이드카 컨테이너가 애플리케이션 프로세스에 접근해야 할 때가 있다. 이런 경우 파드 정의에
shareProcessNamespace: true설정을 추가하면 이런 접근이 가능해진다. 파드의 모든 컨테이너가 컴퓨팅 공간을 공유하며 서로의 프로세스를 볼 수 있게 된다.
spec:
shareProcessNamespace: true
containers:
- name: container-name1
image: container-image-1
- name: container-name2
image: container-image-2