Varnish Cache

高性能HTTPアクセラレーター。リバースプロキシキャッシュで、Webサイトのレスポンス時間を大幅短縮。VCL(Varnish Configuration Language)による柔軟設定。

キャッシュサーバーHTTPアクセラレーターリバースプロキシVCL高性能

キャッシュサーバー

Varnish Cache

概要

Varnish CacheはWebアプリケーション加速器として知られる高性能HTTPリバースプロキシです。HTTPを話すサーバーの前面に配置し、コンテンツをキャッシュすることで、通常300〜1000倍の配信速度向上を実現します。VCL(Varnish Configuration Language)という独自のドメイン固有言語により、リクエスト処理ポリシーを自由に記述可能。20Gbpsの配信性能を通常のハードウェアで実現し、30万リクエスト/秒の処理能力を誇ります。設定変更時はVCLをC言語にコンパイルし、共有オブジェクトとして動的ロードするため再起動不要でのポリシー更新が可能です。

詳細

Varnish Cache 2024年版は、高トラフィックWebサイトにおけるHTTPアクセラレーターの決定版として確固たる地位を維持しています。メモリベースのキャッシュ機能とリバースプロキシによる高速配信で、CDNやメディアサイトでの重要な役割を担います。VCLの強力な設定機能により、キャッシュルール、認証処理、エラーハンドリング、ESI(Edge Side Includes)処理、WebSockets対応まで幅広くカスタマイズ可能。マルチスレッド対応によりモダンなマルチコアプロセッサーを効率活用し、ネットワークバウンドな性能特性でネットワーク速度が事実上の制約となります。

主な特徴

  • 超高速パフォーマンス: 通常のハードウェアで20Gbps配信可能
  • VCL設定言語: 柔軟なポリシー記述とリアルタイム更新
  • リバースプロキシ: HTTPリクエストの高速処理とキャッシング
  • ESI対応: エッジサイドインクルードによる部分キャッシング
  • WebSockets対応: アップグレードヘッダーの適切な処理
  • 動的設定更新: VCL再起動なしでの設定変更

メリット・デメリット

メリット

  • 極めて高いパフォーマンス(300-1000倍の速度向上)
  • VCLによる高度なカスタマイズ性と柔軟性
  • 再起動不要の動的設定変更機能
  • 豊富なヘルスチェックとフェイルオーバー機能
  • ESI、gzip圧縮、SSL終端などの統合機能
  • 大規模サイトでの実績豊富なエンタープライズ採用

デメリット

  • VCL習得に専門知識が必要で学習コストが高い
  • メモリ使用量が大きく適切なサイジングが必要
  • 複雑な設定でのデバッグとトラブルシューティングが困難
  • SSL終端時のCPU負荷増大
  • キャッシュ無効化の制御が複雑
  • 設定ミスによるサービス影響のリスク

参考ページ

書き方の例

インストールと基本セットアップ

# Ubuntu/Debianでのインストール
sudo apt update
sudo apt install varnish

# CentOS/RHEL/Fedoraでのインストール
sudo yum install varnish

# macOS (Homebrew)
brew install varnish

# ソースからのビルド
curl -s https://packagecloud.io/install/repositories/varnishcache/varnish70/script.deb.sh | sudo bash
sudo apt install varnish

# Varnishの起動
sudo systemctl start varnish
sudo systemctl enable varnish

# 設定ファイルの確認
varnishd -f /etc/varnish/default.vcl -T localhost:6082 -a :6081 -s malloc,256m

# 動作確認
curl -I http://localhost:6081

基本VCL設定(/etc/varnish/default.vcl)

vcl 4.0;

# バックエンドサーバー定義
backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .connect_timeout = 60s;
    .first_byte_timeout = 60s;
    .between_bytes_timeout = 60s;
}

