Python

VPSを使ってウェブアプリを作りたい1(VPSへのSSH接続とGunicorn,Nginx設定)

はじめに

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)、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"
    仮想環境の 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 を実行した際に起動対象に登録される。

ファイルを保存したら、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で管理する。

  1. developブランチを作り、ローカル(PC)で開発する。
  2. ローカルでdevelopブランチを変更したらリモート(Github)にpushする。
  3. Github上でプルリクエスト(mainブランチにdevelopブランチをマージする)を作成し、
    developブランチの変更をmainブランチに反映させる。
  4. Github Actionsにより、自動でテストとVPSへの反映を行う。

つづき

つぎはGithub ActionsでのCI/CD設定

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

COMMENT

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

CAPTCHA