외부에서 HAProxy의 VIP로 curl 명령 실행시 뒷단의 Web 서버로 LoadBalancing이 되는 것이 확인된다.
앞단에서 HAProxy Master역할을 하는 것은 Keepalived의 Master로 설정된 노드이다.
이제 Master VM 장애 발생을 가정하고 Failover 테스트를 진행한다.
Slave에는 Haproxy Service가 실행 중이다.
(기본 커널 파라미터가 셋팅이 안된상태에서 Slave의 HAProxy 데몬은 실행에 실패한다.테스트를 위해 Master를 Off후 서비스를 올린 뒤에 다시 Master를 On 시켜 테스트 환경을 만든 것이다.
데몬이 실패한 이유는 haproxy에 의해 VIP가 네트워크 인터페이스에 바인딩되어야하는데 커널파라미터가 비활성화 값이므로 실패하였다.)
Master VM을 Poweroff 하였을때 Keepalived에 의해 Slave VM이 Master로 승격되었다.
외부에서 HAProxy로 curl 명령 실행시 무중단으로 실행되는 것도 확인되었다.
이제 다시 Master VM을 실행시켜 FailBack을 할 예정이다.
Slave는 정상이나 Master가 다시 정상으로 돌아왔을 경우 HAProxy에 대한 응답은 실패했다.
Master에서 확인해보니 아래와 같이 HAProxy Service가 실패하였다.
실패 원인
haporxy는 haproxy.cfg에 있는 VIP를 바인딩해야하는데 VIP가 현재 로드밸런서에 없으므로 오류를 발생시킨다. (오류 내용 - 프록시 : 소켓을 바인드 할 수 없음)
Master -> Slave로 FailOver하는 경우 Keepalived에 의해 VIP가 옮겨간다. Slave에서 서비스 구동을 하다가 Master가 정상으로 돌아오면 Keepalived는 가중치에 의해 다시 Master가 트래픽을 받도록 변경한다.
이 상태에서 VIP도 Master로 옮겨오는데 Keepalived가 VIP를 Master의 네트워크 인터페이스에 바인딩하고 HAProxy가 서비스가 실행되면 커널파라미터에 상관없이 HAProxy에서 VIP를 네트워크 인터페이스에서 인식하여 정상적으로 FailBack이 된다.
# vim /lib/systemd/system/haproxy.service
위 방법은 Service의 Boot Type에서 notify를 idle로 변경한 것이다.
notify의 경우 simple과 동일하나 unit이 구동되면 systemd에 시그널을 보낸다. (unit이 시작된 즉시 systemd는 유닛의 시작이 완료되었다고 판단한다. 그런데 다른 유닛과 통신하기 위해 소켓을 사용하는 경우 이 설정을 사용하면 안된다.)
HAProxy의 경우 Keepalived와 통신하기 때문에 HA를 구성하기 위해서는 Type이 notify가 되어서는 안된다. 이런 부분을 해결해주는 것이 net.ipv4.ip_nonlocal_bind 파라미터이다.
Master에서 Type을 notify -> idle로 변경하였을 경우 net.ipv4.ip_nonlocal_bind=0 이더라도 정상적으로 FailBack이 진행된다.
하지만 Slave의 경우 Unit의 Type을 변경하더라도 서비스는 실패가 된다. (실패한 이유는 Keepalived의 VIP가 Master에 있기때문에 통신할 소켓이 없어서 발생한다. 네트워크 인터페이스의 바인딩 문제)
net.ipv4.ip_nonlocal_bind=1 일경우 Unit의 Type에 상관없이 HAProxy 서비스는 정상 시작된다.
2. 커널 파라미터 변경후 FailOver
net.ipv4.ip_nonlocal_bind=1 로 변경한 경우 정상적으로 FailOVer & FailBack이 진행된다. 다면 FailBack 과정에서 짧은시간동안 순단이 발생할 수 있으며 순단시에는 HTTP Code 503을 발생한 후 정상적으로 HTTP Code 200을 반환한다.
실제 운영환경에서는 자동 FailBack이 아닌 수동으로 FailBack을 진행하는 것을 권장한다. 잠시동안이라도 발생하는 네트워크 순단현상을 최대한 방지하기 위해서이다.
3. 정리
Master & Slave 구성에서 Master는 Service Unit의 Type 값을 변경해 정상적인 FailBack을 할 수 있고 Slave에서는 커널 파라미터를 변경하여 FailOver 환경을 만들수 있으나 정상적인 방법은 아니므로 기본 커널 파리미터 값을 변경하여 HA를 구성하는 것을 권장한다.
간단하게 설명하면 VIP (가상 IP)를 기반으로 작동하며 Master 노드를 모니터링하다가 해당 노드에 장애가 발생했을시 Stanby 서버로 Failover되도록 지원한다.
즉, Heartbeat 체크를 하다가 Master에 장애발생시 Slave로 FailOver하는 역할을 하는 것이다.
아래 그림은 keepalived의 구조이다.
LoadBalancing을 하기위해서 LVS (Linux Virtual Server)의 구성 요소인 IPVS를 사용한다.
Keepalived와 IPVS를 조합해 HAProxy와 같은 LoadBalancer를 사용하여 L7 LoadBalancing을 할 수도 있지만 Keepalived에서 제공하는 LoadBalancing 기능을 사용하여 L4 LoadBalancing을 할 수도 있다.
Keepalived는 HA를 하기위해 VRRP Protocol을 사용하며 LoadBalancer 설정을 위한 LVS와는 독립적으로 동작한다.
Keepalived는 LoadBalancing 쪽에서 사용되는 LVS(IPVS)와 NAT, Masquerading에 쓰이는 Netfilter와 VIP 할당 및 해제에 쓰이는 Netlink, VRRP Advertisement 패킷 전송을 위해 사용되는 Multicast와 같은 Kernel 컴포넌트로 구성되어 있다.
Keepalived의 HA는 자신의 IP 주소와는 별개로 VIP를 설정해두고 문제가 생겼을때 이 VIP를 다른곳으로 인계하여 같은 IP주소를 통해서 서비스가 지속되도록 해주는것이 핵심인데 이 부분은 Keepalived가 VIP 할당 및 해제를 자동으로 해주기 때문에 별도의 설정을 하지 않아도 문제가 없다.
HA구성에서 Master의 장애인지 검출할 수 있는 방법은 아래와 같다.
ICMP (L3)
ping을 사용하는 것이다.
네트워크 구간이 정상이고 서버가 살아있다면 ICMP echo 요청에 대한 응답이 돌아올 것이지만 ICMP 패킷이 바이러스 침투나 공격으로 사용되는 사례가 많기때문에 ICMP를 차단하는 경우가 많아 잘 사용하지 않는다.
TCP 요청 (L4)
서비스를 올린 후 방화벽 허용 확인을 위해 telnet 명령을 실행해 정상 체크를 하기도 한다. 이는 TCP 요청이 정상적으로 응답하는지 확인하는 방법이다.
서비스가 살아 있어 Port Listen은 하고 있지만 서버나 프로그램의 오류가 있어 HTTP 500 Code를 반환하는 경우 정확한 확인이 불가능한 단점이 있다.
HTTP 요청 (L7)
실제 실행중인 서비스에 Heathcheck를 위한 Endpoint를 두고 서비스에 요청을 날려 200 OK가 반환되는지 확인하는 것이다.
HTTP 요청 자체가 ICMP나 TCP 요청에 비해 무겁기 때문에 고려가 필요하다.
Keepalived는 Heathcheck를 위해 TCP, HTTP, SSL, MISC와 같은 방법을 사용한다.
TCP_CHECK
비동기방식으로 Time-Out TCP 요청을 통해 장애를 검출하는 방식이다.
응답하지 않는 서버는 Server Pool에서 제외한다.
HTTP_GET
HTTP Get 요청을 날려 서비스의 정상 동작을 확인한다.
SSL_GET
HTTP Get과 같은 방식이지만 HTTPS 기반이다.
MISC_CHECK
시스템상에서 특정 기능을 확인하는 Script를 돌려 그 결과가 0인지 1인지를 가지고 장애를 검출하는 방법이다.
네트워크가 아니라 시스템상에서 돌고 있는 서비스의 정상 동작을 확인하는데 유용하다.
Keepalived 구성
환경
VitualBox
OS : Ubuntu 18.04
VIP : 192.168.219.119
Active : 192.168.219.120
Stanby : 192.168.219.121
1. keepalived 설치 (Active & Stanby)
# apt update
# apt install -y keepalived
2. keepalived 설정 (Active & Stanby)
2.1) Active Node 설정
# vim /etc/keepalived/keepalived.conf
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 200
advert_int 1
authentication {
auth_type PASS
auth_pass 7388
}
virtual_ipaddress {
192.168.219.119
}
}
2.2) Stanby Node 설정
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 7388
}
virtual_ipaddress {
192.168.219.119
}
}
3. 테스트
3.1) Fail Over Test
# ping 192.168.219.119
3.2) Active Node Poweroff
# poweroff
3.3) Fail Over Log Check
Active Node가 Poweroff 되자 Keepalived 데몬이 VIP (192.168.219.119) 를 Stanby Node에 인계하여 FailOver를 하였다.
- REST는 Representational State Transfer이라는 용어의 약자로 기본적으로 웹의 기존 기술과 HTTP 프로토콜을 그대로 활용하기 때문에 웹의 장점을 최대한 활용할 수 있는 아키텍처
- 웹에 존재하는 모든 자원(이미지, 동영상, DB 자원 등)에 고유한 URI를 부여해 활용하는 것으로 자원을 정의하고 자원에 대한 주소를 지정하는 방법론
- 자원의 이름(자원의 표현)으로 구분하여 해당 자원의 상태(정보)를 주고 받는 모든 것을 의미
- 자원(Resource)의 표현(Representation)에 의한 상태 전달
자원(Resource)의 표현(Representation)
* 자원 : 해당 소프트웨어가 관리하는 모든 것 (Ex. 문서, 그림, 데이터, 해당 소프트웨어 자체 등등)
* 자원의 표현 : 그 자원을 표현하기 위한 이름 (Ex. DB의 학생 정보가 자원일 때 ’Students’를 자원의 표현으로 정함)
상태(정보) 전달
* 데이터가 요청되어지는 시점에서 자원의 상태(정보)를 전달
* JSON 혹은 XML을 통해 데이터를 주고 받는 것이 일반적
- REST 아키텍처는 Hypermedia API의 기본을 충실히 지키면서 범용성을 보장
- Network 상에서 Client와 Server 사이의 통신 방식 중 하나
REST 개념
- HTTP URI을 통해 자원을 명시하고 HTTP Method (POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것을 의미
- REST는 자원 기반의 구조(ROA, Resource Oriented Architecture) 설계의 중심에 Resource가 있고 HTTP Method를 통해 Resource를 처리하도록 설계된 아키텍처를 의미
- 웹 사이트 이미지, 텍스트, DB 내용 등의 모든 자원에 고유한 ID인 HTTP URL을 부여
- CRUD Operation
Create : 생성 (POST)
Read : 조회 (GET)
Update : 수정 (PUT)
Delete : 삭제 (DELTE)
HEAD : header 정보 조회 (HEAD)
Richardson Maturity Model (RMM)
레벨 0
- 웹의 기본 메커니즘을 사용하지 않는 단계로 HTTP를 통해 데이털르 주고 받지만 모든 데이터 요청 및 응답과 관련한 정보를 HTTP의 Body정보를 활용
POST /appointmentService HTTP/1.1
[various other headers]
<openSlotRequest date = "2010-01-04" doctor = "mjones"/>
- POX (Plain Old XML)로 요청과 응답을 주고받는 RPC 스타일 시스템으로 HTTP Method는 POST만 사용하며 특정 서비스를 위해 클라이언트에서 접근할 Endpoint는 하나
레벨 1 - 리소스
- RMM에서 REST를 위한 첫 단계는 리소스를 도입하는 것으로 모든 요청을 단일 서비스 Endpoint로 보내는 것이 아니라 개별 리소스와 통신
POST /doctors/mjones HTTP/1.1
[various other headers]
<openSlotRequest date = "2010-01-04”/>
- 즉 함수를 호출하고 인자들을 넘기는 것이 아니라 다른 정볼르 위해 인자들을 제공하는 특정 리소스로 요청을 보냄
- 이럴 경우 이점은 자원이 외부에 보여지는 방식과 내부에 저장되는 방식을 분리할 수 있다는 것
- 예를 들어 클라이언트 단에서 완전히 다른 포맷으로 저장하더라도 JSON 형태의 데이터를 요청할 수 있음
레벨 2 - HTTP Method
- 레벨 1의 URL + HTTP Method 조합으로 리소스를 구분하는 것으로 응답상태를 HTTP Status code를 활용 (현재 가장 많은 RESTful API 이 단계를 제공)
GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk
- 발생한 에러의 종류를 커뮤니케이션하기 위해 상태코드(Status Code)를 사용하는 것, 그리고 안전한 오퍼레이션 (GET 등)과 안전하지 않은 오퍼레이션 간의 강한 분리를 제공하는 것이 레벨 2의 핵심 요소
- Status Code를 사용한다는 것은 기존에는 유저 생성 요청을 했을 경우 302 등의 리다이렉트 요청을 서버에서 내려주는 방식이었다면 지금은 201(created)로 유저 생성 성공을 알릴 뿐 페이지 이동은 Client 단에서 해결하는 방식 (Login의 경우 성공은 200, 실패시 인증실패는 401, 성공했으나 권한 문제는 403 등)
- 즉 서버는 순수하게 API로서의 기능만 제공하게 되는 것 (View는 Client에서)
- 강한 분리를 제공하는 것은 HTTP에서 GET은 멱등박식으로 자원을 추출하는데 이에 어떤 순서로든 얼마든지 호출하든 매번 같은 결과를 얻도록함 (이에 통신상의 모든 참여자에게 캐싱 기능을 지원하는 다양한 방법을 제공)
레벨 3 - 하이퍼미디어 컨트롤
- HATEOAS (Hypertext As The Engine Of Application State) 애플리케이션 상태 엔진으로서의 하이퍼 미디어
- 하이퍼 미디어란 하나의 컨텐츠가 텍스트나 이미지, 사운드와 같은 다양한 포맷의 컨텐츠를 링크하는 개념
- 이것은 관련 컨텐츠를 보기위해 링크를 따라가는 방식과 유사한 방식으로 동작하는데, 클라이언트가 다른 자원에 대한 링크를 통해 서버와 (잠재적으로 상태 변이를 초래하는) 상호작용을 함
- 즉 특정 API를 요청한 후 다음 단계로 할 수 있는 작업을 알려주는 것이며 다음 단계의 작업을 위한 리소스의 URL을 알려주는 것
- 이 단계를 적용하면 클라이언트에 영향을 미치지 않으면서 서버를 변경하는 것이 가능
GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk
HTTP/1.1 200 OK
[various headers]
<openSlotList>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
<link rel = "/linkrels/slot/book"
uri = "/slots/1234"/>
</slot>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
<link rel = "/linkrels/slot/book"
uri = "/slots/5678"/>
</slot>
</openSlotList>
- 단점은 클라이언트가 수행할 작업을 찾기 위해 링크를 따라가기 때문에 컨트롤 탐색에 꽤 많은 호출이 발생할 수 있다는 것
- 또한 복잡성이 증가할 수 있으며 HTTP 요청상에 나타나는 부하로 낮은 지연시간이 요구될 때 문제가 될 수 있음
- HTTP 기반의 REST 페이로드는 JSON 또는 바이너리 등의 포맷을 지원하므로 실제로 SOAP보다 훨씬 간결할 수 있지만 쓰리프트와 같은 바이너리 프토토콜에는 상대가 되지 못함
- 또한 웹 소켓의 경우 HTTP 초기 핸드세이크 후에 클라이언트와 서버 간에 TCP 접속이 이루어지고 브라우저에서 스트림 데이터를 보낼 때 효율적일 수 있음
- 따라서 HTTP가 대규모 트래픽에는 적합할 수 있지만 TCP 또는 다른 네트워킹 기술 기반의 프토콜과 비교하면 낮은 지연시간이 필요한 통신에는 그다지 좋은 선택이 아닐 수 있음
- 이러한 단점에도 HTTP 기반의 REST는 서비스 대 서비스의 상호작용을 위한 합리적이고 기본적인 선택
REST 구성 요소
1. 자원 (Resource) : URL
- 모든 자원에 고유한 ID가 존재하고 이 자원은 Server 존재
- 자원을 구별하는 ID는 ‘/group/:group_id’ 와 같은 HTTP URL
- Client는 URL을 이용해서 자원을 지정하고 해당 자원의 상태(정보)에 대한 조작을 Server에 요청
2. 행위 (Verb) : HTTP Method
- HTTP 프로토콜의 Method를 사용
- HTTP 프로토콜은 GET, POST, PUT, DELETE와 같은 Method를 제공
3. 표현 (Representation of Resource)
- Client가 자원의 상태(정보)에 대한 조작을 요청하면 Server는 이에 적절한 응답을 보냄
- REST에서 하나의 자원은 JSON, XML, TEXT, RSS 등의 여러 형태의 표현으로 나타낼 수 있음
- JSON 혹은 XML을 통해 데이터를 주고 받는 것이 일반적
REST의 특징
1. Server-Client (서버-클라이언트 구조)
- 자원이 있는 쪽이 Server, 자원을 요청하는 쪽이 Client
REST Server : API를 제공하고 비즈니스 로직 처리 및 저장을 책임
Client : 사용자 인증이나 Context(세션, 로그인 정보) 등을 직접 관리하고 책임
- 상호간 의존성 줄어듬
2. Stateless (무상태)
- HTTP 프로토콜은 Stateless Protocol이므로 REST 역시 무상태성을 가짐
- 무상태성이란 작업을 위한 상태정보를 따로 저장하고 관리하지 않음 (세션 정보나 쿠키 정보를 별도로 저장하고 관리하지 않기 때문에 API Server는 들어오는 요청만 단순히 처리하면 됨)
- Client와 Context를 Server에 저장하지 않음 (세션과 쿠키와 같은 Context 정보를 신경쓰지 않아도 되므로 구현이 단순해짐)
- Server는 각각의 요청을 완전히 별개의 것으로 인식하고 처리
각 API 서버는 Client의 요청만을 단순 처리
이전 요청이 다음 요청의 처리에 연관되어서는 안된다는 것 (이전 요청이 DB를 수정하여 DB에 의해 바뀌는 것은 허용)
Server의 처리방식에 일관성을 부여하고 부담이 줄어들어 서비스 자유도가 높아짐
3. Cacheable (캐시 처리 기능)
- 웹 표준 HTTP Protocol을 그대로 사용하므로 웹에서 사용하는 기존의 인프라를 그대로 활용 가능
HTTP가 가진 가장 강력한 특징 중 하나인 캐싱 기능 적용할 수 있음
HTTP Protocol 표준에서 사용하는 Last-Modified 태그나 E-Tag를 이용하면 캐싱 구현이 가능
- 대량의 요청을 효율적으로 처리하기 위해 캐시가 요구
- 캐시 사용을 통해 응답시간이 빨라지고 REST Server 트랙잭션이 발생하지 않기 때문에 전체 응답시간, 성능, 서버의 자원 이용률을 향상시킬 수 있음
4. Layered System (계층화)
- Client는 REST API Server만 호출
- REST Server는 다중 계층으로 구성될 수 있음
API Server는 순수 비즈니스 로직을 수행하고 그 앞단에 보안, 로드밸런싱, 암호화, 사용자 인증 등을 추가하면 구조상의 유연성을 줄 수 있음
로드밸런싱, 공유 캐시 등을 통해 확장성과 보안성을 향상시킬 수 있음
- Proxy, Gateway 같은 네트워크 기반의 중간 매체를 사용할 수 있음
5. Self-descriptiveness (자체 표현 구조)
- REST API 메시지만 보고도 이를 쉽게 이해할 수 있는 자체 표현 구조로 되어 있음
- Server로부터 스크립트를 받아서 Client에서 실행
- 반드시 사용할 필요 없음
6. Uniform Interface (인터페이스 일관성)
- URL로 지정한 Resource에 대한 모든 조작이 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일
- HTTP 표준 프로토콜에 따르는 모든 플랫폼에서 사용 가능 (특정 언어나 기술에 종속되지 않음)
REST의 단점
- HTTP 메소드의 한계에 묶임
REST API는 HTTP 메소드를 이용하여 URL에 대한 행위를 정의하는데 따라서 간단한 수준의 메소드만 지원할 수 있음 (예를 들어 메일을 보낸다라는 행위는 단일 메소드로 구현하기 쉽지않음)
- 표준이 없어서 관리하기 어려움
REST API는 API 설계 가이드일뿐 명시적인 표준이 아님, 따라서 관리가 어렵고 좋은 API 디자인에 대한 가이드가 쉽지 않음, 어떤 가이드는 특정한 REST API에는 맞지만 또 다른 곳에서는 맞지 않을 수 있음
- RDMS와의 관계
REST API를 RDBMS에 적극적으로 사용하기 위해서는 RESTful한 테이블 구조가 필요하게 된는데 이것보다는 NoSQL 쪽이 더 잘 맞는 추세
REST API
- HTTP 통신에서 어떤 자원에 대한 CRUD요청을 Resource와 Method로 표현하여 특정한 형태로 전달하는 방식
RESTful
- REST API의 설계 의도를 정확하게 지켜주는 API를 RESTful 하다라고 표현
- RESTful한 API는 구성요소들의 역활이 명확하게 분리되어 있어야 함
- URL은 자원을 정확하고 인식하기 편하게 표현하는데에 집중하고 지우너에 대한 행위는 Uniform하게 HTTP 메소드를 통해 정의해야함