[번역] Microsoft Azure – Azure Service Fabric, 그리고 마이크로서비스 아키텍처

원문: https://msdn.microsoft.com/en-us/magazine/mt595752.aspx

마이크로서비스는 요즘 꽤 인기있는 단어입니다. 이에 대한 많은 발표자료가 있지만 개발자들은 여전히 혼란스럽습니다. 우리가 자주 듣는 질문은 바로 이겁니다. – “이거 SOA나 DDD와 유사개념 아닌가요?”

네, 실제로 마이크로서비스에는 SOA와 DDD 경험을 한 개발자가 만든 기술이 많이 있습니다. 마이크로서비스가 “SOA를 제대로 구현한 것”이라고 생각해도 큰 무리는 아닙니다. 서비스 간의 자주성, Bounded-Context 패턴, Event-driven과 같은 개념은 SOA와 DDD에 뿌리를 두고 있습니다.

이 글에서는 마이크로서비스의 이론과 구현에 대해 다룰 것입니다. 마이크로서비스에 대한 짧은 소개를 한 후, Azure Service Fabric에 마이크로서비스를 만들고 배포하는 것을 실제 사례를 들며 말할 것입니다. 끝으로 우리는 이 플랫폼이 왜 마이크로서비스를 만드는데 최적인지 보여줄 것입니다.

이름이 암시하듯, 마이크로서비스 아키텍처는 작은 서비스들의 집합으로 서버 어플리케이션을 만드는 방법입니다. 각 서비스는 각자의 프로세스에서 돌고 HTTP나 웹소켓으로 통신합니다. 각 마이크로서비스는 특정 도메인이나 비즈니스에 따라 Bounded-Context 별로 나뉘고 자동화된 매커니즘을 통해 자치적으로 개발하고 독립적으로 배포할 수 있도록 구현합니다. 끝으로 각 서비스는 각자의 도메인 데이터 모델과 도메인 로직을 가지고 있어야 하며, 각자가 다른 데이터 스토리지 기술을 사용할 수 있어야 하고, 심지어 다른 프로그래밍 언어로 개발할 수도 있어야 합니다.

마이크로서비스의 예시로는 프로토콜 게이트웨이, 사용자 프로필, 쇼핑카트, 재고 처리, 구매 시스템, 결제 프로세스, 큐와 캐시도 포함됩니다.

왜 마이크로서비스일까요? 한 마디로 민첩함(agility)입니다. 오랜 세월동안 마이크로서비스는 대규모의 복잡하고 확장이 중요한 시스템에서 유지 보수하기에 탁월한 장점을 보였습니다. 여러 개의 서비스를 독립적으로 배포할 수 있도록 어플리케이션을 디자인하여 세밀하게 릴리즈 계획을 세우는 것이 가능했기 때문입니다.

또 다른 장점은, 마이크로서비스는 독립적으로 확장할 수 있다는 것입니다. 거대한 모노리식 어플리케이션 블럭을 가지고 한 번에 확장하는게 아니라, 특정 마이크로서비스만 수평적으로 확장할 수 있습니다. 이를 통해 더 많은 프로세싱 파워나 네트워크 대역폭을 필요로 할 때 어플리케이션 전체를 확장하는게 아니라 필요한 것만 확장할 수 있습니다.

잘게 나눈 마이크로서비스 어플리케이션 구조로 지속적인 통합과 배포가 가능합니다. 그러므로 어플리케이션에 새 기능도 더 빠르게 구현할 수 있습니다. 어플리케이션을 잘게 나누면 테스트와 실행도 보다 격리된 형태로 할 수 있고, 서비스 간 엄격한 관계는 유지한 채 독립적으로 마이크로서비스를 개선할 수 있습니다. 인터페이스나 계약 관계를 일부러 깨지 않는 이상 각각의 마이크로서비스를 어떻게 바꾸든 새 기능을 추가하든 다른 마이크로서비스에는 영향을 주지 않습니다.

그림 1에서 보듯, 마이크로서비스로 개발하는 것은 민첩한 변화와 빠른 반복을 위한 최고의 효율을 목표로 합니다. 대규모 어플리케이션에서 특정 부분만을 바꾸고 배포할 수 있기 때문입니다.

Microservices Approach Compared to Traditional Server Application Approach

그림 1 마이크로서비스와 전통적인 서버 어플리케이션의 접근 방법 비교

마이크로서비스마다의 데이터 자주성

중요한 규칙은, 각 마이크로서비스가 독립적인 배포가 가능하도록 고유의 자동화된 생명주기를 가지고 각자의 도메인 데이터와 로직을 가진다는 것입니다. 사실 이것은 온전한 하나의 어플리케이션이 자신의 로직과 데이터를 가지고 있다는 것과 다를 바 없습니다.

개념적인 모델로서의 도메인은 서브시스템이나 마이크로서비스와 다를 수 있습니다. 예를 들어, 기업용 고객관리(CRM) 어플리케이션에서 구매내역 서브시스템과 고객 지원 서브시스템은 고객 엔티티에서 각자가 필요한 속성과 데이터를 부르고 서로 다른 Bounded-Context에서 사용합니다.

원리상으로는, DDD의 개념에서 각 Bounded-Context는 패턴의 차이로 구분되는 서브시스템 또는 서비스이며 반드시 각자의 도메인모델(데이터와 로직)을 가지고 있다는 것과 유사합니다. DDD에서의 각각의 Bounded-Context를 별개의 마이크로서비스라고 생각할 수 있습니다.

한 편으로, 많은 어플리케이션에서 사용되는 전통적인 모놀리식 구현은 그림 2와 같이 중앙집중형 데이터베이스(대개의 경우 정규화된 SQL 데이터베이스)를 가지고 전체 어플리케이션 또는 전체 내부 서브시스템이 사용하고 있습니다. 이 방법은 내부적으로는 단순해보이고 엔티티를 재사용하는 관점에서는 서로 다른 서브시스템에 일관성을 줄 수도 있습니다. 하지만 실제로는 각 서브시스템이 주는 정보를 모두 담는 거대한 테이블을 만들게 되고 그 안에 대부분의 경우에는 불필요한 속성과 컬럼이 만들어집니다. 이건 마치 짧은 코스로 하이킹할 때와 하루 종일 운전할 때, 그리고 지리학을 공부할 때에 대해 단 하나의 지도만 사용하려는 것과 같습니다.

