26 August 2015

scalability(확장성)이란?

필요한 자원의 양에 따라 웹 어플리케이션에 많은 자원을 추가로 할당하거나 줄여서 할당할 수 있는, 즉 애플리케이션의 규모를 가변적으로 변화시킬 수 있는 능력을 말한다.

Scale Up vs Scale Out

서버 에 추가적인 자원 할당이 필요할 때 다음의 두 가지 방법을 사용할 수 있다.

  특징 장점 단점
Scale Up 기존의 서버에 용량을 증설하는 방식 기술적 난이도가 적고, 구성이 심플하고 추가비용이 적다. 하나의 노드에서 증설할 수 있는 용량에는 한계가 있다.
Scale Out 새로운 노드(가상 또는 실제 서버)를 시스템에 추가하고 하나의 시스템처럼 연동해서 운영. Scale Up 방식에 비해서 용량의 확장성이 더 크다. 여러개의 노드 중 일부에 장애가 생겨도 나머지 장비로 대으이 가능하기 때문에 안정성이 증가한다. 구성이 복잡하고 기술적 난이도도 높다. 네트워크장비, 컨트롤러등 추가적인 비용이 필요하다.

SPOF(Single Point Of Failure)

단일 장애점. 시스템 구성에서 어떤 한 부분이 장애를 일으키면 전체 시스템이 작동 불능이 된다면 해당 부분은 SPOF이다. 시스템 상에서 여러 부분이 존재할 수 있다.

Imgur

위와 같은 구성에서 사용자의 요청을 받아 응답하는 과정은 다음과 같다.

  1. 사용자가 웹브라우저에 도메인 주소를 입력한다.
  2. 브라우저는 DNS에 도메인 주소를 보내 slipp.net의 IP주소를 받아와 접속한다.
  3. slipp.net의 DNS는 dev.slipp.net에 해당하는 WS의 IP를 보내주고 브라우저는 다시 접속한다.
  4. WS가 요청을 받아서 설정에 따라 응답할 것을 응답하고, 필요하면 로컬환경의 다른 포트를 통해 WAS로 다시 요청한다.
  5. WAS가 요청을 받으면 필요시 DB에 접속에 정보를 가져와 응답한다.

위의 3,4,5 과정에서 다음의 Point들이 관련되는데 모든 포인트가 현재는 단일 구성으로 되어 있으며, 하나라도 작동 불능이면 사용자는 적절한 응답을 받을 수 없으므로 SPOF에 해당한다. 특히 위 구성에서는 WS와 WAS가 localhost로 요청하는것으로 보아 하나의 ip에 할당된 물리적 기기 내에서 동작하는것을 알 수 있는데, 해당 기기 자체에 장애가 발생하면 동시에 죽어버리는 현상이 발생하고, WAS를 거치지 않아도 되는 캐시된 데이터나 static resource조차도 서비스할 수 없게 되어 버린다.

  • DNS
    기본적으로 도메인 등록을 하면 여러개의 DNS를 등록할 수 있다. 가장 앞단에서 요청을 받는 부분인데, 여러개의 DNS를 등록하면 그중에 응답이 가장 빠른 서버로 요청이 들어온다. 따라서 여러 개의 DNS를 구성하고, 1차 DNS에서 2차 -> 3차 … 로 순차적으로 동기화하면서 요청을 부담하도록 하면 개중 하나가 장애를 일으켜도 계속 서비스를 할 수 있다.

  • WS(NGINX)
    웹 서버를 여러대 운영하고, 그 앞단에 L4 스위치를 운영해서 로드밸런싱을 하면 웹서버의 부하에 따라서 요청을 분산해주므로 각 서버가 장애를 일으킬 확률도 줄고 한대가 장애를 일으키더라도 다른 서버에 요청을 할당할 수 있으므로 서비스 실패를 막을 수 있다. 단 요청이 전체 시스템의 부하를 넘어갈 정도로 과도하면 한 서버가 장애를 일으킬 경우 전체 시스템의 스케일이 축소되므로, 연쇄적으로 장애를 일으킬 수 있으므로 여유있게 운영해야 한다.

    또 static resource 요청에 대해서는 클라우드 CDN 을 이용한다든지 해서 위험을 감소시킬 수 있다.

  • WAS(TOMCAT)
    WAS역시 WS 단에서의 로드밸런싱을 통해서 여러대의 서버를 운영하면서 장애의 위험을 감소시킬 수 있다. 네트워크의 과도한 요청에 따른 부하로 서버가 죽을 수도 있지만, 프로그램 상의 오류로 인해 장애가 발생할 수도 있다. 전자의 경우는 물리적인 Scale Up이나 Scale Out을 통해서 해결할 수 있지만, 후자의 경우는 필요시 WS의 가상 호스트 기능을 이용해서 인스턴스를 여러개 띄우는 것 만으로도 안정성을 높일 수도 있다.

  • DB(MySQL)
    DB도 replication을 통해서 여러대를 운영할 수 있다. DB는 보통 master 한대와 slave 여러 대로 구성하고, master의 정보를 slave에 복제하는 방식으로 동기화를 유지한다. 이때 아무리 짧은 시간이라도 시차가 발생할 수 있는점에 대해서 유의해서 운영해야 한다. 또, DB 서버에 대한 로드 밸런싱은 어떻게 처리할 것인가에 대한 문제도 있는데, WAS에서 application 레벨에서 처리할 지, proxy를 두고 처리할지 로드밸런서를 하나 더 운영할지 상황에 따라서 고려해야한다.

  • 공통적으로 모든 장비는 장애가 발생할 가능성이 있기때문에 이중화를 해야 하며 이에 따라 작업을 어떻게 할당할지의 문제가 발생한다. 또 한 대의 물리적 기기 내에서도 스토리지 디바이스가 하나일 경우 SPOF가 될 수 있으므로 RAID구성을 이용해서 안정성을 높일 수 있으며, 장애 발생시 하드웨어 교체가 쉽게 가능한지도 고려해야 한다.

