VPSでflaskサイトを安全に動かすための基礎

初めてのサイトでAIに言われるまま成り行きで作成してしまったので、1からすべて復習していく。

またFlaskサイトを一番初めに作成したのでFlaskで進めていくが、他のNode.jsやWordpressなどで構築したサイトでも共通の部分が多い。

違いは「何でアプリを起動するか」と「何をインストールするか」。セキュリティや基本の設定はほぼ同じになる。

まずVPSを知ろう

VPSとは(バーチャルプライベートサーバーの略)。

  • OSを自分で選んでインストールできる(Ubuntu・CentOSなど)
  • root権限(管理者権限)がある → なんでもインストールできる
  • Webサイト、アプリ、ゲームサーバーなど自由に構築できる

自由度はほぼ無制限だが、サーバーの知識やセキュリティの設定、バックアップ、OSのアップデートなどまですべて自分で行う

一方レンタルサーバーは、OSの選択ができなかったりサーバーやセキュリティの設定を管理会社が行ってくれる。その分費用が少し高い。

またWordpressであればサイトすべてがWordpressで構築されてしまうなど、ある程度の制限も受けることになる。(VPSではサイトの内の一部だけWordpressにすることも可能)

VPSで使うOSってなに?

OS(オペレーティングシステム)とはサーバーを動かす基本ソフトのこと。

パソコンにWindowsやmacOSが入っているように、VPS(仮想サーバー)にもOSをインストールしないと何もできない。

またVPSではWindowsやmacOSではなく、「Linux系OS」が主流。

無料・軽くて安定・ネットワークに強い・世界中で使用されている・WindowsのOSは有料で高額、などの点で定番となっている。

OS選択で何が変わるの?

OS選択で変わることは、「コマンドの書き方」・「入っているソフトやバージョン」・「サポート期間」・「更新方針」などが異なる。

インストールコマンドソフトのバージョンサポート期間方針
Ubuntuapt install nginx比較的新しい5年安定 + 新しさのバランス
Debianapt install nginx安定重視で古め約5年安定重視(更新頻度低い)
Rocky Linux /
AlmaLinux
yum install nginx または
dnf install nginx
企業向けで保守的10年超安定、企業向け

Ubuntuは日本語記事が多くエラー解決しやすかったり、Debianは英語メインだが技術者向けの情報が多く、Rocky Linux / AlmaLinuxは商用サポート契約ができたりもする。

「Ubuntu」・「Debian」・「Rocky Linux / AlmaLinux」だいたいこの3つを覚えておけばいい。

この記事では個人開発でFlaskサイトの構築なので、Ubuntuを選択して進めていくものとする。

VPSを作成したらログインしてユーザー名を作成する

VPSを作成するとレンタル会社から接続に必要な「IPアドレス」がもらえる。

ssh root@サーバーのIPアドレス

これをターミナルに打ち込んでサーバーにログインする。

このssh root にある「root」は、サーバー内に関する全権限をもっている。

このrootは全サーバーでの共通名でもあるため、以下のコードをターミナルに打ち、このrootを任意のユーザー名に変更する。

adduesr ユーザー名

ユーザー名を打ちEnterを押すと、パスワード設定を求められる。2回同じものを繰り返す。

次に表示されるFull name や Room number などはEnterで省略できる。

最後に以下のコードが表示されるので、y → Enterを押してユーザーの作成が完了。

Is the information correct? [Y/n] 

作成したユーザーに管理者権限を付与する

新しく作成したユーザーは「管理者権限(root権限)」がない普通のユーザーであるため、重要なファイルの編集などができない状態。

管理者権限を一時的に借りるコマンドが「sudo」であるため、以下のコマンドで作成したユーザー名をsudoを使用できるグループに追加する。

usermod -aG sudo ユーザー名

# Enterの後 "groups ユーザー名" を実行し、"ユーザー名:ユーザー名 sudo" が表示されればOK

SSHログインを試し、rootでの直ログインを禁止にする

SSHログインとは、自分のPCからインターネット経由でサーバーに接続してコマンド操作できる仕組みのこと。

