블로그 이미지
평범하게 살고 싶은 월급쟁이 기술적인 토론 환영합니다.같이 이야기 하고 싶으시면 부담 말고 연락주세요:이메일-bwcho75골뱅이지메일 닷컴. 조대협


Archive»


 
 

분산 로그 수집기 Fluentd 소개


조대협 (http://bcho.tistory.com)



요즘 들어 빅데이타 분석 관련 기술들을 보다보니, 역시나 여러 데이타 소스에서 데이타를 수집해 오는 부분이 여러 데이타 소스를 커버해야 하고, 분산된 여러 서버에서 데이타를 수집해야 하는 만큼 수집 컴포넌트의 중요성이 점점 더 올라가는 것 같다.

그래서 요즘 빅데이타를 위한 데이타(및 로그) 수집 플랫폼을 보고 있는데, 예전 Flume 등 여러 로그 수집 솔루션이 있었는 것에 비해서 조금 정리된 느낌이라고나 할까?  Scribed, Fluentd 그리고 ELK (Elastic Search + Logstash + Kibana 조합)에서 사용되는 Logstash등이 있는데, 대부분 Fluentd와 Logstash로 수렴 되는 것 같다. 양쪽 모두 오픈소스이고 별도의 엔터프라이즈 라이센스 정책을 가지고 있다.

Logstash는 아키텍쳐 적응에 대한 유연성과 연동 솔루션에 대한 호환성을 강조하고 있기 때문에 타 솔루션과 연동이 강하고 반면, Fluentd는 아키텍쳐의 단순성과 이를 기반으로 한 안정성을 초점을 두고 있다. 그래서 아키텍쳐 구성이나 설정이 간단하다.


이 글에서는 Fluentd에 대한 간략한 개념과 사용 방법에 대해서 알아보도록 하겠다.

Fluentd를 이용한 로그 수집 아키텍쳐

Fluentd를 이용한 로그 수집 아키텍쳐를 살펴보면 다음과 같다.

아래 그림과 같이 각 서버에, Fluentd를 설치하면, 서버에서 기동되고 있는 서버(또는 애플리케이션)에서 로그를 수집해서 중앙 로그 저장소 (Log Store)로 전송 하는 방식이다.




위의 그림은 가장 기본적인 구조로 Fluentd가 로그 수집 에이전트 역할만을 하는 구조인데, 이에 더해서 다음과 같이 각 서버에서 Fluentd에서 수집한 로그를 다른 Fluentd로 보내서 이 Fluentd가 최종적으로 로그 저장소에 저장하도록 할 수 도 있다.


중간에 fluentd를 넣는 이유는, 이 fluentd가 앞에서 들어오는 로그들을 수집해서 로그 저장소에 넣기 전에 로그 트래픽을 Throttling (속도 조절)을 해서 로그 저장소의 용량에 맞게 트래픽을 조정을 할 수 있다.

또는 다음 그림과 같이 로그를 여러개의 저장소에 복제해서 저장하거나 로그의 종류에 따라서 각각 다른 로그 저장소로 라우팅이 가능하다.


Fluentd 내부 구조

Fluentd를 이용해서 로그 수집 아키텍쳐를 구성하는 방법을 대략적으로 알아보았는데, 그렇다면 Fluentd 자체의 구조는 어떻게 되어 있을까?

Fluentd는 크게 다음 그림과 같이 Input,Parser,Engine,Filter,Buffer,Ouput,Formatter 7개의 컴포넌트로 구성이 된다.  7개의 컴포넌트중 Engine을 제외한 나머지 6개는 플러그인 형태로 제공이 되서 사용자가 설정이 가능하다.

일반적인 데이타 흐름은 Input → Engine → Output 의 흐름으로 이루어 지고,  Parser, Buffer, Filter, Formatter 등은 설정에 따라서 선택적으로 추가 또는 삭제할 수 있다.



Input

Input은 로그를 수집하는 플러그인으로, 다양한 로그 소스를 지원한다. HTTP, tail, TCP 등 기본 플러그인 이외에도, 확장 플러그인을 통해서 다양한 서버나 애플리케이션으로 부터 다양한 포맷의 데이타를 수집할 수 있도록 해준다.

Parser (Optional)

Input 플러그인을 통해서 데이타를 읽어도 데이타 포맷이 Fluentd에서 지원하지 않는 데이타 포맷인 경우가 있기 때문에, 이 데이타를 파싱 하기 위해서, Parser 플러그인을 선택적으로 사용할 수 있다. Regular expression  기반으로 스트링을 Parsing 하는 플러그인 뿐 아니라, apache, nginx, syslog등 다양한 포맷의 데이타를 파싱할 수 있는 플러그인을 제공한다.

Filter (Optional)

Filter 플러그인을 읽어드린 데이타를 output으로 보내기 전에, 다음과 같은 3가지 기능을 한다.

  • 필터링

  • 데이타 필드 추가

  • 데이타 필드 삭제 또는 특정 필드 마스킹

필터링은 특정 데이타만 output 필드로 보내고, 나머지는 버리도록 한다. 예를 들어 로그 데이타에 “seoul”이라는 문자열이 있을 경우만 로그 서버로 보내거나 “error”, “warning”과 같은 특정 패턴이 있을 경우에만 로그 저장소로 보내도록할 수 있다.


데이타 필드 추가는 기존 들어온 로그 데이타에 데이타를 전송한 서버명 (Host명)등을 추가해서 로그 저장소로 보낼 수 있다.

마지막으로 데이타 필드 삭제는 불필요한 필드를 삭제하거나 개인 정보등 민감 정보를 삭제하거나 해쉬화하여 데이타 저장소로 보낼 수 있는 기능을 한다.

Output

Output은 Input 플러그인과 반대로, 앞에서 필터링된 데이타를  데이타 저장소 솔루션에 데이타를 저장하도록 한다. (mongodb나 AWS S3 , Google의 Big query등)

Formatter (Optional)

Output 플러그인을 통해서 데이타를 저장소에 쓸 때, Formatter 를 이용하면 쓰는 데이타의 포맷을 정의할 수 있다.(cf. Input의 parser가 포맷에 맞게 읽는 플러그인이라면, Formatter는 Output을 위한 포맷을 지정하는 플러그인이라고 보면 된다.)

Buffer (Optional)

Input에서 들어온 데이타를 바로 Output으로 보내서 쓰는것이 아니라 중간에 선택적으로 Buffer를 둬서 Throttling을 할 수 있다. 버퍼는 File과  Memory 두가지를 사용할 수 있다.

간단하게 구조와 작동 원리를 보면 다음과 같다.



<그림. fluentd의 로그 writing 흐름>

원본  http://docs.fluentd.org/articles/buffer-plugin-overview


버퍼에는 로그데이타를 분리하는 tag 단위로 chunk가 생성이 된다.

chunk는 태그별 큐라고 보면 된다. 예를 들어 error, info, warning, user 와 같이 태그를 분리하면 error 로그는 error chunk에 저장이 되고, info 로그는 info chunk에 저장된다.

Chunk에 데이타가 쌓여서 buffer_chunk_limit 만큼 chunk가 쌓여서 full이 되거나, 또는 설정값에 정의된 flush_interval 주기가 되면 로그 저장소로 로그를 쓰기 위해서 Queue에 전달이 된다.


<그림. Memory buffer 설정 예제>

참고 : http://docs.fluentd.org/articles/buffer-plugin-overview


다음 Queue에서는 데이타를 읽어서 로그 저장소에 데이타를 쓰는데, 로그 저장소에 문제가 없다면 바로 로그가 써지겠지만, 네트워크 에러나 로그 저장소 에러로 로그를 쓰지 못할때는 retry_wait 시간 만큼 대기를 한 후에, 다시 쓰기를 시도한다. 다시 쓰기를 실패하면 전에 기다린 시간의 2배 만큼, 또 실패하면 또 2배만큼을 기다린다. (1초, 2초, 4초,8초…) 다시 쓰기 시도는 설정값에 지정된 retry_limit 횟수까지 계속 진행한다.

만약에 Queue 가 차버렸을때 처리에 대한 정책을 설정할 수 있는데, “exception”과, “block” 모드 두가지고 있고, exception 모드일 경우에는 BufferQueueLimitError 를 내도록 하고, block 모드의 경우에는 BufferQueueLimitError가 해결될때 까지, input plugin을 중지 시킨다 (더이상 로그를 수집하지 않는다는 이야기).

Queue가 차버렸을때 다른 처리 방법으로는 큐가 다 찾을때, Sencondary output을 지정해서, 다른 로그 저장소에 로그를 저장하는 방법이 있다. 예를 들어 로그를 mongodb에 저장하도록 했는데, mongodb 나 네트워크 장애로 로그를 쓸 수 없는 경우에는 secondary output을 AWS S3로 지정해놓고, S3로 로그를 일단 저장하게 하고 나중에 mongodb가 복구된 후에, S3에서 다시 mongodb로 로그를 수집하는 방식을 취할 수 있다.



<그림. Secondary output 설정 예제>

출처 : http://docs.fluentd.org/articles/buffer-plugin-overview


Buffer 플러그인과, 에러 처리에 대한 자세한 내용은 http://docs.fluentd.org/articles/buffer-plugin-overview 를 참고하기 바란다.


데이타 구조

다음으로 Fluentd가 내부적으로 어떻게 로그 데이타를 핸들링 하는지 데이타 구조를 살펴보면 다음과 같다.



출처 :http://pt.slideshare.net/frsyuki/fluentd-set-up-once-collect-more


데이타는 크게 3가지 파트로 구성된다. Time, tag, record

  • Time : 로그데이타의 생성 시간

  • Record : 로그 데이타의 내용으로 JSON형태로 정의된다.

  • Tag : 이게 가장 중요한데, 데이타의 분류이다. 각 로그 레코드는 tag를 통해서 로그의 종류가 정해지는데, 이 tag에 따라서 로그에 대한 필터링,라우팅과 같은 플러그인이 적용 된다.

간단한 테스트

테스트 환경은 맥북을 기준으로 하였다.

http://docs.fluentd.org/articles/install-by-dmg 를 따라서 테스트를 하면 되는데, 먼저 fluentd를 받아서 인스톨을 한다.

인스톨이 끝나면, fluentd 프로세스인 td-agent는 /opt/td-agent/usr/sbin/에 인스톨이 된다.

그리고 디폴트 설정 파일은 /etc/td-agent/td-agent.conf에 저장된다.

td-agent.conf의 내용을 보면 다음과 같다.



<ROOT>

 <match td.*.*>

   type tdlog

   apikey xxxxxx

   auto_create_table

   buffer_type file

   buffer_path /var/log/td-agent/buffer/td

   <secondary>

     type file

     path /var/log/td-agent/failed_records

     buffer_path /var/log/td-agent/failed_records.*

   </secondary>

 </match>

 <match debug.**>

   type stdout

 </match>

 <source>

   type forward

 </source>

 <source>

   type http

   port 8888

 </source>

 <source>

   type debug_agent

   bind 127.0.0.1

   port 24230

 </source>

</ROOT>



<source> 부분을 보면 type이 http, port가 8888인 정의가 있다. 이 정의는 http://localhost:8888 로 부터 로그를 수집하겠다는 정의이다.

다음 <match>부분을 보면 <match debug.**> 라는 정의로 태그가 debug.**  로 정의된 로그에 대해서 type stdout으로, stdout (화면)으로 바로 출력하겠다는 정의이다.

http://localhost:8888/{debug.**} 로 들어오는 요청에 대해서 stdout으로 로그를 출력하겠다는 설정이다.


설정 파일을 확인했으면, 이제 기동을 해보자

/opt/td-agent/usr/sbin 디렉토리에서 -c 옵션으로 설정 파일을 지정하고 td-agent를 다음과 같이 실행해보자

% ./td-agent -c /etc/td-agent/td-agent.conf


에이전트가 실행되었으면 curl 명령을 이용하여 http://localhost:8888/debug.test 로 {"json":"message"} 로그 문자열을 전송해보자

% curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test


다음은 실행 결과 이다.





다음과 같이 td-agent가 기동된 후에,  맨 아랫줄에 debug.test 라는 태그 이름으로 {“json”:”message”}라는 로그가 수집되어 출력된것을 볼 수 있다.

데몬으로 실행하기

앞에서는 CLI상에서 foreground로 실행을 하였는데, 맥에서 서비스로 백그라운드 작업으로 실행을 할 수 있다. 실행 방법은

%sudo launchctl load /Library/LaunchDaemons/td-agent.plist

를 실행하면 백그라운드로 실행된다. 백그라운드로 실행을 위한 스크립트인 td-agent.plist는 fluentd설치시  /Library/LaunchDaemons/td-agent.plist에 자동 생성된다.

백그라운드 작업이기 때문에, stdout이 없고 stdout으로 출력되는 로그는 /var/log/td-agent/td-agent.log로 확인할 수 있다.





실행중인 프로세스를 종료 하는 방법은

%sudo launchctl unload /Library/LaunchDaemons/td-agent.plist

를 사용하면 된다.


다음 글에는 실제로 fluentd 를 설정해서 Google의 Bigquery또는 큐로 로그를 전달하는 설정 방법에 대해서 알아보겠다.



저작자 표시 비영리
신고

REST API의 이해와 설계

아키텍쳐 /대용량 아키텍쳐 | 2016.04.05 22:33 | Posted by 조대협

REST API 이해와 설계


조대협 (http://bcho.tistory.com)


REST API에 대한 이해와 디자인 방법 그리고 보안방법에 대해서 설명합니다.




대용량 아키텍쳐와 성능 튜닝 책의 4장을 정리한 내용인데, 강의나 스터디등에 자유롭게 사용하셔도 됩니다.  (출처 명기 필요)




저작자 표시 비영리
신고

MSA의 중복 개발에 대한 단상


조대협 (http://bcho.tistory.com)


어제 우연한 기회로, API Academy 세미나에 다녀왔습니다.

발표자는 Mike Amundsen  이라는 분으로, CA의 API 아카데미의 일원이며, OReilly 출판사를 통해서 REST API에 대한 저서를 집필한 분이기도 합니다.

http://www.oreilly.com/pub/au/1192





API 아카데미였지만, 결국 MSA와 조직 문화 소통으로 수렴되는 내용이었습니다만, MSA 에 관련하여 재미있는 접근이 있어서 남겨놓고자 합니다.


예로 든것중의 하나가 API를 외부용, 내부용, 그리고 특정한 파트너용 3가지로 나누어서 접근을 하였는데, 이 접근 방식보다 특이했던 것이 구현의 주체입니다.


일반적인 생각으로는 유사한 API라면, API를 만들어놓고, 차이가 나는 부분만 Mediation을 통해서 재 사용하는 개념을 접근하였습니다.


예를 들어 GET /users/{userid} 라는 API가 있을때, 이 API를 내부용으로 만들어놓고, 이 API를 wrapping해서, 추가 인증/인가 기능을 추가하여 외부용, 파트너용으로 제공하는 방법이 일반적인 방식인데,


Amunsen 은 다른 접근 방법을 제시했습니다. "외부용,내부용,파트너용 API를 각각의 팀이 따로 만든다"

였습니다. 데이타가 같으니 일관성은 보장이 될터이고, 각 팀으로 서비스를 나눠서 각팀이 각각의 API를 만들게 되면, 기동성이 늘어나고, 고객의 요구 사항이 각각 다르니 거기에 기민하게 대응할 수 있다는 논리입니다.


중복 개발이나, 종속성에 대한 문제가 발생할 수 는 있겠지만, 오히려 이게 맞는 접근 방법이 아닌가도 싶습니다.

MSA의 근본적인 목적은 작은 서비스로 나눠서 개발의 기민성을 확보하고, 고객의 요구 사항을 빠르게 반영하는데 있는데, 공통 API를 재사용하게 되면, 재 사용성 자체는 올라갈지 모르겠지만, 반대로 의존성때문에 전체 프로젝트 일정에 문제가 생길 수 있습니다.


같은 논리로, 웹,모바일 백앤드가 있을때, 팀의 크기가 크다면, 공통 API 시스템을 쓸것이 아니라, 각자 API 백앤드 시스템을 만들어서 기민성을 확보하는 것도 하나의 예가 되겠습니다.


중복 개발과 낭비는 있겠지만, 기민성 확보와 각각의 팀이 컨택스트와 도메인을 완전하게 이해할 수 있다는 점에서, 이 방식도 충분히 고려해볼만한 가치가 있지 않을까 합니다.

저작자 표시 비영리
신고

마이크로서비스 아키텍쳐에 대한 소고



간만에 낚시성 제목을 달아봤는데, MSA (마이크로 서비스 아키텍쳐)가 필수라는 이야기는 꼭 틀린말이라고 볼 수 는 없습니다. 특히나 개발팀의 규모가 큰 경우나, 지리적으로 개발팀이 나눠져 있는 경우에는 서비스 단위로 나눠서 각 팀이 서비스를 개발하고, 독립된 기술과 개발 체계를 가지면서 빠르게 개발해 나가는게 효율적이기 때문에, 규모가 어느정도 되는 팀에서는 효율성이 높습니다.


중앙에서 통제할 필요 없이, 각자가 알아서 설계하고, 만들고, 테스트 하고 운영하기 때문입니다.

이것을 분산 거버넌스라고 하는데, 관리나 의사결정의 권한을 중앙의 팀이 중앙 통제하지 않고, 각자의 팀에 자율적으로 맏기고, 책임도 맏긴다는 이야기 입니다.


그러면 분산 거버넌스를 하면,중앙 거버넌스가 필요하지 않냐?


MSA글을 보면, 보통 분산 거버넌스와 이에 대한 필요성과 장점에 대해서만 이야기를 한다.

그러나, MSA에 과연 중앙 거버넌스가 필요하지 않을까?


개인적인 생각으로는 이 또한 강한 중앙 거버넌스가 필요하다.

첫번째로, API로 서비스를 제공하기 위해서는 API 표준이 맞아야 한다. 헤더 구조, URI 컨벤션, Granuality (서비스 크기), 에러 추적을 위한 로깅 메세지의 표준화가 필요하다.

두번째로, 서비스의 기능에 대해서, 어떤 기능이 필요한지, 필요하다면 기능이 어떤 동작을 어떻게 하는지에 대한 정확한 스펙에 대한 정의가 필요하다 이건 서비스를 제공하는 팀 보다는 서비스를 사용하는 팀 입장에서 해야 한다.

세번째로, 이러한 서비스를 엮을려면, 필요에 따라서 aggregation API를 만들거나 또는 서비스 자체에 기능을 추가해야 하는데, 팀 이기주의로 인하여 타팀으로 일을 떠넘기는 현상이 생길 수 있기 때문에 중앙에서 이를 조율할 필요가 있다.


실제로 MSA와 유사한 구조로 서비스를 개발해본 경험으로 보면, 이러한 중앙 통제가 필요하고 거기에 드는 추가적인 비용 (코디네이션/조율)이 만만하지 않다.


또한 다른 문제점중의 하나는 각 서비스팀별 개발 수준이나 품질 수준이 다르기 때문에, 기능을 추가하고자 할때도 어느 서비스(즉 팀으로 보낼것인가)를 고민해야 한다는 것이다.


MSA의 도입은, 각 서비스 개발팀의 수준이 높아야 권한을 분산할 수 있고, 또한 중앙집중형 거버넌스 조직을 통해서 표준화등이 강력하게 이루어져야 한다.


결론적으로 난이도가 높은 아키텍쳐 스타일이라는 이야기이고, 전사 개발팀 차원에서의 많은 투자가 필요할듯 하다.


SSAG에서 토론글 중, 홍성진님이 재미있는 링크를 올려주셔서 첨부합니다

http://siliconangle.com/furrier/2011/10/12/google-engineer-accidently-shares-his-internal-memo-about-google-platform/


저작자 표시 비영리
신고

아키텍쳐 문서화는 어떤 도구를 사용하는게 좋을까?

조대협(http://bcho.tistory.com)


제 현재 본업은 아키텍트 입니다. 주로 시스템을 설계하는 역할을 하는데, 이 아키텍쳐 문서를 주로 PPT를 사용합니다. 문서는 워드로 만드는게 좋을 수 도 있고, EA나 StarUML등등 많은 툴이 있는데,  굳이 PPT를 사용하는 이유를 적어놓고자 합니다.

사실 예전에는 MS WORD로 설계문서를 만들었습니다. 만들어 놓으면 멋도 있고, 자세한 내용 표현이 가능해서 탐독하면서 이해하기도 좋습니다. 

그런데, 고객사의 요구 때문에, PPT로 바꾼후, 거의 습관처럼 PPT로 아키텍쳐 문서를 만들다 보니, 몇가지 장점이 있습니다.





1. PPT 는 표현에 제약이 없다.

아키텍쳐 디자인은 특성상 많은 다이어그램과 노트등을 달아야 하는데, WORD로 작성할 경우, 다른 툴에서 다이어그램을 그린 후 가져다 붙여야 합니다. 문서 만드는데 여간 노력이 많이 들어가고, 또한 UML 도구 같은 것을 사용하면 그 도구에서만 제공하는 다이어그래밍이 가능하지 다양하고 쉬운 다이어그래밍이 어렵습니다. 그냥 그리고 싶은 데로 표현하고 싶은데로 자유롭게 하면 되니 일단 문서 작성이 쉽습니다.


2. PPT는 발표용이지 문서용이 아니다

SI때나, 예전 전통적인 개발 방법론에서는 아키텍쳐 설계서 산출물이 나오면 이걸 받아서 개발자들이 잘 읽고 개발을 시작하는 프로세스였다면 근래의 개발 프로세스는 아키텍트가 설계를 하고,이 내용을 개발자들에게 설명을 합니다.

그냥 문서 하나 휙던지고 마는게 아니라는 겁니다. 예전 포스팅에서도 몇번 언급했지만, 문서화는 커뮤니케이션을 원할하게 하기 위한 하나의 도구이지, 산출물이 목적이 아닙니다.

그런점에서 PPT는 발표를 위한 도구인점에서 메리트가 많습니다. 보고용 자료가 아니라 발표/토론용 자료를 만드는 게 아키텍쳐 문서화의 목적이면 거기에 맞는 툴을 쓰는게 맞기 때문입니다.



저작자 표시 비영리
신고

MSA 아키텍쳐 구현을 위한 API 게이트웨이의 이해 #2

API 게이트웨이 기반의 디자인 패턴

조대협 (http://bcho.tistory.com)



API 게이트 웨이는 여러개의 엔드포인트를 설정하고, 각 엔드포인트 마다 메세지 흐름을 워크 플로우 엔진 설정을 통해서 API 에 대한 Mediation, Aggregation 설정을 할 수 있는 미들웨어 이다. 쉽게 말하면 설정과 프로그래밍이 가능한 툴일 뿐이다. 그래서, API 게이트 웨이를 도입한다고 게이트웨이가 재 역할을 하는 것이 아니라, 게이트웨이 를 이용하여 적절한 게이트 웨이 아키텍쳐를 설계해야 한다. 


여기서는 API 게이트 웨이를 이용한 아키텍쳐 설계시 참고할 수 있는 디자인 패턴에 대해서 소개 한다. 

※ 이 패턴들은 예전에 ESB 기반으로 SOA 프로젝트를 했을 때 사용했던 패턴으로 일반적인 이론이 아니라 실제 적용 및 검증을 거친 패턴이다.


다중 API 게이트웨이 아키텍쳐


API 게이트 웨이를 배포할때는 하나의 게이트웨이가 아니라 용도나 목적에 따라서 게이트 웨이를 분리하는 전략을 취할 수 있다. 몇가지 분리 패턴에 대해서 알아보도록 하자

내부 게이트웨이와 외부 게이트 웨이 엔드포인트 분리

가장 유용한 패턴중의 하나는 외부 서비스용 게이트웨이와 내부 서비스용 게이트웨이를 분리하는 방안이다. 물리적으로 게이트 웨이 자체를 두개로 나누는 방법도 있지만, 하나의 게이트웨이에서 엔드포인트를 내부용과 외부용으로 분리하는 방안이다.




<그림. 외부 및 내부 게이트웨이를 분리한 패턴>


같은 내부 API를 외부와 내부로 나눠서 서비스를 하되, 외부 엔드포인트에 대해서는 인증/인가 로직을 거치도록 한다.

내부 API 엔드포인트는 내부 IP를 가지도록 하고, 방화벽 안에서만 오픈하되 별도의 인증/인가 로직을 거치지 않고 내부 서버들이 API를 호출하는데 사용할 수 있도록 한다. 


파일 업/다운로드 엔드포인트 분리


API 게이트웨이는 내부 구조는 쓰레드 풀 기반의 멀티 쓰레드나 또는 비동기 IO 기반의 싱글 쓰레드 모델을 사용한다.

쓰레드 풀 모델은 톰캣같은 WAS와 비슷한 모델로, 쓰레드 풀내의 하나의 쓰레드가 하나의 API 요청에 대해서 응답까지 모두 처리하는 모델로, API 요청이 들어오면 응답을 보낼때 까지 그 쓰레드는 해당 API 호출에 의해서 점유 된다 그래서, 이러한 모델의 API 게이트웨이는 일반적인 WAS와 마찬가지로 동시에 서비스 할 수 있는 트렌젝션 수가 쓰레드풀의 전체수밖에 되지 않는다.

싱글 쓰레드 모델은 비동기 IO 기반의 방식으로 멀티 쓰레드 모델에 비해서 많은 클라이언트를 처리할 수 있다.

(비동기 IO에 대한 개념은 http://bcho.tistory.com/881 을 참고하기 바란다. Node.js가 대표적인 비동기 IO 기반의 싱글 쓰레드 모델이다.)

파일 업로드나 다운로드와 같은 트렌젝션은 CPU는 많이 사용하지 않지만, 요청 처리에 시간이 많이 걸리는 작업이기 때문에, 쓰레드 풀 형태의 API 게이트 웨이 제품에서는 파일 업/다운로드에 쓰레드가 오랫동안 잡혀있기 때문에, 서비스를 할 수 있는 유휴 쓰레드 수가 적게 되고, 다른 일반 API 서비스에 영향을 줄 수 있다.

싱글 쓰레드 기반의 비동기 IO 게이트웨이의 경우에는 비동기 IO이기 때문에 파일 업/다운로드에는 다소 유리할 수 있지만, 네트워크 대역폭을 상당 부분 소모해버리기 때문에 마찬가지로 다른 API  서비스를 하는데 영향을 줄 수 있다.

그래서 이러한 파일 업/다운로드는 가급적이면 게이트 웨이를 거치지 않고 별도의 독립된 엔드포인트나 프록시를 사용하는 것이 좋은데, 다음은 별도의 프록시를 넣는 아키텍쳐 설계 방식의 예이다.

 


<그림. 파일 업/다운로드를 API 게이트웨이에서 분리해내는 방법>


1. API 클라이언트가 파일 서버에 API를 이용하여 파일 다운로드를 요청한다.

2. 파일 서버는 API에 대한 응답으로 파일을 바로 내리는 것이 아니라, 파일을 다운로드 받을 수 있는 URL과 함께, 임시 인증 토큰을 발급(현재 API 클라이언트에 IP 에만 유효하고, 특정시간 예를 들어 발급후 30분 이내만 사용이 가능한 토큰)하여, API 클라이언트에게 리턴한다.

3. API 클라이언트는 2에서 받은 URL로 임시 인증 토큰과 함께 파일 다운로드를 파일 다운로드 프로젝시를 통해서 요청한다.

4. 파일 다운로드 프록시는 임시 인증 토큰을 인증한 다음에, 파일 다운로드를 수행한다.


파일 다운로드 프록시는 일반적인 리버스 프록시 (HA Proxy, Nginx,Apache)등을 사용할 수 있으며 여기에 간단하게 다운로드용 임시 인증 토큰 로직을 넣으면 된다. 또는 아마존 클라우드의 CDN과 같은 서비스들도 임시 다운로드 토큰과 같은 서비스를 제공하기 때문에, CDN 사용시에도 유사하게 적용할 수 있는 아키텍쳐이다.


특수 목적 엔드포인트 분리


파일 업로드/다운로드 엔드 포인트를 분리한 것 처럼, 특수 목적의 엔드포인트는 별도의 API 게이트웨이로 분리해 내는 것이 좋다.

예를 들어 인증등이 없이 고속으로 많은 로그를 업로드 하는 엔드 포인트같은 경우, 부하량이 많기 때문에 다른 일반 API 엔드포인트에 부담을 주지 않기 위해서 분리 할 수 있다.


트렌젝션 ID 추적 패턴


MSA 아키텍쳐를 기반으로 하게 되면, 클라이언트에서 호출된, 하나의 API 요청은 API 게이트웨이와 여러개의 서버를 거쳐서 처리된 후에, 최종적으로 클라이언트에 전달된다.

만약에 중간에 에러가 났을 경우, 어떤 호출이 어떤 서버에서 에러가 났는지를 연결해서 판단해야 할 수 가 있어야 한다. 예를 들어 서버 A,B,C를 거쳐서 처리되는 호출의 경우 서버 C에서 에러가 났을때, 이 에러가 어떤 메세지에 의해서 에러가 난 것이고, 서버 A,B에서는 어떻게 처리되었는 찾으려면, 각 서버에서 나오는 로그를 해당 호출에 대한 것인지 묶을 수 있어야 한다.

하나의 API 호출을 트렌젝션이라고 정의하자, 그리고 각 트렌젝션에 ID를 부여하자. 그래서 API 호출시, HTTP Header에 이 트렌젝션 ID를 넣어서 서버간의 호출시에도 넘기면 하나의 트렌젝션을 구별할 수 있다.

여기에 추가적인 개념이 필요한데, 서버 A,B,C가 있을때, API 서버 B가 하나의 API 호출에서 동 두번 호출된다고 가정하자. 그러면 에러가 났을때 B 서버에 있는 로그중에, 이 에러가 첫번째 호출에 대한 에러인지, 두번째 호출에 대한 에러인지 어떻게 구분할까?

아래 그림을 서버 A->B로의 첫번째 호출과, 두번째 호출 모두 트렌젝션 ID가 txid:1로, 이 txid로는 구별이 불가하다.

 


<그림. 단일 트렌젝션 ID를 사용했을때 문제>

그래서 이러한 문제를 해결하기 위해서는 글로벌 트렌젝션 ID(gtxid)와, 로컬 트렌젝션 ID (ltxid)의 개념을 도입할 수 있다.

API 호출을 하나의 트렌젝션으로 정의하면 이를 글로벌 트렌젝션 gtx라고 하고, 개별 서버에 대한 호출을 로컬 트렌젝션 ltx라고 한다. 이렇게 하면 아래 그림과 같이 하나의 API호출은 gtxid로 모두 연결될 수 있고 각 서버로의 호출은 ltxid로 구분될 수 있다

※ 사실 이 개념은 2개 이상의 데이타 베이스를 통한 분산 트렌젝션을 관리하기 위한 개념으로, 글로벌 트렌젝션과 로컬 트렌젝션의 개념을 사용하는데, 그 개념을 차용한것이다.

 


<그림. 글로벌 트렌젝션과 로컬 트렌젝션 개념을 이용한 API 트렌젝션 추적 방법>


이런 글로벌 트렌젝션과 로컬 트렌젝션 개념을 API 게이트웨이와 연동하여 아키텍쳐를 설계하면 다음과 같은 모양이된다.

다음 그림을 보자.

 


<그림, gtxid,ltxid를 이용한 API 트렌젝션 추적 아키텍쳐>


API 클라이언트는 API를 호출한다. API 클라이언트는 트렌젝션 ID에 대한 개념이 없다.

API 게이트 웨이에서, HTTP 헤더를 체크해서 x-gtxid (글로벌 트렌젝션 ID)가 없으면 신규 API  호출로 판단하고 트렌젝션 ID를 생성해서 HTTP 헤더에 채워 넣는다. 로컬 트렌젝션 ID로 1로 세팅한다.

2번에서, API 서버 A가 호출을 받으면, 서버 A는 헤더를 체크하여 ltxid를 체크하고, ltxid를 하나 더 더해서, 로그 출력에 사용한다. 같은 gtxid를 이용해서 같은 API호출임을 알 수 있고, ltxid가 다르기 때문에 해당 API서버로는 다른 호출임을 구별할 수 있다.

이런식으로 서버B,C도 동일한 gtxid를 가지지만, 다른 ltxid를 갖게 된다.

각 API 서버는 이 gtxid와  ltxid로 로그를 출력하고, 중앙에서 로그를 수집해서 보면, 만약에 에러가 발생한 경우, 그 에러가 발생한 gtxid로 검색을 하면, 어떤 어떤 서버를 거쳐서 트렌젝션이 수행되었고 어떤 서버의 몇번째 호출에서 에러가 발생하였는지 쉽게 판별이 가능하다.

작은 팁중에 하나로, 자바로 API 서버를 개발할 경우 서블릿 필터를 넣어서, gtxid가 헤더로 들어오면 이 gtxid를 TheadLocal 변수에 저장하고, ltxid는 새로 생성해서 ThreadLocal 변수에 저장 해놓으면, 로그를 출력할때 ThreadLocal 변수에 있는 gtxid와 ltxid를 꺼내서 같이 출력하면 번거롭게 클래스의 메서드등으로 gtxid와 ltxid를 넘길 필요가 없다.  그리고 로그 수집은 SL4J나 Log4J와 같은 일반 로깅 프레임웍을 이용해서 gtxid와 ltxid 기반으로 로그를 출력하고 출력된 로그를 파일이 아니라 logstash를 이용해서 모아서, ElasticSearch에 저장하고, Kibana로 대쉬 보드를 만들면, 손쉽게 트렌젝션 ID기반으로 API 호출 로그 추적이 가능해진다.




저작자 표시 비영리
신고

MSA 아키텍쳐 구현을 위한 API 게이트웨이의 이해 #1

조대협 (http://bcho.tistory.com)


MSA(마이크로 서비스 아키텍쳐, 이하 MSA)와 함께 근래에 떠오르고 있는것이 API 게이트 웨이이다. API 게이트웨이는 API서버 앞단에서 모든 API 서버들의 엔드포인트를 단일화하여 묶어주고 API에 대한 인증과 인가 기능에서 부터 메세지에 따라서 여러 서버로 라우팅 하는 고급기능 까지 많은 기능을 담당할 수 있다.

API 게이트웨이의 시작은 MSA가 SOA(서비스 지향 아키텍쳐)에서 시작한것 처럼 ESB (Enterprise Service Bus)에서 부터 시작 되었다. 그래서 ESB의 대부분의 컨셉을 많이 승계했는데, ESB의 실패와 단점을 보완해서 만들어진 사상이 API 게이트웨이이다. ESB가 SOAP/XML 웹서비스 기반의 많은 기능을 가지는 구조였다면, API 게이트 웨이는 JSON/REST 기반에 최소한의 기능을 처리하는 경량화 서비스 이다. 그리고 ESB는 SOA의 사상에서 개념적으로 탄생한 솔루션이라면, API 게이트 웨이는 ESB의 실패와, MSA, REST 구현 사례를 통해서 필요에 의해서 탄생한 솔루션이기 때문에, 그 실용성이 차이가 난다.


MSA에 대한 개념은 http://bcho.tistory.com/948 를 참고하기 바라며, 이 글은 API 게이트웨이에 대한 바른 이해를 돕고, API 게이트웨이를 도입하고자 하는데 필요한 내용을 서술하고자 한다.


API 게이트웨이의 주요 기능


먼저 API 게이트웨이의 주요 기능에 대해서 알아보자


인증/인가에 관련된 기능


API 게이트웨이의 가장 기본적인 기능은 API 에 대한 인증과 인가 관련 기능이다. 인증은, API 를 호출하는 클라이언트에 대한 identity(신분)를 확인 해주는 기능이고, 인가는 클라이언트가 API를 호출할 수 있는 권한이 있는지를 확인해주는 기능이다. 

쉽게 이야기 하면 내가 페이스북 계정을 가지고 있는 사용자가 맞는지 , API 호출시 어느 권한 (일반사용자, 관리자 권한)까지 호출할 수 있는지를 판단하여 API 호출을 허가하는 기능이라고 볼 수 있다.


API 토큰 발급


인증 인가를 거칠때 마다 매번 사용자의 인가/인증 절차를 거치기는 불편하다. 사용자로 부터 매번 사용자 ID와 비밀 번호를 받기는 번거롭고, 그렇다고 사용자 ID와 비밀 번호를 저장해놓는 것은 해킹의 빌미를 제공한다.

그래서 보통 사용하는 방식이 토큰이라는 방식을 사용하는데, 사용자 인가가 끝나면, 사용자가 API를 호출할 수 있는 토큰을 발급해준다. API 서버는 이 토큰으로 사용자의 identity 와 권한을 확인한후, API 호출을 허가해준다.


API 게이트 웨이는 클라이언트를 인증한 후, 이러한 API 토큰을 생성 및 발급해주는 역할을 한다.


 

<그림. 일반적은 토큰 발급 절차>


토큰 발급을 위해서는 먼저 클라이언트를 인증해야 한다.


클라이언트를 인증하는 방법은 가장 간단하게 사용자의 id와 password를 넣는 방법에서 부터, 공인 인증서를 이용하는 방법, 지문이나 OTP (One time password) 등을 이용하는 방법등 다양한 방법이 있다. 각 보안 요건에 요구 되는 다양한 방식에 따라서 클라이언트를 인증한 후에, apitoken을 발급하게 된다.


이때, 클라이언트에 대한 인증은 직접적으로 API 게이트웨이가 하지 않고 뒷단에 있는 인증 서버가 이를 수행하는데, 간단하게는 내부 계정 관리를 위한 Active Directory, LDAP 또는 RDBMS등이 될 수 도 있으며, 외부 인증 서버로는 예를 들어서 온라인 게임 서비스에 가입할때, 페이스북 계정을 사용하는 경우, 온라인 게임 서버가 페이스북에 이 사용자의 인증을 요청하고, 페이스북이 인증을 해주면 온라인 게임서버가 apitoken을 발급해주는 흐름등을 들 수 있다.


그래서 API 게이트웨이의 중요한 기능중의 하나는 다양한 인증 서버와 연계가 가능한 것이 좋다.


이렇게 발급된 토큰을 API를 호출할 수 있는 권한 정보와 연관이 되는데, 이 권한 정보를 토큰 자체에 저장하느냐 또는 서버에 저장해놓느냐에 따라서 두 가지 종류로 나눌 수 있다.

토큰 자체가 이러한 정보를 갖는 형태를 클레임 기반의 토큰 (Claim based token)이라고 하는데, 근래에 유행하는 JWT (JSON Web Token)이나 SAML 토큰등이 이에 해당한다. 예를 들어 토큰 자체가 아래와 같은 정보를 가지고 있는 형태라고 생각하면 된다. 


{

“name”:”Terry”,

“role”:[“admmin”,”enduser”]

“org”:”petstore”

}

<그림. 클레임 기반의 토큰 예>

JWT가 이러한 형태의 토큰인데 JWT에 대한 자세한 설명은 http://bcho.tistory.com/999 와 http://bcho.tistory.com/1000 를 참고하기 바란다.

클레임 기반의 토큰이 아닌 경우, 이러한 클레임 정보를 서버에 저장해놓게 되는데, 클라이언트로는 unique한 string만을 리턴해주는 경우이다.

 


<그림. 서버에 토큰을 저장하는 경우>


이 서버 기반의 토큰이 현재 일반적으로 가장 많이 사용되는 형태인데, token에 연관되는 정보가 서버에 저장되기 때문에 안전하고, 많은 정보를 저장할 수 있으며, token에 대한 정보를 수정하기가 용이하다. 그러나 서버단에서 별도의 토큰 저장소를 유지해야 하기 때문에 구현 노력이 더 높게 든다. 토큰은 매 API 호출마다 정보를 가지고 와야 하기 때문에, DBMS와 같은 FILE IO 기반의 저장소 보다는 redis, memcached와 같이 메모리 기반의 고속 스토리지를 사용하는 것이 좋다.


클레임 기반의 토큰은 이러한 토큰 저장소가 필요 없다는 장점이 있어서 구현은 용이하지만, 토큰 자체에 클레임 정보가 들어가 있기 때문에, 토큰의 길이가 커지기 때문에 일정 양 이상의 정보를 담기가 어려우며, 한번 발급된 토큰은 변경이 어렵다. 예를 들어 role:admin으로 관리자 권한으로 발급된 토큰은 서버쪽에서 파기가 불가능하기 때문에 토큰 통제가 어렵다는 단점을 가지고 있다. 그래서, 클레임 기반의 토큰을 사용할때는 토큰의 유효기간을 둬서 반드시 강제적으로 토큰을 주기적으로 재발급 받도록 하는 것이 좋다.


엔드포인트별 API 호출 인증


Apitoken이 발급되었으면, 클라이언트는 이 apitoken을 이용하여 API를 호출하는데, 이 때 API 게이트웨이는 apitoken을 검증함으로써 API 호출을 승인할지 여부를 결정한다.

서버에 토큰 정보가 저장되는 형태의 경우 매 API 호출 마다 해당 apitoken을 가지고 연관 정보를 토큰 저장소로 부터 읽어와서 비교한후, 그 정보를 기반으로 API 호출 가능 여부를 결정한다.

 




<그림. Apitoken을 이용한 API 호출 인증>


클레임 기반의 토큰의 경우에는 이러한 작업이 없이 그냥 API 게이트 웨이에서 apitoken을 까보고, 그 안에 있는 내용을 가지고 API 호출 가능 여부를 결정한다.


이렇게 api token으로 인증을 하는 방법이 일반적인 방법이지만, 서버대 서버간의 통신은 내부 서버의 경우 별도의 인증 없이 API 를 제공하는 경우도 있고, 외부 서버와의 통신은 특정 ip 주소와 통신을 허용 하거나 높은 보안을 요구하는 경우 양방향 SSL등의 인증 방식을 사용함으로써 apitoken없이도 API 호출을 인증하는 방법도 있다..

이렇게 각각의 클라이언트나 서비스 별로 제공되는 엔드포인트에 대해서 API 인증 방식이 다르기 때문에, API 게이트웨이에서는 각 엔드 포인트 별로 다양한 형태의 인증 방식을 제공해야 한다. API 게이트를 이용하여 다양한 엔드포인트를 통해 서비스를 제공하는 방법은 뒤에서 다시 설명하도록 한다.


엔드포인트별 API 요청 인가


인증(Authentication)과 인가(Authorization)은 다른 의미를 갖는데, API를 호출 하는 것이 Terry가 맞다는 것을  확인 해주는 것을 인증이라고 한다면, 이 Terry가 이 API를 호출할 수 있는 권한이 있는 것을 확인해주는 것이 인가(Authorization)이다. 쉽게 생각하면, 일반 사용자용 API와 관리자용 API를 생각하면 이해가 쉽다.


이렇게 권한을 제어하는 방식은 여러가지가 있는데, 각 개별 권한을 토큰에 부여 하는 방식과 역할(ROLE) 기반으로  권한을 부여하는 방식이 대표적이다.


개별 권한을 토큰에 부여 하는 방식은 다양한 권한 정책을 세밀하게 관리할 수 있다는 장점을 가지고 있다.

 


<그림. 토큰에 역할을 부여하는 방식>


토큰에 제한적으로 권한을 부여할 수 있다는 장점을 가지고 있는데, 페이스북이 이런 형태의 권한 통제 모델을 사용한다. 

https://developers.facebook.com/docs/facebook-login/permissions/v2.2?locale=ko_KR

에 보면 api 토큰에 연동할 수 있는 권한 리스트들이 있는데, 페이스북의 써드파티 애플리케이션을 보면, 페이스북의 API의 권한을 일부 요청 하는 형태로 토큰에 권한을 연결한다.


그렇지만,이 방식의 경우에는 권한의 종류가 많을 경우, 관리가 어려워 지고 복잡해지기 때문에, 일반적으로 역할(ROLE)기반으로 권한을 관리 하는 방식을 많이 사용한다.


직접 권한을 토큰에 연결하는 것이 아니라, 역할이라는 개념을 두고, 역할별로 권한을 연결한 다음에, 이 역할을 토큰에 부여하는 개념이다 쉽게 이야기 하면, 관리자용 기능과 일반 사용자용 기능을 분리한 다음에, 관리자나 일반 사용자와 같은 역할(ROLE)을 토큰에 부여하는 방식이다. 이를 RBAC (Role Based Access Control)이라고 한다.


이 RBAC 기반으로 하면, 통제 해야 하는 권한의 숫자가 줄어들기 때문에, 다음과 같이 엔드포인트를 나눠서 권한 접근 제어가 가능하다. (예를 들어 총 권한이 100개가 있다고 했을때, 이를 관리자용 기능과 일반 사용자용 기능으로 나누어 버리면, 관리해야 하는 두개의 권한 집합을 나뉘어 진다.) 


이런 경우 관리자용 API 엔드포인트(/service/admin), 일반 사용자용 API 엔드포인트(/service/users) 두 개로 나눈 다음에, apitoken에 권한을 admin,user 두가지 종류로 정의한 후에, /service/admin 엔드포인트에서는 api token이 admin 권한이 있을 경우에만, 호출을 허용하도록 하면 된다. 


 

<그림. 역할(ROLE)별로 엔드포인트를 나눠서 권한 인가를 하는 구조>

API 라우팅


API 게이트웨이에서 다음으로 유용한 기능중의 하나가 API 호출을 라우팅 하는 기능이다. 같은 API라도 사용하는 서비스나 클라이언트에 따라서 다른 엔드포인트를 이용해서 서비스를 제공하거나, 데이타 센터가 여러개일때, 데이타 센터간의 라우팅등을 지원하는 기능이다. 주요 기능들을 보면 다음과 같다.


백엔드 API 서버로의 로드 밸런싱


가장 기본적인 기능으로는 로드밸런서 기능이다. API 게이트 웨이 뒷단에 다수의 API 서버가 있다고 할때, 여러개의 API 서버로 부하를 분산하는 기능이 필요하다.

 


<그림. API 게이트 웨이를 통한 API 서버로의 로드 밸런싱>


단순하게 Round Robin 방식으로 부하를 분산하는 기능뿐만 아니라, 각 서버 하드웨어에 따라 부하를 가중치를 줘서 분산하는 기능등을 고려해볼 수 있겠고, 무엇보다 중요한 기능은 API 서버가 장애가 났을때 이를 감지해서 로드 밸런싱 리스트에서 빼고, 복구 되었을때 다시 로드 밸런싱 기능에 넣는 기능들이 필요하다.


단순하게, HA Proxy와 같은 L4의 기능처럼, 뒷단의 서버가 살아 있으면 부하를 보내고 죽었으면 부하를 안보내는 기능에서 부터, 고급 기능으로는 API 서버가 Hang up (멈춤)에 걸렸을 때 이를 인지해서 부하를 안보내는 기능등을 고려해볼 수 있다. 이러한 고급 기능은 API 서버의 애플리케이션 상태를 인지해야 하기 때문에 단순히 IP 포트가 살아 있음을 가지고 판단 하는 것이 아니라 쓰레드 수, 응답 시간등으로  서버의 장애 상태를 판단해야 한다.  


서비스 및 클라이언트 별 엔드포인트 제공


또 다른 유용한 기능중의 하나는, 같은 API를 여러개의 엔드포인트를 통해서 서비스를 제공할 수있다는 점인데, 하나의 시스템이 다양한 서비스나, 다양한 클라이언트등으로 서비스를 제공할때, 각각 다른 서비스 별 또는 클라이언트 별로 다른 엔드포인트를 제공할 수 있다.

예를 들어서 IOT 플랫폼 서비스가 있다고 하자. 이 플랫폼은 REST API를 제공하는데, 이를 통해서 센서로 부터 데이타를 수집해서 분석하는 시스템이라고 가정하자.

이 시스템은 선박용 서비스, 비행기용 서비스, 차량용 서비스를 지원한다고 가정하자.

각 서비스별로 API의 특성이나 노출되는 API가 다소 다를 수 있는데, 각 서비스 별로

  • 선박용 /ships/
  • 비행기용 /airplanes/
  • 차량용 /cars/

라는 식으로 각각의 다른 엔드 포인트를 제공할 수 있다.

그리고, 이 서비스에서는 센서로 부터 데이타를 수집하는 시나리오와, 관리자가 웹을 통해서 시스템을 관리하기 위한 API가 있다고 가정하면, 앞의 API는 다음과 같이 클라이언트의 종류에 따라서 분리 될 수 있다.


  • 선박 센서용 /ships/sensors/, 선박 관리자 웹 /ships/admin
  • 비행기 센서용 /airplanes/sensors/, 비행기 관리자용 /airplanes/admin
  • 차량 센서용 /cars/sensors, 차량 관리자용 /cars/admin

그리고 각각의 엔드포인트 별로 노출(expose)하는 API를 다르게 할 수 있다.

 


< 그림. API를 엔드포인트 별로 다르게 노출>


API 게이트 웨이는 API 서버가 공통적인 API를 가지더라도, 각 서비스나 클라이언트 타입에 따라서 각각 다른 API 를 선별적으로 서비스 할 수 있도록 해준다.


※ 실제로 멀티 서비스를 제공하는 플랫폼형태의 경우에는 이 기능이 매우 유용하다.특히 같은 API라도 클라이언트의 종류에 따라서 인증 방식이 다를 수 있고 보안 메커니즘이 다를 수 있다.


메세지 또는 헤더기반 라우팅


라우팅에서 유용한 기능중의 하나는 메세지 내용을 기반으로 하는 라우팅이다. 예를 들어 그림과같이 HTTP 헤더에 country code가 있을 경우, country code에 따라서 유럽에 있는 API를 호출하거나 또는 미국에 있는 API 서버를 호출할 수 있도록 Routing을 할 수 있다.

 


<그림. 메세지 기반의 글로벌 라우팅 예시>


특히 글로벌 단위로 배포되는 시스템인 경우 각 데이타 센터간에 메세지를 라우팅 시키는데 유용하게 사용할 수 있다. 위의 예에서 처럼, 특정 데이타 센터로 조건에 따라 라우팅을 할 수 도 있고, 또는 중앙 집중형 시스템의 경우, 각 지역에 API 게이트 웨이를 두고, 클라이언트는 가까운 API  게이트 웨이를 호출하지만, 중앙 데이타 센터에만 있는 API 서버의 경우 중앙 데이타 센터로 호출을 라우팅 할 수 있다.


데이타 복제가 필요할 경우, 미국에 있는 API 게이트웨이로 호출하면 API 게이트 웨이가 미국 API서버와, 유럽 API 서버를 동시에 호출해서, 업데이트성 트렌젝션을 모든 데이타 센터에 복제함으로써 API를 통한 데이타 복제가 가능해진다.

라우팅에 있어서 고려해야할 사항은 먼저 메세지에 대한 라우팅인데, REST API를 기준으로 설명하면, REST API는 HTTP URL,HTTP Header,HTTP Body 3가지로 구분이 된다.


메세지를 기반으로 라우팅을 하기 위해서는 API 게이트 웨이가 이 메세지를 파싱해야 한다.

예를 들어 country_code가 HTTP Body에 JSON으로 다음과 같이 들어가 있다고 가정하자


{

“country_code”:”US”

  :

}


이 경우 이 API 호출에 대해서 라우팅 정보를 추출하기 위해서 매번 HTTP Body에 있는 JSON을 API 게이트웨이가 파싱해서 열어봐야 한다. 이는 빠르게 메세지가 통과해야 하는 API 게이트웨이의 역할에 많은 부담을 준다. 만약에 이러한 라우팅 정보를 HTTP Header로 옮긴다면, HTTP Body는 파싱하지 않고, Header만 파싱한후, Body 정보는 라우팅되는 서버로 그냥 포워딩만 해도 된다.


그래서 메세지 기반의 라우팅을 사용할 때는 이러한 파싱에 대한 오버헤드를 잘 고려하고, 가능하면, HTTP URL이나 HTTP Header에 라우팅 필드를 넣는 것이 좋다. 


부득이하게, HTTP Body에 있는 내용으로 라우팅을 해야 하는 경우에는 호출 빈도가 적은 API인 경우 API 게이트웨이에서 담당하고, 다른 경우에는 별도의 게이트웨이 인스턴스(프로세스)로 분리를 하거나 뒷단의 API서버가 라우팅을 하도록 하는 것도 하나의 방안이 된다.


공통 로직 처리


API 게이트웨이는 특성상 모든 API 서버 앞쪽에 위치 하기 때문에, 모든 API 호출이 이 API 게이트를 거쳐간다. 그렇기 때문에, 모든 API 가 공통적으로 처리해야 하는 공통 기능이 필요할 경우 이러한 공통 기능을 API 게이트웨이로 옮기게 되면 별도로 API 서버에서 이러한 기능을 개발할 필요 없이 비지니스 로직 자체 구현에만 집중할 수 있게 된다.

아래 그림은 각 API 서버에서 인증과, 로깅에 관련된 로직을 API 게이트웨이로 이전한 구조이다.

API 로깅이나 인증은 전체 시스템에 대해 공통된 기능으로, 공통 계층에서 처리하게 되면 개발 중복을 줄일 수 있는 장점뿐만 아니라, 표준 준수가 더 쉽다는 장점을 가지고 있다. 

 


<그림 API 게이트웨이를 이용하여 공통 로직을 API 서버에서 API 게이트웨이로 이전한 구조>


메디에이션 기능 (Mediation)


메디에이션이란, 한글로 “중재”또는 “조정” 이라는 의미를 갖는데, API서버에서 제공되는 API가 클라이언트가 원하는 API 형태와 다를때, API 게이트웨이가 이를 변경해주는 기능을 이야기 한다. 구체적인 기능을 보자


메세지 포맷 변환 (Message format transformation)


메세지 포맷을 변환하는 기능이란, JSON으로 된 요청(Request) 메세지가 들어왔을때, 이를 API 서버로 보낼때 변환 해서 보내거나, 또는 API 서버에서 생성된 응답을 클라이언트에 리턴할때 변경해서 보내는 기능을 의미한다.


예를 들어보자, 아래와 같이 terry의 연봉(salary) 정보를 구하는 API가 필요하다고 하자. 그런데, 시스템에는 연봉 정보만 주는 API는 없고, 전체 사용자 정보를 리턴하는 API만 있는 상황이다.


이런 경우, API 게이트 웨이를 통해서 /users/salary라는 새로운 API를 제공하고, 이를 기존에 전체 사용자 정보를 주는 /users/details라는 API로 라우팅 한다. /users/details에서 사용자 정보를 뽑았을때, 클라이언트에게 응답을 줄때는 API 게이트웨이에서 아래 그림과 같이 name과 salary 정보만 뽑아서 리턴하도록 한다.

 


<그림. 메세지 포맷 변환의 예시>


일단 간단한 기능으로 구현이 가능하기 때문에 서술은 해놨지만, 그다지 권장하고 싶지 않은 기능이다. 메세지 포맷이 변환이 된다면, 차라리 필요한 포맷에 맞는 API를 따로 뽑아 내는 것이 났지 않나 싶다.


프로토콜 변환 


다양한 서비스나 클라이언트를 지원하게 되면, 클라이언트나 서비스별로 다른 통신 프로토콜을 사용해야 하는 경우가 있다. 웹에서는 JSON기반의 REST가 많이 사용되지만, 배나 비행기에 사용되는 센서들의 경우에는 REST도 무겁기 때문에 바이너리 기반의 경량 프토토콜을 사용하거나, 또는 예전 엔터프라이즈 시스템의 경우 XML 기반의 웹서비스를 이용할 수 도 있다.


이렇게 다양한 타입의 프로토콜을 지원하기 위해서, 각 서비스들이 새롭게 구현을 하는 것이 아니라 API 게이트웨이 계층에서 프로토콜 변환을 통하여, 같은 API를 다른 프로토콜로 서비스 할 수 있도록 할 수 있다.

 


<그림. API 게이트 웨이를 통한 프로토콜 변환>


실제로 유용한 기능인데, 내부 API는 REST가 아니라 페이스북 Thrift나 구글의 Protocol Buffer로 구현을 하고, 외부에 제공하는 API는 API 게이트 웨이단에서 REST 로 변경해서 서비스 하는 구조를 이용하면, 내부 API 성능을 올리고, 외부로는 REST API로 서비스 함으로써 범용성을 확보할 수 있다. (실제 사례가 있었다.)


또한 근래에 M2M이나 IOT (Internet of things)와 같은 개념이 활성화 되면서, HTTP REST 뿐 아니라 기존의 센서에서 통신에 사용되는 다양한 프로토콜을 지원하여 백엔드 API 서버의 프로토콜로 맞춰줘야 하는 필요가 점점 증대되고 있다.


메세지 호출 패턴 변환 (Message Exchange Pattern : MEP)


메세지 호출 패턴, 보통 MEP(Message Exchange Pattern)라고 하는데, 동기,비동기 호출과 같은 API를 호출하는 메세지 패턴을 정의한다.

API 게이트웨이의 좋은 기능중의 하나가 이 MEP를 변경할 수 있다는 건데, 쉽게는 Async API호출을 Sync 호출로 바꿔 준다거나, 하나의 API 호출을 여러 데이타 센터로 복제 해준다거나 하는 형태의 메세징 패턴을 변화 시킬 수 있다.

 


<그림. 비동기 호출을 API게이트웨이를 통해서, 동기 호출로 변경한 구조>


위의 그림의 예제는 로그를 수집하는 시스템에 대한 구조이다.뒷단의 로그저장 API 서버가 대용량 트래픽에 대한 대응 능력이 없을때, API 게이트 웨이에서 큐를 이용해서 API 요청을 받고 (1), 바로 클라이언트에 ACK를 준후에, 메세지큐 연동을 이용하여 메세지를 저장한후, 로그 저장 API 서버의 성능에 맞게 흘려주는 방식이다. 클라이언트 입장에서는 동기 호출이지만 실제 메세지 흐름은 큐를 이용한 비동기 구조로 변경되었다.


어그레게이션 (aggregation)


SOA에서는 Orchestration(오케스트레이션)이라고 불렀는데, 여러개의 API를 묶어서 하나의 API로 만드는 작업을 이야기 한다. 예를 들어서, 계좌 이체를 한다고 했을때,


A은행에서 잔액 확인

A은행에서 인출

B은행으로 입금


하는 3개의 API 호출을 하나의 API 인 POST transfer(인출계좌,입급계좌,금액)으로 구현한다고 하자.이를 API 게이트웨이에서 구현 하면 다음과 같은 형태로 구현할 수 있다.

 


<그림. API 게이트 웨이를 이용한 API Aggregation>


대부분의 API 게이트 웨이 제품들은 이러한 aggregation을 할 수 있는 일종의 워크플로우 엔진과 유사한 기능들을 가지고 있다.


 


<그림. Apigee 제품의 워크플로우 저작도구 화면>


이러한 aggregation 기능이 언뜻 보면 좋아보이지만, 하나의 플로우에서, 여러 API를 호출해야 하고, 비지니스 로직을 수행하면서 실제로 API 메세지 BODY까지 파싱해야 하기 때문에, API 게이트 웨이 입장에서는 부하가 매우 크다. 


MSA 의 전신인 SOA에서 API 게이트웨이와 유사한 역할을 했던 ESB역시 이러한 aggregation (ESB에서는 보통 오케스트레이셔이라고 함)을 남발해서, ESB의 성능이 떨어져서 시스템 개발이 실패하는 아키텍쳐를 많이 봤다.

그래서 본인의 경우에는 이러한 무거운 aggregation 로직은 별도의 Mediator API 서버라는 계층을 만들어서, API 게이트웨이 밖에서 따로 하는 방법을 권장한다.


아래 그림과 같이 여러 API를 조합하는 목적의 API 서버를 별도로 둬서, 이러한 기능을 API 게이트웨이에서 제거한다.

 


<그림. API aggregation을 API 게이트웨이에서 Mediation API 서버로 분리한 구조>


aggregation 로직을 API 게이트웨이 안에 넣으면 확실하게  게이트웨이가 받는 부하량은 올라간다. 설치형 API 게이트웨이의 경우, 이는 추가적인 하드웨어 박스를 더 구매하고, 상용 API 게이트웨이의 경우 라이센스를 더 구매해야 한다는 것을 의미하기 때문에, Mediation API 서버 계층을 사용하는 것을 권장한다.


클라우드형 API 게이트웨이의 경우, 호출 수로 과금을 하기 때문에 aggregation 로직을 API 게이트웨이에 넣는 방안을 고려해볼 수 있으나, aggregation 로직이 게이트웨이 안에 있으면 디버깅이나 테스팅이 쉽지 않기 때문에, 이를 적절히 고민한 후 판단해서 aggregation 로직의 위치를 결정해야 한다.


로깅 및 미터링


API 게이트웨이의 비기능적인 요건으로 중요한 기능이 로깅과 미터링이다. 


API 호출 로깅


앞서 공통 로직 처리 부분에서도 언급하였지만, API 호출시 API 게이트웨이는 공통적으로 호출되는 부분인 만큼 모든 로그를 중간에서 수집하기가 가장좋다.


근래의 애플리케이션 아키텍쳐가 클라이언트와 서버간의 통신이 모두 API를 기반하는 형태로 변경이되어감에 따라 API 호출 패턴을 분석하면 사용자의 사용 패턴을 분석해낼 수 있기 때문에, 빅데이타 영역과 연계하여 API 호출 로그는 아주 중요한 자산으로 다루어지고 있다.


또한 API 호출 로그는 차후 문제가 발생하였을때, 문제를 추적하기 위한 중요한 자료로 사용된다. (Audit: ‘감사’의 목적) 그래서, API 로그 수집은 단순 분석 목적뿐 아니라, 향후 감사 목적용으로도 저장되어야 한다.


근래에 출시되고 서비스되는 클라우드형 API 게이트웨이의 경우에는 특히나 이 API에 대한 로그 분석 기능을 강화해서 출시되고 있다.

 


<그림. Apigee.com의 API 모니터링>


API 미터링 & 차징 (Metering & Charing)


API 미터링과 차징은 유료 API 서비스를 위한 기능으로,  미터링은 과금을 위한 API 호출 횟수,클라이언트 IP, API 종류,IN/OUT 용량등을 측정 기록하는 서비스이고,

차징은 미터링이 된 자료를 기반으로 하여, API 서비스 사용 금액을 금액 정책에 따라서 계산 해내는 서비스이다. 

대부분의 SNS 오픈 API 서비스는 무료인 경우가 많지만, 구글 API 의 경우에도, 특정 호출 횟수(/일)을 넘어가면 과금등을 하도록 되어 있다.


QoS 조정 (Quality of service)


마지막으로 QoS 조정 기능이란, API 서비스를 클라이언트 대상에 따라서 서비스 레벨을 조정하는 기능이다.

유료 서비스가 있는  API 서비스라고 가정할때, 무료 사용자의 경우 1일 1000건으로 호출횟수를 제한 한다거나, 전송 용량이나, 네트워크 대역폭을 유료/무료 사용자에 따라 다르게 적용하는 것과 같은 기능을 QoS 기능이라고 한다.

유료 서비스인 경우만 아니라, 플랫폼 차원에서 다양한 클라이언트나 다양한 서비스로 API 를 제공하는 경우, 각 클라이언트나 서비스에 따라서 이 QoS를 조정하는 기능은 유용하게 사용될 수 있다. 특정 서비스나 클라이언트가 폭주하여 API를 과도하게 사용하여 다른 서비스들이 API를 사용할 수 없게 한다던가 그런 문제를 미연에 예방할 수 있다.


결론


지금까지 간단하게나마 API 게이트웨이의 대략적인 기능에 대해서 알아보았다. 다음에는 API 게이트웨이 기반 아키텍쳐를 확장하는 방법과 API 게이트웨이의 안티패턴과 설계 방법론 등에 대해서 소개하도록 한다.


참고

API 플랫폼의 이해 http://bcho.tistory.com/808

대용량 분산 시스템을 위한 마이크로서비스 아키텍쳐 http://bcho.tistory.com/948




저작자 표시 비영리
신고

JWT(JSON Web Token)을 이용한 API 인증 - #1 개념 소개

조대협 (http://bcho.tistory.com)


REST API에 대한 보안과 인증이 화두가 되면서 많이 언급되는 것이 OAuth인데, 근래에 들어서 화두가 되고 있는 것이 JWT (JSON Web Token)이라는 표준이다.


Claim기반 토큰의 개념


OAuth에 의해서 발급되는 access_token은 random string으로 토큰 자체에는 특별한 정보를 가지고 있지 않는 일반적인 스트링 형태 이다. 아래는 페이스북에서 발급된 access_token의 형태로 일반적인 문자열 형태임을 확인할 수 있다.



<그림1.  Facebook의 Oauth에서 사용하는 일반적인 스트링 기반 토큰 예제>

 

 API나 서비스를 제공하는 서버 입장에서 그 access_token을 통해서 사용자에 연관된 권한(예를 들어 scope같은 것)을 식별한 뒤 권한을 허용해주는 구조이다.

즉 서비스를 제공하는 입장에서는 토큰을 가지고 그 토큰과 연관된 정보를 서버쪽에서 찾아야 한다. (사용자 ID나 권한등).

JWT는 Claim 기반이라는 방식을 사용하는데, Claim이라는 사용자에 대한 프로퍼티나 속성을 이야기 한다. 토큰자체가 정보를 가지고 있는 방식인데, JWT는 이 Claim을 JSON을 이용해서 정의한다.

다음은 Claim을 JSON으로 서술한 예이다.이 JSON 자체를 토큰으로 사용하는 것이 Claim 기반의 토큰 방식이다.

{

  "id":"terry"

  ,"role":["admin","user"]

  ,"company":"pepsi"

}

<코드 1. JSON으로 Claim을 기술한 토큰의 형태 >

자 그렇다면, 이러한 Claim 방식의 토큰은 무엇이 좋을까? 이 토큰을 이용해서 요청을 받는 서버나 서비스 입장에서는 이 서비스를 호출한 사용자에 대한 추가 정보는 이미 토큰안에 다 들어가 있기 때문에 다른 곳에서 가져올 필요가 없다는 것이다.

“사용자 관리” 라는 API 서비스가 있다고 가정하다.

 이 API는 “관리자(admin)” 권한을 가지고 있는 사용자만이 접근이 가능하며, “관리자” 권한을 가지고 있는 사용자는 그 관리자가 속해 있는 “회사(company)”의 사용자 정보만 관리할 수 있다. 라고 정의하자

이  시나리오에 대해서 일반적인 스트링 기반의 토큰과 JWT와 같은 Claim 기반의 토큰이 어떤 차이를 가질 수 있는 지 알아보도록 하자.


OAuth 토큰의 경우



<그림 2. String 토큰에 의한 API 권한 인증 흐름>

 

1.    API 클라이언트가 Authorization Server (토큰 발급서버)로 토큰을 요청한다.

이때, 토큰 발급을 요청하는 사용자의 계정과 비밀번호를 넘기고, 이와 함께 토큰의 권한(용도)을 요청한다. 여기서는 일반 사용자 권한(enduser)과 관리자 권한(admin)을 같이 요청하였다.

2.    토큰 생성 요청을 받은 Authorization Server는 사용자 계정을 확인한 후, 이 사용자에게 요청된 권한을 부여해도 되는지 계정 시스템등에 물어본 후, 사용자에게 해당 토큰을 발급이 가능하면 토큰을 발급하고, 토큰에 대한 정보를 내부(토큰 저장소)에 저장해놓는다.

3.    이렇게 생성된 토큰은 API 클라이언트로 저장된다.

4.    API 클라이언트는 API를 호출할때 이 토큰을 이용해서 Resource Server(API 서버)에 있는 API를 호출한다.

5.    이때 호출되는 API는 관리자 권한을 가지고 있어야 사용할 수 있기 때문에, Resource Server가 토큰 저장소에서 토큰에 관련된 사용자 계정, 권한 등의 정보를 가지고 온다. 이 토큰에 (관리자)admin 권한이 부여되어 있기 때문에, API 호출을 허용한다. 위에 정의한 시나리오에서는 그 사용자가 속한 “회사”의 사용자 정보만 조회할 수 있다. 라는 전제 조건을 가지고 있기 때문에, API 서버는 추가로 사용자 데이타 베이스에서 이 사용자가 속한 “회사” 정보를 찾아와야한다.

6.    API서버는 응답을 보낸다.


JWT와 같은 Claim 기반의 토큰 흐름을 보자

 



<그림 3. Claim 기반의 토큰을 이용한 API 권한 인증 흐름 >

 

1.    토큰을 생성 요청하는 방식은 동일하다.  마찬가지로 사용자를 인증한다음에, 토큰을 생성한다.

2.    다른 점은 생성된 토큰에 관련된 정보를 별도로 저장하지 않는다는 것이다. 토큰에 연관되는 사용자 정보나 권한등을 토큰 자체에 넣어서 저장한다.

3.    API를 호출하는 방식도 동일하다.

4.    Resource Server (API 서버)는 토큰 내에 들어 있는 사용자 정보를 가지고 권한 인가 처리를 하고 결과를 리턴한다.

결과적으로 차이점은 토큰을 생성하는 단계에서는 생성된 토큰을 별도로 서버에서 유지할 필요가 없으며

토큰을 사용하는 API 서버 입장에서는 API 요청을 검증하기 위해서 토큰을 가지고 사용자 정보를 별도로 계정 시스템 등에서 조회할 필요가 없다는 것이다.


참고 : 다른 Claim 기반 토큰은?


그러면 이러한 Claim 기반의 토큰이 JSON이 처음일까? 이미 이전에, XML기반의 SAML 2.0이 이와 비슷한 개념을 가지고 있다. Assertion이라는 개념으로 XML안에 이러한 Claim 정보를 넣어서 넘길 수 있었으나, 문제점은 전체적인 사이즈가 너무 크고, 구조가 복잡하여 쉽게 접근이 어려웠다. 더군다가 크기가 크기 때문에 API와 같이 자주 호출해야 하는 경우에는 HTTP 헤더등에 실어서 보내기가 어렵고, 파싱에 대한 오버해드가 크기 때문에 적절하지 않았다. (주로 다른 사이트나 시스템간의 SSO에서 상호 사용자 인증등을 위해서 사용된다. 무겁기는 하지만 표준화가 잘되어 있기 때문에 사용자 인증 시나리오에서는 현재에도 많이 사용된다.)

JWT는 이JSON Claim을 BASE64로 인코딩하여HTTP Header에 쉽게 넣을 수 있으며, JSON 기반이기 때문에 파싱과 사용이 쉽다.

결과적으로 Claim 기반의 토큰은 토큰 자체가 정보를 담음으로써, 토큰을 가지고 서비스나 API 접근을 제어할 때 별도의 작업이 서버에서 필요하지 않으며, 토큰 자체를 서버에서 관리할 필요가 없기 때문에 구현이 상대적으로 단순해진다.


JWT에 대한 소개


Claim 기반의 토큰에 대한 개념을 대략적으로 이해했다면, 그러면 실제로 JWT가 어떻게 구성되는지에 대해서 살펴보도록 하자.


Claim (메세지) 정의

JWT는 Claim을 JSON형태로 표현하는 것인데, JSON은 “\n”등 개행문자가 있기 때문에, REST API 호출시 HTTP Header등에 넣기가 매우 불편하다. 그래서, JWT에서는 이 Claim JSON 문자열을 BASE64 인코딩을 통해서 하나의 문자열로 변환한다.

{

  "id":"terry"

  ,"role":["admin","user"]

  ,"company":"pepsi"

}

<코드 2. JSON 기반의Claim 예제>

문자열을 BASE64 인코딩 한 결과

ew0KICAiaWQiOiJ0ZXJyeSINCiAgLCJyb2xlIjpbImFkbWluIiwidXNlciJdDQogICwiY29tcGFueSI6InBlcHNpIg0KfQ0K

<코드 3. JSON 기반의 Claim 코드 2를 BASE64 인코딩 한 결과>


변조 방지

위의 Claim 기반의 토큰을 봤으면, 첫번째 들 수 있는 의문이 토큰을 받은 다음에 누군가 토큰을 변조해서 사용한다면 어떻게 막느냐? 이다. 이렇게 메세지가 변조 되지 않았음을 증명하는 것을 무결성(Integrity)라고 하는데, 무결성을 보장하는 방법중 많이 사용되는 방법이 서명(Signature)이나 HMAC 사용하는 방식이다. 즉 원본 메세지에서 해쉬값을 추출한 후, 이를 비밀 키를 이용해서 복호화 시켜서 토큰의 뒤에 붙인다. 이게 HMAC방식인데,  누군가 이 메세지를 변조를 했다면,변조된 메세지에서 생성한 해쉬값과 토큰뒤에 붙어 있는 HMAC값이 다르기 때문에 메세지가 변조되었음을 알 수 있다. 다른 누군가가 메세지를 변조한후에, 새롭게 HMAC값을 만들어내려고 하더라도, HAMC은 앞의 비밀키를 이용해서 복호화 되었기 때문에, 이 비밀키를 알 수 없는 이상 HMAC을 만들어 낼 수 없다.


※ HMAC에 대한 자세한 설명은http://bcho.tistory.com/807 를 참고하기 바란다.

그래서 앞의 JSON 메세지에 대해서 SHA-256이라는 알고리즘을 이용해서 비밀키를 “secret” 이라고 하고, HMAC을 생성하면 결과는 다음과 같다.

i22mRxfSB5gt0rLbtrogxbKj5aZmpYh7lA82HO1Di0E

<코드 4. 코드 2의 JSON기반 Claim에 대해서, SHA1-256으로 생성한 HMAC>

서명 생성 방식

그러면 무결성 보장을 위해서 사용할 수 있는 알고리즘이 SHA1-256 HMAC 뿐일까? 보안요건에 따라서 SHA1-256,384,512. 그리고 공인 인증서 (Ceritification)을 이용한 RS256 등등 다양한 서명 방식을 지원한다. 그렇다면 JWT 토큰이 어떤 방식으로 서명이 되어 있는지는 어떻게 알 수 있을까?

그래서 JWT 토큰의 맨 앞부분에는 서명에 어떤 알고리즘을 사용했는지를 JSON형태로 정의한후, 이 JSON을 다시 BASE64 방식으로 인코딩한 문자열을 붙인다

{"alg":"HS256","typ":"JWT"}

<코드 5. JSON으로 서명 생성 방식은 SHA1-256으로 정의한 예>

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

<코드 6. 위의 코드 5 JSON 문자열을 BASE64 인코딩한 결과>

 

전체 메세지 포맷


위에서 설명한, 서명 방식, JSON 기반의 Claim,그리고 서명(Signature)까지 포함된 전체적인 JWT 토큰의 구조를 보면 다음과 같다.

{서명 방식을 정의한 JSON을 BASE64 인코딩}.{JSON Claim을 BASE64 인코딩}.{JSON Claim에 대한 서명}

이를 정리해서 그림으로 서술해 보면 다음과 같다.


<그림. JWT 토큰 구조>

그리고 결과로 나온, JWT 토큰은

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.ew0KICAiaWQiOiJ0ZXJyeSINCiAgLCJyb2xlIjpbImFkbWluIiwidXNlciJdDQogICwiY29tcGFueSI6InBlcHNpIg0KfQ0K.i22mRxfSB5gt0rLbtrogxbKj5aZmpYh7lA82HO1Di0E

가 된다.


2편에서 다룰 내용

  • 유출 방지(암호화)

  • 상세 스펙

  • 구현예제


 

 

 

 

저작자 표시 비영리
신고

마이크로 서비스 아키텍쳐 (MSA의 이해)

조대협(http://bcho.tistory.com)

 

배경


마이크로 서비스 아키텍쳐(이하 MSA)는 근래의 웹기반의 분산 시스템의 디자인에 많이 반영되고 있는 아키텍쳐 스타일로, 특정 사람이 정의한 아키텍쳐가 아니라, 분산 웹 시스템의 구조가 유사한 구조로 설계 되면서, 개념적으로만 존재하던 개념이다.

얼마전 마틴파울러(Martin folwer)가 이에 대한 MSA에 대한 개념을 글로 정리하여, 개념을 정립 시키는데 일조를 하였다.

이 글에서는 대규모 분산 웹시스템의 아키텍쳐 스타일로 주목 받고 있는 MSA에 대한 개념에 대해서 알아보도록 한다.


모노리틱 아키텍쳐(Monolithic Architecture)


마이크로 서비스 아키텍쳐를 이해하려면 먼저 모노리틱 아키텍쳐 스타일에 대해서 이해해야 한다

모노리틱 아키텍쳐 스타일은 기존의 전통적인 웹 시스템 개발 스타일로, 하나의 애플리케이션 내에 모든 로직들이 모두 들어 가 있는 “통짜 구조” 이다.

예를 들어, 온라인 쇼핑몰 애플리케이션이 있을때, 톰캣 서버에서 도는 WAR 파일(웹 애플리케이션 패키징 파일)내에, 사용자 관리,상품,주문 관리 모든 컴포넌트들이 들어 있고 이를 처리하는 UX 로직까지 하나로 포장되서 들어가 있는 구조이다.




각 컴포넌트들은 상호 호출을 함수를 이용한 call-by-reference 구조를 취한다.

전체 애플리케이션을 하나로 처리하기 때문에, 개발툴 등에서 하나의 애플리케이션만 개발하면 되고, 배포 역시 간편하며 테스트도 하나의 애플리케이션만 수행하면 되기 때문에 편리하다.

문제점

그러나 이러한 모노리틱 아키텍쳐 시스템은 대형 시스템 개발시 몇가지 문제점을 갖는다.

모노리틱 구조의 경우 작은 크기의 애플리케이션에서는 용이 하지만, 규모가 큰 애플리케이션에서는 불리한 점이 많다.

크기가 크기 때문에, 빌드 및 배포 시간, 서버의 기동 시간이 오래 걸리며 (서버 기동에만 2시간까지 걸리는 사례도 경험해봤음)

프로젝트를 진행하는 관점에서도, 한두사람의 실수는 전체 시스템의 빌드 실패를 유발하기 때문에, 프로젝트가 커질 수 록, 여러 사람들이 협업 개발하기가 쉽지 않다

또한 시스템 컴포넌트들이 서로 로컬 콜 (call-by-reference)기반으로 타이트하게 연결되어 있기 때문에, 전체 시스템의 구조를 제대로 파악하지 않고 개발을 진행하면, 특정 컴포넌트나 모듈에서의 성능 문제나 장애가 다른 컴포넌트에까지 영향을 주게 되며, 이런 문제를 예방하기 위해서는 개발자가 대략적인 전체 시스템의 구조 등을 이해 해야 하는데, 시스템의 구조가 커질 수 록, 개인이 전체 시스템의 구조와 특성을 이해하는 것은 어려워진다.

특정 컴포넌트를 수정하고자 했을때, 컴포넌트 재 배포시 수정된 컴포넌트만 재 배포 하는 것이 아니라 전체 애플리케이션을 재 컴파일 하여 전체를 다시 통으로 재배포 해야하기 때문에 잦은 배포가 있는 시스템의 경우 불리하며,

컴포넌트 별로, 기능/비기능적 특성에 맞춰서 다른 기술을 도입하고자 할때 유연하지 않다. 예를 들어서, 전체 애플리케이션을 자바로 개발했다고 했을 때, 파일 업로드/다운 로드와 같이 IO 작업이 많은 컴포넌트의 경우 node.js를 사용하는 것이 좋을 수 있으나, 애플리케이션이 자바로 개발되었기 때문에 다른 기술을 집어 넣기가 매우 어렵다.

※ 모노리틱 아키텍쳐가 꼭 나쁘다는 것이 아니다. 규모가 작은 애플리케이션에서는 배포가 용이하고, 규모가 크더라도, call-by-reference call에 의해서 컴포넌트간 호출시 성능에 제약이 덜하며, 운영 관리가 용이하다. 또한 하나의 구조로 되어 있기 때문에, 트렌젝션 관리등이 용이하다는 장점이 있다. 즉 마이크로 서비스 아키텍쳐가 모든 부분에 통용되는 정답은 아니며, 상황과 필요에 따라서 마이크로 서비스 아키텍쳐나 모노리틱 아키텍쳐를 적절하게 선별 선택 또는 변형화 해서 사용할 필요가 있다.


마이크로 서비스 아키텍쳐


마이크로 서비스 아키텍쳐는 대용량 웹서비스가 많아짐에 따라 정의된 아키텍쳐인데, 그 근간은 SOA (Service Oriented Architecture : 서비스 지향 아키텍쳐)에 두고 있다.

SOA는 엔터프라이즈 시스템을 중심으로 고안된 아키텍쳐라면, 마이크로 서비스 아키텍쳐는 SOA 사상에 근간을 두고, 대용량 웹서비스 개발에 맞는 구조로 사상이 경량화 되고, 대규모 개발팀의 조직 구조에 맞도록 변형된 아키텍쳐이다.


아키텍쳐 구조


서비스


마이크로 서비스 아키텍쳐에서는 각 컴포넌트를 서비스라는 개념으로 정의한다. 서비스는 데이타에서 부터 비지니스 로직까지 독립적으로 상호 컴포넌트간의 의존성이 없이 개발된 컴포넌트(이를 버티컬 슬라이싱/Vertical Slicing-수직적 분할이라고 한다.)로 REST API와 같은 표준 인터페이스로 그 기능을 외부로 제공한다.

서비스 경계는 구문 또는 도메인(업무)의 경계를 따른다. 예를 들어 사용자 관리, 상품 관리, 주문 관리와 같은 각 업무 별로 서비스를 나눠서 정의한다. 사용자/상품 관리 처럼 여러개의 업무를 동시에 하나의 서비스로 섞어서 정의하지 않는다.

REST API에서 /users, /products와 같이 주요 URI도 하나의 서비스 정의의 범위로 좋은 예가 된다.  


마이크로 서비스 아키텍쳐의 구조


마이크로 서비스 아키텍쳐의 구조는 다음과 같은 모양을 따른다.

각 컴포넌트는 서비스라는 형태로 구현되고 API를 이용하여 타 서비스와 통신을 한다.



배포 구조관점에서도 각 서비스는 독립된 서버로 타 컴포넌트와의 의존성이 없이 독립적으로 배포 된다.

예를 들어 사용자 관리 서비스는 독립적인 war파일로 개발되어, 독립된 톰캣 인스턴스에 배치된다. 확장을 위해서 서비스가 배치된 톰캣 인스턴스는 횡적으로 스케일 (인스턴스 수를 더함으로써)이 가능하고, 앞단에 로드 밸런서를 배치하여 서비스간의 로드를 분산 시킨다.



가장 큰 특징이, 애플리케이션 로직을 분리해서 여러개의 애플리케이션으로 나눠서 서비스화하고, 각 서비스별로 톰캣을 분산 배치한 것이 핵심이다.


데이타 분리


데이타 저장관점에서는 중앙 집중화된 하나의 통 데이타 베이스를 사용하는 것이 아니라 서비스 별로 별도의 데이타 베이스를 사용한다.       보통 모노리틱 서비스의 경우에는 하나의 통 데이타 베이스 (보통 RDBMS를 사용) 하는 경우가 일반적이지만, 마이크로 서비스 아키텍쳐의 경우, 서비스가 API에서 부터 데이타 베이스까지 분리되는 수직 분할 원칙 (Vertical Slicing)에 따라서 독립된 데이타 베이스를 갖는다.



데이타 베이스의 종류 자체를 다른 데이타 베이스를 사용할 수 도 있지만, 같은 데이타 베이스를 사용하더라도 db를 나누는 방법을 사용한다.

이 경우, 다른 서비스 컴포넌트에 대한 의존성이 없이 서비스를 독립적으로 개발 및 배포/운영할 수 있다는 장점을 가지고 있으나, 다른 컴포넌트의 데이타를 API 통신을 통해서만 가지고 와야 하기 때문에 성능상 문제를 야기할 수 있고, 또한 이 기종 데이타 베이스간의 트렌젝션을 묶을 수 없는 문제점을 가지고 있다. (이러한 데이타 분산에 의한 트렌젝션 문제는 SOA 때부터 있어 왔다.) 데이타 분산으로 인한 트렌젝션 문제는 뒤에서 조금 더 자세하게 설명하도록 한다.


API Gateway


마이크로 서비스 아키텍쳐 설계에 있어서 많이 언급되는 컴포넌트 중의 하나가 api gateway 라는 컴포넌트 이다. api gateway는 마치 프록시 서버 처럼 api들 앞에서 모든 api에 대한 end point를 통합하고, 몇가지 추가적인 기능을 제공하는 미들웨어로, SOA의 ESB (Enterprise Service Bus)의 경량화 버전이다. Apigateway가 마이크로 서비스 아키텍쳐 상에서 수행하는 주요 기능을 살펴보면 다음과 같다.

EndPoint 통합 및 토폴로지 정리

마이크로 서비스 아키텍쳐의 문제점 중의 하나는 각 서비스가 다른 서버에 분리 배포 되기 때문에, API의 End point 즉, 서버의 URL이 각기 다르다는 것이다.

사용자 컴포넌트는 http://user.server.com, 상품 컴포넌트는 http://product.server.com 과 같은 분리된 URL을 사용하는데, 이는 API 사용자 경험 관점에서도 사용하기가 불편하다. 특히 마이크로 서비스 아키텍쳐는 컴포넌트를 되도록이면 업무 단위로 잘게 짜르는 fine grained (작은 덩어리)의 서비스를 지향하기 때문에, 컴포넌트의 URL 수는 더 많이 늘어 날 수 있다.

API를 사용하는 클라이언트에서 서버간의 통신이나, 서버간의 API 통신의 경우 p2p(Point to Point)형태로 토폴로지가 복잡해지고 거미줄 모양의 서비스 컴포넌트간의 호출 구조는 향후 관리의 문제를 일으킬 수 있다. 하나의 end point를 변경하였을때, 제대로 관리가 되지 않을 경우가 있다.




<그림. P2P 형태의 토폴리지>

이러한 토폴로지상의 문제점을 해결하기 위해서 중앙에 서비스 버스와 같은 역할을 하는 채널을 배치 시켜서, 전체 토폴로지를 p2p에서 hub & spoke 방식으로 변환 시켜서, 서비스간 호출을 단순화 시킬 수 있다.




<그림. 버스 기반의 Hub & Spoke 토폴리지>

Orchestration


다른 기능으로는 orchestration 이라는 개념이 있다. 기존 open api의 mash up과 같은 개념으로, 여러개의 서비스를 묶어서 하나의 새로운 서비스를 만드는 개념이다.

예를 들어, 포인트 적립과, 물품 구매라는 서비스가 있을때, 이 두개의 서비스를 묶어서 “물품 구입시 포인트 적립”이라는 새로운 서비스를 만들어 낼 수 있다. 이러한 orchestration 기능은, api gateway를 통해서 구현될 수 있다.

이는 마이크로 서비스 아키텍쳐가 서비스 자체가 fine grained 형태로 잘게 쪼게졌기 때문에 가능한 일인데, 사실 orchestration을 api gateway 계층에서 하는 것은 gateway 입장에서 부담이 되는 일이다. 실제로 과거의 SOA 시절에 많은 ESB(Enterprise Service Bus) 프로젝트가 실패한 원인 중의 하나가 과도한 orchestration 로직을 넣어서 전체적인 성능 문제를 유발한 경우가 많았다. 그래서 orchestration 서비스의 활용은 마이크로 서비스 아키텍쳐에 대한 높은 이해와 api gateway 자체에 대한 높은 수준의 기술적인 이해를 필요로 한다.

실제로 넷플릭스의 경우 마이크로 서비스 아키텍쳐를 사용하면서, 여러개의 서비스들을 gateway 계층을 통해서 orchestration 하는 모델을 사용하고 있다. 


공통 기능 처리 (Cross cutting function handling)


또한 API에 대한 인증 (Authentication)이나, Logging과 같은 공통 기능에 대해서 서비스 컴포넌트 별로 중복 개발해야 하는 비효율성을 유발할 수 있다. api gateway에서 이러한 공통 기능을 처리하기 되면, api 자체는 비지니스 로직에만 집중을 하여 개발에 있어서의 중복등을 방지 할 수 있다.

mediation

이외에도 XML이나 네이티브 메세지 포맷을 json등으로 상호 변환해주는 message transformation 기능이나, 프로토콜을 변환하는 기능, 서비스간의 메세지를 라우팅해주는 기능등 여러가지 고급 mediation 기능을 제공을 하지만, api gateway를 최대한 가볍게 가져간다는 설계 원칙 아래서 가급 적이면 고급적인 mediation 기능을 사용할 때는 높은 수준의 설계와 기술적인 노하우를 동반해야 한다.


※ ESB vs APIgateway

SOA 프로젝트의 실패중의 하나가 ESB로 꼽히는 경우가 많은데, 이는 ESB를 Proxy나 Gateway처럼 가벼운 연산만이 아니라, 여러개의 서비스를 묶는 로직에  무겁게 사용했기 때문이다. (사용하면 안된다는 것이 아니라 잘 사용해야 한다는 것이다.) ESB는 메세지를 내부적으로 XML로 변환하여 처리하는데, XML 처리는 생각하는것 보다 파싱에 대한 오버헤드가 매우 크다.  또한 ESB의 고유적인 버스나 게이트웨이로써의 특성이 아니라 타 시스템을 통합 하기 위한 EAI적인 역할을 ESB를 이용해서 구현함으로써 많은 실패 사례를 만들어 내었다. 그래서 종종 ESB는 Enterprise Service Bus가 아니라 EnterpriSe nightmare Bus로 불리기도 한다. J

이러한 개념적인 문제를 해결하기 위해서 나온 제품군이 apigateway라는 미들웨어 제품군들인데, ESB와 기본적인 특성은 유사하나 기능을 낮추고 EAI의 통합 기능을 제거하고 API 처리에만 집중한 제품군들로, 클라우드상에서 작동하는 PaaS (Platform As A Service)형태의 서비스로는 apigee.com이나 3scale.com 등이 있고, 설치형 제품으로는 상용 제품인 CA社의 Layer7이나 오픈소스인 Apache Service Mix, MuleSoft의 ESB 제품 그리고 WSO2의 API Platform 등이 있다.

Apigateway 부분에 마이크로 서비스 아키텍쳐의 다른 부분 보다 많은 부분을 할애한 이유는, 컴포넌트를 서비스화 하는 부분에 까지는 대부분 큰 문제가 없이 적응을 하지만 apigateway의 도입 부분의 경우, 내부적인 많은 잡음이 날 수 있고, 또한 도입을 했더라도 잘못된 설계나 구현으로 인해서 실패 가능성이 비교적 높은 모듈이기 때문이다. 마이크로 서비스 아키텍쳐의 핵심 컴포넌트이기도 하지만, 도입을 위해서는 팀의 상당 수준의 높은 기술적인 이해와 개발 능력을 필요로 한다.


배포


마이크로 서비스 아키텍쳐의 가장 큰 장점 중의 하나가 유연한 배포 모델이다. 각 서비스가 다른 서비스와 물리적으로 완벽하게 분리되기 때문에 변경이 있는 서비스 부분만 부분 배포가 가능하다 예를 들어서, 사용자 관리 서비스 로직이 변경되었을 때, 모노리틱 아키텍쳐의 경우에는 전체 시스템을 재 배포해야 하지만, 마이크로 서비스 아키텍쳐의 경우에는 변경이 있는 사용자 관리 서비스 부분만 재 배포 하면 되기 때문에, 빠르고 전체 시스템의 영향도를 최소화한 수준에서 배포를 진행할 수 있다.


확장성


서비스 별로 독립된 배포 구조는 확장성에 있어서도 많은 장점을 가지고 오는데, 부하가 많은 특정 서비스에 대해서만 확장이 가능하여 조금 더 유연한 확장 모델을 가질 수 있다. 모노리틱 아키텍쳐의 경우에는 특정 서비스의 부하가 많아서 성능 확장이 필요할때, 전체 서버의 수를 늘리거나 각 서버의 CPU 수를 늘려줘야 하지만, 마이크로 서비스 아키텍쳐의 경우에는 부하를 많이 받는 서비스 컴포넌트 만 확장을 해주면 된다.


Conway’s Law (컨웨이의 법칙)


마이크로 서비스 아키텍쳐의 흥미로운 점중의 하나는 아키텍쳐 스타일의 조직 구조나 팀 운영 방식에 영향을 준다는 것인데, 마이크로 서비스 아키텍쳐는 컨웨이의 법칙에 근간을 두고 있다.

컨웨이의 법칙은 “소프트웨어의 구조는 그 소프트웨어를 만드는 조직의 구조와 일치한다”는 이론이다.

현대의 소프트웨어 개발은 주로 애자일 방법론을 기반으로 하는 경우가 많다. 애자일 팀의 구조는 2 피자팀(한팀의 인원수는 피자 두판을 먹을 수 있는 정도의 인원 수가 적절하다.)의 모델을 많이 따르는데, 한 팀이 7~10명정도로 이루어지고, 이 인원 수가 넘어가면 팀을 분리하는 모델이다.

마이크로 서비스 아키텍쳐는 각 컴포넌트를 팀에 배치해서 책임지고 개발하는 것을 근간으로 하며, 팀간의 의존성을 제거해서 각 팀이 컴포넌트 개발을 독립적으로할 수 있는 구조로 잡혀있다.


마이크로 서비스 아키텍쳐의 문제점


분홍빛 미래 처럼 보이는 마이크로 서비스 아키텍쳐는 아무런 문제가 없는 것일까? 당연히 여러가지 장점을 제공하는 대신에 그만한 단점을 가지고 있다.


성능


모노리틱 아키텍쳐는 하나의 프로세스 내에서 서비스간의 호출을 call-by-reference 모델을 이용한다. 반면 마이크로 서비스 아키텍쳐는 서비스간의 호출을 API 통신을 이용하기 때문에 값을 json이나 xml에서 프로그래밍에서 사용하는 데이타 모델 (java object등)으로 변환하는 marsharing 오버헤드가 발생하고 호출을 위해서 이 메세지들이 네트워크를 통해서 전송되기 때문에 그만한 시간이 더 추가로 소요된다.


메모리


마이크로 서비스 아키텍쳐는 각 서비스를 독립된 서버에 분할 배치하기 때문에, 중복되는 모듈에 대해서 그만큼 메모리 사용량이 늘어난다.

예를 들어 하나의 톰캣 인스턴스에서 사용자 관리와 상품 관리를 배포하여 운용할 경우, 하나의 톰캣을 운영하는데 드는 메모리와, 스프링 프레임웍과 같은 라이브러리를 사용하는데 소요되는 메모리 그리고 각각의 서비스 애플리케이션이 기동하는 메모리가 필요하다.

그러나 마이크로 서비스 아키텍쳐로 서비스를 배포할 경우 사용자 관리 서비스 배포와 상품 관리 서비스 배포를 위한 각각의 별도의 톰캣 인스턴스를 운용해야 하고, 스프링 프레임웍과 같은 공통 라이브러리도 각각 필요하기 때문에, 배포하고자 하는 서비스의 수 만큼 중복된 양의 메모리가 필요하게 된다.

위의 두 문제는 반드시 발생하는 문제점이기는 하나 현대의 인프라 환경에서는 크게 문제는 되지 않는다. (기존에 비해 상대적으로). 현대의 컴퓨팅 파워 자체가 워낙 발달하였고, 네트워크 인프라 역시 기존에 1G등에 비해서 내부 네트워크는 10G를 사용하는 등, 많은 성능상 발전이 있었다. 또한 메모리 역시 비용이 많이 낮춰지고 32bit에서 64bit로 OS들이 바뀌면서, 가용 메모리 용량이 크게 늘어나서 큰 문제는 되지 않는다. 또한 성능상의 문제는 비동기 패턴이나 캐슁등을 이용해서 해결할 수 있는 다른 방안이 많기 때문에 이 자체는 큰 문제가 되지 않는다.

그보다 더 문제점은 아래에서 언급하는 내용들인데,


테스팅이 더 어려움


마이크로 서비스 아키텍쳐의 경우 서비스들이 각각 분리가 되어 있고, 다른 서비스에 대한 종속성을 가지고 있기 때문에, 특정 사용자 시나리오나 기능을 테스트하고자 할 경우 여러 서비스에 걸쳐서 테스트를 진행해야 하기 때문에 테스트 환경 구축이나 문제 발생시 분리된 여러개의 시스템을 동시에 봐야 하기 때문에 테스팅의 복잡도가 올라간다.

운영 관점의 문제


운영 관점에서는 서비스 별로 서로 다른 기술을 사용할 수 있으며, 시스템이 아주 잘게 서비스 단위로 쪼게 지기 때문에 운영을 해야할 대상 시스템의 개수가 늘어나고, 필요한 기술의 수도 늘어나게 된다.


서비스간 트렌젝션 처리


구현상의 가장 어려운 점중의 하나가, 트렌젝션 처리이다. 모노리틱 아키텍쳐에서는 RDBMS를 사용하면서 하나의 애플리케이션 내에서 트렌젝션이 문제가 있으면 쉽게 데이타베이스의 기능을 이용해서 rollback을 할 수 있었다. 여러개의 데이타베이스를 사용하더라도, 분산 트렌젝션을 지원하는 트렌젝션 코디네이터 (JTS – Java Transaction Service)등을 이용해서 쉽게 구현이 가능했는데, API 기반의 여러 서비스를 하나의 트렌젝션으로 묶는 것은 불가능 하다.

쉽게 예를 들어서 설명을 하면, 계좌에서 돈을 빼는 서비스와, 계좌에 돈을 넣는 서비스가 있다고 하자. 이 둘은 API를 expose했을 때, 계좌에서 돈을 뺀 후, 계좌에 돈을 넣기 전에 시스템이 장애가 나면, 뺀 돈은 없어지게 된다. 모노리틱 아키텍쳐를 사용했을 경우에는 이러한 문제를 트렌젝션 레벨에서 롤백으로 쉽게 해결할 수 있지만 API 기반의 마이크로 서비스 아키텍쳐에서는 거의불가능하다.

사실 이 문제는 마이크로 서비스 아키텍쳐 이전에도, 서비스와 API를 기본 컨셉으로 하는 SOA에도 있었던 문제이다.

이러한 문제를 해결하기 위해서 몇가지 방안이 있는데,

그 첫번째 방법으로는 아예 애플리케이션 디자인 단계에서 여러개의 API를 하나의 트렌젝션으로 묶는 분산 트렌젝션 시나리오 자체를 없애는 방안이다. 분산 트렌젝션이 아주 꼭 필요할 경우에는 차라리 모노리틱 아키텍쳐로 접근하는 것이 맞는 방법이다. 앞서도 언급했듯이 마이크로 서비스 아키텍쳐의 경우, 금융이나 제조와 같이 트렌젝션 보장이 중요한 엔터프라이즈 시스템보다는 대규모 처리가 필요한 B2C 형 서비스에 적합하기 때문에, 아키텍쳐 스타일 자체가 트렌젝션을 중요시 하는 시나리오에서는 적절하지 않다.

그럼에도 불구하고, 트렌젝션 처리가 필요할 경우, 트렌젝션 실패시 이를 애플리케이션 적으로 처리해 줘야 하는 데, 이를 보상 트렌젝션(compensation transaction)이라고 한다. 앞의 계좌 이체 시나리오에서 돈을 뺀 후, 다른 계좌에 넣다가 에러가 났을 경우에, 명시적으로, 돈을 원래 계좌로 돌려주는 에러 처리 로직을 구현해야 한다.

마지막 방법으로 복합 서비스 (composite service)라는 것을 만들어서 활용하는 방법이 있는데, 복합 서비스란 트렌젝션을 묶어야 하는 두개의 시스템을 트렌젝션을 지원하는 네이티브 프로토콜을 이용해서 구현한 다음 이를 API로 노출 시키는 방법이다.

두개의 데이타 베이스는 XA(eXtended Architecture)와 같은 분산 트렌젝션 프로토콜을 써서 서비스를 개발하거나 또는 SAP나 Oracle 아답터와 같이 트렌젝션을 지원하는 네이티브 아답터를 사용하는 방법이다. 기존에 SOA에서 많이 했던 접근방법이기는 하나, 복합 서비스를 사용할 경우, 복합서비스가 서로 다른 두개의 서비스에 걸쳐서 tightly coupled하게 존재하기 때문에, 마이크로 서비스 아키텍쳐의 isolation(상호 독립적)인 사상에 위배되고 서비스 변경시에 이 부분을  항상 고려해야 하기 때문에 아키텍쳐상의 유연성이 훼손되기 때문에 꼭 필요하지 않은 경우라면 사용하지 않는 것이 좋다.


거버넌스 모델


거버넌스 (governance)란, 시스템을 개발하는 조직의 구조나 프로세스를 정의한 것으로, 일반적으로 중앙 집중화된 조직에서 표준 프로세스와 가이드를 기반으로 전체 팀을 운용하는 모델을 사용한다. 이를 중앙 집중형 거버넌스 모델 (Centralized governance model) 이라고 하는데, 이 경우 전체 시스템이 동일한 프로세스와 기술을 가지고 개발이 되기 때문에, 유지 보수가 용이하고 팀간의 인원 교체등이 편리하다는 장점을 가지고 있다. 전통적인 개발 모델들은 이러한 중앙 집중현 거버넌스 모델을 사용한다.

그러나 현대의 웹 개발의 경우, 오픈 소스 발달로 선택 가능한 기술들이 많고 각 요구 사항에 따라서 효율성 측면등을 고려할때 각각 최적화된 기술을 사용하는 것이 좋은 경우가 있다.

예를 들어, 전체 표준을 자바+RDBMS로 정했다 하더라도, 파일 업로드 다운로드 관련 컴포넌트는 io 성능과 많은 동시접속자를 처리할 수 있는 node.js가 유리하다던지, 데이타의 포맷은 복잡하지만, 복잡한 쿼리가 없을 경우에는 json document 기반의 mongodb와 같은 NoSQL등이 유리한 사례 등이 된다. 이러한 기술을 도입하기 위해서는 중앙 집중형 거버넌스 모델에서는 모든 개발팀을 교육 시키고, 운영 또한 준비를 해야하기 때문에 기술에 대한 적용 민첩성이 떨어지게 된다.

이러한 문제점을 해결 하는 거버넌스 모델이 분산형 거버넌스 모델 (De-Centralized governance model)인데, 이는 각 팀에 독립적인 프로세스와 기술 선택 권한을 주는 모델로, 각 서비스가 표준 API로 기능을 바깥으로 노출할 뿐 내부적인 구현 기술 구조는 추상화되어 가능한 사상이다

분산형 거버넌스 모델을 수행하려면, 이에 맞는 팀구조가 필요한데, 이 팀 구조는 다음과 같은 몇가지 특징을 가지고 있어야 한다.


Cross functional team


기존의 팀 모델은 역할별로 나뉘어진 모델로 팀을 구분한다. 기획팀,UX팀,개발팀,인프라 운영팀 등 공통적인 특성으로 나누는 것이 기존의 팀 모델이다. 이런 팀 모델은 리소스의 운영에 유연성을 부여한다. 개발 인력이 모자르면 팀 내에서 개발인원을 다른 프로젝트에서 충당하는 등의 리소스 운영이 가능하지만 반대로 팀간의 커뮤니케이션이 팀이라는 경계에 막혀서 원할하지 않고 협의에 걸리는 시간으로 인해서 팀의 운영 속도가 떨어진다.

cross function team 모델은 하나의 팀에 UX, 개발팀,인프라팀등 소프트웨어 시스템을 개발하는데 필요한 모든 역할을 하나의 팀에 구성하고 움직이는 모델로, 각 서비스 개발팀이 cross functional team이 되서 움직인다.



<그림 역할 중심의 개발팀 과 cross functional team에 대한 모델 비교>

이 경우 서비스 기획에서 부터 설계,개발,운영이 가능해지고 다른 팀에 대한 의존성이 없어짐으로써 빠른 서비스 개발이 가능해진다.


You build,You run-Devops


기술에 대한 독립성을 가지려면 구현 뿐만 아니라 운영 또한 직접할 수 있는 능력을 가져야 한다. 그래서 개발과 운영을 하나의 조직에 합쳐 놓는 구조를 Devops라고 한다.

Devops는 Development와 Operation을 합성한 단어로, 개발팀과 운영팀이 다른 팀으로 분리되어 있어서 발생하는 의사 소통의 문제점을 해결하고, 개발이 운영을 고려하고, 운영에서 발생하는 여러 문제점과 고객으로부터의 피드백을 빠르게 수용하여 서비스 개선에 반영하는 개발 모델이다.

이런 모델이 가능해진 이유는 운영팀만의 고유 영역이었던 인프라에 대한 핸들링이 클라우드의 도입으로 인해서 쉬워져서, 애플리케이션 개발자도 웹사이트를 통해서 손쉽게 디스크나 네트워크 설정, 서버 설정등이 가능해졌기 때문이다.

Devops는 대단히 좋은 모델이고 아마존이나 넷플릭스등이 적용하고 있는 모델이기는 하나, 이 역시 대단히 높은 수준의 팀의 성숙도가 필요하다.

개발자가 애플리케이션 개발 뿐만 아니라, 인프라에 대한 설계 및 운영까지 담당해야 하기 때문에 기존의 애플리케이션만 개발하던 입장에서는 대단히 부담이 되는 일이다.

좋은 모델이기는 하지만 충분히 준비가 되지 않은 상태에서 넘어가게 되면은 운영상의 많은 장애를 유발하기 때문에, 팀의 성숙도에 따라서 심각하게 고민해보고 적용을 해보기를 권장한다.


Project vs product


분산형 거버넌스 모델에서 중요한 점 중의 하나는 연속성이다. 거버넌스를 분산 시켜버렸기 때문에 팀별로 다른 형태의 표준과 기술 프로세스를 통해서 개발을 하기 때문에, 새로운 인원이 들어오거나 다른 팀으로 인원이 이동하였을 경우 팀에 맞는 형태의 재 교육이 필요하고 그간의 축적된 노하우가 100% 활용되지 못할 수 가 있기 때문에 가능하면 팀원들은 계속해서 해당 서비스 개발에 집중할 필요가 있다.

이를 위해서는 프로젝트의 컨셉 변화가 필요한데, 일반적으로 프로젝트란 일정 기간에 정해진 요구 사항을 구현하는데 목표가 잡혀 있으며, 프로젝트가 끝나면 인원은 다시 흩어져서 새로운 프로젝트에 투입 되는 형태로 역할 중심의 프로젝트팀 운용 방식에는 적절하다.

그러나 분산형 거버넌스 모델에서는 팀원의 영속성을 보장해줘야 하는데 이를 위해서는 프로젝트가 아니라 프로덕트(즉 상품)형태의 개념으로 개발 모델이 바뀌어야 한다. 팀은 상품에 대한 책임을 지고, 요구사항 정의 발굴에서 부터 개발 그리고 운영까지 책임을 지며, 계속해서 상품을 개선해 나가는 활동을 지속해야 한다. 이를 상품 중심의 개발팀 모델이라고 한다.


Self-organized team


이러한 요건등이 만족 되면, 팀은 독립적으로 서비스 개발을 할 수 있는 형태가 된다. 스스로 기획하고 개발하며 운영을 하며 스스로 서비스를 발전 시키는 하나의 회사와 같은 개념이 되는 것이다.

이렇게 독립적인 수행 능력을 가지고 있는 팀 모델을 self-organized team 모델이라고 한다.


Alignment 


이러한 분산형 거버넌스 모델을 수행하기 전에 반드시 주의해야 할 점이 있는데, alignment 라는 개념이다 alignment는 각 팀간의 커뮤니케이션 방법이나 프로세스등 최소한 표준과 기술적인 수준을 맞추는 과정인데, 쉽게 이야기해서 개발 경험이 전혀 없는 대학을 갓졸업한 사람들로 팀을 만들고, 기존의 팀들은 4~5년차 경력 인원들만으로 팀을 만들어서 전체 팀을 운용하면 어떻게 될까?

마이크로 서비스 아키텍쳐는 각 서비스들이 상호 의존성을 가지고 있기 때문에, 개발경험이 없는 팀이 전체 팀의 개발 속도를 못 따라오고, 또한 품질등에도 심각한 문제가 생긴다. 그래서 어느 일정 수준 이상으로 팀의 능력을 끌어 올려주고, 전체 팀에서 사용하는 최소한의 공통 프로세스등에 대해서는 서로 맞추어 놓을 필요가 있다. 이것이 바로 alignment 의 개념이다.

분산형 거버넌스 모델을 잘못 해석하거나 악용이 되면 팀에게 무조건적인 자치권을 부여하는 것으로 오역되서.. “분산형 거버넌스가 대세랍니다. 우리팀은 우리가 알아서 할테니 신경 끄세요.” 라는 형태의 잘못된 요청으로 전체 팀과 전체 시스템 아키텍쳐를 망쳐 버릴 수 있다.

제대로 된 해석은 “우리는 전체 팀이 나가야 할 방향과 비지니스 밸류에 대해서 이해를 하고 있습니다. 또한 이미 팀간의 커뮤니케이션이나 전체 시스템 구조에 대한 이해를 하고 있습니다. 이를 바탕으로 조금 더 빠른 개발과 효율성을 위한 모든 기능(역할)을 가지고 있는 팀을 운영하고자 합니다.” 가 제대로 된 해석이라고 볼 수 있겠다.


Evolutionary Model (진화형 모델)


지금 까지 간략하게나마 마이크로 서비스 아키텍쳐에 대해서 알아보았다.

마이크로 서비스 아키텍쳐는 서비스의 재사용성, 유연한 아키텍쳐 구조, 대용량 웹 서비스를 지원할 수 있는 구조등으로 많은 장점을 가지고 있지만, 운영하는 팀에 대해서 높은 성숙도를 필요로 한다. 그래서 충분한 능력을 가지지 못한 팀이 마이크로 서비스 아키텍쳐로 시스템을 개발할 경우에는 많은 시행 착오를 겪을 수 있다.

마이크로 서비스 아키텍쳐를 적용할때는 처음 부터 시스템을 마이크로 서비스 아키텍쳐 형태로 설계해서 구현할 수 도 있겠지만, 모노리틱 시스템에서 부터 시작하여, 비지니스 운용시 오는 문제점을 기반으로 점차적으로 마이크로 서비스 아키텍쳐 형태로 진화 시켜 나가는 방안도 좋은 모델이 된다. 비지니스와 고객으로 부터 오는 피드백을 점차적으로 반영 시켜나가면서 동시에 팀의 성숙도를 올려가면서 아키텍쳐 스타일을 변화 시켜가는 모델로, 많은 기업들이 이런 접근 방법을 사용했다. 트위터의 경우에도, 모노리틱 아키텍쳐에서 시작해서 팀의 구조를 점차적으로 변환 시켜 가면서 시스템의 구조 역시 마이크로 서비스 아키텍쳐 형태로 전환을 하였고, 커머스 시장에서 유명한 이베이 같은 경우에도, 그 시대의 기술적 특성을 반영하면서 비지니스의 요구 사항을 적절히 반영 시켜가면서 시스템을 변화 시켜 나가는 진화형 모델로 아키텍쳐를 전환 하였다.


SOA와 비교


마이크로 서비스 아키텍쳐는 종종 SOA와 비교 되며, SOA는 틀리고 마이크로 서비스 아키텍쳐는맞다 흑백 논리 싸움이 벌어지고는 하는데,

SOA와 마이크로 서비스 아키텍쳐는 사실상 다른 개념이 아니라 SOA가 마이크로 서비스 아키텍쳐에 대한 조상 또는 큰 수퍼셋의 개념이다. 흔히 SOA가 잘못되었다고 이야기 하는 이유는 SOA를 아키텍쳐 사상으로 보는 것이 아니라 SOAP 기반의 웹서비스나, Enterprise Service Bus와 같은 특정 제품을 SOA로 인식하기 때문이다. SOA는 말 그대로 설계에 대한 사상이지 특정 기술을 바탕으로 한 구현 아키텍쳐가 아니다.

큰 의미에서 보자면 마이크로 서비스 아키텍쳐의 서비스 역시, SOA에서 정의한 서비스 중에서 fine grained 서비스로 정의되는 하나의 종류이며, api gateway역시 SOA 에서 정의한 ESB의 하나의 구현방식에 불과 하다.

만약에 기회가 된다면 마이크로 서비스 아키텍쳐에 대해 제대로 이해하기 위해서 SOA 를 반드시 공부해보기를 바란다.


결론


마이크로 서비스 아키텍쳐는 대용량 웹시스템에 맞춰 개발된 API 기반의 아키텍쳐 스타일이다. 대규모 웹서비스를 하는 많은 기업들이 이와 유사한 아키텍쳐 설계를 가지고 있지만, 마이크로 서비스 아키텍쳐가 무조건 정답은 아니다. 하나의 설계에 대한 레퍼런스 모델이고, 각 업무나 비지니스에 대한 특성 그리고 팀에 대한 성숙도와 가지고 있는 시간과 돈과 같은 자원에 따라서 적절한 아키텍쳐 스타일이 선택되어야 하며, 또한 아키텍쳐는 처음 부터 완벽한 그림을 그리기 보다는 상황에 맞게 점진적으로 진화 시켜 나가는 모델이 바람직하다.

 특히 근래의 아키텍쳐 모델은 시스템에 대한 설계 사상 뿐만 아니라 개발 조직의 구조나 프로젝트 관리 방법론에 까지 영향을 미치기 때문에 단순히 기술적인 관점에서가 아니라 조금 더 거시적인 관점에서 고려를 해볼 필요가 있다.

 

참고 자료

Ebay 아키텍쳐 : http://www.addsimplicity.com/downloads/eBaySDForum2006-11-29.pdf

Netflix 아키텍쳐 : http://techblog.netflix.com/2013/01/optimizing-netflix-api.html

infoQ Microservice Architecture : http://www.infoq.com/articles/microservices-intro

MicroService 개념 http://microservices.io/patterns/microservices.html

Martin folwer : http://martinfowler.com/articles/microservices.html

Dzone microservice architecture : http://java.dzone.com/articles/microservice-architecture

Thought works의 PPT : http://www.infoq.com/presentations/Micro-Services

node.js로 apigateway 만들기 : 정리 잘되어 있음. http://plainoldobjects.com/presentations/nodejs-the-good-parts-a-skeptics-view/

저작자 표시
신고

대용량 분산 시스템 아키텍쳐 디자인


대용량 분산 시스템에 대한 아키텍쳐 설계에 대한 내용을 공유합니다. 아직 많이 부족합니다. 많은 피드백 부탁드립니다.


1. 아키텍쳐 설계 프로세스


2. 대용량 분산 시스템 아키텍쳐


3. 대용량 분산 시스템 아키텍쳐 디자인 패턴


4. 레퍼런스 아키텍쳐 - SOA 


5. 레퍼런스 아키텍쳐 - REST


저작자 표시
신고

API 플랫폼에 대한 소개

아키텍쳐 /대용량 아키텍쳐 | 2013.10.30 01:02 | Posted by 조대협

API Platform

조대협


근래에 들어서 모바일 애플리케이션의 발전과 SNS의 발전과 더불어서 Open API 에 대한 관심도가 급격하게 높아졌다. Open API, API를 내부 사용자뿐만 아니라 외부 개발자에게까지 공개해서, 외부 개발자가 API를 이용해서 새로운 서비스를 만들도록 하는 모델이다. 근래에 들어서는 API만 전문적으로 개발 및 서비스를 해서 이를 통해서 수익을 창줄하는 비지니스 모델까지 생겨나고 있다. 이런 배경으로 API 관리에 대한 중요성이 대두되었는데, API에 대한 쉬운 관리, 모니터링 및 유료화 그리고, API에 대한 편리한 사용법, 샘플 코드 및 메뉴얼 제공하는 시나리오가 필요하게 되었는데, 이를 하나의 플랫폼 형태로 묶어 놓은 것을 API 플랫폼이라고 한다.

API 플랫폼은 크게, API에 대한 단일 진입 포인트 역할을 하는 게이트 웨이와, API 문서나 샘플 코드들을 제공하는 API 포탈, 그리고 API 서비스 상황을 모니터링할 수 있는 관리 모니터링 기능으로 분리된다.

제품군으로는 크게 두 가지 종류로 나뉘어 지는데, 예전 SOA(Service Oriented Architecture) ESB (Enterprise Service Bus) 를 게이트웨이로 사용하면서 발전해온 SOA 진영쪽 제품과, 처음부터 API 플랫폼으로 디자인되서 서비스되는 이 양쪽으로 나뉘어 진다. 전자의 경우에는 MuleSoft WSO2 API 플랫폼이 대표적인 예이고, 후자의 경우에는 apigee,3scale,Mashery,Layer 7(CA) 등이 있다. 전자의 경우에는 ESB를 기반으로 하기 때문에, API에 대한 워크 플로우 기능이 매우 강력하다. 워크플로우 기능이란, 메세지를 변경하거나 라우팅 또는 다른 기능을 추가하는 부분이 매우 강력하다.반면 포탈이나 모니터링 기능들은 후자에 비해서 약하다. 후자의 경우에는 워크 플로우 기능은 약한 반면, 유료화 모델이나 다양한 사용자 계층 지원 그리고 무엇보다 포탈이 매우 강력하다.

Apigee Mashery 같은 경우는 설치형이 아니라 클라우드 형으로 서비스를 제공하는데, 개발사나 개발자가 API를 개발하고, API 서비스는 클라우드에 설치된 이 API 플랫폼을 통해서 하면, 이 플랫폼이 중간에 Gateway 역할로써 API에 대한 워크플로우 기능 및 모니터링 기능등을 제공하면서 함께, 포탈등의 부가 기능을 제공한다.

예전에 미국 캘리포니아 실리콘 밸리에 있는 apigee 를 방문할 일이 있었다. 긴 시간은 아니었지만, 서비스와 인력들에 대해서 이야기를 나눌 기회가 있었는데, 재미있는 점은 인력 구성원의 많은 인원들이 기존의 SOA 회사 출신(BEA)으로 이루어져 있다는 것이다. API 서비스의 근간은 SOA를 기반으로 하고 있으며, 기술 역시 SOA ESB와 연관된 기술을 많이 사용함으로 이해할 수 있었다. 회사의 분위기는 여타 실리콘 밸리의 회사와 같이 매우 젊고 활기에 차있었다.

API 플랫폼을 서비스로 제공하는 만큼 운영에 대해서 많이 신경을 쓰고 있었는데, 실제로 플랫폼을 서비스로 제공하면서 얻는 노하우는 대단하리라고 본다.

 

주요 기능



API 인증

먼저 API를 사용하려면, API를 호출하는 클라이언트가 인증이 된 사용자인지 아닌지를 구분할 수 있는 인증 Authentication 기능이 필요하다. 앞에서 REST API 보안에서 설명한것 처럼, API Key를 발급하거나 OAuth 기반의 인증을 제공할 필요가 있다. 근래의 API Platform OAuth 기반 인증을 제공하는 것이 일반적인 흐름이다.

사용자 인증

사용자 인증은 API 인증을 받기 위한 Key OAuth 인증을 받거나, 또는 API 포탈에 접속해서 메뉴얼을 보거나 Admin 으로써, API에 대한 모니터링을 하기 위해서 필요한 시나리오이다. 사용자를 API를 사용하는 일반 사용자, 내부 관리자 또는 일반 사용자/Silver/Gold 등의 레벨로 나눠서 서비스를 하는 등의 다양한 서비스 시나리오에 대한 사용자 관리 기능을 제공한다.

SLA management

SLA Service Level Agreement의 약자로, 서비스에 대해서 약속한 수준의 비기능 요건을 만족해주는 것을 이야기 한다. 쉽게 이야기 해서, API 제공자별로 하루에 5000건의 call을 제공하기로 했으면, 5000건 까지만 제공한다던지, API 사용자별로 하루에 1000건의 call을 제공한다던지의 계약 레벨별로 서비스 수준을 조정해줄 수 있는 기능을 제공한다.

Mediation

Mediation은 들어온 API 호출에 대해서 변형을 가하는 것이다.예를 들어 원래 API가 주문 API였다면, 나중에, 주문 API에 포인트 적립 API를 추가해준다거나 API version에 따라서 라우팅을 해주거나 하는 등의 기능을 한다. 이렇게 원래 호출 내용에 대해서 변경을 가하는 것을 Mediation이라고 한다. 그러면 몇가지 Mediation 패턴에 대해서 소개한다. 이런 Mediation 패턴은 앞서 설명한 Workflow 기능에서 제공되며, 이 챕터의 뒤에서 소개할 SOA 아키텍쳐의 ESB의 부분을 참고하면 좋다.

아래는apigee 플랫폼에서 workflow를 적용하는 UI 화면의 일부이다. 보통 workflow IDE같은 개발환경에서 순서도 같은 흐름에 노드를 추가하는 환경으로 적용한다.



출처 : https://blog.apigee.com/detail/api_platform_update_api_proxy_editor_traffic_composition_reports_updated_policies

 

Routing

들어온 메세지에 대해서 라우팅 작업을 한다. 예를 들어서 결재 시나리오에서 API request 필드에 국가 필드가 나뉘어져 있으면, 국가 필드에 따라서 API를 그 국가의 서버로 라우팅을 하거나, 헤더에 정의된 API버전에 따라서 구 API서버나 새로운 버전의 API서버로 라우팅 하는 시나리오를 예로 들 수 있ㄷ.

Function adding

이 기능은 기존에 있던 API 기능에 새로운 기능을 추가하는 것이다. 예를 들어 기존 구매 프로세스에서 포인트 적립 기능을 추가해주는 것등을 할 수 있다. 기존의 클라이언트나 서버의 구현의 변동없이 새로운 기능을 추가할 수 있다.

Message Transformation

메세지 변경은 들어온 메세지를 다른 포맷으로 변경해주는 것이다. 단순하게는 JSON XML로 변경해준다거나 필드가 틀린것을 맵핑 해준다거나 두 필드를 한필드로 합친다거나 반대로 한필드를 여러 필드로 나눈다거나 하는 등의 기능을 제공 한다.

Monitoring

모니터링은 API 서비스 사업자로써 아주 중요한 위치를 차지하는데, API의 사용량, 어떤 API가 많이 사용되는지, 국가별, APP별 통계량이나 평균 응답 시간등에 대한 리포트는 서비스의 정상적인 상태 체크 뿐만 아니라 향후 API 서비스 개발에 유용하게 사용될 수 있다. 아래는 apigee에서 제공하는 API Traffic 모니터링 화면이다.



출처 : http://apigee.com/docs/enterprise/content/monitor-performance-your-api

 

Monetization

Monetization은 쉽게 이야기 해서 유료화 모델이다. API의 과금 정책 월단위, 사용자 단위, 호출 단말건,호출 건수별 등 다양한 정책을 적용할 수 있다. 이에 더해서 이벤트 가격 적용, 또는 사용자 수나, API 호출 수를 구간 별로 나눠서 금액을 책정하는 등의 다양한 가격 정책을 적용할 수 있는데, 이를 Charging이라고 하며, Charging을 하기 위해서는 누가? 어떤 디바이스가 언제? 어떤 API를 호출했는지 등의 Charging을 하기 위한 기초 로그 데이타를 수집하고, Charging이 끝난후에, 신용카드등을 통해서 Payment를 하게 되면, 서비스 국가에 따라서 세금을 내는 Taxation 그리고 세금을 땐 수익을 다시 API 제공자에게 분배 하는 정산 (Pay out)등의 인프라를 제공한다.

Portal

포탈은 API 사용자가 로그인을 한 후, API에 대한 사용법이나 샘플 또는 테스트 등을 할 수 있는 기능등을 제공한다. API를 만드는 것도 중요하지만 편이성을 제공해서 API사용자가 잘 사용할 수 있데 하는 것이 중요하다.

단순하게 메뉴얼 뿐만 아니라 API사용자 입장에서 얼마나 API를 사용했는지 어떤 API가 많이 호출되었는지등의 자료는 API 사용자 입장에서도 과금에 관련되고 또 향후 서비스 개선을 위한 중요한 자료이기 때문에, API 포탈의 기능은 매우 중요하다.

외부 API 사용자를 위한 기능도 있지만, API를 판매하지 않고, 내부에서 다른 부서에 API를 제공하기 위해서도, 쉽게 API를 사용할 수 있게 함으로써 개발 생산성을 높일 수 있다.

만약에 API 메뉴얼 또는 테스트 사이트를 제공하고 싶은 경우에는 Swagger 라는 오픈소스를 참고해보기를 권장한다. https://github.com/wordnik/swagger-core

Swagger API에 대한 메뉴얼 자동 생성 및 테스트 사이트 생성 기능을 제공한다. 아래는 Swagger에 의해서 생성된 API 메뉴얼 사이트 인데, API에 대한 사용법 및 샘플 데이타를 제공하고, 무엇보다 테스트 호출을 날려볼 수 있는 기능을 제공한다.



출처 : http://swagger.wordnik.com

API Governance

이런 API Platform을 사용함으로써 얻게 되는 부가적인 이득은 API 에 대한 관리이다.

내부적으로 API를 사용하면 가장 크게 문제가 되는 것은 API에 대한 관리인데, 여러 부서가 일을하는 경우, API 의 메세지 포맷이나 API URL Naming convention이 다른 일이 발생한다. 특히 SOA 시절의 WSDL 기반의 WebService를 사용하면 최소한의 표준 준수가 가능했으나 (WebService 자체가 표준이다.) REST/JSON의 경우 WSDL과 같은 메세지 포맷에 대한 표준 자체가 없기 때문에 자유도가 증가하여 표준화가 매우 어렵다.

예를 들어 A부서는 version URI에 넣어서 http://myapi/adduser/version3 와 같은 식으로 서비스하거나, B부서는 version HTTP header에 넣어서 http://myapi/addorder URI로 제공하고http headerx-myapi-version:3 식으로 넣어서 같은 회사인데도 API 메세지 포맷에 대한 통일 성을 읽어 버릴 수 있다.

API플랫폼을 사용하면, 서비스 되는 API는 모두 API 플랫폼을 통해야 함으로 API 플랫폼 관리 조직이 API가 표준에 맞지 않으면 API 플랫폼을 통해서 서비스 하는 것을 거부할 수 있으므로, API 표준 준수를 강요할 수 있는 강제성을 통한 통제성을 확보함으로써 API를 중앙에서 집중 관리할 수 있는 장점이 생긴다.

 

저작자 표시
신고

REST API에 대한 보안

아키텍쳐 /대용량 아키텍쳐 | 2013.10.28 00:10 | Posted by 조대협

REST API 보안

조대협

REST API에 대한 보안에 대해서 알아보자. API 에 대한 보안은 인증, 메세지 암호화, 무결성 크게 3가지 관점에서 고민해볼 수 있다.


1)  인증

인증은, REST API를 호출한 사람(클라이언트)가 적절한 사용자 인가를 판단해주는 것이다. 아무나 API를 호출하는 것이 아니라 인증을 받은 사람많이 API를 호출해주게 하는 것인데, 쉽게 생각하면 사용자의 id,passwd로 서비스에 로그인을 하는 개념을 생각할 수 있다.

API Key 방식

API에 대한 인증 방법은 몇가지가 있는데, 그 중에서 가장 기초적인 방법은 API Key를 이용하는 방법이다. API Key, 특정 사용자만 알 수 있는 일종의 문자열이다. 현재 Amazon이 이 방식을 사용하고 있는데 API를 사용하고자 할때, 개발자는 API 제공사의 포탈 페이등에서, API Key를 발급 받고, API를 호출할때, API Key를 메세지 안에 넣어서 호출한다. 서버는 메세지 안에서 API Key를 읽어서, API가 누가 호출한 API인지를 인증하는 흐름이다.

OAuth 방식

가장 기초적이고 구현이 쉬운 방법이지만, 이 방법의 경우에는 표준이 없으며, API 서비스를 제공하는 쪽의 디자인 표준을 따르기 때문에, 근래에는 OAuth라는 형식의 인증 방식을 API 인증에 많이 사용한다. 구글이 이 방식을 사용하는데, OAuth Provider로 부터, 인증을 받으면, OAuth Token이라는 것이 생성된다. API를 호출하는 입장에서는 이 OAuth Token API 호출시 함께 넣어서 호출을 해서, 인증을 받는 방식이다.  OAuth의 경우 API Key 방식보다 조금 더 높은 보안 방식을 제공하고, 표준화가 되어 있기 때문에, API 서비스 제공자 입장에서는 서비스 확산을 위해서 권장되는 방법이다. OAuth의 경우에는 OAuth 인증 표준을 제공하는 인증 서버를 구축해야 하기 때문에, 구현 난이도가 API Key 방식에 비해서 높다.

Bi-diretional Certification

가장 높은 수준의 인증 방식을 제공할 수 있는 개념으로, 보통 HTTPS 통신을 사용할때, 서버에 인증서를 놓고, 단방향으로 SSL을 제공한다. 이 방식은 클라이언트에도 인증서를 놓고, 양방향으로 SSL을 제공하면서, API 호출에 대한 인증을 클라이언트의 인증서를 통해서 하는 방식이다. 가장 구현 방법이 복잡한 방식이기는 하지만, 공인 기관에서 발행된 인증서를 사용한다면 API 를 호출하는 쪽의 신원을 확실하게 할 수 있고, 메세지 까지 암호화 되기 때문에, 가장 높은 수준의 인증을 제공한다. 이런 인증 방식은 일반적인 서비스에서는 사용되지 않으며, 몇몇 높은 인증 수준을 제공하는 서비스나 특정 서버간의 통신을 위해서 사용 하는 것이 좋다.


2)  메세지 암호화

API 내용안에 다른 사람이 봐서는 안될 내용이 있다면 API 메세지를 암호화 해야 한다. 메세지를 암호화 하는 방법은 몇가지 접근 방법이 있는데, 그중에서 가장 손쉬운 방법이 HTTPS를 사용하는 방식이다. 이 방식의 경우에는 서버 설정만으로도 가능하고, 가장 널리 쓰이는 방식이기 때문에 신뢰할만하다.

그런데, HTTPS는 인증서를 이용해서, 초기 통신시에 SSL handshake를 통해서 Secure Connection을 제공하는 방식인데, HTTPS Client Server 사이에서 인증서를 바꿔치면, 중간에 이 인증서를 바꿔친 사람은 메세지를 볼 수 있다. 이를 Man in middle attack이라고 하는데, Server A Client B에게 인증서를 내려줄때, 중간에 Hacker C Server A의 인증서를 자신의 인증서로 바꿔서 Client B에게 전달한다. Client B Hacker C의 인증서로 메세지를 암호화해서 보내면, Hacker C가 중간에 메세지를 자신의 인증서를 이용해서 복호화해서 본후에, Server A가 보낸 원래 인증서로 다시 암호화 해서, 보내면, Server A는 문제가 없다고 판단한다. 실제로 중국 정부의 경우, 중국의 외부에서 들어오는 Traffic을 이러한 방법으로, 인증서를 바꿔쳐서 메세지를 복호화해서 필터링한다고 한다.

이런문제를 해결 하기 위해서는 Server A 에서 발급하는 인증서가 공인된 인증 기관의 인증서임을 확실하게 해주면 된다. 이렇게 인증서를 공인해주는 기관을 CA라고 하는데 Verisign 등이 대표적인 기관이다. 이렇게 공인된 인증서인지는 Client B가 인증서를 받아보고, 해당 인증서가 공인된 기관에서 발급된 인증서인지를 확인하면 된다. (클라이언트 쪽에, 인증서가 공인된 인증서인지를 확인하는 로직을 넣으면 Man in middle attack을 방지할 수 있다.)

데이타 레벨의 암호화

다음으로는 간단하게 암호화가 필요한 특정 필드만 애플리케이션단에서 암호화 해서 보내는 방법이 있다.



http://www.javamex.com/tutorials/cryptography/ciphers.shtml 를 참고하면, 대칭키 기반의 암호화 알고리즘 속도를 비교해놓은 것이 있다. 일반적으로 AES256을 사용하면 빠른 암호화 속도와 높은 보안성을 보장 받을 수 있다. (위, 대칭키 기반의 암호화 알고리즘 속도 비교)


3)  무결성 보장

무결성이란, 서버 입장에서 API 호출을 받았을때, 이 호출이 신뢰할 수 있는 호출인지, 아닌지를 구별하는 방법이다. Hacker가 중간에서 메세지를 가로챈후에, 내용을 변조해서 Server에 보냈을때, 내용이 변조되었는지 여부를 판단하는 방법인데, 일반적으로 HMAC을 이용한 방식이 널리 사용된다. 어떤 원리로 작동하는지 살펴보도록 하자.

먼저 전제는 Rest API를 호출하는 클라이언트와, 서버간에는 대칭키 기반의 암호화 키 “Key”를 가지고 있다고 전제하자. Key는 앞에서 설명한 API Key가 될 수 도 있고, OAuth 토큰이 될 수 도 있고, 또는 사용자가 임의로 정한 키가 될 수 도 있다.



(1) 먼저 클라이언트는 호출하고자 하는 REST API URL을 앞에서 정의한 Key를 이용해서 HMAC 알고리즘을 사용하여 Hash 값을 추출한다.

* 중요 : 여기서는 편의상 URL을 가지고 HMAC 해쉬를 생성하였는데, 전체 메세지에 대한 무결성을 보장하려면 URL이 아니라 메세지 전문 자체에 대해서 Hash를 추출해야 한다.

(2) 그리고 API를 호출할때, 메세지 (또는 URL)에 이 추출한 HMAC을 포함해서 호출한다.

(3) 서버는 호출된 URL을 보고 HMAC을 제외한 나머지 URL을 미리 정의된 Key를 이용해서, HMAC 알고리즘으로 Hash 값을 추출한다.

(4) 서버는 (3)에서 생성된 HMAC값과, API 호출시 같이 넘어온 HMAC 값을 비교해서, 값이 같으면 이 호출을 유효한 호출이라고 판단한다.

만약에 해커가 메세지를 중간에서 가로채서 변조하였을 경우, 서버에서 Hash를 생성하면 변조된 메세지에 대한 Hash가 생성되기 때문에 클라이언트에서 변조전에 보낸 Hash 값과 다르게 된다. 이를 통해서 메세지가 변조되었는지 여부를 판단할 수 있다.

Time stamp를 이용한 replay attack의 방지

그런데, 위의 알고리즘 상의 문제는, 만약에 메세지를 변경하지 않고, Hacker가 동일한 요청을 계속 보낸다면? 메세지를 변조하지 않았기 때문에 서버는 이를 유효한 호출로 인식할 수 있다. 이를 replay attack이라고 하는데 이를 방지하기 위해서는 time stamp를 사용하는 방법이 있다.

HMAC을 생성할때, 메세지를 이용해서만 Hash 값 생성하는 것이 아니라, timestamp를 포함해서 메세지를 생성하는 것이다.

HMAC (Key, (메세지 데이타+timestamp) )

그리고, API를 호출할때, timestamp 값을 같이 실어서 보낸다.

http://service.myapi.com/restapiservice?xxxxx&hmac={hashvalue}&timestamp={호출시간}

이렇게 하면 서버는 이 메세지가 호출된 시간을 알 수 있게 되고, 호출된 시간 +- 10(아니면 개발자가 정한 시간 폭) 만큼의 호출만 정상적인 호출로 인식하고 시간이 지난 호출의 메세지는 비 정상적인 호출로 무시해버리면 된다.

* 참고 : Hacker timestamp URL등을 통해서 볼 수 있다고 하더라도, Key값을 모르기 때문에, timestamp 를 변조할 수 없다. timestamp를 변조할 경우에는 원본 Hash가 원본 timestamp로 생성되었기 때문에, timestamp가 변조된 경우 hash 값이 맞지 않게 된다.

HMAC을 구현하는 해쉬 알고리즘은 MD5,SHA1등이 있는데, 보통 SHA-1 256bit 알고리즘을 널리 사용한다.

HMAC 기반의 REST Hash 구현 방법은

http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/

에 설명이 잘나와 있으니 참고하기 바란다.

또한 HMAC 알고리즘 구현은 위키 http://en.wikipedia.org/wiki/HMAC 를 보면 각 프로그래밍 언어별로 예제 링크가 나와 있으므로 참고하기 바란다.

지금까지 간단하게 나마 REST API 보안 방식에 대해서 알아보았다. 보통 REST API를 디자인하는데, 가장 신경 안쓰는 부분 중의 하나가 API 에 대한 보안이다. 대신 한번 뚤리기 시작하면 걷잡을 수 없이 문제를 만드는 부분이 또 보안이다. API 디자인시에는 반드시 보안에 대한 부분을 고려하고, 위의 3가지 관점에 따라서 API 보안을 반영하기 바란다.


글이 예전 내용이라서 새 글이 업데이트 되었씁니다.

REST API 이해와 설계 - #1 개념 잡기 http://bcho.tistory.com/953

REST API 이해와 설계 - #2 디자인 가이드  http://bcho.tistory.com/954

REST API 이해와 설계 - #3 보안 가이드  http://bcho.tistory.com/955



저작자 표시
신고

MySQL Cluster

MySQL Cluster, which is a different product than the MySQL database server, provides several of these features natively. Synchronous replication and automatic partitioning are standard features in MySQL Cluster; however, this is a very different product than the MySQL server. The name is appropriate for features, yet deceiving when compared with MySQL.

While this product includes a SQL interface, data access for high throughput environments can be achieved from an Application Programming Interface (API). The strengths and features of MySQL Cluster are not always applicable to an Online Transaction Processing (OLTP) application using traditional MySQL replication. There are also additional limitations, including the database size with available memory. This product is ideal in certain situations and is widely used in the telecommunications and gaming industries. The use, understanding, and benefits of MySQL Cluster are topics for an entire book.

It should be noted that MySQL Cluster mainly differs from a regular MySQL server for the following additional reasons:

   There is currently no referential integrity. Foreign keys are being developed for a future version.

   You can only use the NDB storage engine and are then limited to READ-COMMITTED transaction level.

   While primary key lookups are fast and concurrent access is faster than a regular MySQL server, more complex queries, including table joins and group by operations, are more expensive.

For more information about MySQL Cluster, refer to http://www.mysql.com/products/cluster/.

 

Galera Cluster for MySQL

Galera Cluster for MySQL provides synchronous replication and multi-master features when using InnoDB. This means a Galera Cluster supports reads and writes to any node, and provides a true multi-master experience with no lag and no loss of transactions.

Galera Cluster is built on a more generic replication API called wsrep (Write Set REPlication). The wsrep API defines an interface between the database server and the replication plugin and is a separate open source project by Codership. MySQL-wsrep is a patch for MySQL to implement the wsrep API in the database server. This patch will enable the MySQL server to use a wsrep provider plugin, for example, Galera. Galera is the wsrep provider including synchronous multi-master replication.

Galera 2.x is the currently supported version available with MySQL 5.1 GA and MySQL 5.5 GA. Galera is an open source project available under the GPL v3 license, with integration components available under the GPL v2 license to be compatible with MySQL.

Current Limitations

When comparing Galera with a traditional MySQL server, there are some limitations; however, these are not serious roadblocks to trying and using Galera effectively:

   Only InnoDB tables are supported in replication.

   LOCK and UNLOCK statements, GET_LOCK() and RELEASE_LOCK() functions are not supported.

   Log output to TABLE using log_output is not supported. Logging must be to FILE.

   XA (eXtended Architecture) transactions are not currently supported.

 

Tungsten Replicator

Tungsten Replicator provides an extensive list of additional replication features to MySQL, including supporting seamless failover of MySQL servers, flexible filtering of operations, multi-master and multi-master to slave support (i.e., fan in), parallel replication, and much more.

In addition to MySQL replication, Tungsten Replicator can provide heterogeneous data management with other RDBMS and noSQL products, including data synchronization between MySQL and Oracle and vice versa.

Tungsten Replicator is available under the open source GNU GPL v2 license. Continuent, the creators of Tungsten Replicator, also provide commercial support with an enterprise offering. More information can be found athttp://www.continuent.com/solutions/overview.

Features

There is a long list of possible features that can be discussed. In this section, which is an introduction to Tungsten Replicator, the following will be discussed and demonstrated:

   Master failover via slave promotion

   Bidirectional replication (e.g., active/active multi-master)

   Parallelization and replication prefetching for improved performance

   Complex topologies

With traditional MySQL replication, the version of a MySQL slave should be the same or greater than that of the master. With Tungsten Replicator, this limitation does not exist. It is possible to replicate from MySQL 5.5 to MySQL 5.0, for example. There are limitations to any features that are in the newer version of MySQL; however, Tungsten can handle the error condition gracefully and not interrupt replication from continuing.

NOTE    Tungsten Replicator has one significant benefit over other products. There is no change to the standard MySQL installation necessary. There are no custom or patched MySQL binaries to install or maintain. Tungsten Replicator works with your existing MySQL environment and can replicate between MySQL 4.11*, 5.0, 5.1, 5.5, and 5.6 as well as different flavors, including MySQL Community, MySQL Enterprise, MariaDB, and Percona Server.

 

SchoonerSQL

SchoonerSQL by Schooner Information Technology (recently acquired by SanDisk) provides a commercial synchronous MySQL replication solution using a modified version of InnoDB. The documented features include no loss of data, instance failover, and automated recovery capabilities. A SchoonerSQL solution can also include traditional MySQL replication for additional slaves. As a commerical product, there is no access to the software without a formal pre-sales process. A request to review the software for this book was not granted.

 

 

저작자 표시
신고


Auto-Sharding

이번 MySQL의 Enhancement 중 재미있는 기능중의 하나가 Auto Sharding이라는 기능이다. 

Sharding에 대한 개념은 http://bcho.tistory.com/670 를 참고하면 되고.

보통 Sharding이라 하면 Application 에서 작성해서 컨트롤해야 하기 때문에, 데이타 저장 용량을 늘릴 수 있을지 몰라도, 전체적으로 Application의 구현 복잡도를 올리는 문제가 있었다.

이번 MySQL에서는 Auto-Sharding이라는 기능을 제공하는데, 요는 MySQL이 알아서 데이타를 여러 데이타 노드에 분산 저장함으로써 전체 저장 용량을 늘리고, 데이타를 분산 배치 함으로써, 처리 능력을 향상 시킬 수 있다.



위의 그림은 Auto-Sharding 아키텍쳐이다. Primary Key에 의해서 홀수는 좌측 클러스터에, 짝수는 우측 클러스터에 자동으로 나눠서 저장이 되게 된다.


Data Replication

또한 특정 노드 장애에 대비해서 데이타 노드에 저장된 데이타에 대해서 다른 노드에 복제본을 저장한다.


위의 하나의 좋은 예가 있는데, 두개의 클러스터 노드 그룹이 있고, 4개의 데이타 노드가 있다고 가정하자, 하나의 테이블은 데이타 노드 수인 4개만큼 Sharding으로 나뉘어서 각 노드에 저장되고, 각 노드 그룹안에서는 상대방 서버에 대한 Sharding 파티션에 대한 복제본을 저장한다. 이렇게 함으로써, 특정 노드가 장애가 나더라도, 정지 없고, 데이타 유실 없이 서비스가 가능한 구조가 된다.



저작자 표시
신고

     본 문서는 MySQL Cluster 버전을 기준으로 한다.

 

MySQL 배포 구조

MySQL 배포 구조는 다음과 같다.



크게 3가지 종류의 노드를 갖는다.

     MySQL Data Node : Data Node는 실제로 데이타를 저장하고, Query 등을 수행하는 역할을 한다.

     MySQL Application Node : 일종의 라우터 역할로, MySQL 클라이언트에 의해서 들어오는 request를 적절한 Data Node로 라우팅 한다.

     MySQL Management Node : 전체 클러스터에 대한 관리 기능을 수행한다.

위의 그림과 같이 Application Node Data Node는 다수가 존재할 수 있다. Application Node는 실제로 데이타를 저장하지 않고 라우팅 하는 역할만 하기 때문에, 부하 분산과 장애시 Fail Over가 가능하고, Data Node 역시 Node간에 데이타를 replication (복제) 하기 때문에, 특정 Node가 장애가 나더라도 전체 서비스에 문제가 없는 Single Point of Failure 가 없는 구조로 고 가용성을 보장한다.

Memcached 기반의 NoSQL

이번 릴리즈에서 재미있는 기능중 하나는 NoSQL이 유행하다 보니, 이 부분에 대한 보강이 이루어졌다.



Memcached NoSQL에 포함시켰는데, memcached MySQL의 맨 앞단에 넣고, MySQL과 통합 시켜 버렸다. Memcached Key/Value 기반의 스토리지 기능을 제공하는데, 메모리 기반이라는 한계로 인하여, 영구적으로 데이타를 저장할 수 없고 또한, 용량적인 한계도 가지고 있었다. 뒷단에 MySQL을 통합함으로써,

     전체 저장 용량을 확장하고

     데이타를 영구적으로 저장할 수 있으며

     MySQL Cluster의 복제 기능을 이용하여, 다른 MySQL과 복제가 가능하게 되서, 데이타 센터내의 복제나, 데이타 센터간의 복제가 가능하게 되었다.

기존에 MySQL을 사용하고 있으면, 운영 경험을 되살려서 쉽게 운영할 수 있으며, memcached NoSQL 계열에서 복잡도가 가장 낮고 사용이 편리하기 때문에 양쪽의 장점을 잘 융합한 형태라고 볼 수 있다.

Multi Site Replication

SNS 등의 영향으로 인하여 시스템이 거대화 되고, 지역적으로 분리된 데이타 센터를 거쳐서 서비스 하는 시나리오 많아짐에 따라 MySQL도 여러 데이타 센터에 걸친 복제(클러스터링)모델을 제공한다.



Multi-Site Replication이라고 하는데, 거의 근 실시간으로 물리적으로 떨어진 데이타 센터간의 데이타 동기화가 가능하다. 물론 네트웍 구간 속도에 대한 상당한 제약 사항을 가지고 있다.

Ÿ  Latency between remote data nodes must not exceed 20 milliseconds

Ÿ  Bandwidth of the network link must be more than 1 Gigabit per Second

데이타 센터간에 고속 네트웍이 없으면 사실상 어려운 설정이다. 그러나 같은 도시 정도의 근거리에 있는 센터라면 충분히 구축이 가능한 복제 모델이다.

 

이외에도,

1) Auto Sharding을 통한 용량 향상

2) Virtualization 환경을 지원 (XenServer와 OracleVM 만 Certi)

3) OnLine 상태에서 정지 없이 Scale out 할 수 있는 기능 (아마도 클라우드 환경에서 Auto Scale Out을 고려 한듯)

과 같이, 기존에 RDBMS가 가지고 있는 한계점을 보완하고, 클라우드 환경에 맞춰진 기능들이 보강된것이 많이 보인다...

물론 검증되고 잘 보강되어야 하겠지만, 이정도면 왠만한 애플리케이션은 NoSQL없이도 충분히 개발이 가능하지 않을까 싶다.


 

저작자 표시
신고

대용량 시스템을 위한 데이타베이스 아키텍쳐
Sharding & Query Off Loading

RDBMS는 크게 request를 바로 처리하는 트렌젝션 처리용의 OLTP(On-Line Transaction Processing)성과, 데이타를 모아서 분석하고 리포팅하는 OLAP(On-Line Analytical Processing) 두가지로 분리된다. 여기서 설명하는 RDBMS OLTP성의 데이타 베이스 이다.

RDBMS 2차원 테이블 구조의 데이타를 KEY 값을 중심으로 여러개의 컬럼으로 저장되며, 저장된 각각의 로우()은 다른 테이블의 로우와 관계를 가질 수 있다.

RDBMS를 이용한 설계를 하는데, 고려할만한 아키텍쳐는 성능 향상을 위한 Query Off Loading, Sharding이라는 기법이 있다.


Query Off Loading

 Query Off Loading DB의 성능 향상을 위한 기법이다. (정확하게 이야기 하면, 처리량을 증가 시키기 위한 설계 기법이다.) DB 트렌젝션의 70~90%는 대부분 READ 성이 많다. 나머지 10~30% Create/Delete/Update와 같은 트렌젝션인데, Update 성 트렌젝션과 Read 트렌젝션을 분리하는 기법이다.



먼저 Master DB에는 쓰기(Update)만을 허용하고, Master DB의 내용을 중간의 Staging DB라는 곳으로 복사한다. Staging DB는 복제된 내용은 N개의 Slave DB로 복제한다.

애플리케이션은 Master DB에만 쓰기를 하고, 읽기는 Slave DB에서만 수행한다. 이를 위해서 Application DB에 대한 쓰기 로직과 읽기 로직을 분리해서 구현해야 하며, 이 분리된 로직은 쓰기 DB로 접근하기 위한 DB Connection과 읽기 DB로 접근하기 위한 DB Connection을 이용해서 접근한다. 일반적으로 application server에서는 이러한 Connection Connection Pool 을 이용해서 관리 하는데, 읽기 DB의 경우에는 N개의 Slave DB로 부터 읽기 때문에, Application이 이 N개의 Slave DB에 대한 요청을 Load Balacing을 해야 한다. 또한 특정 Slave DB 장애시 다른 Slave DB 인스턴스에 접근을 할 수 있도록 HA (High Availability) 기능을 제공해야 하는데 Connection Pool 자체에 Load Balancing HA 기능을 가지고 있는 Connection Pool을 이용하거나 또는 JDBC Driver와 같이 DBMS Driver 자체에 Load Balancing HA 기능을 사용한다.

Master DB Slave DB는 각각 쓰기와 읽기를 위해서 접근된다고 하지만 그렇다면 중간에 Staging DB의 역할은 무엇일까? Staging DB Slave DB로 복제하기 위한 중간 경유지 역할을 한다. 다수의 Slave DB로 복제를 해야 하기 때문에, 이에 대한 부하가 크다. 만약 Master DB에서 Slave DB로 바로 복제를 하게 되면, Master DB가 쓰기 트렌젝션 이외에 복제에 대한 부분을 처리해야 하기 때문에 성능 저하를 유발할 수 있다. 이를 방지 하기 위해서 중간에 Staging DB를 넣는 것이다.

그렇다면 Master à Staging à Slave DB로의 복제는 어떤 기술을 이용할까?

CDC (Change Data Capture)라는 기술을 이용한다. DBMS들은 공통적으로 Create/Update/Delete와 같은 쓰기 관련 작업을 수행할때, 데이타를 실제로 저장하기 전에 먼저 해당 작업에 대한 request BackLog라는 곳에 저장한다. (BackLog는 일반적으로 Local 파일이다.) 이는 실제로 데이타를 쓰기전에 장애가 났을때, restart하면서 이 BackLog를 읽어서 복구를 위한 용도로 사용이 된다. CDC는 이 Back Log를 이용해서 데이타를 복제하는데, Source DB로 부터 이 Back Log를 읽어서, 복제를 하고자하는 Target DB replay 하는 형식이다.

Source DB에서, insert A,B,C를 하면 이는 모두 Back Log에 기록되고, 이를 읽어서 Target DB에서 다시 replay – insert A,B,C를 순차적으로 수행하는 것이다.

대표적인 CDC 제품으로는 Oracle Golden Gate, Quest Share Flex가 있고, 오픈소스 제품으로는 Galera[1]라는 제품이 있다.


Sharding

Sharding은 데이타베이스의 용량 한계를 극복하기 위한 기술이다. 클러스터링 기술을 사용하더라도 데이타 베이스는 물리적인 용량 한계를 갖는 경우가 많다. 수년 전에만 해도, 하나의 서비스에 수천만명이 사용하는 서비스는 없었다. 인터넷의 발전등에 따라서 사용자가 급격하게 늘어나고 저장해야 하는 데이타의 양도 급격하게 늘어났다. 데이타 베이스 시스템은 이러한 용량 증가를 따라가지 못했다. 그래서 Sharding이라는 아키텍쳐가 유행하기 시작했는데, Sharding은 쉽게 말해서 데이타를 여러개의 데이타 베이스에 나눠 담는 방법이다.

하나의 데이타 베이스가 10억개의 레코드만 저장할 수 있다면, 100억개의 데이타를 저장하려면 10개의 데이타 베이스를 사용하여 분산 저장하는 방법이다. 이 각 10개의 데이타 베이스를 Shard라고 한다.

Sharding은 데이타를 분산 하는 방식에 따라서 Vertical(수직적) Sharding Horizontal(수평적) Sharding으로 나뉘어 진다.

Vertical Sharding은 연속된 데이타에 대해서 범위별로 데이타를 나누는 방법이다. 아래 예는 연령대 별로, 데이타를 나누는 예이다.



Figure 1 Vertical Sharding


다음으로는 Horizontal Sharding이 있는데, 이는 연속된 키가 아니라 “Category”와 같은 종류에 따라서 데이타를 수평적으로 분리하는 방법이다.



Figure 2 Horizontal Sharding


데이타를 분산 저장할때 위와 같이 meaningful한 데이타를 사용할 수 있는데, 이 경우에는 데이타의 몰림 현상이라는 것이 발생할 수 있다. 위의 Vertical Sharding을 예를 들어보면, 해당 서비스를 사용하는 연령층이 20대와 30대에 편중되어 있다면, 20,30 Shard에는 데이타가 몰리게 되고, 부하도 더 많이 받게 될 것이다. 그래서 이렇게 meaningful한 데이타를 KEY로 사용할 경우에는 데이타 몰림현상을 고려하여 각 Shard 서버의 성능을 비 대칭적으로 설계할 수 있다. 20,30대의 Shard에는 더 좋은 CPU와 메모리를 갖는 서버를 배치하는 방법이 대안이 된다.

만약에 데이타 편중 현상에 대한 예측이 어려운 경우에는, meaningful 하지 않은 KEY를 사용해서 Sharding을 할 수 있다. 10개의 Shard를 갖는 데이타 베이스에서, 사용자 레코드를 등록할때, KEY Sequence를 이용해서 순차적으로 부여한다. 첫번째 사용자는 1, 두번째는 2, 다음은 3,4,5,6.. 등으로.. 그리고 이 ID 10으로 나눈 나머지 값을 가지고 Shard를 결정하면, 데이타를 모든 Shard에 걸쳐서 골고루 분산 시켜 배치 할 수 있다. (Hash 방식)

Sharding을 구현하는 방법은 DBMS 단에서 Sharding을 지원 하는 방법과, OR Mapper와 같은 DB 접근용 프레임웍에서 Sharding을 제공하는 방법 그리고, Application Code 자체에서 지원하는 방법 세가지가 있다.

DBMS 단에서 제공하는 방법으로는 Microsoft SQL Server Azure federation model[2]이나 RDBMS는 아니지만 NoSQL MogoDB의 경우 1.6부터 Sharding DB단에서 지원한다.

프레임웍단에서는 자바의 Hibernate의 경우 Hibernate Shard[3]라는 기능을 통해서 Sharding을 지원한다프로그래밍 언어인 Grail의 경우에도 자체 프레임웍에서 “Grails Sharding Plug in”을 통한 Sharding 을 지원한다.

직접 Application에서 구현할 경우 Key에 따라서 DB Instance를 선택적으로 고를 수 있는 구조를 가져야 하며, 특히 다른 Shard간의 데이타 Join등은 불가능하기 때문에, 구현상에 이에 대한 고려가 필요하다.

Sharding 이라는 것이 데이타를 분산 저장함으로써 시스템의 전체 용량을 늘릴 수 는 있지만 Application의 복잡도가 올라가고, 데이타 편중 방지등 여러가지 요소를 고려한후에 설계, 반영해야 한다..

저작자 표시
신고

소셜 시스템이라고 이름은 지었는데,

정의는 Google,FaceBook,Twitter들과 같이 B2C 서비스를 지향하면서 대용량 서비스를 제공하는 웹서비스 시스템을 정의한다.

이 시스템들은 기존의 J2EE나 APM (Apache PHP MySQL) 다음 세대의 기술셋을 사용하는데

특징적으로

- 오픈 소스를 많이 이용한다.

- NoSQL을 이용한 대용량 데이타 저장 구조

- 분산 처리 아키텍쳐

등등이다.

 

J2EE 시대에 Servlet/JSP + EJB + JDBC가 필수 기술이었고

J2EE 다음 시대인 오픈 소스 과도기에는 Spring + IBatis (or Hibernate) + Struts 가 필수 기술이었다면

다음은 무엇일까?

 

나름 여러 문건이나 트랜드들을 조사해본 결과

1. NoSQL

- 대용량 데이타 처리에는 Cassandra나 HBase가 주류

- MongoDB는 NoSQL과 RDB의 중간적 특성으로 Rich Data Handling의 경우 사용

2. DataGrid

- memcached와 같은 메모리 캐쉬로 요즘은 Redis가 대세

3. MessageQ

- RabbitMQ

4. Distributed File System

- Gluster가 대세

- 경우에 따라서 SWIFT등의 Object Storage등 사용

5. 분산 처리 프레임웍

- Hadoop

정리해보자면, MongoDB,Cassandra,HBase,Redis,RabbitMQ,Gluster,Hadoop 정도 알면 소셜 시스템에 대한 기반 기술들은 어느정도 습득했다고 생각해도 되지 않을까? 물론 프로그래밍 언어와 미들웨어는 별도고.. ^^;

 

여기에 양념으로 AWS, ZooKeeper등은 알고 있어야 한다.

 

저작자 표시
신고

 

국내만 아니라, 해외에도 서비스 하는 플랫폼을 설계할때 고려해야 할 기술적인 사항을

인프라(하드웨어+데이타 센터) 관점과 소프트웨어 관점에서 간략하게 정리하였습니다

 


저작자 표시
신고

페이스북의 Photo 서비스 시스템 아키텍쳐
The Photos application is one of Facebook’s most popular features. Up to date, users have uploaded over 15 billion photos which makes Facebook the biggest photo sharing website. For each uploaded photo, Facebook generates and stores four images of different sizes, which translates to a total of 60 billion images and 1.5PB of storage. The current growth rate is 220 million new photos per week, which translates to 25TB of additional storage consumed weekly. At the peak there are 550,000 images served per second. These numbers pose a significant challenge for the Facebook photo storage infrastructure.

1.5 PB의 사진 저장. 2009년 아키텍쳐 문서

http://www.facebook.com/note.php?note_id=76191543919

얻은 아이디어
- 하드웨어 디자인시, 디스크에 Write Back 캐쉬 적용

하나의 이미지 파일이 올라오면 4개의 Resize된 thumnail을 유지.
하나의 PhotoKey에 대해서, 1,2,3,4 thumnail에 대한 reference를 저장
Photo Key + Thumnail reference 를 Index형태로 저장하고 메모리에 유지하여 성능 향상

물리 저장 파일과 (HeyStack Store File)
Index 파일을 나눠서 저장하는 데이타 구조 (HeyStack Index File)-Index 파일은 물리 저장 파일을 통해서 다시 Build가 가능
물리 파일을 먼저 기록한 다음, Index를 Async 방식으로 기록하는 방식. Index 파일은 위와 같은 이유로 Less Critical
Delete Operation은 Index Flag에 deleted로 표시 해놓고, 물리 저장 파일은 Compaction 단계에서 collection
Index는 모두 Memory에 Loading하는 방식을 사용

Summary
Write를 Append 방식으로 하고, search나 read 성능 향상을 위해서 index 파일을 별도 유지하고, index는 memory에 로딩되는 방식을 사용. Update나 Delete는 New file을 append하고, Compaction 단계에서 Copy&Scavange와 유사한 방식으로, deleted로 mark된 needle(file)을 제거하고 복사하는 형태를 사용함



 

저작자 표시
신고
개인 클라우드 서비스를 하다보니.. 고민할 것이 정말 많다.
특히 공유 부분에서 기존의 슈가싱크처럼 B2C 기반으로 공유된 대상이 READ만 가능한 형태로 갈것인가? MS 쉐어포인트 처럼, WRITE 가 가능한 형태로 갈것인가?
이건 서비스 특성에 대한 변화 부터 요구 한다. 슈가 싱크는 B2C 기반의 개인 클라우드 서비스, 쉐어포인트등은 SmartWorkPlace처럼 Share보다는 Collaboration에 대한 요구 사항이기 때문에 서비스의 사상 자체가 변한다.

또한 이러한 개인 클라우드 서비스는 클라우드에 대한 어마어마한 투자를 바탕으로 하고 있기 때문에 안정적인 수익 모델이 있지 않는 이상은 쉽게 대규모 서비스를 하기가 어렵다. 드롭박스야 이미 20% 유료화 전환율을 가지고 있고, 국내의 통신사나 포탈에서 제공하는 서비스는 부분 유료화 또는 상품에 대한 부가 서비스 개념을 가지고 있기 때문에 가능 하지만 단독적인 서비스 배포에는 ROI 관점에서 고민해야할 점이 많다.

더군다나 국내 정도야 끽해야. 100만 사용자 계정 정도면 충분한데, 이 서비스를 제대로 글로벌로 할려면 사용자 수 뿐만아니라 아키텍쳐 자체도 분산 대용량 아키텍쳐를 사용해야 하기 때문에 시스템 디자인 부터가 달라여 한다.

요즘 재미있는 서비스 모델중 하나가 Tonido,ZumoCast,FogoPlug 같이 집의 개인 PC등을 홈서버로 해서, 집의 PC를 클라우드로 사용하는 모델이 생기기 시작하는데, 이는 단순히 집의 PC를 클라우드로 사용하는 것 + 개인 퍼블릭 클라우드를 융합한 형태의 서비스도 제공한다. (포고플러그의 경우). 이 서비스 시나리오는 이동성이 중요한 데이타는 클라우드에 올리고, 클라우드에 Lock-in이 되거나 또는 안정성등에 대한 우려가 있는 데이타, 그리고 용량적으로 부담이 될때, 그런 데이타는 PC에 저장하는 시나리오가 된다.

이 시나리오는 사용자 입장에서는 클라우드 유료화 비용에 대한 부담을 덜고, 더 높은 컴퓨팅 파워를 제공하며, 클라우드에 대한 데이타 보안 이슈를 해결하며, 서비스 제공자 입장에서는 대규모 스토리지 서비스 투자를 최소화 하고, 사용자에게 합리적인 서비스 비용을 제공할 수 있다.

개인 클라우드도 가만히 보니까는 정말 쉴세 없이 변하고 있다. 그런데 한국은 어디에 서 있는 것일까?
뛰어난 생각을 가진 기획자와 개발자는 아무리 한국 IT 시장이 변하고 있다지만, 이런 사람들의 능력이 100%,110% 발휘되는 것이 아니라, 이런 생각을 설명하고 보고서 작성하느냐고 오늘도 아마 어디쯤에서 밤을 세면서.. 50%의 능력만 발휘하고 있을 것 같다...

저작자 표시
신고