티스토리 뷰

728x90
반응형

개요

Pod는 Kubernetes의 최소 배포 단위이다. 어플리케이션은 Container로 구성되지만, Kubernetes에 배포하여 서비스하기 위해서는 Pod의 일부로 동작해야 한다.

Kubernetes Object는 다음과 같은 종속성을 갖고 동작한다.

Deployment > Replica > Pod > Container의 종속 관계

Pod는 컨테이너 Spec을 포함하여 선언되며, 하나 이상의 컨테이너를 선언할 수 있다. 이때 각 컨테이너는 독립된 어플리케이션으로 구성되며, 독립된 Process로 기동된다. 이로 인해 가능하면 Pod 당 1개의 Container 구성을 권고하지만, 때로 Multi Container 구성이 필요한 경우가 있다.

지금부터 Multi Container를 설계하는 프로젝트에서 고려해야 할 사항에 대해 알아보자.


Multi Container 설계 고려 사항

1 Pod - 1 Container 정책은 관행화된 규칙처럼 Kubernetes를 구축하고 운영하는 엔지니어들 사이의 불문율로 지켜지고 있다. 이와 같이 설계하는 이유 중 하나는 Kubernetes의 원활한 관리를 위해서이다. Kubernetes는 init process를 기준으로 container의 lifecycle을 관리하기 때문에 여러 container가 기동될 경우 Pod를 관리하는 것이 복잡해지고, 장애를 인지하기 어려운 점이 있다. 그럼에도 불구하여 때때로 Multi Container 구성으로 Pod를 설계해야 하는 경우가 있는데 이를 이해하기 위해 먼저 Pod 내 Container들의 동작 방식과 공유 방법에 대해 알아볼 필요가 있다.

  • Pod에는 IP가 할당된다. Pod의 모든 컨테이너는 동일한 IP를 공유한다.
  • Pod에 볼륨이 생성되면 Pod의 일부인 모든 컨테이너가 스토리지를 공유할 수 있다.
  • Container간 localhost를 통해 통신할 수 있다.

위와 같이 컨테이너 간 공유 서비스가 손쉽게 동작하지만, 왜 Pod 당 1 Container를 선호하는지 고민해 보자.

약간 극단적인 예시이긴 하지만, 위와 같이 WEB SERVER, WAS SERVER, DATABASE 및 MESSAGE QUEUE Layer를 갖는 웹 애플리케이션을 살펴보자. 왼쪽은 4개 Layer 모두를 단일 Pod에 구현하였다. 각 Layer 별 주요 워크로드를 다음과 같이 정의해 보자.

  • WEB SERVER의 경우 Server Side에서 정적 이미지 처리를 중점적으로 담당하며, 외부 서비스와 직접 통신하는 Back-end For Front-end Pattern을 지원한다. 이로 인해 I/O 사용량이 높다.
  • WAS SERVER의 경우 API 중심 서비스를 제공하며, 데이터베이스를 조회하거나, 또 다른 클라우드 내 마이크로서비스, 미니서비스 또는 레거시 서비스의 인터페이스 역할을 함께 담당한다. 이로 인해 네트워크 소모값이 크고, 웹 어플리케이션의 특성에 따라 메모리 사용량이 높다.
  • DATABASE의 경우 솔루션 영역으로 확장되지 않는 StatefulSet 구조로 구성된다. 연산처리를 위해 CPU 사용량이 높다. 또한, 자동 확장 구성을 지원하지 않는 솔루션이 설치되어 있다.
  • Message Queue의 경우 솔루션 영역으로 확장되지 않는 StatefulSet 구조로 구성된다. 클러스터로 구성되어 있어 Pod간 전달해야 할 네트워크 비용이 높고, 메모리 상에 점유하는 메시지에 의해 Memory 사용량이 높다.

위와 같을 경우 각 Layer는 요구하는 리소스, 구성방식이 모두 다르기 때문에 각 Layer가 갖고 있는 특성을 살린 컨테이너 환경 구성이 어렵다. 또한 이 경우 Layer 별 확장이 불가능하여 불필요한 리소스를 낭비해야 한다. 특히 확장이 불가능한 Container가 포함되어 있을 경우 확장 자체가 불가능할 수도 있다.

예를 들어 WAS SERVER에 병목이 발생하여 확장이 필요할 경우 WEB SERVER, DATABASE 및 MESSAGE QUEUE의 컨테이너도 함께 확장되어야 한다. 이는 불필요한 서비스 확장으로 이어진다. 이와 같은 이유로 각 Pod 당 독립적인 어플리케이션이 동작할 수 있도록 1 Container 구성 및 이를 확장할 수 있도록 관리하는 것을 선호한다.

그렇다면 동일한 Pod에서 여러 컨테이너를 사용할 수 있는 경우는 언제일까?

  • 사례 1 - 컨테이너의 LifeCycle이 동일한 경우

위 예시에서 이야기한 것 처럼 컨테이너가 여러개 동작 할 경우 lifecycle이 서로 다른 process의 동작에 의해 container management platform(kubernetes)에서 관리 컨트롤이 어렵다는 점에서 컨테이너 lifecycle이 동일한 container의 경우 예외적인 multi container 사용이 가능하다. 이는 대체로 솔루션 영역에 종속되는 경우가 많으며, 단일 솔루션 내 여러 프로세스가 기동되고 각 프로세스는 하나가 종료되었을때 전체가 동일하게 동작할 수 있도록 내부 매커니즘이 설계되어 있는 경우 LifeCycle이 동일하다고 판단할 수 있다.

  • 사례 2 - 두 개의 컨테이너가 매우 높은 결합도를 보이는 경우

