【Python講座 付録】初心者卒業制作。ApacheとFlaskで作る「画像アップロード&スライドショーアプリ」完全構築ガイド

「動くだけ」のアプリから、「使ってもらえる」サービスへ。

こんにちは!「LINUX工房」管理人の「リナックス先生」です。
全8回の「初心者向けPython講座」、本当にお疲れ様でした!

これまでの講座で、Pythonの基礎から自動化までを学びました。
この「特別付録」では、その集大成として「Webブラウザで動くフォトアルバムアプリ」を作成します。

ただし、今回はただ作るだけではありません。
開発用の簡易サーバーではなく、世界中のWebサイトで使われている本物のWebサーバー「Apache(アパッチ)」を使って、インターネット上に公開する「本番環境構築」に挑戦します。

コウ君

先生! Apacheって名前は聞いたことあります。
でも、PythonでWebアプリを作るのに、なんで別のソフト(Apache)が必要なんですか?
前回みたいに python app.py じゃダメなんですか?

リナックス先生

良い質問ね、コウ君。
python app.py はあくまで開発者がテストするための機能なの。
本番環境では、セキュリティや大量のアクセスに耐えるために、Apacheのような「プロの受付係」を前に立たせるのが常識よ。
今回はエンジニアとして恥ずかしくない、本格的な構成でアプリを公開しましょう!

本記事では、VPS(AlmaLinux 9)上に、以下の構成でWebアプリケーション環境を構築します。


1. アプリケーションの概要と構成

今回作成するのは、スマホやPCから画像をアップロードし、スライドショー形式で閲覧できる「フォトアルバム」です。

システム構成図

役割 採用技術 説明
Webサーバー Apache (httpd) インターネットからのアクセスを受け付け、静的ファイル(画像など)を高速に配信し、動的な処理を後ろ(Gunicorn)に流します。
Appサーバー Gunicorn Pythonで作られたWebアプリを動かすための専用サーバーです。FlaskとApacheの仲介役をします。
Webアプリ Flask Pythonの軽量Webフレームワーク。アップロード処理や削除機能を担当します。

2. Flaskアプリの実装(バックエンド&フロントエンド)

まずは、アプリケーション本体を作成します。
VPSに接続し、作業用ユーザー(python_user)で実行してください。

2-1. プロジェクト作成とライブラリインストール

# ディレクトリ作成
mkdir ~/flask_album
cd ~/flask_album

# 仮想環境の作成と有効化
python3 -m venv .venv
source .venv/bin/activate

# FlaskとGunicornのインストール
pip install Flask gunicorn

2-2. ディレクトリ構成の準備

Flaskの標準構成に合わせてフォルダを作ります。

mkdir templates
mkdir static
mkdir static/uploads  # 画像保存場所

2-3. アプリ本体 (app.py) の作成

vi app.py で以下のコードを作成します。
ファイルアップロードの脆弱性を防ぐため、secure_filename でファイル名を無害化する処理を入れています。

import os
from flask import Flask, render_template, request, redirect, url_for, flash
from werkzeug.utils import secure_filename

app = Flask(__name__)
app.secret_key = "super_secret_key"  # 本番ではランダムな文字列に変更推奨

# 設定
UPLOAD_FOLDER = 'static/uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/')
def index():
    images = os.listdir(app.config['UPLOAD_FOLDER'])
    # 隠しファイルを除外
    image_list = [img for img in images if allowed_file(img)]
    return render_template('index.html', images=image_list)

@app.route('/upload', methods=['POST'])
def upload_file():
    if 'file' not in request.files:
        flash('ファイルがありません')
        return redirect(request.url)
    
    file = request.files['file']
    
    if file.filename == '':
        flash('ファイル名が空です')
        return redirect(request.url)
    
    if file and allowed_file(file.filename):
        # セキュリティ対策:ファイル名の無害化
        filename = secure_filename(file.filename)
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
        flash('アップロード成功!')
        return redirect(url_for('index'))
    else:
        flash('許可されていないファイル形式です')
        return redirect(request.url)

@app.route('/delete/', methods=['POST'])
def delete_file(filename):
    filename = secure_filename(filename)
    path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    if os.path.exists(path):
        os.remove(path)
        flash('削除しました')
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)

2-4. 画面デザイン (templates/index.html) の作成

vi templates/index.html で作成します。
Bootstrap 5を使って、スマホでも綺麗に見えるデザインにします。