Data Sovereignty Comparison: Microservices vs. Monolithic

그림 2 마이크로서비스와 모놀리식 구조의 데이터 자주성 비교

Stateless와 Stateful 마이크로서비스란?

앞서 말했듯이, 각 마이크로서비스는 각자의 도메인 모델을 가지고 있습니다. stateless 마이크로서비스의 경우에는, 데이터베이스가 외부에 놓일 수 있어서 SQL 서버같은 관계형 데이터베이스나 MongoDB 같은 NoSQL, 또는 Azure DocumentDB 같은 것을 사용할 수 있습니다. 더 나아가 서비스 그 자체는 stateful할 수 있는데, 이 말은 데이터가 마이크로서비스에 속해있다는 것입니다. 이런 형태는 데이터가 같은 서버에 있는 것이 아니라 같은 마이크로서비스 프로세스에 있기 때문에 데이터를 인메모리와 하드디스크에 보존하고 다른 노드에 복제본을 둘 수도 있습니다.

Stateless 방식은 기존에 흔히 사용해왔던 패턴과 유사하기 때문에 stateful에 비해 구현하기 쉽고 충분히 쓸만했습니다. 그렇지만 stateless 마이크로서비스는 프로세스와 데이터 소스가 분리되어 있다보니 어느 정도 지연이 있다는 의미이기도 한데, 이런 부분에 성능을 향상시키기 위해서 캐시와 큐를 추가적으로 사용하고 그로 인한 유동적인 부분도 늘어납니다. 결과적으로 우리는 여러 층의 티어를 가진 매우 복잡한 구조를 만들어내곤 했습니다.

Stateful 마이크로서비스는, 그와는 반대로 데이터와 도메인 로직 간에 지연이 없기 때문에 더욱 발전된 시나리오도 생각할 수 있습니다. 대용량 데이터 처리, 게임 백엔드, 서비스화된 데이터베이스 등 낮은 지연시간을 원하는 시나리오라면 뭐든 stateful 서비스로 이득을 얻을 수 있습니다. 내부적으로 상태를 더 빠르게 액세스 할 수 있기 때문입니다.

단점: Stateful 서비스는 수평 확장(scale-out)하는데 어느 정도의 복잡성을 가집니다. 외부 데이터베이스를 사용하는 기능이 있다면 stateful 마이크로서비스가 확장됨에 따라 그 안에서의 데이터 복제본 관리, 파티셔닝 등의 문제에 대해 어떻게든 해결해야 합니다. 이 부분이 바로 Service Fabric을 도입할 때 가장 도움받을 수 있는 부분입니다. stateful 마이크로서비스에 대해 개발과 생명 주기를 단순화하기 때문입니다.

Service Fabric의 장점

마이크로서비스를 적용해서 얻는 장점에는 한계점도 있습니다. 분산컴퓨팅 환경과 복잡한 마이크로서비스 배포를 모두 손수 하려고 하면 관리가 매우 어렵습니다. Service Fabric을 이용하면 마이크로서비스를 만들고, 배포하고, 실행하고, 관리하는 것을 효율적이고 효과적으로 할 수 있습니다.

Service Fabric은 무엇인가요? 클라우드에서 확장성이 뛰어나면서도 신뢰성있는 어플리케이션을 만들고 쉽게 관리할 수 있게 하는 분산 시스템 플랫폼입니다. Service Fabric은 클라우드 어플리케이션을 개발하고 관리하는데 부딧히는 중요한 문제를 해결해줍니다. Service Fabric을 사용하면 개발자와 관리자가 복잡한 인프라 문제를 해결하느라 시간을 쏟지 않는대신 확장성, 신뢰성, 관리편의가 필수적인 요구사항들을 구현하는데 집중할 수 있습니다. Service Fabric은 엔터프라이즈급 서비스를 개발하고 관리하는데 적합한 마이크로소프트의 차세대 미들웨어 플랫폼이며 최고의 클라우드 스케일 서비스입니다.

Service Fabric은 범용적인 배포 환경입니다. .NET Framework, Node.js, Java, C++ 등 실행가능한 것이면 어떤 언어도 가능하며 MongoDB 런타임과 같은 데이터베이스도 가능합니다.

그러므로, Azure Service Fabric이 마이크로서비스 어플리케이션에만 국한되는 것은 아닙니다. web app이나 service와 같은 기존의 어플리케이션도 호스팅하고 배포할 수 있으며 이 때 확장성, 로드밸런싱, 빠른 배포 등의 장점을 얻을 수 있습니다. 다만 그림 3에서 보듯, Azure Service Fabric이 밑바닥부터 새로 만들어진 플랫폼이며 하이퍼스케일, 마이크로서비스 시스템에 더 알맞도록 디자인되었을 뿐입니다.

Microsoft Azure Service Fabric

그림 3 Microsoft Azure Service Fabric

Service Fabric의 장점은 아래와 같습니다:

  • Azure에서 실행할 뿐만 아니라, 온프레미스나 다른 클라우드에서도 실행 가능. Service Fabric의 매우 중요한 특징은 Azure나 온프레미스 뿐만 아니라, 소유 중인 베어메탈 서버나 가상머신, 심지어 다른 서드파티 호스팅 클라우드에서도 구동할 수 있다는 것입니다. 특정 클라우드에 락-인(lock-in)하지 않습니다. Amazon Web Services (AWS)에서도 동작합니다.
  • 윈도우 및 리눅스 지원. 현재(2015년 말) Service Fabric은 윈도우만 지원하지만, 리눅스와 컨테이너(윈도우 이미지와 Docker 이미지) 를 지원할 예정입니다.
  • 완벽한 검증. Service Fabric은 수 년전부터 마이크로소프트의 여러 클라우드 제품에서 사용해왔습니다.

Service Fabric은 마이크로소프트 내부의 대형 서비스를 개발하기 위해서 만들어졌습니다. SQL Server 같은 제품을 클라우드에서 구동하는 서비스(Azure SQL Database)로 만들면서도 신속하고 신뢰성 있고 확장성있으면서도 비용을 절약할 수 있는 분산 기술이 필요했습니다. 이러한 복잡한 문제를 해결하기 위한 핵심 기술을 만드는 동안, 이와 같은 변화가 필요한 제품이 SQL Server 만이 아니라는 것을 알게 되었습니다. 예를 들어, Skype for Business가 마이크로서비스 기반 어플리케이션으로 발전해야 하는 과정에서도 비슷한 문제가 있었습니다. Service Fabric은 이런 문제들을 해결하면서 발전했고 마이크로소프트의 다양한 아키텍처와 요구사항이 있는 대형 서비스들에 사용해온 어플리케이션 플랫폼입니다. InTune, DocumentDB, Cortana의 백엔드, Skype for Business 모두 Service Fabric에서 동작 중입니다.

