Rath World Notes by Jang-Ho Hwang

19Jun/12Off

CloudFoundry 구조 살펴보기 시작

Posted by Jang-Ho Hwang

지난번 CloudFoundry 소개글에 이어 이번에는 CloudFoundry 구조를 살펴본다.

일단 rightscale 블로그 글에 첨부된 이미지를 무단 링크하고 시작한다.

항목 별로 간단히 나열해보면,

  • Router
  • Cloud Controller
  • Droplet Execution Agent (그림에서의 App Exec Engine)
  • Services

이렇게 나눠볼 수 있다. Health Manager는 각 구성요소들이 잘 동작하는지 주기적으로 체크하다가 이상한 녀석이 있으면 신고해주는 역할을 하므로 설명을 생략한다. 각 구성요소들이 어떤 역할을 하는지 알아보기전에 사용자 기준 시나리오를 설명하는 것으로 시작하는게 좋을 것 같다.

먼저 CloudFoundry를 주도적으로 사용하는 웹개발자 입장에서의 흐름을 살펴본다.

  • VMC -> Router -> Cloud Controller -> (요청 종류에 따라 분기)

VMC는 CloudFoundry 이용자(웹개발자)가 사용하는 CLI 툴이다. VMC를 통해 로그인도 하고, 웹 어플리케이션을 업로드 하기도 하고, 인스턴스 수를 늘렸다 줄이기도 하고, 새 서비스(mysql, mongodb 등)를 자신이 올린 앱과 bind 하기도 한다. 로그파일을 보는 것도 vmc 로 한다. 지금 언급한 vmc 의 기능들은 모두 Cloud Controller 속에 구현되어있다. 그런데 왜 Router를 거치는가? 아니 Router 가 도대체 무엇인가?

Router 는 우리가 알고 있는 하드웨어 라우터가 아니다. Ruby 로 만들어진 HTTP 라우터 서버다. Host 헤더를 보고 어느 서버로 포워드해야하는지만을 알고 있는 녀석이다. 이 Ruby 로 만들어진 Router 는 이슈가 좀 있지만 어찌됐든 오직 하나의 목적을 가지고 있다. 외부에서 HTTP 요청이 오면 이 요청을 담당하는 서버가 어디있는지 알려주는 것이다.

Cloud Controller의 기능들도 모두 HTTP로 제공되기 때문에 구지 Cloud Controller를 Router 밖으로 분리할 이유가 없기 때문에 Router 아래에 위치한 것이다. 이 설정은 얼마든지 바꿀 수 있다. Cloud Controller 설정파일을 보면 use_nginx: true|false 가 있기도 하다. Router랑 안붙이고 독립 웹서버(Nginx)에 붙일 수도 있고, 아무것도 없이 Cloud Controller 가 80 포트를 바인드해서 서비스를 해도 (이중화 문제가 생기겠지만) 된다.

 

이제 CloudFoundry를 투명하게 사용하게 되는 엔드유저 입장에서의 흐름을 살펴보자.

  • 웹브라우저 -> Router -> Droplet Execution Agent -> Services

Drop Execution Agent (이하 DEA)는 Web Application Server 라고 보면 된다. 실제로 apache2+php 가 돌거나 python, ruby vm이 돌거나 Tomcat 이 돌아가는 곳이다.

Services는 WAS에 붙어있는 MySQL 이라고 이해하면 된다. 실제로는 MySQL 말고 NoSQL이 될 수도 있고 memcached 나 RabbitMQ 가 될 수도 있다. WAS가 사용하는 백엔드 서비스들을 총칭하는 것이 Service 이다.

딱히 이해하기 어려운 부분이 없다. 그런데 앱이 여러개이고 앱당 WAS 인스턴스가 늘어나게 되면 그 숫자만큼 DEA 가 필요할텐데 앱을 적당한 DEA에 배포하고 이를 Router 에게 알려주고 하는 등의 작업들은 누가 할까. Cloud Controller 가 한다. Cloud Controller 는 정말로 많은 일을 한다. 다행히 엔드유저의 요청을 처리할 때는 Cloud Controller 가 조금도 관여하지 않기 때문에 논리적으로 기능상 병목이 될 수는 있을지언정 트래픽이 몰려서 엔드유저가 사용하는 웹서비스가 느려진다거나 하는 등의 일은 발생하지 않는다.

