Welcome to the cloud, Spring

프로그래머 세상에서 요즘 아이크로 서비스는 메인 화두중에 하나이다.

Microservice은 distributed, loosely coupled 소프트웨어 서비스이며, 잘 정의된 태스크들을 처리하는 시스템이다. (여기서 개인적으로 잘 정의된 태스크라는 부분이 가장 중요한 포인트인것 같다.)

Monolithic Architecture

  • 하나의 배포 아티팩트로 시스템에 배포하는 방식이다.
  • 모든 UI, 비즈니스, 데이터베이스 엑세스 로직 등이 모두 하나의 아티팩트로 패키징 됨
  • 서비스가 확장되고 관련 팀이 늘어나면 크기와 복잡도가 매우 높아지는 단점이 있음
  • 커뮤니케이이션 비용의 증대, 협업 비용이 증가하며, 확정성이 매우 떨어진다.
  • 매번 팀이 배포를 하기 위해서는 전체 어플리케이션에 대해서 리빌드를 하고, 다시 테스트를 해야하며, 배포시 다른 팀에 영향을 준다.

Microservice Architecture

  • 작고, loosely coupled, 분산 서비스를 제공
  • 큰 시스템을 작은 단위로 분리하여 관리가 쉽고, 좁은 도메인의 책임만을 가지도록 분리한다.
  • 분리된 작은 서비스들을 가진 팀은 완젼히 자신들이 컨트롤 할 수 있는 작은 단위의 서비스 코드와 인프라를 관리할수 있다.
  • 다른 팀과 독립적으로 빌드, 디플로이, 테스트를 수행할 수 있다.

Microservice Architecture 의 특징

  1. 어플리케이션 로직은 작은 단위의 컴포넌트로 분리되며, 이 컴포넌트는 타 컴포넌트와 협업을 통해서 전체 미션을 수행한다. 이때 컴포넌트는 잘 정의된 바운더리를 가져야한다.
  2. 각 컴포넌트는 작은 도메인 영역의 책임만을 가진다. 이는 완젼히 다른 컴포넌트와 독립적으로 수행된다. 마이크로 서비스는 단일파트의 비즈니스 도메인만을 책임지며, 복수의 어플리케이션들을 재 사용할 수 있다.
  3. 마이크로 서비스는 몇가지 기본 원칙을 기반으로 커뮤니케이션한다. 커뮤니케이션을 위해서는 간결한 프로토콜 (JSON, XML, HTTP) 등을 이용하여 상호 데이터를 교환한다.
  4. 각 서비스는 기술이나 언어에 대해서 다른 컴포넌트와는 관련이 없다. 즉 다양한 기술, 언어 위에서 개발이 되며, 프로토콜만 공통으로 맞추면된다.
  5. 컴포넌트는 작고, 독립적이고, 분산되어 있기 때문에 팀을 작은 단위로 유지가 가능하며, 이때 팀은 잘 정의된 책임 영역을 관리한다. 이 팀은 자신만의 단일 골을 위해서 노력하게 된다.

어플리케이션 개발 방식이 바뀌는 이유

우리는 역사적인 변곡점에 서있다. 현대 사회는 인터넷을 통해서 서로 커뮤니케이션한다. 회사들은 지역 시장을 목표로 시스템을 구성했으나 현재는 글로벌 고객들의 눈높이를 따라가야함을 이해하고 있다. 글로벌 경쟁이 치열해지고 있으며 글로벌 경쟁시장에서 고려해야하는 사항은 다음과 같은것이 있다.

  1. 복잡도를 없애야한다. - 고객은 조직의 모든 파트가 자신이 누구인지 알고자 한다. 오늘날의 어플리케이션들은 다양한 서비스들과 커뮤니케이션이 필요하며, 회사내부의 데이터센타에 있는 데이터만을 접근하지는 않는다. 또한 외부 시스템은 인터넷으로 서로 통신할 수 있어야한다.
  2. 고객은 빠른 서비스 지원을 원한다.- 고객은 더이상 과거처럼 다음 버젼을 위해서 1년을 기다리지 않는다. 대신에 그들은 소프트웨어 프로덕트가 새로운 기능이 생기면 전체 릴리즈를 위해 오랜기간 기다리기를 원하지 않는다.
  3. 성능 과 확장성 - 글로벌 어플리케이션은 얼마나 많은 트랜잭션 규모가 시스템에서 처리 되어야하는지 예측하기가 극도로 어렵다. 갑작스러운 트랜잭션 요청이 들어올 수도 있다. 어플리케이션은 복수개의 서버들로 빠르게 스케일업 할 수 있어야하고, 필요없을때에는 스케일 축소가 가능해야한다.
  4. 고객은 어플리케이션이 항상 자신의 업무를 수행하도록 원한다. - 고객은 한번의 클릭으로 경쟁자의 간섭없이 자신의 일을 하기를 원한다. 이렇게 하기 위해서는 매우 높은 회복성을 가져야한다. 한 부분의 실패 혹은 문제가 전체의 어플리케이션에 확장되지 않도록 해야한다.