이런 중요한 시스템에 적용해온 경험을 통해 마이크로소프트는 인프라 자원과 확장성있는 어플리케이션의 요구사항을 제대로 이해하는 플랫폼을 디자인했습니다. 이 플랫폼의 자동 업데이트, 자가 치유(self-healing) 기능은 고가용성과 안정성을 보장하면서 확장성을 갖추게 합니다. 마이크로소프트는 이제 이 강력한 무기를 모두가 사용할 수 있게 만들었습니다.

Azure Service Fabric 프로그래밍 모델

Service Fabric은 서비스를 만드는데 두 개의 고수준 프레임워크를 제공합니다: Reliable Services API와 Reliable Actors API 입니다. 두 개 모두 동일한 Service Fabric 코어 위에 만들어졌지만, 그림 4에서 보는 바와 같이 동시성, 파티셔닝, 통신에 관하여 단순함과 유연성 사이에서 서로 다른 균형점을 가지고 있습니다.  만들고자 하는 서비스가 어떤 프레임워크에 더 맞는지 선택하려면 두 모델이 어떻게 동작하는지를 파악하는 것이 좋습니다. 많은 어플리케이션 시나리오에서 하이브리드 형태로 둘 모두를 사용할 수 있습니다. 일부 마이크로서비스는 Actor를 사용하면서 그 Actor에서 보내는 데이터를 Reliable Service가 취합하는 용도로 사용할 수도 있습니다. 그 외에도, reliable service는 actor service를 조율하는 형태로 많이 사용됩니다.

그림 4 Service Fabric 프로그래밍 모델 비교

Reliable Actor API Reliable Services API
서비스 시나리오에서 상태와 논리를 여러개의 독립적인 단위/오브젝트로 구성할 수 있습니다. (라이브 IoT 오브젝트 또는 게임 백엔드가 좋은 예) 서비스 시나리오에서 여러 엔티티 타입과 콤포넌트 간에 로직과 큐를 유지해야 합니다.
확장성과 일관성을 가진 많은 수의 싱글스레드 오브젝트를 사용해야 하는 경우. Reliable 컬렉션(.NET Reliable Dictionary, Reliable Queue)을 사용하여 상태와 엔티티를 저장해야 하는 경우.
동시성과 입자성을 프레임워크에서 관리하기를 원하는 경우. 상태의 동시성과 입자성을 직접 제어하고 싶은 경우.
통신은 Service Fabric이 알아서 관리하도록 하고 싶은 경우. 통신 프로토콜을 직접 선택하고 관리하고 구현하고 싶은 경우. (Web API, WebSockets, Windows Communication Foundation 등)
Service Fabric가 stateful 액터 서비스의 파티셔닝을 알아서 하고 개발시에는 통합된 모습으로 다루고 싶은 경우. Stateful 서비스의 파티션 규칙까지 직접 제어하고 싶은 경우.

 

Azure Service Fabric에서 Stateless 마이크로서비스 만들기

Azure Service Fabric의 마이크로서비스는 .NET Framework, Node.js, Java, C++를 가리지 않고 어떤 형태의 프로세스든 서버에서 실행할 수 있습니다. 현재 Service Fabric API는 .NET과 C++ 라이브러리만 제공하고 있으므로 마이크로서비스의 저수준 구현은 .NET Framework와 C++로 해야 합니다.

앞서 말했듯이, stateless 마이크로서비스는 말그대로 상태(state)를 전혀 보존하지 않는 서비스이거나 프로세스(프론트엔드 서비스나 비즈니스 로직 서비스)가 종료됨에 따라 상태도 읽게 되는 것입니다. 그러므로 동기화, 복제, 보존, 고가용성 모두 불필요합니다. 외부에 관계를 만들어서 상태를 가질 수 있습니다. 이 경우 보통 외부 스토리지로 Azure SQL 데이터베이스, Azure 저장소, Azure DocumentDB, 서드파티 스토리지 엔진(관계형 또는 NoSQL) 등을 사용합니다. 이런 이유로, 클라우드에서 운영 중인 ASP.NET Web API, Worker Role, Azure 웹앱 같은 것은 쉽게 Service Fabric stateless 서비스로 마이그레이션할 수 있습니다.

개발 환경을 셋업하려면 Service Fabric SDK를 설치해야 합니다. Service Fabric SDK로 개발용 로컬 클러스터를 만들 수 있는데 이것은 에뮬레이터가 아니고 Azure에 동작하는 것과 완벽히 동일합니다. 또한, 비주얼스튜디오 2015와 연동하여 개발과 디버깅을 더 쉽게 할 수 있습니다.

로컬에서 서비스를 배포하고 디버깅하기 위해서는 노드의 묶음인 클러스터를 만들어야 합니다. 파워셸을 열어서 DevClusterSetup.ps1 스크립트를 실행하면 됩니다. 문서를 보려면 bit.ly/1Mfi0LB 링크에서 “Install and Start a Local Cluster” 섹션에 “Prepare Your Development Environment”를 참고하세요. 링크로 소개한 문서를 보면 설치 절차에 대해 하나씩 따라할 수 있습니다.

Stateless Service Base Class: Service Fabric의 모든 Stateless 서비스는 Microsoft.ServiceFabric.Services.StatelessService에서 상속받아야 합니다. 이 클래스는 API 메소드, Service Fabric 클러스터에 대한 콘텍스트와 실행 환경을 제공하고 여러분이 만드는 서비스의 라이프사이클에 관여합니다.

여러분이 만드는 서비스가 stateful이든 stateless든 상관없이 Reliable Service는 개발 코드에 간단히 추가만 하면 시작하는 단순한 라이프사이클을 제공합니다. Service Fabric 서비스를 실행하는데 한두 개의 메소드만 구현하면 됩니다. 보통은 RunAsync와 CreateServiceReplicaListeners 만 하는데, 이에 대해서는 통신 프로토콜을 다룰 때 자세히 설명하겠습니다.