ユーザー名がrootのままだとパスワードの総当たり攻撃でハッキングなどの被害にあう可能性があるため、rootでの直ログインをできないように変更する。

新しいターミナルを起動して、以下のコマンドでサーバーにログイン。

ssh ユーザー名@サーバーのIPアドレス

# ユーザー名@hostname:~$ が表示されればOK

以下のコマンドでsudo権限が使えるかテスト。

sudo whoami

# 初回はパスワードを求められる。rootと表示されればOK

ユーザー名でsudo権限の確認が取れたら、rootでの直ログインを禁止にしていく。(実行前に必ず権限を確認)

ターミナルに再度rootでログインし、以下のコマンドでSSHの設定ファイルを開く。

sudo nano /etc/ssh/sshd_config

# PermitRootLogin yes の行を探し、PermitRootLogin no に変更する。
# Ctrl+O → Enter → Ctrl+X で保存

sudo systemctl restart ssh
#SSHを再起動

再起動をかけたターミナルはそのままで、新しいターミナルを開きssh root@でログインを実行して「Permission denied」が表示されれば完了。

これでrootでのアクセスは封じられ、指定したユーザーでのみアクセスができるようになった。

パスワードの入力を鍵認証に切り替える

rootでのログインはできないようになったが、パスワードの盗み見や総当たり攻撃などで突破される可能性がまだ残っている。

これを鍵認証に切り替えることで、さらにセキュリティを上げることができる。

鍵認証にパスフレーズを設定すれば、入力の手間は増えるがPCの盗難などでも被害が防げる。
パスフレーズを設定しなければ、パスなしでログインが高速になり運用が楽になる。
チーム開発ではパスフレーズを設定し、個人開発ではパスフレーズを設定しないなど、用途によって変えるといい。

サーバーとPC両方に設定が必要になるため、まずローカルPCにSSH鍵を作成する。

PCのスタートボタンからcmdを開き、以下のコマンドを実行。

ssh-keygen -t ed25519 -C "ユーザー名@vps"

# ひとつめに保存場所を聞かれる。デフォルトでOKならEnter
# 次にパスフレーズを聞かれる。なしでいいならEnter
# パスフレーズの再入力を求められる。

その後作成されるid_ed25519に「.pubがつく方:公開鍵」「つかないほう:秘密鍵」になる。

サーバーに公開鍵を登録していくので、cmdで以下のコマンドを実行しコピペする中身を確認する。

type %USERPROFILE%\.ssh\id_ed25519.pub

# ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJfN9m5tXXXXXXXXXXXX ユーザー名@vps
こんな感じの1行をまるっとコピー

そしたらターミナルから”ssh ユーザー名@IPアドレス”でサーバーにログインし、以下のコマンドでサーバーに公開鍵を登録していく。

mkdir -p ~/.ssh
#現在ログイン中のユーザーのホームディレクトリに.sshフォルダを作成

chmod 700 ~/.ssh
#フォルダの権限を700に変更。所有者のみ読み書き実行すべてOK。他者は不可。

nano ~/.ssh/authorized_keys
#.sshフォルダにファイルを作成&保存
# authorized_keysには、cmdの"type %USERPROFILE%\.ssh\id_ed25519.pub" で確認した1行をまるっとコピペする。

chmod 600 ~/.ssh/authorized_keys
#ファイルの権限を600に変更。所有者のみ読み書きOKに。他者は不可。

~(チルダ)は、ログインしているユーザーのホームディレクトリを表す記号。
“ls / “で開いたサーバー内の /home/ユーザー名 がホームディレクトリに当たる。
/ 直下に作成されるわけではないことに注意。(上のコマンドであれば、/home/ユーザー名/.ssh が作成される)

これでサーバーに公開鍵があり、ローカルのPCに秘密鍵がある状態になった。

そしたらターミナルから、ssh ユーザー名@IPアドレス でログインを実行。

パスフレーズを設定した場合は打ち込んでログインができるか?、設定していない場合はパスフレーズを求められずにログインできることを確認する。

確認が取れたら、パスワード入力でのログインを禁止にしていく。

