OpenResty背后的子项目

API7.ai

September 12, 2022

OpenResty (NGINX + Lua)

nginx.confからLuaコードを抽出し、読みやすく保守可能な状態に保つにはどうすればよいでしょうか?その解決策は非常にシンプルです。OpenRestyを使ってこれを実装する方法を見てみましょう。

まず、luaというディレクトリを作成します。そして、すべての.luaファイルをその中に配置します。

$ mkdir lua

$ cat lua/hello.lua
ngx.say("hello, world")

次に、nginx.confファイル内のcontent_by_lua_filecontent_by_lua_blockに置き換えます。

pid logs/nginx.pid;
events {
  worker_connections 1024;
}

http {
  server {
    listen 8080;
    location / {
      content_by_lua_file lua/hello.lua;
      }
    }
  }

最後に、OpenRestyのサービスを再起動して完了です!

$ sudo kill -HUP `cat logs/nginx.pid`

content_by_lua_fileを使用することで、nginx.confを更新する代わりにLuaファイルを直接更新できます。しかし、いくつかの疑問が生じます:

  1. content_by_lua_file lua/hello.luaセクションで相対パスを使用していますが、OpenRestyは実際のLuaファイルをどのように見つけるのでしょうか?
  2. Luaコードを変更した後、OpenRestyを再起動して反映させる必要がありますが、効率的にデバッグする方法はあるのでしょうか?
  3. Luaファイルを含むディレクトリをOpenRestyの検索パスに追加するにはどうすればよいでしょうか?

これらの疑問について考えることをお勧めします。これらはすべて公式ドキュメントで回答できます。そのため、私は常にドキュメントの重要性を強調しています。

最初の質問について、相対パスが指定されている場合、OpenRestyが起動する際に、OpenRestyの起動コマンドライン引数にある-p PATHをプレフィックスとして相対パスを絶対パスに連結します。これにより、OpenRestyはLuaファイルをスムーズに見つけることができます。

2番目の問題は、Luaコードが最初のリクエスト時にロードされ、デフォルトでキャッシュされることです。そのため、Luaソースファイルを変更するたびに、OpenRestyをリロードして反映させる必要があります。nginx.conflua_code_cacheをオフにすることで、リロードを回避できます。ただし、この方法は開発とデバッグのためだけに一時的に使用する必要があります。本番環境でデプロイする場合は、必ずキャッシュを有効にしてください。そうしないと、パフォーマンスに大きな影響を与えます。

最後の質問について、OpenRestyはLuaモジュールの検索パスを設定するためのlua_package_pathディレクティブを提供しています。例えば、lua_package_path$prefix/lua/?.lua;;に設定できます:

  • $prefixは起動パラメータの-p PATHです。
  • /lua/?.luaは、.luaサフィックスを持つLuaディレクトリ内のすべてのファイルを示します。
  • 最後の2つのセミコロンは、組み込みのコード検索パスを表します。

インストール後のディレクトリ構造

最初のHello Worldプログラムを理解した後、OpenRestyがインストールされた後のディレクトリ構造と、その中に含まれるファイルについて詳しく見ていきましょう。

まず、-Vオプションを渡して、OpenRestyがどこにインストールされているかを確認します。以下の結果では、多くのモジュールコンパイルパラメータを省略していますが、後で追加します:

$ openresty -V

nginx version: openresty/1.13.6.2
built by clang 10.0.0 (clang-1000.10.44.4)
built with OpenSSL 1.1.0h  27 Mar 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/openresty/1.13.6.2/nginx ...

私はMacでbrewを使ってOpenRestyをインストールしました。パスは/usr/local/Cellar/openresty/1.13.6.2/nginxで、環境によって異なる場合があります。このパスには、binluajitlualibnginxpodなどのディレクトリが含まれています。これらのフォルダの意味を理解することは、OpenRestyをよりよく学ぶために重要です。それでは、一つずつ見ていきましょう。

まずは重要なbinディレクトリです。

$ ll /usr/local/Cellar/openresty/1.13.6.2/bin

