Human Resource AI【Ubuntu VPS公開編】
はじめに
これまでの連載では、Python・Flask・Dify・RAGを利用して、人事業務を支援するWebアプリケーション「Human Resource AI」を構築してきました。
今回は、完成したHuman Resource AIをUbuntu VPS上のDocker環境へ配置し、すでに稼働しているサービスと共存させながら、外部からHTTPSで利用できるようにします。
今回利用するUbuntu VPSでは、Docker Engine、Docker Compose、Nginx Proxy Managerがすでに稼働しているものとします。WordPressや別のアプリケーションなどが同じVPS上で動作している場合でも、本記事ではそれらの内部構成には立ち入りません。
本記事では、このDocker環境へ Human Resource AI を独立したDocker Composeプロジェクトとして追加します。
Human Resource AIは既存サービスへ組み込むのではなく、独立したDocker Composeプロジェクトとして追加します。Nginx Proxy Managerとは、複数のComposeプロジェクトで共有する外部proxyネットワークを利用して接続します。
Internet
↓
HTTPS
↓
Nginx Proxy Manager
↓
proxyネットワーク
↓
Human Resource AI
Dockerコンテナ
↓
Gunicorn :5000
↓
Flask
├─ SQLite
└─ Dify API
本記事では既存のその他のサービスは変更せず、Human Resource AIだけを個別に起動・停止・更新できる構成にします。
本記事で学ぶこと
| 項目 | 内容 |
|---|---|
| VPS配置 | Human Resource AIをUbuntu VPSへ配置する |
| Docker | Flaskアプリケーションをコンテナ化する |
| Docker Compose | Human Resource AIを独立して管理する |
| Gunicorn | Flaskを本番向けWSGIサーバーで起動する |
| SQLite | Human Resource AI専用DBを永続化する |
| Docker Network | Nginx Proxy Managerと接続する |
| Nginx Proxy Manager | 独自ドメインからFlaskへ転送する |
| HTTPS | Let's EncryptでSSL化する |
| Dify | VPS上のFlaskからDify APIを利用する |
| 共存 | 既存サービスへ影響を与えず追加する |
1. 今回の配置方針
Ubuntu VPSには、すでに複数のDocker Composeプロジェクトが存在しているものとします。Human Resource AIを既存のComposeプロジェクトへ追加するのではなく、専用ディレクトリを作成して独立して管理します。
Ubuntu VPS
│
├─ /opt/docker/proxy/
│ └─ Nginx Proxy Manager(Composeプロジェクト)
│
├─ その他の既存サービス
│ └─ 各Composeプロジェクト
│
└─ /opt/docker/human-resource-ai-product/
├─ compose.yml
├─ Dockerfile
├─ .dockerignore
├─ .env
├─ app.py
├─ config.py
├─ database.db
├─ requirements.txt
├─ services/
├─ templates/
└─ static/
Human Resource AIを操作するときだけ、次のディレクトリへ移動してDocker Composeを実行します。
cd /opt/docker/human-resource-ai-product
他のサービスとはComposeプロジェクトを分離するため、Human Resource AIだけを個別にビルド・起動・停止・更新できます。
2. 既存Docker環境を確認する
まずVPSへSSH接続します。
接続後、OSを確認します。
cat /etc/os-release
Dockerを確認します。
sudo docker --version
Docker Composeを確認します。
sudo docker compose version
稼働中のコンテナを確認します。
sudo docker ps
ここでは、Nginx Proxy Managerと、同じUbuntu VPS上ですでに稼働しているその他のサービスが正常に動作していることを確認します。
この時点では、Human Resource AIはまだ稼働していません。以降の手順で新しいDocker Composeプロジェクトとして追加します。
既存サービスを停止する必要はありません。本記事では既存コンテナを稼働させたままHuman Resource AIを追加します。
3. 共有proxyネットワークを確認する
Nginx Proxy ManagerからHuman Resource AIへ通信するため、Nginx Proxy Managerが利用している共有Dockerネットワークを確認します。
sudo docker network ls
本記事では、共有ネットワーク名を次のようにします。
proxy
詳細を確認します。
sudo docker network inspect proxy
すでにproxyネットワークが存在する場合は、新しく作成する必要はありません。
存在しない場合のみ作成します。
Human Resource AIは、このproxyネットワークへ参加させます。
Nginx Proxy Manager
│
│ proxy network
│
▼
Human Resource AI
│
▼
Gunicorn :5000
4. Human Resource AIのソースを配置する
Human Resource AIのソースはGitHubリポジトリから取得します。本記事では、リポジトリ名をhuman-resource-ai-productとします。
まず、Docker Composeプロジェクトを配置する/opt/dockerへ移動します。
cd /opt/docker
GitHubからリポジトリをcloneします。
git clone https://github.com/aiota-jp/human-resource-ai-product.git
cloneすると、/opt/docker/human-resource-ai-productディレクトリが作成され、その中へソースコードが配置されます。
cd /opt/docker/human-resource-ai-product
配置後、ファイルを確認します。
ls -la
例として、次のような構成になっていることを確認します。
human-resource-ai-product/
├─ app.py
├─ config.py
├─ database.py
├─ create_admin.py
├─ requirements.txt
├─ Dockerfile
├─ compose.yml
├─ .dockerignore
├─ .env.example
├─ services/
├─ templates/
├─ static/
├─ uploads/
└─ outputs/
GitHubから取得した.env.exampleをコピーして、VPSで実際に使用する.envを作成します。
cp .env.example .env
.env.exampleは、必要な環境変数の項目だけを示すテンプレートとしてGitHubで管理します。一方、.envにはSECRET_KEY、Dify APIキー、初期ユーザーのパスワードなど、VPSで実際に使用する値を設定します。
このあと.envを編集し、本番環境用の値を設定します。.envにはAPIキーなどの秘密情報を保存するため、GitHubへコミットしません。
公開前にHuman Resource AIの単体セキュリティを確認する
ここからは、Human Resource AIをインターネットへ公開する前に、アプリケーション単体で確認しておきたいセキュリティ設定を整理します。
今回確認したHuman Resource AIのソースでは、すでに次の対策が実装されています。
| 項目 | 現在の実装 |
|---|---|
| パスワード保存 | Werkzeugのgenerate_password_hash()でハッシュ化 |
| 認証 |
authenticate()でパスワードハッシュを検証 |
| 認可 |
login_required、roles_requiredでロール制御 |
| アップロード名 |
secure_filename()を使用 |
| アップロード形式 |
xlsx、csvに制限 |
| アップロード容量 | 最大16MB |
| 秘密情報 |
.envを利用し、.gitignoreで除外 |
| SQLite |
database.dbをGit管理外に設定 |
| Dify API | APIキーを環境変数から取得 |
今回の完成版ソースでは、初期ユーザーのパスワードをソースコードへ直接記述しません。services/auth_service.pyは、次の4ユーザーの初期パスワードを環境変数から取得します。
admin
staff
user
EMP001
パスワードはVPS上の.envに設定し、ユーザー作成時にWerkzeugのgenerate_password_hash()でハッシュ化してSQLiteへ保存します。
.env
│
├─ ADMIN_INITIAL_PASSWORD
├─ STAFF_INITIAL_PASSWORD
├─ USER_INITIAL_PASSWORD
└─ EMP001_INITIAL_PASSWORD
↓
services/auth_service.py
↓
generate_password_hash()
↓
database.db
本記事では、実際のパスワードやAPIキーは掲載しません。.envには本番用の強いパスワード、SECRET_KEY、Dify APIキーを設定してください。
1. 初期ユーザーのパスワードを.envから取得する
完成版のservices/auth_service.pyでは、初期パスワードを環境変数から取得します。ソースコードには実際のパスワードを保存しません。
def get_default_users():
return (
("admin", os.environ.get("ADMIN_INITIAL_PASSWORD"), "admin"),
("staff", os.environ.get("STAFF_INITIAL_PASSWORD"), "staff"),
("user", os.environ.get("USER_INITIAL_PASSWORD"), "user"),
("EMP001", os.environ.get("EMP001_INITIAL_PASSWORD"), "user"),
)
ensure_default_users()は、ユーザーがまだ存在しない場合だけgenerate_password_hash()でパスワードをハッシュ化して登録します。そのため、すでにdatabase.dbに存在するユーザーのパスワードは、.envを書き換えただけでは変更されません。
今回は本番運用前の初期構築を想定し、新しい.envを設定したうえでSQLiteデータベースを初期化して初期ユーザーを作成します。
2. SECRET_KEYを必須にする
現在のconfig.pyでは、SECRET_KEYが未設定の場合に開発用文字列へフォールバックします。
本番環境では、推測可能なデフォルト値を利用しないようにします。
SECRET_KEY = os.getenv("SECRET_KEY")
if not SECRET_KEY:
raise RuntimeError("SECRET_KEY が設定されていません")
VPSの.envには十分に長いランダム値を設定します。
FLASK_ENV=production
SECRET_KEY=<十分に長いランダム文字列>
.envはGitHubへ登録せず、VPS上で権限を制限します。
chmod 600 .env
3. セッションCookieを本番向けに設定する
HTTPS公開を前提として、config.pyへ次の設定を追加します。
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
それぞれ、
Secure
→ HTTPS通信でのみCookieを送信
HttpOnly
→ JavaScriptからセッションCookieを直接読み取らせない
SameSite=Lax
→ クロスサイト経由のCookie送信を制限
という役割があります。
4. CSRF対策を追加する
現在のHuman Resource AIには、社員登録・研修登録・評価生成・日報登録・Excelインポート・ログインなど複数のPOST処理があります。
インターネット公開する場合は、CSRF対策を追加します。
requirements.txtへ追加します。
Flask-WTF
app.pyでCSRF保護を有効にします。
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.config.from_object(Config)
csrf = CSRFProtect(app)
POSTフォームにはCSRFトークンを追加します。
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
対象はログインだけではありません。POSTを利用する各フォームへ追加します。
ログイン
社員登録・更新・削除
研修登録・履歴更新
評価コメント生成
Excelインポート
日報登録
RAG検索
その他のPOSTフォーム
FAQチャットのfetch()によるJSON POSTについても、CSRFトークンをHTTPヘッダーへ付与するようにします。
5. ログイン試行回数制限を追加する
現在のログイン処理には、連続したパスワード試行を制限する処理はありません。
公開環境では、Flask側またはNginx Proxy Manager側でログイン試行対策を追加します。
アプリケーション側で実装する場合は、例えばFlask-Limiterなどを利用して/loginへの試行回数を制限できます。
/login
↓
一定時間内の試行回数を制限
↓
総当たりログインを緩和
本記事では、少なくとも公開前にログイン試行制限を導入してから外部公開する方針とします。
6. アップロード機能を確認する
現在のソースでは、
ALLOWED_EXTENSIONS = {"xlsx", "csv"}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024
となっており、ファイル名にはsecure_filename()を利用しています。
この方針は維持します。
さらに、本番環境ではアップロードされたファイルをWeb公開ディレクトリへ置かず、Human Resource AIコンテナ内部または専用永続領域でのみ扱います。
7. データベースを直接公開しない
Human Resource AIのSQLiteはファイルDBなので、外部向けポートを持ちません。
既存サービスについても、
MariaDB 3306
PostgreSQL 5432
MCP 8000
をVPSのグローバルIPへ直接公開しない構成を維持します。
外部からHuman Resource AIへアクセスする経路は、
Internet
↓
HTTPS :443
↓
Nginx Proxy Manager
↓
proxyネットワーク
↓
Human Resource AI :5000
に限定します。
Human Resource AIのcompose.ymlでは、ホストへ5000番を公開するports:ではなく、
expose:
- "5000"
を使用します。
8. Gunicornを利用し、Flask開発サーバーを公開しない
ソースコードにはローカル開発用として、
app.run(debug=Config.DEBUG, host="0.0.0.0", port=5000)
があります。
これはローカル実行用として残して構いませんが、VPSではこの方法で公開しません。
DockerコンテナではGunicornからFlaskを起動します。
Nginx Proxy Manager
↓
Gunicorn
↓
Flask
また、.envは必ず、
FLASK_ENV=production
とし、デバッグモードを無効にします。
9. 公開前チェック
Human Resource AIをNginx Proxy Managerへ登録する前に、最低限次を確認します。
| 確認項目 | 公開前の状態 |
|---|---|
| 初期ユーザーパスワード |
.envから取得し、ソースコードへ直接記述しない |
| 初期ユーザー |
.envの初期パスワードから作成 |
SECRET_KEY |
本番用ランダム値 |
.env |
Git管理外・chmod 600
|
| Flask DEBUG | OFF |
| Cookie | Secure / HttpOnly / SameSite |
| CSRF | POST処理を保護 |
| ログイン試行 | 回数制限を導入 |
| Human Resource AI :5000 | 外部へ直接公開しない |
| HTTPS | Nginx Proxy Managerで有効化 |
| SQLite | バックアップ対象にする |
これらを確認してから、以降のDocker構築とNginx Proxy ManagerによるHTTPS公開へ進みます。
記事公開・VPS公開前の秘密情報最終チェック
最後に、記事本文・GitHubリポジトリ・VPSの3か所を必ず確認します。
「.envをGit管理から除外したから安全」と考えるのではなく、記事へ貼り付けたコマンドや画面キャプチャ、Gitの過去コミット、VPS上の設定ファイルまで含めて確認します。
1. 記事本文を確認する
Qiitaへ投稿するMarkdown本文と掲載画像を確認します。
確認対象
├─ APIキー
├─ SECRET_KEY
├─ DBパスワード
├─ 外部サービスの認証情報
├─ SSH秘密鍵
├─ セッショントークン / Cookie
├─ 実際の管理者パスワード
├─ VPSの管理情報
└─ 画面キャプチャに写り込んだ秘密情報
記事では実値を掲載せず、次のようにプレースホルダーへ置き換えます。
SECRET_KEY=<十分に長いランダムな文字列>
DIFY_API_KEY=<Human Resource AI用Dify APIキー>
DIFY_FAQ_API_KEY=<FAQ Chatbot用Dify APIキー>
2. GitHubリポジトリを確認する
現在のファイルだけでなく、Gitの過去コミットも確認します。
少なくとも次のファイルを公開リポジトリへ登録しません。
.env
database.db
*.pem
*.key
.gitignoreへ登録されていることに加え、次を実行して現在Git管理されているファイルを確認します。
git status
git ls-files
APIキーやパスワードを過去に一度でもGitHubへpushした場合は、ファイルを削除するだけで安全になったとは判断しません。該当する認証情報を失効または再発行し、必要に応じてGit履歴からも削除します。
3. VPSを確認する
VPSでは、秘密情報の保存場所とファイル権限、公開ポートを確認します。
cd /opt/docker/human-resource-ai-product
ls -la
stat .env
sudo docker compose ps
sudo ss -lntp
.envはGitHubへ登録せず、VPS上で次のように権限を制限します。
chmod 600 .env
外部公開が必要なのは、基本的にNginx Proxy Managerが受け付けるHTTP/HTTPSです。
80 HTTP
443 HTTPS
一方、次のアプリケーション・データベース用ポートは、インターネットへ直接公開しないことを確認します。
5000 Human Resource AI
8000 MCP
3306 MariaDB
5432 PostgreSQL
Human Resource AIの5000番は、Nginx Proxy Managerと共有するDockerのproxyネットワークからアクセスさせます。
最終確認
[ ] 記事本文に秘密情報がない
[ ] 掲載画像に秘密情報が写っていない
[ ] GitHubの現在のファイルに秘密情報がない
[ ] GitHubの過去コミットに秘密情報が残っていない
[ ] .envがGit管理されていない
[ ] database.dbがGit管理されていない
[ ] VPSの.envがchmod 600になっている
[ ] 本番用SECRET_KEYへ変更済み
[ ] Dify APIキーを本番用に設定済み
[ ] 初期ユーザーのパスワードを`.env`へ本番用の値で設定済み
[ ] Flask DEBUGがOFF
[ ] Human Resource AIの5000番ポートを直接公開していない
[ ] HTTPSが有効
この確認が完了してから、記事の公開およびHuman Resource AIの本番公開を行います。
5. requirements.txtへGunicornを追加する
ローカル開発では、
python app.py
でFlaskの開発サーバーを利用していました。
VPSではFlask開発サーバーではなく、GunicornからFlaskアプリケーションを起動します。
requirements.txtに本番実行用のGunicornと、CSRF対策用のFlask-WTFを追加します。
gunicorn
Flask-WTF
既存のFlaskやrequestsなどの依存ライブラリはそのまま残します。
構成は次のようになります。
Internet
↓
Nginx Proxy Manager
↓
Gunicorn
↓
Flask
6. Dockerfileを作成する
プロジェクトルートにDockerfileを作成します。
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "1", "--timeout", "60", "app:app"]
ここでは、
app.py
└─ app = Flask(__name__)
という構成を前提に、
app:app
を指定しています。
Human Resource AIはSQLiteを利用しているため、本記事ではまずGunicornを--workers 1で起動します。複数workerから同じSQLiteファイルへ同時書き込みする構成は、ロック競合を起こしやすいためです。将来アクセス数が増え、複数worker・複数コンテナへ拡張する場合は、PostgreSQLなどのサーバー型DBへの移行を検討します。
7. .dockerignoreを作成する
Dockerイメージへ不要なファイルや秘密情報を含めないため、.dockerignoreを作成します。
.git
.gitignore
.env
__pycache__
*.pyc
.venv
venv
database.db
.envとdatabase.dbはDockerイメージへ直接含めません。
8. VPS用.envを作成する
VPS上で.envを作成します。
nano .env
Human Resource AIで利用する設定を記述します。
SECRET_KEY=<十分に長いランダムな文字列>
FLASK_ENV=production
DIFY_API_URL=https://api.dify.ai/v1
DIFY_API_KEY=<Human Resource AI用Dify APIキー>
DIFY_FAQ_API_KEY=<FAQ Chatbot用Dify APIキー>
ADMIN_INITIAL_PASSWORD=<admin用の強いパスワード>
STAFF_INITIAL_PASSWORD=<staff用の強いパスワード>
USER_INITIAL_PASSWORD=<user用の強いパスワード>
EMP001_INITIAL_PASSWORD=<EMP001用の強いパスワード>
保存後、権限を制限します。
chmod 600 .env
DifyAPIキーやSECRET_KEYをDockerfile、compose.yml、GitHubリポジトリへ直接記述しないようにします。
9. SQLiteデータベースと永続化ディレクトリを準備する
Human Resource AIではSQLiteを利用します。既存サービスのデータベースとは共有しません。
今回の完成版では、database.dbに加えて、アップロードファイルと出力ファイルもVPS側へ永続化します。
cd /opt/docker/human-resource-ai-product
mkdir -p uploads outputs
本番運用前で既存のSQLiteデータを破棄してよい場合は、古いDBをバックアップして新しく作成します。
cp database.db database.db.bak
rm database.db
touch database.db
database.dbがまだ存在しない場合は、単純に次を実行します。
touch database.db
配置は次のようになります。
/opt/docker/human-resource-ai-product/
├─ database.db
├─ uploads/
└─ outputs/
これらはcompose.ymlからコンテナへbind mountします。
Ubuntu VPS
/opt/docker/human-resource-ai-product/database.db
/opt/docker/human-resource-ai-product/uploads/
/opt/docker/human-resource-ai-product/outputs/
│
│ bind mount
▼
Docker Container
/app/database.db
/app/uploads/
/app/outputs/
これにより、コンテナを再作成してもSQLiteデータ、アップロードファイル、生成ファイルをVPS側へ残せます。
10. compose.ymlを作成する
Human Resource AIは、既存環境とは別のComposeプロジェクトとして管理します。このComposeプロジェクト内で、Human Resource AIをDockerコンテナとして起動します。
【Human Resource AI】
Composeプロジェクト
│
▼
Human Resource AI
Dockerコンテナ
│
Gunicorn
│
Flask
┌────┴────┐
▼ ▼
SQLite Dify
/opt/docker/human-resource-ai-product/compose.ymlを作成します。
services:
human-resource-ai:
build:
context: .
container_name: human-resource-ai
restart: unless-stopped
env_file:
- .env
volumes:
- ./database.db:/app/database.db
- ./uploads:/app/uploads
- ./outputs:/app/outputs
expose:
- "5000"
networks:
- internal
- proxy
networks:
internal:
driver: bridge
proxy:
external: true
ポイントは、Human Resource AI専用のinternalネットワークと、Nginx Proxy Manager接続用のproxyネットワークを分けていることです。
Human Resource AI
│
├─ internal
│ └─ Human Resource AI専用
│
└─ proxy
└─ Nginx Proxy Managerとの通信
11. portsではなくexposeを使用する
今回のHuman Resource AIでは、
ports:
- "5000:5000"
とはしません。
代わりに、
expose:
- "5000"
を使用します。
外部からHuman Resource AIへ直接、
http://VPS-IP:5000
でアクセスさせる必要がないためです。
外部アクセスは必ず、
Internet
↓
80 / 443
↓
Nginx Proxy Manager
↓
proxy network
↓
human-resource-ai:5000
という経路にします。
これにより、既存サービスとのポート競合も避けやすくなります。
12. Compose設定を確認する
起動前にCompose設定を確認します。
cd /opt/docker/human-resource-ai-product
sudo docker compose config
エラーが表示されなければ、設定ファイルとして読み込めています。
13. Human Resource AIをビルドする
Dockerイメージをビルドします。
sudo docker compose build
ビルドが完了したら起動します。
sudo docker compose up -d
状態を確認します。
sudo docker compose ps
Human Resource AIがUpになっていればコンテナは起動しています。
14. 初期ユーザーを作成する
コンテナを起動したら、.envに設定した初期パスワードを使用して、Human Resource AIの初期ユーザーをSQLiteへ登録します。
sudo docker compose exec human-resource-ai python -c "from app import app; from services.auth_service import ensure_default_users; app.app_context().push(); ensure_default_users()"
この処理では、.envに設定した次の初期パスワードが使用されます。
ADMIN_INITIAL_PASSWORD
STAFF_INITIAL_PASSWORD
USER_INITIAL_PASSWORD
EMP001_INITIAL_PASSWORD
パスワードはそのままSQLiteへ保存せず、generate_password_hash()でハッシュ化して保存します。
初期ユーザーが登録されたことを確認します。
sudo docker compose exec human-resource-ai python -c "import sqlite3; c=sqlite3.connect('/app/database.db'); print(c.execute('SELECT username, role FROM user').fetchall())"
次のように表示されれば、初期ユーザーの作成は完了です。
[('admin', 'admin'), ('staff', 'staff'), ('user', 'user'), ('EMP001', 'user')]
ensure_default_users()は、すでに存在するユーザーを作り直しません。そのため、.envの初期パスワードを変更してこのコマンドを再実行しても、既存ユーザーのパスワードは変更されません。
15. Gunicornのログを確認する
ログを確認します。
sudo docker compose logs --tail=100 human-resource-ai
Gunicornが正常に起動していれば、5000番ポートで待ち受けます。
リアルタイムで確認する場合は、
sudo docker compose logs -f human-resource-ai
を使用します。
終了する場合は、
Ctrl + C
を押します。
16. コンテナ内部からFlaskを確認する
python:3.12-slimにはpsコマンドが含まれていないため、コンテナ内部の確認にはPythonからFlaskアプリケーションを読み込む方法を利用します。
sudo docker compose exec human-resource-ai python -c "from app import app; print(app)"
エラーなくFlaskアプリケーションが表示されれば、app:appを読み込めています。
17. proxyネットワークへの参加を確認する
Human Resource AIがproxyネットワークへ参加していることを確認します。
sudo docker network inspect proxy
一覧に、
human-resource-ai
が表示されることを確認します。
これにより、Nginx Proxy Managerから、
http://human-resource-ai:5000
で接続できる構成になります。
18. Human Resource AI用DNSを設定する
Human Resource AIへアクセスするためのサブドメインを用意します。
例として、
hr.example.com
を利用する場合、DNSへAレコードを設定します。
hr.example.com
↓
Ubuntu VPSのグローバルIP
既存サービスとは別のホスト名を利用します。
既存サービス用ドメイン
↓
既存サービス
hr.example.com
↓
Human Resource AI
同じVPSのIPアドレスでも、Nginx Proxy Managerがホスト名によって転送先を分けます。
19. Nginx Proxy ManagerへProxy Hostを追加する
Nginx Proxy Managerの管理画面を開きます。
Hosts
↓
Proxy Hosts
↓
Add Proxy Host
Human Resource AI用に設定します。
| 項目 | 設定例 |
|---|---|
| Domain Names | hr.example.com |
| Scheme | http |
| Forward Hostname / IP | human-resource-ai |
| Forward Port | 5000 |
| Block Common Exploits | ON |
転送先としてVPSのIPアドレスではなく、
human-resource-ai
というDockerコンテナ名を指定します。
これはNginx Proxy ManagerとHuman Resource AIが同じproxyネットワークへ参加しているためです。
20. SSL証明書を取得する
Nginx Proxy Managerを使用して、Human Resource AIをHTTPSで公開します。
DNSの名前解決を確認する
SSL証明書を取得する前に、公開するドメインのDNSがUbuntu VPSのグローバルIPアドレスを参照していることを確認します。
Windowsの場合は、PowerShellから名前解決を確認できます。
nslookup hr.example.com
次のようにVPSのIPアドレスが返れば準備完了です。
名前: hr.example.com
Address: IPv4アドレス
Proxy Hostを追加する
Nginx Proxy Managerの管理画面を開きます。
上部メニューから、次の順に選択します。
Hosts
↓
Proxy Hosts
↓
Add Proxy Host
Details タブを次のように設定します。
Domain Names
hr.example.com
Scheme
http
Forward Hostname / IP
human-resource-ai
Forward Port
5000
Access List
Publicly Accessible
Options は次のように設定します。
Cache Assets OFF
Block Common Exploits ON
Websockets Support OFF
Forward Hostname / IP にはコンテナのIPアドレスではなく、Dockerコンテナ名の human-resource-ai を指定します。
設定が完了したら、上部の SSL タブをクリックします。
Let's Encryptの証明書を取得する
SSL タブを次のように設定します。
SSL Certificate
Request a new Certificate
Force SSL
ON
HTTP/2 Support
ON
HSTS Enabled
OFF
HSTS Sub-domains
OFF
Use DNS Challenge
OFF
設定が完了したら Save をクリックします。
Nginx Proxy ManagerがLet's Encryptへ証明書を申請します。
正常に証明書を取得できると、Proxy Hostsの一覧に戻ります。
hr.example.com の SSL に Let's Encrypt、STATUS に Online と表示されていることを確認します。
HTTPSでアクセスする
ブラウザから次のURLへアクセスします。
https://hr.example.com
Human Resource AIのログイン画面がHTTPSで表示されれば、公開設定は完了です。
通信経路は次のようになります。
https://hr.example.com
↓
DNS
↓
IPv4アドレス
↓
Nginx Proxy Manager
↓
human-resource-ai:5000
↓
Gunicorn
↓
Flask
21. ログイン画面を確認する
ブラウザからHuman Resource AIへアクセスします。
https://hr.example.com
ログイン画面が表示されることを確認します。
「## 14. 初期ユーザーを作成する」で登録した初期ユーザーを使用してログインします。
パスワードは、VPS上の.envに設定した各ユーザーの初期パスワードを使用します。
admin
staff
user
EMP001
各ユーザーでログインでき、権限制御がローカル環境と同じように動作することを確認します。
22. Human Resource AIの基本機能を確認する
VPS公開後、主要機能を順番に確認します。
| 操作 | 確認内容 |
|---|---|
| ログイン | ユーザー認証できる |
| 社員管理 | 社員一覧・登録・更新が動作する |
| 研修管理 | 研修情報を操作できる |
| 評価管理 | 評価情報を確認できる |
| 日報 | 日報を登録・表示できる |
| Excel | インポート・エクスポートできる |
| RAG検索 | Difyを利用して検索できる |
| FAQ | Dify FAQ Chatbotへ質問できる |
23. Dify API連携を確認する
Human Resource AIからDify APIを利用する機能を確認します。
構成は次のようになります。
Browser
↓
Nginx Proxy Manager
↓
Human Resource AI
↓
Flask
↓
Dify API
↓
Dify Application / Knowledge
FAQチャットでは、
Human Resource AI
↓
DIFY_FAQ_API_KEY
↓
Dify FAQ Chatbot
という接続になります。
回答が取得できない場合は、まずコンテナログを確認します。
sudo docker compose logs --tail=100 human-resource-ai
次に.envを確認します。
grep DIFY .env
APIキーそのものを画面共有や記事へ掲載しないように注意します。
24. SQLiteへの保存を確認する
Human Resource AIからデータを登録した後、SQLiteファイルを確認します。
ls -lh /opt/docker/human-resource-ai-product/database.db
コンテナを再起動します。
sudo docker compose restart human-resource-ai
再度ログインし、登録済みデータが残っていることを確認します。
これにより、
Container再起動
↓
database.dbはVPS側に残る
↓
データを継続利用
できていることを確認できます。
25. 既存サービスが動作していることを確認する
Human Resource AI追加後も、同じUbuntu VPS上で稼働していた既存サービスへ影響していないことを確認します。
sudo docker ps
Human Resource AIを追加する前から稼働していたコンテナが、引き続きUpになっていることを確認します。
本記事では既存サービスの内部構成や個別の動作確認手順までは扱いません。Human Resource AIを独立したComposeプロジェクトとして追加したことで、既存サービスを停止・変更せずに共存できていることを確認します。
26. VPS全体のコンテナを確認する
VPS全体のコンテナを確認します。
sudo docker ps
イメージとしては次のようになります。
Ubuntu VPS
└─ Docker環境
├─ Nginx Proxy Manager
├─ その他の既存サービス
└─ Human Resource AI
サービスごとにComposeプロジェクトを分離することで、同じUbuntu VPS上で共存できます。
27. Human Resource AIだけを更新する
Human Resource AIのソースを更新する場合は、Human Resource AIのディレクトリだけで作業します。
cd /opt/docker/human-resource-ai-product
GitHubから最新ソースを取得します。
git pull
通常のソース更新では、再ビルドして起動します。
sudo docker compose up -d --build
初期パスワード方式へ変更するなど、依存関係やDockerイメージを確実に作り直したい場合は、次のように再ビルドできます。
sudo docker compose down
sudo docker compose config
sudo docker compose build --no-cache
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs --tail=100 human-resource-ai
docker compose down -vは使用しません。Human Resource AIのComposeプロジェクトだけを操作するため、同じVPS上の既存サービスは停止しません。
28. Human Resource AIだけを停止・起動する
停止する場合は、
cd /opt/docker/human-resource-ai-product
sudo docker compose stop
起動する場合は、
sudo docker compose start
コンテナを削除して停止する場合は、
sudo docker compose down
再度起動する場合は、
sudo docker compose up -d
とします。
これらの操作は/opt/docker/human-resource-ai-productのComposeプロジェクトだけが対象です。
29. トラブルシューティング
Human Resource AIが起動しない
状態を確認します。
sudo docker compose ps
ログを確認します。
sudo docker compose logs --tail=200 human-resource-ai
app:appを読み込めない
次を実行します。
sudo docker compose exec human-resource-ai python -c "from app import app; print(app)"
app.py内のFlaskインスタンス名がapp以外の場合は、Gunicornの指定を実際の構成に合わせて修正します。
502 Bad Gatewayになる
Human Resource AIが起動しているか確認します。
sudo docker compose ps
proxyネットワークを確認します。
sudo docker network inspect proxy
Nginx Proxy Managerの転送先が、
Scheme: http
Forward Hostname: human-resource-ai
Forward Port: 5000
になっていることを確認します。
Difyへ接続できない
.envを確認します。
grep DIFY .env
コンテナへ環境変数が渡っているか確認します。
sudo docker compose exec human-resource-ai env | grep DIFY
初期ユーザーを作成できない
初期パスワード用の環境変数が設定されているか確認します。値そのものを画面へ表示しないよう、変数名だけを確認します。
grep -E '^(ADMIN|STAFF|USER|EMP001)_INITIAL_PASSWORD=' .env | cut -d= -f1
次の4項目が表示されることを確認します。
ADMIN_INITIAL_PASSWORD
STAFF_INITIAL_PASSWORD
USER_INITIAL_PASSWORD
EMP001_INITIAL_PASSWORD
既存ユーザーがdatabase.dbに存在する場合、.envの値を変更してコンテナを再作成しても既存パスワードは変更されません。本番運用前でDBを初期化してよい場合は、バックアップ後にdatabase.dbを作り直します。
SQLiteでエラーになる
ファイルを確認します。
ls -l database.db
コンテナ内でも確認します。
sudo docker compose exec human-resource-ai ls -l /app/database.db
必要に応じてファイルの所有者・権限を確認します。
30. セキュリティ上の注意
Human Resource AIは人事情報を扱うため、インターネットへ公開する場合は特に注意が必要です。
前半の「公開前にHuman Resource AIの単体セキュリティを確認する」で設定した内容に加え、最低限、次を確認します。
- Flask開発サーバーを本番利用しない
- Gunicornを利用する
- 5000番ポートをVPS外部へ直接公開しない
- HTTPSを利用する
-
.envをGitHubへ登録しない - Dify APIキーをソースコードへ直接記述しない
-
SECRET_KEYを推測困難な値にする - SQLiteを定期的にバックアップする
- 管理者アカウントへ強いパスワードを設定する
- 初期ユーザーのパスワードをソースコードへ直接記述しない
- 初期ユーザーのパスワードは
.envから取得する - セッションCookieへSecure / HttpOnly / SameSiteを設定する
- POST処理へCSRF対策を適用する
- ログイン試行回数を制限する
- 不要なVPSポートを公開しない
特にHuman Resource AIでは、
社員情報
研修情報
評価情報
日報
社内ナレッジ
などを扱うため、公開前に認証・権限設定を確認します。
31. バックアップ
最低限、Human Resource AIのSQLiteデータベースをバックアップ対象にします。
/opt/docker/human-resource-ai-product/database.db
手動バックアップの例です。
cd /opt/docker/human-resource-ai-product
cp database.db database.db.backup
日時を付ける場合は、
cp database.db "database.db.$(date +%Y%m%d_%H%M%S).backup"
とできます。
.envも重要ですが、秘密情報を含むため安全な場所へ保管します。
32. 完成時の構成
今回の構築が完了すると、Ubuntu VPS上ではHuman Resource AIが既存サービスとは独立したComposeプロジェクトとして稼働します。
Ubuntu VPS
│
└─ Docker環境
├─ Nginx Proxy Manager
│ Composeプロジェクト
├─ その他のサービス
│ Composeプロジェクト
└─ Human Resource AI
Composeプロジェクト
│
▼
Human Resource AI
Dockerコンテナ
│
Gunicorn
│
Flask
┌───┴───┐
▼ ▼
SQLite Dify API
Human Resource AIの通信経路は次のようになります。
利用者
↓
HTTPS
↓
Human Resource AI用ドメイン
↓
Nginx Proxy Manager
↓
proxyネットワーク
↓
human-resource-ai:5000
↓
Gunicorn
↓
Flask
├─ SQLite
└─ Dify API
既存サービスの内部構成は本記事では扱いません。重要なのは、
既存サービスは変更しない
+
Human Resource AIを独立追加
+
Nginx Proxy Managerのproxyネットワークを共有
という構成です。
動作確認
最後に、次の項目を確認します。
確認項目
| 操作 | 期待する動作 |
|---|---|
docker compose ps |
Human Resource AIが起動している |
| HTTPSアクセス | Human Resource AIのログイン画面が表示される |
| ログイン | admin / staff / userでログインできる |
| 社員管理 | 権限に応じて操作できる |
| 研修管理 | 登録・表示できる |
| 評価管理 | Dify連携を含めて動作する |
| 日報 | 登録・表示できる |
| RAG検索 | Dify Knowledgeを利用して回答できる |
| FAQ | 会話形式で回答できる |
| SQLite | コンテナ再起動後もデータが残る |
すべて確認できれば、既存サービスと共存したHuman Resource AIのUbuntu VPS公開は完了です。
おわりに
今回は、ローカル環境で開発してきたHuman Resource AIを、すでにDockerサービスが稼働しているUbuntu VPSへ追加しました。
Nginx Proxy Manager
↓
外部公開を集約
その他のサービス
↓
独立したComposeプロジェクト
Human Resource AI
↓
独立したComposeプロジェクト
Human Resource AIは、
Docker
↓
Gunicorn
↓
Flask
├─ SQLite
└─ Dify API
として独立させることで、同じUbuntu VPS上の他サービスを停止せずに追加・更新できます。
この構成により、Nginx Proxy Managerを共通の入口として利用しながら、複数のWebアプリケーションを同一Ubuntu VPS上で分離して運用できます。
本連載について
本連載では、Python・Flask・Dify・RAGを利用しながら、Human Resource AIを段階的に構築してきました。
今回の公開編では、完成したアプリケーションをローカル環境からUbuntu VPSへ展開し、既存Dockerサービスと共存させるところまで進めました。
| 回 | タイトル | 対応画面 | 主な内容 |
|---|---|---|---|
| 第1回 | 業務システム全体設計編 | ― | システム概要・技術選定・アーキテクチャ |
| 第2回 | 要求定義・要件定義編 | ― | 業務分析・機能要件・非機能要件 |
| 第3回 | ER図・画面設計編 | ― | テーブル設計・画面遷移図・ワイヤーフレーム |
| 第4回 | Flaskログイン機能編 | 🔐 ログイン | セッション・ハッシュ・ロール認可・社員番号ログイン |
| 第5回 | 社員管理CRUD編 | 👤 社員管理 | 一覧・検索・登録・更新・論理削除・入力制御 |
| 第6回 | 研修管理編 | 📚 研修管理 | 研修登録・LEFT JOIN・履歴一括入力・UPSERT |
| 第7回 | Excel業務自動化編 | 📥 取込 / 📤 出力 | pandas取込・openpyxl出力 |
| 第8回 | Dify API連携編 | 📊 評価管理 | API・フォールバック・生成中UI・二重送信防止 |
| 第9回 | RAG構築編 | 🔍 文書検索 | Dify Chat API・質問送信・回答表示・エラー処理 |
| 第10回 | FAQチャットボット編 | 💬 FAQチャット | 会話ID・Fetch API・生成中表示・二重送信防止 |
| 公開編 | Ubuntu VPS・Docker公開編 | 🌐 サービス公開 | Ubuntu VPS・Docker・Nginxを使った本番環境構築とWebアプリケーション公開 |
参考リンク
- Human Resource AI GitHubリポジトリ
- Docker Documentation
- Docker Compose
- Gunicorn
- Nginx Proxy Manager
- Flask
- Dify