サーバーにログインしているターミナルで以下のコマンドを実行。rootでのログインを禁止にしたのと同じファイルを開く。

sudo nano /etc/ssh/sshd_config

# PasswordAuthentication yes のコメントアウトを外して PasswordAuthentication no に変更して保存。

sudo systemctl restart ssh
#SSHの再起動

これで「rootでの直ログイン」と「パスワード入力でのログイン」が禁止され、秘密鍵を持っているPCからのみアクセスができるようになった。

ファイアウォールを設定する

まずファイアウォールの要点は、「レンタル会社のマイページで設定するもの」「SSH通信でサーバー内(OS)に設定するもの」は、設置位置と役割が全く異なる別物であるということ。

  • レンタル会社のファイアウォール:外部からのアクセスがサーバーに届く前に弾く
  • OSのファイアウォール:サーバー内に入ってきたアクセスをさらに審査する

基本的に本番環境では、どちらもポート22(SSH)・80(HTTP)・443(HTTPS)のみを開けておく設定にする。

Flaskの開発用途でポート5000や8000を開ける場合は、一時的に開放するようにする。

以降ででてくるGunicornやNginxでの設定とも深く関わってくるので、以下の位置関係は理解しておくこと。

レンタル会社側のファイアウォール設定

まずレンタル会社側のファイアウォールの設定は、

  • ポート80:0.0.0.0で、全世界からのアクセスを許可
  • ポート443:0.0.0.0で、全世界からのアクセスを許可
  • ポート22:サーバーに表示されるMy IPのアドレスのみを指定(例:121.105.xxx.xxxなど)
  • その他5000や8000:基本不要。必要なときに一時的に通信できるように設定

【一時的にポート5000を開くときって?】

本番環境でなんらかのエラーが発生したときに、Nginx + Gunicornの設定なのか、アプリ自体に問題があるのか突き止める必要がある。
その際にポート5000を一時的に開放(レンタル会社側とOS側の両方)して、pythonの起動コマンドなどでFlaskアプリを直接開けるかを確認する。
開ければアプリ自体に問題はなく、Nginx + Gunicornの設定に問題があることがわかる。
確認ができたら開いたポートは必ず閉じること。

OS側のファイアウォール設定

選択したOSのUbuntuには、デフォルトでUFW(アンコンプリケイテッドファイアウォール)というファイアウォールが入っている。

まず以下のコマンドで今の状態を確認する。

sudo ufw status
# inavtive であれば無効、active であれば有効な状態

無効だった場合は、以下の手順で有効化する。(ポート22が許可されていない状態で有効化すると、自分が締め出されることがある)

sudo ufw allow 22/tcp
# 無効の場合は、まずポート22を許可

sudo ufw enable
# 有効化

その後、許可状況に合わせて以下を許可する。

sudo ufw allow 80/tcp
# HTTP通信の許可

sudo ufw allow 443/tcp
# HTTPS通信の許可

一時的にポート5000などを開く場合は、以下のコマンドを実行する。

sudo ufw allow 5000/tcp
# 全世界解放

sudo ufw allow from IPアドレス to any port 5000 proto tcp
# 特定のIPアドレスからのみ許可(自宅など)


sudo ufw delete allow 5000/tcp
# 作業後に全世界解放を閉じる

sudo ufw delete allow from IPアドレス to any port 5000 proto tcp
# 特定のIPアドレスの許可を閉じる

この方法で削除が適用されないものがあれば、以下のコマンドで番号指定で削除できる。

sudo ufw status numbered
# 今の状態を番号付きで表示

sudo ufw delete 番号
# 指定した番号の許可を削除

以下にアクセスがブロックされたログが溜まる。

sudo less /var/log/ufw.log

ここまででVPSの基本的なセキュリティ設定が完了した。

Flaskを動かすための環境を構築する

基本のセキュリティ設定が完了したら、サーバーにPythonなどの必要なソフトをインストールしてFlaskを動かせる環境を構築していく。

ターミナルから以下のコマンドを実行し、Pythonを最新の状態でインストールする。

sudo apt update
# インストールできるパッケージ一覧を最新の状態に更新

