どんぶらアニマル さんぽ道

CBR250RR(MC22)とNSR80(HCO6)とAPE50(AC16)を中心とした備忘録。

pythonで書いたCGIでMySQLのデータベースにアクセスする

MySQLのデータベースに入ってるデータをグラフにしてwebから見られるようにしたくて、pythonでCGIを書こうとしたけど躓いたのでその対処の備忘録です。

 

pythonでwebアプリを作る場合、一般的にはdjango等のフレームワークを使うのが今時だと思うし、実際別件でdjangoを使い始めてる。でもフレームワークはフレームワークで使いこなすまでが大変だったりして、現にdjangoはやりたいことは頭に浮かんでて、データフローやHTML、javascript、pythonでの処理もわかってるのにdjangoのしきたりに合わせた書き方をしようとするとファイルの依存関係とかがややこしく感じてしまってなかなか進まない。。。長く継続的にフレームワークを触り続けるならいいんだろうけど、たまにしか使わない自分にはなかなかハードルが高い気がして、今回はフレームワークを使わない方法を採用した。

環境

今回の環境は以下の通り。

  • OS:Ubuntu 22.04.1 LTS(確認コマンド:lsb_release -a)
  • apache:Apache/2.4.55(確認コマンド:apache2 -V)
  • python:3.10.12(確認コマンド:python -V)

pythonは以下のエイリアスを切って使ってる。

alias python="/usr/bin/python3"
alias pip="/usr/bin/pip3"

apache2の設定

pythonに限らず、CGIを使うためにはapacheの設定が必要なので、まずはServer Worldさんのapache2でCGIを使う手順を参考にapacheの設定から。

 

CGIモジュールを有効にする

kirin@cf-n10$ sudo a2enmod cgid
[sudo] kirin のパスワード:
Enabling module cgid.
To activate the new configuration, you need to run:
  systemctl restart apache2

 

CGIの実行を許可するディレクトリを設定する

/etc/apache2/conf-available/cgi-enabled.confを作成して、実行するCGIを置くフォルダを設定する。

CGIを置くフォルダは/var/www/html/hogehoeとし、拡張子がcgi、pyのファイルを実行できるように設定した。

<Directory "/var/www/html/hogehoe">
    Options +ExecCGI
    AddHandler cgi-script .cgi .py

 

CGI の実行を許可する

a2enconfでCGIの実行を許可し、apacheに設定を反映させる。

kirin@cf-n10$ sudo a2enconf cgi-enabled
Enabling conf cgi-enabled.
To activate the new configuration, you need to run:
  systemctl reload apache2

kirin@cf-n10$ sudo systemctl reload apache2

 

動作確認

/var/www/html/hogehoeの下に、下記のtest01.pyファイルを作成。

#!/usr/bin/python3

print ("Content-Type: text/html\n\n")
print ("hello klirin!!")

 

スクリプトのパーミッションを設定。

chmod 755 test01.py

 

curlでアクセスしてみると正常に表示された。

kirin@cf-n10:$ curl http://xx.xx.xx.xx/hogehoge/test01.py

hello klirin!!

 

データベースにアクセスするスクリプトを作る

apacheの準備が整ったのでMySQLのdbからレコードを読み出して表示するスクリプトを作ったところ、500 Internal Server Errorになって動かなかったのでその対処のメモ。

 

MySQLを使うためにモジュールをインストールする

pip install mysql-connector-python

 

dbからレコードを読みだして表示するスクリプトを作成する

@IKEHさんの記事を参考にスクリプトを作ってみた。

 

コピペで作ったのがこれ(tmp2.py)。

#!/usr/bin/python3

import mysql.connector
import cgitb; cgitb.enable()

print ("Content-Type: text/html\n\n")

# DBへ接続
conn = mysql.connector.connect(
    user='root',         ← MySQLのid
    password='password', ← MySQLのパスワード
    host='localhost',
    database='hogehoge'  ← db名
)

# DBの接続確認
if not conn.is_connected():
    raise Exception("MySQLサーバへの接続に失敗しました")

cur = conn.cursor(dictionary=True)  # 取得結果を辞書型で扱う設定

query__for_fetching = """
SELECT
    time,
    name
FROM counters
ORDER BY time
LIMIT 5
;
"""

cur.execute(query__for_fetching)

for fetched_line in cur.fetchall():
    time = fetched_line['time']
    name = fetched_line['name']
    print(f'{time}:{name}<br>')

パーミッションを設定を忘れずに、

chmod 755 tmp2.py

 

動作確認

とりあえず、コマンドラインで実行してみると、意図した通の結果が得られた。

kirin@cf-n10:/var/www/html/hogehoge$ python tmp2.py
Content-Type: text/html


2023-02-20 00:15:05:counter100
2023-02-20 00:15:30:counter100
2023-02-20 00:16:35:counter101
2023-02-20 00:17:05:counter102
2023-02-20 00:17:20:counter100

 

curlを使ってwebアクセスしてみると、、、なんと500 Internal Server Errorが。

kirin@cf-n10:/var/www/html/hogehoge$ curl http://xx.xx.xx.xx/hogehoge/tmp2.py
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>500 Internal Server Error</title>
</head><body>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error or
misconfiguration and was unable to complete
your request.</p>
<p>Please contact the server administrator at
 webmaster@localhost to inform them of the time this error occurred,
 and the actions you performed just before this error.</p>