# 追加バックエンド例
backend webserver1 {
    .host = "192.168.1.10";
    .port = "80";
    .probe = {
        .url = "/health";
        .timeout = 1s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

backend webserver2 {
    .host = "192.168.1.11";
    .port = "80";
    .probe = {
        .url = "/health";
        .timeout = 1s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

# ロードバランサーの設定
import directors;

sub vcl_init {
    new cluster = directors.round_robin();
    cluster.add_backend(webserver1);
    cluster.add_backend(webserver2);
}

# リクエスト受信処理
sub vcl_recv {
    # バックエンドサーバーの選択
    if (req.url ~ "^/api/") {
        set req.backend_hint = cluster.backend();
    } else {
        set req.backend_hint = default;
    }
    
    # 静的ファイルのキャッシュ対象
    if (req.url ~ "\.(jpg|jpeg|png|gif|ico|css|js|pdf|txt)$") {
        unset req.http.Cookie;
        return(hash);
    }
    
    # POST/PUT/DELETEリクエストはキャッシュしない
    if (req.method != "GET" && req.method != "HEAD") {
        return(pass);
    }
    
    # 管理ページはキャッシュしない
    if (req.url ~ "^/admin") {
        return(pass);
    }
    
    # 認証が必要なページはキャッシュしない
    if (req.http.Authorization) {
        return(pass);
    }
    
    # Cookieを持つリクエストの処理
    if (req.http.Cookie) {
        # セッションCookieがある場合はパス
        if (req.http.Cookie ~ "session_id") {
            return(pass);
        }
        # その他のCookieは削除
        unset req.http.Cookie;
    }
    
    return(hash);
}

# バックエンド応答処理
sub vcl_backend_response {
    # キャッシュ時間の設定
    if (bereq.url ~ "\.(jpg|jpeg|png|gif|ico)$") {
        set beresp.ttl = 1h;
        set beresp.grace = 1m;
    } elseif (bereq.url ~ "\.(css|js)$") {
        set beresp.ttl = 1h;
        set beresp.grace = 1m;
    } elseif (bereq.url ~ "^/api/") {
        set beresp.ttl = 5m;
        set beresp.grace = 30s;
    } else {
        set beresp.ttl = 10m;
        set beresp.grace = 1m;
    }
    
    # ESI処理の有効化
    if (bereq.url == "/dynamic-page") {
        set beresp.do_esi = true;
        set beresp.ttl = 1h;
    }
    
    # gzip圧縮の有効化
    if (beresp.http.content-type ~ "text|javascript|json|xml") {
        set beresp.do_gzip = true;
    }
    
    # キャッシュしない条件
    if (beresp.status >= 400) {
        set beresp.ttl = 0s;
    }
    
    # セットクッキーヘッダーがある場合はキャッシュしない
    if (beresp.http.Set-Cookie) {
        set beresp.ttl = 0s;
    }
    
    return(deliver);
}

# 配信前処理
sub vcl_deliver {
    # キャッシュヒット/ミスの情報を追加
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
        set resp.http.X-Cache-Hits = obj.hits;
    } else {
        set resp.http.X-Cache = "MISS";
    }
    
    # デバッグ情報の追加(本番では削除)
    set resp.http.X-Served-By = "Varnish";
    
    return(deliver);
}

# エラーページのカスタマイズ
sub vcl_backend_error {
    if (beresp.status == 503 && bereq.retries < 3) {
        return(retry);
    }
    
    set beresp.http.Content-Type = "text/html; charset=utf-8";
    synthetic({"
    <html>
        <head><title>Service Temporarily Unavailable</title></head>
        <body>
            <h1>Service Temporarily Unavailable</h1>
            <p>The server is temporarily unable to service your request.</p>
        </body>
    </html>
    "});
    
    return(deliver);
}

VCL管理操作(varnishadm)

# varnishadmでの接続
sudo varnishadm

# VCL設定の管理
varnishadm> vcl.list
# ロードされているVCL一覧表示

varnishadm> vcl.load new_config /etc/varnish/new.vcl
# 新しいVCL設定をロード

varnishadm> vcl.use new_config
# 新しい設定をアクティブ化

varnishadm> vcl.discard old_config
# 古い設定を削除

# キャッシュ管理
varnishadm> ban req.url ~ "^/api/"
# 特定URLパターンのキャッシュを無効化

varnishadm> ban req.http.host == "example.com"
# 特定ホストのキャッシュを無効化

varnishadm> ban.list
# バン(無効化)ルールの一覧表示

# 統計情報の確認
varnishadm> stats
# Varnish統計情報表示

varnishadm> param.show
# パラメータ一覧表示

varnishadm> param.set default_ttl 300
# デフォルトTTLを5分に設定

# バックエンドヘルスチェック状況
varnishadm> backend.list
# バックエンドサーバーの状態確認

# ログ出力制御
varnishadm> param.set vsl_mask +VCL_call,-Hit
# VCLコール有効化、ヒット情報無効化

ヘルスチェックとフェイルオーバー設定

vcl 4.0;

# 詳細なヘルスプローブ設定
probe healthcheck {
    .url = "/health";
    .request = 
        "GET /health HTTP/1.1"
        "Host: example.com"
        "Connection: close"
        "User-Agent: Varnish Health Probe";
    .timeout = 2s;
    .interval = 5s;
    .window = 5;
    .threshold = 3;
    .initial = 3;
}

# プライマリバックエンド
backend primary {
    .host = "192.168.1.10";
    .port = "80";
    .probe = healthcheck;
    .connect_timeout = 5s;
    .first_byte_timeout = 10s;
    .between_bytes_timeout = 2s;
}

# セカンダリバックエンド
backend secondary {
    .host = "192.168.1.11";
    .port = "80";
    .probe = healthcheck;
    .connect_timeout = 5s;
    .first_byte_timeout = 10s;
    .between_bytes_timeout = 2s;
}

# フェイルオーバーディレクター
import directors;

sub vcl_init {
    new failover = directors.fallback();
    failover.add_backend(primary);
    failover.add_backend(secondary);
}

sub vcl_recv {
    set req.backend_hint = failover.backend();
    return(hash);
}

# バックエンドエラー時のリトライ処理
sub vcl_backend_error {
    if (beresp.status == 503 && bereq.retries < 2) {
        return(retry);
    }
    
    # カスタムエラーページ
    set beresp.http.Content-Type = "text/html; charset=utf-8";
    set beresp.status = 503;
    synthetic({"
    <!DOCTYPE html>
    <html>
    <head>
        <title>Service Unavailable</title>
        <style>
            body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
            .error { color: #cc0000; }
        </style>
    </head>
    <body>
        <h1 class="error">Service Temporarily Unavailable</h1>
        <p>申し訳ございませんが、一時的にサービスをご利用いただけません。</p>
        <p>しばらく経ってから再度アクセスしてください。</p>
        <p><a href="javascript:history.back()">戻る</a></p>
    </body>
    </html>
    "});
    
    return(deliver);
}

ESI(Edge Side Includes)設定例

# ESI用バックエンド設定
sub vcl_backend_response {
    # ESI処理対象ページの設定
    if (bereq.url ~ "^/dynamic/") {
        set beresp.do_esi = true;
        set beresp.ttl = 1h;  # メインコンテンツのTTL
    }
    
    # ESIフラグメントの設定
    if (bereq.url ~ "^/fragments/") {
        set beresp.ttl = 5m;  # フラグメントのTTL
    }
    
    return(deliver);
}

HTMLテンプレートでのESI使用例:

<!DOCTYPE html>
<html>
<head>
    <title>Dynamic Page with ESI</title>
</head>
<body>
    <h1>メインコンテンツ</h1>
    <p>このページは1時間キャッシュされます。</p>
    
    <!-- ユーザー固有情報(5分キャッシュ) -->
    <esi:include src="/fragments/user-info?user=123" />
    
    <!-- 新着記事(1分キャッシュ) -->
    <esi:include src="/fragments/latest-news" ttl="60" />
    
    <!-- 天気情報(キャッシュなし) -->
    <esi:include src="/fragments/weather" ttl="0" />
    
    <!-- エラー時のフォールバック -->
    <esi:include src="/fragments/ads">
        <esi:alt>
            <p>広告を読み込めませんでした。</p>
        </esi:alt>
    </esi:include>
</body>
</html>

WebSockets対応設定

# WebSocket接続の処理
sub vcl_recv {
    # WebSocketアップグレード要求をパイプ経由で転送
    if (req.http.upgrade ~ "(?i)websocket") {
        return(pipe);
    }
}

sub vcl_pipe {
    # アップグレードヘッダーの転送
    if (req.http.upgrade) {
        set bereq.http.upgrade = req.http.upgrade;
        set bereq.http.connection = req.http.connection;
    }
}

SSL/TLS終端とHTTPS設定

# Nginxとの組み合わせ例(SSL終端)
# /etc/nginx/sites-available/varnish-ssl

server {
    listen 443 ssl http2;
    server_name example.com;
    
    ssl_certificate /path/to/certificate.crt;
    ssl_certificate_key /path/to/private.key;
    
    location / {
        proxy_pass http://127.0.0.1:6081;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
    }
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

VCLでのHTTPS処理:

sub vcl_recv {
    # HTTPSリダイレクト処理
    if (req.http.X-Forwarded-Proto != "https") {
        return(synth(301, "https://" + req.http.host + req.url));
    }
    
    # セキュリティヘッダーの設定
    if (req.http.host == "example.com") {
        set req.http.X-Forwarded-Proto = "https";
    }
}

sub vcl_deliver {
    # セキュリティヘッダーの追加
    set resp.http.Strict-Transport-Security = "max-age=31536000; includeSubDomains";
    set resp.http.X-Content-Type-Options = "nosniff";
    set resp.http.X-Frame-Options = "DENY";
    set resp.http.X-XSS-Protection = "1; mode=block";
    
    return(deliver);
}

監視とロギング設定

# varnishlogによるアクセスログ
sudo varnishlog -w /var/log/varnish/access.log

# 特定条件でのフィルタリング
sudo varnishlog -q "ReqURL ~ '^/api/'" -w /var/log/varnish/api.log

# リアルタイム監視
sudo varnishlog -q "RespStatus >= 400"

# 統計情報の取得
varnishstat -1

# Prometheusメトリクス出力
varnish_exporter --varnish.instance=""