sudo apt install -y python3 python3-venv python3-pip
# python3 = 本体
# python3-venv = 仮想環境を作るためのツール
# python-pip = Pythonパッケージ管理ツール
# -y:表示される質問すべてに自動でyesで進める

【installコマンドの apt とか pip って何?】

apt はUbuntu(OS)全体のパッケージマネージャー。root権限(sudo)が必要。
pip はPython専用のパッケージマネージャー。root権限は不要。

Node.jsなど言語が変わるとパッケージマネージャーの名前も npm や yarn のように変わる。

【仮想環境って何?】

pip install 〇〇 で実行するとサーバー全体に指定のPythonライブラリが入るのに対し、仮想環境(venv)を使用することでそのプロジェクト専用のライブラリ置き場を作成できる。
AプロジェクトはFlask2.0、BプロジェクトはFlask3.0、のように別々のバージョンを同じサーバー内で使えるようになる。

pythonがインストールできたら、Flaskアプリを置くフォルダを作成する。

mkdir -p ~/myflaskapp
# -p:親ディレクトリが存在しない場合は自動で作成。すでにある場合は作成をスキップ。

cd ~/myflaskapp
# 作成したフォルダーに移動。

移動したmyflaskapp内に、以下のコマンドで仮想環境を作成する。

python3 -m venv venv
# venv という名前の仮想環境を作成
# -m はPython標準ライブラリのモジュールを直接実行する指定

source venv/bin/activate
# 作成した仮想環境に入る。プロンプトの先頭に(venv)がつけばOK
# 仮想環境を抜けたいときは、deactivate と入力

仮想環境が有効になっている状態で、flaskをインストール。

これでこのプロジェクト(~/myflaskapp)にだけflaskがインストールされた状態になる。

pip install flask

そしたらdeactivateで一度仮想環境を抜け、簡単なテストアプリを作成する。

cd ~/myflaskapp

nano app.py
# app.pyを作成

===== app.py にコピペ =====
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask!"

if __name__ == "__main__":
    app.run(host="0.0.0.0")

# Ctrl+O → Enter → Ctrl+X で保存
現在のフォルダ構成

保存したらflaskをインストールした仮想環境(venv)を有効化して、以下のコマンドでapp.pyを起動。

python app.py

app.pyを起動したターミナルとは別に新しいターミナルを開き、SSHでログイン。

以下のコマンドを入力して「Hello from Flask!」とターミナルに表示されたら完了。

curl http://127.0.0.1:5000
# curl はコマンドからHTTPリクエストを送る。

【ブラウザで127.0.0.1:5000が表示されないワケ】

SSHからapp.pyを起動しているため、VPS内が127.0.0.1のローカルサーバーになる。
そのためターミナルを開いているPCでも、ブラウザからアクセスしてしまうとVPS内ではない外部のPCからのアクセス扱いになり表示されない。

ブラウザで表示を確認したい場合は、基本のセキュリティ設定で閉じているレンタル会社側とOS側のポート5000を解放。
外部からの通信を許可した後で「http://レンタル会社のIPアドレス:5000」にアクセスすると「Hello from Flask!」がwebページとして表示される。
確認したら解放したポートは再度閉じる。

【IPアドレスの127.0.0.1って何?】

「127.0.0.1」は、このサーバーの中からだけアクセスできるようにするIPアドレス。

ローカルPCからpython app.py で立ち上げた開発環境のサイトが「http://127.0.0.1:5000」でローカルサーバー内で開くように、VPSに適用すると、このVPS内でのみ通信できるように設定できる。

GunicornでFlaskの本番サーバーを構築する

Gunicorn(グニコーン)は、Pythonのwebアプリを本番環境で安定して動かすためのWSGIサーバー。

python app.pyで開けるのはFlaskに付属している開発用サーバーで、この代わりに使う。

【WSGIって何?】

WebサーバーとPythonアプリを繋ぐ共通規格のこと。
Webサーバーを作る人とWebアプリを作る人は別世界の人たちであるため、共通規格(ルール)を設けることで統一している。
WSGIは、リクエストが来て → レスポンスを返すという、同期型処理に特化している。
ASGIというWSGIの進化版で、非同期処理にも対応しているものもある。

