トップページに戻る

WSGIアプリのデプロイ


WSGIについて

詳細  昔ながらのWebページは、クライアント  (Webブラウザ) 側がWebサーバに対してリソースを要求し、
Webサーバが返すresponseをWebブラウザに表示するというように、
情報はWebサーバからクライアントへと一方向へ流れるのが主です。

 一方、最近のWebアプリは、クライアントからもサーバへ情報を送信しつつ、サーバとクライアントが
連携して機能しています。このため、この種のWebアプリを動かすには、
従来のWebサーバ (httpサーバ) だけでは不十分です。

Flask, FastAPI, Django, Dashなどは、pythonでインタラクティブなWebアプリを作成できる
Webフレームワークですが、上記の理由のため、WSGI (Web Server Gateway Interface) という、
pythonのWebアプリケーションとWebサーバー間のインターフェースを利用しています。


注意

以下の例では、多くの場合にWSGIプログラムの起動プログラムとしてapp.pyという、同一の名前を使っています。
pythonの仮想環境 dash を共有しているため、sys.path が共有されてしまい、想定しているのとは異なるapp.pyを
読み込むことがあるようで、httpdの再起動ごとにことなる挙動をしています。

これを回避するには、pythonの仮想環境を別々に作るか、sys.pathに入れたディレクトリのスクリプト名を
すべて変える必要がありそうです。実際には、app.pyを、[Application Name]_app.py のようにして名前が
被らないようにするのが無難そうです。

 この修正はおいおい進めていきますが、plotly-Dash-Flaskプログラム のプログラムでは、期待したものとは異なるプログラムが起動されることがありますが、ご容赦ください。


apache2 + mod_wsgi を使ったWSGIのインストール

上記のように、Webフレームワークを用いたpythonプログラムを
Webサーバ上で稼働させる (Deploy: デプロイと呼びます) ためには、
WSGIあるいは同等の機能をインストールする必要があります。

 本ページでは、AlmaLinux9のapache2にmod_wsgiをインストールする方法を説明します。

mod_wsgiの動作

 mod_wsgiでは、WSGIScriptAlias の設定に対応するURIでアクセスがあった場合、
mod_wsgiにより*.wsgiファイルが起動されます。

 そのため、以下の流れでインストール、設定を行います

インストール:

  1. apache、python、pythonモジュールのインストール (コンソールでのFlaskアプリの動作確認)
  2. pipで mod_wsgi をインストール
  3. /etc/httpd/conf/httpd.confの設定
    ・ mod_wsgiの.soを LoadModule

アプリごとの設定:

  1. アプリ作成
  2. .wsgiスクリプトの作成
  3. apacheがアプリのディレクトリ、ファイルへアクセスできるように許可
  4. httpd.confの設定
     起動用 .wsgi スクリプトのパスと Aliasの設定
     実行に使うpython環境、ライブラリパスの設定
  5. httpdの再起動

接続:

  1. Webブラウザから接続

使用しているpythonモジュール:

% pip uninstall dash dash-core-components dash-html-components dash-table -y
% pip install flask dash Werkzeug

で最新版をインストール。本ページ作成時のバージョンは以下の通り

(dash) [root@usrv1 test1]# pip list | grep -E "dash|Flask|Werkzeug"
dash 2.9.3
dash-core-components 2.0.0
dash-html-components 2.0.0
dash-table 5.0.0
Flask 3.1.0
Flask-Bcrypt 1.0.1
Flask-Compress 1.17
Flask-DebugToolbar 0.16.0
Flask-Login 0.6.3
Flask-SQLAlchemy 3.1.1
Werkzeug 3.1.3 

具体的な手順は以下の通り

アプリ群のhome: /home/conf/app
 注: セキュリティの観点から、httpdでアクセスできない (documnet_root以下以外) パスが望ましい
    ただし、本Webでソースを公開しているアプリは /D2MatE/web/wsgi 以下に作っている
python仮想環境: /home/conf/dash
 注: セキュリティの観点から、httpdでアクセスできない (documnet_root以下以外) パスに作る

1.apacheのインストール

  1. apacheはインストールして、一般的な設定で動いていることを前提
    $ sudo dnf install httpd -y
  2. ヘッダファイル等が必要なのでインストール
    $ sudo dnf install httpd-devel -y

2.pythonインストール

% sudo dnf install python3 -y
% sudo dnf install python3-devel -y
% sudo dnf install python3-pip -y

どのpythonを使うかを確認し、開発環境で必要であればPATHを設定

仮想環境を作った方がいい
・ apacheがpython、site_packagesにアクセスできる必要がある
・ dashの場合、apacheがdashに含まれるReactへアクセスできるようにする必要がある

本Webの場合、仮想環境はアプリケーションルートの下に作っている
% cd /home/conf/app         # WSGIアプリケーションルート
% python -m venv dash       # 仮想環境home
仮想環境のpythonのhome: /home/conf/app/dash
仮想環境のライブラリ: /home/conf/app/dash/lib/python3.11
 
