diff --git a/content/docs/deploy/air-gapped.ja.mdx b/content/docs/deploy/air-gapped.ja.mdx new file mode 100644 index 00000000..5f30ad90 --- /dev/null +++ b/content/docs/deploy/air-gapped.ja.mdx @@ -0,0 +1,74 @@ +--- +title: エアギャップデプロイメント +seoTitle: "エアギャップデプロイメント:ライセンスモードと設定" +description: エアギャップはファイアウォールの設定ではなくライセンスモードです。切断されたデプロイメントが宣言しなければならない 2 つの設定と、起動時に拒否される組み合わせを説明します。 +editions: [enterprise] +translation: + source_sha: d0fbf234ac1fc9be7bcdb652e29a259e133103a63533267bf3f065bdee48ce9b + guide_rev: 1 + mode: auto +--- + +## エアギャップはライセンスモードである [#air-gap-is-a-license-mode] + +ネットワークを抜くだけでは、エアギャップのデプロイメントにはなりません。通常の ObjectOS ライセンスは**オンライン**で検証されます。ランタイムはコントロールプレーンでライセンスキーを短期のエンタイトルメントと交換し、定期的に再確認します。その経路を断つと、ライセンスは「オフラインにフォールバック」するのではなく、そもそも一切有効になりません。 + +**エアギャップライセンスは別のライセンス**であり、私たちが意図的にオフラインで発行します。イメージに埋め込まれた公開鍵に対してローカルで検証され、**ネットワークトラフィックなし**でエンタイトルメントを有効にします。エアギャップ運用は Enterprise の機能であり([ライセンスと料金](/docs/editions/license)を参照)、必要なライセンスは設定するものではなく申請するものです。 + +したがって、切断されたデプロイメントは次の **2 つ**を同時に宣言します。 + +- **エアギャップライセンス** +- **コントロールプレーンなし**:クラウドの姿勢の設定を明示的に `off` にします(別名の `none`、`local`、`disabled` も同じ意味です) + +## 設定を未設定のままにすることは「クラウドなし」ではない [#leaving-the-setting-unset-is-not-no-cloud] + +これはこのページで最も高くつく間違いなので、専用の見出しを設けています。 + +> **未設定のクラウドの姿勢の設定は、公開コントロールプレーンに解決されます。** + +未設定は中立だったことは一度もありません。接続された動作を選びます。そのため、「何もしなかった」デプロイメントは、誰とも通信しないことが前提のサイトであるにもかかわらず、マーケットプレイスのプロキシがマウントされ、デバイスバインディングのルートが公開された状態になります。姿勢を宣言することは受け入れられますが、**決めないことは受け入れられません。** `off` を明示的に書いてください。 + +## サポートされる組み合わせ [#the-supported-combinations] + +これらは設定の読み込み時、ライセンスに関わる処理が始まる**前に**確認されます。サポートされない組み合わせは警告でも劣化でもありません。ランタイムは*両方*の設定を名指しした致命的エラーを表示して終了します。 + +| ライセンス | クラウドの姿勢 | 結果 | +|---|---|---| +| エアギャップライセンス | `off` | **ネットワークトラフィックなしで、完全にライセンスされた状態で動作します。** 切断環境の答えです。 | +| ライセンスなし | `off` | Community の動作で実行されます。マルチ組織の姿勢には引き続きライセンスが必要です。 | +| 通常の(オンライン)ライセンス | 到達可能なコントロールプレーン、デバイスバインディング完了済み | 通常の接続されたデプロイメント。 | +| 通常の(オンライン)ライセンス | `off` | **起動時に拒否されます。** | +| 通常の(オンライン)ライセンス | 未設定 | 公開コントロールプレーンとして扱われます(上記を参照)。動作するかどうかは、そのコントロールプレーンに実際に到達でき、バインドされているかどうかによります。 | + +### 拒否が存在する理由 [#why-the-refusal-exists] + +オンラインライセンスは、デプロイメントをコントロールプレーンにバインドする際に発行されたランタイムトークンを提示する交換によって検証されます。コントロールプレーンがなければバインディングもなく、トークンは存在しえず、交換は決して行われません。今回の起動でも、以降の起動でもです。猶予期間も役に立ちません。猶予は最後に*成功した*検証から計測されますが、最初の成功が永遠に来ないからです。 + +強制しなければ、その設定は後になって失敗し、間違ったものを責めます。マルチ組織の姿勢では、デプロイメントが「ライセンスを必要とする」という苦情で終わります。ライセンスこそが唯一間違っていなかったものなのにです。ランタイムは代わりに早い段階で拒否し、どの 2 つの設定が食い違っているかを示します。拒否はあらゆる検証の試行より前に行われるため、拒否された起動は**パケットを一切送信しません**。 + +## ホスト型のマルチ組織形態もエアギャップライセンスである [#the-hosted-multi-organization-shape-is-air-gap-licensed-too] + +**composed** 形態(公開済みアプリをこのデプロイメントのアプリとして起動し、分離の壁の背後で複数の組織が 1 つのデータベースを共有する)を実行する場合、そこでもエアギャップライセンスは任意ではありません。このモードはマーケットプレイスとクラウド接続のサーフェスを完全に拒否するため、オンラインライセンスに必要なバインディングがこのモードで行われることはありません。エアギャップライセンスと `off` が必須で、どちらも起動時に強制され、デプロイバンドル内に専用の環境テンプレートがあります。単一環境のテンプレートを手直ししてこの形態にしないでください。composed のテンプレートをコピーすれば、それが決めるすべての値が一緒についてきます。 + +## イメージをネットワーク内に持ち込む [#getting-the-image-inside-the-network] + +ObjectOS のランタイムイメージは、切断されたサイトからは到達できないプライベートレジストリにあるため、イメージは他の成果物と同じ方法で境界を越えます。 + +検証は、レジストリとツールがまだ使える接続側で行います。内部へ持ち込もうとしている正確なダイジェストについて、**署名**、**SBOM**、**ビルドの来歴**を確認します(コマンドはバンドルの README に記載されています)。あとは**ダイジェスト**に任せます。ダイジェストはコンテンツアドレスなので、ネットワーク内のコピーが同じダイジェストであることを確認すれば、それが検証したのと同じバイトであることの証明になります。デプロイメントがタグではなくダイジェストを固定するのもこのためです。境界の内側には、タグを再解決する相手がありません。 + +アップグレードも同じ方法で行います。新しいダイジェストを外部で検証し、内部に転送し、固定するダイジェストを変更して再起動します。以前のダイジェストは残しておいてください。エアギャップのネットワーク内では、それがロールバック先となる唯一のコピーかもしれません。 + +## 到達可能でなければならないもの [#what-still-has-to-be-reachable] + +エアギャップとは、*ObjectOS 自体が*必要とするトラフィックがないということです。ライセンスの検証も、マーケットプレイスも、更新の確認もありません。デプロイメントにネットワークがないという意味ではありません。境界の内側では、引き続き次のものが必要です。 + +- 設定されたデータベース(クラスター構成の場合は Redis も) +- インバウンドのイングレスまたはロードバランサー +- **あなたが**通信するよう設定したもの:アイデンティティプロバイダー、SMTP、オブジェクトストレージ、Webhook の送信先、AI プロバイダーのエンドポイント。ユーザーが OIDC でサインインする場合、そのアイデンティティプロバイダーにこのネットワークから到達できる必要があります。できない場合はローカルアカウントを使います。 + +ライセンス自体には有効期限があり、起動のたびにローカルで確認されます。外部への問い合わせはないため、更新はランタイムが取得するものではなく、納品として計画してください。 + +## 次へ [#next] + +- [Docker → Enterprise で追加されるもの](/docs/deploy/docker#enterprise-adds):検証、ライセンス、スケーリング、アップグレードの全体。 +- [アップグレード](/docs/operate/upgrade)と[バックアップ](/docs/operate/backup):上記の手順の詳細な手順書。 diff --git a/content/docs/deploy/docker.ja.mdx b/content/docs/deploy/docker.ja.mdx new file mode 100644 index 00000000..7a3ef42c --- /dev/null +++ b/content/docs/deploy/docker.ja.mdx @@ -0,0 +1,299 @@ +--- +title: Docker +seoTitle: "Docker:ObjectOS Community と Enterprise を実行する" +description: Docker で ObjectOS を実行します。イメージを固定し、必須の 3 つの変数を設定し、Compose で PostgreSQL を追加し、プローブとプロキシを接続し、アップグレードとロールバックを行います。Enterprise イメージでは、プライベートレジストリ、検証、ライセンスが加わります。 +editions: [community, enterprise] +translation: + source_sha: 7fb88e85e29666d6c9ec80215a289bfa2bbe75dc1a35ab842ddd53f33bf4ea72 + guide_rev: 1 + mode: auto +--- + +Docker は、自社のサーバーで ObjectOS を実行する標準的な方法です。手順はセルフマネージドの両エディションで同じです。正確に固定したひとつのイメージ、実行時に注入する設定、コンテナの外にあるデータベース。このページでは、オープンな **Community** イメージ `ghcr.io/objectstack-ai/objectstack` を使って手順を説明します。ライセンスされたイメージで変わる点は、最後の [Enterprise で追加されるもの](#enterprise-adds)のセクションで扱います。 + +## イメージを固定する [#pin-the-image] + +Community イメージは ObjectStack のリリースごとに公開されます。タグは内部の ObjectStack CLI のバージョンを反映しています。リリース `X.Y.Z` は `X.Y.Z` としてプッシュされ、`latest`、`X.Y`、`X` がそこへ移動します。移動するタグは公開のたびに動くため、`latest` に対する日常的な `docker compose pull` によって、デプロイの操作なしに実行中のバージョンが置き換わることがあります。 + +正確なタグが固定するのはバージョンであって、バイトではありません。メンテナーは、たとえば `node:22-slim` ベースイメージのセキュリティパッチを取り込むために、すでにリリースされたバージョンを再ビルドすることがあり、その再ビルドは同じ `X.Y.Z` タグを新しいイメージに再びプッシュします。内部の ObjectStack CLI は同じバージョンのままですが、ベースイメージと、バージョン範囲でインストールされるデータベースドライバーは新たに解決されます。[公開ワークフロー](https://github.com/objectstack-ai/objectstack/blob/main/.github/workflows/docker-publish.yml)がその再ビルドを宣言しています。同じバイトを永続的に指すのは**ダイジェスト**だけです。 + +**本番では正確なバージョンを実行してください。** アプリをビルドした CLI と同じバージョンです。そして、すべてのホストで、すべての再起動後に同じバイトが必要な場所では、そのダイジェストを固定します。ダイジェストはレジストリから読み取ります。 + +```bash +docker buildx imagetools inspect ghcr.io/objectstack-ai/objectstack:X.Y.Z +``` + +`Name:` の下にある最初の `Digest:` の行を固定します。これはイメージ全体(両方のアーキテクチャ)を指します。`Manifests:` の下に並ぶダイジェストは、それぞれ 1 つのプラットフォームを指します。そのダイジェストでイメージを参照し、ダイジェストからはどのリリースかわからないので、バージョンを横に書いておきます。 + +```bash +# ObjectStack X.Y.Z +ghcr.io/objectstack-ai/objectstack@sha256:<64 hex characters> +``` + +以前のバージョンとそのダイジェストは書き残しておいてください。それがロールバックです。イメージは `linux/amd64` と `linux/arm64` 向けに公開され、非 root ユーザーで実行され、ポート **8080** で待ち受け、独自のヘルスチェックを持っています。PostgreSQL(`pg`)と MySQL(`mysql2`)のドライバーが含まれています。 + +## 設定する 3 つの値 [#the-three-values-you-set] + +それ以外はすべて、使えるデフォルトがあります。次の 3 つにはありません。 + +| 変数 | ない場合に起きること | +|---|---| +| `OS_DATABASE_URL` | データはランタイムのホームディレクトリの下の SQLite ファイルに書き込まれます。永続的な単一ホストでは問題ありませんが、コンテナのファイルシステムとともに失われます。本番では `postgres://…` を使います。 | +| `OS_AUTH_SECRET` | `/api/v1/auth/*` の下のサインインエンドポイントが**スキップ**され、サーバーは認証なしで実行されます。 | +| `OS_SECRET_KEY` | ランタイムは保存されたシークレット用に独自のキーを生成し、自身のファイルシステムに保持します。コンテナではそのキーはコンテナとともに消え、それで暗号化されたすべてのシークレットが読めなくなります。 | + +2 つのシークレットは一度だけ生成し、シークレットマネージャーに保管します。 + +```bash +OS_AUTH_SECRET=$(openssl rand -hex 32) +OS_SECRET_KEY=$(openssl rand -hex 32) +``` + +**どちらのシークレットもイメージに焼き込まないでください。** オーケストレーターのシークレットストアから実行時に渡します。`OS_SECRET_KEY` はデータベースと一緒にバックアップします。キーなしで復元したデータベースは、自身のシークレットを復号できません。[バックアップ](/docs/operate/backup)を参照してください。完全な一覧は[環境変数](/docs/reference/environment-variables)にあります。 + +## 実行する [#run-it] + +イメージにはアプリが含まれていません。`os build` がコンパイルする `objectstack.json` を、次の 3 つの方法のいずれかで渡します。 + +**アーティファクトをマウントする。** イメージのビルドは一切不要です。イメージは `/srv/app/objectstack.json` でアプリを探します。 + +```bash +docker run -p 8080:8080 \ + -v "$PWD/dist/objectstack.json:/srv/app/objectstack.json:ro" \ + -e OS_DATABASE_URL="postgres://objectos:secret@db-host:5432/objectos" \ + -e OS_AUTH_SECRET \ + -e OS_SECRET_KEY \ + ghcr.io/objectstack-ai/objectstack:X.Y.Z +``` + +**アーティファクトを URL で指定する。** `OS_ARTIFACT_URL` は公開済みのアーティファクトを指します。固定したイメージと 1 つの変数で動くアプリになり、アプリのアップグレードはその変数の変更と再起動で済みます。 + +```bash +docker run -p 8080:8080 \ + -e OS_ARTIFACT_URL="https://releases.example.com/crm-2.2.2.json#sha256=<64 hex characters>" \ + -e OS_DATABASE_URL -e OS_AUTH_SECRET -e OS_SECRET_KEY \ + ghcr.io/objectstack-ai/objectstack:X.Y.Z +``` + +任意の `#sha256=` フラグメントはアーティファクトのバイトを固定し、ランタイムは起動時にそれを守ります。 + +| 状況 | ランタイムの動作 | +|---|---| +| 固定なし | 検証なしで起動します。アーティファクトを取得できない場合、オーケストレーターが再試行するよう起動は**失敗**します。フォールバックできるキャッシュはありません。 | +| 固定あり、バイトが一致 | 起動し、検証済みのコピーを保持します。 | +| 固定あり、バイトが不一致 | 期待するダイジェストと実際のダイジェストを示して**起動を拒否**します。 | +| 固定あり、ホストに到達不能 | キャッシュされたコピーが固定と一致する場合に限りそこから起動し、警告でその旨を表示します。 | + +マウントしたファイルには `file:///…` も使えます。アーティファクトは不変で、バージョン名の付いたオブジェクトとして公開し、書き込みアクセスはリリースパイプラインだけに与えてください。そうすることで、アプリのロールバックがロールフォワードと同じ操作になります。 + +**アーティファクトを自分のイメージにコピーする。** リリースごとに自己完結したイメージを 1 つ作ります。プロジェクトの雛形(`npm create objectstack`)が、この形の Dockerfile を書き出してくれます。 + +```dockerfile title="Dockerfile" +FROM node:22-slim AS build +WORKDIR /app +COPY package.json pnpm-lock.yaml* pnpm-workspace.yaml* package-lock.json* ./ +RUN if [ -f pnpm-lock.yaml ]; then corepack pnpm@10 install --frozen-lockfile; \ + else npm ci; fi +COPY . . +RUN npx os build # → dist/objectstack.json + +FROM ghcr.io/objectstack-ai/objectstack:X.Y.Z +COPY --from=build --chown=node:node /app/dist/objectstack.json /srv/app/objectstack.json +``` + +## PostgreSQL と Compose で構成する [#compose-with-postgresql] + +単一ホストの本番スタックです。上記のイメージと、その横のデータベース。 + +```yaml title="docker-compose.yml" +services: + app: + build: . + ports: + - "8080:8080" + environment: + OS_DATABASE_URL: postgres://objectos:${POSTGRES_PASSWORD}@db:5432/objectos + OS_AUTH_SECRET: ${OS_AUTH_SECRET} + OS_SECRET_KEY: ${OS_SECRET_KEY} + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + db: + image: postgres:17 + environment: + POSTGRES_USER: objectos + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: objectos + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U objectos -d objectos"] + interval: 5s + timeout: 3s + retries: 10 + restart: unless-stopped + +volumes: + pgdata: +``` + +```bash +# .env beside docker-compose.yml — never committed +POSTGRES_PASSWORD=… +OS_AUTH_SECRET=… +OS_SECRET_KEY=… + +docker compose up -d +curl -fsS http://localhost:8080/api/v1/health +``` + +マネージド PostgreSQL に移行するときは、`OS_DATABASE_URL` をそこに向けて `db` サービスを削除します。スキーマはランタイムの起動時に最新の状態にされます。安全な変更は自動的に実行され、破壊的な変更は、`os migrate apply --allow-destructive` で意図的に適用するまで、各変更を示して**起動を拒否**します([CLI](/docs/reference/cli#database-migrations--os-migrate))。 + +## ヘルスチェック [#health-checks] + +エンドポイントは 2 つあり、互換性はありません。 + +| エンドポイント | 答える内容 | 接続先 | +|---|---|---| +| `/api/v1/health` | プロセスが起動し、HTTP を提供している。データベースには一切触れません。 | Liveness | +| `/api/v1/ready` | ランタイムが起動し、**データドライバーが応答している**。応答しないドライバーがある間は、そのドライバーを示して `503` を返します。 | Readiness、およびロードバランサー | + +取り違えると、ひどい失敗になります。`/api/v1/ready` に対する liveness プローブは、データベースの障害時にすべてのレプリカを再起動させ(再起動では直りません)、設定の不具合を、それを隠す再起動ループに変えてしまいます。反応すべきなのは readiness です。データベースが不在の間はレプリカをローテーションから外し、データベースが戻れば再起動なしで元に戻します。アプリがバインドするデータソースも起動時に接続できなければならず、そうでなければ起動はその場で失敗します。到達できないデータベースはデプロイの失敗であり、起動してからすべてのリクエストでエラーを返すコンテナではありません。 + +## リバースプロキシと TLS [#reverse-proxy-and-tls] + +ランタイムの前段で TLS を終端し(Caddy、nginx、Traefik、またはクラウドのロードバランサー)、3 つの値を公開アドレスと揃えておきます。 + +```bash +OS_AUTH_URL=https://app.example.com # sign-in callbacks and cookie origin +OS_TRUSTED_ORIGINS=https://app.example.com # sign-in callback and CSRF allow-list +OS_PORT=8080 # the proxy's upstream port +``` + +```text title="Caddyfile" +app.example.com { + reverse_proxy localhost:8080 +} +``` + +ポートやオリジンのずれは、セルフホスティングの典型的な失敗です。アプリは動くのに、サインインは跳ね返され、ブラウザは API 呼び出しをブロックします。Console と API が同じ公開オリジンを共有している間は、CORS の設定は不要です。**別の**オリジンから提供されるフロントエンドは、`OS_CORS_ORIGIN` に明示的に列挙します。認証情報が有効な間、`*` は起動時に拒否されます。HSTS は TLS を確認してから有効にしてください。残りは[本番環境への準備](/docs/operate/production)にあります。 + +## 初回起動:最初の管理者 [#first-boot-the-first-administrator] + +新しいデータベースでは、デプロイメントのアドレスを開いて**登録します。最初のアカウントが管理者になります。** 最初のデプロイの直後、アドレスを共有する前に行ってください。本番サーバーはデフォルトの認証情報を投入しません。`os dev` で見覚えのある `admin@objectos.ai` アカウントは、開発用データベースにしか存在しません。 + +代わりに設定で管理者を指定するには、`OS_PLATFORM_OWNER_EMAIL` に 1 つのアドレスまたはカンマ区切りのリストを設定します。列挙された各アカウントは、メールアドレスが確認されるとその地位を持ちます。マルチ組織の姿勢ではこれが必須です。[環境変数](/docs/reference/environment-variables#multi-organization-deployments)を参照してください。 + +サインインアカウントなしで人のレコードを投入するアプリは、この入り口を閉ざします。最初のアカウントのルールは、ログインではなく人を数えるからです。起動ログにその旨が表示されます。[トラブルシューティング](/docs/operate/troubleshooting#nobody-can-sign-in)を参照してください。 + +## マルチノード [#multi-node] + +ObjectOS Community は**レプリカ 1 つ**で実行します。ロック、キュー、スケジュールといった調整はプロセス内にあるため、2 つ目のレプリカはそれを共有しません。ライセンスなしで複数のレプリカを宣言すると(`OS_CLUSTER_REPLICAS` が 1 より大きい)、プロセスごとのロックで実行するのではなく、**起動を拒否**します。クラスタリングと高可用性は Enterprise の機能です。後述の[マルチノード](#multi-node-on-enterprise)を参照してください。 + +## アップグレードとロールバック [#upgrade-and-rollback] + +1. 通過するすべての ObjectStack バージョンのリリースノートと、それらがオペレーターに求める[バージョン固有の手順](/docs/operate/upgrade#version-specific-steps)を読みます。 +2. **データベースをバックアップします。** スキーマのマイグレーションは前にしか進みません。イメージをロールバックしても、スキーマはロールバックされません。 +3. イメージを新しい正確なバージョン(ダイジェストを固定している場合はそのダイジェスト)に変更し、再起動します。スキーマは、ランタイムが準備完了を報告する前の起動時に最新の状態にされます。 +4. `/api/v1/ready` が 200 を返すことを確認します。 +5. ロールバックするには、以前のタグまたはダイジェストに戻して手順 3 を繰り返します。離れるバージョンが互換性のないスキーマ変更を行っていない限り有効です。 + +アプリは別にバージョン管理されます。`OS_ARTIFACT_URL` やマウントするアーティファクトを変更すれば、イメージに触れずにアプリをアップグレードできます。 + +## Enterprise で追加されるもの [#enterprise-adds] + +ライセンスされた Enterprise イメージも同じ方法で実行し、プライベートレジストリ、サプライチェーンの検証、ライセンス、マルチノードが加わります。固定すべきダイジェストはリリースノートに記載されています。リリースには**デプロイバンドル**が付属し、Compose スタック、デプロイメント形態ごとの環境テンプレート、そして正確なバージョンのコマンドを記載した README が含まれます。このセクションは、そのバンドルが設定するものの判断マップです。 + +### 提供されるもの [#what-you-are-given] + +ライセンスには、プライベートなランタイムイメージ用の**レジストリの認証情報**が付属します。サインインにはパッケージの **read** アクセスを持つトークンが必要です。公開の Enterprise イメージやソースからのビルドはありません。イメージは、配布されないソースから私たちのリリースパイプラインが生成するため、`docker build` という手段は存在しません。 + +**サポートされるアーキテクチャ:`linux/amd64` のみ。** これは記載漏れではなく宣言です。ビルドは、宣言していないアーキテクチャのイメージの生成を拒否します。Apple Silicon や Graviton のホストではエミュレーションで実行されることになり、試すには問題ありませんが、本番には不適切です。 + +### ダイジェストでイメージを固定する [#pin-the-image-by-digest] + +本番で実行するイメージの参照は、タグではなく**ダイジェスト**です。 + +``` +OS_EE_IMAGE=@sha256:<64 hex characters> +``` + +タグは人が読める名前であり、別のバイトに付け替えられることがあります。ダイジェストはコンテンツアドレスです。同じダイジェストは、どのホストでも永遠に同じイメージです。タグでは、問題が起きたときに実際に何が動いていたかを後から証明する方法がありません。ダイジェストはリリースノートに記載されているので、そのまま貼り付けてください。**以前の**ダイジェストも残しておきます。 + +### イメージは自分が何であるかを示せる [#the-image-can-tell-you-what-it-is] + +イメージには 2 つの異なるバージョン番号が付いており、混同してはいけません。ObjectOS の**製品バージョン**と、それに組み込まれた **ObjectStack ランタイムの互換性範囲**で、後者は独自のスケジュールで動きます。どちらも、イメージのビルド元となったソースのリビジョンとともに、OCI ラベルおよび機械可読なリリースマニフェストとしてイメージ自体の中に記録されています。どのビルドを持っているかを私たちに尋ねる必要はありません。 + +このマニフェストは、サポートリクエストに添付するものとして最も役立つものでもあります。それだけで正確なイメージを再現できます。 + +### 本番の前にイメージを検証する [#verify-the-image-before-production] + +イメージは(リリースパイプラインの OIDC アイデンティティによるキーレス方式で)**署名**されており、**SBOM** と**ビルドの来歴**が付属します。本番へのロールアウトの前に一度検証してください。3 つのチェックは、それぞれ異なる問いに答えます。 + +| チェック | 答える問い | +|---|---| +| 署名 | このダイジェストは、他の誰かではなく ObjectOS のリリースパイプラインによって生成されたか | +| SBOM | このイメージの中に何が入っているか | +| 来歴 | このイメージはどのソースから、どのようにビルドされたか | + +バンドルの README には、照合すべきアイデンティティと発行者を含む検証コマンドが、リリースに合わせて記載されています。 + +### ライセンス [#license] + +**ライセンスは通常の手順の一部です。** ライセンスなしの Enterprise イメージの動作は、何を求めたかによって異なります。 + +- **単一組織、ライセンスなし**:デプロイメントは ObjectOS Community として実行されます。オープンな MCP サーバーと、自前の AI の持ち込み。何も動かなくなることはありません。 +- **単一組織、ライセンスあり**:自社のインフラストラクチャ上で製品内 AI が有効になります。AI Builder と「データに質問する」です。 +- **マルチ組織(壁で区切られたテナンシーの姿勢)、ライセンスなし**:ランタイムはライセンスを名指しして**起動時に終了**します。組織間の分離はライセンスされた機能であり、分離を求めたデプロイメントが、分離があるふりをしてトラフィックを提供することがあってはなりません。 + +同じ流れに 2 つ目の制約があり、こちらが人を驚かせるものです。**ライセンスにはモードがあり、そのモードはクラウドの姿勢と結びついています。** 通常のライセンスはコントロールプレーンに対してオンラインで検証され、エアギャップライセンスはローカルで検証されてネットワークを一切必要としません。通常のライセンスと「コントロールプレーンなし」の組み合わせは、劣化した状態ではなく不可能な状態です。後でライセンスについての誤解を招く苦情で失敗するのではなく、両方の設定を名指しして**起動時に拒否されます**。サポートされる組み合わせの完全な表は[エアギャップ](/docs/deploy/air-gapped)にあります。 + +### 壁で区切られたデプロイメントはさらに 2 つの判断を宣言しなければならない [#a-walled-deployment-must-declare-two-more-decisions] + +マルチ組織の姿勢では、2 つの設定に安全なデフォルトがないため、ランタイムは**決めるまで起動を拒否します**。どちらの値を宣言しても受け入れられますが、決めないことは受け入れられません。 + +| 判断 | デフォルトにできない理由 | +|---|---| +| **新しいサインアップが何に参加するか** | メンバーシップそのものがテナントの境界である場所では、メンバーシップは明示的な行為(招待、管理者、SSO プロビジョニング)によってのみ付与されるべきです。自動参加は、既存の組織の中に見知らぬ人を入れてしまいます。 | +| **どの AI エージェントをマウントするか** | 「ask」エージェントは読み取ります。「build」エージェントは**メタデータを作成**します。そしてメタデータの範囲は組織ではなくデプロイメントです。共有データベースでは、ある顧客の build のターンが、他のすべての顧客が使うスキーマを書き換えます。 | + +単一組織のデプロイメントでは、どちらのチェックも行われません。 + +### 提供されるスタック [#the-shipped-stack] + +バンドルの Compose スタックは、デプロイメントをあるべき順序で進めます。他のどのオーケストレーターでもこの順序を再現する必要があるため、知っておく価値があります。 + +1. データベースが正常な状態になります。 +2. **1 回限りのマイグレーション**がスキーマを完了まで実行します。アプリケーションのレプリカが起動する前なので、同時に動くレプリカが互いにスキーマ変更を実行し合うことはありません。 +3. アプリケーションのレプリカが起動します。イメージは独自のヘルスチェックを持っています。 +4. リバースプロキシは、レプリカが単に起動しただけでなく実際に応答するようになってから転送を始めます。 + +本番では、デプロイメントを**マネージド**の PostgreSQL(クラスター構成の場合は Redis も)に向け、バンドルのデータベースサービスを削除します。自動 HTTPS を得るには、プロキシにドメインを指定します。ランタイムのコンテナは非 root ユーザーで実行され、ランタイムの状態を宣言されたボリュームに保持します。 + +### Enterprise でのマルチノード [#multi-node-on-enterprise] + +レプリカを 2 つ以上にスケールすると、必須となるものが変わります。 + +- **クラスタードライバー**(Redis ベース)を有効にします。 +- **共有シークレットキーが必須になります。** クラスタードライバーがない場合、ランタイムは独自のキーを生成して永続化します。レプリカ間ではそれらのキーが黙って食い違うことになるため、食い違う代わりに**ランタイムは起動を拒否します**。キーを 1 つ生成し、すべてのレプリカに同じ値を渡してください。 +- **認証シークレットもすべてのレプリカで同一でなければなりません**。変更すると既存のすべてのセッションが無効になり、署名キーが宙に浮きます。ローテーションに伴う 1 回限りのクリーンアップは、環境テンプレートに記載されています。 +- `OS_CLUSTER_REPLICAS` は、バンドルのスタックが起動するアプリケーションのレプリカ数を設定します。 + +### ダイジェストでアップグレードする [#upgrade-by-digest] + +上記のアップグレード手順は、タグの代わりにダイジェストを使って同じように適用されます。 + +1. リリースノートから**新しいダイジェスト**を取得し、ロールアウトの前にイメージのリリースマニフェストを読みます。製品のメジャーラインが現在のものと同じであることを確認し、組み込まれたランタイムの互換性範囲が動いたかどうかを確認します。ランタイムのメジャーバージョンの移動は下層に破壊的変更があることを意味するため、先にそのリリースのノートを読んでください。 +2. データベースをバックアップします。 +3. イメージのダイジェストを変更し、スタックを再起動します。マイグレーションは、新しいレプリカがトラフィックを受ける前に完了まで実行されます。 +4. readiness が 200 を返し、レプリカが正常と報告することを確認します。 +5. ロールバックするには、以前のダイジェストに戻して手順 3 を繰り返します。離れるバージョンが互換性のないスキーマ変更を行っていない限り有効です(行った場合はリリースノートに記載されています)。 + +## 次へ [#next] + +- [Kubernetes](/docs/deploy/kubernetes):オーケストレーター上の同じイメージ。 +- [本番環境への準備](/docs/operate/production):本番稼働前のチェックリスト。 +- [オブザーバビリティ](/docs/operate/observability):ログ、メトリクス、監査。 +- [エアギャップ](/docs/deploy/air-gapped):切断されたサイト向けの Enterprise ライセンスモード。 diff --git a/content/docs/deploy/index.ja.mdx b/content/docs/deploy/index.ja.mdx new file mode 100644 index 00000000..aad5c9d8 --- /dev/null +++ b/content/docs/deploy/index.ja.mdx @@ -0,0 +1,83 @@ +--- +title: デプロイ +seoTitle: "デプロイ:セルフマネージドの ObjectOS、Community と Enterprise" +description: 自社のインフラストラクチャで ObjectOS を実行します。オープンな Community イメージとライセンスされた Enterprise イメージ、ひとつのランタイム、ひとつの設定の契約、そしてライセンスが追加するものを説明します。 +editions: [community, enterprise] +translation: + source_sha: 8e3a6c8cf20d9cbca8dbf4e4162996b6398eecf09e17b90912fd9914ec86e3f6 + guide_rev: 1 + mode: auto +--- + +ObjectOS にはセルフマネージドのエディションが 2 つあり、デプロイ方法は同じです。**ObjectOS Community** はオープンソースのランタイムイメージで、**ObjectOS Enterprise** はライセンスされたイメージです。後者は、シングルノードの Business Self-Managed としても、完全な Enterprise デプロイメントとしても実行できます。どちらも同じランタイムを実行し、同じ設定(データベース、認証シークレット、シークレットキー)を読み込みます。違いは、イメージの入手元、アプリの組み込み方、ライセンスで有効になるものです。このセクションの各ページは両方を対象に一度だけ書かれており、ライセンスされたイメージがより多くを行う箇所では、最後に **Enterprise で追加されるもの** のセクションがあります。 + +## 2 つのイメージ [#the-two-images] + +| | **Community** | **Enterprise** | +|---|---|---| +| イメージ | `ghcr.io/objectstack-ai/objectstack`、公開 | ライセンスされた Enterprise イメージ。プライベートレジストリから取得し、レジストリの認証情報はライセンスとともに提供されます | +| 固定する方法 | 内部の ObjectStack CLI と一致する正確なバージョンタグ(`X.Y.Z`)、そしてバイトが変わってはならない場所ではその**ダイジェスト**([理由](/docs/deploy/docker#pin-the-image)) | リリースノートに記載されたイメージの**ダイジェスト** | +| アーキテクチャ | `linux/amd64` と `linux/arm64` | `linux/amd64` のみ | +| アプリの組み込み方 | `os build` でコンパイルした `objectstack.json` を、マウントする、自分のイメージにコピーする、または `OS_ARTIFACT_URL` で指定する。空のランタイムでは、代わりに Console のマーケットプレイスからアプリをインストールします。 | 3 つの実行形態のいずれか:イメージにバンドルされたアプリ、公開済みアーティファクト、またはイメージの Enterprise プラグインと組み合わせた公開済みアーティファクト([後述](#how-the-app-gets-in)) | +| 設定が必須の変数 | `OS_DATABASE_URL`、`OS_AUTH_SECRET`、`OS_SECRET_KEY`(イメージはすでに `OS_PORT=8080` を設定しています) | 同じもの、加えて `OS_LICENSE_KEY` と意図して設定した `OS_CLOUD_URL` | +| コピーできる設定 | これらのページの例 | 各リリースとともに提供される**デプロイバンドル** | +| ライセンスで有効になるもの | — | 製品内 AI(AI Builder、「データに質問する」)、AI ガバナンス、クラスタリングと高可用性、1 つのデプロイメント上の複数の組織、SCIM、エアギャップ運用 | + +[エディションの比較](/docs/editions/compare)に完全な一覧があります。Community で構築したアプリは通常の ObjectStack メタデータであり、Enterprise でもそのまま動作します。 + +## すべてのデプロイメントが決める 3 つのこと [#every-deployment-decides-three-things] + +| 判断 | 意味 | +|---|---| +| **どのバイトを実行するか** | 正確なイメージをひとつに固定します。`latest` のような移動するタグは知らないうちに付け替えられることがあり、正確なバージョンタグもそのバージョンが再ビルドされれば同様です。それが起きないのはダイジェストだけです。以前のものは残しておきます。それがロールバックです。 | +| **設定をどこに置くか** | イメージとアプリの外です。データベース URL、シークレット、デプロイメントの識別情報は、実行時に注入される環境変数であり、決して焼き込みません。契約は[環境変数](/docs/reference/environment-variables)にあります。 | +| **どのデータベースか** | 本番は PostgreSQL で実行します。`OS_DATABASE_URL` がない場合、ランタイムはホームディレクトリの下に SQLite ファイルを書き込みます。これは永続的な単一ホストには適していますが、コンテナの一時的なファイルシステムではデータが失われます。 | + +## アプリの組み込み方 [#how-the-app-gets-in] + +Community イメージには**アプリが含まれていません**。`os build` がコンパイルするアーティファクト `dist/objectstack.json` を、次の 3 つの方法のいずれかで渡します。 + +| 方法 | やり方 | 使う場面 | +|---|---|---| +| **マウントする** | イメージがデフォルトで探す `/srv/app/objectstack.json` にファイルをマウントします | イメージのビルドを一切したくない | +| **自分のイメージにコピーする** | 公式イメージを `FROM` とする 2 段階の Dockerfile | リリースごとに自己完結したイメージを 1 つ出荷する | +| **URL で指定する** | `OS_ARTIFACT_URL`、任意で `#sha256=` による完全性の固定 | アプリのアップグレードを環境の変更と再起動で済ませたい | + +[Docker](/docs/deploy/docker#run-it) に 3 つすべてを示しています。アーティファクトがまったくない場合、ランタイムは空のカーネルで起動し、アプリは Console のマーケットプレイスからインストールします。 + +## ガイド [#guides] + +- [Docker](/docs/deploy/docker):イメージを実行し、設定し、プロキシの背後に置き、アップグレードします。 +- [Kubernetes](/docs/deploy/kubernetes):オーケストレーター上の同じイメージと、維持すべき性質。 +- [エアギャップ](/docs/deploy/air-gapped):Enterprise のみ。**ライセンスモード**としてのエアギャップと、起動時に拒否される組み合わせ。 + +コンテナランタイムがない場合は、Node.js 22 のホスト上で、同じアーティファクトを `os start` で実行できます。[ObjectOS Community をローカルで実行する](/docs/start/community)を参照してください。 + +## Enterprise で追加されるもの [#enterprise-adds] + +### さらに 2 つの判断:ライセンスとクラウドの姿勢 [#two-more-decisions-license-and-cloud-posture] + +| 判断 | 意味 | +|---|---| +| **どのライセンスか** | ライセンスは追加オプションではなく、通常の手順の一部です。デプロイメントの形態によっては、ライセンスがないと*起動を拒否*します。 | +| **どのクラウドの姿勢か** | このランタイムがコントロールプレーンと通信するかどうか。これは**ライセンスモードと結びついて**おり、サポートされない組み合わせは、黙って劣化するのではなく起動時に拒否されます。 | + +実際には、この 2 つはひとつの判断です。間違えるとゆっくりとした漏れではなく、起動の失敗になります。サポートされる組み合わせの全体は[エアギャップ](/docs/deploy/air-gapped)に記載されています。組み合わせが*なぜ*そうなっているかを説明しているページなので、サイトがネットワークに接続されている場合でも読んでください。 + +### デプロイバンドル [#the-deploy-bundle] + +リリースには**デプロイバンドル**が付属します。Compose スタック、サポートされる各形態の環境テンプレート、そしてそのバージョンの正確なコマンド(レジストリの参照、ダイジェスト、サプライチェーン検証の手順を含む)を記載した README です。 + +これらのページが扱うのは**判断**です。なぜダイジェストなのか、どの形態にどのライセンスモードか、マルチノードで何が必須になるか、ランタイムが何を拒否するか。バンドルが扱うのは**コピーする値**です。両者が食い違うように見えるときは、イメージとともに提供されたバンドルが正しいものです。バンドルは、それが設定するランタイムとともにバージョン管理されています。 + +### 3 つの実行形態 [#three-run-shapes] + +Enterprise イメージは 3 つの方法でアプリを実行でき、その選択によって利用できるものが変わります。単なるパッケージングの問題ではありません。 + +| 形態 | アプリの出どころ | 使う場面 | +|---|---|---| +| **Config-authored** | イメージ自身にバンドルされたアプリケーション。イメージ内で作成されたメタデータと、そこにインストールされたもの | 単一組織のデプロイメント。Compose スタックが設定するデフォルトで、設定が不要な形態です。 | +| **Artifact-pinned** | URL で指定された公開済みのアプリアーティファクト。デプロイメントの設定が実行される前に解決されます | 公開済みのアプリを 1 つ実行し、イメージを再ビルドせずに、環境の変更と再起動でアプリをアップグレードしたい。この経路では、デプロイメントの Enterprise プラグインは読み込まれ**ない**ことに注意してください。 | +| **Composed** | このデプロイメントの Enterprise プラグイン*と*組み合わせた公開済みのアプリアーティファクト | 複数の組織が 1 つのデータベースを共有する。これはホスト型の単一アプリ SaaS の形態であり、**エアギャップライセンスのみ**で、起動時に強制されます。 | + +各形態には、デプロイバンドル内に専用の環境テンプレートがあります。別のものを手直しするのではなく、自分の形態のものをコピーしてください。テンプレートは姿勢を決める値が異なり、それらはまさにランタイムが何かを提供する前に確認する値です。 diff --git a/content/docs/deploy/kubernetes.ja.mdx b/content/docs/deploy/kubernetes.ja.mdx new file mode 100644 index 00000000..8bdfbf5c --- /dev/null +++ b/content/docs/deploy/kubernetes.ja.mdx @@ -0,0 +1,112 @@ +--- +title: Kubernetes +seoTitle: "Kubernetes:プローブ、シークレット、固定、マイグレーション" +description: Kubernetes で ObjectOS イメージを実行します。参照用の Deployment、正しいエンドポイントへの正しいプローブ、Secret からのシークレット、そしてマルチレプリカの Enterprise デプロイメントが維持すべきものを説明します。 +editions: [community, enterprise] +translation: + source_sha: 2270aa161805c5ab5a80c2c48562ac8ab6c54573019e3c7d17acc51587a2d6c0 + guide_rev: 1 + mode: auto +--- + +**Helm チャートや公式のマニフェスト一式はありません。** イメージは Kubernetes でもそのまま動作するため、このページでは最小限の参照例と、マニフェストが維持しなければならない性質を示します。それぞれ、ランタイムが依存するか強制するものです。[Docker](/docs/deploy/docker) の内容はすべて当てはまります。同じイメージ、同じ変数、同じアップグレードのルールです。 + +## 参照用の Deployment [#a-reference-deployment] + +Community イメージのレプリカ 1 つ、`Secret` からのシークレット、それぞれ専用のエンドポイントに向けたプローブ: + +```yaml title="deployment.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: objectos +spec: + replicas: 1 + selector: + matchLabels: { app: objectos } + template: + metadata: + labels: { app: objectos } + spec: + containers: + - name: objectos + image: registry.example.com/crm:2.2.2 # your image FROM ghcr.io/objectstack-ai/objectstack:X.Y.Z + ports: + - containerPort: 8080 + envFrom: + - secretRef: + name: objectos-secrets # OS_DATABASE_URL, OS_AUTH_SECRET, OS_SECRET_KEY + livenessProbe: + httpGet: { path: /api/v1/health, port: 8080 } + periodSeconds: 30 + readinessProbe: + httpGet: { path: /api/v1/ready, port: 8080 } + initialDelaySeconds: 5 + periodSeconds: 10 +--- +apiVersion: v1 +kind: Service +metadata: + name: objectos +spec: + selector: { app: objectos } + ports: + - port: 80 + targetPort: 8080 +``` + +## 維持すべき性質 [#properties-to-preserve] + +### イメージが正確に固定されている [#the-image-is-pinned-exactly] + +イメージは `@sha256:` ダイジェストで参照し、読み取り元の正確なバージョンを横に書いておきます。移動するタグでは決して参照しないでください。オーケストレーターでは、単一ホストよりもこれが重要です。プルポリシーと移動するタグの組み合わせでは、「同じ」Deployment の 2 つの Pod が異なるビルドを実行することがあります。移動するタグはリリースのたびに動き、正確なバージョンタグもそのバージョンが再ビルドされれば動きます。[Docker → イメージを固定する](/docs/deploy/docker#pin-the-image)を参照してください。 + +### プローブが正しいエンドポイントに向いている [#probes-address-the-right-endpoints] + +`/api/v1/health` は liveness です。`/api/v1/ready` は readiness です。readiness は、ランタイムの起動中、シャットダウン中、そしてデータドライバーが応答しなくなったときに **503** を返します。そのため、ローリング再起動はきれいにドレインされ、データベースを失った Pod はエラーを返す代わりに Service のエンドポイントから外れます。 + +readiness を liveness プローブに使わないでください。そうしないと、データベースの障害ですべての Pod が一斉に再起動し(それではデータベースは戻りません)、設定の不具合はその不具合自体ではなく `CrashLoopBackOff` として現れます。データベースが戻れば Pod は自動的に回復します。readiness プローブが、不在の間は Pod をドレインし、その後に再び受け入れます。 + +### シークレットは Secret から [#secrets-come-from-a-secret] + +`OS_AUTH_SECRET` と `OS_SECRET_KEY` は `Secret` に入れ、一度だけ生成してすべての Pod にマウントします。Pod ごとに生成したり、イメージに焼き込んだりは決してしないでください。独自のシークレットキーを生成した Pod は、次の再スケジュールでそのキーを失い、それで暗号化されたすべてのシークレットも一緒に失います。 + +### 業務データはクラスターの外に置く [#business-data-lives-outside-the-cluster] + +デプロイメントはマネージド PostgreSQL に向けます。クラスター内の永続ボリュームは、業務データの置き場所として想定されていません。 + +### Community ではレプリカ 1 つ [#one-replica-on-community] + +Community の調整はプロセス内で行われるため、`replicas: 1` のままにします。それ以上を宣言すると(`OS_CLUSTER_REPLICAS` が 1 より大きい)、ライセンスなしでは起動が拒否されます。マルチレプリカは Enterprise の機能です(後述)。 + +### Ingress [#ingress] + +TLS はエッジで終端し、`OS_AUTH_URL` と `OS_TRUSTED_ORIGINS` を公開アドレスに設定します。クロスオリジンのフロントエンドは `OS_CORS_ORIGIN` に明示的に列挙し、ワイルドカードのオリジンと認証情報付きのリクエストを組み合わせないでください。ランタイムがプロキシの背後にある場合は、クライアントが指定した forwarded-for ヘッダーをプロキシが自身のもので置き換えるようにしてください。レート制限と監査証跡は、呼び出し元の識別情報が信頼できることに依存しています。 + +## Enterprise で追加されるもの [#enterprise-adds] + +**私たちが提供するのは Compose スタックであり、Helm チャートや Kubernetes マニフェストではありません。** リリースのデプロイバンドルが、サポートされテストされた構成です。Kubernetes ではマニフェストを自分で組み立てることになるため、そのスタックの次の性質も再現してください。オーケストレーターが何であれ、デプロイメントが受けられる権利は変わりません。 + +### イメージはダイジェストで固定する [#the-image-is-pinned-by-digest] + +Pod の spec では、Enterprise イメージを `@sha256:` ダイジェストで参照します。プルには、ライセンスされたレジストリの認証情報を持つプルシークレットが必要です。 + +### マイグレーションは、レプリカが提供を始める前に一度だけ、完了まで実行する [#migrations-run-once-to-completion-before-replicas-serve] + +Compose スタックは、アプリケーションのレプリカが起動する前に完了しなければならない 1 回限りのマイグレーションコンテナを実行します。Kubernetes ではこれは Job(または同等の提供前フック)であり、各レプリカの init コンテナではありません。要点は、スキーマ変更がレプリカごとに並行してではなく**一度だけ**実行されることです。イメージはまさにこのためにマイグレーションして終了するモードをサポートしており、その変数はバンドルに記載されています。 + +### すべてのレプリカに同一のシークレットを渡す [#every-replica-gets-identical-secrets] + +マルチレプリカは、レプリカ数を増やすだけの問題ではありません。 + +- Redis をバックエンドとする**クラスタードライバー**を有効にする必要があります。 +- **共有シークレットキー**が必須になります。クラスタードライバーがない場合、各レプリカは独自のキーを生成します。レプリカ間ではそれらが食い違うことになるため、食い違う代わりに**ランタイムは起動を拒否します**。1 つの値を、すべてのレプリカにマウントしてください。 +- **認証シークレット**も同様に、すべてのレプリカで同一でなければなりません。 + +### ライセンスとクラウドの姿勢はデプロイメント全体で共通 [#license-and-cloud-posture-are-deployment-wide] + +ライセンスキー、ライセンスモード、クラウドの姿勢の設定は、デプロイメントの識別情報の一部です。すべてのレプリカで同じでなければならず、サポートされる組み合わせは**起動時に**確認されます。設定が食い違う Pod は劣化した状態で動くのではなく、終了します。組み合わせについては[エアギャップ](/docs/deploy/air-gapped)を参照してください。 + +### ローリングアップグレードはダイジェストを変更する [#rolling-upgrades-change-the-digest] + +アップグレードとはイメージのダイジェストの変更であり、新しいレプリカが提供を始める前にマイグレーションの Job が完了まで実行されます。スキーマのマイグレーションは前にしか進みません。イメージをロールバックしてもスキーマはロールバックされないため、先にバックアップを取り、ロールバック先として以前のダイジェストを残しておいてください。アプリ自体は、公開済みアーティファクトとして提供される場合、イメージとは独立してバージョン管理されます。[デプロイ](/docs/deploy#three-run-shapes)の実行形態を参照してください。 diff --git a/content/docs/operate/audit-logs.ja.mdx b/content/docs/operate/audit-logs.ja.mdx new file mode 100644 index 00000000..1da3f217 --- /dev/null +++ b/content/docs/operate/audit-logs.ja.mdx @@ -0,0 +1,114 @@ +--- +title: 監査ログ +seoTitle: "監査ログ:すべての変更、サインイン、イベント" +description: デプロイメント上のすべての変更、サインイン、発行されたイベントを記録する、改ざんできない監査証跡と診断ログを読みます。 +editions: [community, enterprise] +translation: + source_sha: 8c6b53d05235befa452e3b9a3953df31e5b1ba4c01aaf168a9392a49da21197f + guide_rev: 1 + mode: auto +--- + +監査ログは、デプロイメントで何が起きたかの改ざんできない記録です。誰が、何を、いつ、どこから変更したかを示します。このページは、**Setup → Diagnostics** の下にある診断サーフェスの詳細なリファレンスです。ログ、メトリクス、リクエストの識別についてのオペレーター向けの概要は、[オブザーバビリティ](/docs/operate/observability)を参照してください。 + +> これらのサーフェスには `manage_platform_settings` 権限が必要です。 + +次のことを証明または調査する必要があるときに、監査ログを使います。 + +- 権限に関わる変更 +- 設定と構成の変更 +- ユーザーとセッションのアクティビティ +- 連携と自動化のアクティビティ +- 拒否されたアクセスの分析 + +## 監査ログ [#audit-logs] + +**Setup → Diagnostics → Audit Logs**(`sys_audit_log`)にあります。このオブジェクトは**読み取り専用で改ざんできません**。プラットフォームのイベントを追記専用で記録した証跡です。行の絞り込み、グループ化、確認はできますが、Setup から行を編集・削除することはできません。 + +### 各閲覧者に提供されるもの [#what-each-reader-is-served] + +すべての行は保存されたままですが、ObjectStack 17.6 以降、あるレコードに関する行は、閲覧者がそのレコード自体を読める場合にだけ提供されます。レコードを開けるかどうかを決めるのと同じ、オブジェクト権限、共有、行レベルセキュリティです。これにより次のものが除外されます。 + +- すべての `delete` の行と、その後削除されたレコードに関するその他すべての行 +- サインアウトの行と、その後セッションが削除されたサインインの行 +- 閲覧者が開けないレコードに関する行 + +どのレコードにも関係しない行(構成の変更、ユーザーインポートの実行、セッションを持たない認証イベント)は、従来どおり台帳自体の付与のもとで提供されます。 + +**`view_all_audit_log`** 機能はこの絞り込みを解除します(ObjectStack 17.7)。その保持者には、削除とサインアウトの証跡、そして開けないレコードに関する行が提供されます。プラットフォーム管理者はデフォルトでこれを保持していますが、組織の管理者は保持していないため、証跡全体を読む必要があるその他の人には明示的に付与してください。 + +フィールドレベルのセキュリティは、すべての行の中で引き続き適用されます。`old_value` と `new_value` は、マスクなしでそのフィールドを読める閲覧者にだけそのフィールドを提供します。また、フィルターが等価条件で 1 つのオブジェクトを指定し(`object_name`)、かつそのオブジェクトのすべてのフィールドを読める場合を除き、これらでの絞り込み、並べ替え、グループ化は拒否されます(`403`)。 + +### プリセットビュー [#preset-views] + +一覧は、アクションのカテゴリで絞り込むタブとともに開きます。どのタブにも上記のルールが適用されます。 + +| ビュー | 表示内容 | +|---|---| +| `Recent` | すべてのイベント、新しい順 | +| `Writes` | 作成 / 更新 / 削除のアクション。`delete` の行は `view_all_audit_log` がある場合のみ | +| `Auth` | サインインとサインアウトのイベント。サインアウトと、その後セッションが削除されたサインインは `view_all_audit_log` がある場合のみ | +| `Config` | 設定と構成の変更 | + +### 列とフィールド [#columns-and-fields] + +デフォルトの列は `Timestamp`、`Action`(create / update / … を示す色付きのピル)、`Object`、`Record ID`、`Actor` です。フッターにはレコードの総数が表示されます(例:*36 records*)。 + +各行は完全なフィールドのセットを持ちます。 + +| フィールド | 意味 | +|---|---| +| `created_at` | イベントが記録された日時 | +| `action` | 操作(create、update、delete、auth など) | +| `user_id` | 実行した User へのルックアップ | +| `actor` | 実行者の識別情報(テキスト) | +| `object_name` | イベントが影響したオブジェクト(例:`sys_user`) | +| `record_id` | 影響を受けたレコード | +| `old_value` | 変更前の値(テキストエリア) | +| `new_value` | 変更後の値(テキストエリア) | +| `ip_address` | リクエストの送信元 IP | +| `user_agent` | クライアントのユーザーエージェント | +| `tenant_id` | 所有するテナント | +| `metadata` | 追加の構造化されたコンテキスト | + +> `Config` ビューは、「誰がこの設定を変更したか」に答える最も速い方法です。行を読み始める前に、証跡を構成のアクションに絞り込みます。 + +### 変更を読む [#reading-a-change] + +書き込みアクションで重要な詳細は、変更前と変更後です。行の `⋮` メニューを開いて `old_value` と `new_value` を展開すると、そのレコードで何が変わったかが表示されます。読めるフィールドはすべて完全に表示され、それ以外は何も表示されません。`Object` と `Record ID` の列は*何が*影響を受けたかを、`old_value`/`new_value` は*どのように*を、`actor`、`ip_address`、`user_agent` は*誰が*、*どこから*を示します。 + +> 規制のある環境では、ストレージの層でも改ざん不可能性を担保してください。データベースでテーブルを追記専用にし、保持ポリシーを定めます。 + +## 通知イベント [#notification-events] + +**Setup → Diagnostics → Notification Events**(`sys_notification`)にあります。これは**通知システムの受信ログ**であり、`emit()` ごとに 1 行が、配信の判断より前に書き込まれます。通知が受信トレイに届いた理由、届かなかった理由の診断に使います。その後のルーティングと具体化については、[通知](/docs/configure/notifications)に記載されています。 + +プリセットビューは `Recent` と `By Topic` です。デフォルトの列は `Topic`(例:`project.digest`)、`Severity`(info/warn/… を示すピル)、`Actor`、`Source Object`、`Created At` です。 + +| フィールド | 意味 | +|---|---| +| `topic` | 発行されたトピック | +| `payload` | イベントのペイロード(JSON) | +| `severity` | 重大度 | +| `dedup_key` | 重複した発行をまとめるためのキー | +| `source_object` | イベントを発生させたオブジェクト | +| `source_id` | イベントを発生させたレコード | +| `actor_id` | 発行をトリガーしたプリンシパル | +| `created_at` | イベントが取り込まれた日時 | + +`dedup_key` は重複した発行をまとめ、`payload` はテンプレートの描画元となる生のイベントコンテキストを保持します。このログはルーティングの*上流*にあるため、ここに行があるのに対応する受信トレイのメッセージがない場合、通常は対象者がそのユーザーに解決されなかったか、ユーザーの設定がそのチャネルでそのトピックをミュートしています。ユーザーからアラートが届かないと報告があったときは、[通知](/docs/configure/notifications)を確認してください。 + +下流では、各イベントはユーザーごとの受信トレイのメッセージ(`sys_inbox_message`)に具体化されます。受信者側の表示については[通知](/docs/use/notifications)にあります。 + +## セッション [#sessions] + +**Setup → Diagnostics → Sessions** にあります。Sessions には有効なサインインセッションと最近のサインインセッションが一覧表示されるため、ユーザーのアクティビティを調査したり、アクセスを取り消したりできます。サインインのインシデントを確認するときは、`Auth` 監査ビューと組み合わせて使います。 + +## 次に進む [#where-to-go-next] + +| タスク | ページ | +|---|---| +| ログ、メトリクス、リクエストの識別についてのオペレーター向けの概要 | [オブザーバビリティ](/docs/operate/observability) | +| 通知のルーティングと描画の仕組みを理解する | [通知](/docs/configure/notifications) | +| 本番稼働の前にデプロイメントを堅牢化する | [本番環境への準備](/docs/operate/production) | +| 運用の概要に戻る | [運用](/docs/operate) | diff --git a/content/docs/operate/backup.ja.mdx b/content/docs/operate/backup.ja.mdx new file mode 100644 index 00000000..2dc6f5ea --- /dev/null +++ b/content/docs/operate/backup.ja.mdx @@ -0,0 +1,144 @@ +--- +title: バックアップと災害復旧 +description: 何をバックアップし、どのように復元し、障害にどう備えるか。 +editions: [community, enterprise] +translation: + source_sha: ef15634a21d14d8b172d6f1b203d01b045bb64b427e5810ca1d6c85c4ae69004 + guide_rev: 1 + mode: auto +--- + +ObjectOS 自体はステートレスです。ランタイムはコンテナイメージから再構築できます。**バックアップの対象は、ObjectOS 自体ではなく、ObjectOS が依存するデータです。** 復旧はそれらのデータセットを中心に計画してください。このページの内容はすべて、Community にも Enterprise にも当てはまります。 + +## バックアップするもの [#what-to-back-up] + +| 資産 | 所有者 | バックアップ戦略 | +|---|---|---| +| 業務データベース | 顧客 | データベースネイティブのバックアップ(マネージドサービスでは PITR)。四半期ごとに復元をテスト | +| コンパイル済みアーティファクト(`objectstack.json`) | アプリケーション/リリースチーム | 公開したすべてのアーティファクトを不変のストレージに保存し、決して上書きしない | +| シークレットの基準値。何よりも `OS_SECRET_KEY` | 顧客のシークレットマネージャー | 値そのものを預託する。ローテーションの頻度はポリシーに従う | +| オブジェクト/ファイルストレージ(`storage` 機能が有効な場合) | 顧客 | バケットのバージョニング + 必要に応じてリージョン間レプリケーション | +| ローカルストレージ上のアップロードされたファイル(`OS_STORAGE_LOCAL_ROOT`) | 顧客 | データベースと同じスケジュールで。レコードとその添付ファイルが同じ時点に復元されるように | +| 顧客管理のアイデンティティプロバイダー | IdP ベンダー | ベンダーネイティブ | + +> **`OS_SECRET_KEY` のないデータベースバックアップは不完全なバックアップです。** 保存されたすべてのシークレット(暗号化された設定、`secret` フィールド、データソースの認証情報)は、その 1 つのキーで暗号化されています。元のキーなしで復元したデータベースは、それらの値を読めないまま永久に保持することになります。キーは生成したその日に預託し、キーとデータベースをひとつのバックアップ単位として扱ってください。 + +`OS_DATABASE_URL` と `OS_SECRET_KEY` を設定したコンテナは、自身のファイルシステムをバックアップする必要がありません。キャッシュは再構築できます。キャッシュされたアーティファクトは読み取りのたびに完全性の固定に照らして再検証されるため、キャッシュを失っても取得が 1 回増えるだけで、正しさが損なわれることはありません。デフォルト設定で動作する**単一ホスト**は事情が異なります。そのホームディレクトリには SQLite データベース、アップロードされたファイル、自己生成したシークレットキーがあるため、ファイルのバックアップに含めてください。 + +## データベースのバックアップ [#database-backups] + +戦略はドライバーに合わせます。 + +| ドライバー | 推奨される方法 | +|---|---| +| PostgreSQL | 継続的な WAL アーカイブ + ベースバックアップ。ポイントインタイムリストア。小さなデータベースでは、`pg_dump --format=custom` も実用的な論理バックアップです | +| MySQL | バイナリログのアーカイブ + フルダンプ。ポイントインタイムリストア | +| MongoDB | レプリカセットのスナップショット。PITR には oplog | +| SQLite(シングルノード / デスクトップ) | WAL モード + `VACUUM INTO` または Online Backup API によるホットスナップショット。任意で継続的なレプリケーションに Litestream。後述の [SQLite のデプロイメント](#sqlite-deployments)を参照 | + +ドライバーにかかわらず、次のことを検証してください。 + +- バックアップが顧客の RPO 内に完了する +- 新しいデータベースで、同じアーティファクトから ObjectOS を起動し、トラフィックを提供できる +- 復元テストを少なくとも四半期に一度、エンドツーエンドで実施する + +## SQLite のデプロイメント [#sqlite-deployments] + +SQLite はデフォルトのドライバーであり、**シングルノード**の ObjectOS(デスクトップアプリ、小規模チームの社内ツール、エッジ / オンプレミスのアプライアンス、評価環境)にとって正当な本番の選択肢です。トレードオフは構造的なものです。SQLite は単一ライターなので、マルチノードの ObjectOS、高い同時書き込みスループット、共有データベースのデプロイメントには適しません。それらには PostgreSQL を使ってください。 + +本番で SQLite を実行する場合は、安全にするための設定とバックアップの手順をセットで考えます。 + +**ランタイムの設定** + +- ランタイムはファイルベースの SQLite を WAL モードで保持します(読み取りが書き込みをブロックせず、クラッシュに対して安全)。つまり、データベースの横にある `app.db-wal` には、まだ `app.db` に入っていないコミット済みの書き込みが含まれることがあります。ランタイムの実行中に `app.db` だけをコピーしないでください。 +- `PRAGMA synchronous=NORMAL` は、デスクトップ/シングルノードの通常のデフォルトです。WAL と組み合わせれば安全で、`FULL` よりはるかに高速です。 +- データベースファイルは**ローカルディスク**に置いてください。ランタイムの実行中に、NFS、SMB、または Dropbox / OneDrive / iCloud で同期されるフォルダーの中に置か**ない**でください。それらでは SQLite のロックが信頼できず、SQLite のデプロイメントが破損する最も一般的な原因です。 + +**ホットスナップショット(ダウンタイムなし)** + +SQLite の Online Backup API または `VACUUM INTO` でスナップショットを取る間も、ランタイムはトラフィックを提供し続けます。 + +```sql +VACUUM INTO '/var/backups/objectos/db-2026-05-27T13-00Z.sqlite'; +``` + +RPO に合った頻度でスケジュールします(cron、systemd タイマー、またはプロセス内のスケジューラー)。各スナップショットには、それを生成したアーティファクトのバージョンをタグ付けし、データベースとアーティファクトを一緒にロールバックできるようにします(後述の[アーティファクトのバージョン管理](#artifact-versioning)を参照)。 + +**オフサイトのコピー** + +ローカルのスナップショットはディスクの喪失を乗り越えられません。各スナップショットを耐久性のあるストレージに送ります。 + +- サーバー / VM:S3 または S3 互換のバケットに送ります(`storage` 機能のためにすでに設定しているものがあるはずです)。 +- デスクトップアプリ:ユーザーが管理する同期フォルダー(OneDrive / iCloud / Google Drive)にスナップショットを書き込みます。稼働中のデータベースと違い、スナップショットファイルは**閉じていて不変**なので、ここでは問題ありません。 + +**継続的なレプリケーション(より低い RPO)** + +SQLite のまま 1 分未満の RPO を実現するには、ObjectOS のプロセスと並べて [Litestream](https://litestream.io) を実行します。WAL を S3 互換のストレージにストリーミングし、ポイントインタイムリストアをサポートします。シングルノードの SQLite デプロイメントでデータ損失をほぼゼロにする必要がある場合の推奨される方法です。 + +**復元** + +ランタイムを停止し、データベースファイルを選んだスナップショットで置き換え(または `litestream restore` を実行し)、古い `-wal` や `-shm` ファイルが横に残っていないことを確認してから、スナップショットを生成したのと同じアーティファクトのバージョンでランタイムを起動します。 + +## アーティファクトのバージョン管理 [#artifact-versioning] + +公開したアーティファクトは不変として扱います。各アーティファクトには、コンパイルに使ったリリース id をタグ付けします(例:`objectstack-2026-05-24.json`)。正常だとわかっている業務の状態への復旧は、通常次のようになります。 + +1. データベースを選んだ時点に復元します。 +2. ObjectOS を、その時点で稼働していたアーティファクトのバージョンに向け直します。 +3. ランタイムを再起動します。 + +アーティファクトをその場で上書きすると、データベースのバックアップが完璧でも、きれいにロールバックする能力を失います。 + +## RPO/RTO の計画 [#rporto-planning] + +顧客のデプロイメントで実用的な最初の目標: + +| 区分 | RPO | RTO | 備考 | +|---|---|---|---| +| 評価 / デモ | ベストエフォート | ベストエフォート | SQLite のスナップショットで十分 | +| デスクトップアプリ / 小規模チームのシングルノード | 1 時間以内(スナップショット)または数秒以内(Litestream) | 数分 | SQLite + WAL + スケジュールされた `VACUUM INTO` + オフサイトのコピー | +| シングルテナントの本番 | 15 分以内 | 1 時間以内 | PITR 付きのマネージド PostgreSQL + ウォームイメージ | +| マルチテナント / 規制対象 | 5 分以内 | 30 分以内 | HA データベース + マルチ AZ の ObjectOS + テスト済みのランブック | + +より厳しい目標には、ObjectOS のコンテナ自体の外にあるプラットフォームの変更(HA データベース、マルチ AZ のイングレス、ウォームレプリカ)が必要です。 + +## 予行演習すべき障害モード [#failure-modes-worth-rehearsing] + +- **データベースが長時間利用できない。** ObjectStack 17.0 以降、これは期待するものではなく契約上の動作です。データドライバーが応答しない間、`/api/v1/ready` は 503 を返すため、readiness プローブは Pod を準備未完了とマークします。*復旧*を予行演習してください。データベースが戻ればレプリカが再参加すること、そしてそれに気づくために何も再起動を必要としないことです。オブジェクトがバインドするデータソースで、起動時に接続できないものは起動を失敗させるようになったため、アプリケーションのレプリカがロールする前に復元が完了することを確認してください。 +- **アーティファクトの不具合。** アーティファクトのポインターをロールバックします。データには影響しません。 +- **シークレットのローテーション。** `OS_AUTH_SECRET` をローテーションすると、すべてのセッションが無効になります。メンテナンスウィンドウ中に実施するか、レプリカごとに時間をずらして実施してください。 +- **リージョンの障害。** 顧客がリージョンのフェイルオーバーを求める場合、業務データベース、シークレットマネージャー、イングレスのすべてがリージョンをまたぐ必要があります。ObjectOS 自体は、イメージが利用できる場所ならどこでも実行できます。 + +## 復元の予行演習 [#the-restore-drill] + +本番稼働の**前に**一時的なホストでこれを予行演習し、ストレージを変更した後にもう一度行ってください。復元したことのないバックアップは、バックアップではなく希望にすぎません。 + +1. バックアップを取得したときと同じイメージのバージョンで、**クリーンなホストを用意します**。 +2. **データベースを復元します**。プロバイダーのスナップショットを使うか、次のようにします。 + + ```bash + pg_restore --clean --if-exists -d "$OS_DATABASE_URL" backup-2026-07-14.dump + ``` +3. **元のシークレットを用意します。** `OS_SECRET_KEY` と `OS_AUTH_SECRET` は預託した元の値であり、新たに生成した値であってはなりません。ローカルストレージを使っている場合は、アップロードされたファイルも復元します。 +4. **同じアーティファクトのバージョンで起動し、検証します。** + + ```bash + curl -fsS https://app.example.com/api/v1/health + curl -fsS https://app.example.com/api/v1/ready + ``` + + 次に、復元をエンドツーエンドで証明します。実際のアカウントでサインインし、バックアップ前から存在したレコードを開き、暗号化された設定から値を読み取ります。これが `OS_SECRET_KEY` を検証する操作です。 + +## ランブックに記載すべきこと [#what-to-capture-in-the-runbook] + +- 各データセットのバックアップのスケジュール、保持期間、オンコールの連絡先。 +- ステップごとの復元手順(まずデータベース、次にアーティファクト、そして ObjectOS の起動)。 +- 復元したデータベースの整合性を確認する検証クエリ。 +- ObjectOS のイメージとアーティファクトのバージョンの両方のロールバック計画([アップグレードとロールバック](/docs/operate/upgrade)を参照)。 +- 顧客に見えるインシデントのための連絡テンプレート。 + +## Enterprise で追加されるもの [#enterprise-adds] + +- **以前のイメージのダイジェストをバックアップと一緒に保管します。** ある時点への復元は、その時点で稼働していたダイジェストへの復元です。エアギャップのサイトでは、以前のダイジェストが唯一のコピーかもしれません。[エアギャップ](/docs/deploy/air-gapped#getting-the-image-inside-the-network)を参照してください。 +- **`OS_LICENSE_KEY` はシークレットの基準値に含めます。** 復元したデプロイメントは与えられたライセンスで起動し、マルチ組織の姿勢ではライセンスがないと起動を拒否します。 +- **レプリカがロールする前に復元します。** バンドルのスタックは、どのレプリカよりも先に 1 回限りのマイグレーションを実行するため、スタックが起動する時点でデータベースが完全でなければなりません。 diff --git a/content/docs/operate/index.ja.mdx b/content/docs/operate/index.ja.mdx new file mode 100644 index 00000000..894953f2 --- /dev/null +++ b/content/docs/operate/index.ja.mdx @@ -0,0 +1,38 @@ +--- +title: 運用 +seoTitle: "運用:本番環境への準備、バックアップ、アップグレード" +description: 稼働中の ObjectOS デプロイメントを健全に保ちます。本番環境への準備、オブザーバビリティ、監査証跡、バックアップ、アップグレード、復旧を扱います。 +editions: [community, enterprise] +translation: + source_sha: a17ed607565693a65e32a25c8fe8db345aa917afac9348647fecca7a35e9c8f7 + guide_rev: 1 + mode: auto +--- + +このセクションは、ObjectOS を**本番で健全に**保つためのものです。[設定](/docs/configure)がセットアップ時の管理(ユーザー、アクセス、サインイン、連携)を扱うのに対し、*運用*は稼働後の作業を扱います。デプロイメントの堅牢化、監視、何が起きたかの証明、そして問題が起きたときの復旧です。 + +ここのすべてのページは、セルフマネージドの両エディション、ObjectOS Community と ObjectOS Enterprise を対象としています。両者は同じランタイムを実行するため、運用方法も同じです。ライセンスされた Enterprise イメージがより多くを行う箇所(リリースノートに記載されたダイジェスト、ライセンスキー、複数のレプリカ)では、ページの最後に **Enterprise で追加されるもの** のセクションがあります。ObjectOS Cloud では、私たちがデプロイメントを運用します。 + +## 運用のページ [#operations-pages] + +| ページ | 用途 | +|---|---| +| [本番環境への準備](/docs/operate/production) | 本番稼働の前後にデプロイメントを堅牢化する | +| [オブザーバビリティ](/docs/operate/observability) | ログ、リクエスト id、メトリクス、エラーを接続する | +| [監査ログ](/docs/operate/audit-logs) | 改ざんできない監査証跡と診断ログを読む | +| [バックアップ](/docs/operate/backup) | バックアップと災害復旧を計画する | +| [アップグレード](/docs/operate/upgrade) | 安全にアップグレードまたはロールバックする | +| [トラブルシューティング](/docs/operate/troubleshooting) | 壊れたデプロイメントを診断する | + +## 設定と運用の違い [#configure-vs-operate] + +セットアップ時の管理(ユーザーの追加、アクセスの付与、メール、ストレージ、連携の接続)は[設定](/docs/configure)にあります。デプロイメントが稼働し、監視、監査、バックアップ、アップグレードが必要になったら、ここに戻ってきてください。 + +## 次に進む [#where-to-go-next] + +| タスク | ページ | +|---|---| +| 本番前のチェックリスト | [本番環境への準備](/docs/operate/production) | +| 変更やサインインを追跡する | [監査ログ](/docs/operate/audit-logs) | +| 監視を設定する | [オブザーバビリティ](/docs/operate/observability) | +| セットアップ時の管理 | [設定](/docs/configure) | diff --git a/content/docs/operate/observability.ja.mdx b/content/docs/operate/observability.ja.mdx new file mode 100644 index 00000000..49cb34af --- /dev/null +++ b/content/docs/operate/observability.ja.mdx @@ -0,0 +1,96 @@ +--- +title: オブザーバビリティ +seoTitle: "オブザーバビリティ:ログ、リクエスト ID、メトリクス、エラー" +description: ログ、リクエスト id、メトリクス、エラー、セッション、監査ログ。 +editions: [community, enterprise] +translation: + source_sha: 6260a1763af40fdb7320387d9d4a60879fc6f529e51b110ee98e9a10dff64f6e + guide_rev: 1 + mode: auto +--- + +ObjectOS の運用には、インフラストラクチャのシグナルとアプリケーションのシグナルの両方が必要です。フレームワークはリクエスト id、メトリクス、エラーレポーター、監査のプリミティブを提供し、どこにエクスポートするかはデプロイメントが決めます。 + +## リクエストの識別 [#request-identity] + +すべての本番デプロイメントは、リクエスト id を伝播または生成すべきです。 + +```text +X-Request-Id +``` + +これを使って次のものを関連付けます。 + +- イングレスのログ +- ObjectOS のログ +- データベースのスロークエリ +- エラーレポート +- 顧客サポートのチケット + +## メトリクスとエラー [#metrics-and-errors] + +ランタイムはメトリクスとエラーレポートをプロセス内に保持し、エクスポーターを選んだときにだけエクスポートします。少なくとも次のものを追跡してください。 + +| シグナル | 理由 | +|---|---| +| ルート/ステータス別のリクエスト数 | エラーの急増を検知する | +| リクエストの所要時間 | レイテンシの悪化を検知する | +| 5xx エラー | ランタイムの障害でアラートを出す | +| readiness の遷移 | プロセスはカーネルが構築されてから初めて準備完了を報告し、データドライバーが停止すると `/api/v1/ready` は 503 を返します | + +リクエスト数と所要時間は、サインインや REST データ API を含むすべての受信リクエストについて、メソッド、ルートパターン、ステータスのラベル付きで `http_requests_total` と `http_request_duration_ms` として出力されます。 + +### OpenTelemetry でエクスポートする [#export-with-opentelemetry] + +メトリクスは **OTLP** でランタイムの外に出ます。`OS_OBS_EXPORTER=otlp` **と** `OS_OTLP_ENDPOINT`(例:`https:///otlp`)を設定します。エクスポーターのデフォルトは `noop`(実行時コストゼロ)なので、エクスポートはオプトインです。`OS_OBS_EXPORTER=otlp` なしでエンドポイントを設定しても何も出力されず、エンドポイントなしの `otlp` は警告を出して `noop` にフォールバックします。ローカルのデバッグには `OS_OBS_EXPORTER=console` または `json` を使い、デプロイメントのタグ付けには `OS_OBS_DEPLOYMENT_ENV` を使います(デフォルトは `production`)。残りは[環境変数](/docs/reference/environment-variables#observability)に一覧があります。 + +ランタイムは独自のスクレイプエンドポイントを提供しません。Prometheus を使うには、エクスポーターを OpenTelemetry Collector に向け、コレクターから Prometheus にメトリクスを公開させます。あるいは、Grafana Cloud や Datadog など、OTLP を受け付ける任意のバックエンドに送信します。 + +パイプラインがランタイムのメトリクス名を維持していると仮定した、最初のアラートの例です。 + +```yaml +groups: + - name: objectos + rules: + - alert: ObjectOS5xxSpike + expr: | + sum(rate(http_requests_total{status=~"5.."}[5m])) + / sum(rate(http_requests_total[5m])) > 0.02 + for: 5m + annotations: + summary: "ObjectOS 5xx rate above 2% for 5 minutes" +``` + +アラートは `http_request_errors_total` ではなく `http_requests_total{status=~"5.."}` に対して設定してください。前者の系列は ObjectStack 17.2 で廃止され、常にゼロを示すため、正常なサーバーのように見えてしまいます。 + +コールドスタートは一覧にありません。カーネルは起動時に一度だけ構築され、プロセスはその後に初めて[準備完了](/docs/deploy/docker#health-checks)を報告します。定常状態でのキャッシュミスのシグナルはないので、代わりに readiness でアラートを出してください。 + +認証の失敗も一覧にありません。ランタイムのメトリクスレジストリは HTTP、ストレージ、キャッシュ、バックグラウンドジョブ、パッケージレジストリを対象としており、そのどれも拒否された認証情報を数えないため、アラートを出す系列がありません。`http_requests_total` から 401/403 を数えても代わりにはなりません。拒否された API キーは認証情報をまったく持たないリクエストと区別できず、どちらのステータスも、問題なく認証されたユーザーに対する通常の権限拒否が大半を占めます。代わりに、`view_all_audit_log` の保持者として、Setup の `Auth` 監査ビューでサインインのアクティビティを確認してください。プラットフォーム管理者はデフォルトでこれを保持しています。これがないと、サインアウトと、その後セッションが削除されたサインインは提供されません。[監査ログ](/docs/operate/audit-logs#what-each-reader-is-served)を参照してください。 + +## 監査ログ [#audit-logs] + +監査機能が有効な場合、ObjectOS は監査レコードを `sys_audit_log` に書き込みます。 + +監査ログは次の用途に使います。 + +- 権限に関わる変更 +- 設定の変更 +- ユーザー/セッションの調査 +- 連携のアクティビティ +- 拒否されたアクセスの分析 + +規制のある環境では、データベースまたはストレージの層で監査テーブルを追記専用にし、保持ポリシーを定めてください。 + +完全なフィールドのリファレンス、プリセットビュー、関連する診断ログについては、[監査ログ](/docs/operate/audit-logs)を参照してください。 + +## Setup の診断 [#setup-diagnostics] + +Setup は次のような運用向けのサーフェスを提供します。 + +- Sessions +- Audit Logs +- Notifications +- Webhook Deliveries(Webhook が有効な場合) +- System と Security の概要ダッシュボード + +これらは開発者だけでなく、オペレーターやサポートエンジニアを対象としています。 diff --git a/content/docs/operate/production.ja.mdx b/content/docs/operate/production.ja.mdx new file mode 100644 index 00000000..7798854c --- /dev/null +++ b/content/docs/operate/production.ja.mdx @@ -0,0 +1,110 @@ +--- +title: 本番環境への準備 +seoTitle: "本番環境への準備:堅牢化、シークレット、CORS、本番稼働" +description: セルフマネージドの ObjectOS デプロイメントを本番で安全に実行するためのチェックリストです。ヘッダー、シークレット、レート制限、CORS、起動時のガード、スケジュールされた処理、保持期間を扱います。 +editions: [community, enterprise] +translation: + source_sha: 0d1f08122f21ab1bab4439881d84e8ef4cd8c08c847bfc56516c59fbcc0d48c2 + guide_rev: 1 + mode: auto +--- + +ObjectOS を本番トラフィックに公開する前に、このチェックリストを使ってください。Community にも Enterprise にも当てはまります。最後の [Enterprise で追加されるもの](#enterprise-adds)のセクションには、ライセンスされたイメージに伴う項目を挙げています。 + +## HTTP の堅牢化 [#http-hardening] + +ランタイムは、デフォルトでルートに保守的なセキュリティヘッダーを送信します。ヘッダーを書き換えるプロキシを通った後の、最終的なレスポンスを確認してください。 + +- `Content-Security-Policy` +- `X-Content-Type-Options` +- `X-Frame-Options` +- `Referrer-Policy` +- `Permissions-Policy` +- `Cross-Origin-Resource-Policy` +- HSTS。ランタイムはデフォルトでは送信しません。TLS を確認してからプロキシで追加してください。 + +```bash +curl -I https://app.example.com +``` + +## シークレット [#secrets] + +これらはシークレットマネージャーに保管し、実行時に注入します。 + +| シークレット | 目的 | +|---|---| +| `OS_AUTH_SECRET` | セッション署名の基本シークレット。**すべてのレプリカで同一**にします。値の異なるレプリカは互いのセッションを拒否します。 | +| `OS_SECRET_KEY` | 保存されたすべてのシークレットを暗号化するキー。自己生成したキーがファイルシステムとともに消えるコンテナでは必須です。**データベースと一緒にバックアップしてください**。一方だけでは何も復号できません。 | +| データベースの認証情報 | 業務データベースへのアクセス | +| OIDC クライアントシークレット | シングルサインオン | +| プロバイダーの API キー | メール、ストレージ、AI、連携 | + +シークレットをアーティファクトやイメージに焼き込まないでください。 + +コントロールプレーンに**バインド**されたデプロイメントはランタイムトークンも保持しますが、それは設定するシークレットではありません。バインディングの際に発行され、ランタイムが永続化します。そのために配布するデプロイメントレベルの API キーはありません。 + +## レート制限 [#rate-limiting] + +ランタイムには組み込みのトークンバケット方式のレート制限があり、**アプリが予算を宣言するまでオフ**です(スタックの `server.security.rateLimit`)。予算を超えた呼び出し元には、`Retry-After` 付きの `429` が返されます。バケットのキーは、サインインしたユーザー、匿名のトラフィックでは呼び出し元のアドレスです。 + +リバースプロキシの背後では、アプリが `server.trustProxy: true` を設定しない限り、呼び出し元のアドレスはプロキシのものになります。これは、すべてのリクエストで `X-Forwarded-For` を上書きするプロキシの背後でだけ設定してください。そうでなければ、呼び出し元が自分のアドレスを選べてしまいます。カウンターはプロセスごとにメモリ内に保持されます。サインインには、**Setup → Authentication** で、より厳しい専用の予算を設定できます。 + +制限を代わりにエッジ(イングレスやゲートウェイ)に置く場合は、次のような予算から始めてください。 + +| トラフィック | 制限の例 | +|---|---:| +| 認証エンドポイント | 10/分/IP | +| 書き込みリクエスト | 60/分/IP | +| 読み取りリクエスト | 600/分/IP | + +## CORS [#cors] + +`OS_CORS_ORIGIN` がない場合、ランタイムはクロスオリジンの呼び出し元を**一切**受け入れません。Console と API が同じ公開オリジンを共有している間は、それで問題ありません。別のオリジンから提供されるフロントエンドは、カンマ区切りで明示的に列挙します。 + +```bash +OS_CORS_ORIGIN=https://portal.example.com,https://admin.example.com +``` + +認証情報が有効な間、`*` は起動時に拒否されます。 + +## 起動時のガードはオンのままにする [#boot-guards-stay-on] + +開発用に 2 つの抜け道があります。どちらも本番には不適切です。 + +- **`OS_ALLOW_DRIVER_CONNECT_FAILURE` は未設定にします。** 起動時に接続できないデータベースは起動を拒否するため、誤った `OS_DATABASE_URL`、ローテーションされたパスワード、閉じたネットワーク経路はデプロイの失敗になります。すべてのリクエストに 500 を返す「起動済み」のサーバーにはなりません。このフラグはデータベースなしで起動し、再接続することはありません。 +- **`OS_ALLOW_DEGRADED_TENANCY` は未設定にします。** 組織のランタイムなしで複数の組織を求めるデプロイメントは起動を拒否します。このフラグは、分離が欠けたまま起動させてしまいます。 + +## スケジュールされた処理は判断である [#scheduled-work-is-a-decision] + +アプリやパッケージが宣言するスケジュールされた処理(時間で起動するフローとパッケージ化された cron ジョブ)は、デプロイメントが `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true` を設定するまで**オフ**です。ダイジェスト、スイープ、リマインダーの起動を期待するデプロイメントは、これをオンにする必要があります。オフのままにするのも正当な答えです。意図して決めてください。実効値は `os doctor` で確認し、起動時のサマリーを読んでください。そこに *disabled by deployment policy* と表示される処理は壊れているのではなく、オフになっているだけです。[環境変数](/docs/reference/environment-variables#runtime-services)を参照してください。 + +## 保持期間は本番稼働前に設定する [#retention-is-set-before-go-live] + +プラットフォームはテレメトリをスケジュールに従って削除します。アクティビティは 14 日後、ジョブの実行は 30 日後、通知は 90 日後、監査レコードは 90 日後にホットストレージから外れます。コンプライアンス上より長い期間が必要な場合は、本番稼働の**前に** `lifecycle.retention_overrides` 設定で期間を延長してください。ポリシーによって削除されたデータは、その後に取得するすべてのバックアップから消えています。 + +## 本番稼働のチェックリスト [#go-live-checklist] + +- [ ] TLS がエッジまたはイングレスで終端されている。 +- [ ] 最終的なレスポンスにセキュリティヘッダーが含まれている。 +- [ ] TLS の検証後に HSTS が有効になっている。 +- [ ] `OS_AUTH_URL` と `OS_TRUSTED_ORIGINS` が公開アドレスと一致している。 +- [ ] CORS が、提供するクロスオリジンのフロントエンドだけを正確に受け入れている。 +- [ ] レート制限がサインインと書き込みのエンドポイントを保護している。 +- [ ] `OS_AUTH_SECRET` と `OS_SECRET_KEY` が強力で、シークレットとして保管され、預託されている。 +- [ ] アドレスを共有する前に、最初の管理者が登録されている。 +- [ ] OIDC のコールバック URL が公開ドメインと一致している。 +- [ ] `OS_ALLOW_DRIVER_CONNECT_FAILURE` と `OS_ALLOW_DEGRADED_TENANCY` が未設定である。 +- [ ] `OS_AUTOMATION_SCHEDULED_WORK_ENABLED` が、誰かが下した判断になっている。 +- [ ] 保持期間がポリシーと一致している。 +- [ ] 業務データベースのバックアップと復元がテストされている。[バックアップ](/docs/operate/backup#the-restore-drill)を参照。 +- [ ] 監査ログがポリシーに従って保持されている。 +- [ ] 監視が 5xx の割合と readiness でアラートを出す。[オブザーバビリティ](/docs/operate/observability)を参照。 +- [ ] ロールバック計画が、イメージのバージョンとアプリのアーティファクトのバージョンの両方を対象にしている。 + +## Enterprise で追加されるもの [#enterprise-adds] + +- [ ] `OS_LICENSE_KEY`(デプロイメントのエンタイトルメント)がシークレットとして保管されている。 +- [ ] `OS_CLOUD_URL` が宣言され、ライセンスモードと一致している。通常のライセンスと `off` の組み合わせは起動時に拒否されます。[エアギャップ](/docs/deploy/air-gapped#the-supported-combinations)を参照。 +- [ ] イメージがダイジェストで固定され、その署名、SBOM、来歴が検証されている([Docker](/docs/deploy/docker#verify-the-image-before-production))。 +- [ ] 複数のレプリカでは、`OS_AUTH_SECRET` と `OS_SECRET_KEY` がすべてのレプリカで同一である。 +- [ ] マルチ組織の姿勢では、メンバーシップのポリシーとマウントする AI エージェントが宣言され、`OS_PLATFORM_OWNER_EMAIL` が確認済みの管理者を 2 人以上指定し、組織をまたぐアクセスの否定テストが合格している。 diff --git a/content/docs/operate/troubleshooting.ja.mdx b/content/docs/operate/troubleshooting.ja.mdx new file mode 100644 index 00000000..7cc1518c --- /dev/null +++ b/content/docs/operate/troubleshooting.ja.mdx @@ -0,0 +1,113 @@ +--- +title: トラブルシューティング +seoTitle: "トラブルシューティング:起動、ログイン、権限" +description: 起動、アーティファクト、認証、権限、デプロイメントの問題を診断します。 +editions: [community, enterprise] +translation: + source_sha: 46522dea81d25b77061b5acda2115df72c67d2e29c36c1829b96b0df1da194eb + guide_rev: 1 + mode: auto +--- + +症状から始め、最も小さな境界から確認していきます。 + +## ObjectOS が起動しない [#objectos-does-not-start] + +**何よりも先に、ログの最後の数行を読んでください。** ここでの起動失敗の多くは、クラッシュではなく*意図的な拒否*です。ランタイムは与えられた設定を確認し、食い違っている変数を名指しした致命的エラーを表示して終了します。それらのメッセージは問題を正確に示しており、何度再起動しても結果は変わりません。 + +どちらのエディションでも最もよく遭遇する拒否は次のとおりです。 + +| ログの内容 | 意味 | +|---|---| +| データドライバーが接続できなかった | `OS_DATABASE_URL` のデータベース、またはアプリがオブジェクトをバインドするデータソースに到達できないか、認証情報が間違っています。すべてのリクエストでエラーを返すサーバーを提供する代わりに、起動が拒否されます。 | +| 破壊的なスキーマ変更が保留中で、各変更が示されている | 新しいアプリまたはバージョンに、データを失う変更が必要です。バックアップを取ってから、`os migrate apply --allow-destructive` で意図的に適用し、再起動します。 | +| アーティファクトの期待するダイジェストと実際のダイジェスト | `OS_ARTIFACT_URL` の完全性の固定が、取得したバイトと一致しません。[後述](#the-app-is-not-the-one-you-expected)を参照してください。 | +| ポートが使用中 | `os start` は別のポートに移ることはありません。ポートを解放するか、`OS_PORT` とプロキシのアップストリームを一緒に変更してください。 | +| マルチノードのトポロジーが宣言された | マルチノードのライセンスがない、または調整がプロセス内で行われるランタイムで、`OS_CLUSTER_REPLICAS` が 1 より大きくなっています。レプリカ 1 つで実行してください。[Docker → マルチノード](/docs/deploy/docker#multi-node)を参照してください。 | +| `Cannot find module 'pg'` | 素の Node.js のインストールには PostgreSQL ドライバーがありません。イメージには `pg` と `mysql2` が含まれています。イメージの外では、CLI と並べてドライバーをインストールします(`npm install -g pg`)。 | + +ログに致命的エラーがない場合は、一般的な原因を確認します。失敗しているサービスのコンテナログと、プローブです。`/api/v1/health` はプロセスが HTTP を提供し始めると応答し、`/api/v1/ready` はデータベースが応答してから初めて応答します。 + +## アプリが期待したものと違う [#the-app-is-not-the-one-you-expected] + +デプロイメントが提供するアプリはただ 1 つの場所から来ており、その場所はデプロイメントが宣言する判断です。[起動モード](/docs/architecture#boot-modes)を参照してください。実行中のアプリが公開したものと違う場合: + +- **このデプロイメントがどのモードかを確認します。** アーティファクトの参照が設定されていない場合、ランタイムはイメージが持っているものを提供しています。Community では `/srv/app/objectstack.json` のファイル、または空のカーネルとそこにインストールされたアプリ。Enterprise では、イメージ内で作成されたメタデータとそこにインストールされたもの。新しいアーティファクトを公開しても、デプロイメントがそれを指定するまでは何も変わりません。 +- **起動時のバナーを確認します。** 実際に読み込まれたアプリの名前が表示されます。 +- **一致しない完全性の固定は起動を拒否し**、期待するダイジェストと実際のダイジェストの両方を表示します。両方が一緒に表示されることが要点です。再公開されたアーティファクトと、すり替えられたアーティファクトを区別できます。 +- **固定がなく、アーティファクトのホストに到達できない場合、起動ははっきりと失敗します。** 固定なしの経路にはキャッシュへのフォールバックがありません。キャッシュされたコピーを認証する手段がないからです。オーケストレーションが再試行します。ランタイムが別のアプリをでっち上げることはありません。 +- **固定があり、ホストに到達できない場合、ローカルにキャッシュされたコピーが提供されることがあります。** ただし、そのバイトのハッシュが固定と一致するものに限られ、警告でその旨が表示されます。キャッシュは読み取りのたびに再ハッシュされます。ファイル名が根拠になることはありません。 +- **廃止されたアーティファクトの変数を設定しても、アプリは選択されません。** Enterprise イメージでは、そのうちの 1 つは起動をその場で拒否します。その他はまったく読み取られないため、デプロイメントは何も設定しなかったかのように起動します。[廃止された名前](/docs/reference/environment-variables#retired-names)に、それらと代替となる変数の対応が一覧になっています。 + +## 誰もサインインできない [#nobody-can-sign-in] + +新しいデプロイメントでは、最初に登録したアカウントが管理者になります。このルールは**ログインではなく人**を数えます。サインインアカウントなしで人のレコードを投入するアプリは、データベースにデータがあるように見せるため登録は閉じたままになり、投入された人にはサインインに使うパスワードがありません。起動ログは、これを `no_sign_in_account_at_boot` としてエラーレベルで報告します。復旧は帯域外で行います。招待の行を 1 つデータベースに書き込み、通常の登録を行います。手順は ObjectStack の[セルフホスティングガイド](https://objectstack.ai/docs/deployment/self-hosting#a-seeded-directory-opens-no-bootstrap-window)で説明されています。これを防ぐには、人と一緒にサインインアカウントを投入するか、最初の管理者ができるまでディレクトリを空のままにしてください。 + +## ログインに失敗する [#login-fails] + +確認すること: + +- `OS_AUTH_SECRET` が設定されている。これがないと、サインインエンドポイントはまったく有効になりません +- `OS_AUTH_URL` と `OS_TRUSTED_ORIGINS` が公開アドレスを指している +- OIDC のコールバック URL が公開アドレスと一致している +- OIDC のディスカバリー URL に ObjectOS から到達できる +- 信頼されたオリジンに公開ドメインが含まれている +- Cookie のスコープがデプロイメントの公開ホスト名になっている + +## ユーザーがレコードを見られない [#user-cannot-see-records] + +確認すること: + +1. デプロイメントのホスト名が正しい。 +2. ユーザーが想定した組織に所属している。 +3. オブジェクトの `read` 権限。 +4. 行レベルセキュリティ。 +5. 共有ルールまたはレコードの共有。 +6. 一部のフィールドだけが見えない場合は、フィールドのセキュリティ。 + +## 設定を編集できない [#settings-are-not-editable] + +設定は環境のオーバーライドによってロックされている場合があります。実効的な設定は次の順序で解決されます。 + +```text +Environment -> Tenant -> User -> Default +``` + +環境が値を提供している場合、実行時の編集は、黙って上書きするのではなく拒否されるべきです。 + +## 保存されたシークレットを読めない [#stored-secrets-cannot-be-read] + +暗号化された設定、`secret` フィールド、データソースの認証情報は、`OS_SECRET_KEY` で暗号化されています。再起動や復元の後に復号できなくなった場合は、キーが変わっています。キーを設定しなかったコンテナが独自のキーを生成し、ファイルシステムとともに失ったか、データベースが元のキーなしで復元されたかです。預託した元の値を設定してください。[バックアップ](/docs/operate/backup)を参照してください。新しいキーで、古いキーで暗号化された値を回復することはできません。 + +## Webhook やジョブが実行されない [#webhooks-or-jobs-do-not-run] + +確認すること: + +- アーティファクトの `requires` リストに必要な機能が含まれている +- ObjectOS イメージにオプションのサービスパッケージが含まれている +- キュー/ジョブサービスの設定が利用できる +- スケジュールフローと cron ジョブには `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true` が必要。未設定はオフです +- 送信先へのアウトバウンドのネットワークアクセスが許可されている +- 配信ログまたはジョブの実行が Setup の診断で表示される + +## データベースのエラー [#database-errors] + +確認すること: + +- データベース URL とドライバーの種類 +- ObjectOS からデータベースへのネットワークアクセス +- 認証情報と TLS のオプション +- スキーマの同期/マイグレーションのログ +- ローカルの SQLite を使っている場合は、ストレージの永続性 + +## Enterprise で追加されるもの [#enterprise-adds] + +ライセンスされたイメージは、起動時にさらにいくつかの設定を拒否します。 + +| ログの内容 | 意味 | +|---|---| +| 2 つの設定がサポートされない組み合わせとして一緒に示されている | 両立しないライセンスモードとクラウドの姿勢です。最も多いのは、コントロールプレーンなしで設定されたデプロイメント上の、オンラインで検証される通常のライセンスです。サポートされる組み合わせの表は[エアギャップ](/docs/deploy/air-gapped)にあります。 | +| 壁で区切られたデプロイメントにはライセンスが必要 | ライセンスなしでマルチ組織のテナンシーの姿勢が求められました。分離はライセンスされた機能であり、ランタイムは分離があるふりをしてトラフィックを提供することはありません。 | +| 判断が下されていない | マルチ組織の姿勢では、新規サインアップのメンバーシップのポリシーと、マウントする AI エージェントのセットに安全なデフォルトがありません。どちらの値を宣言しても受け入れられますが、未設定のままにすることは受け入れられません。 | +| 廃止された変数が設定されている | 古いアーティファクト選択の変数は起動を拒否し、代替となる変数を示します。[廃止された名前](/docs/reference/environment-variables#retired-names)を参照してください。 | +| クラスターのシークレットがない | クラスタードライバーを有効にすると、すべてのレプリカで共有する 1 つのシークレットキーが必須になります。 | diff --git a/content/docs/operate/upgrade.ja.mdx b/content/docs/operate/upgrade.ja.mdx new file mode 100644 index 00000000..c55ccb0b --- /dev/null +++ b/content/docs/operate/upgrade.ja.mdx @@ -0,0 +1,142 @@ +--- +title: アップグレードとロールバック +description: データベースをバックアップしたうえで、固定する正確なイメージを変更して、セルフマネージドの ObjectOS デプロイメントをアップグレードまたはロールバックします。ObjectStack の各リリースが求めるオペレーターの手順も扱います。 +editions: [community, enterprise] +translation: + source_sha: 98d9f60a7e174ca49ba4e63e570cba5e24d158bd80e44ddfc9cc8d36d2fb0d5d + guide_rev: 1 + mode: auto +--- + +アップグレードとは固定する正確なイメージの変更であり、ロールバックとは以前のイメージに戻すことです。Community では、それはイメージの正確なバージョンタグと、固定している場合はそのダイジェストです。タグはそのバージョンが再ビルドされると新しいバイトに再びプッシュされることがありますが、ダイジェストは決して動きません([Docker → イメージを固定する](/docs/deploy/docker#pin-the-image)を参照)。Enterprise では、リリースノートに記載されたダイジェストです([Enterprise で追加されるもの](#enterprise-adds))。このページのそれ以外の内容は両方に共通です。[Docker → アップグレードとロールバック](/docs/deploy/docker#upgrade-and-rollback)に手順と各ステップの理由を記載しています。このページはそれをチェックリストとして繰り返し、特定のリリースが求める手順を追加します。 + +## アップグレード [#upgrade] + +1. 実行中のバージョンから移行先のバージョンまでの、すべての ObjectStack リリースのノートを読み、後述の[バージョン固有の手順](#version-specific-steps)を順に実施します。メジャーバージョンの移動は、下層に破壊的変更があることを意味します。 +2. **データベースをバックアップします。** スキーマのマイグレーションは*前にしか進みません*。イメージをロールバックしても、スキーマはロールバックされません。 +3. イメージを新しい正確なバージョン(ダイジェストを固定している場合はそのダイジェスト)に変更し、再起動します。スキーマはランタイムが準備完了を報告する前に最新の状態にされます。破壊的な変更は、`os migrate apply --allow-destructive` で適用するまで、その変更を示して起動を拒否します。 +4. readiness(`/api/v1/ready`)が 200 を返すことを確認します。 + +## ロールバック [#roll-back] + +**以前の**バージョンと、固定している場合はそのダイジェストを残しておきます。それがロールバックです。ロールバックするには、それに戻して手順 3 を繰り返します。離れるバージョンが互換性のないスキーマ変更を行っていない限り有効です(行った場合はリリースノートに記載されています)。シークレットにも同じ一方通行の境界があります。ObjectStack 17.7 以降で設定またはローテーションしたシークレットは、17.7 より前のリリースでは読めません(後述の [17.7](#objectstack-177) を参照)。 + +## アプリは別にバージョン管理される [#the-app-versions-separately] + +アプリ自体は、公開済みアーティファクトとして提供される場合、イメージとは独立してバージョン管理されます。[デプロイ → アプリの組み込み方](/docs/deploy#how-the-app-gets-in)を参照してください。公開したアーティファクトは不変として扱ってください。その場で上書きすると、データベースのバックアップが完璧でも、きれいにロールバックする能力を失います([バックアップ → アーティファクトのバージョン管理](/docs/operate/backup#artifact-versioning))。 + +## バージョン固有の手順 [#version-specific-steps] + +上記の手順はすべてのリリースで同じです。ObjectOS の下層にある ObjectStack のリリースの中には、オペレーターに対応を求めるものもあります。新しいイメージが起動する前に変数を設定する、データマイグレーションを実行する、認証情報をローテーションする、などです。Community イメージのバージョンは ObjectStack のバージョン**そのもの**です。Enterprise では、イメージの[リリースマニフェスト](/docs/deploy/docker#the-image-can-tell-you-what-it-is)に、組み込まれた ObjectStack のバージョンが記載されています。通過するすべてのバージョンのサブセクションを、古いものから順に、上記の手順と並べて実施してください。 + +各サブセクションは、そのリリースのアップグレードチェックリストのうちオペレーターが担う部分であり、チェックリスト全体へのリンクがあります。リリースが実行中のアプリ(そのメタデータとコード)に対して変更する内容は、同じチェックリストと[変更履歴](/docs/resources/changelog)にあります。`os migrate` コマンドは、新しいイメージが組み込むバージョンの ObjectStack CLI です。メタデータを読むためにアプリを起動するので、デプロイメントの起動時と同じ環境(同じデータベースと同じアプリ)で実行してください。[CLI → データベースのマイグレーション](/docs/reference/cli#database-migrations--os-migrate)を参照してください。 + +> **コピーで予行演習してください。** リリースノートでは、チェックリストのほとんどの行が *not exercised* とされています。実際のアップグレードを通して確認したものではなく、変更内容から導いたものです。各手順はまずデータベースのコピーに対して実行し、本番の前にバックアップを取ってください。 + +### ObjectStack 17.0 [#objectstack-170] + +- **2 つのデータマイグレーションを実行します。** `os migrate files-to-references` を実行してから `--apply` 付きでもう一度実行します。そして `os migrate value-shapes` を実行し、何も報告されなくなったら `--apply` を実行します。それぞれがフラグを記録するまで、17.0 より前に作成されたデータベースは不正な形式のファイル値と参照値を警告付きで受け入れ続けます。記録後は拒否します。期限までに修正できない連携については、[抜け道](/docs/reference/environment-variables#data-contract-escape-hatches)を参照してください。 +- **アプリがバインドするすべてのデータソースが接続できなければなりません。** 接続できないものがあると起動は失敗し、応答しなくなったものがある間、`/api/v1/ready` は `503` を返します。 + +チェックリスト全体:[17.0.0](https://objectstack.ai/docs/releases/v17/17-0#upgrade-checklist)。 + +### ObjectStack 17.1 [#objectstack-171] + +- **アップグレードの前に、オフにされた権限セットとポジション**(`active: false`)**を一覧にします。** 17.1 以降、それらは権限を付与しなくなり、無効化されたポジションを指定する共有ルールは共有が取り消されます。引き続き権限を付与すべきものは再度有効にしてください。 +- **`isolated` では API キーを再発行します。** 17.1 より前に発行されたキーは組織を持たないため、`OS_TENANCY_POSTURE=isolated` では拒否されます。 +- **エクスポートはオブジェクトごとに付与されます。** 標準の管理者権限セットは、すべてのオブジェクトをエクスポートできなくなりました。エクスポートを意図する場所では、アプリ自身の権限セットで `allowExport` を付与してください。 + +チェックリスト全体:[17.1.0](https://objectstack.ai/docs/releases/v17/17-1#upgrade-checklist)。 + +### ObjectStack 17.2 [#objectstack-172] + +- **`http_request_errors_total` をキーとする監視を向け直します。** この系列は廃止され、常にゼロを示すため、正常なサーバーのように見えてしまいます。`http_requests_total{status=~"5.."}` を使ってください。 +- 外部データソースのルートを呼び出す認証情報には、**`manage_platform_settings` または `manage_metadata` を付与します**。これらのルートは、それ以外の呼び出し元を `403` で拒否するようになりました。 + +チェックリスト全体:[17.2.0](https://objectstack.ai/docs/releases/v17/17-2#upgrade-checklist)。 + +### ObjectStack 17.3 [#objectstack-173] + +- **壁で区切られた姿勢では、新しいイメージが起動する前に `OS_PLATFORM_OWNER_EMAIL` を設定します。** そうしないと起動を拒否します。[環境変数](/docs/reference/environment-variables#multi-organization-deployments)を参照してください。 +- **新しい一意インデックスを構築できるように、自動採番の重複を解消します。** `os migrate duplicates` を実行し、報告された重複を修正してから、`os migrate plan` を再実行します。それまではインデックスは構築されず、一意性も強制されません。起動時にはログが出るだけなので、問題なく起動したことはインデックスが存在する証拠になりません。 +- **`sys_record_share` をバックフィルします。** 17.3 より前に書き込まれた共有の付与は組織を持たず、テナントスコープの読み取りでは見つからなくなります。ObjectStack はこのバックフィルを、`os` コマンドではなく、`@objectstack/plugin-sharing` の 2 つの関数 `planSysRecordShareOrganizationBackfill`(ドライラン)と `runSysRecordShareOrganizationBackfill` として提供しており、起動時に自動実行されることはありません。 +- **オープンなサインアップは宣言が必要になりました。** 対象者の姿勢を宣言しないアプリは招待制です。アドレスに保留中の招待がない限り、自己登録は `403 SELF_REGISTRATION_CLOSED` で拒否されます(新規インストールの最初のアカウントは例外です)。 + +チェックリスト全体:[17.3.0](https://objectstack.ai/docs/releases/v17/17-3#upgrade-checklist)。 + +### ObjectStack 17.4 [#objectstack-174] + +- **`isolated` では、組織を持たない API キーを再発行します。** それらは `200` から `401` に変わります。`group` では、そのようなキーは引き続き機能します。 +- 認可ストアに到達できない間、**ストレージのダウンロードと設定のルートは `503` を返します**。以前は黙って劣化していました。 + +チェックリスト全体:[17.4.0](https://objectstack.ai/docs/releases/v17/17-4#upgrade-checklist)。 + +### ObjectStack 17.5 [#objectstack-175] + +アップグレードの前に: + +- **壁で区切られた姿勢では、すべてのプラットフォーム管理者を `OS_PLATFORM_OWNER_EMAIL` に列挙します**(カンマ区切り、確認済みのアドレス)。`admin_full_access` の付与はその地位を与えなくなり、宣言のないデプロイメントはプラットフォーム管理者なしで起動します。 +- **スケジュールされた処理について決めます。** 時間で起動するフローとパッケージ化された cron ジョブは、デプロイメントが `OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true` を設定しない限り実行されなくなります。`isolated` では、まず各スケジュールフローの開始ノードに実行時の `organization` を指定し、中断中の実行を排出してください。[フローと自動化](/docs/build/automation/flows#scheduled-run-on-a-clock)を参照してください。 +- **アカウント識別情報の変更を事前確認します。** `os migrate account-issuer` を実行し(読み取り専用。2 つのアカウントが衝突する間はゼロ以外で終了します)、報告されたすべての衝突を解消します。衝突する行が自動的にマージされることはありません。 + +リリースへの移行: + +```bash +os migrate account-issuer # expect: safe to drop +os migrate plan # read every destructive change first +os migrate apply --allow-destructive # drops sys_account.issuer, after a backup +os migrate account-issuer # again: expect zero collisions +``` + +`--allow-destructive` は、これだけでなくプランに列挙されたすべての破壊的な変更を適用します。そのため、先にプランを確認します。 + +その後: + +- 匿名の `GET /api/v1/auth/get-session` は、`null` 付きの `200` ではなく `401` を返します。 +- Analytics は、オブジェクトを読めない呼び出し元を `403` で拒否します。 +- 失敗した通知の配信は 90 日ではなく 7 日間保持されます。 + +チェックリスト全体:[17.5.0](https://objectstack.ai/docs/releases/v17/17-5#upgrade-checklist)。 + +### ObjectStack 17.6 [#objectstack-176] + +- **アプリがオブジェクトを読み書きする匿名エンドポイント**(`authRequired: false`)**を宣言している場合は、アップグレードを保留します。** 17.6 以降、そのエンドポイントはすべてのオブジェクトで拒否され、匿名の呼び出し元に権限セットを付与するサポートされた方法は、17.7 でもありません。公開フォームは別の入り口であり、引き続き機能します。 +- `/api/v1/meta` を通じて `datasource` または `external_catalog` のメタデータを読む、あるいは `datasource` のメタデータを保存するすべてのユーザーまたは連携に、**`manage_platform_settings` を付与します**。 +- **パッケージ化されたフローと同じ名前を持つ保存済みのフローを見つけます**(起動ログがそれぞれについて警告します)。17.6 以降はパッケージ化されたフローが優先され、保存済みのものは実行されなくなります。必要なものは新しい名前で複製してください。 +- **保存されたメタデータ本体のコピーを書き換えます:** `os migrate audit-metadata-bodies` を実行してから、`--apply` 付きでもう一度実行します。⚠️ 17.6.0 ではドライランでもデータベースに書き込みます。コピーに対して実行してください。 +- **西暦 1000 年より前の `datetime` の値を見つけて**書き換えます。アップグレード後は、そのような値を含む書き込みはすべて拒否されます。 + +チェックリスト全体:[17.6.0](https://objectstack.ai/docs/releases/v17/17-6#upgrade-checklist)。 + +### ObjectStack 17.7 [#objectstack-177] + +アップグレードの前に: + +- **廃止されたフィールドの残りの列を読む変換は**、そのフィールドがまだ宣言されている間に**実行します**。アップグレード後は、ランタイムのどの入り口もその列を返しません。読めるのは `os migrate unmapped-columns --object NAME` だけです。 +- **公開のままにする必要があるすべての公開フォームに `sharing.enabled: true` を追加します**。アプリと保存済みのオーバーレイの両方です。これがないと、フォームは `404 FORM_NOT_FOUND` を返します。 +- **17.7 より前にロールバックする可能性がある場合は、すべてのシークレットを再設定できる手段を残しておきます。** 17.7 で設定、ローテーション、または再封印されたシークレットは、以前のリリースでは開けない形式で書き込まれます。 +- **インメモリの起動ストアを置き換えます**(`OS_DATABASE_DRIVER=memory` または `memory://` の URL)。これは拒否されます。 + +リリースへの移行:`os migrate audit-metadata-bodies` を実行してから `--apply` 付きでもう一度実行し、監査とアクティビティのコピーから保存されたコンテンツハッシュを削除します。17.7 のドライランは書き込みを行わなくなりました。アップグレード前から保持しているメタデータのバージョントークンは、一度だけ `409 METADATA_CONFLICT` を返します。項目を読み直して新しいトークンを取得してください。 + +最初の起動の後: + +- **すべてのフローのシークレット**(受信フックの `secret` と `http` ノードの `signingSecret`)**をローテーションします**。起動ログが `sys_flow_credential` に移動したと報告してから行い、署名または検証する人に新しい値を渡してください。 +- **JWT の署名キーをローテーションし、非公開のままにすべき共有リンクを取り消して再発行します。** 以前の値は監査証跡にコピーが残っている可能性があり、アップグレードはそれを書き換えません。 +- **アップグレード前に一時停止したフローの実行を、再開、キャンセル、またはパージします。** 保存された変数には、まだ認証情報が平文で残っています。 +- **任意。17.7 より前にロールバックしないと決めてから:** `os secret rewrap` を実行してから `--apply` 付きでもう一度実行すると、古いシークレットが新しい形式で再封印されます。アップグレードの過程でこれが実行されることはありません。 +- `/api/settings/localization` に書き込むクライアントから、**廃止されたローカライズのキーを削除します**:`date_format`、`time_format`、`number_format`、`first_day_of_week`。いずれかを指定したリクエストは、全体が `400` で拒否されます。 +- **未確認のローカルメールアドレスへの外部サインインは拒否されます**(`account_not_linked`)。そのアカウントに黙ってリンクされることはなくなりました。 + +チェックリスト全体:[17.7.0](https://objectstack.ai/docs/releases/v17/17-7#upgrade-checklist)。 + +## Enterprise で追加されるもの [#enterprise-adds] + +Enterprise イメージはタグではなく、常に**ダイジェスト**で固定します。したがって、アップグレードはダイジェストの変更であり、ロールバックは以前のダイジェストに戻すことです。 + +1. リリースノートから**新しいダイジェスト**を取得し、ロールアウトの前にイメージの[リリースマニフェスト](/docs/deploy/docker#the-image-can-tell-you-what-it-is)を読みます。製品のメジャーラインが現在のものと同じであることを確認し、組み込まれたランタイムの互換性範囲が動いたかどうかを確認します。 +2. データベースをバックアップしてから、イメージのダイジェストを変更してスタックを再起動します。マイグレーションは、新しいレプリカがトラフィックを受ける前に完了まで実行されます。 +3. readiness が 200 を返し、レプリカが正常と報告することを確認します。ロールバック先として以前のダイジェストを残しておいてください。 + +- **Kubernetes**:アップグレードは同じくイメージのダイジェストの変更であり、新しいレプリカが提供を始める前にマイグレーションの Job が完了まで実行されます。[Kubernetes → ローリングアップグレードはダイジェストを変更する](/docs/deploy/kubernetes#rolling-upgrades-change-the-digest)を参照してください。 +- **エアギャップ**:新しいダイジェストを外部で検証し、内部に転送し、固定するダイジェストを変更して再起動します。以前のダイジェストは残しておいてください。エアギャップのネットワーク内では、それがロールバック先となる唯一のコピーかもしれません。[エアギャップ → イメージをネットワーク内に持ち込む](/docs/deploy/air-gapped#getting-the-image-inside-the-network)を参照してください。