total 320
-r-xr-xr-x  1 ming  admin    19K  3 27 12:54 md2pod.pl
-r-xr-xr-x  1 ming  admin    15K  3 27 12:54 nginx-xml2pod
lrwxr-xr-x  1 ming  admin    19B  3 27 12:54 openresty -> ../nginx/sbin/nginx
-r-xr-xr-x  1 ming  admin    62K  3 27 12:54 opm
-r-xr-xr-x  1 ming  admin    29K  3 27 12:54 resty
-r-xr-xr-x  1 ming  admin    15K  3 27 12:54 restydoc
-r-xr-xr-x  1 ming  admin   8.3K  3 27 12:54 restydoc-index

これには、前のセクションで説明したOpenResty CLIのrestyと、コア実行ファイルであるopenrestyが含まれています。openrestyは実際にはnginxへのシンボリックリンクです。ディレクトリ内の他のツールは、restyと同様に、すべてPerlスクリプトです。

その中で、opmはパッケージ管理ツールで、さまざまなサードパーティパッケージを管理できます。これについては後ほどセクションで説明します。また、restydocは、OpenRestyが提供するドキュメントビューアで、OpenRestyとNGINXのドキュメントを閲覧できます。

$ restydoc -s ngx.say
$ restydoc -s proxy_pass

上記の2つの例は、それぞれOpenResty APIとNGINXコマンドを検索しています。restydocは、サーバーサイドエンジニアが開発に集中するための非常に役立つツールです。

binディレクトリを閲覧した後、次にpodディレクトリに進みます。

まず、ここでのpodはKubernetesのpodとは関係ありません。代わりに、podはPerlモジュールのドキュメントを書くために使用されるマークアップ言語です。このディレクトリには、OpenRestyNGINXlua-resty-*LuaJITのドキュメントが含まれており、これらは先ほど説明したrestydocと結びついています。

次に、おなじみのNGINXluajitディレクトリです。これらは主にNGINXとLuaJITの実行ファイルと依存関係を格納しており、OpenRestyの基盤です。多くの人がOpenRestyはLuaに基づいていると言いますが、それは正確ではありません。上記で見たように、OpenRestyは実際にはLuaJITに基づいています。

実際、初期段階では、OpenRestyにはLuaとLuaJITの両方が含まれており、コンパイルオプションによってLuaまたはLuaJITを使用するかを決定できました。しかし、Luaは廃止されつつあり、現在はより高性能なLuaJITのみがサポートされています。

最後に、lualibディレクトリを見てみましょう。これには、OpenRestyで使用されるLuaライブラリが含まれており、主にngxrestyの2つのディレクトリに分かれています。

  • ngxディレクトリには、公式のlua-resty-coreプロジェクトからのLuaコードが含まれており、これはOpenResty APIのFFIベースの再実装です。なぜ再実装が必要なのかについては、後ほど特別な章で説明します。
  • restyディレクトリには、さまざまなlua-resty-*プロジェクトからのLuaコードが含まれており、これについては次に触れます。

このコースの慣例に従い、この時点でこれらのディレクトリのソースを提供します。これはオープンソースプロジェクトの楽しみの一つでもあります。もしあなたが「なぜ?」と追求するのが好きなら、常にさらに興味深いものを見つけることができます。

以下は、CentOS用のOpenRestyのパッケージングスクリプトで、上記のすべてのディレクトリが含まれています。

%files
%defattr(-,root,root,-)