그림 5에 RunAsync 메소드가 보입니다. 이 부분에서 여러분이 구현하는 서비스가 백그라운드로 동작합니다. cancellation token이 있어서 메소드가 반드시 멈춰야 하는 신호를 받을 수 있도록 했습니다.

그림 5 Azure Service Fabric에서 Stateless 서비스의 기본 구조
using Microsoft.ServiceFabric.Services;

namespace MyApp.MyStatelessService
{
public class MyStatelessService : StatelessService
{
//... Service implementation
protected override async Task RunAsync(CancellationToken cancellationToken)
{
int iterations = 0;
while (!cancellationToken.IsCancellationRequested)
{
ServiceEventSource.Current.ServiceMessage(this, "Working-{0}",
iterations++);
// Place to write your own background logic.
// Example: Logic doing any polling task.
// Logic here would be similar to what you usually implement in
// Azure Worker Roles, Azure WebJobs or a traditional Windows Service.
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
}
}

Azure Service Fabric Reliable Service의 통신 프로토콜: Azure Service Fabric Reliable Service는 플러그 가능한(pluggable) 커뮤니케이션 모델입니다. HTTP를 통한 ASP.NET WebAPI, 웹소켓, WCF(Windows Communication Foundation), 직접 만드는 TCP 프로토콜 등을 골라서 사용할 수 있습니다.

선택한 통신 프로토콜을 직접 구현할 수도 있고 Service Fabric이 제공하는 통신 스택을 사용할 수도 있습니다. 그 중 ServiceCommunicationListener/ServiceProxy는 Reliable Services 프레임워크에서 기본적으로 제공하는 통신 스택입니다. 그 외에 Service Fabric에 플러그-인 할 수 있는 별도의 통신 스택도 NuGet 패키지에서 설치할 수 있습니다.

Service Fabric에서 ASP.NET Web API를 마이크로서비스로 사용하기: 앞서 말했듯이, Service Fabric에서는 서비스가 어떻게 통신하는지를 선택할 수 있습니다. 가장 흔히 사용되는 것은 .NET 프레임워크에서 ASP.NET WebApi를 이용한 HTTP 서비스입니다.

Service Fabric의 Web API는 여러분이 이미 아는 ASP.NET Web API와 같습니다. Controller, HttpRoutes, MapHttpRoute나 메소드에 속성(attribute)을 적용하여 REST 서비스를 구성하는 것 모두 그대로 사용할 수 있습니다. Service Fabric에서 다른 점은 Web API 어플리케이션을 호스팅하는 방법입니다. 우선, IIS는 Service Fabric에서 사용할 수 없습니다. 왜냐하면, 단순한 실행 프로세스를 클러스터에 복제하는 형태이기 때문입니다. 그러므로 코드에 HTTP 리스너를 추가하고 self-host로 구현해야 합니다. 이 방식대로 Web API 서비스를 구현하려면 두 가지 선택지가 있습니다.

  • ASP.NET 5 Web API (코드네임 “Project K”, 번역시점에는 ASP.NET Core로 명명)를 사용하고 HTTP 리스너를 Service Fabric 마이크로서비스 프로세스에 추가하여 self-host 하는 방법.
  • OWIN/Katana와 ASP.NET 4.x Web API를 사용하고  마찬가지로 Service Fabric 마이크로서비스 프로세스에 추가하여 self-host 하는 방법.

ASP.NET 5는 Azure Service Fabric에서 마이크로서비스를 구성하기에 가장 알맞습니다. 왜냐하면, 앞서 말했듯이 Service Fabric에서는 여러 개의 서비스를 각 노드/VM에 배포할 수 있고 클러스터마다마이크로서비스를 빽빽하게 구성할 수도 있기 때문입니다. 이렇게 고밀도로 구성할 때 Service Fabric은 진가를 발휘합니다. ASP.NET 5에서 .NET Core 5와 CoreCLR 지원은 향후 지원될 예정이지만, 최선의 선택이라는 점은 맞습니다. .NET Core 5는 경량 프레임워크로 .NET 4.x 프레임워크보다 메모리를 적게 차지하기 때문에 마이크로서비스를 매우 밀도 높게 구성할 수 있기 때문입니다.

MVC 형태의 웹앱을 사용한 Service Fabric 마이크로서비스: ASP.NET MVC는 통상의 웹사이트 개발에 흔히 사용되는 프레임워크입니다. 하지만 Service Fabric에서는 MVC 5 또는 그 이하의 기존 MVC 프레임워크를 사용할 수 없습니다. 왜냐하면, Service Fabric은 self-host로 동작해야 하고 HTTP 리스너를 프로세스에 추가해야 하는데 OWIN/Katana는 ASP.NET 4.x MVC를 지원하지 않고 ASP.NET 4.x Web API만 지원하기 때문입니다. 그러므로, MVC 형태의 웹 어플리케이션을 Service Fabric에서 구현하려면 다음과 같이 구현해야 합니다.

