サーバーがいっぱい。PythonのCherryPyを使って、リーンな環境でスケーラブルなプロトタイプを素早く作成する
TL;DR
年配のエンジニアになった結果、私は単純にプロジェクトを完了させたいと思うようになりました。 動作しない、実装が不十分、ドキュメントが不十分なライブラリをハックすることに時間を費やすのは、コーディングの楽しみではなく、面倒なことです。 だからこそ、私は次のような人たちと一緒に仕事ができたことを嬉しく思っています。 CherryPy 最近では
- Dockerファイルは複雑ではない
- 静的コンテンツを素早く提供できる
- Pythonスクリプトは第一級市民
- テストはわかりやすい
- 私のアプリケーションをスケーリングすると、ワンライナーになります。
CherryPyとは?
CherryPyはミニマルなPythonウェブフレームワークです。 企業向けソフトウェアの大規模で完全なフレームワークに取って代わることはできないが、何かを立ち上げて実行するには素晴らしい。 伝統的には、私は SimpleHttpServer しかし、機能が少なく、サーバーに負荷をかけるようなことをするとすぐにアップデートしなければなりませんでした。 CherryPyを使うことで、大きなフレームワークに乗り換えることなく、アプリケーションを十分に拡張することができます。 参考までに、Digital Oceanでは 比較 パイソンの WSGI のサーバーを使用しています。
DockerとCherryPy
ShareThis ではすべての作業に Docker を使用していますので、テストを兼ねた完全な開発環境を構築するために、まずこの作業を行いましょう。 私たちのリポジトリをクローンするだけで、この作業をすぐに始めることができます。 最初にこれをセットアップするには10分ほど余分にかかりますが、コーディングの最初の数日間で何倍もの利益を得ることができます。 Dockerの中のCherryPyとコマンドラインのSimpleHttpServerを交換するための唯一のコストと言っても過言ではありません。
ドッカーファイル
FROM python
RUN pip install cherrypy coverage # cherrypy server + python 3 test coverage
ADD *.py /
ADD *.sh /
CMD python run.py
testing.sh
#!/bin/bash
coverage run -a --omit=test.py /test.py
coverage report -m
run.sh
#!/bin/bash
# docker kill and rm prevent funny errors
docker kill cherrypy > /dev/null 2>&1
docker rm cherrypy > /dev/null 2>&1
# build, test, and run the image
docker build -t cherrypy .
docker run cherrypy /testing.sh
docker run -ti --name cherrypy -p 80:80 cherrypy $@
test.py
import unittest
class TestMethods(unittest.TestCase):
def test_simple(self):
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()
ファースト・パススクリプトコンテンツ
誰かがあるページにたどり着くかどうか、無駄のない実験を始めたいとしましょう。 一番手っ取り早いのは、静的なページを配信して、アクセス数を記録することです。 欠落しているrun.pyスクリプトを追加しましょう。
run.py (V1)
import cherrypy
class Landing(object):
@cherrypy.expose
def index(self):
return scripted landing
if __name__ == "__main__":
cherrypy.quickstart(Landing(), '/')
この時点で、あらゆることができるスケーラブルなWebサーバーができあがりました。 私はこれを、ダッシュボードやモニタリング、その他多くの社内ツールに利用しています。
この時点で、設定の変更を開始したいと思うでしょう。 例えば、ウェブサーバーを127.0.0.1ではなく、0.0.0で宣言し、パブリックルーティングを行いたいとします。 ライブトラフィックのために、ポート80でサービスを提供したい。 複数のライブ接続のために10個以上のスレッドが必要である(詳細は後述)。 トレイリングスラッシュがあってもトラフィックをルーティングしたい。 を使用しています。 その他諸々 を実現します。
への呼び出しを追加して、いくつかのコンフィグを追加してみましょう。 cherrypy.config.update
. さらに、静的コンテンツと設定ファイルの読み込みを許可するために、以下のように変更します。 cherrypy.quickstart(Landing(), '/')
にしています。 cherrypy.quickstart(Landing(), '/', "prod.conf")
. prod.confをDockerfileに追加することを忘れないでください!。
run.py (V2)
import cherrypy
class Landing(object):
@cherrypy.expose
def index(self):
return scripted landing
# The config.update is optional, but this will prevent scaling issues in a moment
cherrypy.config.update({
'server.socket_host': '0.0.0.0', # 127.0.0.1 is default
'server.socket_port': 80, # 8080 is default
'server.thread_pool': 100, # 10 is default
'tools.trailing_slash.on': False # True is default
})
if __name__ == "__main__":
cherrypy.quickstart(Landing(), '/', "prod.conf")
このあたりになると静止画も必要になってくるでしょう。 静止画を参照する設定ファイルを追加してみましょう。 静的資産はいくつでも追加できます。 私は宣言型のアプローチを好みますが、次のような方法もあります。 ディレクトリの提供 もあります。
prod.conf
[/logo.png]
tools.staticfile.on = True
tools.staticfile.filename = "/logo.png"
急速に拡大する
プロトタイプをスケールアップするということは、ブロッカーを素早く取り除くことです。 ここでは、CherryPyに影響を与えるよくある問題と、それらをできるだけ早く取り除く方法について説明します。
ヘルスチェックと追加ハンドラー
ShareThis では、Kubernetesの中でアプリケーションを実行しています。 をAWS上で実行しています。 コンテナが生きているかどうか、ロードバランサーが健全であるかどうかをシステムが知るために、Dockerコンテナの状態を監視する必要があります。 これは大まかに言えば、追加のハンドラーの必要性に当てはまります。 ここでは、シンプルなHealthCheckエンドポイントを書き、ハンドラーをLandingオブジェクトに追加する簡単な例を示します。 これで、次のことができるようになります。 http://localhost/healthcheck を使って、サーバーが生きているかどうかを確認します。
...
class HealthCheck(object):
def index(self):
return "Yay
\n"
index.exposed = True
class Landing(object):
healthcheck = HealthCheck()
...
Kubernetes上でのモニタリングの副作用として、ネットワークが複雑さを隠してしまうことがあります。 Kubernetesクラスタの各ノードには、その時点でたまたまDockerコンテナが動作している場所にデータをシャッフルするプロキシがあります。 AmazonのElastic Load Balancer (ELB)は、クラスタに対するヘルスチェックを行うために、各ノードへの接続を維持しようとします。 つまり、クラスタ内のすべてのノードについて、サーバで利用可能なスレッド数を考慮しなければなりません。 20ノードのクラスタでは、ELBが一度に20のスレッドにアクセスするため、テストアプリが機能しません。 CherryPyのデフォルトは10スレッドなので、プロキシが継続的にローテーションから追い出されてしまいます。 したがって、上記のように100スレッドに増やすことで、運用上の問題なくスケールすることができます。
テストの追加
変更したときに何が起こるかわからない複雑さは、生産性を低下させます。 テストスイートにシンプルなコントラクトを追加して、コードが何を意図しているかを将来の開発者(自分を含む)に伝えることで、多くのオーバーヘッドなしにコードのスケーリングを維持することができます。
スクリプトとカバレッジツールを用意しましたので、任意のpythonスクリプトにimport文を追加すれば、あとはカバレッジが処理してくれるはずです。 ただし、その際には mock
パッケージをあなたの pip install
ステートメントを使用すると、テストが容易になります。
概要
CherryPyはドキュメントが充実していて、うまくスケールアップしてくれます。 多くの場合、スケールした環境でアプリを立ち上げるのに必要なのはこのテンプレートだけです。 テンプレートから始めれば、すぐにpythonコードをネイティブに実行するWebサーバーを構築できます。 ShareThis では、レストインターフェイスによるバックエンドデータの処理、S3やBQのフィードの監視、データ用のAPIに接続する必要のあるダッシュボードの迅速な立ち上げなどに使用しています。 Dockerを使うことで、本番環境でのアプリケーションのデプロイとメンテナンスが簡単になりました。