Failover

시스템에서 이상이 생겼을 때 예비 시스템으로 자동전환되는 기능. 위의 예에서 보듯이, 평상시에도 적절한 로드 밸런싱을 통해서 장애 발생에 대비하지만 실제 장애가 발생한 상황에서도 예비해둔 다른 노드로 작업의 전환이 이루어질 수 있도록 해야 한다.

Load Balancing

여러 대 장비의 정상작동 여부와 부하를 모니터링 하고 작업에 에단 요청이 들어왔을 때, 부하를 적절히 분배해주는 동작을 말하며, 이러한 기능을 하는 프로그램이나 하드웨어 장비를 Load Balancer라고 한다.

L4 vs L7

네트워크상에는 여러 계층에서 작동하는 수많은 switch가 있다. 그중에 3번째인 네트워크 레이어 이하에서 동작하는 스위치들은 패킷을 목적지로 전송해주는 라우팅 역할만 하기때문에 Round Robin 등의 방식으로 이중화를 가능하게 해줄 수는 있지만, ip주소 이외의 조건에 따른 로드밸런싱이 불가능하다. 또한 클라이언트측에서 DNS를 캐시하기때문에 실제 장애가 발생할 경우 민첩한 대응이 불가능하다. 이러한 이유로 트랜스포트 레이어 이상에서의 로드밸런싱이 필요해진다.

L4 switch라 함은 OSI7 레이어 중 네번째, 즉 transport레이어에서 작동하는 switch를 뜻한다. transport 레이어는 TCP / UDP 프로토콜이 존재하는 레이어로, ip주소와 TCP/UDP 에 존재하는 HTTP, FTP, SMTP 등의 프로토콜, port 정보를 이용해서 스위칭을 수행한다.

L7 switch는 마찬가지로 OSI7 레이어 중 application레이어에서 작동하며 최종 레이어에서 작동하는 만큼 요청된 url이나 쿠키 등의 전송된 내용까지 참조하여 더욱 세밀하게 로드밸런싱을 수행할 수 있다.

HAProxy

일반적으로 하드웨어로 구성된 L4, L7 스위치에 비교하여, 오픈소스 소프트웨어로 구성되어있는 Reverse Proxy이다. 일단 proxy application 이기 때문에, 레이어7에서 동작하며 로드밸런싱 기능을 수행하므로 소프트웨어 L7 스위치로 봐도 좋을 것 같다.

Reverse Proxy

일반적으로 client가 최종 목적지에 도달하기 전에 거쳐가도록 설정하는 Forward Proxy에 비해서 Reverse Proxy는 목적지가 되는 server측에서 실제 요청을 처리하기 전에 먼저 요청을 받아서 전달하는 proxy이다.

DB

master / slave

