ホンモノのエンジニアになりたい

ITやビジネス、テクノロジーの話を中心とした雑記ブログです。

NginX+Gunicorn+Bottleでアプリケーションを公開する?

ローカル環境でPythonのBottleを使ってWEBアプリを書いて、いざ公開というときに実際のサーバ環境をどうするかで悩みました。

色々調べながら環境を作りましたが、非WEB屋の私には躓いたところも多く、悩み調べた内容、そして最終的に到達した理解の部分までこのウェブログに残しておきます。個人的な理解のため間違いもあるかもしれないですがご容赦ください。なおこのエントリでは各ミドルウェアの設定値については触れません。あくまで考え方や全体の構成についてのエントリになります。

  

1.本番でもBottleでrunするんでしょ?

WEBフレームワーク初心者の私にとっては、「BottleでrunってすればWEBサーバ動くじゃない、本番サーバでも80ポートでrunしてlistenしてればいいんでしょ?」と思っていました。

ただ調べていくとどうやらBottleのrunでrunさせるのは性能面や信頼性の観点で良くないらしい。確かにローカル仮想サーバでrunさせてる時には10回に1回くらいの割合でコネクションエラーが発生していました。

オープンソースプロジェクトとして時間をかけて進化してきたNginXやApacheと、フレームワークにちょこんと居座るビルトインサーバのどちらがWEBサーバとして優れているかといったらそりゃ前者って話なんでしょう。餅は餅屋の原理。

 

2.NginXからのSupervisorしたGunicornからのアプリケーション(Bottleなど)がトレンドなんだって

さて、どのお餅屋さんを使おうかしら、と調べていくとNginXをフロントに使ってSupervisorしたGunicornからアプリケーション(DjangoとかFlaskとかBottleとか)を稼働させるというのが最近のトレンドらしい。

  

トレンドってなんやねん!と思う気持ちもあるが、それは置いておこう。

ただ、ただただ素直に意味がわからない。

私が勉強不足なだけですが、正直何が何だか全く意味がわからなかった。NginXがWEBサーバというのは知っていましたし、アプリケーションは私の場合Bottleになるのでしょう。GunicornとSupervisorは初めて聞きました。

調べてみるとGunicornはPythonWSGIサーバというものらしい。WSGIサーバはWikipediaにこう書いてある。

Web Server Gateway Interface (WSGI; ウィズギー) は、プログラミング言語Pythonにおいて、WebサーバとWebアプリケーション(もしくはWebアプリケーションフレームワーク)を接続するための、標準化されたインタフェース定義である。

Web Server Gateway Interface - Wikipedia

 

なるほど合点!標準化されたインタフェース定義か!

って意味わからん!

 

Wikiのページの中にWSGIサーバという項があってこのように書いてある。

WSGIサーバ(WSGIアプリケーションコンテナ)は、WSGIアプリケーションを常駐させ、HTTPクライアントからリクエストを受け取るごとに、WSGIアプリケーションのcallableオブジェクトを呼び出す。これによって、クライアントからのリクエストがアプリケーションに転送される。

GunicornはWSGIサーバなので正にここに書いてあるお仕事をします。Bottleで書いたアプリケーションを常駐させて、クライアントからのリクエストが来ると適宜Bottleアプリへリクエストを渡していく。

 

ピンと来ない人もいるでしょうが、WSGI意味わからん勢は説明書読むより動かした方が理解が早いです。大抵のソフトウェアは動かした方が理解が早まると思うんですが、私は説明書を読みこむタイプなので、ガッツリ調べてしまいました。素直に動かせば何となくわかると思う。

 

私はGunicornを勉強しはじめた時に「WSGIは標準化されたインタフェース定義である」に対して「WSGIは定義?じゃあWSGIサーバは定義をServeしてくれんの?意味不なんだが!」と思ったが、そんな風に考えてはいけなかった。

