Overview
haproxy에 대해서 알아본다.
haproxy란?
High Availability Proxy(고가용성 프록시)를 의미하는 HAProxy는 널리 사용되는 오픈 소스 소프트웨어인 TCP/HTTP 로드 밸런서 및 프록싱 솔루션이다. 고성능, 안정성, 낮은 메모리 공간은 물론 수만 개의 동시 연결을 처리할 수 있는 능력으로 잘 알려져 니다. HAProxy는 Linux, Solaris 및 FreeBSD 운영 체제에서 실행할 수 있다.
주요 특징
- 로드 밸런싱(Load Balancing): HAProxy는 워크로드를 여러 서버에 분산하여 단일 서버가 요청으로 인해 압도당하지 않도록 함으로써 웹 애플리케이션의 성능과 안정성을 향상시킬 수 있다.
- 고가용성(High Availability): 장애 조치 기능을 제공하여 오류 발생 시 운영 서버로 트래픽을 자동으로 다시 라우팅하여 서비스의 지속적인 가용성을 지원한다.
- SSL/TLS 종료(SSL/TLS Termination): HAProxy는 SSL/TLS 연결을 종료하여 애플리케이션 서버에서 이 작업을 오프로드할 수 있다. 이를 통해 SSL 인증서를 중앙 집중식으로 관리할 수 있으며 성능이 향상된다.
- 상태 점검(Health Checks): 백엔드 서버에서 상태 점검을 수행하여 트래픽이 정상 서버로만 전송되는지 확인할 수 있다.
- 세션 지속성(Session Persistence): HAProxy는 세션 지속성을 유지하는 다양한 방법을 지원하여 세션 기간 동안 사용자 세션이 동일한 백엔드 서버로 유지되도록 보장한다.
- 보안 기능(Security Features): 다양한 기준에 따라 액세스를 제한할 수 있는 ACL(액세스 제어 목록) 및 DDoS 보호 메커니즘과 같은 여러 보안 기능을 제공한다.
- 모니터링 및 통계(Monitoring and Statistics): HAProxy는 자세한 모니터링 및 통계를 제공하여 트래픽 패턴, 성능 및 서버 상태에 대한 실시간 통찰력을 제공한다.
사용 사례
- 웹 애플리케이션 로드 밸런싱(Web Application Load Balancing): HTTP 요청을 서버 풀에 분산하여 로드 밸런싱을 조정하고 고가용성을 보장한다.
- 데이터베이스 부하 분산(Database Load Balancing): 여러 데이터베이스 서버에 걸쳐 데이터베이스 쿼리를 분산하여 성능과 가용성을 향상시킨다.
- SSL 오프로딩(SSL Offloading): HAProxy 계층에서 SSL/TLS 트래픽을 해독하여 웹 서버의 암호화 부하를 줄인다.
- GSLB(Global Server Load Balancing): 사용자 경험을 개선하기 위해 가장 가깝거나 성능이 가장 좋은 데이터 센터로 사용자 트래픽을 라우팅한다.
- 콘텐츠 캐싱 및 압축(Content Caching and Compression): 자주 액세스하는 콘텐츠를 캐싱하고 데이터를 압축하여 로드 시간과 대역폭 사용량을 줄인다.
아키텍처
Haproxy 로드벨런싱 알고리즘
HAProxy는 여러 로드 밸런싱 알고리즘을 지원하므로 애플리케이션의 특정 요구 사항과 동작을 기반으로 적절한 알고리즘을 선택할 수 있다. 이러한 알고리즘은 들어오는 요청이 백엔드 서버에 분산되는 방식을 결정한다. 그리고 전역적으로 또는 백엔드별로 적용할 수 있으므로 다양한 유형의 트래픽이 균형을 이루는 방식에 유연성을 제공한다. 알고리즘 선택은 애플리케이션의 요구 사항, 서버 용량 및 클라이언트 요청의 성격을 고려해야 한다.
# HAProxy 전역 설정
global
log stdout format raw local0
# 모든 프론트엔드/백엔드를 위한 기본 설정
defaults
log global
option httplog
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# 들어오는 요청을 위한 프론트엔드
frontend http_front
bind *:8080
mode http
stats uri /haproxy?stats
# health check 확인
acl health_check path /health
# 경로가 /game 인지 확인하는 ACL
acl path_game path_beg /game
# 조건에 따라 적절한 백엔드로 요청 라우팅
use_backend health_check_backend if health_check
use_backend game_back if path_game
# "/game" 경로를 처리하는 백엔드
backend game_back
mode http
balance roundrobin
# /game/x 요청이 /x로 변환되어 game 서비스로 전송됩니다.
http-request set-path %[path,regsub(^/game,/)]
server game game.dev1-game.svc.cluster.local:80 check
# 헬스 체크를 위한 백엔드 설정
backend health_check_backend
mode http
http-request return status 200 content-type text/plain string "OK"
- Round Robin (roundrobin): 가장 간단하고 가장 일반적인 로드 밸런싱 알고리즘이다. 들어오는 요청은 나열된 순서대로 백엔드 서버 전체에 순차적으로 배포된다. 목록이 끝나면 처음부터 다시 시작된다. 이 방법은 모든 서버가 처리 속도와 로드 처리 측면에서 동일하다고 가정한다.
- Least Connections (leastconn): 이 알고리즘은 활성 연결이 가장 적은 서버에 요청을 보낸다. 이는 라운드 로빈보다 더 정교하며 세션 처리 시간이 매우 다양한 상황에 더 적합하다. 이는 특히 교통량이 많은 상황에서 부하를 보다 균일하게 분산시키는 데 도움이 된다.
- Source (source): 소스 알고리즘은 소스 IP 주소의 해시를 기반으로 백엔드 서버를 선택한다. 이렇게 하면 백엔드 서버가 다운되거나 켜지지 않는 한 동일한 클라이언트 IP 주소의 요청이 항상 동일한 서버로 전달된다. 세션 지속성에 도움이 된다.
- URI(uri): 이 방법은 요청의 URI(Uniform Resource Identifier) 해시를 사용하여 사용할 백엔드 서버를 결정한다. 이렇게 하면 동일한 URI에 대한 모든 요청이 동일한 서버로 전송되므로 캐싱에 유용하다.
- Header (hdr): 헤더 알고리즘은 요청 헤더 중 하나의 해시를 기반으로 서버를 선택한다. 해시할 헤더를 지정할 수 있으므로 특정 헤더 값이 있는 요청이 동일한 서버에 고정되도록 하는 다목적 옵션이 된다.
- Random (random): 이 알고리즘은 각 요청에 대해 서버를 무작위로 선택한다. 특정 서버에 유리하도록 가중치를 적용할 수 있다. 이는 단순한 형태의 로드 분산을 제공하지만 서버 전체에 균일한 로드 분산을 보장하는 데는 효과적이지 않을 수 있다.
- Consistent Hashing (consistent): 일부 요청 속성(예: 소스 IP 또는 URI)의 해시를 기반으로 하는 이 알고리즘은 동일한 해시 값을 초래하는 요청이 항상 동일한 서버로 라우팅되도록 보장한다. 이는 캐시 지역성에 특히 유용하지만 서버 추가 또는 제거를 처리하려면 신중한 관리가 필요하다.
- First (first): 이 알고리즘은 특정 임계값 미만의 로드가 있는 것으로 식별된 첫 번째 서버로 요청을 라우팅한다. 특정 서버의 과부하를 방지하는 간단한 방법이다.
- Static-RR (static-rr): 서버의 정적 가중치를 허용하는 라운드 로빈의 변형이다. 가중치가 높은 서버는 더 많은 연결 비율을 받는다.
Haproxy 구성 실습
GCP에 Haproxy를 사용해서 하나의 도메인으로 2개의 Application을 MultiPath 구성을 해보려고 한다. GKE Autopilot을 사용하여 구성하였다. 아래와 같이 구성하면, 하나의 도메인으로 2개의 Application을 /mario /tetris 이렇게 배포가 가능하다.
apiVersion: v1
kind: Namespace
metadata:
name: mario-ns
---
apiVersion: v1
kind: Namespace
metadata:
name: tetris-ns
---
apiVersion: v1
kind: Namespace
metadata:
name: haproxy-ns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mario
namespace: mario-ns
spec:
replicas: 1
selector:
matchLabels:
app: mario
template:
metadata:
labels:
app: mario
spec:
containers:
- name: mario
image: pengbai/docker-supermario
---
apiVersion: v1
kind: Service
metadata:
name: mario
namespace: mario-ns
spec:
selector:
app: mario
ports:
- port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tetris
namespace: tetris-ns
spec:
replicas: 1
selector:
matchLabels:
app: tetris
template:
metadata:
labels:
app: tetris
spec:
containers:
- name: tetris
image: bsord/tetris
---
apiVersion: v1
kind: Service
metadata:
name: tetris
namespace: tetris-ns
spec:
selector:
app: tetris
ports:
- port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: v1
kind: ConfigMap
metadata:
name: haproxy-config
namespace: haproxy-ns
data:
haproxy.cfg: |
# HAProxy 전역 설정
global
log stdout format raw local0
# 모든 프론트엔드/백엔드를 위한 기본 설정
defaults
log global
option httplog
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# 들어오는 요청을 위한 프론트엔드
frontend http_front
bind *:8080
mode http
stats uri /haproxy?stats
# 헬스 체크 경로를 위한 ACL
acl health_check path /health
# 경로가 /mario 또는 /tetris로 시작하는지 확인하는 ACL
acl path_mario path_beg /mario
acl path_tetris path_beg /tetris
# /health 경로에 대한 헬스 체크 백엔드 사용
use_backend health_check_backend if health_check
# 조건에 따라 적절한 백엔드로 요청 라우팅
use_backend mario_back if path_mario
use_backend tetris_back if path_tetris
# "mario" 서비스를 제공하는 백엔드
backend mario_back
mode http
balance roundrobin
http-request set-path %[path,regsub(^/mario,/)]
server mario mario.mario-ns.svc.cluster.local:80 check
# "tetris" 서비스를 제공하는 백엔드
backend tetris_back
mode http
balance roundrobin
http-request set-path %[path,regsub(^/tetris,/)]
server tetris tetris.tetris-ns.svc.cluster.local:80 check
# 헬스 체크를 위한 백엔드 설정
backend health_check_backend
mode http
http-request return status 200 content-type text/plain string "OK"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: haproxy
namespace: haproxy-ns
spec:
replicas: 1
selector:
matchLabels:
app: haproxy
template:
metadata:
labels:
app: haproxy
spec:
containers:
- name: haproxy
image: haproxy:latest
ports:
- containerPort: 8080 # 컨테이너 포트를 8080으로 변경
volumeMounts:
- name: config-volume
mountPath: /usr/local/etc/haproxy/haproxy.cfg
subPath: haproxy.cfg
volumes:
- name: config-volume
configMap:
name: haproxy-config
---
apiVersion: v1
kind: Service
metadata:
name: haproxy
namespace: haproxy-ns
annotations:
cloud.google.com/neg: '{"ingress": true}'
cloud.google.com/backend-config: '{"default": "haproxy-backend-config"}'
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: haproxy
type: ClusterIP
---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: games-managed-certificate
namespace: haproxy-ns
spec:
domains:
- game.somaz.link
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: haproxy-ns
name: ingress-games
annotations:
kubernetes.io/ingress.global-static-ip-name: "mgmt-gke-game-lb-ip"
networking.gke.io/v1beta1.FrontendConfig: "games-frontend-config"
networking.gke.io/managed-certificates: "games-managed-certificate"
kubernetes.io/ingress.class: "gce"
spec:
rules:
- host: game.somaz.link
http:
paths:
- path: /mario
pathType: Prefix
backend:
service:
name: haproxy
port:
number: 80
- path: /tetris
pathType: Prefix
backend:
service:
name: haproxy
port:
number: 80
---
apiVersion: networking.gke.io/v1beta1
kind: FrontendConfig
metadata:
name: games-frontend-config
namespace: haproxy-ns
spec:
redirectToHttps:
enabled: true
---
apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
name: haproxy-backend-config
namespace: haproxy-ns
spec:
healthCheck:
checkIntervalSec: 30
timeoutSec: 5
healthyThreshold: 1
unhealthyThreshold: 2
type: HTTP
requestPath: /health
port: 8080
helm으로 작성하면 아래와 같다.
configs:
- name: haproxy-configmap
namespace: haproxy
datas:
haproxy.cfg: |
# HAProxy 전역 설정
global
log stdout format raw local0
# 모든 프론트엔드/백엔드를 위한 기본 설정
defaults
log global
option httplog
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
# 들어오는 요청을 위한 프론트엔드
frontend http_front
bind *:8080
mode http
stats uri /haproxy?stats
# 경로가 /game, /auth, /log, /health 인지 확인하는 ACL
acl health_check path /health
acl path_game path_beg /game
acl path_auth path_beg /auth
# acl path_log path_beg /log
# 조건에 따라 적절한 백엔드로 요청 라우팅
use_backend health_check_backend if health_check
use_backend game_back if path_game
use_backend auth_back if path_auth
# use_backend log_back if path_log
# 헬스 체크를 위한 백엔드 설정
backend health_check_backend
mode http
http-request return status 200 content-type text/plain string "OK"
# "/game" 경로를 처리하는 백엔드
backend game_back
mode http
balance roundrobin
# /game/x 요청이 /x로 변환되어 game 서비스로 전송됩니다.
http-request set-path %[path,regsub(^/game,/)]
server game game.dev1-game.svc.cluster.local:80 check
# "/auth" 경로를 처리하는 백엔드
backend auth_back
mode http
balance roundrobin
# /auth/x 요청이 /x로 변환되어 auth 서비스로 전송됩니다.
# http-request set-path %[path,regsub(^/auth/,/)]
# /auth/x 요청이 /auth/x로 변환되어 auth 서비스로 전송됩니다.
http-request set-path %[path,regsub(^/auth/auth/,/auth/)]
server auth auth.dev1-auth.svc.cluster.local:80 check
# "/log" 경로를 처리하는 백엔드
backend log_back
mode http
balance roundrobin
# /log/x 요청이 /x로 변환되어 log 서비스로 전송됩니다.
# http-request set-path %[path,regsub(^/log/,/)]
# /log/x 요청이 /log/x로 변환되어 log 서비스로 전송됩니다.
http-request set-path %[path,regsub(^/log/log/,/log/)]
server log log.dev1-log.svc.cluster.local:80 check
volumes:
- name: haproxy-config
configMap:
name: haproxy-configmap
items:
- key: haproxy.cfg
path: haproxy.cfg
구조는 아래와 같다.
graph LR
A[HAProxy] -->|/health| B[Health Check Backend]
A -->|/game/*| C[Game Backend]
A -->|/auth/*| D[Auth Backend]
A -->|/log/*| E[Log Backend]
B -->|200 OK| F["Return 'OK'"]
C -->|Round Robin| G[Game Service]
D -->|Round Robin| H[Auth Service]
E -->|Round Robin| I[Log Service]
G --> J(( ))
G --> K(( ))
G --> L(( ))
H --> M(( ))
H --> N(( ))
H --> O(( ))
I --> P(( ))
I --> Q(( ))
I --> R(( ))
style A fill:#f9f,stroke:#333,stroke-width:4px
style B fill:#bbf,stroke:#333,stroke-width:2px
style C fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#bbf,stroke:#333,stroke-width:2px
style E fill:#bbf,stroke:#333,stroke-width:2px
style F fill:#bfb,stroke:#333,stroke-width:2px
style G fill:#ddf,stroke:#333,stroke-width:2px
style H fill:#ddf,stroke:#333,stroke-width:2px
style I fill:#ddf,stroke:#333,stroke-width:2px
style J fill:#fff,stroke:#333,stroke-width:1px
style K fill:#fff,stroke:#333,stroke-width:1px
style L fill:#fff,stroke:#333,stroke-width:1px
style M fill:#fff,stroke:#333,stroke-width:1px
style N fill:#fff,stroke:#333,stroke-width:1px
style O fill:#fff,stroke:#333,stroke-width:1px
style P fill:#fff,stroke:#333,stroke-width:1px
style Q fill:#fff,stroke:#333,stroke-width:1px
style R fill:#fff,stroke:#333,stroke-width:1px
Reference
https://www.techtarget.com/searchnetworking/definition/HAProxy
https://bizsecure-apac.com/haproxy-load-balancer/
https://github.com/somaz94/network/tree/main/haproxy
https://github.com/somaz94/helm-chart-template/tree/main/gcp/haproxy
https://webhostinggeeks.com/blog/haproxy-features-functions-benefits/
'Networking, Security, Protocols' 카테고리의 다른 글
Reverse Proxy(역방향 프록시)란? (2) | 2024.11.13 |
---|---|
Cilium이란? (2) | 2024.02.06 |
Vault 설치와 사용법 (2) | 2024.01.24 |
Curl(Client URL)이란? (0) | 2023.05.26 |
SSID(Service Set Identifier)란? (0) | 2023.04.05 |