はじめに
個人開発で複数の Web サービスを並行して作っていると、
インフラ構築の作業が毎回ほぼ同じで、正直かなり面倒だと感じてきた。
- ディレクトリ作成Python venv
- Flask / Gunicorn インストール
- systemd サービス作成
- Nginx のリバースプロキシ設定
- Let’s Encrypt で SSL
- 権限調整
- 動作確認
新しいサービスを作るたびにこれを繰り返すのは非効率すぎる。
そこで今回、「サービス名」と「ポート番号」を指定するだけで、
Flask サービスの本番環境が 1 コマンドで自動構築される仕組みを作った。
実際には次のように実行するだけで完了する。
sudo create_flask_site.sh newservice 9999
この記事では、この仕組みの構成と実際に使ってみた結果をまとめる。
なぜ自動化したのか
個人開発とはいえ、作っているサービスが増えてくると、とにかく「新しいサービスをすぐ試したい」欲求が強い。
毎回手動でインフラを組むと、作業時間とミスのリスクが積み重なっていく。
「アイデアが湧いた瞬間に、すぐ本番環境で動かしたい」
この思想に沿うには、プロビジョニング自体を自分用に最適化したツール化が必要だった。
構成の全体像
採用した構成はシンプルで、個人開発向けに最適化している。
- OS:Ubuntu(ConoHa VPS)
- Webアプリ:Flask
- WSGIサーバ:Gunicorn
- プロセス管理:systemd
- リバースプロキシ:Nginx
- SSL:Let’s Encrypt(certbot)
- ディレクトリ構成:
/srv/app/<service_name> - 実行ユーザー:
www-data
各サービスは次のように独立した構造になっている。
/srv/app/
├── Service1/
├── Service2/
├── Service3/
│ ├── venv/
│ ├── app.py
│ └── ...
1 VPS で複数サービスを同時に動かす前提で設計している。
create_flask_site.sh が自動でやっていること
1コマンドで、以下のすべてを自動実行する。
① サービス用ディレクトリの作成
/srv/app/<service_name>
※権限はすべて www-data に変更しています
② Python 仮想環境の作成 + Flask / Gunicorn インストール
python3 -m venv venv
source venv/bin/activate
pip install flask gunicorn
※pythonは事前にインストールをおねがいします
③ 最小構成の app.py を自動生成
動作確認用として、下記のような簡易アプリを生成する。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello from <service_name>"
④ systemd ユニットファイルの生成 + 有効化
例:/etc/systemd/system/service1.service
[Unit]
Description=service1 Gunicorn Service
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/srv/app/service1
Environment="PATH=/srv/app/service1/venv/bin"
ExecStart=/srv/app/service1/venv/bin/gunicorn -b 127.0.0.1:8003 --timeout 300 --workers 3 app:app
[Install]
WantedBy=multi-user.target
サービス起動:
systemctl enable service1
systemctl start service1
⑤ Nginx のリバースプロキシ設定生成
/etc/nginx/sites-available/service1.domain.com
server {
server_name service1.domain.com;
location / {
proxy_pass http://127.0.0.1:9999;
proxy_set_header Host $host;
}
}
有効化して reload:
nginx -t
systemctl reload nginx
⑥ DNS が生きていれば SSL を自動発行
スクリプトがユーザーに聞く:
今すぐ SSL(Let's Encrypt)を設定しますか? (yes/no)
yes を選べば certbot が実行される:
sudo certbot --nginx -d service1.domain.com
DNS が未反映なら失敗するが、その場合は
「後で手動で実行すべきコマンド」を表示してくれる。
メリット
- 新規サービスの立ち上げ時間が 30分 → 5秒 に短縮
- 手作業ミスがゼロになる
- VPS の複数サービス管理が大幅に楽
- 気軽に新サービスを試せる
- ローカルで作ったものをすぐ本番に出せる
- “個人開発の回転速度” が上がる
これは間違いなく開発効率をあげる仕組みになった。
Shellのコード全文です。よかったら使ってください
#!/usr/bin/env bash
set -e
#======================================================================
# 使い方:
# sudo create_flask_site.sh <service_name> <port>
# 例:
# sudo create_flask_site.sh flashlog 8002
#
# 要件:
# - 作成パス: /srv/app/<service_name>
# - 実行ユーザ: www-data
# - systemd + gunicorn + nginx 自動生成
#======================================================================
if [ "$EUID" -ne 0 ]; then
echo "root権限で実行してください(sudo create_flask_site.sh ...)"
exit 1
fi
SERVICE="$1"
PORT="$2"
if [ -z "$SERVICE" ] || [ -z "$PORT" ]; then
echo "使い方: $0 <service_name> <port>"
exit 1
fi
APP_DIR="/srv/app/${SERVICE}"
DOMAIN="${SERVICE}.ideaworks.tech"
echo "=== サービス作成: $SERVICE ==="
echo "APP_DIR: $APP_DIR"
echo "PORT : $PORT"
echo "DOMAIN : $DOMAIN"
echo
#---------------------------------------------------------
# 1) ディレクトリ作成
#---------------------------------------------------------
if [ -d "$APP_DIR" ]; then
echo "ディレクトリ ${APP_DIR} は既に存在します。中断。"
exit 1
fi
mkdir -p "$APP_DIR"
chown -R www-data:www-data "$APP_DIR"
cd "$APP_DIR"
#---------------------------------------------------------
# 2) 仮想環境作成 & Flask + Gunicorn インストール
#---------------------------------------------------------
sudo -u www-data python3 -m venv venv
sudo -u www-data bash -c "
source venv/bin/activate &&
pip install --upgrade pip &&
pip install flask gunicorn
"
#---------------------------------------------------------
# 3) app.py 雛形作成
#---------------------------------------------------------
cat > app.py <<EOF
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello from ${SERVICE}!"
if __name__ == "__main__":
app.run(host="127.0.0.1", port=${PORT}, debug=True)
EOF
chown www-data:www-data app.py
#---------------------------------------------------------
# 4) systemd ユニットファイル作成
#---------------------------------------------------------
SERVICE_FILE="/etc/systemd/system/${SERVICE}.service"
cat > "${SERVICE_FILE}" <<EOF
[Unit]
Description=${SERVICE} Gunicorn Service
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=${APP_DIR}
Environment="PATH=${APP_DIR}/venv/bin"
ExecStart=${APP_DIR}/venv/bin/gunicorn -b 127.0.0.1:${PORT} --timeout 300 --workers 3 app:app
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "${SERVICE}"
systemctl restart "${SERVICE}"
echo "=== systemd 起動済み ==="
systemctl status "${SERVICE}" --no-pager
#---------------------------------------------------------
# 5) Nginx config 作成
#---------------------------------------------------------
NGINX_CONF="/etc/nginx/sites-available/${DOMAIN}"
cat > "${NGINX_CONF}" <<EOF
server {
listen 80;
server_name ${DOMAIN};
location / {
proxy_pass http://127.0.0.1:${PORT};
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
EOF
ln -s "${NGINX_CONF}" /etc/nginx/sites-enabled/ || true
nginx -t
systemctl reload nginx
echo
echo "=========================================================="
echo " 構築完了! 次の作業:"
echo
echo "【必須】DNS に以下を登録してください:"
echo " Aレコード: ${DOMAIN} → <VPSのIP>"
echo
echo "DNS反映後に SSL 有効化:"
echo " sudo certbot --nginx -d ${DOMAIN}"
echo
echo "=========================================================="
#==========================================================
# 6) SSL 設定(ユーザー確認つき)
#==========================================================
echo
echo "=========================================================="
echo "DNS が反映されている場合、SSL を有効化できます。"
echo "今すぐ SSL(Let's Encrypt)を設定しますか? (yes/no)"
echo "=========================================================="
read SSLANSWER
if [ "$SSLANSWER" = "yes" ]; then
echo "=== SSL設定を開始します ==="
if certbot --nginx -d ${DOMAIN}; then
echo "=== SSL設定が完了しました ==="
else
echo "=== SSL設定に失敗しました ==="
echo "DNS がまだ反映されていない可能性があります。"
echo
echo "後で手動で実行してください:"
echo " sudo certbot --nginx -d ${DOMAIN}"
fi
else
echo "SSL設定はスキップされました。後で以下を実行してください:"
echo " sudo certbot --nginx -d ${DOMAIN}"
fi
まとめ
今回作った create_flask_site.sh によって、
Flask の本番構築が 1 コマンドで完了する環境が整った。
個人開発で複数サービスを並行して作る場合、
これは生産性を上げる武器になる。
今後も開発・運用で得た知見をこのブログにまとめていく予定です。