「動くだけ」のアプリから、「使ってもらえる」サービスへ。
こんにちは!「LINUX工房」管理人の「リナックス先生」です。
全8回の「初心者向けPython講座」、本当にお疲れ様でした!
これまでの講座で、Pythonの基礎から自動化までを学びました。
この「特別付録」では、その集大成として「Webブラウザで動くフォトアルバムアプリ」を作成します。
ただし、今回はただ作るだけではありません。
開発用の簡易サーバーではなく、世界中のWebサイトで使われている本物のWebサーバー「Apache(アパッチ)」を使って、インターネット上に公開する「本番環境構築」に挑戦します。
先生! Apacheって名前は聞いたことあります。
でも、PythonでWebアプリを作るのに、なんで別のソフト(Apache)が必要なんですか?
前回みたいに python app.py じゃダメなんですか?
良い質問ね、コウ君。python app.py はあくまで開発者がテストするための機能なの。
本番環境では、セキュリティや大量のアクセスに耐えるために、Apacheのような「プロの受付係」を前に立たせるのが常識よ。
今回はエンジニアとして恥ずかしくない、本格的な構成でアプリを公開しましょう!
本記事では、VPS(AlmaLinux 9)上に、以下の構成でWebアプリケーション環境を構築します。
📚 全8回・Python講座カリキュラム(復習用)
ここまでの知識に不安がある方は、各回を振り返ってみてください。
目次
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」
Pythonスキルを活かす
「ITエンジニア転職」


コメント