Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

本番デプロイガイド

このガイドでは、ローカル Docker Compose デモから eds serve(HTTP/TLS)および eds serve-mqtt の本番グレードデプロイへの移行について説明します。ローカルのクイックスタートはインタラクティブデモを、オブザーバビリティ・アラート・バックアップ/リストア手順については運用ランブックを参照してください。


前提条件

コンポーネント最小バージョン備考
edgesentry-rs バイナリ現行 mainHTTPS には --features transport-http,transport-tls でビルド;MQTT には transport-mqtt を追加
PostgreSQL14監査台帳および操作ログ
S3 互換ストアAWS S3、MinIO ≥ RELEASE.2023、または Cloudflare R2
(任意)MQTT ブローカーMosquitto ≥ 2.0eds serve-mqtt に必要な場合のみ

1 — TLS 証明書管理

1.1 Let’s Encrypt によるプロビジョニング(推奨)

# certbot をインストール
apt install certbot

# イングレストエンドポイント用の証明書を発行
certbot certonly --standalone \
  -d ingest.example.com \
  --agree-tos --non-interactive \
  -m ops@example.com

# 証明書の出力先:
#   /etc/letsencrypt/live/ingest.example.com/fullchain.pem  (証明書 + チェーン)
#   /etc/letsencrypt/live/ingest.example.com/privkey.pem    (秘密鍵)

1.2 TLS を有効にした eds serve-tls の起動

eds serve-tls \
  --addr 0.0.0.0:8443 \
  --tls-cert /etc/letsencrypt/live/ingest.example.com/fullchain.pem \
  --tls-key  /etc/letsencrypt/live/ingest.example.com/privkey.pem \
  --allowed-sources 10.0.0.0/8 \
  --device lift-01=<PUBLIC_KEY_HEX>

eds serve-tls は rustls 経由で TLS 1.2 最小・TLS 1.3 優先を適用します。追加設定は不要です。

1.3 証明書のローテーション(ゼロダウンタイム)

eds serve-tls は起動時にのみ証明書ファイルを読み込みます。ダウンタイムなしのローテーション手順:

# 1. 証明書を更新
certbot renew --quiet

# 2. 実行中プロセスに SIGTERM を送信(systemd が再起動を処理)
systemctl reload edgesentry
# — または systemd を使わない場合 —
kill -TERM $(pidof eds)
# プロセスが正常終了し、スーパーバイザー/systemd が再起動して新しい証明書を読み込む

cron/systemd タイマーを追加して更新を自動化:

# /etc/systemd/system/certbot.timer
[Timer]
OnCalendar=weekly
Persistent=true

[Install]
WantedBy=timers.target
systemctl enable --now certbot.timer

1.4 自己署名証明書(内部/エアギャップ環境)

# 10 年の自己署名証明書を生成
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 \
  -nodes -keyout server.key -out server.crt \
  -subj "/CN=ingest.internal" \
  -addext "subjectAltName=IP:10.0.1.5,DNS:ingest.internal"

server.crt を信頼 CA としてすべてのエッジデバイスに配布してください。


2 — PostgreSQL: スキーマ・インデックス・接続数設定

2.1 スキーママイグレーション

スキーマは db/init/001_schema.sql にあります。本番データベースに適用してください:

psql "$DATABASE_URL" -f db/init/001_schema.sql

スキーマは冪等(CREATE TABLE IF NOT EXISTS)であり、再実行しても安全です。

2.2 推奨インデックス

ベーススキーマには UNIQUE (device_id, sequence) 制約が含まれており、これが B-tree インデックスを兼ねてデータベースレベルでリプレイ攻撃を拒否します。一般的なクエリパターン向けに以下のインデックスを追加してください:

-- デバイスごとの最新レコード検索(チェーンヘッドクエリ)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_audit_device_seq
    ON audit_records (device_id, sequence DESC);

-- コンプライアンスレポート用の時間範囲クエリ
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_audit_ingested_at
    ON audit_records (ingested_at);

-- 決定種別によるオペレーションログフィルタリング
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_oplog_decision_device
    ON operation_logs (decision, device_id, created_at DESC);

CONCURRENTLY を指定することで、本番環境でテーブルをロックせずにインデックスを作成できます。

2.3 接続プールのサイジング

PostgresAuditLedgerPostgresOperationLog はそれぞれ postgres クレート経由で 1 つの同期接続を開きます。マルチノードデプロイ(§5 参照)では各 eds プロセスが 2 つの接続を保持します。postgresql.confmax_connections を以下のように設定してください:

max_connections = 2 × <eds インスタンス数> + 10   # psql・監視用のヘッドルーム

高速なイングレストレート(500 レコード/秒超)の場合、カスタム AsyncAuditLedger 実装として sqlx + PgPool を使用した非同期接続プールに置き換えてください。

2.4 長期保持のためのパーティショニング

テーブルが 1 億行を超えると予想される場合は、ingested_ataudit_records をパーティション分割してください:

-- 範囲パーティションテーブルに変換(データ蓄積前に一度実行)
CREATE TABLE audit_records_new (LIKE audit_records INCLUDING ALL)
    PARTITION BY RANGE (ingested_at);

CREATE TABLE audit_records_2026_q1
    PARTITION OF audit_records_new
    FOR VALUES FROM ('2026-01-01') TO ('2026-04-01');

-- アタッチ、スワップ、削除
ALTER TABLE audit_records RENAME TO audit_records_old;
ALTER TABLE audit_records_new RENAME TO audit_records;
DROP TABLE audit_records_old;

3 — オブジェクトストレージ: バケットポリシーとライフサイクルルール

3.1 AWS S3 — バケットポリシー(最小権限)

イングレストサービス専用の IAM ロールを書き込み専用アクセスで作成してください:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "IngestWriteOnly",
      "Effect": "Allow",
      "Action": ["s3:PutObject"],
      "Resource": "arn:aws:s3:::edgesentry-audit/*"
    },
    {
      "Sid": "ListBucket",
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::edgesentry-audit"
    }
  ]
}

コンプライアンス監査者には別の読み取り専用ロールを付与してください。

3.2 ライフサイクルルール(保持期間 + コスト管理)

{
  "Rules": [
    {
      "Id": "TransitionToIA",
      "Status": "Enabled",
      "Filter": { "Prefix": "" },
      "Transitions": [
        { "Days": 90,  "StorageClass": "STANDARD_IA" },
        { "Days": 365, "StorageClass": "GLACIER_IR" }
      ]
    },
    {
      "Id": "ExpireOldObjects",
      "Status": "Enabled",
      "Filter": { "Prefix": "" },
      "Expiration": { "Days": 2555 }
    }
  ]
}

CLI で適用:

aws s3api put-bucket-lifecycle-configuration \
  --bucket edgesentry-audit \
  --lifecycle-configuration file://lifecycle.json

3.3 MinIO(オンプレミス)

# オブジェクトロック付きでバケットを作成(コンプライアンス向け不変性)
mc mb --with-lock minio/edgesentry-audit

# ライフサイクル設定: 90 日後に低コストティアへ移行
mc ilm import minio/edgesentry-audit <<EOF
{
  "Rules": [{
    "ID": "expire-3-years",
    "Status": "Enabled",
    "Expiration": { "Days": 1095 }
  }]
}
EOF

# 保存時のサーバーサイド暗号化
mc encrypt set sse-s3 minio/edgesentry-audit

4 — プロセス管理

4.1 systemd サービスユニット(HTTP + TLS)

# /etc/systemd/system/edgesentry.service
[Unit]
Description=EdgeSentry-RS ingest server
After=network-online.target postgresql.service
Wants=network-online.target

[Service]
Type=exec
User=edgesentry
Group=edgesentry
ExecStart=/usr/local/bin/eds serve-tls \
    --addr 0.0.0.0:8443 \
    --tls-cert /etc/edgesentry/server.crt \
    --tls-key  /etc/edgesentry/server.key \
    --allowed-sources 10.0.0.0/8 \
    --device lift-01=<PUBLIC_KEY_HEX>
Restart=on-failure
RestartSec=5
Environment=RUST_LOG=edgesentry_rs=info

# セキュリティ強化
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/edgesentry
PrivateTmp=true
CapabilityBoundingSet=

[Install]
WantedBy=multi-user.target
# インストールと起動
install -m 755 target/release/eds /usr/local/bin/eds
useradd --system --no-create-home edgesentry
mkdir -p /var/log/edgesentry && chown edgesentry:edgesentry /var/log/edgesentry

systemctl daemon-reload
systemctl enable --now edgesentry
systemctl status edgesentry

4.2 systemd サービスユニット(MQTT)

# /etc/systemd/system/edgesentry-mqtt.service
[Unit]
Description=EdgeSentry-RS MQTT ingest subscriber
After=network-online.target mosquitto.service
Wants=network-online.target

[Service]
Type=exec
User=edgesentry
Group=edgesentry
ExecStart=/usr/local/bin/eds serve-mqtt \
    --broker 10.0.1.10 \
    --port 1883 \
    --topic edgesentry/ingest \
    --client-id eds-prod-1 \
    --device lift-01=<PUBLIC_KEY_HEX>
Restart=on-failure
RestartSec=10
Environment=RUST_LOG=edgesentry_rs=info

[Install]
WantedBy=multi-user.target

4.3 ヘルスチェック

eds serve 自体は /health エンドポイントを公開しません。ロードバランサーまたは監視エージェントで TCP チェックを設定してください:

