⚖️

僕「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でのトラフィックフロー:

HA Proxy

僕「なるほど、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でしたっけ。」
よく見るやつ:
https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/

メ「できるけど、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
https://www.haproxy.org/

ALB Controller
https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/

Discussion