Nginxのコードを読むために準備したこと

はじめに

僕には無理な話だった。

いろいろな事情があって、 nginxのモジュールの仕組みなどを知る必要が出てきた。 チョットデキル人になる必要はないが、完全に理解する必要があり、重い腰をあげてソースコードを読むことにした。

読み始めて3時間で後悔し、何週間かで完全理解するまでに自分が行ったことをメモとして残しておく。

読むにあたって準備したもの

今回、僕がNginxを読むためにいくつか準備したものがあるので記述しておく

今回Nginxのソースはgithubのものを用いることにし、リバースプロキシの部分を追いたかったので、 curl でリクエストしたら hello が返ってくるような ping 程度のAPIサーバとnginxサーバを Docker を用いて用意することにした。

またnginxのソースコードは非常に抽象化されており、僕のC言語のスキルでは太刀打ちできる気配もなかったため、gdb を用いてブレークポイントを貼ることで順次コードを追うアプローチをとることにした。僕にはvoidポインタが何者にも見えなかった。

gdbデバッグするにあたって公式のDebugging NGINX を参考に、 --with-debug を有効にするなど、あらかじめ nginxデバッグできるようなオプションをつけてビルドを行った。

余談だが、 上のような構成で gdb を起動する場合、 Docker 内で gdb -p ${pid} によってプロセスへアタッチする必要があるが、権限周りでオペレーションが拒否されたので注意が必要。

僕は docker--privileged オプションを使って gdb を実行することで回避した。

Nginxについて理解する

ソースコードを読む前に、そのソフトウェアがどういった背景や設計思想(アーキテクチャ)の元開発されたものかを知ることは非常に大事だと思う。

ソースコードの読解効率にも関わってくるし、書かれたコードの裏側にある意図をきちんと理解するためにもまずはソフトウェアの全体像を知っておいた方が良いはず。

というかむしろ全体像さえ知っておけばソースコード読まなくても困らないことの方が多いし、本当に必要にならない限りはコードを読むのはカロリーが高いし僕は身が持たない。 YAGNI の思考でいこう。

ここでは今回僕がソースコードを読む上で知っておいて良かったものに関して、ざっくり簡単に記載しておく。記載内容にあまり自信もなく精度もあまり良くないと感じているので、できればちゃんと自分で調べることをオススメする。

Nginx

エンジンエックスと読む。ンギックスではない。

一言で簡単にざっくり言えば、10K問題を解決したWebサーバーで、多重化されたノンブロッキングI/Oや非同期のイベント駆動アーキテクチャを採用したWebサーバ。

よく比較されていたApacheは、マルチプロセスモデルを採用しており差別化されていたらしい。 なお、Apacheも現在ではいくつかのモデルを採用できるのかもしれないが本筋ではないので調査しておらず不明。

詳しい話はいくつかのLinkを貼るのでそちらを参照してもらえれば良いと思う。下からいくらでもキーワードは抽出できると思うので、あとは自分で調べて欲しい。

Nginxのモジュールについて

この仕組みを引数などにとにかく副作用的に入れ込んで実現しているので読むの辛かった

全体の設計概念よりもう少し内部の実装に近い設計の話になるが、ソースコードを読むにおいては理解しておくべき内容だと思う。

nginx はプラガブルに設計されており、 nginxが持つ様々な機能は module という単位で管理されている。

例えば、リバースプロキシも module の一つとして管理されているし、 mainmaster/worker プロセスが実行される前に呼び出されるコアな機能もCore Module として管理されている。

モジュールにもいくつかあって、コアとしてあるもの、標準モジュールとして提供されているもの、オプションとして提供されているもの、サードパーティ/自作のものがある。

当然プラガブルな実装ということで抽象化されており、各々が好き勝手に実装されているというわけではなくいくつかのインタフェースを実装することで実現されている。

http://mogile.web.fc2.com/nginx/dev/development_guide.html#Modules

もし、自作したい場合はインタフェースに則った実装をすることで作ることができる。

この辺りは、 Hello world のような最小単位の自作モジュールを作っている人もいるのでその辺りを参照すると良いかもしれない。

また、auto/moduleauto/modules を読んでみると、どうやら modulenginx をbuildする際に、オプションなどをもとに nginx_modules.c を自動生成することでバイナリに含むようなアプローチをとっているみたい。

ソースコードを読む

ざっくり nginx について理解できたのであとは読むだけ。

nginx が起動しているコンテナ内で worker プロセスに対して gdb を起動し、nginx.conf で指定した port に対して curl でリクエストを投げる。

その後、br などを用いてブレークポイントを仕込んだりして、一歩一歩コードを読み進めていけば良いはず。

docker$ ps aux | grep nginx
docker$ gdb -p ${worker_process_id}
bash$curl http://localhost:${port}/${request_uri}

おわりに

Dockergdb を用いながら nginx を読もうとして結構色々ハマったので念のため残しておくことにした。

ここではあくまで準備した事しか書いていないが実際に読んで、カーネル弱弱すぎて、epoll の使い方の基本を知ったり色々と勉強になる部分も多かった。

また、nginx の内部をざっくりと理解するという点では、読んで良かったかなとも感じる。

とはいえ学習カロリーが半端ないし、このカロリー消費してやらないといけない事も結構あったりするので、本当に必要になるまでもうこれ以上を読むことはきっとないだろう。優先順位の問題ともいう。モチベーションがもたない