11.17
주요뉴스
뉴스홈 > 칼럼
[강좌] 다운타임 없는 서비스 구현 패턴 (3)정윤진 피보탈 랩 프린시플 테크놀로지스트

[컴퓨터월드]

   
▲ 정윤진 Pivotal Lab Principal Technologist

1. 스프링 부트  <2017.5월호>
2. 스프링 클라우드 Config server  <2017.6월호>
3. 스프링 클라우드 Service discovery  <이번호>
4. 스프링 클라우드 Zuul
5. 스프링 클라우드 Hystrix
6. 스프링 클라우드 Zipkin


서비스 디스커버리

클라우드 환경은 동적으로 자원을 할당받고 해제하는 것이 언제든 일어나는 동적인 환경이다. 기존에 고정된 수량의 시스템을 운용하는 데 익숙해진 경우, 이것이 장점이 아닌 단점으로 받아들여지는 경우가 많다. 그렇지만 서버가 부족해 추가하려는 경우나 너무 많이 구매해서 다시 되팔고 싶은 경우를 생각해본다면 클라우드가 제공하는 장점이 매우 중요하다는 것을 알 수 있게 된다.

문제는, 이들을 운용하는 입장에서는 동적으로 변화하는 각종 설정으로 인해 자동화되지 못한 수작업들이 매우 자주, 그리고 반복적으로 발생하고, 이것을 정확하게 처리하지 못한다면 즉각 장애로 연결되는 환경이기 때문에 더욱 민감해질 수 있다. 즉, 고정된 수량의 시스템을 운용할 때 사용하던 기법들이 수량이 동적으로 변경하는 환경에서는 그다지 쓸모가 없거나, 아니면 다른 도구로 대체돼야 한다. 그것들 중 대표적인 것이 바로 DNS를 ‘서비스 디스커버리’라 불리는 방법으로 대체하는 것이다.

서비스 디스커버리에 앞서 DNS 체계에 대해 잠시 생각해보자. 시스템의 입장에서 DNS는 보통 /etc/resolv.conf 에 설정된 네임서버를 참조해 도메인을 IP정보로 바꿔주는 역할을 한다. 문제는 이 레코드가 업데이트되고 참조되는 방식이 1970년대 3600bps 모뎀을 사용하던 시절 이전의 기술이라는 것이다.

열악한 네트워크를 가진 환경일수록 캐시의 사용이 권장된다. 네트워크 너머로 질의가 많이 발생하면 발생할수록 네트워크에 사용되는 비용이 증가하고 응답을 기다리는 동안 사용자경험이 엄청나게 떨어지므로, 더 빠른 응답을 위해 한번 질의 및 응답된 도메인에 대해서는 지정한 TTL 값만큼 연관된 시스템들에서 보존하게 된다. 첫 번째 문제는 바로 이 TTL 자체에 있다.

다른 하나는 동일한 도메인에 새로운 호스트가 등록되거나, 기존에 등록된 호스트를 제거하거나, 변경을 위해 업데이트를 하는 경우엔 Zone file 이라고 불리는 파일을 업데이트하고 프로세스를 재시작해야 하는 문제가 있다. 이전처럼 일 년에 한두 번 정도 서버가 추가되거나 업데이트되는 환경이라면 작업 공지를 하고 네임서버를 업데이트하는 것이 가능하지만, 동일한 동작이 수분 내에 수십 대 이상의 서버에서 발생하는 환경이라면 어떨까.

네임서버는 기본적으로는, 그러니까 기본적으로는 네임서버가 응답하는 도메인과 연결된 호스트들에 대한 헬스체크를 수행하지는 않는다. 즉 관리자가 설정한 호스트의 IP가 동작하는지의 여부와는 관계없이 지정된 응답만을 제공하도록 동작하는 체계라는 것이다.

물론 최근의 네임서버들은 다양한 요구를 반영하기 위해 고도로 발달하고 있는 것은 사실이다. 질의하는 클라이언트의 지리적 위치를 참조해 가까운 지역의 서버를 응답한다든지, 지리적으로 다수의 네임서버를 준비하고 이들을 동일한 IP주소로 묶어 any cast 같은 방법으로 고가용성을 구현한다든지, 그리고 네임서버에 등록된 호스트들이 동작하는지 여부를 주기적으로 헬스체크를 수행하고 문제가 발생한 경우 지정한 fallback 호스트 그룹으로 응답을 우회하거나, TTL을 0에 가깝게 설정해서 변화에 더 빠른 대응을 하도록 한다든지 하는 것들이 바로 그렇다.

