tl;dr
とあるサービスの移行作業で 200 個くらいのリダイレクト処理を ngx_mruby で実装したのでメモしておきます.
ngx_mruby とは
matsumotory さんが実装された Nginx に組み込むことで, Nginx の各種イベントをフックして mruby コードを実行することが出来る OSS です. Apache でも同様に mruby を実行出来る mod_mruby も同じく matsumotory さんによって実装されています.
リダイレクト案件
実はシンプル
今回のリダイレクトは特に複雑なことはなく, A サイト (https://A/path/100) を B サイト (https://B/2778425522) にリダイレクトをかけるだけです. ただし, リダイレクト元とリダイレクト先でパスの名前には関係性は無い為, リダイレクト 1 件毎に, リダイレクト元とリダイレクト先を記述していくことを検討しました. また, このような場合, 以下のように Nginx の rewrite を利用することで対応出来そうです. (他にも手法があるかもしれません)
以下のように, リダイレクト先が三件くらいまでなら, まあ, なんとか頑張れると思いましたが...
server { listen 80; server_name A; rewrite ^/path/100$ https://B/2778425522$1 permanent; rewrite ^/path/101$ https://B/5499644550$1 permanent; rewrite ^/path/102$ https://B/0739584639$1 permanent; }
ところが,今回はリダイレクト対象が csv で 200 件近く渡されました.
さて, どうしようか
先述の通り, rewrite 文を 200 個くらい並べるのは現実的ではありません. また, 今後, リダイレクト設定の追加や削除が想定される場合には, リダイレクトの管理は CSV に統一しておいたほうが良さそうです.
以下はリダイレクト元とリダイレクト先が記述された CSV ファイルのサンプルです.
/path/to/100,https://example.com/1483465573 /path/to/101,https://example.com/9009130268 /path/to/102,https://example.com/2431394607 ... /path/to/199,https://example.com/4117255063 /path/to/200,https://example.com/8894250467
ここで, ngx_mruby の登場です.
戦略として, CSV ファイルを読み込んでおいて, リクエストパスと照合して, 一致したら Location にリダイレクト先の URL を設定してステータス 301 を返すようにしたいと思います.
Nginx の設定
以下は Nginx の設定です.
user daemon; daemon off; master_process off; worker_processes 1; error_log stderr notice; #access_log /dev/stdout; events { worker_connections 1024; } http { map $http_user_agent $log_ua { ~ELB-HealthChecker 0; default 1; } access_log /dev/stdout combined if=$log_ua; server { listen 80; location ~ ^/path/to/.*$ { mruby_content_handler /usr/local/nginx/hook/redirects.rb; } } }
/path/to/xxxx
でアクセスされた場合, mruby_content_handler
で指定された mruby コードを実行するようにしています. この mruby コードで CSV ファイルを読み込み, パスのチェック, リダイレクト処理を実行します.
mruby コード
以下は mruby のコードです.
# /usr/local/nginx/hook/redirects.rb r = Nginx::Request.new redirect_csv = '/usr/local/nginx/conf/redirects.csv' hash = {} begin File.open(redirect_csv, 'r') do |f| while l = f.gets key, value = l.chomp.split(',') hash[key] = value end end rescue Nginx.errlogger Nginx::LOG_CRIT, "Could not read redirect list!!!" Nginx.return Nginx::HTTP_INTERNAL_SERVER_ERROR end key = r.uri if hash[key] Nginx.errlogger Nginx::LOG_INFO, "Redirect working! SRC: #{key} DST: #{hash[key]}" Nginx.redirect(hash[key], Nginx::HTTP_MOVED_PERMANENTLY) else Nginx.errlogger Nginx::LOG_NOTICE, "No redirect target. Move to https://example.com." Nginx.redirect("https://example.com", Nginx::HTTP_MOVED_PERMANENTLY) end
思ったよりもシンプルに書くことが出来て驚いています.
Nginx::Request
クラスにはリクエスト情報が含まれてて, インスタンスメソッドで各種情報 (スキーマやメソッド, パス等) を取得出来ますNginx
クラスはレスポンスを操作出来るメソッドが含まれていて,return
やredirect
等のクラスメソッドを利用しています- HTTP ステータスは定数として用意されていますので, 今回は 301 を返したいので
Nginx::HTTP_MOVED_PERMANENTLY
を利用しています
詳細はドキュメントをご一読下さい.
ちょっと動かしてみた
以下のリポジトリの Dockerfile を利用させて頂きました.
mruby コードと Nginx の設定, CSV ファイルを用意するだけで動作確認することが出来ました.
$ git clone https://github.com/matsumotory/docker-ngx_mruby.git $ cd docker-ngx_mruby $ vim docker/conf/nginx.conf # 先述の内容を記述 $ vim docker/conf/redirects.sample.csv /path/to/100,https://yahoo.co.jp/ /path/to/101,https://google.co.jp/ $ vim docker/hook/redirects.rb # 先述の内容を記述 $ docker build -t ngx_mruby . $ docker run -d -p 18080:80 ngx_mruby
コンテナを起動後, リクエストを投げてみます.
$ curl -s localhost:18080/path/to/100 -LI HTTP/1.1 301 Moved Permanently Server: nginx/1.15.12 Date: Sun, 14 Jun 2020 14:50:12 GMT Content-Type: text/html Content-Length: 170 Connection: keep-alive Location: https://yahoo.co.jp/ HTTP/1.1 301 Redirect Date: Sun, 14 Jun 2020 14:50:12 GMT Connection: keep-alive Cache-Control: no-store Location: https://www.yahoo.co.jp/ Content-Type: text/html Content-Language: en Content-Length: 306 HTTP/2 200 content-length: 0 content-type: text/html; charset=utf-8 date: Sun, 14 Jun 2020 14:50:12 GMT etag: W/"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk" set-cookie: B=65eq4ktfece94&b=3&s=nd; expires=Wed, 15-Jun-2022 14:50:12 GMT; path=/; domain=.yahoo.co.jp vary: Accept-Encoding x-vcap-request-id: 268b7a41-fdfa-4ce9-4d52-770b9291ad90 age: 0 via: http/1.1 edge1801.img.bbt.yahoo.co.jp (ApacheTrafficServer [c sSf ]) server: ATS
レスポンスから yahoo.co.jp にリダイレクトされていることが判ります. また, 以下のようなログが記録されています.
172.18.0.1 - - [14/Jun/2020:14:50:12 +0000] "HEAD /path/to/100 HTTP/1.1" 301 0 "-" "curl/7.54.0"
いい感じです.
最後に
ngx_mruby についてはほぼ初めての利用となりましたが, とても簡単に大量のリダイレクトを処理することが出来そうでエンジニアリング魂が揺さぶられました. これからも機会があれば ngx_mruby をはじめ mruby をギョームで利用出来れば良いなと考えています.