[NET-008][중급] 프록시와 로드밸런서 흐름 이해하기
페이지 정보

본문
[이번 수업]
프록시는 요청과 응답의 중개자입니다. 세 종류를 방향과 책임으로 구분하고, 로컬 입구가 두 서버에 요청을 번갈아 보내는 흐름을 확인합니다.
[선수지식]
NET-007의 HTTP 요청·응답, URL, 상태 코드와 포트를 알아야 합니다.
[학습목표]
1. 정방향 프록시와 리버스 프록시의 위치를 설명할 수 있습니다.
2. 로드밸런서의 라운드 로빈 방식을 이해할 수 있습니다.
3. 전달 헤더와 백엔드 주소를 안전하게 다룰 수 있습니다.
[핵심개념]
정방향 프록시는 클라이언트를 대신해 외부로 요청합니다. 리버스 프록시는 원본 서버처럼 보이는 입구로서 요청을 내부 백엔드에 전달합니다. HTTP 표준에서는 이를 게이트웨이라고도 부릅니다.
로드밸런서는 여러 백엔드 중 보낼 곳을 고릅니다. 라운드 로빈은 목록을 순환하고, 최소 연결 방식은 연결이 적은 서버를 고릅니다. 운영에서는 헬스 체크, 연결 제한, 시간 초과가 필요합니다. 상태를 바꾸는 요청을 재시도하면 중복 실행될 수 있습니다.
흐름은 `클라이언트 → 리버스 프록시·로드밸런서 → 백엔드 A 또는 B`입니다.
[따라하기]
아래를 local_proxy.py로 저장하세요. 로컬 주소와 자동 선택 포트만 쓰는 학습용 예제입니다.
```python
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread
from urllib.request import urlopen
def app(name):
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = f"{name} served {self.path}".encode()
self.send_response(200); self.end_headers(); self.wfile.write(body)
def log_message(self, *args): pass
return Handler
backends = [HTTPServer(("127.0.0.1", 0), app(n)) for n in ("app-a", "app-b")]
targets = [f"http://127.0.0.1:{s.server_port}" for s in backends]
class Proxy(BaseHTTPRequestHandler):
index = 0
def do_GET(self):
target = targets[Proxy.index % 2]; Proxy.index += 1
body = urlopen(target + self.path).read()
self.send_response(200); self.end_headers(); self.wfile.write(body)
def log_message(self, *args): pass
front = HTTPServer(("127.0.0.1", 0), Proxy)
for server in [*backends, front]:
Thread(target=server.serve_forever, daemon=True).start()
for _ in range(4):
print(urlopen(f"http://127.0.0.1:{front.server_port}/hello").read().decode())
for server in [front, *backends]:
server.shutdown(); server.server_close()
```
Windows는 `py local_proxy.py`, macOS와 Linux는 `python3 local_proxy.py`로 실행합니다. 결과입니다.
```text
app-a served /hello
app-b served /hello
app-a served /hello
app-b served /hello
```
이는 입구 하나가 라운드 로빈으로 두 백엔드를 고른 결과입니다. Python의 http.server는 기본 보안 검사만 제공하므로 운영용이 아닙니다.
[흔한 실수]
첫째, 프록시와 백엔드의 시간 초과를 따로 설계하지 않습니다. 둘째, 처리 시간이 다른데 라운드 로빈이면 부하가 같다고 생각합니다. 셋째, 한 서버 메모리에만 로그인 세션을 저장해 다른 서버로 갈 때 로그인이 풀립니다.
[보안 주의]
본인 소유 로컬 환경에서만 실행하세요. 사용자가 목적지 주소를 정하면 내부 주소로 요청하는 SSRF 위험이 있으므로 백엔드는 허용 목록으로 고정합니다. 외부의 `Forwarded`나 `X-Forwarded-For`를 신뢰하지 말고, 신뢰하는 앞단에서 제거하거나 검증해 다시 설정하세요. IP 수집과 보관을 제한하고 백엔드 구간에도 인증·암호화·접근 통제를 적용합니다.
[직접 해볼 과제]
백엔드를 app-c까지 늘리고 `% 2`를 서버 수에 맞게 고쳐 6번 요청하세요. 세 서버가 두 번씩 출력되는지 확인하고 느린 서버가 있을 때 라운드 로빈의 한계를 설명하세요.
[확인문제]
1. 정방향 프록시와 리버스 프록시는 각각 누구를 대신하나요?
2. 라운드 로빈은 다음 백엔드를 어떻게 고르나요?
3. 외부에서 받은 전달 헤더를 그대로 신뢰하면 안 되는 이유는 무엇인가요?
[다음 학습]
다음 CLOUD-008에서는 서버리스와 관리형 서비스를 고르는 기준을 배웁니다.
[공식 참고 자료]
HTTP 중개자와 게이트웨이 표준: https://www.rfc-editor.org/rfc/rfc9110.html
Forwarded 헤더 표준과 보안 고려사항: https://www.rfc-editor.org/rfc/rfc7239.html
HTTP 로드밸런싱 공식 설명서: https://nginx.org/en/docs/http/load_balancing.html
Python 학습용 HTTP 서버 주의사항: https://docs.python.org/3/library/http.server.html
프록시는 요청과 응답의 중개자입니다. 세 종류를 방향과 책임으로 구분하고, 로컬 입구가 두 서버에 요청을 번갈아 보내는 흐름을 확인합니다.
[선수지식]
NET-007의 HTTP 요청·응답, URL, 상태 코드와 포트를 알아야 합니다.
[학습목표]
1. 정방향 프록시와 리버스 프록시의 위치를 설명할 수 있습니다.
2. 로드밸런서의 라운드 로빈 방식을 이해할 수 있습니다.
3. 전달 헤더와 백엔드 주소를 안전하게 다룰 수 있습니다.
[핵심개념]
정방향 프록시는 클라이언트를 대신해 외부로 요청합니다. 리버스 프록시는 원본 서버처럼 보이는 입구로서 요청을 내부 백엔드에 전달합니다. HTTP 표준에서는 이를 게이트웨이라고도 부릅니다.
로드밸런서는 여러 백엔드 중 보낼 곳을 고릅니다. 라운드 로빈은 목록을 순환하고, 최소 연결 방식은 연결이 적은 서버를 고릅니다. 운영에서는 헬스 체크, 연결 제한, 시간 초과가 필요합니다. 상태를 바꾸는 요청을 재시도하면 중복 실행될 수 있습니다.
흐름은 `클라이언트 → 리버스 프록시·로드밸런서 → 백엔드 A 또는 B`입니다.
[따라하기]
아래를 local_proxy.py로 저장하세요. 로컬 주소와 자동 선택 포트만 쓰는 학습용 예제입니다.
```python
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread
from urllib.request import urlopen
def app(name):
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
body = f"{name} served {self.path}".encode()
self.send_response(200); self.end_headers(); self.wfile.write(body)
def log_message(self, *args): pass
return Handler
backends = [HTTPServer(("127.0.0.1", 0), app(n)) for n in ("app-a", "app-b")]
targets = [f"http://127.0.0.1:{s.server_port}" for s in backends]
class Proxy(BaseHTTPRequestHandler):
index = 0
def do_GET(self):
target = targets[Proxy.index % 2]; Proxy.index += 1
body = urlopen(target + self.path).read()
self.send_response(200); self.end_headers(); self.wfile.write(body)
def log_message(self, *args): pass
front = HTTPServer(("127.0.0.1", 0), Proxy)
for server in [*backends, front]:
Thread(target=server.serve_forever, daemon=True).start()
for _ in range(4):
print(urlopen(f"http://127.0.0.1:{front.server_port}/hello").read().decode())
for server in [front, *backends]:
server.shutdown(); server.server_close()
```
Windows는 `py local_proxy.py`, macOS와 Linux는 `python3 local_proxy.py`로 실행합니다. 결과입니다.
```text
app-a served /hello
app-b served /hello
app-a served /hello
app-b served /hello
```
이는 입구 하나가 라운드 로빈으로 두 백엔드를 고른 결과입니다. Python의 http.server는 기본 보안 검사만 제공하므로 운영용이 아닙니다.
[흔한 실수]
첫째, 프록시와 백엔드의 시간 초과를 따로 설계하지 않습니다. 둘째, 처리 시간이 다른데 라운드 로빈이면 부하가 같다고 생각합니다. 셋째, 한 서버 메모리에만 로그인 세션을 저장해 다른 서버로 갈 때 로그인이 풀립니다.
[보안 주의]
본인 소유 로컬 환경에서만 실행하세요. 사용자가 목적지 주소를 정하면 내부 주소로 요청하는 SSRF 위험이 있으므로 백엔드는 허용 목록으로 고정합니다. 외부의 `Forwarded`나 `X-Forwarded-For`를 신뢰하지 말고, 신뢰하는 앞단에서 제거하거나 검증해 다시 설정하세요. IP 수집과 보관을 제한하고 백엔드 구간에도 인증·암호화·접근 통제를 적용합니다.
[직접 해볼 과제]
백엔드를 app-c까지 늘리고 `% 2`를 서버 수에 맞게 고쳐 6번 요청하세요. 세 서버가 두 번씩 출력되는지 확인하고 느린 서버가 있을 때 라운드 로빈의 한계를 설명하세요.
[확인문제]
1. 정방향 프록시와 리버스 프록시는 각각 누구를 대신하나요?
2. 라운드 로빈은 다음 백엔드를 어떻게 고르나요?
3. 외부에서 받은 전달 헤더를 그대로 신뢰하면 안 되는 이유는 무엇인가요?
[다음 학습]
다음 CLOUD-008에서는 서버리스와 관리형 서비스를 고르는 기준을 배웁니다.
[공식 참고 자료]
HTTP 중개자와 게이트웨이 표준: https://www.rfc-editor.org/rfc/rfc9110.html
Forwarded 헤더 표준과 보안 고려사항: https://www.rfc-editor.org/rfc/rfc7239.html
HTTP 로드밸런싱 공식 설명서: https://nginx.org/en/docs/http/load_balancing.html
Python 학습용 HTTP 서버 주의사항: https://docs.python.org/3/library/http.server.html
- 이전글[CLOUD-008][중급] 이벤트로 서버리스 함수 실행하기 26.09.03
- 다음글[OS-008][중급] 로그로 장애 원인 좁히기 26.09.03
댓글목록
등록된 댓글이 없습니다.
