본문 바로가기
프로그래밍/MSA

[MSA] 12요소 어플리케이션; 클라우드 네이티브 어플리케이션

by 사바라다 2019. 12. 15.

많은 조직들이 기존 어플리케이션을 들어내며 클라우드로 옮기고 있습니다. 그리고 처음부터 클라우드로 개발을 하기도합니다. 이런 클라우드의 장점은 비용, 속도, 애자일성, 유연성 ,탄력성 측면에서 많은 장점을 가지고 있습니다. 사실 이런 장점을 모든 개발사들이 누리고 있는 것은 아닙니다. 왜냐하면 기존의 온프레미스 아키텍처가 클라우드 환경과는 맞지 않는 부분이 있기 때문입니다.

어떻게 하면 어플리케이션이나 마이크로서비스를 다른 여러 클라우드 서비스상에서 매끄럽게 운영하고 탄력성 같은 서비스의 장점을 누릴 수 있을까요? 허로쿠(Heroku)가 제시한 12 요소 어플리케이션은 클라우드에서 장점을 살릴 수 있는 특징을 기술하는 방법론입니다.

오늘은 이런 12 요소 어플리케이션에대해서 한번 알아보도록 하겠습니다.

단일 코드 베이스

코드와 어플리케이션 사이에는 항상 1대 1 관계가 성립해야합니다.앱의 코드베이스는 한개여야하지만, 앱의 배포는 여러개가 될 수 있습니다. 개발, stage, 운영에 대해서 1개의 코드베이스를 유지한다라는 의미입니다. 아래의 이미지와 같이 말이죠.

코드는 SVN 또는 Git과 같은 버전 컨트롤 시스템을 사용하여 변화를 추적합니다.

여러개의 앱이 동일한 코드를 공유한다면 12요소 어플리케이션을 위반하는 것입니다. 이를 해결하기 위해서는 공유하는 코드를 라이브러리화 시키고, 해당 라이브러리를 종속성 매니저로 관리해야합니다.

이것을 마이크로서비스에 적용해보면 마이크로서비스는 저마다 코드 베이스를 가져야하고 이 코드 베이스는 다른 마이크로서비스와 공유되지 않는다는 것을 의미합니다. 공유(의존)이 필요하다면 별도의 모듈로 분리 후 의존성으로써 추가해야한다는 의미입니다.

의존성 꾸러미

의존성 꾸러미(Bundleing dependencies)원칙에 따르면 모든 어플리케이션은 필요한 모든 의존성을 어플리케이션과 함께 하나의 Bundle에 담아야 한다는 원칙입니다. Bundle이란 gradle, maven과 같은 POM(Project Object Model)을 말합니다. 그리고 이런 의존성 꾸러미를 함께 최종 실행 파일인 war또는 jar에 포함해야합니다.

환경설정 외부화

어플리케이션에서 환경설정이란 배포(개발, 스테이징, 운영)마다 달라지는 모든것을 의미합니다. 이러한 설정에는 DB 등 리소스 핸들러, 외부 서비스에 대한 인정 증보, 호스트의 이름 등이 있습니다. 온프로미스 환경에서 이들은 같은 프로젝트에 같이 상수로 저장했다면 클라우드 환경에서는 코드에서 환경설정을 엄격하게 분리되는 것을 권장합니다.

이러한 원칙은 MSA에서는 그대로 적용될 수 있습니다. 마이크로서비스 환경설정 파라미터는 외부의 소스에서 읽어 오는게 좋습니다. 환경에 따라 달라지는 유일한 것은 환경설정 파라미터 밖에 없기 때문에 이를 외부화 함으로써 출시나 배포 프로세스 자동화에 도움이 됩니다.

후방 지원 서비스 접근성

후방 지원 서비스라는 것은 어플리케이션 정상 동작 중 네트워크로 접근하는 모든 서비스를 가리킵니다. 본인이 작성한 REST API 서버일 수 있으며, 서드파티에서 제작한 서비스 일 수도 있습니다. 예로는 카카오오픈API가 있을 수도 있구요. 더 친근한건 데이터베이스 시스템인 MySql, 캐시 시스템인 Redis가 있습니다.

