Choosing a Microservices Deployment Strategy
Choosing a Microservices Deployment Strategy에 대한 요약이다.
Motivations
모놀리틱 어플리케이션은 보통 큰 어플리케이션 하나를 여러개로 복사해서 실행한다.
즉, N개의 서버에 각 서버마다 M개의 어플리케이션 인스턴스가 실행되는 것이다.
모놀리틱 어플리케이션의 배포가 쉽다는 것은 아니다.
하지만 마이크로서비스 어플리케이션의 배포에 비해서 단순하다.
마이크로서비스 어플리케이션은 수십, 수백개의 서비스들로 구성되어 있고,
각 서비스 마다 다른 언어, 프레임워크를 사용 할 수 있다.
각 서비스들은 자신만의 배포, 자원, 스케일링, 모니터링 등이 필요하다.
예를 들면, 서비스는 수요에 따라서 여러 인스턴스를 실행 할 필요가 있고,
각 서비스의 성격에 맞게 적절한 CPU, 메모리, I/O 자원 등이 할당 되어야 한다.
마이크로 서비스의 배포는 이런 복잡성에도 불구하고, 빠르고 안정적이고, 비용 효율적이어야 한다.
Multiple Service Instances per Host Pattern
physical/vitual host 를 하나 이상 실행하고, 각 호스트 별로 여러 서비스를 실행하는 패턴이다.
각 서비스 인스턴스는 호스트의 well-known port를 사용하여 실행된다.
이 패턴의 주요 장점은 Resource 사용이 상대적으로 효율적이다.
여러 서비스 인스턴스가 서버 및 OS를 공유한다.
Apache Tomcat이나 JVM 같은 프로세스를 공유하는 상황이라면 더욱 효율적일 것이다.
또다른 장점으로 서비스 인스턴스를 배포하는 것이 상대적으로 빠르다.
서비스를 호스트에 복사하고 실행하면 된다.
Java라면 jar나 war 파일일 테고, Node.js 나 Ruby라면 소스코드를 복사하면 된다.
이런 장점에도 불구하고, 이 패턴은 몇가지 주요한 단점이 있다.
프로세스로 분리되어 있을 지라도, 서비스 인스턴스들을 격리(Isolation) 시킬 수 없다.
각 인스턴스가 사용하는 리소스를 제한 할 수 없다. 특정 서비스가 호스트의 모든 CPU, Memory를 소비해 버릴 수도 있다.
또다른 단점은, 운영팀이 각 서비스를 어떻게 배포해야 하는지 상세히 알고 있어야 한다.
서비스는 다양한 언어와 프레임워크를 사용하고 있을 수 있고, 기타 수 많은 내용들이 운영 팀에게 모두 공유되어야만 한다.
이런 복잡함이 배포 중 에러를 발생시킬 위험을 증가시킨다.
Service Instance per Virtual Machine Pattern
VM 이미지(ex. Amazon EC2)마다 하나의 서비스가 패키징되는 패턴이다.
각 서비스 인스턴스는 VM 이미지를 사용하여 실행된 하나의 VM 이다.
Netflix에서 사용하는 주요 접근법이다. 서비스들은 Aminator를 통해 EC2로 패키징된다.
이 배포 패턴의 주요 장점은 각 서비스 인스턴스가 완벽하게 격리된 상태로 실행된다는 것이다.
고정된 CPU, Memory를 할당 받고, 다른 서비스들의 자원에 영향을 주지 않는다.
다른 장점은 Load Balancing, Auto Scaling 등 Cloud Infra를 잘 활용 할 수 있다.
또한, 서비스의 구현기술을 캡슐화 할 수 있다. 서비스가 VM으로 패키징되면 블랙박스 처럼 된다.
따라서 배포는 훨씬 더 간단하고 안정적으로 이뤄질 수 있다.
하지만, 자원을 사용하는 효율성이 떨어진다는 단점이 있다.
모든 VM이 OS를 포함하는 오버헤드가 있고,
전형적인 IaaS는 VM을 고정된 크기로 제공하기 때문에 충분히 활용되지 못할 가능성이 있다.
IaaS에서 오토 스케일링을 제공하지만 수요의 변화에따라 빠르게 반응하기는 어렵다.
(참조: http://techblog.netflix.com/2013/11/scryer-netflixs-predictive-auto-scaling.html)
따라서 VM이 불필요하게 할당되고 배포 비용이 증가하게 된다.
또다른 단점으로는 서비스의 새로운 버전이 배포되는 속도가 느리다는 것이다.
VM 이미지는 size가 크기때문에 빌드되고 실행하는데 느리다.
VM내의 OS가 실행되는 시간도 소요된다.
Service Instance per Container Pattern
각 서비스 인스턴스가 자신 만의 컨테이너(Container) 안에서 실행되는 패턴이다.
컨테이너는 OP level의 가상화 기술로 샌드박스 안에서 실행되는 하나이상의 프로세스들로 구성된다.
자신만의 port namespace, root filesystem 등을 가지고, 컨테이너 마다 CPU, Memory, I/O 등의 자원을 제한 할 수도 있다.
컨테이너 이미지는 어플리케이션 및 서비스가 실행되는데 필요한 라이브러리로 구성된 파일시스템이다.
Docker, Solaris Zones 등의 기술이 있다.
서비스가 컨테이너 이미지로 패키징되면, 여러 컨테이너로 실행 시킬 수 있다.
보통 한 호스트에 여러 컨테이너가 실행되고, Kubernetes 등의 클러스터 매니져를 사용할 수 있다.
이 패턴의 장점은 VM을 사용할 때와 비슷하다.
서비스 인스턴스를 격리된 상태로 실행 할 수 있고, 다른 컨테이너가 사용하는 자원을 쉽게 모니터링 할 수 있다.
컨테이너를 통해 서비스의 구현기술을 캡슐화 할 수 도 있다.
하지만 컨테이너는 VM에 비해서 경량화된 기술이다.
컨테이너 이미지는 매우 빠르게 빌드되고, OS 부팅이 없기 때문에 빠르게 실행된다.
즉, 컨테이너가 실행된다는 것은 서비스가 실행된다는 의미이다.
컨테이너를 사용하는 것의 단점은 컨테이너 기술의 발전 속도가 매우 빠르긴 하지만 VM 인프라 정도는 아니다는 것이다.
컨테이너는 호스트 OS의 커널을 다른 컨테이너와 공유하기 때문에 VM에 비해 안전하지 않다.
또한, 수 많은 컨테이너 이미지를 관리해야 되는 책임을 갖을 뿐만 아니라, 컨테이너가 실행되는 VM 인프라 또한 관리해야 된다.
일반적으로 컨테이너가 배포되는 클라우드 인프라는 VM당 가격이 책정되기 때문에
로드가 급증하는 경우 과도하게 프로비져닝되어 추가 비용이 발생할 수도 있다.
Serverless Deployment
AWS Lambda는 서버리스 배포 기술의 한 예다. Java, Node.js, C#, Python 서비스를 지원한다.
마이크로서비스를 Zip 파일로 패키징해서 메타데이터와 함꼐 AWS Lambda로 업로드 하면된다.
AWS Lambda는 요청을 처리하기 위해 충분한 인스턴스가 자동으로 실행되고 시간이나 메모리 사용량 등에 따라 요금을 지불한다.
AWS Lambda는 여러 제약사항이 있지만, 서버나 VM, 컨테이너 등을 신경쓰지 않아도 되기 때문에 매력적이다.
Lambda Function은 Stateless 한 서비스이고, 주로 AWS 서비스를 실행하면서 요청을 처리한다.
예를 들어 이미지가 S3에 업로드 됐을 때, DynamoDB에 아이템을 저장하고, 이미지 처리를 위해 Kinesis 스트림에 메시지를 퍼블리싱한다.
물론 외부 웹서비스를 실행 할 수도 있다.
1 | Lambda function 이 실행되는 경우 |
AWS Lambda는 마이크로서비스를 배포하기 편리한 방법이다.
실제로 사용한 만큼만 비용이 발생하며, IT 인프라에 대한 고민 없이 어플리케이션 개발에만 집중하면 된다.
하지만 몇가지 주요한 제약사항이 있다.
오래 실행되는 서비스를 배포하는 데에는 맞지 않다. 요청은 300초 이내로 끝나야 한다.
요청이 있을때마다 별도의 인스턴스가 실행되기 때문에 서비스는 Stateless 해야된다.
지원되는 언어도 제약이 있을 뿐만 아니라, Time out되어 종료될 수 도 있기 때문에 서비스는 빠르게 시작되어야 한다.
Summary
마이크로서비스를 배포하는데는 많은 어려움이 있다.
다양한 언어와 프레임워크로 작성된 수십 수백개의 서비스들이 존재한다.
각 서비스들은 자신만의 배포환경, 자원, 스케일링, 모니터링 등이 요구된다.
서비스를 배포하는 방법으로는 VM당 서비스 하나씩 배포하는 방법,
컨테이너당 서비스 하니씩 배포하는 방법, AWS Lambda를 활용하여 서버리스하게 배포하는 방법 등이 있다.