/etc/init.d/%{name}
/usr/bin/%{name}
%{orprefix}/bin/openresty
%{orprefix}/site/lualib/
%{orprefix}/luajit/*
%{orprefix}/lualib/*
%{orprefix}/nginx/html/*
%{orprefix}/nginx/logs/
%{orprefix}/nginx/sbin/*
%{orprefix}/nginx/tapset/*
%config(noreplace) %{orprefix}/nginx/conf/*
%{orprefix}/COPYRIGHT

OpenRestyプロジェクトの概要

OpenRestyと言えば、lua-nginx-moduleを思い浮かべるでしょう。確かに、このNGINX CモジュールはOpenRestyのコアですが、OpenRestyと同等ではありません。多くのエンジニアがOpenRestyをngx + luaと呼んでいますが、これは技術カンファレンスで共有・出版された書籍でも使用されています。これは厳密ではなく、OpenRestyコミュニティでも推奨されていません。

なぜそうなのか、そしてlua-nginx-module以外にOpenRestyにはどのような関連プロジェクトがあるのかについて説明します。

GitHubでOpenRestyのプロジェクトホームページを開くと、OpenRestyには68の公開プロジェクトがあり、それらはおおよそ以下の7つのカテゴリに分類されます。それぞれを簡単に紹介し、あなたが迅速に学べるようにします。

NGINX Cモジュール

OpenRestyのプロジェクト名は標準化されており、*-nginx-moduleという名前のものはNGINX Cモジュールです。

OpenRestyには20以上のCモジュールがあり、このセクションの最初に使用したopenresty -Vでそれらを見つけることができます。

$ openresty -V

nginx version: openresty/1.13.6.2
built by clang 10.0.0 (clang-1000.10.44.4)
built with OpenSSL 1.1.0h  27 Mar 2018
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/openresty/1.13.6.2/nginx --with-cc-opt='-O2 -I/usr/local/include -I/usr/local/opt/pcre/include -I/usr/local/opt/openresty-openssl/include' --add-module=../ngx_devel_kit-0.3.0 --add-module=../echo-nginx-module-0.61 --add-module=../xss-nginx-module-0.06 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.32 --add-module=../form-input-nginx-module-0.12 --add-module=../encrypted-session-nginx-module-0.08 --add-module=../srcache-nginx-module-0.31 --add-module=../ngx_lua-0.10.13 --add-module=../ngx_lua_upstream-0.07 --add-module=../headers-more-nginx-module-0.33 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.19 --add-module=../redis2-nginx-module-0.15 --add-module=../redis-nginx-module-0.3.7 --add-module=../ngx_stream_lua-0.0.5 --with-ld-opt='-Wl,-rpath,/usr/local/Cellar/openresty/1.13.6.2/luajit/lib -L/usr/local/lib -L/usr/local/opt/pcre/lib -L/usr/local/opt/openresty-openssl/lib' --pid-path=/usr/local/var/run/openresty.pid --lock-path=/usr/local/var/run/openresty.lock --conf-path=/usr/local/etc/openresty/nginx.conf --http-log-path=/usr/local/var/log/nginx/access.log --error-log-path=/usr/local/var/log/nginx/error.log --with-pcre-jit --with-ipv6 --with-stream --with-stream_ssl_module --with-stream_ssl_preread_module --with-http_v2_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-http_stub_status_module --with-http_realip_module --with-http_addition_module --with-http_auth_request_module --with-http_secure_link_module --with-http_random_index_module --with-http_geoip_module --with-http_gzip_static_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-threads --with-dtrace-probes --with-stream --with-stream_ssl_module --with-http_ssl_module

ここで--add-module=の後に続くのはOpenRestyのCモジュールです。コアとなるのはlua-nginx-modulestream-lua-nginx-moduleで、前者はレイヤー7のトラフィックを処理し、後者はレイヤー4のトラフィックを駆動します。

これらのCモジュールの中には、特別な注意が必要で、推奨されていないものもありますが、デフォルトでOpenRestyにコンパイルされています。例えば、redis2-nginx-moduleredis-nginx-modulememc-nginx-moduleは、RedisとMemcachedとの相互運用に使用されます。これらのCライブラリはOpenRestyの初期段階で推奨されていましたが、cosocket機能が追加された後、lua-resty-redislua-resty-memcachedに置き換えられ、現在はメンテナンスされていません。

OpenRestyは今後、より多くのNGINX Cライブラリを開発するのではなく、cosocketベースのLuaライブラリに焦点を当てる予定です。これが未来です。

lua-resty-* ライブラリ

公式のOpenRestyリポジトリには、18のlua-resty-*ライブラリが含まれており、Redis、MySQL、Memcached、WebSocket、DNS、トラフィック制御、文字列処理、プロセス内キャッシュなどの標準ライブラリが含まれています。公式のものに加えて、さらに多くのサードパーティライブラリがあります。これらは非常に重要であり、次のセクションでこれらのライブラリについてさらに時間を割きます。

自己管理のLuaJITブランチ

OpenRestyは、独自のOpenSSLパッチに加えて、独自のLuaJITブランチを管理しています。2015年、LuaJITの作者であるMike Pallは、新しいLuaJITのメンテナーを見つけるために引退を発表しましたが、Mikeは適切なメンテナーを見つけることができませんでした。現在は主にバグ修正のメンテナンスを行っており、新機能の開発は停止しています。そのため、OpenRestyは独自のLuaJITブランチを管理しています。

Luaと比較して、LuaJITは多くの重要なユニークな機能を追加していますが、多くのエンジニアがそれらを知らないため、半ば隠れたスキルとなっています。これについては後ほど紹介します。

テストフレームワーク

OpenRestyのテストフレームワークはtest-nginxで、Perlで開発されています。名前からわかるように、これはNGINX関連のプロジェクトをテストするために特別に設計されています。OpenRestyの公式テストケースは、Cモジュールとlua-restyライブラリのすべてがtest-nginxによって駆動されています。これは、一般的なアサーションベースのフレームワークとは異なり、はるかに強力で独立したシステムです。

一部のOpenRestyの貢献者もこのテストフレームワークを理解しておらず、複雑なCおよびLuaコードを含むPRを提出することがありますが、それに対応するテストケースを書くことにまだ恐れを感じています。そのため、OpenRestyプロジェクトの/tディレクトリ内のテストケースを見てまだ混乱している場合は、自分を疑わないでください。ほとんどの人は同じです。

test-nginxに加えて、mockeagainプロジェクトは、低速なネットワークをシミュレートし、プログラムが1バイトずつ読み書きできるようにします。これはウェブサーバーにとって非常に便利なツールです。

デバッグツールチェーン

OpenRestyプロジェクトは、科学的かつ動的にコードをデバッグする方法に多くの努力を費やしています。

OpenRestyの2つのプロジェクト、openresty-systemtap-toolkitstapxxは、systemtapという動的デバッグおよびトレーシングツールに基づいています。systemtapを使用する最大の利点は、ターゲットアプリケーションに対して完全に非侵襲的でありながら、生体内分析を可能にすることです。

例えば、systemtapは病院に行ってCTスキャンを受けるようなもので、痛みもなく、知覚もありません。さらに良いことに、systemtapはパフォーマンス分析のための視覚的なフレームグラフを生成できます。これについては後ほど説明しますが、ここではフレームグラフを見て感覚をつかんでください。

OpenResty関連のフレームグラフ

パッケージ

OpenRestyのパッケージングスクリプトは、さまざまなディストリビューションオペレーティングシステム(CentOS、Ubuntu、macOSなど)で手書きされており、より満足のいく制御性を実現しています。インストール後のディレクトリ構造を紹介した際に、これらのパッケージ関連のプロジェクト:openresty-packaginghome-brewについてすでに説明しました。これに興味がある場合は、自分で学んでください。ここでは繰り返しません。

エンジニアリングツール

これらの大きなプロジェクトに加えて、OpenRestyにはいくつかのエンジニアリングツールがあり、ほとんどが隠されています。

例えば、openresty-devel-utilsは、OpenRestyとNGINXを開発するためのツールセットです。もちろん、これらもPerlで開発されており、ほとんどのツールはドキュメント化されていません。しかし、OpenResty開発者にとって、これらのツールは非常に便利です。いくつかを選んで簡単に紹介します。

  • lj-relengは、シンプルで効果的なLuaJITコード検査ツールで、luacheckに似ており、グローバル変数の潜在的な問題を見つけることができます。
  • reindexは、インデックスを再構築することを意味し、test-nginxテストケースをフォーマットし、テストケース番号を再配置し、余分な空白を削除するツールです。reindexは、OpenResty開発者が毎日使用するツールの一つです。
  • opsboyは、自動化されたデプロイメントを行うために使用されます。OpenRestyが各リリース前にAWS EC2クラスタで行う回帰テストをデプロイおよび駆動するために使用されます。詳細については、公式ドキュメントを参照してください。opsboyはPerlで実装されたDSLです。OpenRestyの作者は、問題を解決するためにさまざまなDSLを作成するのが好きです。

まとめ

今日は、OpenRestyのインストール後のディレクトリ構造と、その背後にあるいくつかのサブプロジェクトについて主に学びました。今日の内容を学んだ後、OpenRestyのプロジェクトについてさらに理解を深めてほしいと思います。OpenRestyは、NGINXのロードバランシングとリバースプロキシの範囲をはるかに超え、独自のエコシステムを実現しています。次回はこれについて詳しく話します。

さらなる読書