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
の一つとして管理されているし、 main
でmaster/worker
プロセスが実行される前に呼び出されるコアな機能もCore Module
として管理されている。
モジュールにもいくつかあって、コアとしてあるもの、標準モジュールとして提供されているもの、オプションとして提供されているもの、サードパーティ/自作のものがある。
当然プラガブルな実装ということで抽象化されており、各々が好き勝手に実装されているというわけではなくいくつかのインタフェースを実装することで実現されている。
http://mogile.web.fc2.com/nginx/dev/development_guide.html#Modules
もし、自作したい場合はインタフェースに則った実装をすることで作ることができる。
この辺りは、 Hello world
のような最小単位の自作モジュールを作っている人もいるのでその辺りを参照すると良いかもしれない。
また、auto/module
や auto/modules
を読んでみると、どうやら module
は nginx
を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}
おわりに
Docker
で gdb
を用いながら nginx
を読もうとして結構色々ハマったので念のため残しておくことにした。
ここではあくまで準備した事しか書いていないが実際に読んで、カーネル弱弱すぎて、epoll
の使い方の基本を知ったり色々と勉強になる部分も多かった。
また、nginx
の内部をざっくりと理解するという点では、読んで良かったかなとも感じる。
とはいえ学習カロリーが半端ないし、このカロリー消費してやらないといけない事も結構あったりするので、本当に必要になるまでもうこれ以上を読むことはきっとないだろう。優先順位の問題ともいう。モチベーションがもたない