네임서버는 인터넷에서는 없어서는 안 될 존재기는 하지만, 그리고 위에 열거한 다양한 기능들과 함께 서비스의 유연성을 높여줄 수 있는 방법들이 존재하기는 하지만, 만약 내부 서비스 간 연동을 위해 네임서버가 필요한 경우라면 어떨까.

우리는 매우 자주, 서비스에 별도의 내부 네임서버를 배치하는 것을 볼 수 있다. 이것은 시스템의 환경 설정을 네임서버로 분리함으로써 변경에 조금 더 유연함을 확보할 수 있다. 하지만 전술했듯, 하루에 수 대, 또는 수십 대, 아니면 수백 수천 대의 호스트가 생겨나고 사라진다면 어떻게 해야 할까. 이를 위해 내부 네임서버를 업데이트하는 스크립트라도 쓰기엔 너무 비효율적이며, 네임서버 관리라는 작업이 애초에 그다지 쉬운 일이 아니다.

따라서 우리는 서비스 내부에 애플리케이션과 애플리케이션이 서로를 찾고, 호스트가 다른 호스트를 찾아내는 동작이 아무리 많이, 그리고 자주 발생하더라도 지원할 수 있는 새로운 방법이 필요하다. 그리고 이 새로운 방법을 지칭하는 단어는 여러 개가 있을 수 있지만, 보통 ‘서비스 디스커버리’를 사용한다.

서비스 디스커버리로 지칭되는 도구들이 수행하는 동작의 핵심은 비교적 간단하다. 클러스터로 구성된 서비스 디스커버리 서버는 클라이언트에 자신이 갖고 있는 호스트의 정보를 전달 및 업데이트해준다. 클라이언트는 기동되는 순간 자신의 네트워크 정보, 구동하고 있는 애플리케이션 정보와 같은 내용을 서비스 디스커버리 서버에 전달한다.

서버는 전달받은 정보를 바탕으로 필요하면 헬스체크를 수행하고, 고가용성을 위해 클러스터링돼있는 다른 서버와 업데이트된 내용을 동기화한다. 디스커버리 클라이언트들은 서버로부터 전달받은 정보를 지정된 시간동안 캐싱을 수행함으로써, 서비스 디스커버리 서버가 동작하지 않는 경우에도 이미 저장된 리스트를 바탕으로 서버 응답 없이도 다른 호스트에 요청하는 것이 가능하다.

   
 

위 그림은 넷플릭스 유레카(Eureka)가 동작하는 방식을 보여준다. 유레카는 서버와 클라이언트로 이뤄져있으며, 클라이언트는 각 애플리케이션 안에서 에이전트처럼 동작한다. 넷플릭스에서는 유레카 서버를 다수의 데이터센터에 구성해 하나의 유레카 서버에 문제가 발생하더라도 전체 서비스에는 이상이 없도록 구성하고 있다. 이는 유레카 클라이언트들이 등록된 호스트의 정보를 받아오는데 하나의 서버만을 사용하도록 구성하지 않고 다수의 서버를 참조하도록 구현됐기 때문에 가능하다.

클라이언트 애플리케이션은 시작되는 순간 자신의 정보를 서버로 등록한다. 이렇게 등록된 신규 클라이언트 정보는 다른 클라이언트들에 전달되고, 이를 통해 다른 클라이언트 애플리케이션들은 ‘서로 통신이 가능한’ 상태가 된다. 이를 통해 클라이언트 애플리케이션에서는 ‘동적으로 변화하는 환경에 대한 정보’를 바탕으로 원하는 호스트 또는 애플리케이션에 직접 연결, 특정 호스트가 응답하지 않는 경우 다른 호스트로 밸런싱 등의 다양한 동작을 처리할 수 있는 능력이 생긴다.

따라서 클라우드에서 동작하는 거의 모든 넷플릭스의 애플리케이션들은 이 유레카 서비스에 자신을 등록하고, 다른 애플리케이션들에서 접근하는데 문제가 없도록 구성하고 있다. 그리고 이 애플리케이션은 마이크로서비스일 뿐만 아니라 캐시, 영구적 데이터저장소 등 매우 다양하다.

서비스 디스커버리의 구성은 클라우드 환경에서 매우 놀라운 장점들을 제공한다. 첫 번째는 높은 가용성이다. 예를 들어 특정 애플리케이션에 문