PR

ロリポップレンタルサーバーのCGIでFlaskアプリを作ってみた

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

レンタルサーバーでPythonのWebフレームワーク「Flask」を使ったアプリケーションを作りたいことはありませんか?通常、Flaskアプリケーションはサーバー上で常時起動するものですが、ロリポップなどの一般的なレンタルサーバーではCGI環境でのみPythonを実行することが可能です。

この記事では、CGI環境でFlaskアプリケーションを動かす方法と、通常のFlask開発環境との違いを解説します。データベースとの連携方法も含め、シンプルなユーザー管理アプリケーションを例に実装してみましょう。

スポンサーリンク

CGI環境とは?

CGI(Common Gateway Interface)は、Webサーバーが外部プログラムと通信するための標準的な方法です。レンタルサーバーではPHPのように常時起動するアプリケーションサーバーを立てられないことが多いため、リクエストごとにPythonスクリプトを起動するCGI方式を使用します。

通常のFlask環境とCGI環境の違い

通常のFlask環境(常時起動する場合)

  • アプリケーションサーバー(Gunicorn, uWSGI など)が常時起動
  • URLルーティングはFlaskが直接処理
  • 開発時は flask run コマンドで起動
  • リクエストごとにプロセスの起動・終了が発生しない

CGI環境(レンタルサーバーなど)

  • リクエストごとにPythonプロセスが起動・終了
  • URLルーティングは .htaccess で設定
  • wsgiref.handlers.CGIHandler を使用
  • パスのプレフィックスを考慮したリダイレクト設定が必要(ただし、.htaccess の設定次第では通常のリダイレクトも可能)

注意事項

ロリポップではCGI利用に関する注意事項があります。ルールを守って利用しましょう。

CGIを利用する前に、ローカル環境で十分にテストしてください。万が一、CGIが暴走してサーバーをダウンさせたり、負荷をかけるようなことがあった場合、以降CGIを利用できなくなります。
高負荷なCGIやPHPを使用したゲーム、Webリングなど、他のサーバーから呼ばれるスクリプトやサーバに過度な負荷をかけるものは禁止されています。詳細は利用規約や禁止事項をご確認ください。

詳細はこちら: ロリポップ – CGI利用の注意事項

仮想環境の準備

レンタルサーバーでは、システム全体にパッケージをインストールできないため、Pythonの仮想環境を作成してパッケージをインストールします。
SSH接続が可能なサーバーであれば、以下のようにセットアップします。

# ホームディレクトリに移動
cd ~/web

# 仮想環境を作成
python3 -m venv myenv

# 仮想環境を有効化
source myenv/bin/activate

# 必要なパッケージをインストール
pip install flask mysql-connector-python

# 仮想環境を終了
deactivate

SSH接続ができない場合は、ローカルで仮想環境を作成し、必要なファイルをFTPでアップロードするといった方法もあります。

実装サンプル

今回はロリポップサーバー上でFlaskとMySQLを使った簡単なユーザー管理アプリケーションを作成しました。

ファイル構成

/
├── .htaccess
├── index.cgi
└── templates/
    └── index.html

1. .htaccess の設定

まず、すべてのリクエストを index.cgi に転送するための .htaccess ファイルを作成します。これにより、「/add」のようなURLを index.cgi で処理できるようになります。

RewriteEngine On  # mod_rewrite を有効化
RewriteCond %{REQUEST_FILENAME} !-f  # 実際のファイルが存在しない場合のみ適用
RewriteCond %{REQUEST_FILENAME} !-d  # 実際のディレクトリが存在しない場合のみ適用
RewriteRule ^(.*)$ /index.cgi/$1 [QSA,L]  # すべてのリクエストを /index.cgi/ に転送(クエリ文字列維持)

2. index.cgi の実装

次に、Flaskアプリケーションの本体となる index.cgi を作成します。シバン行(最初の行)は、作成した仮想環境内のPythonインタプリタを指定します。