이러한 후방 지원 시비스들에 대해서 12요소 어플리케이션에서는 느슨한 결합을 유지해야한다고 합니다. 즉, Mysql을 쓰다가도 설정변경만으로 H2나 Oracle DB를 사용할 수 있어야 한다는 것입니다. 이러한 것은 Spring에서 추구하는 AOP와도 많이 닮아 있는 것을 알 수 있었습니다.

그리고 마이크로서비스에서는 이러한 Resource들에 대해서 URL에 의해서 접근가능해야 한다고 합니다. DB의 경우 jdbc://, 일반 API서버라면 http://, https:// 정도가 될 것입니다.

빌드, 출시(릴리즈), 실행의 격리

코드베이스는 철저하게 분리된 빌드, 출시, 실행의 3단계를 거쳐 배포로 전환합니다. 각단계에 대해서 알아보도록 하겠습니다.

  • 빌드 : 코드를 실행가능한 번들파일로 변환시키는 단계
  • 릴리즈 : 빌드된 파일을 환경 설정과 결합하는 단계
  • 실행 : 릴리즈된 파일이 실행환경에서 런타임이 되는 단계

Spring에서 마이크로서비스에서 빌드란 실행 가능한 JAR 파일을 만들어낼 수 있는데, 이 실행 파일에는 HTTP 리스너와 같은 서비스 런타임 환경(톰켓 등)도 포함됩니다. 출시 단계에서는 이런 실행 파일들이 운영 환경 URL과 같은 환경설정 정보와 합쳐저 출시 버전을 만들어 내는데, 대부분 도커와 유사한 컨테이너로 만들어집니다. 운영 단계에서는 출시 단계에서 만들어낸 컨테이너가 스케줄러에 의해 운영 환경에 배포됩니다.

무상태, 비공유 프로세스

어플리케이션은 하나 혹은 여러개의 무상태(stateless) 프로세스로 실행됩니다. 무상태 프로세스라는 것은 어플리케이션은 별도의 상태가 없어야하고 아무것도 공유하지 않아야 한다고 합니다. 어플리케이션이 상태를 갖지 않으면 좋은점은 장애 대응성이 좋아지고 쉽게 시스템을 확장할 수 있다는 것입니다.

만약 상태를 저장해야 하는 요구사항이 있다면 데이터베이스나 인메모리 캐시 같은 후방 지원 서비스에서 처리하는게 좋습니다.

  • 마이크로서비스는 확장에 따라 각 서비스가 2개 ~ 3개 떠있을 경우가 있을 수 있는데 1번째 요청과 2번째 요청을 같은 마이크로서비스가 처리한다고 보장할 수 없습니다. 그렇기 때문에 상태를 저장하는 프로세스가 있다면 오류를 발생시키거나 원하는 응답을 주지 않을 수 있습니다.

서비스를 포트에 바인딩해서 노출

전통적인 어플리케이션은 웹서버나 아피치 톰캣, 또는 JBoss같은 어필리케이션 서버에 배포됩니다. 하지만 12요소 어플리케이션은 외부의 웹서버에 배포 의존 하지 않습니다. 즉, 톰켓과 같은 Http 리스너는 서비스나 어플리케이션 자체에 내장되어야합니다. 포트 바인딩은 마이크로서비스가 자율적이고 자기 완비적인 특성을 유지하는데 필요한 기본적인 요규 사항 중 하나입니다.

ex) Spring은 tomcat, jeus 등에 배포하여 라이프사이클을 관리하거나 하였습니다. 하지만 Spring Boot는 내장형 tomcat을 이용하여 스스로 포트바인딩을하여 http를 listen합니다.

포트 바인딩을 통해 노출한다는 것은 나의 어플리케이션이 다른 어플리케이션의 백엔드 서비스가 될 수 있다는 뜻입니다.

