あなたの最初のOpenRestyプログラム: Hello World

API7.ai

September 9, 2022

OpenResty (NGINX + Lua)

新しい開発言語を学び始める際、チュートリアルでは簡単な hello world の例が提供されます。そこで、まずインストールをスキップして、OpenResty でこの例をどのように書くかを見てみましょう。

$ resty -e "ngx.say('hello world')"
hello world

これはおそらく最もシンプルな hello world コードでしょう。Python と似ていますね。

$ python -c 'print("hello world")'
hello world

この背後には、OpenResty の哲学が表れています。コードは簡潔であるべきで、「入門から挫折まで」という考えを捨てられるようにするべきです。この記事では、hello world の例に焦点を当てます。

OpenResty と NGINX の違いの記事で述べたように、OpenResty は NGINX をベースにしています。そのため、ここで NGINX の影が見えないのはなぜかと疑問に思うかもしれません。心配ありません。1行のコードを追加して、resty の背後で何が動いているのかを見てみましょう。

$ resty -e "ngx.say('hello world'); ngx.sleep(10)" &

ngx.sleep メソッドを追加したので、文字列 hello world を出力した後にプログラムが終了しなくなります。これにより、次のように確認できます。

$ ps -ef | grep nginx
root     15944  6380  0 13:59 pts/6    00:00:00 grep --color=auto nginx

ついに NGINX プロセスが現れました!どうやら resty コマンドは本質的に NGINX サービスを起動しているようです。では、resty とは何でしょうか?

あなたのマシンには OpenResty がインストールされていないかもしれません。そこで、最初にスキップしたインストール手順に戻り、OpenResty をインストールしてから続けましょう。

インストール

他のオープンソースソフトウェアと同様に、OpenResty はさまざまな方法でインストールできます。例えば、OS のパッケージマネージャーを使う、ソースコードからコンパイルする、Docker イメージを使うなどです。しかし、まずは yumapt-getbrew などのパッケージマネージャーを使って OpenResty をインストールすることをお勧めします。この記事では、macOS を例にします。

$ brew tap openresty/brewbrew install openresty

他の OS でも同様です。まず、OpenResty のリポジトリ URL をパッケージマネージャーに追加し、その後パッケージマネージャーを使って OpenResty をインストールします。詳細な手順は、公式ドキュメントを参照してください。

しかし、この一見シンプルなインストールの背後には、2つの問題があります。

  1. なぜソースコードからインストールすることを勧めないのか?
  2. なぜ OS の公式リポジトリから直接インストールできず、別のリポジトリを設定する必要があるのか?

これらの質問についてまず考えてみてください。

ここで一つ追加したいことがあります。このコースでは、表面的なものの背後にある多くの「なぜ」を取り上げます。新しいことを学びながら考えることを望んでいます。結果が正しいかどうかは重要ではありません。残念ながら、技術分野でも独立した思考は希少です。各人の技術分野や深さの違いにより、どのコースでも教師は個人的な意見や知識の誤りを持ちます。学習プロセスでいくつかの「なぜ」を問い、それを理解することで、徐々に自分の技術体系を形成できます。

多くのエンジニアはソースコードからビルドすることを楽しんでいます。私も何年も前はそうでした。しかし、オープンソースプロジェクトを使う際、私は常に手動で configuremake を行い、いくつかのコンパイルパラメータを変更したいと思っていました。これがこのマシンの環境に最適で、性能を最大限に引き出す最良の方法だと感じていました。

しかし、現実はそうではありません。毎回ソースコードをコンパイルする際に奇妙な環境問題に遭遇し、つまずきながらインストールするしかありませんでした。今では、私たちの本来の目的はオープンソースプロジェクトを使ってビジネスニーズを解決することであり、環境との戦いに時間を浪費すべきではないと理解しています。パッケージマネージャーやコンテナ技術は、まさにこれらの問題を解決するために存在します。

本題に戻りましょう。OpenResty をソースコードからインストールする場合、手順が煩雑で、PCREOpenSSL などの外部依存関係を解決する必要があります。さらに、対応するバージョンの OpenSSL に手動でパッチを当てる必要があります。そうしないと、SSL セッション を扱う際に機能が不足します。例えば、yield を引き起こす ngx.sleep のような Lua API が使えなくなります。この部分について詳しく知りたい場合は、公式ドキュメントを参照してください。

OpenResty はこれらのパッチを OpenSSL パッケージスクリプトで自ら管理しています。OpenResty が OpenSSL のバージョンをアップグレードする際には、対応するパッチを再生成し、完全な回帰テストを行う必要があります。

Source0: https://www.openssl.org/source/openssl-%{version}.tar.gz

Patch0: https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-1.1.0d-sess_set_get_cb_yield.patch
Patch1: https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-1.1.0j-parallel_build_fix.patch

同時に、CentOS での OpenResty の パッケージスクリプトを見て、他の隠れたポイントがあるかどうか確認できます。

BuildRequires: perl-File-Temp
BuildRequires: gcc, make, perl, systemtap-sdt-devel
BuildRequires: openresty-zlib-devel >= 1.2.11-3
BuildRequires: openresty-openssl-devel >= 1.1.0h-1
BuildRequires: openresty-pcre-devel >= 8.42-1
Requires: openresty-zlib >= 1.2.11-3
Requires: openresty-openssl >= 1.1.0h-1
Requires: openresty-pcre >= 8.42-1