위와 같은 문제를 해결하기 위해서 시스템을 작게 나누고, 다른 팀과 분리되어 개발 및 배포가 될 수 있도록 해야한다. 우리의 어플리케이션을 작은 서비스로 나누고 “Unbundle” 하게 되면 Monolithic 아티팩트로 부터 해방될 수 있다. 그리고 다음과 같은 이점을 가질 수 있다.

  • 유연성 : 디커플된 서비스는 조합되고 재정렬되어 새로운 기능을 빠르게 고객에게 전달될 수 있다. 코드의 유닛을 더 작게 만들면 적은 복잡도를 가지며, 더 짧은 시간에 테스트, 디플로이가 가능하다.
  • 탄력성 : 디커플된 서비스는 더이상 “ball of mud”가 아니다. 하나의 실패가 전체에 영향을 주지 않는다. 실패는 지역화 하여 어플리케이션에서 매우 국소적으 고립시켜 전체 영향을 주지 않도록 한다. 또한 회복이 불가능한 에러에 대해서 그레이스풀 하게 디그레이드 하도록 해준다.
  • 확장성 : 디커플된 서비스는 쉽게 수평확장이 가능하다. 이를 통해서 기능적/서비스적으로 확장이 가능하다.

마이크로서비스 시스템은 다음과 같은 등식이 성립된다.

Small, Simple and Decoupled Services = Scalable, Resilient and Flexible Applications

Cloud 가 정확히 무엇인가?

클라우드 라는 단어는 매우 자주 사용되어지고 있다. 클라우드는 크게 다음과 같이 나눠진다.

  • Infrastructure as a Service (IaaS)
  • Platform as a Service (PaaS)
  • Software as a Service (SaaS)

이해를 돕기 위해서 다음과 같이 음식을 먹으려고 하는데 4가지 선택지가 있다고 하자.

  1. 집에서 음식을 만들 수 있다.
  2. 마트에 가서 이미 만들어진 음식을 사고, 집에와서 데워서 먹을 수 있다.
  3. 음식 배달 서비스를 이용해서 집에서 먹을 수 있다.
  4. 차를타고 레스토랑에 가서 음식을 먹을 수 있다.

위 예제에서 차이가 있는 옵션은 “누가 음식을 만들 책임이 있는가” 그리고 “어디서 음식을 만드는가” 이것이다. 여기서 전제는 음식을 집에서 먹으면 모든 일을 해야한다는 것이다. 자신이 가진 오븐을 이용하고, 재료가 이미 집에 있어야 한다.

  • IaaS 는 상점에서 음식을 사오는 개념으로 상점에서는 상점내 인프라를 이용해서 음식을 만들어 오는 것이다. 이때 음식점에 쉐프를 이용하고, 오븐을 이용할 수 있다. 그러나 여전히 음색을 데우고, 접시를 세척하는 행위는 해야한다.
  • PaaS 는 음식에 대한 책임은 여전히 있지만 다양한 벤더에 의존하여 음식을 만들 수 있는 것이다. 예를 들어 접시나 가구 등을 당신이 제공하고, 음식점 주인은 오븐과 재료 그리고 요리사를 제공해준다.
  • SaaS 는 레스토랑에 가서 준비된 음식을 먹는 것과 같다. 음식을 먹고나면 비용을 지불하면 되고, 접시를 세척하거나 할 필요는 없다.

여기서 핵심은 인프라를 관리하는 것과 어플리케이션 개발을 위해 어떠한 기술을 쓸 것인지에 대한 책임 영역을 결정하는 것이다. IaaS는 기본적인 인프라를 제공해주는 것이다. 그리고 기술을 선택하고 어플리케이션을 만드는 것은 본인이 해야한다. 반면 SaaS는 서비스를 이용만 하면 되고, 인프라나, 기술을 선택하지 않아도 된다. 최근에는 이러한 클라우드 플랫폼이 발전하여 Function as a Service(FaaS)나 Container as a Service(CaaS)로 발전했다. AWS에서 Lambda 기술이나, 구글의 클라우드 펑션이 대표적인 FaaS이다. 그리고 CaaS로는 Docker 서비스를 가상 서버로 제공해주는 서비스들이 있다.