だって例えば「WEBサーバ」はWEB自体をServeしてくれるわけではないし、もう少し具体的に「HTTPサーバ」と言ったとしても、Hyper-TextをTransferするProtocolをServeしてくれるわけじゃない。

あくまでも、Hyper-TextをTransferするProtocolに準拠した仕様のServiceをServeしてくれるものなわけです。WEBサーバやHTTPサーバという言葉を何となくそのまま受け入れているのであれば、同じようにWSGIサーバという言葉も受け入れなければ、それはWSGIさん差別です。HTTPサーバさんと同じようにフワっとした理解でWSGIサーバさんを受け入れられるか、オープンで優しい心を持つ者かどうか試される場と思って間違いない。

 

WEBサーバ・HTTPサーバという言葉と同水準でこのWSGIサーバのことを考えると、要はWSGIサーバというのは「Pythonにおいて、WebサーバとWebアプリを接続するための、標準化されたインタフェース定義に準拠した仕様のServiceをServeしてくれるサーバ」と解釈するのが正しいわけだ。

 

クソ面倒くさい説明を書きましたが、とにかくWEBサーバとWEBアプリを橋渡ししてくれるサーバという抽象的な理解に留めておいて問題はありません。それくらいの理解レベルで手を動かしていった方がトータルで早いです。「WSGIよくわからん...ワイ頭悪いのかも」なんて思い悩む必要は無いです。さーっとWikiを読んでざっくり理解して、設定して動かして、それから具体的な理解へ進めばよろしい。

 

さて、ここでGunicornが何となく理解できたものとすると、今回のNginX+Gunicorn+Bottleという全体構成について見通しがついた感じになると思う。大雑把に以下のような並びでリクエストを処理していくシステムになる。

 

Nginx <-> Gunicorn <-> Bottle

NginX・ ・・・フロントのWEBサーバとして動作

Gunicorn・・・NginXとBottleアプリを繋ぐ謎のインタフェース

Bottle ・ ・・・これはアプリケーション本体

 

ちなみに、ここまで登場しないSupervisorさんはGunicornさんのプロセスを管理する人になるため、全体の流れからは切り離して考えてOKです。あくまでWEBアプリケーションとしての処理はNginx <-> Gunicorn <-> Bottleの3人でワチャワチャやるものです。

 

3.インストールして繋げてみる

はてさて、ここから3人のお友達(Nginx、Gunicorn、Bottle)を連携させて求めるべき姿に仕上げていく。とりあえずBottleアプリは既にあるので、あとはNginXさんとGunicornさんを呼んでくることになる。

NginXとGunicornをインストールして、「いっせーのーせっ!」でくっ付けることもできますが、私はWEB周りはド素人だから1つずつ進めていくことにした。

これは私見100%ですが、非WEB屋さんや全くのド素人がいきなりガッチャンコでこの三人衆を繋げると、上手く接続できない時に原因の特定が難しくなるのでオススメしません。上手く接続出来たら出来たで、「何か知らんけど出来た」状態にしかならないのでエンジニアとしてどうなのよという話もあるし、運用中のトラブルなど発生しようもんなら手に負えなくなるので、やっぱり初心者は1つずつ接続を確認して誰がどういう仕事をしてくれているのか理解することが重要だと思う。

 

さて、それでは1つずつ繋げていくとして、どこを動かして繋げていけばいいか、私が辿った道を紹介します。

①Bottleの単独動作(本番サーバでrunして動作することを確認する)

②NginXの単独動作

③NginXをリバースプロキシとして動作させrunさせたBottleアプリへ接続

④GunicornからBottleアプリへの接続

⑤NginX→Gunicorn→Bottleの接続

 

①Bottleの単独動作(本番サーバでrunして動作することを確認する)