<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Python Album</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .carousel-item img { height: 500px; object-fit: contain; background: #333; }
    </style>
</head>
<body class="bg-light">
<div class="container py-5">
    <h1 class="text-center mb-4">📸 My Photo Album</h1>

    <!-- メッセージ表示 -->
    {% with messages = get_flashed_messages() %}
        {% if messages %}
            <div class="alert alert-info">{{ messages[0] }}</div>
        {% endif %}
    {% endwith %}

    <!-- アップロードフォーム -->
    <div class="card mb-4">
        <div class="card-body">
            <form action="/upload" method="post" enctype="multipart/form-data" class="d-flex gap-2">
                <input type="file" name="file" class="form-control" required>
                <button type="submit" class="btn btn-primary">Upload</button>
            </form>
        </div>
    </div>

    <!-- スライドショー -->
    {% if images %}
    <div id="albumCarousel" class="carousel slide" data-bs-ride="carousel">
        <div class="carousel-inner">
            {% for img in images %}
            <div class="carousel-item {% if loop.first %}active{% endif %}">
                <img src="{{ url_for('static', filename='uploads/' + img) }}" class="d-block w-100">
                <div class="carousel-caption d-none d-md-block">
                    <h5>{{ img }}</h5>
                    <form action="{{ url_for('delete_file', filename=img) }}" method="post">
                        <button class="btn btn-danger btn-sm" onclick="return confirm('削除しますか?')">削除</button>
                    </form>
                </div>
            </div>
            {% endfor %}
        </div>
        <button class="carousel-control-prev" type="button" data-bs-target="#albumCarousel" data-bs-slide="prev">
            <span class="carousel-control-prev-icon"></span>
        </button>
        <button class="carousel-control-next" type="button" data-bs-target="#albumCarousel" data-bs-slide="next">
            <span class="carousel-control-next-icon"></span>
        </button>
    </div>
    {% else %}
        <p class="text-center text-muted">写真がありません。アップロードしてください。</p>
    {% endif %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

3. アプリケーションサーバー(Gunicorn)の導入

アプリをバックグラウンドで常時起動させるために、GunicornをSystemd(Linuxの管理機能)に登録します。
ここからは、root権限(sudo)での操作も含まれます。

3-1. Systemdサービスファイルの作成

sudo vi /etc/systemd/system/flask_album.service を作成し、以下を記述します。
※ユーザー名やパスはご自身の環境(python_userなど)に合わせてください。

[Unit]
Description=Gunicorn instance to serve Flask Album
After=network.target

[Service]
User=python_user
Group=python_user
WorkingDirectory=/home/python_user/flask_album
Environment="PATH=/home/python_user/flask_album/.venv/bin"
# ポート8000で起動
ExecStart=/home/python_user/flask_album/.venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app

[Install]
WantedBy=multi-user.target

3-2. サービスの起動と自動起動

sudo systemctl daemon-reload
sudo systemctl start flask_album
sudo systemctl enable flask_album
# 状態確認(Active: active (running) ならOK)
sudo systemctl status flask_album

4. Webサーバー(Apache)の構築と連携設定

最後に、Apacheをインストールし、外からのアクセス(ポート80)を内部のGunicorn(ポート8000)に転送(リバースプロキシ)する設定を行います。

4-1. Apacheのインストール

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

4-2. リバースプロキシ設定

sudo vi /etc/httpd/conf.d/flask_album.conf を作成し、以下を記述します。

<VirtualHost *:80>
    ServerName your-vps-ip-address
    # IPアドレスまたはドメイン名を指定

    # 画像などの静的ファイルはApacheが直接配信(高速化)
    Alias /static /home/python_user/flask_album/static
    <Directory /home/python_user/flask_album/static>
        Require all granted
    </Directory>

    # それ以外のアクセスはGunicornへ転送
    ProxyPass / http://127.0.0.1:8000/
    ProxyPassReverse / http://127.0.0.1:8000/
</VirtualHost>

4-3. 権限とSELinuxの設定(超重要)

ここが最大の難関です。Apacheがユーザーのディレクトリにアクセスできるようにします。

# 1. Apacheがホームディレクトリに入れるようにする
chmod 755 /home/python_user

# 2. SELinuxでApacheのネットワーク接続を許可(Gunicornへの接続用)
sudo setsebool -P httpd_can_network_connect 1

# 3. Apache設定の反映
sudo systemctl restart httpd

4-4. ファイアウォールの設定

Webサーバーのポート(80番)を開放します。

sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --reload

5. アプリケーションの操作マニュアル

構築おめでとうございます!
ブラウザで http://[あなたのVPSのIPアドレス] にアクセスしてみましょう。
以下のような操作が可能です。

📸 アプリケーションの使い方

1. 画像をアップロードする

画面中央のフォームにある「ファイルを選択」ボタンを押し、スマホやPC内の写真を選びます(jpg, png, gif対応)。
「Upload」ボタンを押すと、VPSの static/uploads フォルダに保存され、スライドショーに追加されます。

2. 画像を閲覧する

アップロードされた画像は、画面下部の大きなエリアに表示されます。
左右の矢印ボタン(またはスワイプ)で、写真を切り替えて閲覧できます。

3. 画像を削除する

写真の下に表示されている「削除」ボタンを押します。
「削除しますか?」という確認ダイアログでOKを押すと、サーバー上からファイルが完全に削除されます。


まとめ:エンジニアとしての新たな一歩

これで、全8回の講座および付録のアプリ開発はすべて終了です。

あなたは今、以下の技術を統合して一つのシステムを作り上げました。

  • Python (Flask): アプリケーションの頭脳
  • Gunicorn: アプリを動かすエンジン
  • Apache: 世界とつながる窓口
  • VPS (Linux): すべてを支える土台

この経験は、プロのエンジニア実務とほぼ変わらない貴重なものです。
エラーが出たり、設定がうまくいかなかったりした経験こそが、あなたの技術力になっています。

これからも、この「LINUX工房」で得た知識を武器に、どんどん新しいモノづくりに挑戦してください。
あなたのエンジニアライフが素晴らしいものになることを応援しています!

▼ Pythonを学ぶならVPSで ▼

AlmaLinuxが使える
「おすすめVPS」

VPSランキングを見る

Pythonスキルを活かす
「ITエンジニア転職」

転職エージェントを見る

コメント