# TLS ポートが接続を受け入れていることを確認
openssl s_client -connect ingest.example.com:8443 -verify_return_error </dev/null
echo $?   # 0 = 正常

Kubernetes では tcpSocket liveness probe を使用してください:

livenessProbe:
  tcpSocket:
    port: 8443
  initialDelaySeconds: 5
  periodSeconds: 15

5 — 水平スケーリング

5.1 アーキテクチャ

                      ┌─────────────────┐
Edge devices  ──TLS──►│  ロードバランサー │
                      │  (nginx /       │
                      │   AWS ALB など) │
                      └────────┬────────┘
                               │  ラウンドロビン
                ┌──────────────┼──────────────┐
                ▼              ▼              ▼
         ┌────────────┐ ┌────────────┐ ┌────────────┐
         │  eds serve │ │  eds serve │ │  eds serve │
         │  ノード 1  │ │  ノード 2  │ │  ノード 3  │
         └──────┬─────┘ └──────┬─────┘ └──────┬─────┘
                └──────────────┼──────────────┘
                               │
                ┌──────────────┼──────────────┐
                ▼              ▼              ▼
         ┌─────────┐    ┌──────────┐   ┌─────────┐
         │Postgres │    │  S3 /    │   │ MinIO   │
         │(プライマリ)│    │  バケット │   │ クラスター │
         └─────────┘    └──────────┘   └─────────┘

5.2 主要な特性

  • IngestState はプロセスごと。eds serve ノードは独自のインメモリシーケンス/ハッシュチェーン状態を保持します。PostgreSQL の UNIQUE (device_id, sequence) 制約がクロスノードのリプレイフェンスとなり、重複インサートは unique-violation エラーとなって PostgresAuditLedger がストアエラーとして通知し、イングレストが拒否・ログ記録されます。
  • スティッキーセッション不要。 シーケンス強制は DB レベルで行われ、どのノードもどのデバイスのリクエストも処理できます。
  • S3/MinIO への書き込みはステートレス。 すべてのノードが同一バケットに書き込みます。オブジェクトキーは object_ref から導出され、エッジデバイスが設定し、慣例として(例:<device_id>/<sequence>.bin)グローバルに一意です。

5.3 nginx TLS 終端 + アップストリームプロキシ

upstream edgesentry_nodes {
    least_conn;
    server 10.0.1.11:8080;
    server 10.0.1.12:8080;
    server 10.0.1.13:8080;
}

server {
    listen 443 ssl;
    server_name ingest.example.com;

    ssl_certificate     /etc/letsencrypt/live/ingest.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ingest.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location /api/v1/ingest {
        proxy_pass         http://edgesentry_nodes;
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_read_timeout 10s;
    }
}

各ノードで eds serve(プライベートポートでのプレーン HTTP)を実行し、nginx に TLS 終端を任せてください。--allowed-sources には nginx アップストリームの IP 範囲を指定します。リバースプロキシを使わずに組み込み TLS を利用する場合は eds serve-tls を使用してください。

注意: TLS がロードバランサーで終端される場合、eds serve はデバイスの IP ではなく LB の IP を認識します。--allowed-sources を LB の内部アドレス範囲に設定し、デバイスごとのソース制御は LB 側のアローリストに委ねてください。

5.4 レポート用 PostgreSQL リードレプリカ

書き込みパス(イングレスト):プライマリのみ。 読み取りパス(コンプライアンスクエリ、チェーン検証):リードレプリカに直接アクセス。

# コンプライアンスツール用のリードレプリカ接続
psql "postgres://audit_ro:pass@pg-replica:5432/audit?sslmode=require"

6 — オブザーバビリティ

構造化ログとトレーシングは tracing ファサードで処理されます。JSON ログフォーマット、ライブラリが出力する構造化イベントフィールド、Prometheus メトリクス導出、OpenTelemetry スパン設定を含む詳細なセットアップは運用ランブック — オブザーバビリティを参照してください。

クイックスタート: stdout への JSON ログ出力(Loki / CloudWatch 向け)

# バイナリラッパーの Cargo.toml
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
# JSON ログで eds を実行
RUST_LOG=edgesentry_rs=info eds serve ... 2>&1 | \
  promtail --stdin --client.url http://loki:3100/loki/api/v1/push

アラート対象の主要ログフィールド

フィールドアラート条件
message"MQTT record rejected" / "record rejected"5 分間の拒否率 > 1 %
reason"invalid signature"任意の発生 — 改ざんの試みの可能性
reason"unknown device"継続的な発生 — 未登録デバイスの探索
message"MQTT event loop error"任意の発生 — ブローカー接続が切断

Prometheus アラートルールについては運用ランブック — アラート定義を参照してください。


関連ドキュメント