  • ASP.NET 5 MVC를 사용하여 Service Fabric 마이크로서비스 프로세스에 HTTP 리스너를 self-host 합니다. ASP.NET 5, Web API, MVC가 모두 하나의 프레임워크에 통합되었기 때문에 IIS에 의존하지 않는 동일한 콘트롤러 매커니즘을 사용하기 때문입니다.
  • OWIN/Kanata의 ASP.NET 4.x Web API를 사용하여 Service Fabric 마이크로서비스에 HTTP 리스너를 self-host합니다. MVC 콘트롤러를 Web API 콘트롤러로 처리하는 방법으로, Web API가 JSON/XML을 출력하지 않고 HTML/JavaScript를 출력하도록 하는 것입니다. 구현 방법은 다음 문서[bit.ly/1UMdKIf]에 나와있습니다.

ASP.NET 4.x Web API와 OWIN/Katana를 이용하여 Self-Hosting 구현하기

이 방법은 여러분이 만든 Web API 어플리케이션에서 바뀌는 것이 없습니다. 기존에 작성했던 코드와 다른 것이 전혀 없고 거의 모든 코드를 곧바로 옮길 수 있습니다. 다만 IIS에서 호스팅했다면 호스팅하는 부분은 조금 다를 수 있습니다.

Service Class의 CreateServiceReplicaListeners 메소드: 이 메소드에서 서비스가 사용할 커뮤니케이션 스택을 정의합니다. 커뮤니케이션 스택은 서비스의 끝점(endpoint)을 정의하고 메시지가 다른 서비스 코드와 어떻게 연계되는지를 정하는 것으로 Web API도 사용할 수 있습니다.

서비스 클래스에 대해 언급했던 그림 4 이전의 내용을 다시 보면, CreateServiceReplica­Listeners는 내가 사용할 최소 하나 이상의 커뮤니케이션 스택입니다. 이번 예시에는 그림 6과 같이 Web API를 사용하는 OwinCommunicationListener 를 사용했습니다.

그림 6. CreateServiceReplicaListeners 메소드를 Stateless Service 클래스에 추가하기

using Microsoft.ServiceFabric.Services;

namespace MyApp.MyStatelessService
{
public class MyStatelessService : StatelessService
{
//... Service implementation.
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// Place to write your own background logic here.
// ...
}
protected override IEnumerable
CreateServiceReplicaListeners()
{
return new List()
{
new ServiceReplicaListener(parameters =>
new OwinCommunicationListener(
"api", new Startup()))
};
}
}
}

한 서비스에 여러개의 communication listeners 추가가 가능하다는 것을 꼭 기억해주세요.

OwinCommunicationListener 클래스는 모든 마이크로서비스에서 재사용할 수 있습니다. 마찬가지로,  WCF, WebSockets 등 다른 프로토콜도 플러그인 개념으로 사용할 수 있습니다.

Web API 마이크로서비스를 만든다면 그에 대한 콘트롤러를 만들 것이고 이는 아래 코드와 같이 Web API 서비스를 만드는 것과 전혀 차이가 없습니다:

// Typical Web API Service class implementation
public class CustomerController : ApiController
{
//// Example - GET /customers/MSFT
[HttpGet]
[Route("customers/{customerKey}", Name = "GetCustomer")]
public async Task GetCustomer(string customerKey)
{
// ... Stateless method implementation.
}
}

이 문서는 Azure Service Fabric과 마이크로서비스에 대한 전반적인 내용을 다루는 것이므로 Service Fabric 마이크로서비스에서 OWIN을 구현하는 방법은 깊게 다루지 않겠습니다. 이에 대한 예시는 GitHub(bit.ly/1OW5Bmj)에 Web API and Service Fabric HelloWorld Example (ASP.NET 4.x) 예시를 참고하시기 바랍니다.

Self-Hosting ASP.NET 5 Web API 구현하기

가장 추천하는 것은 ASP.NET 5 (ASP.NET Core)를 Azure Service Fabric의 마이크로서비스로 구현하는 것입니다. 왜냐하면, Service Fabric은 다수의 서비스를 노드/VM에 배포할 수 있고 클러스터 별로 고밀도의 마이크로서비스를 수용할 수 있기 때문입니다. ASP.NET 5에는 다음과 같은 훌륭한 기능이 있습니다.