이러한 클라우드 서비스를 기반으로 마이크로 서비스를 구현할 수 있다. 중요한 포인트는 마이크로 서비스는 분산된 시스템에서 작은 단위의 컴포넌트를 나누고 서로 상호작용으로 하나의 서비스를 수행하는 것이다.

마이크로 서비스는 코딩 그 이상이다.

마이크로 서비스는 코딩하는것을 넘어서 견고하게 서비스를 수행할 수 있어야 한다. 이러한 기능을 위해서 다음과 같은 요건들이 있따.

  • Right-sized - 운영하는 마이크로 서비스가 절절한 사이즈인지 어떻게 증명할 수 있을까? 적합한 사이즈라는 것은 변화에 대해서 빠르게 대응할 수 있어야 하고 전체 시스템이 멈추는 것을 줄일 수 있어야한다.
  • Location transparent - 이는 물리적으로 분산된 환경에서 복수개의 인스턴스를 빠르게 실행하고, 정지 시킬 수 있는가이다.
  • Resilient - 어떻게 마이크로 서비스 사용자들을 보호할 것인가에 대한 사항이며, 이를 위해 서비스가 실패한경우 어떻게 적절한 대응책으로 라우팅할 것인가, 그리고 어떻게 “fail-fast”를 실현할 것인가에 대한 사항이다.
  • Repeatable - 어떻게 새로운 인스턴스들이 동일한 환경으로 동일한 코드 기반으로 프러덕션의 다은 서비스에서 운영되는 것을 보장할 것인가에 대한 사항이다.
  • Scalable - 어떻게 비동기 프로세싱을 이용하고, 서비스들 간의 직접적인 의존관계를 최소화 할 것인가 그리고 그레이스풀하게 스케일을 확장할 수 있는가에 대한 사항이다.

마이크로 서비스를 위한 6가지 카테고리

  • Core development patterns
  • Routing patterns
  • Client resiliency patterns
  • Security patterns
  • Logging and tracing patterns
  • Build and deployment patterns

핵심 개발 패턴

  • 서비스 크기 : 비즈니스 도메인을 얼마의 크기로 나눠서 개발 운영할 것인가에 대한 사항이다. 사실 이 부분은 마이크로 서비스의 가장 핵심 부분이다.
  • 커뮤니케이션 프로토콜 : 컴포넌트들간, 서비스들간에 어떠한 프로토콜을 이용할 것인가에 대한 사항, HTTP, JSON, Thrift 등.
  • 인터페이스 설계 : 인터페이스 설계는 매우 중요하다. 의도한 대로 URL이 잘 작성이 되었는가, 그리고 그 의도를 잘 대변하는 서비스인가 등
  • 설정 관리 : 서로다른 환경에서 핵심 코드를 변경하지 않고 설정을 반영할 수 있는가 여부이다.
  • 서비스간에 이벤트 처리 : 어떻게 최소화된 하드코드화된 의존성을 가지기 위해서 이벤트를 주고 받을 것인가?

마이크로서비스 라우팅 패턴

시스템 규모에 따라 매우 많은 서비스들이 존재한다. 요청에 따라 어떻게 적절하게 해당 요청을 처리하기 위해 서비스의 위치를 발견하고, 전달할 것인가에 대한 사항이다. 물리적 위치를 하드코딩 하는것이 아닌 추상화된 위치 운영이 필요하다.

  • 서비스 디스커버리 : 클라이언트 어플리케이션이 자신이 원하는 서비스를 하드코딩 하지 않고서도 서비스에 접근할 수 있는가에 대한 사항이다. 또한 서비스가 문제가 있을때 해당 서버로 요청을 하지 않고 정상적인 서비스 컴포넌트를 찾을 것인가에 대한 사항이다.
  • 서비스 라우팅 : 어떻게 하나의 엔트리 포인트로 접근해서 자신이 원하는 서비스로 라우팅 할 것인지에 대한 정책을 관리할 것인가에 대한 사항

회복 탄력성 패턴

