Python

VPSを使ってウェブアプリを作りたい1

はじめに

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付与

  1. 新しいユーザーを作成(好きな名前をつける)
    adduser youruser
    
    • ターミナルででパスワードやユーザー情報を聞かれるので入力する。
  2. 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 ユニットファイル作成

サーバー再起動後も自動で立ち上がる設定。

  1. 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
    
  2. ファイルを保存したら、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"
    仮想環境の pythongunicorn を優先するよう、環境変数 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 に中継。

  1. 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 間で通信できる

  1. 設定の有効化・テスト:
    sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/

    sudo nginx -t
  2. テストが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/ にアクセスし、鍵マークが表示されれば 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

開発の流れ

SSH ローカルポートフォワードによる開発手順
ssh -L 5000:localhost:5000 -p 2222 youruser@YOUR_VPS_IP

このコマンドでローカル環境の 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トンネル経由のローカルアクセスだけが通る。

🎉 まとめ

アプリを公開する土台ができたので、次回はアプリ制作の流れと環境を作る。

ABOUT ME
いなさく
住んでる家が崩れそうなので、建て替え費用をまかなうために 副業をがんばるサラリーマン
ブログランキング・にほんブログ村へ

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA