Python

Rocky Linuxで初めてのPython Webアプリ開発―Flask・Gunicorn・Apacheの環境構築をコマンドごとに解説

この記事は約14分で読めます。

PythonでWebアプリを作ってみたいと思っても、最初は「Pythonを入れた後に何をすればよいのか」「Apacheだけでは動かないのか」「FlaskとGunicornは何が違うのか」と迷いやすいものです。

この記事では、Rocky Linux上にPythonの実行環境を用意し、Flaskで作ったWebアプリをApache経由で公開するまでを、初めて学ぶ人向けに説明します。例として使うホスト名と設置先は次のとおりです。

ホスト名:python.example.com
設置先:/var/www/html/python.example.com

実際に試す場合は、python.example.comを自分のドメインへ読み替えてください。

PythonとPHPではWebアプリの動かし方が少し違う

PHPはApacheがPHPファイルを直接読み込み、処理結果を返す構成がよく使われます。一方、PythonのWebアプリでは、ApacheがPythonファイルを直接実行するのではなく、Gunicornというアプリケーションサーバーへアクセスを転送する構成が一般的です。

ブラウザ
  ↓
Apache
  ↓
Gunicorn
  ↓
Flaskで作ったPythonアプリ

それぞれの役割を簡単に整理すると、FlaskはWebアプリを作るためのフレームワーク、Gunicornは作成したアプリを常時動かすサーバー、ApacheはインターネットからのHTTP・HTTPSアクセスを受け付ける入口です。

Flaskを選ぶ理由

PythonにはDjango、Flask、FastAPIなどのWebフレームワークがあります。初めて小さなWebアプリを作る場合は、構成が単純で必要な機能を少しずつ追加できるFlaskが分かりやすいでしょう。

  • Flask:小規模なWebサイト、学習、簡単な業務アプリ向け
  • Django:ログインや管理画面を含む大きめのサービス向け
  • FastAPI:スマートフォンアプリや外部サービス向けのAPI開発に向く

今回はFlaskを使います。

1.Rocky Linuxを更新する

sudo dnf update -y

sudoは管理者権限でコマンドを実行する指定です。dnfはRocky Linuxでソフトウェアを管理する仕組み、updateはインストール済みパッケージを更新する操作です。-yを付けると、途中の確認へ自動的にyesと回答します。

本番サーバーでは更新による影響がないか確認してから実行してください。

2.Pythonとpipをインストールする

sudo dnf install -y python3 python3-pip

installはパッケージのインストール、python3はPython本体、python3-pipはPython用パッケージ管理ツールです。pipを使うことでFlaskやGunicornを追加できます。

インストール後にバージョンを確認します。

python3 --version
pip3 --version

--versionはインストールされているバージョンを表示する指定です。バージョン番号が表示されれば、Pythonを実行できる状態です。

3.Webアプリ用ディレクトリを作る

sudo mkdir -p /var/www/html/python.example.com
sudo chown -R rocky:rocky /var/www/html/python.example.com
cd /var/www/html/python.example.com

mkdirはディレクトリを作るコマンドです。-pを付けると、途中のディレクトリが存在しない場合もまとめて作成します。

chownは所有者を変更するコマンドです。-Rは内部のファイルやディレクトリにも再帰的に設定する指定です。ここではRocky Linuxへログインしているユーザー名をrockyとしています。別のユーザーを使っている場合は読み替えてください。

cdは作業するディレクトリを移動するコマンドです。

4.Pythonの仮想環境を作る

python3 -m venv venv

python3でPython 3を起動し、-m venvで仮想環境を作る標準機能を呼び出しています。最後のvenvは作成するディレクトリ名です。

仮想環境とは、Webアプリ専用のPython環境です。OS全体へFlaskなどを直接インストールせず、アプリごとに使用するパッケージとバージョンを分けられます。

続いて仮想環境を有効にします。

source venv/bin/activate

sourceは指定した設定ファイルを現在のシェルへ読み込むコマンドです。実行後、プロンプトの先頭に(venv)と表示されれば有効になっています。

仮想環境を終了するときは次を実行します。

deactivate

SSHで再ログインした後は仮想環境が解除されていますが、作り直す必要はありません。アプリのディレクトリへ移動し、再びsource venv/bin/activateを実行します。

5.pipを更新してFlaskとGunicornを入れる