마이크로 서비스는 상당히 분산된 시스템 아키텍처이다. 이는 하나의 문제에 대해서 매우 민감하게 반응 하도록 설계가 되어야하며, 다른 서비스에 영향을 적게 해야한다.

  • 클라이언트 사이드 로드 밸런싱 : 어떻게 서비스 인스턴스의 위치를 캐싱할 것인가? 그것도 클라이언트 사이드에서 이를 수행할 수 있도록 한다.
  • 서킷 브레이커 패턴 : 실패가 발생했얼때 클라이언트가 계속해서 요청을 보내지 않도록 하여 성능문제가 일어나지 않게 할 것인가에 대한 사항이다. 성능이 느려지면 리소스를 클라이언트에 캐싱하고, 실패가 나면 fail-fast 로 적합하게 클라이언트가 반응 하도록 한다.
  • Fallback 패턴 : 서비스가 실패가 발생하면 플러그인 메커니즘으로 적절한 대체 응답을 수행하는 것이다.
  • Bulkhead 패턴 : 마이크로 서비스 어플리케이션들은 멀티 분산 리소스를 이용하여 자신의 작업을 수행한다. 이때 하나의 서비스의 잘못된 행동을 구분해내어 나머지 어플리케이션에 부정적인 영향을 안 줄 것인가에 대한 사항이다.

시큐리티 패턴

  • Authentication : 서비스를 요청하는 유저가 정말 그 사람이 맞는지를 검사한다.
  • Authorization : 인증된 유저가 자원을 요청할때 그 자원은 그 유저에게 허용이 되었는지 검사한다.
  • Credential management and propagation : 인증된 사용자가 어떻게 지속적으로 자신이 원하는 서비스를 호출하게 할것인지에 대한 것이다. 특히 OAuth2와 JavaScript Web Tokens 을 이용한 토큰 기반의 서비스를 할때 인증과 허가에 대한 정보를 서버와 서버사이에 서로 공유하는 기술이다.

로깅, 트리에싱 패턴

마이크로 서비스 아키텍처의 멋진 부분중에 하나는 모놀리틱 아키텍처가 가진 하나의 장애가 전체 서버의 장애가 되는 사항에 대해서 다른 서비스에 영향을 주지 않고 독립적으로 수행되는 것이다. 그러나 마이크로 서비스의 어려운점은 분산되어 있는 로깅을 하나로 그것도 해당 요청을 트레이싱 할 수 있도록 연결해서 수집하는 것은 어려운 일이다.

  • 로그 상관관계 : 어떻게 하나의 유저 트랜잭션에 대해서 서버사이드 간의 로그를 묶어줄 수 있는가에 대한 것으로 통합 아이디를 이용하여 서비스 콜을 수행할때 이들을 가지고 다니는 것이다. 이는 전체 로그들을 하나로 묶어 줄수 있는 기술이다.
  • 로그 수집 : 각 서비스에서 생성된 로그들을 하나로 수집하는 것으로 통합 아이디를 이용하여 해당 로그 정보들을 집계해서 볼 수 있다.

빌드 디플로이 패턴

  • 빌드 디플로이 파이프라인 : 반복적인 빌드와 디플로이와 같은 작업을 한번의 버튼 클릭으로 수행할 수 있도록 한다.
  • 인프라 스트럭쳐 코드 : 어떻게 서비스 코드를 프로비져닝 할 것인가에 대한 것으로 관리된 소스 컨트롤을 수행할 수 있다.
  • 불변 서버 : 마이크로서비스 이미지를 한번 생성하면 디플로이 되고 난뒤에는 변경되지 않을 것인가에 대한 보장

스프링 클라우드 기술

  1. Spring Boot 스프링을 매우 간단하게 설정, 개발할 수 있도록 해준다.
  2. Spring Cloud Config 컨피그 서버를 이용하여, 일관된 설정 정보를 변경, 반영한다.
  3. Spring Cloud service discovery Eureka, Consul 등이 있으며, 서비스를 등록하고, 유효한지 상태정보를 저장한다.
  4. Spring Cloud/Netflix Hystrix and Ribbon Hystrix 는 resiliency 패턴을 지원하며, 서킷브레이커, 벌크헤드 패턴 지원 Ribbon은 클라이언트 사이드 로드 밸런서이며, 서비스위치정보를 클라이언트가 캐싱하는 기술이다.
  5. Spring Cloud/Netflix Zuul 요청을 적절한 서비스로 라우팅하도록 해준다.
  6. Spring Cloud Stream 스프링 클라우드 기술로 서비스간 매시지 패싱을 담당한다.
  7. Spring Cloud Sleuth 로그를 위한 Correlation Id를 발급한다. 이때 Zipkin을 함게 이용하여 서비스 로그를 통합 수집할 수 있다.
  8. Spring Cloud Security 스프링 시큐리티를 클라우드에 적용한다.

마이크로서비스 아키텍처와 스프링 클라우드 기술에 대한 전반적인 사항을 알아보았다. 정말 많은 기술들이 사용된다. 앞서 이야기한 단순 코드만이 아니라 서비스를 안정적으로 유지하기 위한 많은 인프라 및 서비스가 있음을 알게 되었다.