잠깐, 위 그림에도 없고 아직 언급하지 않았지만 CloudFoundry 에서 아주 중요한 것 하나를 생략했다. 그것은 NATS 이다. NATS는 CloudFoundry를 설계한 Derek Collison 이 만든 메시지 버스로  경량 publish, subscribe 프로토콜 및 그 구현체이다.  CloudFoundry의 모든 컴포넌트들 (Router, Health Manager, Cloud Controller, Droplet Execution Agent, Services) 의 설정파일에 mbus: nats://1.2.3.4:4222/ 이렇게 NATS 서버 주소를 쓰게 되어있다. 그래서 새 DEA를 동적으로 붙인다던지, 잘 동작하던 DEA 서버 하나를 종료한다던지 할 경우 NATS를 통해 온 시스템에 해당 이벤트가 전파된다.

이렇게 보면 CloudFoundry 에서 NATS 가 없다면 절대로 돌아가지 않는, CloudFoundry 각 구성요소들을 한데 묶어주는 중요한 역할을 하는 것이다. 그러나 다행히도 NATS 또한 Cloud Controller 처럼 엔드유저의 요청수와 무관하다. 그렇기 때문에 DEA 인스턴스 2,000여개가 돌면서 NATS 에게 이벤트를 발송하더라도 딱히 문제 될 것은 없다. 게다가 publish-subscribe 특성상 메시지가 쌓이는 일도 없다. NATS 서버가 여러개고 각 메시지가 idempotent 라면 서버 하나 몇시간 죽었다 올라와도 전체 시스템에 영향을 미칠 수 없는 것이다.

여기에 하나 더, 각 컴포넌트 내에서 비동기 작업큐로 사용하는 Resque 가 있다.  CloudFoundry 각 컴포넌트들이 거의 다 Ruby 로 작성되어있기도 하고 (Ruby는 타언어에 비해 쓰레드 처리가 약하다), 경량 pubsub 인 nats 에 실어보내기엔 꽤 규모가 되지만 그렇다고 persist 할 필요가 없는 대부분의 작업들에 대해 Resque 를 사용한다. Resque 는 Redis backed 므로 CloudFoundry 인스턴스 내 어딘가에 Redis 가 하나 돌고있어야 한다.

Router 에 관련하여 하나 더. 초반에 말했듯이 CloudFoundry Router 는 Ruby 로 작성되어있다. 사용자 요청을 Ruby 가 바로 받는다니 어쩐지 이상하지 않은가? 실제 환경에서는 당연히 Router 앞에 Nginx 가 붙어있다. Router version 1의 경우 Nginx 가 HTTP 요청을 제일 먼저 받아서 유닉스 소켓을 통해 Ruby router 에게 by pass 하고 Router 는 HTTP 헤더를 보고 어느 DEA로 분기해야하는지 잡아서 그 DEA로 입력스트림을 포워드하고 응답이 올때가지 기다렸다가 이를 받아 다시 Nginx 로 보내고 웹브라우저로 리턴되는 식이였다. 아니 이게 말이 되는가, Ruby Router 의 i/o 병목 어쩔건가. Nginx 웹서버는 로그분석이랑 SSL 처리를 위해 쓴다고 하면 할 말이 없지만 이름(Router)에 어울리지 않는 일을 하고 있었다.

그래서 Router version 2 가 나왔다. Router version 2에는 CloudFoundry 용 Nginx Lua 모듈이 있다. 이 모듈의 역할이 무엇인가하면, HTTP 호스트 헤더를 받아 Ruby Router에게 JSON over HTTP로 묻는다. '이 Host 가려고 하는데 위치가 어디니?' 하면 위치만 리턴한다. 그러면 Nginx Lua 모듈이 이 일련의 과정을 처리하여 proxy_pass 에 들어갈 최종 host/port를 넘기는 것이다. 뭔가 복잡하게 보일수도 있지만 최소한 더이상 Ruby Router가 웹어플리케이션의 i/o를 파이프라이닝 하는 일은 없다. 정말 라우팅 테이블만 관리하는 컴포넌트가 된 것이다.

독자도 지치고 나도 지치고.

Tagged as: 1 Comment
11Jun/12Off

An Overview of CloudFoundry

Posted by Jang-Ho Hwang

