はじめに
AIに聞きながら、VPS(仮想サーバー)を使ってPython の Web フレームワーク Flask アプリを公開するまでの経過と備忘録。
xserverVPSでやってます。
独学プログラマーなので解釈が間違っているところがあるかも。
- SSH 鍵を使った VPS へのログイン
- セキュリティ強化(ユーザー追加・SSH 設定・ファイアウォール)
- 必要なソフトウェアのインストール
- Flask プロジェクトの作成・動作確認
- Gunicorn+systemd で常時起動設定
- Nginx でリバースプロキシ設定
- 開発の流れ
1. VPSへのSSH接続
1.1 鍵ペアの作成
まずはローカル端末(自分のパソコン)で「公開鍵」と「秘密鍵」のペアを作る。
ターミナルで以下のコマンドを入力。
# 鍵を作るコマンド。
ssh-keygen -t ed25519 -C "your_email@example.com"
ssh-keygen
:SSH 用の鍵を作るコマンド-t ed25519
:鍵のアルゴリズム(安全・高速)-C "…"
:鍵につけるラベル(見分けるためのコメント。ここでは自分のアドレス)
実行すると以下2つのファイルができる。
~/.ssh/id_ed25519
(秘密鍵:絶対他人に渡さない)~/.ssh/id_ed25519.pub
(公開鍵:サーバーに登録する)
パスフレーズ(鍵を使うときの追加パスワード)は安全のため設定。
1.2 公開鍵をVPSに登録
生成した公開鍵を VPS(サーバー)に登録。
以下のコマンドを実行すると、自動でサーバー側の ~/.ssh/authorized_keys
に追記される。
# @YOUR_VPS_IP は自分のサーバーIP
ssh-copy-id -i ~/.ssh/id_ed25519.pub root@YOUR_VPS_IP
-i
:登録する公開鍵ファイルを指定root@YOUR_VPS_IP
:サーバーのログインユーザーとIP- rootは全権限を持っているすごいやつ。人に乗っ取られるとヤバイ。
1.3 SSH接続
ルートでサーバーへ入ってみる。
ssh root@YOUR_VPS_IP
サーバーのプロンプト(例:root@hostname:~#
)が表示されれば OK 。
2. 初期セキュリティ強化
2.1 非rootユーザーの作成・sudo付与
- 新しいユーザーを作成(好きな名前をつける)
adduser youruser
- ターミナルででパスワードやユーザー情報を聞かれるので入力する。
- sudo(管理者権限)グループに追加
usermod -aG sudo youruser
- これで
youruser
も必要に応じてsudo
コマンド(rootに近い権限)が使える
- これで
2.2 SSH設定の見直し
2.2.1 sshd_config
の編集
nano /etc/ssh/sshd_config
nano
ターミナル上のテキストエディタを開く。
矢印キーで移動
Ctrl+X
で行を削除
Ctrl+O
(ゼロではなくオー)で保存
Ctrl+X
で終了
上で作った非ルートユーザでログインしている場合はsudoをつける
nano /etc/ssh/sshd_config
次の行を探し、以下のように変更(なければ追加)。
# root の直接ログインを禁止
PermitRootLogin no
# パスワード認証を禁止(鍵ログインのみ有効に)
PasswordAuthentication no
# (任意)SSH ポートを22以外に変更(デフォルトである22番ポートを狙う攻撃を避けるため)
Port 2222
2.2.2 SSH の再起動で設定を反映
systemctl reload sshd
注意
カスタムポート(例:2222)に変更した場合は、次回からssh -p 2222 youruser@YOUR_VPS_IP
で接続
2.3 ファイアウォール(UFW)の設定
Ubuntu 標準の UFW で最低限のポートだけ開ける。
apt update
apt install ufw -y # UFW をインストール
# SSH(例:2222,5000)、HTTP(80)、HTTPS(443) を許可
ufw allow 2222/tcp # SSH接続用
ufw allow 5000/tcp # flaskアプリ開発用。本番では閉じる。
ufw allow http # 本番用
ufw allow https # 本番用
ufw enable # ファイアウォールを有効化(初回は確認(y/n)あり)
ufw status # 設定を確認
3. 必要パッケージのインストール
Web アプリを動かすためのツール類を入れる。
apt update && apt upgrade -y
# Python3, 仮想環境, pip, Git, Nginx, Let's Encrypt
apt install python3 python3-venv python3-pip git nginx certbot python3-certbot-nginx -y
python3-venv
:軽量仮想環境作成ツールnginx
:高速な Web サーバーcertbot
:無料 SSL 証明書を自動取得/更新
4. Flaskプロジェクトの準備
4.1 プロジェクト用ディレクトリ作成
mkdir -p /home/youruser/myapp # サーバーにmyappというディレクトリ(フォルダみないな物)を作る
chown youruser:youruser /home/youruser/myapp # myappの所有者(youruser):グループ(youruser)を設定
/home/youruser/myapp
にアプリの諸々を作っていく。
4.2 非rootユーザーに切り替え
youruserは2.1で決めたユーザー名
su - youruser
cd ~/myapp
4.3 仮想環境の作成・有効化
python3 -m venv venv # venv という名前の仮想環境を作成
source venv/bin/activate # 仮想環境を有効化
- プロンプトの先頭に
(venv)
がつく。
4.4 Flask+Gunicornのインストール
pip install --upgrade pip
pip install flask gunicorn
flask
:シンプルな Python Web フレームワークgunicorn
:本番環境向けの WSGI サーバー
4.5 サンプルアプリ作成
app.py
を作成し、以下のコードを貼り付ける。
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello, myapp"
if __name__ == "__main__":
app.run(host="0.0.0.0")
@app.route("/")
:URL の「/」にアクセスしたときに下の関数を実行。デコレータ。app.run(host="0.0.0.0")
:開発用サーバーを全IP待ち受けで起動
4.6 動作確認(開発用)
flask run --host=0.0.0.0 --port=5000
ブラウザで http://YOUR_VPS_IP:5000
にアクセスし、「Hello,myapp」と表示されれば OK 。
5. Gunicornで本番運用テスト
開発用サーバーではなく、Gunicorn でも動作テストしてみる。
myappディレクトリに移動して、仮想環境を起動(すでにやっていれば飛ばす)
cd ~/myapp
source venv/bin/activate
一時的に8000番ポートを開放してテスト
# ポート8000を開放
sudo ufw allow 8000/tcp
# ポート8000でテスト起動
gunicorn --bind 0.0.0.0:8000 app:app
ブラウザで http://YOUR_VPS_IP:8000
を確認し、同じメッセージ「Hello,myapp」が表示されれば成功。
Ctrl+C
で停止してポートを閉じておく
# ポートを閉じる
sudo ufw delete allow 8000/tcp
6. systemd ユニットファイル作成
サーバー再起動後も自動で立ち上がる設定。
nano /etc/systemd/system/myapp.service
を作成し、以下を貼り付け。[Unit] Description=Gunicorn instance to serve my Flask app After=network.target [Service] User=youruser Group=www-data WorkingDirectory=/home/youruser/myapp Environment="PATH=/home/youruser/myapp/venv/bin" ExecStart=/home/youruser/myapp/venv/bin/gunicorn \ --workers 3 \ --bind unix:/run/myapp.sock \ -m 007 app:app [Install] WantedBy=multi-user.target
- ファイルを保存したら、systemd を再読み込み・有効化・起動。
# ユニットファイルを置いたら… sudo systemctl daemon-reload # systemd 設定の再読み込み sudo systemctl enable myapp # ブート時自動起動を有効化 sudo systemctl start myapp # 起動 sudo systemctl status myapp # 動作確認
[Unit] セクションの説明
Description=
サービスの説明文。systemctl status
時などに表示される。After=network.target
ネットワークが起動した(IP が割り当てられた)後にこのサービスを起動、という依存関係を指定。
[Service] セクションの説明
-
User=youruser
サービスを実行するユーザー。root 権限を避け、youruser
の権限で動かす。 -
Group=www-data
サービスを実行するグループ。Nginx(通常 www-data)とソケットファイルを共有しやすくするために合わせる。 -
WorkingDirectory=/home/youruser/myapp
サービス起動時のカレントディレクトリ。相対パスでファイルを指定している場合などに必要。 -
Environment="PATH=/home/youruser/myapp/venv/bin"
仮想環境のpython
/gunicorn
を優先するよう、環境変数PATH
を上書き。 -
ExecStart=…
サービス実行時に呼び出すコマンドをフルパスで指定。 --workers 3
:同時に処理するワーカープロセス数。サーバーの負荷に応じて変える--bind unix:myapp.sock
:UNIX ソケット(/home/youruser/myapp/myapp.sock
)で待ち受け-m 007
:ソケットファイルのパーミッションマスク(作成時のファイル権限設定)
[Install] セクションの説明
-
WantedBy=multi-user.target
multi‑user(通常利用されるランレベル)で自動起動させる、という指定。systemctl enable myapp
を実行した際に起動対象に登録される。
7. Nginx リバースプロキシ設定
リバースプロキシ(Reverse Proxy)とは、クライアント(ブラウザなど)からのリクエストをいったん受け取り、代わりにバックエンドのサーバー(Web アプリや API サーバー)へ転送し、そのレスポンスを再びクライアントに返す「仲介役」の仕組み。
外部からのアクセスを Nginx→Gunicorn に中継。
nano /etc/nginx/sites-available/myapp サイト設定を作成し以下を入力
server { listen 80; server_name YOUR_DOMAIN_OR_IP; location / { include proxy_params; proxy_pass http://unix:/run/myapp.sock:; } }
server { … }
Nginx における「仮想ホスト」の定義です。ここに書かれた設定が、特定のポートやドメインへのリクエストを処理します。listen 80;
Nginx が TCP 80 番ポート(HTTP の標準ポート)でクライアントからの接続を受け付けるよう指定。
IPv6 も合わせて受けたい場合は
listen [::]:80;
を追加する
HTTPS(443番)を扱う場合は別のserver
ブロックでlisten 443 ssl;
を追加server_name YOUR_DOMAIN_OR_IP;
この
server
ブロックが応答対象とするホスト名(または IP)を指定。ブラウザが 「Host: example.com」 ヘッダーを送ってきたときにマッチさせたい場合は
example.com
。IP アドレスでアクセスしても反応させたいなら123.45.67.89
IP を書く。
ワイルドカード(*.example.com
)や複数指定(domain1.com domain2.com
)も可。location / { … }
URL のパス部分が
/
(ルート)で始まるすべてのリクエストをこの中の設定で処理します。include proxy_params;
/etc/nginx/proxy_params
(あるいは/etc/nginx/proxy.conf
)という別ファイルを読み込んで、その中に書かれた共通のプロキシ設定(ヘッダー転送やタイムアウト等)を適用する。
主な中身の例:
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;
毎回同じヘッダー設定を書く手間を省き、全サイトで共通利用できるようにするための仕組み。
proxy_pass http://unix:/run/myapp.sock:;
クライアントから来たリクエストを、バックエンドのサーバー(ここでは Gunicorn が生成する UNIX ドメインソケット)へ転送する。
http://
…プロトコルは HTTP
unix:
…UNIX ソケット経由で接続
http://unix:/run/myapp.sock:;
…ソケットファイルのパスソケット経由にすることで、TCP ポートを開放せずに高速・安全に Nginx ⇔ Gunicorn 間で通信できる
- 設定の有効化・テスト:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
-
テストがOKなら再起動して反映
sudo systemctl restart nginx
ブラウザで http://YOUR_DOMAIN_OR_IP/
にアクセスし、Flask のメッセージが見えれば成功。
ln -sは「シンボリックリンク(別名:ショートカット)を作成する」コマンド。sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
/etc/nginx/sites-available/myapp
(編集した設定ファイル)を
/etc/nginx/sites-enabled/
ディレクトリ内にリンクする。
Nginx は「sites-enabled にある設定だけ」を読み込む仕組みなので、ここで “有効” になる。sudo nginx -t
Nginx が持つ「設定テスト」機能。
実際に再起動する前に、編集した設定に構文エラーやパスの誤り、ディレクティブの脱字などがないかをチェックする。
問題なければsyntax is ok
/test is successful
と表示。もしエラーがあれば修正箇所が出るので、必ずここで修正してから次へ。-
sudo systemctl restart nginx
systemctl restart
で Nginx サービスを再起動。
新しい設定(sites-enabled に追加した myapp)が読み込まれ、反映される。
再起動中にエラーが出るとサイトが落ちる可能性があるので、事前のnginx -t
が特に重要。
502 Bad Gatewayが出る場合。
- ls -l /home/youruser/myapp/myapp.sock # ソケットが存在しているかチェック
- sudo tail -n50 /var/log/nginx/error.log # nginxのエラーログをチェック
- sudo systemctl status myapp.service # gunicornが起動しているかチェック
- ls -ld /home/youruser
ls -ld /home/youruser/myapp # ディレクトリの編集権限をチェック
権限がなかったら/runの下に/myappディレクトリを作って編集権限をつける。
- sudo mkdir -p /run/myapp
sudo chown youruser:www-data /run/myapp
sudo chmod 770 /run/myapp - ユニットファイルを修正
ExecStart=/home/youruser/myapp/venv/bin/gunicorn \
–workers 3 \
–bind unix:/run/myapp/myapp.sock \
-m 007 app:app - nginxサイトファイルを修正
location / {
include proxy_params;
proxy_pass http://unix:/run/myapp/myapp.sock:;
} - systemd 設定再読み込み&サービス再起動
sudo systemctl daemon-reload
sudo systemctl restart myapp.service - ソケットの確認
ls -l /run/myapp/myapp.sock
# srwxrwx— 1 youruser www-data … /run/myapp/myapp.sock ←こうなっていたらOK - Nginx 設定もテスト&リロード
sudo nginx -t
sudo systemctl reload nginx
ドメイン名が決まっている場合は、IPアドレス直打ちアクセスはドメインに飛ばす方がいい。
# IP直打ち用のサーバーブロック
server {
listen 80 default_server;
server_name _; # どのホスト名にもマッチ
return 301 http://yourdomain.jp$request_uri;
}
# 本来のアプリ用サーバーブロック
server {
listen 80;
server_name yourdomain.jp www.yourdomain.jp;
location / {
include proxy_params;
proxy_pass http://unix:/run/myapp.sock:;
}
}
sudo nginx -tでテストした際に
[emerg] a duplicate default server for 0.0.0.0:80 in
というエラーが出たらデフォルトサーバーが重複しているので
標準で実装されてるdefaultファイルを削除しておく。
sudo rm /etc/nginx/sites-enabled/default
8. SSL(Let’s Encrypt)設定
最後に HTTPS 化。
sudo certbot --nginx -d yourdomain.com
- メールアドレス登録、規約同意、HTTP→HTTPS リダイレクト設定を選択。
完了後、https://yourdomain.com
/
にアクセスし、鍵マークが表示されれば SSL 化成功。
現在の証明書名を確認する方法
ここに「Certificate Name」として表示される名前(多くは取得時のドメイン名)が、既存の証明書。
サブドメインがついた物を追加したい場合
--expand
オプションで再発行
--expand
をつけると、既存の証明書に新しいドメインを追加したうえで再発行される。これまでの設定(SSL→Nginx 自動書き換えなど)もそのまま引き継ぐ。
ここで開発用の5000番ポートを閉じておく
sudo ufw delete allow 5000/tcp
開発の流れ
SSH ローカルポートフォワードによる開発手順
このコマンドでローカル環境の localhost:5000
と VPS 上の localhost:5000
がトンネル接続される。
VPS に入ったまま(SSHセッション内で)
flask run --host=127.0.0.1 --port=5000
# または
python app.py # app.py 内で app.run(host="127.0.0.1", port=5000) としておく
ローカルのブラウザで http://localhost:5000
を開くと、VPS 上の開発用サーバーに接続できる。
VPS の 5000番ポートは 外部には開かれず、SSHトンネル経由のローカルアクセスだけが通る。
🎉 まとめ
アプリを公開する土台ができたので、次回はアプリ制作の流れと環境を作る。