はじめに
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)、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 プロジェクト用ディレクトリ作成
# サーバーにmyappというディレクトリ(フォルダみないな物)を作る
mkdir -p /home/youruser/myapp
# myappの所有者(youruser):グループ(youruser)を設定
chown youruser:youruser /home/youruser/myapp
/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
[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
を実行した際に起動対象に登録される。
ファイルを保存したら、systemd を再読み込み・有効化・起動。
# ユニットファイルを置いたら…
sudo systemctl daemon-reload # systemd 設定の再読み込み
sudo systemctl enable myapp # ブート時自動起動を有効化
sudo systemctl start myapp # 起動
sudo systemctl status 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 のメッセージが見えれば成功。
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
ln -sは「シンボリックリンク(別名:ショートカット)を作成する」コマンド。
/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
/
にアクセスし、URLの先頭に鍵マークが表示されれば SSL 化成功。
現在の証明書名を確認する方法
sudo certbot certificates
ここに「Certificate Name」として表示される名前(多くは取得時のドメイン名)が、既存の証明書。
サブドメインがついた物を追加したい場合
--expand
オプションで再発行
sudo certbot --nginx --expand -d yourdomain.com -d www.yourdomain.com
--expand
をつけると、既存の証明書に新しいドメインを追加したうえで再発行される。これまでの設定(SSL→Nginx 自動書き換えなど)もそのまま引き継ぐ。
ここで開発用の5000番ポートを閉じておく
sudo ufw delete allow 5000/tcp
開発の流れ
Githubで管理する。
- developブランチを作り、ローカル(PC)で開発する。
- ローカルでdevelopブランチを変更したらリモート(Github)にpushする。
- Github上でプルリクエスト(mainブランチにdevelopブランチをマージする)を作成し、
developブランチの変更をmainブランチに反映させる。 - Github Actionsにより、自動でテストとVPSへの反映を行う。