#!/path/to/your/myenv/bin/python3
# -*- coding: utf-8 -*-

import sys
import os
import mysql.connector
from flask import Flask, render_template, request, redirect
from werkzeug.middleware.dispatcher import DispatcherMiddleware

app = Flask(__name__)

# MySQL接続設定
db_config = {
    'host': 'mysql.example.com',
    'user': 'db_user',
    'password': 'db_password',
    'database': 'db_name',
}

def get_db_connection():
    """データベース接続を取得"""
    return mysql.connector.connect(**db_config)

@app.route('/')
def home():
    """データベースからデータを取得して表示"""
    with get_db_connection() as conn:
        with conn.cursor(dictionary=True) as cursor:
            cursor.execute("SELECT * FROM example_table")
            rows = cursor.fetchall()
    return render_template('index.html', rows=rows)

@app.route('/add', methods=['POST'])
def add():
    """新しいレコードを追加"""
    name = request.form.get('name')
    age = request.form.get('age')

    if name and age:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute("INSERT INTO example_table (name, age) VALUES (%s, %s)", (name, age))
                conn.commit()

    return redirect('/')

@app.route('/delete', methods=['POST'])
def delete():
    """指定IDのレコードを削除"""
    id = request.form.get('id')
    if id:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute("DELETE FROM example_table WHERE id = %s", (id,))
                conn.commit()

    return redirect('/')

if __name__ == '__main__':
    # CGI環境用の設定(通常のFlaskとは異なる)
    from wsgiref.handlers import CGIHandler
    application = DispatcherMiddleware(app, {})  # WSGIミドルウェアでアプリをラップ
    CGIHandler().run(application)  # CGI環境でアプリを実行

3. テンプレートの作成

最後に、フロントエンドとなるHTMLテンプレートを作成します。(templates/index.html)

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ユーザー管理</title>
</head>
<body>
    <h1>ユーザー管理システム</h1>
    
    <!-- データ追加フォーム -->
    <h2>新しいユーザーを追加</h2>
    <form action="/add" method="post">
        <div>
            <label for="name">名前:</label>
            <input type="text" id="name" name="name" required>
        </div>
        <div>
            <label for="age">年齢:</label>
            <input type="number" id="age" name="age" required>
        </div>
        <div>
            <button type="submit">追加</button>
        </div>
    </form>

    <h2>ユーザー一覧</h2>
    <table border="1">
        <thead>
            <tr>
                <th>ID</th>
                <th>名前</th>
                <th>年齢</th>
                <th>削除</th>
            </tr>
        </thead>
        <tbody>
            {% for row in rows %}
            <tr>
                <td>{{ row.id }}</td>
                <td>{{ row.name }}</td>
                <td>{{ row.age }}</td>
                <td>
                    <form action="/delete" method="post">
                        <input type="hidden" name="id" value="{{ row.id }}">
                        <button type="submit">削除</button>
                    </form>
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>

CGI環境特有のポイント解説

1. シバン行

#!/path/to/your/myenv/bin/python3

この行はCGIスクリプトの先頭に必須です。自分の仮想環境内のPythonインタプリタのパスを指定します。正確なパスはサーバーのディレクトリ構造によって異なりますので、実際の環境に合わせて変更してください。通常のFlaskアプリではこの記述は不要です。

シバン行のパスを確認するには、SSHで接続して以下のコマンドを実行します:

which python3  # システムのPythonのパスを確認
# または
echo $PWD/myenv/bin/python3  # 仮想環境のPythonのパスを確認

2. CGIHandler

if __name__ == '__main__':
    from wsgiref.handlers import CGIHandler
    application = DispatcherMiddleware(app, {})
    CGIHandler().run(application)

通常のFlaskアプリでは、app.run() で直接起動しますが、CGI環境では wsgiref.handlers.CGIHandler を使ってWSGIアプリケーションを実行します。また、DispatcherMiddleware でアプリをラップすることで、URLのパス処理を適切に行います。

3. .htaccessによるURLルーティング

RewriteEngine On  # mod_rewrite を有効化
RewriteCond %{REQUEST_FILENAME} !-f  # 実際のファイルが存在しない場合のみ適用
RewriteCond %{REQUEST_FILENAME} !-d  # 実際のディレクトリが存在しない場合のみ適用
RewriteRule ^(.*)$ /index.cgi/$1 [QSA,L]  # すべてのリクエストを /index.cgi/ に転送(クエリ文字列維持)

CGI環境では、リダイレクト先にCGIスクリプト名を含める必要があります
上記のような.htaccessの設定が適切に行われていれば、通常のFlaskアプリケーションと同じく単純なredirect('/')でも正常に動作します。これは、.htaccessのリライトルールが/へのリクエストを/index.cgi/に内部的に変換してくれるためです。

4. 実行権限

CGI環境では、スクリプトに実行権限を付与する必要があります。FTPクライアントなどでアップロードした後に、ファイルのパーミッションを 700 に設定しましょう。
* ロリポップは 700 が推奨となっています。

chmod 700 index.cgi

通常のFlaskアプリケーションとの比較コード

参考までに、同じ機能を通常のFlask環境で実装すると以下のようになります。

from flask import Flask, render_template, request, redirect
import mysql.connector

app = Flask(__name__)

# MySQL接続設定
db_config = {
    'host': 'mysql.example.com',
    'user': 'db_user',
    'password': 'db_password',
    'database': 'db_name',
}

def get_db_connection():
    return mysql.connector.connect(**db_config)

@app.route('/')
def home():
    with get_db_connection() as conn:
        with conn.cursor(dictionary=True) as cursor:
            cursor.execute("SELECT * FROM example_table")
            rows = cursor.fetchall()
    return render_template('index.html', rows=rows)

@app.route('/add', methods=['POST'])
def add():
    name = request.form.get('name')
    age = request.form.get('age')

    if name and age:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute("INSERT INTO example_table (name, age) VALUES (%s, %s)", (name, age))
                conn.commit()

    return redirect('/')

@app.route('/delete', methods=['POST'])
def delete():
    id = request.form.get('id')
    if id:
        with get_db_connection() as conn:
            with conn.cursor() as cursor:
                cursor.execute("DELETE FROM example_table WHERE id = %s", (id,))
                conn.commit()

    return redirect('/')

if __name__ == '__main__':
    app.run(debug=True)  # 通常環境では app.run() で起動

ご覧の通り、CGI環境と通常環境の違いは主に以下の点です:

  1. シバン行の有無
  2. CGIHandlerの使用
  3. .htaccessによるURLルーティング

そして、適切な.htaccess設定があれば、リダイレクトの記述も同じにできます。

パフォーマンスの違い

CGI環境では、リクエストごとにPythonプロセスの起動・DB接続の確立・終了が発生するため、通常のFlask環境に比べてパフォーマンスが劣ります。特に以下の点に注意が必要です:

  1. リクエスト処理が遅くなる傾向がある
  2. 同時アクセスが多いと負荷が高くなる
  3. セッション管理にファイルやデータベースの利用が必要

まとめ

CGI環境でもFlaskを使ってWebアプリケーションを作ることは可能ですが、通常の環境とは異なる設定や考慮点があります。本記事で紹介したポイントを押さえれば、レンタルサーバーでもPythonの恩恵を受けたアプリケーション開発が可能です。

ただし、本格的なアプリケーションを開発する場合は、PythonScriptなどのFastCGI対応の環境やVPS、PaaSなどの常時起動可能な環境を検討することをおすすめします。

参考:テーブル構造

今回使用したMySQLのテーブル構造は以下の通りです:

CREATE TABLE example_table (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  age INT NOT NULL
);

この記事が、レンタルサーバーでFlaskアプリケーションを開発する際の参考になれば幸いです。

コメント

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