몇달전부터 CloudFoundry.org를 애용하고 있다.

CloudFoundry는 VMware에서 Ruby로 만든 오픈소스 PaaS (Platform as a Service)이다. 용어에 Platform이 들어가기 시작하면 너도 나도 다른 생각을 하는 경우가 부지기수니 PaaS 라는 용어는 사용하지 않고 CloudFoundry에 집중하도록 하겠다.

CloudFoundry가 이용자(개발자)에게 주는 핵심가치는 편리함이다. CloudFoundry를 사용할 경우 아래와 같은 (개발자 입장에서) 귀찮은 작업들로부터 해방될 수 있다.

  • 관계형 데이터베이스(MySQL, PostgreSQL) 설치/운영
  • NoSQL 무리들(Redis, MongoDB) 설치/운영
  • MQ(Rabbit) 설치/운영
  • 웹사이트 주소가 중요하지 않을 경우, 도메인 등록
  • 웹서버의 가상호스트 설정
  • 웹서버와 어플리케이션 서버간의 프록시 설정
  • 트래픽에 따라 웹어플리케이션 서버 개수 줄이거나 늘리기
  • 배포

위 항목들을 살펴보라. 통채로 보면 지긋지긋하게 보일 수 있겠지만 각 항목 하나씩만 본다면 어려운 것이 없다. 그러나 묘하게 사람 귀찮게 하는 일들이다.

MySQL 을 설치했다 치자. 디비이름도 결정해야 하고 계정이름과 비밀번호도 결정해야 한다. 결정한 뒤에는 어딘가 적어둬야하고 웹서버코드에 옮겨놓거나 설정파일로 뗀다거나 등의 작업을 해야한다.

지인들과 사용할 기능 위주의 웹페이지를 만들었다고 치자. 이런 경우라면 호스트명이 중요하진 않다. 어디 빌붙어사는 느낌이 팍팍나는 our-rooms.cloudfoundry.com 뭐 이런거면 어떤가? 돌아가면 됐지. 그러나 CloudFoundry 가 해주는 것처럼 이용자에게 서브도메인을 자동으로 발급해주는 제품을 사용하지 않는다면, 비록 1분만에 작업이 끝나더라도 그 작업을 해야한다.

DNS 레코드 수정하는 것도 귀찮은 작업이지만, 웹서버에 가상호스트 설정하는 것 또한 귀찮은 작업이다. 웹프로그램 하나 만들때마다 Nginx server 블럭이 하나 추가된다. 물론 다른 호스트꺼 복사해서 붙여서 호스트명과 WAS 프록시 주소만 바꾸면 된다. 그리고 nginx -s reload. 이것 역시 30초도 안걸리는 작업이다. 하지만 그 작업을 해야한다.

트래픽이 늘어나서 WAS 2개를 추가했다. nginx upstream 블럭에 서버 두 줄 추가하면 된다. 편집기로 nginx 설정파일을 열었는데 새로 추가한 WAS 서버 호스트랑 포트가 잘 기억이 나지 않기도 하고, 서버 10대중 3대를 빼는거라면 빼야할 서버 3개가 무엇인지 정확히 파악하느라 일시적인 스트레스를 받기까지 해야한다.

CloudFoundry는 시스템엔지니어와 말 한마디도 섞지 않으면서 서비스 오픈까지 하고 싶은 개발자를 위한 제품이다. 시스템엔지니어 업무들 일부분이 통합되고 자동화된 것이다.

그러므로 자신이 만드는 서비스가 시스템 특성이나 설정을 많이타는 특수한 과업을 견뎌내야하는 상황이라면 CloudFoundry 를 검토하지 말것을 추천한다. CloudFoundry 는 개발자가 딱히 신경쓰지 않아도 될 부분들로부터 자유를 주겠다는 것이지, 중고급 시스템 엔지니어 인건비를 절감해주겠다는 제품이 아니다. 시스템 엔지니어가 눈감고 졸면서 처리할 수 있는 일들을 자동화해주는 것이다.

다음에는 CloudFoundry 구성요소와 설치에 대한 이야기를 이어서 하겠다. CloudFoundry 설치에 대한 내용을 Overview와 함께 풀게되면 포스트의 특정 파트만 하드코어해지는 문제가 생기므로.

Tagged as: 1 Comment