다시 해 보다
서비스가 클라이언트로부터 요청을 받아 처리하고 요청이 실패하면 다시 전송하는 기능입니다.
경우에 따라 요청이 실패하면 클라이언트별로 요청 성공률을 높이기 위해 다시 보낼 수 있습니다. 그러나 클라이언트가 과도한 재시도를 수행하면 서비스가 이를 처리하지 못하여 과부하 상태 및 잠재적인 장애 증상이 발생할 수 있다는 점에 유의해야 합니다.
타임아웃
서비스 요청 처리 시간이 일정 시간 이상 소요될 경우 요청을 종료하는 기능입니다.
클라이언트와 서버 간의 통신이 끊기거나 지연되는 상황에서 클라이언트가 무한정 대기하는 것을 방지할 수 있습니다. 단, 처리 중인 요청을 종료하여 문제가 발생할 수 있으므로 애플리케이션의 특성에 따라 적절하게 조정 후 사용하여야 합니다.
실습을 통해 간단하게 테스트해 봅시다.
훈련
1. 테스트를 위한 클라이언트, 서버 사전 구성
요청을 수신하기 위해 nginx 서버를 배포합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
annotations:
linkerd.io/inject: enabled
spec:
containers:
- name: nginx
image: nginx:latest
nginx 서버 구성을 위한 패키지를 다운로드합니다.
$ kubectl expose service nginx --port=80
$ kubectl exec -it svc/nginx -c nginx -- /bin/bash
$ apt update
$ apt-get install build-essential libpcre3 libpcre3-dev zlib1g-dev libssl-dev vim wget
echo-nginx-module 모듈을 사용하기 위한 구성은 nginx 서버에서 요청에 대한 응답이 지연되도록 다음과 같습니다.
$ wget http://nginx.org/download/nginx-1.23.4.tar.gz
$ tar zxvf nginx-1.23.4.tar.gz
$ cd nginx-1.23.4
## nginx 1.23 버전에 호환되는 echo-nginx-module 다운
$ wget https://github.com/openresty/echo-nginx-module/archive/v0.63.tar.gz
$ tar -xvzf v0.63.tar.gz
## 모듈 컴파일
$ ./configure --with-compat --add-dynamic-module=../echo-nginx-module-0.63
make modules
cp objs/ngx_http_echo_module.so /etc/nginx/modules
이 설정은 /test 경로를 통해 요청이 들어오면 502 오류를 반환하고 /tmout 경로를 통해 요청이 오면 10초 후에 에코 메시지를 반환합니다.
$ vi /etc/nginx/conf.d/default.conf
server {
listen 80;
listen (::):80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /test {
return 502;
}
location /tmout {
echo_sleep 10;
echo "Hello, world!";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
$ nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ service nginx reload
nginx-client를 배포하여 동일한 nginx 이미지를 사용하여 요청을 보냅니다.
apiVersion: v1
kind: Service
metadata:
name: nginx-client
spec:
selector:
app: nginx-client
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-client
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-client
spec:
replicas: 1
selector:
matchLabels:
app: nginx-client
template:
metadata:
name: nginx-client
labels:
app: nginx-client
annotations:
linkerd.io/inject: enabled
spec:
containers:
- name: nginx-client
image: nginx
2. 재시도
ServiceProfile을 통해 nginx 서버의 /test 경로에 대한 재시도 설정 nginx 클라이언트에서 요청을 보내 로그를 확인합시다.
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: nginx.default.svc.cluster.local
namespace: default
spec:
routes:
- condition:
method: HEAD
pathRegex: /test
isRetryable: true ## Retry 설정
name: HEAD /test
$ kubectl logs svc/nginx -c nginx -f
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
192.168.6.169 - - (19/Apr/2023:03:52:49 +0000) "GET /test HTTP/1.1" 502 497 "-" "curl/7.74.0" "-"
약 1건의 요청 실패에 대해 재시도로 100건의 요청을 재시도한 것을 알 수 있습니다.
재시도 값은 아래 설정을 통해 세부적으로 설정할 수 있습니다.
spec:
retryBudget:
retryRatio: 0.2
minRetriesPerSecond: 10
ttl: 10s
- retryRatio : 최대 재시도 비율(0.2 = 20%)
- minRetriesPerSecond : retryRatio에서 허용하는 것 외에 초당 허용되는 재시도 횟수
- ttl: retryRatio 계산을 위해 요청이 고려되는 기간
3. 타임아웃
ServiceProfile을 통해 nginx 서버의 /tmout 경로에 대한 재시도 설정 nginx 클라이언트에서 요청을 보내봅시다.
1초의 딜레이가 발생하면 타임아웃되도록 하는 설정입니다.
apiVersion: linkerd.io/v1alpha2
kind: ServiceProfile
metadata:
name: nginx.default.svc.cluster.local
namespace: default
spec:
routes:
- condition:
method: GET
pathRegex: /test
isRetryable: true
name: GET /test
## timeout path에 timeout 설정 추가
- condition:
method: GET
pathRegex: /tmout
name: GET /tmout
timeout: 1000ms
$ kubectl exec -it svc/nginx-client -c nginx-client -- /bin/bash
$ curl nginx/tmout -vvv
* Trying 10.100.119.208:80...
* Connected to nginx (10.100.119.208) port 80 (#0)
> GET /tmout HTTP/1.1
> Host: nginx
> User-Agent: curl/7.74.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 504 Gateway Timeout
< l5d-proxy-error: HTTP response timeout after 1s
< connection: close
< l5d-proxy-connection: close
< content-length: 0
< date: Wed, 19 Apr 2023 08:00:51 GMT
<
* Closing connection 0
1초 후 504 Gateway Timeout이 발생하여 연결이 끊긴 것을 확인할 수 있습니다.
결론
시간 초과 및 재시도는 서비스의 안정성과 가용성을 개선하는 데 큰 역할을 합니다. 타임아웃 시간이 너무 짧으면 서버에서 응답을 받더라도 클라이언트에서 오류가 발생할 수 있으며, 재시도 횟수나 간격이 너무 짧거나 크면 서버의 부하가 가중될 수 있다. . 따라서 이러한 기능을 설정할 때에는 어플리케이션의 특성에 따라 신중히 고려하여 적용하여야 합니다.