python -m pip install --upgrade pip
pip install flask gunicorn

python -m pipは、現在有効になっているPython環境のpipを実行する書き方です。installはインストール、--upgradeは既に入っているパッケージを新しいバージョンへ更新する指定です。

2行目ではFlaskとGunicornを仮想環境内へインストールしています。

  • flask:URLとPythonの処理を結び付け、Web画面を作るフレームワーク
  • gunicorn:Flaskアプリを本番サーバーで動かすためのWSGIサーバー

6.最小のFlaskアプリを作る

/var/www/html/python.example.com/app.pyを次の内容で作成します。

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    return "Python Webアプリが動いています"

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=8000)

from flask import FlaskはFlask本体を読み込みます。app = Flask(__name__)はWebアプリを作成する処理です。

@app.route("/")は、トップページのURLへアクセスされたときに、その直後の関数を実行する指定です。returnでブラウザへ返す内容を指定しています。

最後のapp.run()は、app.pyを直接実行した場合に127.0.0.1の8000番ポートで開発用サーバーを起動する処理です。127.0.0.1は同じサーバー内部からだけ接続できるアドレスです。

7.開発用サーバーで確認する

cd /var/www/html/python.example.com
source venv/bin/activate
python app.py

別のSSH接続から次を実行します。

curl http://127.0.0.1:8000/

curlはコマンド上からHTTPアクセスを行うツールです。「Python Webアプリが動いています」と返れば、Flaskアプリは正常に動いています。

開発用サーバーは確認には便利ですが、本番公開を目的としたものではありません。本番ではGunicornを使います。

8.Gunicornで起動する

gunicorn --bind 127.0.0.1:8000 app:app

gunicornはアプリケーションサーバーを起動するコマンドです。--bind 127.0.0.1:8000は、同じサーバー内部の8000番ポートで待ち受ける指定です。

最後のapp:appは、コロンの左側がファイル名app.py、右側がファイル内で作成したapp変数を示します。

すでに8000番ポートが使用されている場合は「Address already in use」と表示されます。使用中のプロセスは次のコマンドで確認できます。

sudo ss -lntp | grep :8000

ssは通信ポートの使用状況を調べるコマンド、-lntpは待ち受け中のTCPポートとプロセスを数値表示する指定です。grep :8000で8000番を含む行だけに絞っています。

9.Apacheでpython.example.comだけを転送する

Rocky LinuxではApacheのサービス名とパッケージ名はhttpdです。

sudo dnf install -y httpd
sudo systemctl enable --now httpd

systemctlはサービスを管理するコマンドです。enableはOS起動時の自動起動を有効にし、--nowは自動起動の設定と同時に、その場でサービスも起動します。

/etc/httpd/conf.d/python.example.com.confを作り、次の設定を記載します。

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

    ErrorLog /var/log/httpd/python.example.com-error.log
    CustomLog /var/log/httpd/python.example.com-access.log combined

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:8000/
    ProxyPassReverse / http://127.0.0.1:8000/
</VirtualHost>

VirtualHost *:80はHTTPの80番ポート用設定、ServerNameは対象のホスト名です。ProxyPassをこのVirtualHostの中へ記載することで、python.example.comへのアクセスだけがGunicornへ転送されます。他のホストには影響しません。

ProxyPreserveHost Onは、ブラウザから送られたホスト名を転送先へ引き継ぎます。ProxyPassはApacheからGunicornへ転送する設定、ProxyPassReverseはGunicornから返された応答ヘッダーを外部公開用に調整する設定です。

この構成ではApacheがファイルを直接表示しないため、基本的にDocumentRootは必要ありません。ページの内容はFlaskが返します。

10.Apacheの設定を確認して反映する

sudo apachectl configtest
sudo systemctl reload httpd

apachectl configtestはApache設定の文法確認です。「Syntax OK」と表示されたことを確認してから反映します。

reloadはサービスを停止せずに設定を読み直す操作です。設定に誤りがある状態でいきなり再起動するより、安全に確認できます。

Rocky LinuxでSELinuxが有効な場合は、ApacheからGunicornへのネットワーク接続を許可します。

sudo setsebool -P httpd_can_network_connect 1

setseboolはSELinuxの許可項目を変更するコマンドです。-Pを付けると再起動後も設定が維持されます。実際に転送するホストはApacheのVirtualHost設定で指定したpython.example.comだけです。