% source ./dash/bin/activate

3.python moduleのインストール

  1. WSGIアプリをコンソールから起動し、動作確認をする
    必要であればmoduleをインストール
    $ sudo pip install --ugrade pip
    $ sudo pip install --ugrade wheel setuptools
    $ sudo pip install numpy scipy openpyxl chardet
    $ sudo pip install flask flask_login flask_bcrypt flask_sqlalchemy flask_debugtoolbar
    $ sudo pip install python-dotenv
    $ sudo pip install dash
    ($ install dash --upgrade)
     
  2. mod_wsgiをインストール
    pipにhttpdの場所を知らせる
    # export MOD_WSGI_APACHE_ROOTDIR=/usr/sbin/httpd
     
    # pip install mod_wsgi
     
  3. apacheにアクセス許可
    % chown -R apache.apache /home/conf/app/dash/*
    % chmod -R 775 /home/conf/app/dash/*
     
  4. モジュールのインストール先を確認
    % python3 -c "import mod_wsgi; print(mod_wsgi.__path__)"
    AlmaLinux9、python3.11で、仮想環境を使わない場合:
     /usr/local/lib64/python3.11/site-packages/mod_wsgi
     /usr/local/lib64/python3.11/site-packages/mod_wsgi/server/mod_wsgi-py311.cpython-311-x86_64-linux-gnu.so
    仮想環境を /home/conf/app/dashに作った場合:
    /home/conf/app/dash/lib64/python3.11/site-packages/mod_wsgi
    /home/conf/app/dash/lib/python3.11/site-packages/mod_wsgi
    /home/conf/app/dash/lib64/python3.11/site-packages/mod_wsgi/server/mod_wsgi-py311.cpython-311-x86_64-linux-gnu.so
    /home/conf/app/dash/lib/python3.11/site-packages/mod_wsgi/server/mod_wsgi-py311.cpython-311-x86_64-linux-gnu.so

4.httpdの設定ファイル

* /etc/httpd/conf/httpd.conf か、/etc/httpd/conf.d/wsgi.conf に以下の設定を入れる

WSGIPythonHome: 共通のpythonの実行ファイルパスのhome
python-home: アプリごとのpythonのhome。例えば実行ファイルが/usr/bin/python3.11であれば、/usr
WSGIPythonPath: pythonモジュールパス

WSGIScriptAlias: 設定ファイルcalendar.wsgiへのパスと、別名
WSGIDaemonProcess: アプリごとの設定。
  python-home: pythonのhome。仮想環境を使うならそのディレクトリ、使わない場合は省略してもよい
  python-path: pythonモジュールのパス
WSGIProcessGroup: アプリ固有の名前 

LoadModule: 他のLoadModuleのある所に追加 #WSGIモジュールのロード設定
LoadModule wsgi_module /usr/local/lib64/python3.11/site-packages/mod_wsgi/server/mod_wsgi-py311.cpython-311-x86_64-linux-gnu.so
VirtualHost: ここから sub-interpreterでpythonが動くため、シングルインタープリタモードにする設定として、
WSGIApplicationGroup %{GLOBAL} が必要

WSGIPythonHome /home/conf/app/dash
WSGIPythonPath /home/conf/app/dash/lib/python3.11:/home/conf/app/dash/lib/python3.11/site-packages

<VirtualHost *:80>
ServerAdmin kamiya.t.aa@m.titech.ac.jp
ServerName d2mate.mdxes.iir.isct.ac.jp
DocumentRoot /home/conf/public_html
ErrorLog logs/d2mate-error_log
CustomLog logs/d2mate-access_log common
ScriptAlias /cgi-bin/ "/home/conf/public_html/cgi-bin/"

# アプリケーション1: calendar
WSGIScriptAlias /calendar /home/conf/app/calendar/calendar.wsgi
WSGIDaemonProcess calendar python-home=/home/conf/app/dash python-path=/home/conf/app/dash/lib/python3.11:/home/conf/app/calendar
WSGIProcessGroup calendar

# アプリケーション2: chat
WSGIScriptAlias /chat /home/conf/app/chat/chat.wsgi
WSGIDaemonProcess chat python-home=/home/conf/app/dash python-path=/home/conf/app/dash/lib/python3.11:/home/conf/app/chat
WSGIProcessGroup chat

# アプリケーション3: fileTree
WSGIScriptAlias /fileTree /home/conf/app/fileTree/fileTree.wsgi
WSGIDaemonProcess fileTree python-home=/home/conf/app/dash python-path=/home/conf/app/dash/lib/python3.11:/home/conf/app/fileTree
WSGIProcessGroup fileTree

# Dashアプリケーション
WSGIScriptAlias /test1 /home/conf/app/test1/test1.wsgi
WSGIDaemonProcess test1 python-home=/home/conf/app/dash python-path=/home/conf/app/dash/lib/python3.11:/home/conf/app/test1
WSGIProcessGroup test1
WSGIApplicationGroup %{GLOBAL}

<Directory /home/conf/app/test1>
Require all granted
</Directory>

<Directory /home/conf/app/test1/assets>
Require all granted
</Directory>

<Directory /home/conf/app/calendar>
Require all granted
</Directory>

<Directory /home/conf/app/chat>
Require all granted
</Directory>

<Directory /home/conf/app/fileTree>
Require all granted
</Directory>
</VirtualHost>

5.アプリケーション起動スクリプト

 Dashアプリの場合、WSGIScriptAlias のルーティング先を、pythonスクリプトにも教える必要があるので、確認すること。
templateも対応する必要があるので、Flask vs Dashページを参照

WSGIScriptAlias /test1 /home/conf/app/test1/test1.wsgi の場合:
url_base = '/test1/'
app = dash.Dash(__name__,
           requests_pathname_prefix = url_base,
           serve_locally = True,
           )
server = app.server

必要に応じて以下も追加:
url_base_pathname = '/test1/',  (多分いらない)
assets_folder= '/home/conf/app/test1/assets', (assetsで404エラーが出たら追加)

5.アプリのwsgiファイル

* calendar.wsgi (Flaskの場合) の例:
import sys
sys.path.insert(0, '/home/conf/app/calendar/calendar.wsgi')
from calendar import app as application

最後のimport文は、

import アプリケーション起動pythonスクリプトのファイル名 import applicationのインスタンス変数名 as application

Dashの場合、applicationのインスタンス変数名は app.server (で取得できるFlaskオブジェクトなど) を指定する

6.httpdの再起動

$ sudo systemctl restart httpd

エラーが出た場合はstatusを確認することでエラー原因を特定する
$ sudo systemctl status httpd

7.アクセス

plotly-Dash-Flaskプログラム 参照

8.トラブルシューティング

Flask, Dashアプリ内でprint()で出力した内容はerror_logに出力される

再起動 (アプリ更新時に必要)


異なるVirtual hostを設定することも可能な様子 * httpd.conf

<VirtualHost *:80>
ServerName app1.example.com

WSGIScriptAlias / /home/conf/app/app1/app1.wsgi
WSGIDaemonProcess app1 python-path=/home/conf/app/app1
WSGIProcessGroup app1

<Directory /home/conf/app/app1>
Require all granted
</Directory>
</VirtualHost>

<VirtualHost *:80>
ServerName app2.example.com

WSGIScriptAlias / /home/conf/app/app2/app2.wsgi
WSGIDaemonProcess app2 python-path=/home/conf/app/app2
WSGIProcessGroup app2

<Directory /home/conf/app/app2>
Require all granted
</Directory>
</VirtualHost>

* アクセス方法

virtual hostを利用

app1:http://app1.example.com
app2:http://app2.example.com

WSGIScriptAliasを利用

app1: http://your_server_ip_or_domain/app1
app2:http://your_server_ip_or_domain/app2


以下の記事はMicrosoft Copilotによる回答で、私自身はまだ試していませんが、デプロイ方法の案ということでここに掲載しておきます。

1.簡単そうな方法: Gunicornというpython WSGI HTTPサーバを使用する

参考: https://qiita.com/Nats72/items/fd83bfea047a615ad3a2

% pip install gunicorn
% gunicorn -w 4 -b 0.0.0.0:8050 app:app

2.Gunicornによるdaemon化

 Webサーバ起動中に常時Webアプリを使えるようにするためには、Linuxでは daemon化 (サービス化) するのが一般的です。
以下に、Redhut系 (AlmaLinuxなど) での一般的なdaemon化について説明します。

  1. サービス起動スクリプトの作成: /etc/systemd/system/dash_app.service
    [Unit]
    Description=Dash App Service
    After=network.target

    [Service]
    Environment="MY_VARIABLE=value"
    Environment="ANOTHER_VARIABLE=another_value"
    ExecStart=/usr/bin/python3 /path/to/your/dash_app.py
    WorkingDirectory=/path/to/your
    Restart=always
    User=your_user
    Group=your_group

    [Install]
    WantedBy=multi-user.target
  2. サービス実行
    % sudo systemctl daemon-reload
    サービス起動
    % sudo systemctl start dash_app.service
    サービスをサーバ起動時に実行
    % sudo systemctl enable dash_app.service
    サービスの状態を確認
    % sudo systemctl status dash_app.service

3.Proxy設定のRedirectによる方法

  1. Apacheに必要なモジュール(mod_proxyやmod_proxy_httpなど)を有効にする
    % sudo dnf install httpd -y
    % sudo a2enmod proxy
    % sudo a2enmod proxy_http
     
  2. Apacheをリバースプロキシとして構成します。 Apacheがリクエストを受け取り、バックエンドで実行されているDashサーバーにプロキシしてリクエストを転送します。
     
    <VirtualHost *:80>
    ServerName yourdomain.com
     
    ProxyPass / http://localhost:8050/
    ProxyPassReverse / http://localhost:8050/
    </VirtualHost>