本記事で紹介するプログラムは以下の GitHub リポジトリで公開しています
はじめに
前回の記事では、Node.js と React を使ってローカルネットワーク内の PTZ カメラを Web ブラウザから制御するシステムをご紹介しました。
今回はその続編として、このシステムを Google Cloud Platform (GCP) にデプロイし、インターネット経由でどこからでも安全に遠隔操作できるようにしたアーキテクチャと、その導入手順について詳しく解説します。
ローカル環境のシステムをクラウド化する際に直面する「どうやって安全に社内ネットワークのカメラと通信するか?」という課題を、Raspberry Pi を中継器とすることで解決しました。この記事が、同じような構成を検討している方の助けになれば幸いです。
システム構成:クラウドとローカルのハイブリッドアーキテクチャ
今回のシステムの全体像です。ユーザーが操作するフロントエンドと、司令塔となるバックエンドは GCP 上に配置し、カメラが設置されたローカルネットワークには Raspberry Pi を設置して、両者を安全に接続します。
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ Backend │ │ Raspberry Pi │
│ (Cloud Run) │◄───►│ (App Engine) │◄───►│ (Local) │
│ │ │ │ │ │
│ - React UI │ │ - PTZ API │ │ - Camera Stream │
│ - PTZ Controls │ │ - Stream Proxy │ │ - PTZ Control │
│ - Video Player │ │ - Auth & CORS │ │ - Heartbeat │
└─────────────────┘ └─────────────────┘ └─────────────────┘
- フロントエンド (Cloud Run): ユーザーが直接触れる UI 部分です。React で構築された Web アプリケーションをコンテナ化し、スケーラブルな Cloud Run でホスティングします。
- バックエンド (App Engine): PTZ 操作の API や、映像ストリームの中継、Raspberry Pi の管理などを行う頭脳部分です。デプロイが容易な App Engine を採用しました。
- Raspberry Pi (ローカル): PTZ カメラと同じネットワークに設置する小型コンピュータです。GCP バックエンドからの指示を受けてカメラを物理的に操作したり、カメラの映像 (RTSP) を FFmpeg で変換し、バックエンドへ送信する役割を担います。
この構成の最大のポイントは、Raspberry Pi から GCP へ WebSocket 接続を開始する点です。これにより、ローカルネットワーク側でポートフォワーディングなどの複雑な設定をすることなく、安全な通信経路を確立しています。
ディレクトリ構成
プロジェクト全体のディレクトリ構成は以下のようになっています。
cloud/
├── cloud-backend/ \# GCP バックエンド (App Engine)
│ ├── src/
│ ├── app.yaml \# App Engine 設定
│ └── deploy.sh \# デプロイスクリプト
│
├── cloud-frontend/ \# GCP フロントエンド (Cloud Run)
│ ├── src/
│ ├── Dockerfile
│ └── deploy.sh
│
└── raspberry/ \# Raspberry Pi アプリケーション
├── src/
├── setup.sh \# セットアップスクリプト
└── package.json
🚀 デプロイとセットアップの手順
それでは、実際にこのシステムを構築する手順を見ていきましょう。
前提条件
- Google Cloud Platform アカウント
- Google Cloud CLI (
gcloud
) がインストール済み - Node.js 18 以上
- Docker(フロントエンドデプロイ用)
Step 1: バックエンドのデプロイ (App Engine)
まず、API サーバーとなるバックエンドを GCP App Engine にデプロイします。
# cloud/cloud-backend/ ディレクトリに移動
cd cloud-backend
# 環境変数にあなたのGCPプロジェクトIDを設定
export GOOGLE_CLOUD_PROJECT=your-project-id
# デプロイスクリプトを実行
./deploy.sh
デプロイが完了したら、カメラの認証情報やシステムの認証キーなどを GCP Secret Manager に安全に保管します。これにより、コード内に機密情報をハードコードするのを防ぎます。
重要: 以下のコマンドの 'YOUR_..._VALUE'
部分を、ご自身の実際の値に置き換えて実行してください。
# カメラの接続情報を設定
echo 'YOUR_CAMERA_IP' | gcloud secrets versions add camera-ip --data-file=-
echo 'YOUR_CAMERA_USERNAME' | gcloud secrets versions add camera-username --data-file=-
echo 'YOUR_CAMERA_PASSWORD' | gcloud secrets versions add camera-password --data-file=-
# Raspberry Pi とバックエンド間の認証キーを設定
echo 'YOUR_STRONG_RANDOM_KEY' | gcloud secrets versions add raspberry-pi-auth-key --data-file=-
# ユーザー認証用のJWT秘密鍵を設定
echo 'YOUR_JWT_SECRET_KEY' | gcloud secrets versions add jwt-secret --data-file=-
Step 2: フロントエンドのデプロイ (Cloud Run)
次に、ユーザーインターフェースとなるフロントエンドを GCP Cloud Run にデプロイします。
# cloud/cloud-frontend/ ディレクトリに移動
cd cloud-frontend
# 環境変数にGCPプロジェクトIDと、先ほどデプロイしたバックエンドのURLを設定
export GOOGLE_CLOUD_PROJECT=your-project-id
export APP_URL=your-project-id.an.r.appspot.com # バックエンドのURL
# デプロイスクリプトを実行
./deploy.sh
Step 3: Raspberry Pi のセットアップ
最後に、ローカルネットワークに設置する Raspberry Pi を設定します。
# cloud/raspberry/ ディレクトリに移動
cd raspberry
# .env.exampleをコピーして環境変数ファイルを作成
cp .env.example .env
# セットアップスクリプトを実行(依存関係のインストールなど)
./setup.sh
# nanoなどのエディタで .env ファイルを編集
nano .env
.env
ファイルを開き、あなたの環境に合わせて以下の値を設定してください。
# バックエンドの完全なURL
GCP_BACKEND_URL=[https://your-project-id.an.r.appspot.com](https://your-project-id.an.r.appspot.com)
# このデバイスを識別するためのユニークなID
DEVICE_ID=raspberry-pi-001
# バックエンドのSecret Managerで設定した値と同じ認証キー
AUTH_KEY=YOUR_STRONG_RANDOM_KEY
# PTZカメラのIPアドレスと認証情報
CAMERA_IP=192.168.1.108
CAMERA_USERNAME=admin
CAMERA_PASSWORD=your_camera_password
設定が完了したら、Raspberry Pi 上のアプリケーションを起動すれば、システム全体の準備は完了です!
🔒 セキュリティに関する考慮事項(※重要※)
注意: 本システムの現在の実装は、あくまで技術的なコンセプトを実証するためのものです。このまま本番環境で利用するにはセキュリティが不十分です。
現状の実装では、フロントエンドの URL を知っていれば誰でもカメラの映像を閲覧し、操作できてしまいます。本格的に運用する際は、必ず追加のセキュリティ対策を実装してください。
ここでは、本構成で採用している基本的なセキュリティの仕組みと、本番運用へ向けた課題について解説します。
採用している基本的な仕組み
- 機密情報の隔離: カメラのパスワードや認証キーは、GCP Secret Manager で一元管理し、アプリケーションからは実行時にのみ参照します。これにより、コード内に機密情報をハードコードするのを防いでいます。
- デバイス認証: Raspberry Pi がバックエンドに接続する際は、共有の 認証キー を用いて、許可されたデバイスからの接続であることを確認します。
- 通信の暗号化: ユーザーから GCP、GCP から Raspberry Pi までのすべての通信は HTTPS および WSS (Secure WebSocket) により暗号化されています。
本番環境で利用するための課題
上記の対策は重要ですが、これだけでは不十分です。セキュアなシステムとして利用するためには、少なくとも以下の機能追加が必要です。
- フロントエンドの認証機能: ユーザー ID とパスワードによるログイン機能を実装し、許可されたユーザーのみがカメラを操作できるようにする必要があります。(例: Firebase Authentication, Auth0 等の認証サービスの導入)
- 認可(権限管理): ユーザーごとに「映像の閲覧のみ」「PTZ 操作も可能」といった権限を管理する仕組みを導入することが望ましいです。
- より強固なネットワーク設定: 必要に応じてファイアウォールルールでアクセス元 IP アドレスを制限するなど、ネットワークレベルでのセキュリティ強化も検討してください。
📊 監視とトラブルシューティング
クラウドでシステムを運用する上で、監視は不可欠です。
ログ管理: フロントエンド (Cloud Run) とバックエンド (App Engine) のログは、自動的に Google Cloud Logging に集約されます。問題が発生した際は、以下のコマンドでリアルタイムにログを確認できます。
# バックエンドのログ gcloud app logs tail -s default # フロントエンドのログ gcloud run logs tail --service=ptz-control-frontend --region=asia-northeast1
死活監視: Raspberry Pi は定期的にバックエンドへ ハートビート (Heartbeat) を送信します。これにより、バックエンドは Raspberry Pi が正常にオンラインであるかを常に把握できます。
もし接続エラーなどが起きた場合は、まず各サービスのログを確認し、Secret Manager や .env
ファイルの設定値が正しいかを見直してみてください。
まとめ
今回は、ローカルの PTZ カメラ制御システムを GCP と Raspberry Pi を使ってクラウド化し、どこからでも遠隔操作できる仕組みをご紹介しました。
物理的なデバイスとクラウドサービスを連携させることで、活用の幅は大きく広がります。この記事で紹介したアーキテクチャは、あくまでコンセプト実証の第一歩です。本番環境で利用するには、認証機能をはじめとする本格的なセキュリティ対策が不可欠ですが、IoT やリモートワークに関連するシステム開発の技術的なヒントになれば幸いです。
本記事で紹介するプログラムは以下の GitHub リポジトリで公開しています