~/myflaskapp に移動して仮想環境を有効化してから、以下のコマンドでGunicornをインストール。

pip install gunicorn

そのまま以下のコマンドで、Gunicornを起動させる。

gunicorn -w 4 -b 0.0.0.0:5000 app:app

# -w:ワーカー数(サーバーのCPUコア数に応じて変える、とりあえず4)
# -b:どのIPアドレスとポートで待ち受けるか
# app:app、app.py内のFlaskインスタンス名(ファイル名:変数名)

起動させたターミナルとは別に新しいターミナルを開き、SSHでログイン。

以下のコマンドを実行し、「Hello from Flask!」と表示されたら動作確認は終了。

curl http://127.0.0.1:5000

myflaskappを本番環境を配信する/opt/に移動させる

~/ 内で作業していたのは、sudo権限などを指定なくスムーズに作業するためであり、本番環境を配信するのには向かないため、/optフォルダ内にmyflaskappをフォルダごと以下のコマンドでコピペ移動させる。

sudo cp -r ~/myflaskapp /opt/

ls /opt
# myflaskappが表示されていたらOK

しかしsudo権限で移動させたため、myflaskappにアクセスできるのがrootのみに制限されている。

これを以下のコマンドでwww-dataがアクセスできるように権限を変更する。

sudo chown -R www-data:www-data /opt/myflaskapp

これで本番環境に適した場所にアプリが配置された。

権限について知ろう

権限とは、ファイルやディレクトリに対して、誰が「読み(r)・書き(w)・実行(x)」できるかを決めるルール。

# ls -l で確認できる例
-rwxr-xr-x 1 www-data www-data ...

【一番最初の文字:-】
・-:通常のファイル
・d:ディレクトリ
・l:シンボリックリンク

【次の9文字:rwxr-xr-x】
・r:読み取り
・w:書き込み
・x:実行
・3文字ずつで「所有者 / グループ / その他」に分けて表示。

【数字:1】
・ハードリンクの数。通常は1。

【www-data www-data】
・はじめのwww-data:所有者ユーザー
・次のwww-data:所有グループ

後は、ファイルサイズ、最新更新日時、ファイル名と続く。

また権限は数字でも表現できる。

・r = 4
・w = 2
・x = 1

足し算で表現。

・7 = rwx
・6 = rw-
・5 = r-x

そして各ファイルやフォルダに権限を付与(変更)する場合は、以下のコマンドを使う。

sudo chown -R www-data:www-data ファイルorディレクトリ名

# chown(チェンジオーナーの略)
# -R 指定したディレクトリの中身すべてに対して変更を適用する。
# 所有者ユーザー:所有グループ が触れるようにする。

これだと所有者ユーザーと所有グループを付与しただけなので、rwxの権限を以下で変更する。

sudo chmod -R 755 ファイルorディレクトリ名

# chmod(チェンジモードの略)
# 755 は「所有者/グループ/その他」の足し算表記。

ファイルをサーバーにアップすると基本的に所有者(rootか指定ユーザー)の権限になるため、Gunicornがwww-dataで動いていたら権限もwww-dataに変更する必要がある。(www-dataではサーバーにアップできないため)

【www-data ってなに?】

Linuxに最初から用意されている「webサーバー用のユーザー / グループ」。
webサーバーは常に外部からアクセスされるもののため、Userを全権限を持つrootで実行してしまうとシステム全体を壊される可能性がある。
これをwww-dataとすることで、rootと明確に分けることができ安全に運用できる。
同様の理由で、sudo権限を持っているユーザーの指定も厳禁。

systemdでGunicornを自動起動させる

現状、手動で仮想環境を有効化してGunicornを立ち上げている。

これだとターミナルを閉じたりサーバーを再起動すると終了してしまうため、systemdに登録して自動で再起動させるように設定する。

以下のコマンドでファイルを作成し、コピペして保存する。

sudo nano /etc/systemd/system/myflask.service