위의 DB 항목에서도 설명했지만, DB를 replication할 때, master와 slave로 구성하게 된다. 여러대의 slave는 한대의 master 데이터를 원본으로 복제하게 되며, master와 slave간의 데이터 흐름은 일방적이다. 따라서 일반적으로 write 작업은 master에서 , read 작업은 slave에서 수행하게 되며, 보통 write보다 read가 빈번하게 일어나므로 여러대의 slave를 운영하게 된다. 이러한 작업에 따른 로드 밸런싱을 위해서 다양한 방법이 사용된다.

master는 한대이기 때문에 SPOF가 될 가능성이 있지만 같은 정보를 가지고 있는 slave가 여러대 있으므로, master에 장애가 발생하면 slave중 하나를 master로 대체해서 대응할 수 있다.

sharding

DB에 대량의 데이터를 저장해야 할 경우 데이터를 나누어서 저장하는 기술이다. 한 대의 서버가 가진 storage 한계를 극복할 수도 있고, 부하도 분산되므로 성능상 이점이 있다. 또 하나의 shard가 죽어도 나머지 shard에 저장된 데이터에는 접근이 가능하므로 가용성 측면에서도 이점이 있지만, 같은 데이터가 여러벌 존재하는 것은 아니므로 replication과는 다르다.

Session

세션 정보는 WAS에 저장되고, HTTP는 무상태 프로토콜이므로, 로드밸런서를 사용할 경우, 한 사용자가 사진의 session이 저장된 WAS에 계속 접근한다는 것을 보장할 수 없다. 이 문제를 해결하기 위해 다음과 같은 기술이 사용된다.

Sticky Session

L4 스위치에는 Sticky 라는 옵션이 있다. 해당 옵션을 활성화 시키면 로드밸런서는 timeout 시간 내에 재접속한 동일 사용자에 대해서 같은 서버로 접속할 수 있도록 할당해준다. 단 이렇게 할 경우 session을 유지할 수는 있지만 기본기능인 로드밸런싱에 문제가 생길 수 있다. 각 접속마다 적절한 서버로 할당해주는 것이 아니라 특정 서버로의 할당을 강제하기 때문에 상황에 따라서 한 서버로 접속이 몰리는 상황이 발생할 수 있다.

특히 수많은 사람이 한대의 프록시 서버를 이용해서 접속할 경우, 사용자 ip는 동일하게 나타나므로, 다수의 접속을 한대의 서버에서 모두 소화해야 하는 현상이 발생한다.

이런 문제를 해결하기 위해서 L7나 HAProxy를 사용해서 Cookie에 있는 sessionID 기반의 switching을 수행한다.

Session Clustering

Sticky Session을 이용하더라도 기존 서버의 부하가 과도할 경우 서버가 죽은것 보다는 사용자 한명의 세션정보를 잃는 것이 낫기 때문에, 한 사용자의 요청이 서로 다른 서버로 할당되는 경우가 발생한다. 이럴 경우에도, 사용자가 강제로 로그아웃되어버리거나 세션에 기록된 정보를 잃는 것을 방지하기 위해서는 여러대의 WAS가 서로 session 정보를 공유해야 하는데 이것을 Session Clustering이라 한다.

Session Clustering을 수행하는 방식은 WAS마다 다르지만, 대표적으로 다음의 방식이 있다.

  • 멀티캐스트를 이용해서 모든 서버 인스턴스에 세션정보를 동일하게 기록하는 방법 : 많은 메모리 자원이 소모되고 네트워크 트래픽이 증가하는 단점이 있다.
  • 세션이 스토리지에 파일 형태로 저장된다면 Zookeeper 등을 이용해서 동기화 할 수도 있다.
  • WAS중 한대의 지정된 인스턴스에만 세션정보를 저장하고 이에 대한 backup 서버를 운영한다. 메모리 자원과 네트워크 트래픽을 절약할 수 있다.
  • DB에 세션정보를 저장하는 방식. DB가 여러대일 경우 DB커넥션 부하 증가와 동기화 문제가 발생할 수도 있다.

Session Server

Session Clustering에 따른 오버헤드를 줄이기 위한 방안으로 세션만을 저장하는 별도의 서버를 운영하는 방안을 마련할 수도 있다. 이럴 경우, 장애에 대비하기 위해 이중화를 해야 한다.

Cache

서버 성능을 높이기 위한 효과적인 전략중 하나는 Cache 이다. 서버를 여러대 운영할 경우 각각의 서버에서 이미 다른 서버에 캐시되어 있는 데이터를 중복으로 접근하거나 거꾸로 동일한 캐시 데이터를 중복으로 생성하게 되어 자원의 낭비가 발생하게 된다. 이런 문제를 해결하기 위해 다음의 방법을 사용한다.

