레이블이 개비스콘인 게시물을 표시합니다. 모든 게시물 표시
레이블이 개비스콘인 게시물을 표시합니다. 모든 게시물 표시

wsgidav(webdav) session hang

# k8s ingress > webdav(https://github.com/mar10/wsgidav) pod 환경으로 사용 중 수천개의 파일을 업로그 하는경우 같이 다수의 요청이 발생하면 hangup(응답없음) 현상이 발생한다.

# webdav 테스트이미지(htop, netstat 등 추가)를 새로 만들고 적용해서 모니터링해보자.
# webdav 설정 thread=10 개라 10개의 파이썬 프로세스에서 10개의 tcp session 까지 처리할 수 있는 상태다.
# 도메인으로 요청시 세션(tcp ESTABLISHED)이 늘어났지만 세션 종료는 안돼 새 연결 요청을 받지 못하는것으로 보인다.

# webdav 용 클라이언트 코드에 ysoftman_test:8080 로 하드코딩하고 /etc/host 에 아래와 같이 호스트를 추가하고
10.10.10.10  ysoftman_test

# 아래와 같이 요청을 계속 날리고 webdav 서버 container netstat -nat 로 보면 세션이 늘어나지 않고 잘 처리됐다.
watch -n 0.1 "httpstat http://ysoftamn:abc123@ysoftman_test"

# 위와 같이 ingress(Ingress NGINX Controller)를 경유하지 않으면 된다.
# 그럼 nginx controller 에서 뭔 keepalive 와 같은 일정 시간 연결을 유지하는것 때문이지 않을까?
# controller 의 confimap 에 keepalive 를 사용하지 않도록 하는 설정이 있다.
# ingress nginx controller configmap > data 에 다음 설정을 추가하면 세션 keepalive 를 사용하지 않는다.(요청 완료된 세션 종료)
# 하지만 configmap 설정이라 ingress 를 사용하는 모드 서비스에 영향을 주어 테스트만하고 실제 적용은 하지 않았다. 
upstream-keepalive-connections: "0"

# 참고로 http 1.0 에서는 요청 처리후 연결을 끊지만 http 1.1 부터는 keepalive 가 기본으로 tcp 연결을 일정시간동안 유지한다.
In HTTP/1.1, persistence is the default, and the header is no longer needed (but it is often added as a defensive measure against cases requiring a fallback to HTTP/1.0).

# webdav 에서도 0.4.0 b1 부터 http1.1 을 사용하고 있었다.
0.4.0.b1
- Using HTTP/1.1 with keep-alive (Stéphane Klein)

# 사실 keepalive 는 파일 전송과 같이 지속적인 연결이 필요한 경우 효율성이 좋지만 새로운 연결(요청)은 세션이 부족할 경우 대기하는게 문제다.
# 예를 들어 수천개의 파일을 동시에 올릴때 thread=10개라면 10개는 처리되지만 이후 요청은 10개의 keepalive 가 종료(세션종료)때까지 대기해야 하는 문제가 있다.
# wsgidav 은 numthreads 개수 만큼 파이썬 프로세스가 떠서 요청을 받는 구조인데 numthreads=1000 처럼 늘리면 CPU 사용량은 늘어나겠지만 더 많은 새로운 요청을 받아 줄 수 있다.
# numthreads=1000 로 이미지를 새로 만들고 httpstat 테스트하면 세션수가 일정한 범위내에서 더 늘어나지 않는다.

# 실제 클리이언트를 사용해서 테스트
# 클라이언트는 10개의 worker thread 로 수만의 파일을 업로드 하고 있고 
# 서버의 가능 세션수가 1000 개인 상태에서 업로드를 수행하면
# 앞선 완료된 일정 시간 후 세션 클로즈 되고 다시 사용할 세션수 있는 세션수가 늘어나기 때문에 세션수가 일정하게 유지하면서 업로드를 할 수 있었다.
# 그리고 pod cpu 리소스는 최소 2000m 이상 잡아줘야 쓰레드를 충분히 사용하는것 같다.

# 요것도 원인 찾느라 힘들어서 개비스콘 짤 생생~ㅋ