===== コピペする内容 =====

[Unit]
Description=Gunicorn instance to serve myflaskapp
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/myflaskapp
Environment="PATH=/opt/myflaskapp/venv/bin"
ExecStart=/opt/myflaskapp/venv/bin/gunicorn -w 4 -b 127.0.0.1:8001 app:app

[Install]
WantedBy=multi-user.target

[Unit]セクション、サービスの概要を書く。
Description = は、このサービスの説明文。sudo systemctl status myflask で状態を確認するときに表示される。説明文なので日本語も可。わかりやすいものにする。
After = は、ネットワークが使えるようになったらこのサービスを起動するという指定。

[Service]セクション、サービスの実行方法を書く。
User = は、このサービスをどのユーザー権限で動かすかの設定。全権限を持っているrootで動かすのは危険なためwww-dataにする。
Group = は、このサービスをどのグループ権限で動かすかの設定。(こっちもroot厳禁)

Gunicornがwww-dataで動いていれば、外部ユーザーからのリクエストをwww-data権限で処理できるということ。後述するNginxが標準で www-data で動くので設定しておくと共有がスムーズになる。

WorkingDirectory = は、サービスの作業ディレクトリ。Flaskアプリ app.py がおいてある場所を指定。Gunicornがここを基準に実行される。
Environment = は、環境変数の指定。仮想環境内のbinディレクトリを指定することで、この仮想環境内のPythonやGunicornなどを使うようにしている。
ExecStart = は、サービスの起動時に実行するコマンド。Gunicornが設置されているパスを指定し、worker数と-b 127.0.0.1:8001 app:app(app.py の中のFlask インスタンス名 appをWSGIアプリとして起動) を指定。本番環境では外部に公開せずVPS内でのみ通信できる設定にしたいため、127.0.0.1:ポート番号で起動させる。

[Install]セクション、サービスをどう自動起動させるかを書く。
WantedBy = は、どのタイミングにサービスを紐づけて起動させるかの指定。multi-user.targetは、OSにログインできる普通の運用状態になったらこのサービスも起動させるという指定。(サーバーが再起動したら自動で立ち上がる)

基本ルールとして、ひとつのポートでひとつのプロセスしか使用できない。
Gunicornの例でよくでてくる8000番はテスト環境として扱い、はじめから8001番から割り振っていくと、複数のプロジェクトを扱うときに衝突を避けられる。

プロセスとは、実行中のプログラムの実態のこと。
例えばターミナルでgunicorn -w 4 -b 127.0.0.1:8001 app:appを実行したら、OSがこのプログラム(Gunicorn)を読み込んで動かす。
このプロセスには(PID:プロセスID)が付く。プロセスが生きている間は仕事中のため、ほかのプロセスを追加することはできない。

serviceファイルが書けたら、以下の一連のコマンドを順に実行する。

sudo systemctl daemon-reload
# systemdを再読み込みさせる。新規追加や編集したら必ず行う。

sudo systemctl enable myflask.service
# サーバーを再起動したときにmyflask.serviceを自動で起動させる。
# enable の箇所を disable にすると自動起動設定を削除できる。

sudo systemctl start myflask.service
# サービスを今すぐ起動させる。enableは次回以降の起動設定。
# start の箇所を stop にすると指定サービスが即止まる。

sudo systemctl status myflask.service
# サービスの状態を確認する。

systemdは、OSの起動時に最初に立ち上がってプロセスを管理する。(systemd自体はフォルダではなくinitシステムと呼ばれるもの、サービス(デーモン)の起動・停止・監視を一括で行う。)

systemctl は、このsystemdに指示を出すコマンド。

これで自動起動が完了し、新しいターミナルに以下のコマンドを打つことで「Hello from Flask!」がいつでも表示されるようになった。

curl http://127.0.0.1:8001

ドメインを取得する

次のNginx設定でドメインを使うため、ここでドメインを取得しておく。

色々なサイトやサーバーでくっついてくるものなどもあるが、今回はお名前.comから取得する。

リンクにアクセス → 欲しいドメイン名で検索 → 気に入ったものを選択購入。