<p>More information about this error may be available
in the server error log.</p>
<hr>
<address>Apache/2.4.55 (Ubuntu) Server at xx.xx.xx.xx Port 80</address>
</body></html>

 

 

500 Internal Server Errorの調査

エラーの発生個所だけに絞った下記スクリプト(tmp1.py)で原因の調査をはじめた。先に原因を書くと問題はモジュールのimportだった。

#!/usr/bin/python3

import mysql.connector
import cgitb; cgitb.enable()

print ("Content-Type: text/html\n\n")
print ("hello kirin!!")

 

上記スクリプトにブラウザでアクセスすとInternal Server Errorになる。

もちろん、コマンドラインでpython tmp1.pyとすると正常に動作する。

CGIとして動かしているということは、apacheがスクリプトを実行しているわけで、apacheの実行ユーザ(www-data)では動かない何かがあるのかなといろいろ試してみた。

 

rootでは動くのか?

kirin@cf-n10:/var/www/html/hogehoge$ sudo -u root ./tmp1.py
Traceback (most recent call last):
  File "/var/www/html/hogehoge/./tmp1.py", line 3, in 
    import mysql.connector
ModuleNotFoundError: No module named 'mysql'だ

ダメだった。

 

まずは、モジュールが無いというエラーなので、モジュールのインストールパスを調べる。

kirin@cf-n10:/var/www/html/hogehoge$ python -m pip -V
pip 23.3.1 from /home/kirin/.local/lib/python3.10/site-packages/pip (python 3.10)

モジュールのインストールをしたのは、rootではなく一般ユーザなので、そのユーザのホームの下にある。これまで意識したことはなかった。。。

 

モジュールのパスを設定する方法をググったところ、レバテックのteratailにsys.path.append()を使えばできると書いてあった。

tmp1.pyにモジュールパスを追加するために赤字のコードを追加した。

#!/usr/bin/python3

import sys
sys.path.append("/home/kirin/.local/lib/python3.10/site-packages/")

import mysql.connector
import cgitb; cgitb.enable()

print ("Content-Type: text/html\n\n")
print ("hello kirin!!")

 

rootで試してみると動いた。

kirin@cf-n10:/var/www/html/hogehoge$ sudo -u root ./tmp1.py
Content-Type: text/html


hello kirin!!

 

パスの追加が効果あることはわかった。ではapacheの実行ユーザであるwww-dataで試してみる。

kirin@cf-n10:/var/www/html/hogehoge$ sudo -u www-data ./tmp1.py
Traceback (most recent call last):
  File "/var/www/html/hogehoge/./tmp1.py", line 6, in 
    import mysql.connector
ModuleNotFoundError: No module named 'mysql'や

やっぱりだめ。

 

多分、モジュールのフォルダのアクセス権が無いんじゃないかと試してみたら、予想通りだった。

kirin@cf-n10:/var/www/html/hogehoge$ sudo -u www-data ls /home/kirin/.local/lib/python3.10/site-packages/
ls: '/home/kirin/.local/lib/python3.10/site-packages/' にアクセスできません: 許可がありません

 

いろいろとググってみると、2つの対処法がありそうなことが分かった。

(1) モジュールをインストールしたユーザのグループにwww-dataを追加する
(2) rootにモジュールをインストールする

 

どっちが正当な対処方かというと、セキュリティ、管理面ともに(2)だなぁ。

 

500 Internal Server Errorの対策

rootにpythonのモジュールをインストールする。

 

まず、rootにmysql-connector-pythonがインストールされていないことを確認。

kirin@cf-n10:/var/www/html/hogehoge$ sudo pip list | grep mysql
kirin@cf-n10:/var/www/html/hogehoge$

MySQLに関するモジュールは何も入ってなかった。

 

mysql-connector-pythonモジュールをインストールする。

sudo pip3 install --upgrade pip
sudo pip install mysql-connector-python

 

インストールされたことを確認。

kirin@cf-n10:/var/www/html/hogehoge$ sudo pip list | grep mysql
mysql-connector-python 8.2.0

 

動作確認

モジュールのパスを追加していたコードはいらなくなるので、コメントアウト。

#!/usr/bin/python3

# import sys
# sys.path.append("/home/kirin/.local/lib/python3.10/site-packages/")

import mysql.connector
import cgitb; cgitb.enable()

print ("Content-Type: text/html\n\n")
print ("hello kirin!!")

 

www-dataで実行してみる。

kirin@cf-n10:/var/www/html/hogehoge$  sudo -u www-data ./mon.py
Content-Type: text/html


hello kirin!!

動いた。

 

ブラウザからアクセスしてみる。

kirin@cf-n10:/var/www/html/hogehoge$ curl http://xx.xx.xx.xx/hogehoge/tmp1.py

hello kirin!!

動いた。chromeからも同様に動いた。

 

結論は、apacheの設定の他に、スクリプトで使うモジュールをrootにもインストールする必要があったということ。

ここまで動けば、後はアプリを作っていくだけ。その過程でモジュールを追加した際にrootにインストールし忘れないようにしないと。

気が向いたら感想をお願いします。(ログイン不要、ボタンを押すだけです)