목표: apache mod_proxy를 통한 부드러운 failover
검색해봐도 마땅히 참고할 만한게 없었다.
graceful failover
seamless failover
느낀점은
L4처럼 부드럽게 넘어가는 failover를 하기에는 apache로는 무리가 있다.
failover는 HAproxy사용하는 것이 좋은것 같다.
failover를 위해 몇가지 load balancing 옵션을 가지고 테스트를 해보았다.
apache의 failover 방식
health check 를 통해 failover 하는 방법을 찾았지만 apache에는 아직 없다.
apache는 connection 을 만들때 마다 node를 확인하여 failover 하도록 되어있다.
그래서 아래와 같은 옵션들과 설정도 통하지 않는다.
BalancerMember의 ping 옵션
mod_proxy_hcheck 사용
https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
https://httpd.apache.org/docs/2.4/mod/mod_proxy_hcheck.html
기본적으로 load balnacing을 구성하고 한쪽 서버를 down시키면 어떻게 될까?
(Apache mod_proxy를 이용한 reverse proxy와 로드밸런싱 참고)
1번 서버를 죽인다.
2번 서버에 붙었던 사용자들은 계속 서비스가 된다.
1번 서버에 붙었던 사용자들은 한참을 기다리게 되고 (60초 이상) 2번 서버로 연결되게 된다.
failover가 되기 위해서는 down된 node를 알고 요청을 보내지 않아야 한다.
위에 쓴것 처럼 apache는 connection 마다 node의 status를 확인한다.
사용중에 서버가 내려가면 서버의 요청을 계속 기다리게 되고 기본 timeout 시간 60초 후에 500, 503 응답을 주고 해당 node는 error status가 된다.
mod_proxy.c
if (access_status == OK
|| apr_table_get(r->notes, "proxy-error-override"))
break;
else if (access_status == HTTP_INTERNAL_SERVER_ERROR) {
/* Unrecoverable server error.
* We can not failover to another worker.
* Mark the worker as unusable if member of load balancer
*/
if (balancer
&& !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
worker->s->status |= PROXY_WORKER_IN_ERROR;
worker->s->error_time = apr_time_now();
}
break;
}
else if (access_status == HTTP_SERVICE_UNAVAILABLE) {
/* Recoverable server error.
* We can failover to another worker
* Mark the worker as unusable if member of load balancer
*/
if (balancer
&& !(worker->s->status & PROXY_WORKER_IGNORE_ERRORS)) {
worker->s->status |= PROXY_WORKER_IN_ERROR;
worker->s->error_time = apr_time_now();
}
}
서비스할 수 없는 서버의 상태는 다음과 같다.
서비스중지: connection 실패
어플리케이션 에러 : 500 번대 에러
인증실패 등 client 문제 : 400 번대 에러
httpd 에서는 500, 502 에러는 서비스할 수 없는 상태로 보고 node의 status를 error 상태로 바꾼다.
문제는 connection이 이미 연결된 worker들이 error status 의 node로 request를 보내는데 있다.
timeout(기본 60초) 시간까지 응답을 기다리고 해당 node는 error status 가 된다.
이후에 error상태의 node로는 retry시간 까지 요청을 보내지 않고 다른 서버로 요청을 보낸다.
사용자는 서버가 내려갔는지도 느낄수 없게 부드러운 failover를 apache httpd를 통해 하고 싶었다.
apache 문서에는 session복제가 안되는 환경에서 sticky 세션을 사용하려면 failover 하지 않도록 권장하고 있다. ProxySet nofailover=On
If set to On
, the session will break if the worker is in error state or disabled. Set this value to On
if backend servers do not support session replication.
그러기 위해서는 성능을 생각하지 않고 worker들이 요청할 때마다 connection을 생성하도록 하고 그때 connectiontimeout으로 체크하게 하면 될것이라고 생각했다.
prefork를 사용하고 있었다. ({APACHE_HOME}/bin/httpd -V 로 확인할 수 있다.)
conf/extra/httpd-mpm.conf
<IfModule mpm_prefork_module>
StartServers 1
MinSpareServers 1
MaxSpareServers 1
MaxRequestWorkers 250
MaxConnectionsPerChild 0
</IfModule>
아래 값을 모두 1로 주면 요청할 때마다 프로세스가 생기고 그때 connection을 생성하면서 node의 연결상태를 체크하여 failover가 될것이라고 기대 했다.
MinSpareServers
MaxSpareServers
잘 안된다.
(추가)
prefork 방식은 아래 설정을 추가한다. (connection 유지안되도록 하는 설정값)
SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1
mpm을 worker로 다시 컴파일 해보기로 했다.
./configure --prefix=/usr/local/apache2-lb --enable-proxy --enable-proxy-balancer --with-mpm=worker --with-pcre=/usr/local/pcre --with-apr=/usr/local/apr
conf/extra/http-mpm.conf
<IfModule mpm_worker_module>
StartServers 3
MinSpareThreads 1
MaxSpareThreads 1
ThreadsPerChild 25
MaxRequestWorkers 400
MaxConnectionsPerChild 0
</IfModule>
min, max를 1로 설정
httpd.conf
<Proxy balancer://mycluster>
# WAS1
BalancerMember http://211.63.24.75:19090 route=1 connectiontimeout=1
# WAS2
BalancerMember http://211.63.24.75:29090 route=2 connectiontimeout=1
ProxySet lbmethod=byrequests
ProxySet stickysession=ROUTEID
</Proxy>
1번 서버를 내리면 1~2 초 후에 다른 서버로 이동 되는것을 확인할 수 있다.
session clustinrg 환경을 구성했다면 stateless 한 서비스라면 사용자는 모르고 지나갈 수 있다.
위에 설정에서 timeout을 넣지않고 connectiontimeout 을 넣은 이유는 응답이 오래 걸리는 서비스가 있을 수 있기 때문이다.
해당 설정을 timeout=1 로 해도 똑같이 동작하는 것으로 보일수 있다.
connectiontimeout의 기본값은 timeout 값을 사용하기 때문이다.
하지만 두가지 옵션의 차이가 있다.
1. connectiontimeout 는 connection이 생성될 때 timeout시간을 설정한다. 1초이상 연결되지 않으면 node를 error status로 바꾼다.
2. timeout은 webserver에서 WAS로 요청이 간 후에 timeout 설정 이다.
응답이 오래 걸리는 서비스도 timeout 옵션으로 인해 응답은 실패하고 해당 node는 error status가 된다.
1번 설정을 하고 5초 sleep하는 서비스를 호출했을때 다른서버로 요청이 가지 않고 원래 서버로 요청되는것을 확인할 수 있다.
부족하지만 apache로도 어느정도 만족할 만한 failover를 할수 있었다.
※ 하지만 실제로 서비스 하기위해서는 connection 생성의 오버헤드를 고려해야 한다.
- apache 에서 권장하는 방법은 아니므로 서비스 구조와 결함 발생 가능성은 없는지 검토가 필요하다.
- 이중화 하는 이유는 크게 성능과 고가용성(유지보수) 인데 성능테스트를 통해 검증을 해봐야 한다.