ここでわかるように、OpenResty は独自のバージョンの OpenSSLzlibPCRE を管理しています。ただし、後者の2つはコンパイルパラメータを調整しており、パッチは保持していません。

これらの要素を考慮すると、詳細を知らない限り、ソースコードから OpenResty をコンパイルすることはお勧めしません。

ソースインストールが勧められない理由は明らかでしょう。最初の質問に答える際に、2番目の質問にも答えました。なぜ OS の公式パッケージリポジトリから直接インストールできず、別のリポジトリを設定する必要があるのか?

これは、公式リポジトリがサードパーティが管理する OpenSSLPCREzlib パッケージを受け入れたくないためです。他のユーザーがどれを選ぶべきか混乱を招くのを防ぐためです。一方、OpenResty は正常に動作するために特定のバージョンの OpenSSLPCRE ライブラリを必要とし、システムのデフォルトバージョンは比較的古いものです。

OpenResty CLI

OpenResty をインストールすると、OpenResty CLI resty がデフォルトでインストールされます。これは Perl スクリプトであり、前述の通り、OpenResty のエコシステムツールはすべて Perl で書かれています。これは OpenResty の作者の技術的嗜好によるものです。

$ which resty
/usr/local/bin/resty

$ head -n 1 /usr/local/bin/resty
#!/usr/bin/env perl

resty CLI は非常に強力で、resty -h を使うか、公式ドキュメントを読むことで完全な機能リストを確認できます。次に、2つの興味深い機能を紹介します。

$ resty --shdict='dogs 1m' -e 'local dict = ngx.shared.dogs dict:set("Tom", 56) print(dict:get("Tom"))'

56

上記の例は、Lua コードを含む Nginx 設定を示しています。これは、共有メモリ辞書の設定とクエリを実現しています。dogs 1m は、dogs という名前の共有メモリスペースを 1m のサイズで宣言する Nginx 設定です。この共有メモリは、Lua コード内で辞書として使用されます。

また、--http-include--main-include パラメータは NGINX 設定ファイルを設定するために使用されるため、上記の例を次のように書き換えることができます。

$ resty --http-conf 'lua_shared_dict dogs 1m;' -e 'local dict = ngx.shared.dogs dict:set("Tom", 56) print(dict:get("Tom"))'

OpenResty の世界での標準的なデバッグツール、例えば gdbvalgrindsysetmtapMozilla rr も、resty と一緒に使用できます。これらは通常の開発とテストを容易にするため、resty の異なるコマンドに対応しています。内部実装は非常にシンプルで、コマンドライン呼び出しの追加レイヤーに過ぎません。valgrind を例に挙げましょう。

$ resty --valgrind -e "ngx.say('hello world'); "

ERROR: failed to run command "valgrind /usr/local/openresty/nginx/sbin/nginx -p /tmp/resty_rJeOWaYGIY/ -c conf/nginx.conf": No such file or directory

これらは OpenResty の世界だけでなく、サーバーサイドの一般的なツールでもあります。そのため、段階的に学んでいきましょう。

より正式な「hello world」

最初に書いた OpenResty プログラムは、resty コマンドを使って master プロセスなしで、特定のポートをリッスンせずに実行しました。次に、別の hello world プログラムを実装してみましょう。

これを完了するには、少なくとも3つのステップが必要です。

  1. 作業ディレクトリを作成する。
  2. NGINX 設定ファイルを修正し、Lua コードを埋め込む。
  3. OpenResty サービスを起動する。

まず、作業ディレクトリを作成しましょう。

$ mkdir openresty-sample
$ cd openresty-sample
$ mkdir logs/ conf/

以下は、OpenResty の content_by_lua ディレクティブを含む最小限の nginx.conf です。これにより、ngx.say メソッドが埋め込まれます。

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location / {
            content_by_lua '
                ngx.say("hello, world")
            ';
        }
    }
}

まず、openresty が PATH 環境変数に追加されていることを確認してください。その後、OpenResty サービスを起動します。

$ openresty -p `pwd` -c conf/nginx.conf

エラーが発生しなければ、OpenResty サービスは正常に起動しています。cURL コマンドを使ってアクセスし、返される結果を確認できます。

$ curl -i 127.0.0.1:8080

HTTP/1.1 200 OK
Server: openresty/1.13.6.2
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
hello, world

おめでとうございます!OpenResty での正式な Hello World が完成しました!

まとめ

今日学んだことを振り返りましょう。まず、OpenResty のインストールと CLI を「hello, world」の簡単なコードから始め、最後に OpenResty プロセスを起動し、実際のバックエンドプログラムを実行しました。

その中で、resty は今後頻繁に使用するコマンドラインツールです。そのため、このコースのデモコードは、バックグラウンドで OpenResty サービスを起動するのではなく、resty を使って実行されます。

さらに重要なのは、OpenResty の背後には多くの文化的・技術的な詳細が隠れており、海に浮かぶ氷山のようです。このコースを通じて、表面的な API だけでなく、より包括的な OpenResty を紹介できればと思います。