확장을 위한 동시성

확장을 위한 동시성 원칙은 프로세스의 복제(Replication)를 통해 확장성을 가질 수 있도록 설계되어야 한다는 것입니다. 마이크로서비스에서는 서버의 자원(CPU or RAM)을 늘리는 수직적인 확장이 아니라 서버의 수를 늘리는 수평적 확장방식으로 확장해야 합니다. 다른 말로는 ScaleUp이 아닌 ScaleOut을 통한 확장이라고도 합니다. 이러한 방식을 취하면 변동되는 트래픽에 따라서 유연하게 대처할 수 있습니다.

폐기 영향 최소화

폐기 영향 최소화 원칙이란 프로세스 실행과 종료에 필요한 시간을 최소화해야하며 서버가 종료될 때에는 진행중이던 모든 일은 마무리가 되어야 한다는 원칙(Graceful shutdown)입니다. 자동화 된 배포시스템에서는 가능한 빠르게 시스템을 올리거나 내려야할 경우가 많습니다. 트래픽이 증가하면 새로운 인스턴스를 띄워 트래픽을 분산해줘야하기 때문입니다. 이를 달성하기 위해서는 마이크로서비스의 크기를 작게 유지해야합니다.

개발과 운영의 짝 맞춤

개발환경과 운영환경에는 큰 차이가 3가지 정도가 있다고 얘기되어집니다.

  1. 시간의 차이 - 개발자가 작업한 내용은 product에 반영되기까지 며칠에서 길게는 몇달이 걸릴 수 있습니다.
  2. 담당자의 차이 - 개발자가 개발한 코드를 시스템 엔지니어가 배포합니다.
  3. 툴의 차이 - 운영환경은 아파치, MySQL, 리눅스를 사용하는데, 개발자는 Nginx, SQLite, OS X를 사용할 수 있습니다.

12요소 어플리케이션에서는 개발환경과 운영환경의 차이를 최소화하여 지속적인 배포를 가능하게 하도록 요구하고 있습니다.

  1. 시간의 차이 - 개발자가 작성한 코드는 몇 시간, 심지어는 몇분만에 배포될 수 있습니다.
  2. 담당자의 차이 - 코드를 작성한 개발자들이 배포와 운영 모니터링에 깊게 관여합니다.
  3. 툴의 차이 - 개발환경과 운영환경과의 차이를 최소화합니다.

이러한 원칙은 마이크로서비스에서만 유용한 것이 아니라 모든 형태의 어플리케이션에 적용될 수 있습니다.

로그 외부화

클라우드 환경에서는 로컬 I/O를 피하는 것이 상책입니다. 주어진 인프라에 I/O가 충분히 빠르지 않다면 병목 현상이 발생할 가능성이 높습니다. 이를 방지하는 방법은 중앙 집중식 로깅 프레임워크를 사용하는 것입니다.

마이크로서비스 아키텍처에서는 시스템을 더 작은 서비스로 나누기 때문에 이런 방식은 로그가 분산될 가능성이 높으므로 로그를 중앙 집중화하는 것이 매우 중요합니다. 로그를 각 서비스 인스턴스의 로컬 저장소에 저장하면 각 서비스의 로그 사이의 연관성을 찾기가 극도로 어려워집니다.

관리자 프로세스 패키징

관리자용 테스크는 어플리케이션 본연의 서비스를 실행하는 프로세스와 동일한 환경에서 실행돼야 합니다. 관리자 프로세스란 일시적인 프로세스로 예를 들면 데이터베이스 마이그레이션, shell script 실행 등을 말합니다. 동일한 환경에서 실행되게 함으로써 개발과 운영의 짝 맞춤이 잘 이루어 질 수 있습니다. 그러기 위해서 관리자 프로세스도 클라우드 서비스와 함께 패키징하여 배포하는것이 좋습니다.

참조

스프링 5.0 마이크로서비스 2/e

https://12factor.net/

댓글0