11.本番ではGunicornをsystemdへ登録する

次のようなコマンドでGunicornをバックグラウンド起動することもできます。

gunicorn --bind 127.0.0.1:8000 --daemon app:app

ただし、手動起動ではサーバー再起動後に自動で立ち上がらず、異常終了時の復旧やログ確認も面倒です。本番ではsystemdへサービスとして登録します。

サービス名は任意ですが、何の処理か分かりやすいgunicorn-python-example.serviceとします。

sudo vi /etc/systemd/system/gunicorn-python-example.service

ファイルへ次を記載します。

[Unit]
Description=Gunicorn for python.example.com
After=network.target

[Service]
User=rocky
Group=rocky
WorkingDirectory=/var/www/html/python.example.com

ExecStart=/var/www/html/python.example.com/venv/bin/gunicorn     --workers 2     --bind 127.0.0.1:8000     app:app

Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

systemd設定の意味

  • Description:サービスの説明
  • After=network.target:ネットワークの準備後に起動
  • UserGroup:Gunicornを実行するLinuxユーザーとグループ
  • WorkingDirectory:アプリの作業ディレクトリ
  • ExecStart:実際に起動するGunicornのコマンド
  • --workers 2:リクエストを処理するワーカープロセスを2つ起動
  • Restart=always:Gunicornが停止した場合に再起動
  • RestartSec=5:再起動まで5秒待機
  • WantedBy=multi-user.target:通常のサーバー起動時に利用するサービスとして登録

仮想環境をsourceで有効にしなくても、ExecStartで仮想環境内のGunicornをフルパス指定しているため、正しい環境が使用されます。

12.サービスを登録して起動する

sudo systemctl daemon-reload
sudo systemctl enable --now gunicorn-python-example

daemon-reloadは新しく作成・変更したsystemd設定を読み直す操作です。設定ファイルを編集しただけではsystemdへ反映されないため、必ず実行します。

enable --nowにより、自動起動の有効化と現在の起動を同時に行います。状態は次で確認できます。

sudo systemctl status gunicorn-python-example

active (running)と表示されれば起動しています。

13.プログラム更新後の反映とログ確認

app.pyなどを変更した後は、Gunicornを再起動します。

sudo systemctl restart gunicorn-python-example

Pythonプログラムだけを変更した場合、Apacheを再起動する必要はありません。ApacheのVirtualHost設定を変更した場合だけ、構文確認後にApacheをreloadします。

Gunicornのログを直近100件確認する場合は次を実行します。

sudo journalctl -u gunicorn-python-example -n 100

リアルタイムで確認する場合は次のとおりです。

sudo journalctl -u gunicorn-python-example -f

journalctlはsystemdが管理するログを表示するコマンドです。-uは対象サービス、-n 100は直近100件、-fは新しいログを継続表示する指定です。

よくあるトラブル

502 Proxy Errorが表示される

ApacheからGunicornへ接続できていない可能性があります。Gunicornの状態、8000番ポート、SELinuxを順番に確認します。

sudo systemctl status gunicorn-python-example
sudo ss -lntp | grep :8000
sudo journalctl -u gunicorn-python-example -n 100

Address already in useと表示される

手動で起動したGunicornが残っている、または別のアプリが8000番ポートを使っています。使用中のプロセスを確認し、同じアプリを二重に起動しないようにします。

Apacheの既存サイトまで転送される

ProxyPassがVirtualHostの外側に書かれていないか確認します。python.example.comのVirtualHost内だけに記載すれば、このホストだけが転送対象になります。

まとめ

Pythonのコード自体は比較的読みやすい一方、Webアプリを本番公開するにはFlask、Gunicorn、Apache、systemdの役割を理解する必要があります。最初は登場人物が多く見えますが、それぞれの役割は明確です。

  • Flask:Webアプリの処理を作る
  • Gunicorn:Flaskアプリを本番環境で動かす
  • Apache:外部からのアクセスを受け付けてGunicornへ転送する
  • systemd:Gunicornの起動、停止、自動起動、再起動、ログを管理する

まずはトップページに文字を表示する最小アプリを動かし、仕組みを確認してからHTMLテンプレート、MySQL、投稿フォーム、ログイン機能などを追加していくと理解しやすくなります。

コメント

タイトルとURLをコピーしました