まずは開発環境で作成したBottleアプリケーションが単独動作することを確認する。これはOS設定やPython、パッケージ周りのインストールがミスってないかを確認するテストになる。ここの動作が担保されていないとNginX・Gunicornと繋いだところでうまくいかない時の原因追究が分かりづらくなるだけだから、先に単独で動かしてみる。サーバ上でlocalhost向けにcurlコマンドを打って期待するレスポンスが返って来ればそれでよろしい。

 

②NginXの単独動作

NginXを動かして外の世界からウェルカム画面やHello Worldあたりが出ることを確認しておく。ネットワークやFirewallといった外面の問題が無い事がここで担保される。

 

③NginXをリバースプロキシとして動作させrunさせたBottleアプリへ接続

BottleとNginXがそれぞれ動作したら、とりあえず繋げてみたくなるのが人情。別にこのプロセスは踏まなくてもよかったかもしれません。Bottleアプリを適当なポート(8080ポートとか)でrunさせる。NginXは80ポートでListenさせてリバースプロキシで8080ポートへリクエストを流す。NginXのリバースプロキシが動作することの確認になる。ここの確認は外の世界からブラウザアクセスするのがいいと思う。

 

④GunicornからBottleアプリへの接続

Gunicornをインストールして、GunicornからBottleアプリを起動する。Bottleアプリはrunの行をコメントアウトしておくと確実にGunicornで動作していることが確認できる。Gunicornは外から見るとwebサーバとして動作するので、curlコマンドかブラウザアクセスで確認できる。

 

⑤NginX→Gunicorn→Bottleの接続

GunicornとBottleの接続が取れているので、頭にNginXを噛ませる。NginXのリバースプロキシの向き先をGunicornに設定する。これで目的の形に。

 

ここで調べてもわからなかったことが1つ。NginXからGunicornへリクエストを流す際にIPアドレス+ポートで繋げるか、ソケット通信で繋げるか。色々見てみましたが、みんなどっちというわけでもなく、ある人はIPアドレス、ある人はソケット通信と割とばらばらのご様子でした。性能や拡張性とか保守性とか違いはあるんでしょうけど、いまいちどっちがどう優れているのかはわからなかったです。ソケットの方が速そうと思って私はソケットで繋げました。

 

WEBド素人の私がこの構成でWEBサービスを稼働させて面白いなと思ったのは、NginXは当然として、GunicornもBottleアプリもみんなやろうと思えばWEBサーバとして動作できるところです。それぞれ単独でも動かせるし、それぞれを連携させて動かすこともできる。これは想定通りに動いてくれない時に問題点を局所化出来るので、構築している時のトラブルシュートがすごく楽でした。

ただ一方でちゃんと理解しないと運用中のトラブルに対応出来ないだとか、実は間違った状態で稼働していたとかってことが起こりそうな気がします。あと理解に至るハードルが割と高い。WSGIも動かすまでは何してくれるものか分かりづらいし、構成上、中間にいるGunicornの存在は外から認識しづらいという面がある。NginXはフロントで動くので80ポートとコネクションが取れるかである程度外から挙動が見えるし、バックにいるBottleアプリは最終的なレスポンスによって動作が見える。Gunicornは動作が外から見えないのでハードルが高いです。それぞれのログを見れば動作状況はわかりますが、やっぱりド素人に対してはハードルが高く見える。

本当のITド素人がPython(やRails)アプリ作ってIaaS環境で公開することは多くないと思いますが、、、、あ、Herokuってもしかして、この辺のメンドイ所を楽にしてくれるのかしら

 

おわりに

このエントリはローカル環境で作っていたおもちゃアプリを公開しようと思い、環境面を調べていく中で、よくわかんねーなと思ったのがきっかけでした。「世の情報を集めて整理していってもイマイチ理解できない、それじゃ自分がまとめよう」と勢い込んで書き始めたのですが、長いだけで具体的な設定値も書けてないし、はたして意味があるのかもはやわかりません。

誰か一人でもいいから、このエントリが役に立ってくれるといいなと思い、筆を置きます。