  • 유연한 호스팅: IIS 뿐만 아니라 개별 프로세스로 ASP.NET 5 어플리케이션을 호스팅할 수 있습니다.
  • Web API, MVC, Web Pages: 이 모두를 하나로 통합해서 보다 단순한 개념을 가집니다.
  • 완전한 side-by-side 실행: ASP.NET 5 어플리케이션은 한 머신에 다른 어플리케이션에 영향을 주지 않습니다. 심지어 다른 버전의 다른 .NET Framework 버전을 사용할 경우에도 문제 없습니다. 이는 IT 관리에 있어서 큰 이점을 줍니다.
  • 크로스플랫폼: ASP.NET 5는 Windows, Max OS X, Linux에서 동작합니다.
  • 클라우드: 진단, 세션 상태, 캐시, 설정과 같은 기능이 로컬에서 동작하는 것의 수정없이 클라우드에서도 동작합니다.

좀 더 미래지향적으로 생각해보면, Service Fabric이 .NET Core 5와 CoreCLR을 지원하게 되면 고밀도를 염두하는 것이 훨씬 좋습니다. 아직은 .NET Core와 CoreCLR을 지원하기까지는 작업이 남아있지만 지원할 때의 그 장점은 명확합니다. ASP.NET 5를 .NET Core 5에서 구동하는 것은 경량의 .NET Framework이며, CoreCLR 런타임은 기존의 .NET 4.x보다 적은 메모리를 사용합니다. 이 조합은 그림 7의 “Microservice B”의 경우로, 마이크로서비스의 밀도를 매우 높일 수 있습니다.

Comparing ASP.NET 5 Running on the CLR vs. CoreCLR Within the Service Fabric Context
그림 7 ASP.NET 5가 Service Fabric Context 내에서 CLR과 CoreCLR의 동작 비교.

.NET Core 5와 CoreCLR 지원은 향후 지원될 예정입니다. 현재 엔터프라이즈 환경에서는 Service Fabric에서 .NET 4.x 기반의 ASP.NET 5 개발을 선호합니다. 이렇게 하면 향후 CoreCLR로의 마이그레이션이 수월합니다.

하이퍼스케일 마이크로서비스의 운영과 배포

운영과 배포는 Service Fabric이 하이퍼스케일 마이크로서비스를 구축하는데 왜 좋은지를 보여주는 강점 중 하나입니다. 여러분은 마이크로서비스 개발에만 집중하도록 Service Fabric이 복잡한 것들을 도맡아서 해줍니다.

고밀도의 마이크로서비스는 인프라 비용을 낮추는데 큰 도움이 됩니다. Service Fabric 클러스터는 많은 VM/서버(향후에는 컨테이너)의 풀로 구성되어 있으며 이를 노드라고 부릅니다. 각 노드마다 Service Fabric은 자동으로 수 개의 서비스를 배포합니다. 그러므로 각 서버/VM의 성능에 따라 초고밀도로 마이크로서비스를 클러스터에서 구성할 수 있습니다.

Azure Service Fabric에서는 수백개의 서비스 인스턴스를 각 머신이나 VM에서 실행할 수 있습니다. 이로써 총소유비용(TCO; Total Cost of Ownership)을 대폭 절약할 수 있습니다. 같은 양의 서비스라도 더 적은 하드웨어를 필요로 합니다. 그러므로 Azure Service Fabric의 고밀도는 Azure Cloud Service가 서비스마다 하나의 VM을 필요로 하는 것에 비하면 중요한 차별점입니다. 만약 마이크로서비스를 Web Role 또는 Worker Role에 배포한다면 단일 마이크로서비스에 너무 많은 리소스를 할당하게 될 것입니다. 예를 들어, 그림 8은 서비스 인스턴스 별 하나의 VM이 할당된 모습입니다 (색상은 서비스의 유형을 표시했으며, 각 도형은 서비스 인스턴스를, 박스 모양은 VM을 표현했습니다). 이 방법은 마이크로서비스를 고밀도로 구성하기에는 별 도움이 안되고 작은 VM 여러개를 사용하게 됩니다. 또한, Azure Service Fabric에 배포하는 것과 동등한 수준의 고밀도를 달성할 수는 없습니다.

Services Density Comparison—Azure Cloud Services vs. Service Fabric
그림 8 서비스 밀도 비교—Azure Cloud Services vs. Service Fabric

반면, Service Fabric에서는 노드마다 여러 개의 마이크로서비스를 배포할 수 있습니다. 그러므로 서비스의 밀도 면에서 훨씬 효율적이며 비용 또한 낮아집니다. 클러스터마다 수십, 수백, 수천 개의 VM/서버를 만들 수 있고 또 수십, 수백, 수천 개의 마이크로서비스 인스턴스와 복제본을 노드/VM마다 만들 수 있습니다. 그리고 각 마이크로서비스는 단순히 프로세스일 뿐이므로, 배포와 확장은 서비스마다 새로 VM을 만드는 Azure Cloud Service보다 훨씬 빠릅니다.

Azure 클라우드에 클러스터 만들기: 마이크로서비스를 배포하기 전에 Azure 또는 온프레미스에 노드들의 집합인 클러스터를 만들어야 합니다. Azure 클라우드에 클러스터를 만들면 Azure 포털에서 바로 작업을 할 수도 있고 Azure Resource Manager (ARM)을 사용할 수도 있습니다. 그림 9에서, 우리가 Azure 구독에서 Azure 포털로 만든 Service Fabric 클러스터를 볼 수 있습니다. 내부적으로 클러스터 생성 과정은 Azure ARM과 Azure Resource Group을 기반으로 이루어집니다. 이번에 우리는 5개의 노드/VM로 이루어진 클러스터를 Azure Resource Group에 만들었습니다.

Service Fabric Cluster in the Azure Portal
그림 9 Azure 포털에서의 Service Fabric Cluster

클러스터에 어플리케이션/서비스 배포하기: 노드/VM이 실행되면 서비스를 배포할 수 있습니다. Azure에 있는 프로덕션 클러스터에 배포할 때는 보통 Windows 파워셸 스크립트를 사용하여 배포하곤 합니다. 스테이징/테스트 환경에 배포할 때는 비주얼스튜디오에서 바로 배포할 수 있습니다.

비주얼스튜디오 2015로 개발 PC에 있는 로컬 클러스터에 배포할 때는, IDE에 Service Fabric 어플리케이션 프로젝트를 오른쪽 클릭하여 Deploy 버튼을 클릭합니다. 물론 Windows 파워셸을 이용해서 여러분의 랩톱에 있는 개발용 클러스터에 배포할 수도 있습니다. Azure 클라우드에 있는 클러스터든 개발용 클러스터든 모두 동일한 Service Fabric 바이너리로 동작하기 때문입니다.

장기적으로 서비스를 운영할 때, 매일매일 배포에 대한 운영과 관리를 하는 것이 서비스를 부드럽게 발전하는데에 필수적입니다. Service Fabric에는 어플리케이션 라이프사이클 관리(ALM; Application Lifecycle Management) 기능도 있기 때문에 마이크로서비스 접근법을 염두하면서 해낼 수 있습니다. Service Fabric에 있는 운영과 관리 기능은 빠른 배포, 무중단(zero-downtime) 업그레이드, 서비스 헬스 모니터링, 클러스터의 확장/축소 등을 할 수 있습니다. Service Fabric의 무중단 업그레이드 기능은 순차적인 업그레이드 진행과 자동 헬스 체크 기능의 조합이며, 업그레이드 도중 어플리케이션이 불안정할 때는 롤백을 합니다. Service Fabric의 클러스터와 어플리케이션 관리는 Windows 파워셸 명령어로 할 수 있으며, CLI(Command Line Interface)와 스크립팅 모두 가능합니다. 그 외에 비주얼스튜디오에서 GUI 도구도 지원하므로 쉽게 관리할 수 있습니다.

업그레이드는 단계별로 진행됩니다. 각 단계마다 클러스터 내에 노드의 부분집합 별로 업그레이드가 적용되는데, 이를 업그레이드 도메인이라고 합니다. 그래서 업그레이드를 하는 동안에도 어플리케이션은 사용 가능한 상태로 있습니다. 버전을 붙일 수 있기 때문에 같은 마이크로서비스의 버전1과 버전2가 동시에 배포된 상태에서 요청에 따라 각각에 리다이렉트할 수도 있습니다. 더 자세한 정보는 문서(bit.ly/1kSupz8)를 참고해주세요.

PC에 설치된 개발용 로컬 클러스터에서 디버깅을 할 때 비주얼스튜디오는 서비스 프로세스가 이미 실행중이어도 바로 디버깅을 할 수 있도록 해주기 때문에 편합니다. IDE가 자동으로 프로젝트와 관련된 모든 마이크로서비스 프로세스에 붙여주고 평소처럼 중단점(breakpoint)을 이용하면서 Service Fabric의 마이크로서비스 코드를 디버깅할 수 있습니다. 그저 중단점을 찍고 F5만 누르면 끝입니다. Visual Studio에서 어떤 프로세스에 디버거를 붙일 것인지 찾을 필요도 없습니다.

Service Fabric Explorer: Service Fabric Explorer(탐색기)는 그림 10과 같은 모습입니다. 웹 기반 도구로 클러스터에 배포된 어플리케이션의 상태와 각각의 노드를 볼 수 있고, 다양한 관리를 할 수 있습니다. 탐색기 도구는 Service Fabric REST API가 제공하는 것과 동일한 HTTP Gateway 서비스를 이용하고 있습니다. 접속하려면 http://<your-cluster-endpoint&gt;:19007/Explorer 형식의 주소를 이용하면 됩니다. 로컬 클러스터의 경우, URL은 다음과 같을 것입니다. http://localhost:19007/Explorer.

The Service Fabric Explorer
그림 10 Service Fabric Explorer

Service Fabric Explorer에 대한 더 자세한 내용은, 다음 문서(bit.ly/1MUNyad)에 “Visualizing Your Cluster Using Service Fabric Explorer”를 참고해주시기 바랍니다.Service Fabric 플랫폼은 복잡한 시스템 건강 관리나 업그레이드에 시간을 쓰지 않고 어플리케이션 개발에 최대한 집중할 수 있도록 다양한 기능을 제공합니다. 그 기능은 다음과 같습니다:Stateless Services 수평확장(scale-out): Service Fabric 오케스트레이션 엔진은 새 노드가 클러스터에 추가되면 자동으로 웹앱을 더 많은 머신으로 확장합니다. stateless 서비스를 만들 때 몇 개의 인스턴스를 만들고 싶은지 설정할 수 있습니다. Service Fabric은 한 클러스터의 노드들에 그만큼의 인스턴스를 배치하고 각 노드에는 하나 이상의 인스턴스는 만들지 않도록 합니다. 모든 노드에 하나의 인스턴스를 생성하고 싶다면 인스턴스 숫자를 “-1″로 지정하면 됩니다. 이렇게 하면 클러스터를 수평확장할 때 stateless 서비스의 인스턴스 또한 새로운 노드에 생성될 것입니다.

자동 자원 분배: Service Fabric은 클러스터 내에 총 사용 가능한 자원을 파악하여 자원 밸런싱(또는 자원 오케스트레이션)을 합니다. 그림 11에서 보는 바와 같이, 정의된 정책과 제약사항 하에 생성된 마이크로서비스를 자동으로 VM간에 이동시키고 최적화합니다. 이런 작업은 비용 대비 성능에 초점이 맞추어져 있습니다.

Cluster Showing Services Distribution Across the Nodes and Automatic Resource Balancing
그림 11 클러스터 내에 노드 간 자동 자원 분배를 통한 서비스 분산

 

내장된 오류 해결과 복제 기능: 데이터센터 내의 머신이나 퍼블릭 클라우드는 예기치 못한 하드웨어 장애를 겪곤 합니다. 이런 경우를 방지하기 위해 Service Fabric은 내장된 오류 해결과 복제 기능을 제공합니다. 다시 말해, 하드웨어에 문제가 있더라도 서비스의 사용성은 영향을 주지 않습니다. Service Fabric에서는 각 서비스가 여러 인스턴스와 여러 실패영역(failure domain)에서 실행되기 때문입니다.

배치 제한(Placement Constraints): 클러스터에 프론트엔드 마이크로서비스는 미들티어 마이크로서비스와 같은 머신/노드에 배치하지 않도록 하거나, 특정 타입의 마이크로서비스는 같은 노드에 배치하지 않도록 할 수 있습니다. 이렇게 하면 충돌상황을 최소화할 수 있고 자원에 대한 특정 마이크로서비스에 자원 액세스에 대한 우선권을 줄 수도 있습니다.

요청에 대한 부하 분배: 자동 자원 분배와는 다른 의미로, 요청에 대한 부하 분산은 Service Fabric에서 처리하지 않습니다. 이들은 Azure 부하 분산 장치(Azure Load Balancer) 또는 Service Fabric 외부의 플랫폼에서 처리합니다.

헬스: 시스템을 모니터링하고 진단할 수 있습니다. 또한, 어플리케이션을 업그레이드할 때 안정성을 확인할 때 사용되며, 업그레이드가 불안정할 때 롤백할 수 있도록 합니다. 더 많은 정보는 다음 문서(bit.ly/1jSvmHB)에 “Introduction to Service Fabric Health Monitoring”을 참고해주세요.

Azure Service Fabric의 Stateful Microservices

Stateful 서비스는 Azure Service Fabric에서 흥미로우면서도 중요한 요소입니다. Stateful 서비스에 대해서 자세히 알아보기에는 다소 복잡하고 광범위하기 때문에 이 글의 주제를 벗어나므로 간단히 설명하도록 하겠습니다. 곧 나올 Service Fabric에 대한 문서가 있으니 거기서 중점적으로 다루도록 하겠습니다.

Service Fabric의 Stateful 마이크로서비스는 마이크로서비스 자체적으로 계산 능력과 데이터(인-메모리와 로컬디스크 모두) 뿐만 아니라 상태까지 함께 가지고 있습니다. 상태의 신뢰성은 데이터의 로컬본과 다른 노드/VM의 복제본을 통해 얻습니다. 기본적으로 각 데이터 파티션은 복제본 서비스와 연결되어 있습니다. 각 복제본은 고가용성을 위해 다른 노드/VM에 배포됩니다. 이건 마치 Azure SQL 데이터베이스가 복제본을 관리하는 형태와 유사한데, 그 이유는 다름이 아니라 Azure SQL도 Azure Service Fabric에 기반으로 개발했기 때문입니다.

복잡하고 확장성을 염두한 어플리케이션이라면 stateful 서비스는 기존의 전통적인 3-tier 아키텍처에 외부 캐시와 큐를 사용하는 것보다 구조를 단순화할 수 있고 콤포넌트 수도 줄일 수 있습니다. stateful 서비스를 사용하면 외부 캐시를 사용하던 것을 stateful 서비스 내부에 둘 수 있기 때문입니다. 그 외에, Service Fabric 마이크로서비스 안에 큐를 구현할 수 있으므로 외부에 큐도 필요하지 않습니다.

Stateful 서비스를 사용하면 자동으로 2차(secondary) 핫백업을 생성합니다. 이것은 1차(primary) 마이크로서비스에 하드웨어 장애가 있을 때 동일한 지점을 받아서 작업합니다. 서비스는 사용자 증가에 대응하여 꾸준히 확장해야 하는데, 이는 곧 하드웨어의 증설을 필요로 합니다. Service Fabric은 파티셔닝과 같은 기능을 통해 개발자가 개입할 필요 없이 자동으로 새 하드웨어에 서비스를 배치합니다.

Stateful Reliable Service는 데이터 파티셔닝을 지원하며, 복제본과 리더 선출 기능도 지원합니다. 또한, 복제본 서비스의 네이밍, 게이트웨이 서비스를 이용한 주소 디스커버리도 지원합니다. 트랜젝션을 통한 state 변화에 대한 동시성(concurrency)과 입자성(granularity)을 관리하며, 상태의 복제를 통한 상태 유지, 신뢰성있고 분산처리된 key/value 컬렉션(Reliable Dictionary and Reliable Queue)을 제공합니다. 좋은 점은, Service Fabric은 이 모든 것에 대한 세부적인 것과 복잡한 것을 처리해주므로 여러분은 어플리케이션 개발에 최대한 집중할 수 있다는 것입니다.

Azure Service Fabric의 Reliable Actors Services

Azure Service Fabric Reliable Actor는 Service Fabric의 액터 프로그래밍 모델입니다. 비동기적이며, 싱글스레드인 액터 모델입니다. 액터는 상태와 계산의 한 단위를 표현합니다. 이것은 Microsoft Research에서 만들고 있는 오픈소스 프로젝트 “Orleans”와 유사한 점이 있습니다. 중요한 점은, Service Fabric Reliable Actors API의 인프라는 Service Fabric이 제공한다는 것입니다.

IoT(Internet of Things)의 액터 인스턴스를 구현하는 것이 액터 서비스 사용에 대한 좋은 예시입니다. Vehicle이라는 타입의 액터를 C# 클래스로 구현해서 ‘자동차’ 도메인 로직을 캡슐화하고 여기에 GPS 좌표와 몇몇 데이터를 상태 정보로 구현합니다. 그러면 우리는 수백만개의 액터 오브젝트, 다시 말해 수백만개의 vehicle 클래스의 인스턴스를 클러스터의 여러개의 노드에 분산시킬 수 있을 것입니다.

물론 액터는 라이브 IoT 오브젝트만 의미하는게 아닙니다. 어떤 주제든 상관없습니다. 다만 ‘라이브 IoT 오브젝트’가 좋은 예시일 뿐입니다.

액터는 클러스터에 고가용성과 고확장성을 위해 분산 배치되며 각 VM에 인-메모리 오브젝트로 취급됩니다. 뿐만 아니라, 로컬디스크에 보존되며 클러스터 내에 복제본을 둡니다.

액터는 state와 behavior를 독립적으로 가진 형태의 격리된 싱글스레드 오브젝트입니다. 모든 액터는 Actor 타입의 인스턴스이며, .NET 코드로는 아래와 같습니다:

// Actor definition (State+Behaviour) to run in the back end.
// Object instances of this Actor class will be running transparently
// in the service back end.
public class VehicleActor : Actor, IVehicleActor
{
public void UpdateGpsPosition(GpsCoordinates coord)
{
// Update coordinates and trigger any data processing
// through the State property on the base class Actor.
this.State.Position= coord;
}
}

그 다음, 아래의 클라이언트 코드는 proxy object를 통해 actor를 사용하고 있습니다.

// Client .NET code
ActorId actorId = ActorId.NewId();
string applicationName = "fabric:/IoTVehiclesActorApp";
IVehicleActor vehicleActorProxy =
ActorProxy.Create(actorId, applicationName);
vehicleActorProxy.UpdateGpsPosition(new GpsCoordinates(40.748440, -73.984559));

모든 커뮤니케이션 인프라는 Service Fabric의 Reliable Actors 프레임워크가 알아서 해줍니다.

클러스터에 수백만개의 VehicleActor 오브젝트를 여러 개의 서로 다른 노드/VM에서 구동할 수 있습니다. 이에 대해 Service Fabric은 수백만개의 액터가 골고루 퍼지고 균형을 유지하도록 파티션과 복제본을 조절해줍니다.

Actors 클라이언트 API는 액터 인스턴스와 액터 클라이언트 간의 통신 기능을 제공합니다. 액터와 통신하려면 클라이언트는 액터 인터페이스를 구현한 액터 프록시 오브젝트를 만들어야 합니다. 클라이언트는 액터에 있는 메소드를 프록시 오브젝트를 통해 실행할 수 있습니다. 액터가 어울리는 시나리오라면 Stateful 서비스보다 마이크로서비스 구현을 매우 단순화시킬 수 있습니다. Stateful 서비스는 더 많은 제어권을 주지만 파티션된 데이터의 처리, 파티션 주소와 복제본 관리도 할 필요가 있기 때문입니다. 이렇게까지 세부적인 제어까지 할 필요가 없다면 Reliable Actor로 보다 수월하게 작업할 수 있습니다.

요약

마이크로서비스 아키텍처는 분산형 구조 하에 통제된 아키텍처와 디자인 프로세스가 필요합니다. Azure Service Fabric과 같은 새로운 기술은 팀을 더 소규모 개발 팀 단위로 구성할 수 있게 하므로 보다 역동적으로 변화시킬 것이며 마이크로서비스마다 애자일 원리를 적용할 수도 있습니다.

 


Cesar de la Torre Redmond, Wash에 사는 Microsoft의 시니어 프로그램 매니저입니다. 마이크로서비스 아키텍처와 도메인주도개발 방법론 등을 Microsoft Azure와 .NET 개발에 적용하는데에 관심이 많으며 서비스를 직접 다루는 모바일 앱 개발도 관심이 많습니다.

Kunal Deep Singh Microsoft Azure Service Fabric 팀의 시니어 프로그램 매니저입니다. Azure 이전에는 Windows Phone, Silverlight에서 여러 릴리즈 기간 동안 일했고 게임 개발자로 Xbox 타이틀 작업도 했습니다. Seattle, Wash에 삽니다.

Vaclav Turecek마이크로소프트의 시니어 프로그램 매니저입니다. 차세대 PaaS(Platform-as-a-Service)인 Azure Service Fabric의 뛰어난 사람들과 지치지 않고 일하고 있습니다.

이 문서의 공동저자이자 리뷰를 한 Microsoft 기술 전문가 Mark Fussell Mark Fussell은 클라우드 어플리케이션을 만드는데 열정을 쏟고 있고 Microsoft에서 오랫동안 데이터 액세스와 서버측 통신 기술을 만들었습니다. Service Fabric을 이용하여 개발자들이 마이크로서비스 어플리케이션을 만드는 것에 대해 관심이 많습니다.

이 문서를 번역한 김영재 교육서비스 바로풀기의 개발사 Bapul의 CTO로서 기술로 교육에 새로운 시각을 주기 위해 밤낮없이 개발하고 있습니다.

Advertisements

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Google+ photo

Google+의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Twitter 사진

Twitter의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중