webdav chunked size

# python upload/download 시 tqdm 으로 progressbar 표시한다.
# download 는 resp=requests.get(... stream=True) 와 resp.iter_content 로 스트림으로 받는 있는 데이터 사이즈를 파악할 수 있다.
# upload 는 async 와 async with aiohttp.ClientSession() 를 사용해 표시한다.
# tqdm 참고
# 그런데 upload(aiohttp > seesion > put) 시 http://httpbin.org/put 는 동작되는데
# webdav(http의 확장으로 COPY, MOVE, MKCOL(mkdir), PROPFIND(조회) 등 파일관리 메소드들 제공) 서버(https://github.com/mar10/wsgidav)에서는
# 다음 코드에서 진행되지 않고 CPU 100%를 사용하는 문제가 발생했다.
# 이 상태에서 요청을 계속 하면 서버 행 상태가 된다.
buf = environ["wsgi.input"].readline()

# 관련해서 비슷한 이슈가 오래전에 있어 수정이 되었다.
# chunk 사이즈 명시를 누락하는것 rfc2616(https://datatracker.ietf.org/doc/html/rfc2616#section-3.6.1) 명세를 위한반것으로 예외케이스를 추가한것으로 보인다.

# 클라이언트에서 X_EXPECTED_ENTITY_LENGTH 로 전체보낼 크기를 명시하고
# User-Agent 를 Darwin 으로 명시한다.
headers = {}
# headers["X_EXPECTED_ENTITY_LENGTH"] = repr(os.fstat(fileobj.fileno()).st_size) # fileobject case
headers["X_EXPECTED_ENTITY_LENGTH"] = repr(os.path.getsize(filename))
headers["User-Agent"] = "Darwin"

# 이제 put 을 하면 webdav 의 _stream_data_chunked > Darwin 조건에 다음과 같이 버크 크기를 알수 있어 진행된다.
buf = environ.get("HTTP_X_EXPECTED_ENTITY_LENGTH", "0")

# aiohttp 는 기본 Agent 가 다음과 같았고,
Python/3.9 aiohttp/3.7.4.post0

# 별도 chunk 크기를 명시하는 것이 없다. 
# 암튼 mar10 의 wsgidav chunk put 처리에는 예외 케이스가 있어
# 위처럼 클라이언트를 수정하던 wsgidav 서버 조건을 수정해야 한다.

# 참고로 User-Agent 없이 X_EXPECTED_ENTITY_LENGTH 만 설정하면 좋을것 같아서 문의함

# 이것땜에 한참 고생함ㅠㅠ, 힘들게 원인 파악한 기념으로 개비스콘 짤 생성~ㅋ

k8s ingress-controller model

k8s 에서 설정된 특정(ysoftman.lemon.com) 호스트에 대해 여러개의 인그레스가 있고
각 인그레스는 default backend (path rule 에 없는 경우 처리할 백엔드 서비스 설정) 가 설정되어 있다.
이 중 A 인그레스의 default backend 의 설정 또는 삭제 변경에 대해서만 ingress-nginx-controller 에 반영되어 처리되는 현상이 있어 찾아봤다.

참고

위 링크를 보면 클러스터 전반에 대한 셋팅으로 nginx model(설정이 메모리에 로딩된 상태를 의미하는것 같다.)이라는 것이 있다. 이 모델은 생성(빌드)에 비용이 많이 들어 특정 작업이 있을때만 빌드(로드된다.)
그리고 모델 생성시 규칙이 있는데 여러 인그레스 중 같은 host, path 등을 가지고 있다면 가장 오래된(최초로 만들어진) 인그레스의 설정이 우선한다.

모델 생성시 오래된 rule 우선 규칙으로 인해 가장 오랜된 A 인그레스의 설정만
ingress-nginx-controller 의 default backend 설정에 반영된다.

# 참고로 생성된지 오래된 순으로 ingress 파악
kubectl get ingress --sort-by=.metadata.creationTimestamp 

원인 파악하느라 너무 삽질을 많이 했다. 
이제 속이 후련해서 개비스콘 짤 생성~ㅋ (https://gvsc.rajephon.dev/)