컨테이너 간 높은 결합도가 있는 서비스의 경우 오히려 위에 제시한 ip, 볼륨, 네트워크의 공유로 인해 성능 및 비용 효율성 측면에서 하나의 Pod에 구성하는 것이 효과적일 수 있다. 불필요한 network hop을 줄이고, 업무 영향도를 줄이기 위해 multi container 구성이 가능하다. 물론 이는 서비스 결합도를 낮출 수 있는 여러 개발 패턴으로도 해소할 수 있지만, 단납기 프로젝트의 경우 이를 적용하여 빠르게 개발을 완료하고 서비스를 런칭할 수 있다. 이는 마이크로서비스 패턴의 상위 개념으로 미니서비스 단위로 판단할 수도 있다.

  • 사례 3 - 코드 변경 없이 어플리케이션을 Kubernetes에 배포해야 하는 경우

마지막으로 어플리케이션의 영향도를 최소화 하기 위해 적용할 수 있다. 클라우드 환경의 어플리케이션은 폴리그랏 언어를 지원할 경우 그 형태가 다양하여 이를 지원해야 하는 인프라 복잡도는 사실상 증가하게 된다. 특히 공통 영역은 각 언어와 다양한 솔루션 모두에 최적화 될 수 없기에 예외적으로 multi container를 적용하여 공통 서비스를 호출할 수 있는 인터페이스 영역을 구현하거나, 데이터 포맷을 정형화 하는 등의 용도로 활용이 가능하다.

그럼 지금부터는 위와 같은 사례를 기준으로 multi container로 구현할 수 있는 디자인 패턴 형태에 대해 알아보자.


Multi Container 디자인 패턴

Adapter Pattern

Adapter Pattern은 말 그대로 원 형태의 Main Container에 배포되어 있는 어플리케이션에 Adapter Container를 통해 어플리케이션 또는 데이터를 변환하는 방식이다.

대표적인 Adapter Pattern으로는 로깅 모듈, 공통 모듈, 인터페이스 모듈이 있다. 예를 들어 Kubernetes 클러스터에 log aggregation 환경을 구성할 경우 로그 형태에 따라 추적 분석할 수 있도록 형태를 정형화해야 한다. 그러나 클러스터에는 다양한 형식의 로그를 출력하는 다양한 언어로 작성된 다수의 어플리케이션이 존재할 수 있다. 특히 log aggregation을 위한 도구(elasticsearch, splunk 등)가 변경될 경우 모든 어플리케이션에서 로깅 형태를 다시 변경해야 한다면, 이는 굉장히 비효율적인 업무 처리 방식이 될 것이다. 이 문제를 해결하기 위해 기본 어플리케이션 컨테이너(Main Container)의 로그를 읽고 모니터링 도구에서 원하는 형식으로 변환하여 처리하는 Adapter Container를 생성할 수 있다.

Ambassador Pattern

Ambassador Pattern은 Main Container의 Proxy 역할을 하는 Container이다.

예를 들어 DB URL이 애플리케이션 내부에 localhost로 하드코딩된 레거시 어플리케이션이 있다고 가정하자. 이때 레거시 어플리케이션 코드 변경 없이 Kubernetes에 배포하고자 할 경우 Ambassador 패턴을 사용하면 코드 변경 없이 적용할 수 있다. Ambassador Container를 적용하면, Dev, QA 또는 Stage 환경에 따라 변경되는 DB URL을 수정하지 않고도 원하는 데이터베이스에 연결할 수 있다. 

메인 애플리케이션은 Ambassador Container를 통해 외부 URL에 연결할 수 있다. 이 경우 레거시 어플리케이션의 변경을 최소화 할 수 있으며, 이후 변경되는 URL에 대해 걱정할 필요가 없게 된다.

Sidecar Pattern

Sidecar Pattern은 필수 기능은 아니지만, 어플리케이션 Container의 기능 또는 성능을 향상 시키기 위해 추가적으로 동작하는 업무를 배치하는데 사용된다.

Main Container를 변경하지 않고 기능을 적용하고 향상시킬 수 있다. 예를 들어 특정 폴더에 로그 파일을 생성하는 애플리케이션이 있는 경우 Sidecar Container는 App log를 Streaming 또는 File처리 로직을 통해 전체 로그를 수집하는 Cluster Log Store로 복제할 수 있다. 이때, Main Container는 이를 위해 별도의 수정이 발생하지 않는다.


 

결론

지금까지 Kubernetes Multi Container Design Pattern에 대해 알아보았다. 이를 통해 알 수 있는 사실은 Multi Container를 통해 어플리케이션을 변경하지 않고 또 다른 목적을 갖고 어플리케이션을 처리하거나, 데이터를 조작하는데 활용된다는 점이다. 또한, 이러한 워크로드는 다른 Pod에서 재사용할 수 있는 방식으로 작성되어야 한다는 점도 기억해야 한다.

  • Adapter Pattern : Main Container의 OutPut(데이터, 로그, 인터페이스 대상 정보 등)을 원하는 형태로 변환 처리하는 용도
  • Ambassador Pattern : 자주 변경될 가능성이 있는 또는 공통으로 관리되어야 하는 Nerwork Route 정보를 관리하는 Proxy Server 용도
  • Sidecar Pattern : Main Container를 서포트 하는 유틸리티 서비스를 제공하는 용도

이러한 Multi Container 패턴을 적용하여 Kubernetes Cluster를 확장하는데 유용하게 활용하기를 기대한다.

728x90
반응형