前回の記事でコアサーバで Python CGI は簡単に動くことがわかりましたが、外部ライブラリを使用したところエラーが出るようになりました。
ちょっと苦戦しましたが、なんとか動いたので原因と対策を書いておきます。
外部ライブラリの使用でエラーが発生?
まずは参考にしているページです。
2019年12月の記事ですが、このページではコアサーバーで3つの Python CGI を試してます。
この記事の「Hello World 表示サンプル」「入力値の表示サンプル」までは順調に実施できたのですが、「外部ライブラリ利用サンプル」を行ったところ「Internal Server Error」が表示されました。
まずは実際に実施した内容を記載しておきます。
元記事では ~/my-space に numpy、matplotlib パッケージのインストールをしてますが、python のバージョンが 2 と 3 共存しているので、とりあえずバージョン3のパッケージという意味で、~/my_py3 というディレクトリにしました。
(ちなみに自分も Python はちょっと触ったことがある程度の初心者です。笑)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
[cuzuser@xxxx ~]$ python3 -m pip install numpy matplotlib --target ~/my_py3 Collecting numpy Downloading https://files.pythonhosted.org/packages/14/32/d3fa649ad7ec0b82737b92fefd3c4dd376b0bb23730715124569f38f3a08/numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl (14.8MB) |################################| 14.8MB 12.8MB/s Collecting matplotlib Downloading https://files.pythonhosted.org/packages/d2/43/2bd63467490036697e7be71444fafc7b236923d614d4521979a200c6b559/matplotlib-3.3.3-cp36-cp36m-manylinux1_x86_64.whl (11.6MB) |################################| 11.6MB 47.2MB/s Collecting pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 (from matplotlib) Downloading https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl (67kB) |################################| 71kB 23.9MB/s Collecting pillow>=6.2.0 (from matplotlib) Downloading https://files.pythonhosted.org/packages/b6/c0/442d9d87e0da00bf856ef6dd4916f84a2d710b5f1a367d42d7f3c4e99a6c/Pillow-8.1.0-cp36-cp36m-manylinux1_x86_64.whl (2.2MB) |################################| 2.2MB 26.8MB/s Collecting kiwisolver>=1.0.1 (from matplotlib) Downloading https://files.pythonhosted.org/packages/a7/1b/cbd8ae738719b5f41592a12057ef5442e2ed5f5cb5451f8fc7e9f8875a1a/kiwisolver-1.3.1-cp36-cp36m-manylinux1_x86_64.whl (1.1MB) |################################| 1.1MB 40.4MB/s Collecting python-dateutil>=2.1 (from matplotlib) Downloading https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl (227kB) |################################| 235kB 32.7MB/s Collecting cycler>=0.10 (from matplotlib) Downloading https://files.pythonhosted.org/packages/f7/d2/e07d3ebb2bd7af696440ce7e754c59dd546ffe1bbe732c8ab68b9c834e61/cycler-0.10.0-py2.py3-none-any.whl Collecting six>=1.5 (from python-dateutil>=2.1->matplotlib) Downloading https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl Installing collected packages: numpy, pyparsing, pillow, kiwisolver, six, python-dateutil, cycler, matplotlib Successfully installed cycler-0.10.0 kiwisolver-1.3.1 matplotlib-3.3.3 numpy-1.19.5 pillow-8.1.0 pyparsing-2.4.7 python-dateutil-2.8.1 six-1.15.0 WARNING: You are using pip version 19.1.1, however version 20.3.3 is available. You should consider upgrading via the 'pip install --upgrade pip' command. [cuzuser@xxxx ~]$ ls my_py3/ PIL matplotlib-3.3.3.dist-info Pillow-8.1.0.dist-info mpl_toolkits Pillow.libs numpy __pycache__ numpy-1.19.5.dist-info bin numpy.libs cycler-0.10.0.dist-info pylab.py cycler.py pyparsing-2.4.7.dist-info dateutil pyparsing.py kiwisolver-1.3.1.dist-info python_dateutil-2.8.1.dist-info kiwisolver.cpython-36m-x86_64-linux-gnu.so six-1.15.0.dist-info matplotlib six.py matplotlib-3.3.3-py3.6-nspkg.pth [cuzuser@xxxx ~]$ cd public_html/test2.teqnobreaker.com/work [cuzuser@xxxx work]$ pwd /virtual/cuzuser/public_html/test2.teqnobreaker.com/work [cuzuser@xxxx work]$ vi draw.py [cuzuser@xxxx work]$ chmod +x draw.py |
draw.py のソースも元記事のものから日本語コメント無しにしたものを載せておきます。
また、元記事とはフォルダ構成が違うのでライブラリ読込パスを「../../../my_py3」としてます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#!/usr/local/bin/python3 import io import sys # add library path sys.path.append('../../../my_py3') # import NumPy and Matplotlib import numpy as np import matplotlib import matplotlib.pyplot as plt # make data x = np.arange(0, 6, 0.1) # 0 to 6 every 0.1 y1 = np.sin(x) y2 = np.cos(x) # plot graph plt.figure(figsize=(4, 3), dpi=160) # figure size plt.plot(x, y1, label='sin') plt.plot(x, y2, linestyle = '--', label='cos') # dashed line plt.xlabel('x') # x label plt.ylabel('y') # y label plt.title('sin & cos') # title plt.legend() # usage # make PNG image = io.BytesIO() plt.savefig(image, format='png') image.seek(0) # HTTP header sys.stdout.buffer.write(b'Content-Type: image/png\n\n') # output binary data sys.stdout.buffer.write(image.read()) |
そして、https://test2.teqnobreaker.com/work/draw.py にアクセスしてみたところ以下のエラーとなりました。
エラー原因の調査
「Internal Server Error」で表示されてるメッセージを見ると「コアサーバーの管理者に聞いてね」とのことか。
ちなみに error log に情報が出ているとあり、通常、webサーバー(apache)の error_log は /var/log/httpd/error_log、もしくは「/etc/httpd/conf/httpd.conf」で出力箇所を指定するようです。
しかし、/var/log、/etc 配下は「Permission denied」でアクセス権が無いので見れません。
ソースを見て原因になると思われるものを考えて潰して行きます。
① ライブラリの読込に失敗している「../../../my_py3」
もしかすると、public_html/test2.teqnobreaker.com/ 配下じゃないと参照できないとか?
② そもそも外部ライブラリを許してない
これはそんな制御ができるのかは不明。
③ ライブラリは読み込めているが画像の出力に失敗している
ライブラリが古いとかは無いだろうか。
まずは①を確認しようと、~/my_py3 を public_html/test2.teqnobreaker.com/work 配下に移動して動かしたけど「Internal Server Error」で変わらず。
あ、もしかするとライブラリが python 2 用になってたりするのかも?
そもそも、コアサーバーでライブラリのインストールはできるもんなんだろうか?
検索すると、以下の記事を発見。
あまり詳しくは書いて無いが他のページなども見た結果、以下がわかった。
・pip が古い場合があるので最初に pip のアップグレードをする
・--user を付けると自分のホームディレクトリの .local 配下にライブラリはインストールされる
・.local 配下だとライブラリのパスが通ってるのでソースでパスを追加する必要は無し
前回ライブラリをインストールした時のログをよく見ると、しっかり以下の警告が出てるでは無いか。
|
1 2 |
WARNING: You are using pip version 19.1.1, however version 20.3.3 is available. You should consider upgrading via the 'pip install --upgrade pip' command. |
ということで、pip のアップデートをして numpy、matplotlib をインストールした。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
[cuzuser@xxxx ~]$ python3 -m pip install --upgrade pip --user Collecting pip Downloading https://files.pythonhosted.org/packages/54/eb/4a3642e971f404d69d4f6fa3885559d67562801b99d7592487f1ecc4e017/pip-20.3.3-py2.py3-none-any.whl (1.5MB) |################################| 1.5MB 13.0MB/s Installing collected packages: pip Successfully installed pip-20.3.3 WARNING: You are using pip version 19.1.1, however version 20.3.3 is available. You should consider upgrading via the 'pip install --upgrade pip' command. [cuzuser@xxxx ~]$ python3 -m pip -V pip 20.3.3 from /virtual/cuzuser/.local/lib/python3.6/site-packages/pip (python 3.6) [cuzuser@xxxx ~]$ python3 -m pip install numpy matplotlib --user Collecting matplotlib Using cached matplotlib-3.3.3-cp36-cp36m-manylinux1_x86_64.whl (11.6 MB) Collecting numpy Using cached numpy-1.19.5-cp36-cp36m-manylinux2010_x86_64.whl (14.8 MB) Collecting cycler>=0.10 Using cached cycler-0.10.0-py2.py3-none-any.whl (6.5 kB) Requirement already satisfied: six in /usr/local/python3/lib/python3.6/site-packages (from cycler>=0.10->matplotlib) (1.11.0) Collecting kiwisolver>=1.0.1 Using cached kiwisolver-1.3.1-cp36-cp36m-manylinux1_x86_64.whl (1.1 MB) Collecting pillow>=6.2.0 Using cached Pillow-8.1.0-cp36-cp36m-manylinux1_x86_64.whl (2.2 MB) Collecting pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 Using cached pyparsing-2.4.7-py2.py3-none-any.whl (67 kB) Collecting python-dateutil>=2.1 Using cached python_dateutil-2.8.1-py2.py3-none-any.whl (227 kB) Installing collected packages: python-dateutil, pyparsing, pillow, numpy, kiwisolver, cycler, matplotlib Successfully installed cycler-0.10.0 kiwisolver-1.3.1 matplotlib-3.3.3 numpy-1.19.5 pillow-8.1.0 pyparsing-2.4.7 python-dateutil-2.8.1 [cuzuser@xxxx ~]$ |
この後、ライブラリのパスの指定無しで実行するも「Internal Server Error」のまま。
うーん、そもそもライブラリの読込で失敗してたりするのだろうか。
確認するために新しいライブラリを読み込むだけで「Hello, World!」を表示するソースを「draw1.py」として作ってみた。
|
1 2 3 4 5 6 7 8 9 10 11 |
#!/usr/bin/env python3 import io import sys import numpy import matplotlib print('Content-Type:text/html') print('') print('Hello,World!') |
これなら動くだろうと、https://test2.teqnobreaker.com/work/draw1.py を実施してみるが、まさかの「Internal Server Error」。
ということは、ライブラリ読み込みがダメってことなのか!?
そうか、試しにコマンドラインで実行したらエラーがわかるかも。
|
1 2 3 4 5 |
[cuzuser@xxxx work]$ ./draw1.py Content-Type:text/html Hello,World! [cuzuser@xxxx work]$ |
え!?エラーにならない!?
python を直接動かすとエラーにならないのに、httpアクセスでCGIとして動かすとエラーになるということは、httpアクセスの場合のユーザが違うから .local のパスを見てないということか?
python のライブラリパスは sys.path に入ってるそうなので、これを表示すればコマンド起動時とhttpアクセス時のライブラリパスの違いがわかりそう。
ということで sys.path を表示するプログラム「sys_path.py」を作ってみた。
|
1 2 3 4 5 6 7 8 9 10 |
#!/usr/local/bin/python3 import sys import pprint print('Content-Type:text/html') print('') print('Hello,World!') pprint.pprint(sys.path) print('') |
まずはこれをコマンドで起動してみる。
なるほど途中に「'/virtual/cuzuser/.local/lib/python3.6/site-packages'」が入ってる。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
[cuzuser@xxxx work]$ ./sys_path.py Content-Type:text/html Hello,World! ['/virtual/cuzuser/public_html/test2.teqnobreaker.com/work', '/usr/local/python3/lib/python36.zip', '/usr/local/python3/lib/python3.6', '/usr/local/python3/lib/python3.6/lib-dynload', '/virtual/cuzuser/.local/lib/python3.6/site-packages', '/usr/local/python3/lib/python3.6/site-packages', '/usr/local/python3/lib/python3.6/site-packages/mysqlclient-1.3.4-py3.6-linux-x86_64.egg'] [cuzuser@xxxx work]$ |
今度はhttpで起動。
あれ!?ちゃんと「'/virtual/cuzuser/.local/lib/python3.6/site-packages'」が入ってる!?
これは、、煮詰まりました。。
正解は?
いろいろ検索してなかなかわからなかったのですが、以下の記事を見てわかりました。
簡単に言うと、処理が重いCGIは応答時間が遅いのでwebサーバーが「Internal Server Error」にしてしまうようでした。
この記事では解決策として、php経由でxxx.pyを実行する方法が記載されています。
phpの場合は処理が多少遅くてもエラーにならないということですね。
このページにあるサンプルの test.py、test.php などを作って確かめてみると、確かに test.py はhttp起動ではエラーになるけど、test.php経由だと表示されました。
ただ、このサンプルでは test.py の出力結果を test.php で表示するというものなので、draw.py の場合のバイナリ出力を php 側でもらって画像として出すというのを作ろうと試みましたが、自分の技術では上手く作れなかったです。
元のやり方と違うのでイマイチではありますが、draw2.py で img.png というファイルを出力するようにして、draw2.php で img.png を表示するようにすることで動かすことはできました。
とりあえず外部ライブラリが動くことを確認できたので良しとしよう。笑
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/local/bin/python3 import io import sys import numpy as np import matplotlib import matplotlib.pyplot as plt x = np.arange(0, 6, 0.1) y1 = np.sin(x) y2 = np.cos(x) plt.plot(x, y1, label='sin') plt.plot(x, y2, linestyle = '--', label='cos') plt.xlabel('x') plt.ylabel('y') plt.title('sin & cos') plt.legend() plt.savefig("img.png") |
|
1 2 3 4 5 6 7 8 |
<?php $data = array(); exec("/virtual/cuzuser/public_html/test2.teqnobreaker.com/work/draw2.py 2>&1", $data); header('Content-Type: image/png'); readfile('/virtual/cuzuser/public_html/test2.teqnobreaker.com/work/img.png'); ?> |
画像ではdraw1.phpになってますが文に合わせてdraw2.php にしてます。たしかに表示が遅いです。
https://test2.teqnobreaker.com/work/draw2.php
コメント