ここまでできたら、ネームサーバーの設定に入る。

ネームサーバーを設定する

インターネットでは、最終的に通信はIPアドレスに向けて行われる。

IPアドレスの数字は人間には覚えにくいため、代わりにドメインを使う。

この「ドメイン → IPアドレス」に変換してくれるのが、DNS(ドメインネームシステム)。

そして、ドメインに対してどのサーバーのDNSを参照するかを決めるのがネームサーバー。


今回では、お名前.comでネームサーバーのみを指定し、VPSのレンタル会社側でDNSの設定を行う。

簡潔にまとめると、まずお名前.comで取得したドメインをVPSのDNSに登録する。(AレコードでサーバーのIPアドレスも追加する)

そうするとネームサーバー名が2~3つ表示されるので、それをお名前.com側の指定ドメインに設定。

DNSの伝播に数時間かかるので、ある程度の時間が経ったら以下のコマンドで状態を確認する。

nslookup ドメイン名

# 以下の結果が返る

サーバー:  cdns01.kddi.ne.jp
Address:  2001:xxx:xxxx:x::x

権限のない回答:
名前:    ドメイン名
Address:  160.xxx.xxx.xxx

権限のない回答の部分で、ドメイン名とDNSに登録したサーバーのIPアドレスが返ってきたら完了。

今はドメインにアクセスしてもhttp(ポート80)リクエストを受ける設定をどこにも書いていないため「このサイトにアクセスできません」と表示される。

これをNginxで解決していく。

Nginxでリバースプロキシを設定する

リバースプロキシとは、外部のインターネットからサーバーへアクセスされる通信を中継する仕組み。

NginxはGunicornへの中継役となり、ポート80や443に来たリクエストをGunicornが動いているポート8000番などにつなげる役割を果たす。(Gunicornはあくまでサーバーの内部で動いていて外からは見えない。Nginxは外からのアクセスを受ける役)

  • Gunicorn を直接インターネットに晒さず安全
  • HTTPS 通信を提供(SSL証明書を管理)
  • 静的ファイルを高速に配信
  • 大量アクセスを効率よくさばける

まずNginxをインストールする。

sudo apt update
sudo apt install nginx

Nginxの設定ファイルは、Ubuntu系だと以下に置く。

/etc/nginx/sites-available/ドメイン名

以下のコマンドでファイルを作成し、エディタが開くので中に以下をコピペする。

sudo nano /etc/nginx/sites-available/ドメイン名

# ===== コピペ内容 =====

server {
    listen 80;
    server_name ドメイン名;

    location / {
        proxy_pass http://127.0.0.1:8001;
        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;
    }

    location /static/ {
        alias /opt/myflaskapp/static/;
    }
}

server {} は、仮想ホスト(どこに案内するか)をドメインごとに定義する。

listen 80; はポート番号。この場合はhttpリクエストのみ対応。

server_name ドメイン名; は、どのドメインへのアクセスに対応するかの指定。このドメインにアクセスがきたら、このserver {} が使われる。

location は、Gunicornにリバースプロキシする部分。

location / {} は、/static/ などを指定していないリクエストすべてをどう処理するかの指定(”/” 全パスの意味)。locationでは、完全一致のパスが最優先されるため、指定がないものはすべてここに流れてくることになる。

proxy_pass http://127.0.0.1:8001 は、NginxからGunicornにリクエストを転送する設定。listen に指定したポート80に来たリクエストをproxy_passに渡す。

proxy_set_header Host $host; は、ブラウザがサーバーにアクセスするときにつけるHOSTヘッダーをそのままGunicornに渡す。HOSTヘッダーは、どの「ドメインでアクセスされたか」の情報。この指定がないと勝手に書き換わることがあり、リダイレクトがうまくいかないなどが起きる可能性がある。

