設定ファイル(conf)の「限界」を感じたことはありませんか?
こんにちは!「LINUX工房」管理人の「リナックス先生」です。
前回は、マイクロサービス時代のルーティング技術(gRPC, WebSocket, auth_request)について学びました。
しかし、現場の要求はさらにエスカレートします。
「特定のユーザーIDの時だけ、カナリアリリース用のサーバーに飛ばしたい(データベースを見て判断したい)」
「リクエストの中身を解析して、JSONを書き換えてからバックエンドに渡したい」
「WAFのような複雑なフィルタリングを、自前のロジックで実装したい」
これらを標準の nginx.conf の if 文だけで書こうとすると、設定ファイルはスパゲッティ状態になり、メンテナンス不能に陥ります。
そこで登場するのが、Nginxに「脳」を与える技術、OpenResty(Lua)です。
先生、実はまさにそれで悩んでます。
「A/Bテストをしたいから、Redisに入ってるユーザー情報を元に振り分け先を変えて」って言われたんですけど……。
NginxからRedisなんて叩けるんですか? アプリ側でやるべき処理じゃないんですか?
ふふふ、アプリ側でやると「一度アプリまでリクエストを通す」オーバーヘッドがかかるでしょ?
Nginxの段階でRedisを高速に参照して、瞬時に振り分ける。
これができれば、バックエンドに無駄な負荷をかけずに高度な制御ができるの。
それを実現するのが「Lua言語」と、それを組み込んだNginxディストリビューション「OpenResty」よ。
インフラエンジニアが「コードを書く」時代の到来ね!
本記事では、OpenRestyの導入から、Luaスクリプトを使った動的ルーティング、Redisとの連携、そしてJWTトークンの検証ロジックの実装まで、Nginxを「プログラマブル」に使い倒す方法を解説します。
🚀 Nginx応用講座(全8回)カリキュラム
現在地:【第4回】Nginxをプログラマブルに。Lua言語(OpenResty)による動的処理と機能拡張
- 【第1回】大規模負荷分散とキャッシュ戦略。数万リクエストをさばく「Proxy Cache」と「Upstream」の極意
- 【第2回】WAF(Web Application Firewall)の構築。ModSecurityと最新のNginxセキュリティ
- 【第3回】マイクロサービス時代のルーティング。gRPC、WebSocket、認証プロキシ(auth_request)の統合
- 【第4回】Nginxをプログラマブルに。Lua言語(OpenResty)による動的処理と機能拡張
- 【第5回】絶対停止させない高可用性(HA)。KeepalivedによるVIP管理とフェイルオーバー
- 【第6回】可観測性(Observability)の確保。Prometheus/Grafana連携とカスタムメトリクス
- 【第7回】動的モジュールとカスタムビルド。必要な機能だけを組み込む軽量化テクニック
- 【第8回】Kubernetes Ingress Controller入門。クラウドネイティブ時代のNginx運用
※基本講座の復習はこちら:Nginx基本講座(全8回)アーカイブ
第1章:OpenRestyとは何か?なぜLuaなのか?
OpenResty = Nginx + LuaJIT + 便利なモジュール群
標準のNginxにLuaモジュールを後から追加することも可能ですが、バージョンの整合性を取るのが大変です。
そこで、Nginx本体に最初から「LuaJIT(Luaの高速実行環境)」と、よく使われるライブラリ(Redis接続、JSON処理など)をバンドルしてパッケージ化したものが「OpenResty(オープンレスティ)」です。
世界中のCDNプロバイダや大規模Webサービスで採用されている、信頼性の高いプラットフォームです。
なぜLua言語なのか?
PythonやRubyではなく、なぜLuaなのでしょうか?
- 圧倒的に速い: LuaJITはスクリプト言語の中で最速クラスです。C言語で書かれたNginxの内部APIを、ほぼオーバーヘッドなしで呼び出せます。
- 省メモリ: 非常に軽量で、大量の同時接続をさばくNginxの特性を邪魔しません。
- ノンブロッキングI/O: Luaスクリプト内でDBへの問い合わせを行っても、Nginxのイベントループをブロックしません(ここが最重要!)。
つまり、「Nginxの性能を落とさずに、複雑なロジックを組み込める唯一の解」がLuaなのです。
第2章:OpenRestyのインストール
それでは、標準のNginxではなく、OpenRestyをインストールしてみましょう。
※OS: AlmaLinux 9 を前提とします(Ubuntuなども公式リポジトリがあります)。
1. リポジトリの追加
# 必要なツールのインストール sudo dnf install -y 'dnf-command(config-manager)' # OpenResty公式リポジトリの追加 sudo dnf config-manager --add-repo https://openresty.org/package/rhel/openresty.repo
2. インストール
sudo dnf install -y openresty openresty-resty
これでインストール完了です。
バイナリは /usr/local/openresty/nginx/sbin/nginx に、設定ファイルは /usr/local/openresty/nginx/conf/nginx.conf に配置されます。
3. サービス起動
sudo systemctl enable openresty sudo systemctl start openresty
既存のNginxが動いている場合はポートが競合するため、停止するかポートを変更してください。
第3章:Hello Lua World!(基本構文と実行フェーズ)
まずは簡単なLuaスクリプトを動かして、Nginxの設定ファイル内にどう記述するかを見てみましょう。
content_by_lua_block
レスポンスの中身(Content)をLuaで生成するディレクティブです。
location /hello {
default_type 'text/plain';
content_by_lua_block {
ngx.say("Hello, OpenResty!")
ngx.log(ngx.ERR, "これはエラーログに出力されます")
}
}
curl http://localhost/hello にアクセスすると、「Hello, OpenResty!」と返ってきます。ngx.say はNginxの出力バッファにデータを書き込む関数です。
Nginxの処理フェーズとLua
Luaコードは、Nginxのリクエスト処理の「どのタイミング」で実行するかを指定できます。
| ディレクティブ | 実行フェーズ | 主な用途 |
|---|---|---|
| set_by_lua | 変数のセット | 複雑な計算をして変数に代入する。 |
| access_by_lua | アクセス制限 | 認証、IP制限、WAF的な処理。 |
| content_by_lua | コンテンツ生成 | レスポンスボディの生成、APIの中継。 |
| header_filter_by_lua | ヘッダフィルタ | レスポンスヘッダの書き換え。 |
| log_by_lua | ログ記録 | カスタムログの送信(Fluentd等へ)。 |
第4章:実践1「Redisを使った動的ルーティング」
ここからが本番です。
「Redisに保存されたユーザー情報(例:beta_user:123 = true)」を見て、ベータ版サーバーへ振り分けるロジックを実装します。
Luaスクリプトの作成
設定ファイルに直接書くと長くなるので、外部ファイル(/usr/local/openresty/nginx/lua/routing.lua)に記述します。
-- ライブラリのロード
local redis = require "resty.redis"
local red = redis:new()
-- Redisへの接続(タイムアウト1秒)
red:set_timeout(1000)
local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.log(ngx.ERR, "Redis connection failed: ", err)
return
end
-- クエリパラメータからユーザーIDを取得 (?uid=123)
local args = ngx.req.get_uri_args()
local uid = args["uid"]
-- デフォルトのバックエンド
local target_backend = "http://stable_backend"
if uid then
-- Redisからベータフラグを取得
local is_beta, err = red:get("beta_user:" .. uid)
if is_beta == "true" then
target_backend = "http://beta_backend"
end
end
-- 変数にセット(後でproxy_passで使う)
ngx.var.target = target_backend
-- 接続プールに戻す(これがないと接続が開いたままになる!)
red:set_keepalive(10000, 100)
nginx.confの設定
http {
upstream stable_backend { server 10.0.0.1:80; }
upstream beta_backend { server 10.0.0.2:80; }
server {
listen 80;
location /app {
# 転送先を格納する変数を初期化
set $target "";
# アクセスフェーズでLuaを実行
access_by_lua_file /usr/local/openresty/nginx/lua/routing.lua;
# Luaで決定したバックエンドへ転送
proxy_pass $target;
}
}
}
これで、アプリを改修することなく、インフラ側だけで高度なA/Bテストやカナリアリリースが可能になります。
第5章:実践2「Nginx層でのJWT検証」
APIサーバーへの全てのリクエストで、アプリケーション側でJWT(JSON Web Token)の検証を行っていませんか?
不正なトークンや期限切れのトークンは、アプリに届く前にNginxで弾いてしまえば、アプリサーバーのCPUリソースを大幅に節約できます。
lua-resty-jwt の利用
OpenRestyにはコミュニティ製の便利なライブラリがたくさんあります。lua-resty-jwt を使うと、簡単に検証ができます。
access_by_lua_block {
local jwt = require "resty.jwt"
-- Authorizationヘッダーからトークン取得
local auth_header = ngx.var.http_Authorization
if not auth_header then
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
local token = string.sub(auth_header, 8) -- "Bearer "を除く
-- 秘密鍵で検証
local jwt_obj = jwt:verify("MY_SECRET_KEY", token)
if not jwt_obj.verified then
ngx.status = ngx.HTTP_UNAUTHORIZED
ngx.say("Invalid Token: " .. jwt_obj.reason)
ngx.exit(ngx.HTTP_UNAUTHORIZED)
end
-- 検証OKならバックエンドへ(ここでユーザーIDをヘッダに詰めることも可能)
ngx.req.set_header("X-User-Id", jwt_obj.payload.sub)
}
このように、「認証」という重い処理をゲートウェイ(Nginx)にオフロードすることで、マイクロサービス全体のスループットを向上させることができます。
第6章:ノンブロッキングI/Oと「サブリクエスト」
OpenRestyの真骨頂は、「サブリクエスト(ngx.location.capture)」です。
これは、Nginx内部で擬似的にHTTPリクエストを発行し、その結果をLuaで受け取る機能です。
例えば、「ユーザー情報API」と「商品情報API」を同時に叩いて、結果を合体させて返す(APIアグリゲーション)といったことが可能です。
content_by_lua_block {
-- 2つのAPIを並列(ノンブロッキング)で実行
local res1, res2 = ngx.location.capture_multi({
{ "/api/user" },
{ "/api/product" }
})
if res1.status == 200 and res2.status == 200 then
local cjson = require "cjson"
local user = cjson.decode(res1.body)
local product = cjson.decode(res2.body)
-- 結果をマージ
local result = {
user_name = user.name,
product_title = product.title
}
ngx.say(cjson.encode(result))
else
ngx.exit(500)
end
}
Node.jsなどでBFF(Backend For Frontend)を作るのと似ていますが、Nginx内で完結するため圧倒的に高速です。
第7章:プロの注意点(ブロッキングの罠)
Luaは強力ですが、使い方を間違えるとNginx全体の性能を道連れにして死にます。
以下の点に注意してください。
1. ブロッキング処理を書かない
Luaコード内で os.execute("sleep 10") や、時間のかかる重い計算(暗号化のループなど)を行ってはいけません。
Nginxはイベント駆動モデルなので、1つのリクエスト処理が止まると、他の数万リクエストも道連れになって止まります。
外部通信を行う場合は、必ず lua-resty-* 系のノンブロッキング対応ライブラリを使用してください。
2. グローバル変数を使わない
Luaのグローバル変数は、全てのリクエストで共有されてしまいます(レースコンディションの原因)。
変数は必ず local を付けて宣言しましょう。
3. キャッシュを活用する
Redisへの接続などは、set_keepalive を使ってコネクションプーリングを行いましょう。
毎回 connect していては、TCP接続のオーバーヘッドで性能が出ません。
まとめ:インフラを「コード」で制御せよ
お疲れ様でした!
今回は、Nginxの世界を一変させるOpenRestyとLuaについて解説しました。
今回の重要ポイント:
- OpenRestyは、NginxにLuaJITを組み込んだ最強のプラットフォーム。
content_by_luaやaccess_by_luaで、処理フェーズごとにロジックを挟める。- Redisと連携することで、動的なルーティングやレートリミットが実現できる。
- JWT検証などの「認証」をNginx層で行うことで、バックエンドを保護・軽量化できる。
- 必ず
local変数を使い、ノンブロッキングなライブラリを選ぶこと。
これで、あなたのNginxは単なる「土管(パケットを流すだけ)」から、「頭脳を持ったゲートウェイ」へと進化しました。
「こんなことNginxでできないかな?」と思ったら、まずはOpenRestyで実現できないか考えてみてください。
さて、ここまで高度なことができるようになると、このNginxサーバー自体が落ちてしまった時のダメージが計り知れません。
「単一障害点(SPOF)」を作らないためには、サーバーを冗長化し、1台が死んでも自動的に予備機に切り替わる仕組みが必要です。
次回、第5回は「絶対停止させない高可用性(HA)。KeepalivedによるVIP管理とフェイルオーバー」です。
仮想IPアドレス(VIP)を使って、アクティブ・スタンバイ構成を組むインフラエンジニア必須の技術「VRRP」の世界へご案内します。
物理サーバーでもクラウドでも使える、可用性設計の真髄を学びましょう。お楽しみに!
▼ OpenRestyを実践する環境を作る ▼
Luaスクリプトを試す
「VPS」で自分専用環境
インフラ開発スキルを活かす
「ITエンジニア転職」


コメント