PR
PR

【Python_study_Day17】HTML変換サービスを本番環境にデプロイ

記事内に広告が含まれています。

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アプリケーションを本番環境にデプロイし、インターネットに公開する作業は完了です。

コメント

タイトルとURLをコピーしました