HAProxy+DockerでCustomHeaderを追加するサンプルと設定

Aug 25, 2024 16:40 · 1457 words · 3 minute read Docker

Dockerで動かしているWebアプリの前段にHAProxyを使っている事例を見かけました。 HAProxyを使って下記の2つを実現しています。

  • Webアプリを制御するCustom Headerを追加
  • HAProxyをHTTPSの終端にして、HTTPS対応が少し面倒な背後のWebアプリにHTTPリクエストを流す

HAProxyを初めて知ったので、サンプルコードを書いて動かしてみました。

目次

HAProxyの公式ドキュメント

HAProxy - The Reliable, High Perf. TCP/HTTP Load Balancer

各バージョンのドキュメントの一覧です。

HAProxy Documentation Converter

最新のLTS版、HAProxy 3.0 (LTS)のドキュメントトップはこちら。 基本機能の解説だけでなく、設定ファイルの解説や運用のガイドもあります。

HAProxyでできること

Starter Guideで紹介されている使い方の中から、便利そうだなと思った機能を一部抜粋します。

HAProxy version 3.0.3-28 - Starter Guide

  • 負荷分散
  • HTTP2対応
  • SSLの終端
  • 統計情報の収集
    • アクセス元のIPアドレスを取得し、DDoS対策をする、みたいな使い方もできるらしい
  • キャッシュプロキシ
  • FastCGIのゲートウェイ
  • HTTPヘッダの追加や編集のような文字列のフォーマット
  • Luaスクリプトでちょっとした機能を追加する
  • 値をチェックし、完全にリクエストのみをWebアプリに渡す
  • ヘルスチェック

とても便利そう、かつ多機能ですね。

サンプルコード

HAProxyを動かすサンプルを作ってみました。 Docker ContainerでFastAPIとHAProxyを動かし、FastAPIの前段にHAProxyを配置しています。

GitHub - kapiecii/haproxy-sample

haproxy.cfg定の概要です。

  • 80番ポートのHAProxyでリクエストを受け取り、8000番ポートで動作するFastAPIにリクエストをForwardしています。
  • 「X-Custom-Header」というカスタムヘッダをRequest Headerに追加しています。
  • 8404番ポートでHAProxyの統計情報ページにアクセスできます。
  • 統計情報ページに認証機能を追加しています。認証情報は環境変数で指定し、.envファイルで設定しています。
global
    log stdout format raw local0

defaults
    log global
    mode http
    option httplog
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend http_back

backend http_back
    http-request set-header X-Custom-Header MyValue
    server fastapi_app 127.0.0.1:8000

# Enable HAProxy stats
listen stats
    bind *:8404
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth "$HAPROXY_STATS_USER":"$HAPROXY_STATS_PASSWORD"

FastAPIのコードです。 HAProxyで追加したCustom Headerをログに出力する処理を追加しています。

from fastapi import FastAPI, Request
import logging

app = FastAPI()

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

@app.middleware("http")
async def log_custom_header(request: Request, call_next):
	# Extract the custom header value
	custom_header_value = request.headers.get("X-Custom-Header", "Not Found")
	logger.info(f"X-Custom-Header: {custom_header_value}")
	
	response = await call_next(request)
	return response

@app.get("/")
def read_root():
	return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str = None):
	return {"item_id": item_id, "q": q}

動かした様子

下記のコマンドでDocker Containerを起動し、curlとWebブラウザからアクセスした様子です。

sh start-docker.sh
#!/bin/bash

# Build the Docker image
docker build -t haproxy-sample .

# Run the Docker container with environment variables
docker container run -it --name haproxy-sample --rm --env-file .env -p 80:80 -p 8404:8404 haproxy-sample

curlでアクセスします。

curl http://localhost/

こんな内容がログに出力されます。 X-Custom-Header: MyValueが追加されてますね。  

INFO:fastapi-app:X-Custom-Header: MyValue
INFO:     127.0.0.1:40024 - "GET / HTTP/1.1" 200 OK
172.17.0.1:54762 [24/Aug/2024:08:50:17.585] http_front http_back/fastapi_app 0/0/0/2/2 200 146 - - ---- 1/1/0/0/0 0/0 "GET / HTTP/1.1"

ブラウザからアクセスするとこんな感じ。

統計情報の画面では下記の情報を確認できます。

統計情報をjsonでexportできます。

最後に

HAProxyを初めて動かしてみました。 独自のヘッダを追加したり、スクリプトでちょっとした処理を実装することもできるので、とても便利そうです。

負荷分散や環境のヘルスチェックもできるらしいので、それらの機能も試してみたいですね。

tweet Share