내 마음대로 네트워크 시리즈 - 5

김경준·2025년 12월 6일

네트워크

목록 보기
5/8

개요

원래 'AWS 환경에서 많이 사용되는 NLB, ALB를 사이에 두고 테스트랑 패킷 분석'을 해야하지만, 백엔드 서버에서 timeout 설정도 확실히 해두면 좋을 것 같아서
timeout일 때도 패킷 분석을 해보려고 한다.

백엔드 서버에 timeout 적용

소스코드 작성

일단 아래 이미지를 사용하면 된다.

docker pull ghcr.io/kyeongjun-dev/network:dev

app/app.py/에 추가로 /slow 엔드포인트를 추가했다. 실제 호출할 때는 /slow?wait=5 이런 식으로 wait 매개변수를 전달한다.

from flask import Flask, request
import time

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello from Flask App behind Gunicorn!"

@app.route('/slow')
def slow():
    try:
        wait_time = request.args.get('wait', default=10, type=int)
    except ValueError:
        # 쿼리 파라미터가 정수가 아닌 경우 10초를 사용합니다.
        wait_time = 10

    print(f"Request received. Busy waiting for {wait_time} seconds...")
    # CPU를 계속 사용하는 반복문 사용
    end_time = time.time() + wait_time
    while time.time() < end_time:
        pass  # 아무것도 안 하지만 CPU는 계속 씀 (Yield 안 함)

    print(f"Waited {wait_time} seconds. Sending response now.")
    return f"Finally, here is your slow response..."

timeout이 나는 원리

gunicorn을 아래와 같이 실행할 때, --timeout을 설정한다.

gunicorn --workers 1 -k gevent --bind 0.0.0.0:8000 --timeout 10 --keep-alive 10 app:app --log-level debug

이때 /slow?wait=초로 전달하는 초가 --timeout에 설정한 초보다 크면, 백엔드 서버에서 woker timeout이 발생한다.

실제로 실행시킨 후, curl로 11초를 지정해서 호출하면 WORKER TIMEOUT을 확인할 수 있다.

timeout 패킷 캡처해보기

docker compose로 테스트하기 (소스코드 : 레포 08 디렉토리)

아래 docker-compose를 08 디렉토리에서 실행한다.

services:
  server:
    image: ghcr.io/kyeongjun-dev/network:dev
    hostname: server
    container_name: server
    volumes:
      - ./server_vol:/app/captures
    ports:
      - "8000:8000"
    command: ["gunicorn", "--workers", "1", "-k", "gevent", "--bind", "0.0.0.0:8000", "--timeout", "10", "--keep-alive", "10", "app:app", "--log-level", "debug"]

  client:
    image: ghcr.io/kyeongjun-dev/network:dev
    container_name: client
    volumes:
      - ./client_vol:/app/captures
    command: ["sleep", "infinity"]
    environment:
      HOST: server
      PORT: 8000
      HOST_HEADER: server
      USE_TLS: false
      ENDPOINT: /slow?wait=11

이제 빌드해둔 이미지가 있으므로, ghcr에 올려놓은 이미지를 사용한다. (-d는 백그라운드 실행)

docker-compose up -d

tcpdump를 실행하고

docker exec -it client tcpdump -i any -n 'port 8000' -w /app/captures/client.pcap
docker exec -it server tcpdump -i any -n 'port 8000' -w /app/captures/server.pcap

client에서 server로 요청을 전송한다

docker exec -it client python client.py 11

server 로그를 확인해보면, 08:00:54에 GET 요청을 받은 후, 약 10초 뒤인 08:01:05WORKER TIMEOUT이 발생했다.

패킷 분석하기

client가 172.19.0.3, server가 172.19.0.2

간단히 분석해보면
1. 0초 : tcp 연결 완료
2. 0초대에 /slow?wait=11로 GET 요청 전송
3. (패킷에 안보임) 10초 대에 WORKER TIMEOUT으로 워커 종료
4. 20초 : --keep-alive 10 설정에 의해 10초 뒤인 20초대에 client에 FIN 전송

client(172.19.0.3), server(172.19.0.2)--keep-alive를 20초로 변경한 후, 테스트 했을 때의 패킷은 아래와 같다. 즉, FIN 패킷을 전송할 때까지 걸리는 시간은 timeout + keep alive 타임이다.

위를 보면, 워커가 작업을 수행하는 시간은 keep alive에 포함되지 않는 것 같다.

정리 및 다음 글에서는...

timeout까지 고려하려니 너무 복잡해지는 거 같다. 다음 글 부터는 /slow를 이용해 timeout은 후순위로 고려하려고 한다.
다음 글에서는 AWS 환경 EKS에 nlb를 두고, 패킷 테스트를 해보자.
(이제 진짜 돈이 들겠네...)

profile
DevOps로 일하고 있습니다

0개의 댓글