僕「HAProxyのdeployment?それALBでええやん✋😅」
初プロジェクトでこんなことを考えてたので、質問をしてみました。今回はその備忘録です。
はじめに
僕「なんこれ。」
$ kubectl get deployment haproxy -n default
NAME READY UP-TO-DATE AVAILABLE AGE
haproxy 3/3 3 3 25d
僕「メンターさん、今のプロジェクトでhaproxyってデプロイメントがあるんですけど...。ログにも引っかかったことないし、なんか存在感薄いですよね。これ本当にいるんですか?」
メ「それロードバランサーだよ。」
僕「え...IngressやServiceだけでも大変なのに、またロードバランサが出てくるんですか?」
メンター「まあまあ笑。確かにAWSを使ってるならALB(Application Load Balancer)が便利なんだけど、あえてHAProxyを使うケースもあるんだよ。なぜだと思う?」
僕「えー、でもわざわざ自分でHAProxyを運用するより、ALBみたいなマネージドサービスの方が楽じゃないですか?」
メンター「管理の手間を考えるとALBの方が楽だね。でもこれには必ず理由があるから、まずはALBの仕組みを整理してから考えてみよう。」
Kubernetesにおけるロードバランシングの仕組み
僕「そもそも、KubernetesのIngressとServiceの関係がもう怪しいです。」
メ「じゃあ、Kubernetesでのトラフィックの流れを整理しようか。」
メ「他のプロジェクトでは、外部からのトラフィックは通常この順番で流れることもある」
Service(サービス)の実態
僕「Serviceって抽象的な概念だと思ってたんですが、実態があるんですか?」
メ「そう、Serviceには複数のタイプがあって、それぞれ実際のAWSリソースにマッピングされる」
- ClusterIP: クラスター内部のみアクセス可能(AWSリソースなし)
- NodePort: 各ノードのポートを開放(EC2のセキュリティグループ設定)
- LoadBalancer: 実態はNLB(Network Load Balancer)が作成される
apiVersion: v1
kind: Service
metadata:
name: my-app-service
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: my-app
僕「ServiceのLoadBalancerタイプを作ると、自動的にNLBができるんですね。そういえばそうだった...。」
Ingress(イングレス)の実態
メ「次にIngress。IngressもKubernetesの抽象的なリソースだけど、実際にはIngress Controllerが具体的なロードバランサーを作成する。」
僕「Ingress Controllerって何ですか?」
メ「AWS Load Balancer Controllerを使った場合、**Ingressリソースの実態はALB(Application Load Balancer)**になる。」
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
kubernetes.io/ingress.class: alb # ALBを使用
alb.ingress.kubernetes.io/scheme: internet-facing
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
SharedIngressとは?
僕「あと、うちのプロジェクトってSharedIngressが入ってるんでしたっけ?複数のアプリで共有するってことですか?」
メ「その通り!SharedIngressは複数のサービス(アプリケーション)が1つのALBを共有する仕組みだ。」
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shared-ingress
annotations:
kubernetes.io/ingress.class: alb
alb.ingress.kubernetes.io/scheme: internet-facing # インターネット公開
alb.ingress.kubernetes.io/target-type: ip # Pod を直接ターゲットに
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80}]' # ALBのリスナー設定
alb.ingress.kubernetes.io/group.name: shared-group # ALBグループ化(複数Ingress統合用)
alb.ingress.kubernetes.io/load-balancer-attributes: idle_timeout.timeout_seconds=60
spec:
rules:
- host: api.example.com
http:
paths:
- path: /user
pathType: Prefix
backend:
service:
name: user-service # ユーザーサービス
port:
number: 80
- path: /order
pathType: Prefix
backend:
service:
name: order-service # 注文サービス
port:
number: 80
- path: /payment
pathType: Prefix
backend:
service:
name: payment-service # 決済サービス
port:
number: 80
SharedIngressのメリット:
- コスト削減: 1つのALBを複数サービスで共有(ALB基本料金$200/月を分割)
- SSL証明書の一元管理: 1つのドメインで複数サービスを提供
- 運用の簡素化: ALBの設定・監視が1箇所に集約
SharedIngressのデメリット:
- 影響範囲の拡大: 1つのALBに障害が起きると全サービスに影響
- 設定の複雑化: パスベースルーティングの管理が複雑
- デプロイの依存関係: 1つのサービスの変更が他に影響する可能性
ALB(Application Load Balancer)とは?
僕「ALBってどういう仕組みなんですか?負荷分散やヘルスチェックが仕事でしたっけ」
メ「ALBはAWSが提供するマネージドなロードバランサーサービスだね。レイヤー7(アプリケーション層)で動作して、HTTPやHTTPSのトラフィックを複数のターゲットに分散する。」
メ「さっき説明したように、KubernetesのIngressリソースの実態がALBになることが多い。実は、ALBの内部実装はnginxベースなんじゃないかと思ってる。AWSが公式に明言してるわけじゃないけど、エラーページやヘッダーの挙動を見ると、nginxをベースにしたカスタム実装かなって。」
僕「じゃあnginxを自分で運用するのとどう違うんですか?」
メ「大きな違いは管理の手間だね。ALBなら」
- nginxの設定ファイル管理が不要
- パッチ適用やアップデートが自動
- 高可用性構成が自動で組まれる
- Auto Scalingが自動
- 監視・ログ収集がCloudWatchと統合済み
メ「でも、nginxを直接使えば、より細かい設定やカスタマイズができる。ALBの特徴を整理すると」
ALBの特徴
メリット:
- AWSが完全管理
- Auto Scalingとの連携が簡単
- SSL証明書の管理が楽(ACMとの統合)
- ヘルスチェック機能が標準装備
デメリット:
- 設定の柔軟性に限界がある
- マネージドだからコストが高い(時間課金 + データ処理課金)
- AWSベンダーロックイン
- 細かいカスタマイズができない
僕「なるほど、管理が楽なのはいけてますね。これならデメリットも飲めそう。」
HAProxyとは?
メ「じゃあHAProxyを見てみよう。HAProxyはOSSのロードバランサーかつプロキシのソフトウェアだ。」
僕「HAProxyもKubernetesで使えるんですか?」
メ「うん。でもKubernetes限定のソフトウェアって認識はしなくてもいいかも。HAProxyをKubernetesで使う場合、いくつかのパターンがある。」
パターン1: HAProxy Ingress Controller
# HAProxy Ingress Controllerを使用
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: haproxy-ingress
annotations:
kubernetes.io/ingress.class: haproxy
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
パターン2: HAProxyをPodとしてデプロイ
メ「こんな場合もある。」
apiVersion: apps/v1
kind: Deployment
metadata:
name: haproxy
spec:
replicas: 3
selector:
matchLabels:
app: haproxy
template:
metadata:
labels:
app: haproxy
spec:
containers:
- name: haproxy
image: haproxy:2.8
ports:
- containerPort: 80
- containerPort: 443
volumeMounts:
- name: haproxy-config
mountPath: /usr/local/etc/haproxy/haproxy.cfg
subPath: haproxy.cfg
Kubernetesでのトラフィックフロー:
僕「なるほど、HAProxyもKubernetesの中で動かせるんですね。でもその場合、HAProxyの前にもNLBが必要になるんですか?」
メ「そそ。HAProxyをKubernetesで使う場合、外部からアクセスするためにはServiceのLoadBalancerタイプ(実態はNLB)が必要になる。」
- ALB方式: インターネット → ALB → Pod
- HAProxy方式: インターネット → NLB → HAProxy Pod → アプリPod
メ「HAProxy方式だと、NLB + HAProxyの2段構成になるから、レイテンシが若干増える可能性がある。でも、HAProxyの柔軟性を活かしたい場合は有効な選択肢だ。」
HAProxyの特徴
メリット:
- パフォーマンスが高い
- オープンソースで無料
- クラウドベンダーに依存しない
- 細かい制御が可能(レート制限、カスタムヘッダー操作など)
デメリット:
- 自分で管理する必要がある
- 設定が複雑
- 高可用性の構成を自分で組む必要がある
- 監視・ログ管理を自分で設定
僕「うーん、HAProxyの方が大変そうですね。それでもHAProxyを選ぶ理由って何ですか?」
メ「HAProxyとnginx(ALBの内部実装)にはそれぞれ得意分野がある」
HAProxy vs nginx(ALBベース)の特徴:
項目 | HAProxy | nginx(ALB) |
---|---|---|
パフォーマンス | 極めて高い(特にL7処理) | 高い(静的コンテンツに強い) |
設定の柔軟性 | 非常に高い | 中程度(ALBでは制限あり) |
ロードバランシング | 高度なアルゴリズム | 基本的なアルゴリズム |
ヘルスチェック | カスタマイズ可能 | 標準的な機能 |
SSL終端 | 対応 | 対応(ACM統合で楽) |
管理の手間 | 自分で管理 | AWS管理(楽) |
僕「なるほど、用途によって使い分けるんですね。」
なぜHAProxyが必要になるのか?
メ「いい質問だね。実際のケースを見てみよう。」
ケース1: LBとして高いパフォーマンスが必要
僕「パフォーマンスってそんなに違うんですか?」
メ「HAProxyは1秒間に数十万リクエストを処理できる。ALBも高性能だけど、HAProxyの方が一般的にレイテンシが低く、スループットが高い。ゲームやリアルタイム取引システムなど、ミリ秒単位のレイテンシが重要なシステムでは大きな差になる。」
ケース2: 複雑なルーティングロジックが必要
メ「例えば、こんな要件があったとする。」
- 特定の時間帯だけメンテナンスページを表示したい
- 特定のパス(/api/health など)はメンテナンス中でも通したい
- 運用チームのIPアドレスからのアクセスはメンテナンスをスキップしたい
- カスタムのHTMLを返して、HTTPステータスコードを503にしたい
僕「ALBでも固定レスポンスでメンテナンスページを返せますよね?alb.ingress.kubernetes.io/actions.fixed-response
でしたっけ。」
よく見るやつ:
メ「できるけど、HAProxyなら設定ファイルでこんな柔軟な制御ができる」
frontend web_frontend
bind *:80
# 運用チームのIPは除外
acl admin_ip src 203.0.113.0/24
# ヘルスチェックAPIは許可
acl is_health path_beg /api/health
# メンテナンス時間帯 (例: 02:00-03:00)
acl maint_time time 02:00-03:00
# メンテナンスモード発動条件
use_backend maintenance if maint_time !admin_ip !is_health
default_backend web_servers
backend maintenance
mode http
errorfile 503 /etc/haproxy/errors/maintenance.http
僕「うわあ、確かにこれはALBでは厳しそう...。」
ケース3: SharedIngressの限界を超える場合
僕「SharedIngressでも複数サービスを扱えるなら、HAProxyは不要じゃないですか?」
メ「SharedIngressにも限界がある。例えば」
SharedIngressの限界:
- ALBのターゲットグループ数制限
- 1つのALBあたり最大1000個のターゲットグループ
- 複雑なルーティングルールの制限
- カスタムヘッダー操作の制限
HAProxyなら可能な高度な制御:
frontend shared_frontend
bind *:80
# 複数の条件を組み合わせた複雑なルーティング
acl is_mobile hdr_sub(User-Agent) Mobile
acl is_api path_beg /api/
acl is_premium hdr(X-User-Type) premium
acl high_load nbsrv(backend_servers) lt 2
# プレミアムユーザーのモバイルAPIは専用サーバーへ
use_backend premium_mobile_api if is_mobile is_api is_premium
# 負荷が高い時は軽量サーバーへ
use_backend lightweight_servers if high_load
# 地域別 + デバイス別の細かい振り分け
use_backend asia_mobile if is_mobile hdr_sub(CloudFront-Viewer-Country) JP
default_backend default_servers
僕「確かに、これはSharedIngressでは難しそう...」
メ「ALBと結びついてるから、頻繁に変えたくないしね。」
どちらを選ぶべき?
メ「ちょっと整理してみよう。」
ALB(Ingress)を選ぶべきケース
- AWSオンリーの構成
- 管理工数を最小化したい
- 標準的なWebアプリケーション
- 開発チームのインフラ経験が少ない
- 中小規模のトラフィック
SharedIngress(ALB共有)を選ぶべきケース
- 複数のマイクロサービスがある
- 同一ドメインでパスベースルーティング
- SSL証明書を一元管理したい
- 運用チームのリソースが限られている
HAProxyを選ぶべきケース
- 極めて高いパフォーマンスが必要
- SharedIngressでは実現できない複雑なルーティングロジック
- マルチクラウド・ハイブリッド構成
- 細かいカスタマイズが必要
- インフラ管理の経験とリソースがある
- ALBのターゲットグループ制限を超える規模
僕「なるほど、要件次第ということですね。」
メ「その通り。プロジェクトの要件、チームのスキル、予算、将来の拡張性を総合的に判断しよう。」
まとめ
僕「ありがとうございました!ALBとHAProxyの役割や違いが少し見えてきました。」
メ「そうだね。ポイントは、ALBはマネージドで簡単・便利、HAProxyは柔軟で細かい制御ができる。どちらも使いどころがあるから、最初から「どっちが正解」と決めるんじゃなくて、要件や環境に応じて選べばいい。今回は大きく触れなかったけど、もちろんコストの観点もあるよ。マネージドサービスって高いこと多いし。」
僕「確かに。でも、いきなり複雑な構成にする必要はないですね。まずはALBでシンプルに始めて、必要になったらHAProxyを検討する流れが現実的かも。」
参考
haproxy
ALB Controller
Discussion