전역Cache vs 분산Cache

Session Server를 운영하는것 처럼 Cache를 전담하는 글로벌 캐시를 운영할 수 있다. 이런 경우 일반적으로 모든 서버는 캐시에만 질의하고 캐시는 메모리에 캐시가 된 경우 캐시를 반환하고 아닌 경우 저장소에 접근해서 해당 데이터를 캐시하게 된다.

반면, 각각의 분산된 노드가 자신의 Cache를 가지는 방식으로 전역 Cache의 부하 집중을 완화시킬 수 있지만, 현재 찾고자 하는 데이터가 분산Cache중 어디에 들어있는지를 알아내야 하기 때문에, hash 함수가 필요하다.

memcached, redis

memcached, redis 는 메모리 상에서 key-value를 이용해서 데이터를 빠르게 입출력할 수 있는 저장소이다. 둘다 NoSQL에 속한다. 메모리 캐시는 비용이 비싸므로 효율적으로 적절히 구성하는 것이 중요한데, 예를 들면 파일 시스템을 캐시하는 것은 OS단에서 캐시되는 것이 더 효율적이고, DB 쿼리를 캐시하는 것은 대부분의 상용 DB가 이미 캐시를 자체적으로 하고 있으므로 효율적이지 않다. application 단에서 반복된 연산을 통해서 특정 데이터를 생성하는 경우 사용하는 것이 가장 효율적이다. 두 프로그램상의 장단점에 대해서 많은 토론이 이루어지고 있는 것으로 보다 눈에 띌만큼의 확실한 우위는 없지만 다음과 같은 장단점이 존재한다.

memcached : Redis에 비해 기능이 적지만 더 단순하다. 메타 데이터에 더 적은 메모리를 사용하기 때문에, 작고 변하지 않는 데이타를 저장하기에 좋고, scale out을 좀더 잘 지원한다. 입/출력에 O(1)이 보장된다.

redis : memcached 더 많은 데이터 형식을 사용할 수 있고, 정교한 기능을 제공한다. memcached의 좋은점을 취하고자 나중에 개발되었기 때문에 대부분의 상황에서 더 낫다.

Queue

서비스가 복잡해지고 사용자가 많아지면 즉각 응답해야 하는 요청이 있고, 시간이 많이 걸리는 응답이 생길 수 있다. 이렇게 오래 걸리는 요청은 비동기로 처리하고 즉각적인 요청부터 처리하는 것이 낫다.

Java에서의 비동기 처리

Java에서는 기본적으로 Thread 기반의 비동기 처리를 한다. 직접 Thread 방식의 프로그래밍을 할 수도 있겠지만 Spring Framework에서 지원하는 @Async 어노테이션을 사용하면 간단하게 비동기 처리를 할 수 있다.

비동기 처리를 담당하는 실행기 정의

 <bean id="asyncService" class="...AsyncService"></bean>
      
 <task:executor id="asyncExecutor" pool-size="100-1000" queue-capacity="1000"  rejection-policy="ABORT" />
 <task:annotation-driven executor="asyncExecutor" />

비동기 메쏘드

@Async
public Future<Integer> test() {
    System.out.println("test메서드 실행### ");
    return new AsyncResult<Integer>(-2);
}

테스트

@Test
public void async() throws InterruptedException, ExecutionException {
    assertThat(asyncTest.test().get(), is(-2));
}

bean으로 등록된 클래스의 메쏘드에 @Async 어노테이션을 붙여주고 return 타입을 java.util.concurrent.Future 로 지정해주면 해당 메쏘드는 비동기로 실행되고, 바로 Future를 리턴한다. 결과에 대해서는 callback을 이용해서 처리하거나 return된 FutureisDone() 을 실행해서 완료 여부를 알 수 있다.

Message Queue 서버

서로 다른 네트워크상의 노드 사이에서 즉시 실행될 수 없는 작업들이 비동기로 작업을 진행할 때, 예약을 했다가 나중에 순차적으로 처리하는 것이 중요한 때가 있을 수 있다. 이때 사용하는 것이 Message Queue 서버이다. MQ 서버에서 메시지는 바이트스트림으로 처리되고 Queue 방식으로 순차적으로 처리된다. 최근 대표적으로 사용되는 MQ 서버는 RabbitMQ 등이 있는 것으로 보인다.



blog comments powered by Disqus
처음으로