Pythonの学習17日目です。
今回は、これまでローカル環境で開発してきたHTML変換サービスを、本番環境であるConoHa VPS(Ubuntu 24.04)にデプロイし、conv.server-memo.netというホスト名でインターネット全体に公開するまでの全手順を記録します。
WebサーバーとSSL証明書の準備
外部からのアクセスを受け付けるためのWebサーバー(Nginx)をインストールし、通信を暗号化するためのSSL/TLS証明書を設定します。
Nginxのインストール
Webサーバーとして広く利用されているNginxをインストールします。
詳しい手順は、以下の記事で解説しています。
参考記事:https://vpslife.server-memo.net/ubuntuserver2204_nginx_install/
Let'sEncryptでSSL/TLS証明書を取得
Certbotというツールを使い、無料で利用できるLet's EncryptのSSL/TLS証明書を取得・設定します。
Let'sEncryptでSSL/TLS証明書を取得する方法は、以下の記事で解説しています。
参考記事:https://vpslife.server-memo.net/letsencrypt_nginx/
アプリケーション実行環境の構築
Pythonアプリケーションを動かすための、各種ツールやライブラリをサーバーにインストールしていきます。
Gitインストール
HTML変換サービスのソースコードをGitHubからサーバーに持ってくるため、gitをインストールします。
$ sudo apt update $ sudo apt install git
Redisインストール
このサービスでは、Flask-Limiterによるアクセス制限(レートリミット)機能のデータ保存先としてRedisを使用しています。
そのため、Redisをインストールし、サーバー起動時に自動で立ち上がるように設定します。
# 必要なツールをインストール $ sudo apt install lsb-release curl gpg # Redis公式のGPGキーとリポジトリを追加 $ curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg $ sudo chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg $ echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" \ | sudo tee /etc/apt/sources.list.d/redis.list # リポジトリ情報を更新してRedisをインストール $ sudo apt update $ sudo apt install redis
起動と自動起動設定
Redisの起動と、サーバが起動した際に自動的にRedisを起動させる設定を行います。
$ sudo systemctl start redis-server $ sudo systemctl enable redis-server
Python実行環境のセットアップ
HTML変換サービスを動かすため専用ユーザーを作成し、pyenvを使って特定のバージョンのPythonをインストールします。
Gunicorn起動ユーザの作成
HTML変換サービスではGunicornを使用していますが、セキュリティを高めるためGunicornを直接rootユーザーで動かすのは避けるべきです。
ここでは、Gunicornを起動するための専用ユーザーとして、「gunicorn」というユーザを作成します。
$ sudo adduser gunicorn
pyenvインストールと設定
開発環境と本番環境のPythonバージョンを揃えるため、pyenvをインストールします。
まずは、pyenvをインストールするために必要なパッケージをインストールします。
$ sudo apt install -y build-essential libbz2-dev libdb-dev \ libreadline-dev libffi-dev libgdbm-dev liblzma-dev \ libncursesw5-dev libsqlite3-dev libssl-dev \ zlib1g-dev uuid-dev tk-dev
gunicornユーザーに切り替えて作業
これ以降の作業は、先ほど作成したgunicornユーザーで行います。
$ sudo su - gunicorn
pyenvインストール
インストールスクリプトを実行して、pyenvをインストールします。
$ curl https://pyenv.run | bash
pyenv設定
「~/.bashrc」に以下の設定を追記してpyenvコマンドを使えるようにします。
$ cp -p ~/.bashrc ~/.bashrc_$(date "+%Y%m%d_%H%M%S") $ vi ~/.bashrc
追加する内容は以下のとおりです。
export PYENV_ROOT="$HOME/.pyenv" [[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH" eval "$(pyenv init - bash)"
設定を追加したあとに、sourceコマンドで~/.bashrcを再読み込みし、設定を反映させます。
$ source ~/.bashrc
pyenvのインストールと設定が問題なく完了しているかを確認するために、以下のコマンドを実行してpyenvのバージョンを確認します。
$ pyenv --version pyenv 2.6.8
pyenvで使用するPythonをインストール
pyenvを使って、このHTML変換サービスプロジェクトで必要となる特定のバージョンのPythonをサーバーにインストールします。
$ pyenv install 3.13.3
インストールされたことを確認します。
$ pyenv versions * system (set by /home/tamohiko/.pyenv/version) 3.13.3
HTML変換サービスのデプロイ
Pythonの実行環境が整ったので、いよいよGitHubからHTML変換サービスのソースコードをサーバーにデプロイします。
Gitの初期設定
サーバー上でGitを使うために、あなたのGitHubユーザー名とメールアドレスを設定します。
この設定は、サーバー上で何らかのコミットを行った場合に、その作者を記録するために必要です。
$ git config --global user.name "GitHubユーザー名" $ git config --global user.email "GitHubメールアドレス"
GitHubへのSSH接続設定
サーバーからGitHubへ安全に接続するためにSSH鍵を設定します。
SSH接続を設定することで、パスワードを入力することなく、安全にソースコードをダウンロード(クローン)したり、変更を反映(プッシュ)したりできます。
SSH鍵を用意する方法として、以下の2種類を説明します。
- 開発環境で使っている鍵をコピーして使う
- 新しいSSH鍵を作成する
ちなみに、開発環境で使っている鍵をコピーする方が簡単です。
開発環境のSSH秘密鍵をコピーして使う(推奨)
すでに開発PCでGitHubへのSSH接続に使っている秘密鍵がある場合は、それをサーバーにコピーするのが最も手軽です。
~/.sshディレクトリの作成
SSH鍵を保管するための ~/.ssh ディレクトリを作成し、パーミッションを700に設定します。
$ mkdir ~/.ssh $ chmod 700 .ssh
秘密鍵のコピー
開発PCにある秘密鍵の内容をコピーし、サーバーの ~/.ssh/ ディレクトリにファイルを作成して内容を貼り付けます。
秘密鍵のパーミッションは600に設定して、自分自身だけが読み書き出来るようにしておきます。
新しいSSH鍵を作成する
新しくSSH鍵を作成する場合は以下の手順で行います。
~/.sshディレクトリの作成
SSH鍵を保管するための ~/.ssh ディレクトリを作成し、パーミッションを700に設定します。
$ mkdir ~/.ssh $ chmod 700 .ssh
SSH鍵の生成
ssh-keygen コマンドを使って、新しい秘密鍵と公開鍵のペアを作成します。
$ cd ~/.ssh $ ssh-keygen -t ed25519 -C "GitHub登録メールアドレス"
鍵の保存場所やパスフレーズを聞かれますが、特に指定がなければEnterキーを押して進めて問題ありません。
この場合、以下の名前で鍵が作成されます。
- 秘密鍵:id_ed25519
- 公開鍵:id_ed25519.pub
公開鍵をGitHubに登録
作成した公開鍵 (~/.ssh/id_ed25519.pub) の内容をコピーし、GitHubに登録します。
- WebブラウザでGitHubにログイン
- 画面右上の自分のアイコンをクリックし、[Settings] を選択
- 左側のメニューから [SSH and GPG Keys] をクリック
- 「New SSH key」ボタンを押し、Titleに分かりやすい名前を入力し、Keyの欄にコピーした公開鍵を貼り付けて登録
SSH接続設定の最適化 (configファイルの作成)
複数のSSH鍵を管理している場合や、接続情報をシンプルにするために、SSHクライアントの設定ファイル「~/.ssh/config」を作成することをおすすめします。
このファイルに「どのホストに」「どの鍵を使って」接続するかを記述しておくと、SSH接続がスムーズになります。
$ vi ~/.ssh/config
設定内容は以下のとおりです。
IdentityFileには、GitHubへの接続に使用する秘密鍵のファイル名(例: id_ed25519)を正確に指定します。
Host github.com HostName github.com User git IdentityFile ~/.ssh/秘密鍵 IdentitiesOnly yes
設定が完了したら、以下のコマンドを実行してGitHubへのSSH接続が成功するかを確認します。
$ ssh -T git@github.com
以下のようなメッセージが表示されれば、接続は成功です。
Hi「GitHunのユーザ名」! You've successfully authenticated, but GitHub does not provide shell access.
GitHubからリポジトリをクローンする
準備が整ったら、git cloneコマンドでGitHubからソースコードをサーバーにダウンロードします。
$ git clone git@github.com:servermemo/html_converter.git
ダウンロードが完了したら、プロジェクトのディレクトリに移動しておきます。
$ cd html_converter
.evnの作成
今回のWebサービスに必要な、アプリケーションの秘密鍵 (SECRET_KEY)やRedisの接続情報など、環境に依存する設定を.envファイルに記述します。
.envファイルを使うことで、パスワードやAPIキーといった機密情報をソースコード(Gitリポジトリ)から分離できます。
これにより、万が一ソースコードが外部に漏れても、重要な情報が守られるため、セキュリティが向上します。
$ vi .env
以下の内容を記述してください。
FLASK_SECRET_KEY='シークレットキー' REDIS_URL='redis://localhost:6379'
SECRET_KEYの生成方法
FLASK_SECRET_KEYには、推測不可能なランダムな文字列を設定する必要があります。
これは、Flaskのセッション管理やCSRF保護など、セキュリティ機能の根幹をなす非常に重要なキーです。
Pythonのsecretsモジュールを使えば、以下のコマンド一発で安全なキーを生成できます。
$ python3 -c 'import secrets; print(secrets.token_urlsafe(48))'
表示された文字列をコピーし、.envファイルのFLASK_SECRET_KEYの値として貼り付けてください。
.envファイルは、絶対にGitの管理に含めないでください。
プロジェクトの.gitignoreファイルに.envという行を追加し、誤ってリポジトリにコミットしないように設定しておくのが一般的です。
Python仮想環境の構築とモジュールのインストール
ソースコードの準備ができたので、このプロジェクト専用のPython仮想環境を構築します。
仮想環境を作成することで、他のPythonプロジェクトやシステム全体に影響を与えることなく、このアプリケーションに必要なライブラリ(モジュール)だけをクリーンに管理できます。
今回は、近年注目されている高速なPythonパッケージ管理ツールuvを使用します。
uvインストール
インストールスクリプトを実行してuvをインストールします。
$ curl -LsSf https://astral.sh/uv/install.sh | sh $ source ~/.bashrc
プロジェクトで使うPythonバージョンを指定
pyenvを使い、このプロジェクトのディレクトリ内で有効にするPythonのバージョンを指定します。
このコマンドを実行すると、ディレクトリ内に.python-versionというファイルが作成され、uvなどのツールが自動的にこのバージョンを認識するようになります。
作業は必ずプロジェクトのルートディレクトリで実行してください
$ pyenv local 3.13.3
uvで仮想環境を作成・有効化
uv venvコマンドを実行すると、pyenvが認識しているバージョン(3.13.3)を使って、.venvという名前の仮想環境が作成されます。
$ uv venv
作成した仮想環境を有効化(アクティベート)します。
有効化すると、コマンドプロンプトの先頭に(プロジェクトのディレクトリ名)と表示されます。
$ source .venv/bin/activate (html_converter) $
HTML変換サービスに必要なモジュールをインストール
有効化した仮想環境の中に、プロジェクトで必要なライブラリ一式をrequirements.txtファイルを使ってインストールします。
(html_converter) $ uv pip install -r requirements.txt
これで、アプリケーションを実行するための隔離されたPython環境が、必要なライブラリと共に整いました。
GunicornとSystemdの設定
Nginxからのリクエストを受け取り、Pythonアプリケーションを裏側で動かし続けるため、GunicornをSystemdで管理します。
gunicornユーザーから抜ける
Systemdの設定ファイルは、管理者権限を持つユーザーで作成する必要があります。
これまでの作業はgunicornユーザーで行っていたので、まずはexitコマンドで元のユーザー(sudoが使えるユーザー)に戻ります。
(html_converter) $ exit
Systemd設定ファイルの作成
Systemdでは、「通信の待受(ソケット)」と「実際の処理(サービス)」を分離して管理する「ソケットアクティベーション」という仕組みを使います。
Systemdに登録するための設定ファイルを、それぞれ以下の名前で作成していきます。
- ソケット:/etc/systemd/system/html_converter.socket
- サービス:/etc/systemd/system/html_converter.service
/etc/systemd/system/html_converter.socketファイル作成
ソケット用の設定ファイルを作成します。
$ sudo vi /etc/systemd/system/html_converter.socket
設定内容は以下のとおりです。
[Unit] Description=html_converter socket [Socket] ListenStream=/run/html_converter/html_converter.sock SocketUser=nginx SocketGroup=nginx SocketMode=0660 [Install] WantedBy=sockets.target
/etc/systemd/system/html_converter.serviceファイル作成
サービス用の設定ファイルを作成します。
$ sudo vi /etc/systemd/system/html_converter.service
設定内容は以下のとおりです。
[Unit] Description=html_converter service with Gunicorn After=network.target Requires=html_converter.socket [Service] User=gunicorn Group=gunicorn WorkingDirectory=/home/gunicorn/html_converter ExecStart=/home/gunicorn/html_converter/.venv/bin/gunicorn --workers 3 app:app Restart=on-failure [Install] WantedBy=multi-user.target
Systemdの設定を有効化する
2つの設定ファイルを作成したら、Systemdにその存在を認識させます。
$ sudo systemctl daemon-reload
html_converter.socket起動と自動起動の設定
html_converter.socketの起動と自動起動の設定を行います。
$ sudo systemctl start /etc/systemd/system/html_converter.socket $ sudo systemctl enable /etc/systemd/system/html_converter.socket
これで、サーバーへのリクエスト(Nginx経由)があると、html_converter.socketがそれを受け取り、Systemdがhtml_converter.serviceを起動してGunicornが動き出す、という連携の仕組みが完成しました。
Nginxの設定:リバースプロキシとGunicorn連携
WebサーバーであるNginxを設定し、リバースプロキシとして動作させます。
リバースプロキシとは、インターネットからのリクエストを一度Nginxが受け取り、その後ろで待機しているアプリケーションサーバー(今回はGunicorn)にそのリクエストを転送する仕組みです。
これにより、静的ファイルの配信やSSL/TLSの処理、アクセス制限などをNginxに任せることができ、アプリケーションの負荷を軽減できます。
リバースプロキシ用の共通設定ファイルを作成
設定を使い回せるように、リバースプロキシで必要となる共通のヘッダー情報をproxy_paramsというファイルにまとめておきます。
こうすることで、メインの設定ファイルがスッキリし、管理が楽になります。
$ cd /etc/nginx $ sudo vi proxy_params
以下の内容を記述してください。
これはクライアントの、元のリクエスト情報を正しくGunicornアプリケーションに伝えるための、基本的な設定です。
proxy_set_header Host $http_$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;
レートリミット(アクセス制限)用設定ファイルを作成
不正なアクセスやサーバーへの過負荷を防ぐため、レートリミットを設定します。
ここでは、クライアントのIPアドレスを基準に、1分あたりのリクエスト数を制限します。
今回は、limit_req_zone.confという名前で設定ファイルを作成します。
$ cd conf.d $ sudo vi limit_req_zone.conf
html_converterという名前のゾーンを作成し、クライアントIPアドレスごとに1分あたり10リクエストまでという制限を設ける設定です。
limit_req_zone $binary_remote_addr zone=html_converter:10m rate=10r/m;
HTML変換サービス専用(バーチャルホスト)の設定ファイルを作成
サービス専用の設定ファイルを作成します。
このファイルで、HTTPS通信の設定やGunicornとの連携を定義します。
$ sudo vi conv.server-memo.net.conf
# Gunicornが待機しているソケットを指定します。 # Nginxはここにリクエストを転送します。 upstream app_server { server unix:/run/html_converter/html_converter.sock fail_timeout=0; } server { server_name conv.server-memo.net; index index.html; root /usr/share/nginx/conv; access_log /var/log/nginx/conv_access.log ; error_log /var/log/nginx/conv_error.log ; listen 443 ssl; # managed by Certbot ssl_certificate /etc/letsencrypt/live/conv.server-memo.net/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/conv.server-memo.net/privkey.pem; # managed by Certbot include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot location ^~ /.well-known { root /usr/share/nginx/conv; } location / { include proxy_params; limit_req zone=html_converter burst=5 nodelay; limit_req_status 429; proxy_pass http://app_server; } } server { if ($host = conv.server-memo.net) { return 301 https://$host$request_uri; } # managed by Certbot listen 80; server_name conv.server-memo.net; return 404; # managed by Certbot }
設定反映
Nginxを再起動して、これまでの設定をすべて有効にします。
$ sudo systemctl restart nginx
動作確認
すべての設定が完了したら、Webブラウザから https://conv.server-memo.net にアクセスし、HTML変換サービスが正しく表示・動作することを確認します。
これで、ローカルで開発したFlaskアプリケーションを本番環境にデプロイし、インターネットに公開する作業は完了です。
コメント