proxy_set_header X-Real-IP $remote_addr; は、ユーザーがアクセスしてきた本当のIPアドレスをGunicornに渡す。Nginxを経由することでGunicornから見ると接続元がNginx自身(127.0.0.1)となってしまうためそれを防ぐ。これがないとアクセス制限やセキュリティのログが取れなくなる。

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; は、アクセス元がプロキシをいくつ通ってきたかを全部残して送る設定。なくても動くが、Flaskから見えるのは直前のNginxやCloudflareなどのCDNのIPアドレスだけになり、ユーザーの本当のIPアドレスを失う。ログやセキュリティ的に困るので、基本的に必ず設定しておく。

proxy_set_header X-Forwarded-Proto $scheme; は、元のアクセスがhttpかhttpsかをGunicornに伝える。これがないとGunicornからはすべてhttpでアクセスされているように見えるため、リダイレクトやcookieの挙動がおかしくなる。

location /static/ {} は、静的ファイルを直接配信する部分。http://ドメイン名/static/ で始まるURLにマッチ。

alias /opt/myflaskapp/static/; は、ユーザーがアクセスしてきたstaticのパスと、サーバー上のstaticのパスを対応付ける指定。CSSやjs・imgなどをFlaskを通さずにNginxから直接返すため早い。

Nginxの設定ファイルが保存できたら、シンボリックリンクを作成して有効化する。

sudo ln -s /etc/nginx/sites-available/testsite.click /etc/nginx/sites-enabled/

# ln:linkの略。ショートカットを作成する。
# -s:シンボリックリンクを作成する。 

sites-available は、書きかけも含めてすべてを置いておく倉庫フォルダ。

sites-enabled は、今有効になっている設定だけが入るフォルダ。

実態のファイルをsites-available置いておくことで、設定ファイルのストックとして残しておける。

sites-enabledにはシンボリックリンク(ショートカット)しか入らないため、ファイルの編集はsites-availableの実態ファイルに行う。

シンボリックリンクが作成できたら、Nginxの構文をチェックして、書き間違いがないか確認。

sudo nginx -t

# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
が返ってくればOK

sudo systemctl reload nginx
# 新しい設定を反映させるためにリロード

Gunicornは自分でsystemdにファイルを作成したが、Nginxはinstallした時点で自動で /lib/systemd/system/nginx.service が作成される。
そのため、systemctl で操作ができる。

これでNginxとGunicornがつながったので、登録したドメインにアクセスして「Hello from Flask!」が表示されれば完了。

certbotでSSL化(HTTPS)する

【SSL(HTTPS)って何?】
SSL(ソーシャルソケットレイヤー)は、通信を暗号化する仕組みのこと。
サイトにアクセスするときはHTTPで通信を行うが、HTTPは暗号化なしでやりとりを行うため、IDやパスワード・クレカの情報などが盗聴・改ざんされ放題になってしまう。
HTTPS化されていると通信が暗号化されるため、第三者が見ても中身が分からないようにできる。

最近では改良版のTLS(トランスポートレイヤーセキュリティ)が主流になっている。
昔からの慣習でTLSも含めてSSLと呼ばれている、以下で実装する方法も中身はTLSでの通信になる。

SSL化するには「SSL証明書」を取得しなければならないため、この証明書の取得に必要な certbot をインストールする。

sudo apt install certbot python3-certbot-nginx -y

# certbot と python3-certbot-nginx の2つがインストールされる。

certbot だけで証明書の取得はできるが、それ以降の組込みや更新の設定を自分でする必要がある。

これらをすべて自動で行ってくれるのが、python3-certbot-nginx になる。(Flask + Nginx の構成なら入れたほうが楽)

そしたら以下のコマンドでSSL証明書を取得する。

sudo certbot --nginx -d ドメイン名

# --nginx:certbotにNginxプラグインを使うように指定。証明書取得後の設定を自動で行う。
# -d:対象のドメイン名を指定する。


# Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/testsite.click/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/testsite.click/privkey.pem
This certificate expires on 2025-11-17.
が表示されればOK。

ひとつ目のくくりに上記の公開証明書と秘密鍵の保存場所が記載され、ふたつめのくくりにNginxのSSL設定が追記されたことが書かれている。

これでドメインがSSL化され、ドメイン名を打つだけで「https://ドメイン名」にアクセスできるようになった。

最低限のWebサイトとして動かす基礎は完成。