Vercel Container Registry公開リポジトリ: Sandboxイメージをチーム間で安全に共有する方法

HAGO··schedule12분
공유

Vercel Container Registry(VCR)は2026年8月7日、リポジトリを公開に切り替えられるようになった。ただし、ここでいう「公開」は匿名のインターネット利用者が自由にpullできるという意味ではない。Vercelの発表は「Vercelアカウントを持つ誰でも」、詳細ドキュメントは「すべてのVercelチーム」が読み取れると説明している。公開ユーザーに許可されるのはpullと利用であり、push、削除、設定変更はできない。リポジトリの初期状態も非公開のままだ。

実運用では、この読み取り権限だけを見て判断しないほうがいい。リポジトリの可視性、イメージを特定するタグやdigest、Sandbox内でコードを実行する権限は別々の境界にある。公開が読み取り専用でも、イメージの中身まで安全になるわけではない。非公開でも、latestが別の内容を指すようになれば再現性は失われる。

この記事は2026年8月9日に確認したVercelの発表、公開・共有リポジトリの公式文書、Sandboxイメージの公式文書を基準にしている。VCRは現行文書でBetaと表示されている。例はnpmで確認した@vercel/sandbox 3.0.0とVercel CLI 58.9.0を対象にしたが、実際のVercelアカウントでは実行していない。

何が変わり、何が変わらないのか

従来のVCRは、指定したVercelチームにリポジトリの読み取り権限を共有できた。公式文書にある上限は1リポジトリあたり100チームだ。新しい公開設定は、この個別リストの代わりにすべてのVercelチームへ読み取りを許可する。

ダッシュボードではImagesから対象リポジトリを選び、SettingsのPublic Accessを切り替える。確認時にはリポジトリ名の入力が必要になる。CLIでは次の形が文書化されている。

# Vercel CLI 58.9.0で確認した公式コマンド形式
vercel vcr config my-repository --public true

# 非公開へ戻す
vercel vcr config my-repository --public false

設定単位はタグではなくリポジトリ全体だ。公開すると、そこにあるすべてのタグとイメージが読み取り対象になる。安定版のランタイム、社内デバッグ用ツール、試験中のタグを同じリポジトリに置いているなら、公開ボタンを押す前に分離したほうがいい。

文書で確認できる権限境界

公開または明示的に共有されたチームは、イメージをpullしてVercel Sandboxで利用できる。一方でpush、削除、共有設定の変更、別チームへの再共有はできない。この部分は公式文書に明記されている。

ただし、読み取り専用は内容の無害性を保証しない。利用者はイメージレイヤーを取得し、そのコードを実行できる。ビルド時にトークンや.envをレイヤーへコピーしてしまえば、その情報も読める。秘密情報はイメージへ焼き込まず、別の実行時注入経路で渡す必要がある。

公開と匿名pullを同一視しない

発表と文書はいずれもVercelのアカウントまたはチームを前提にしている。認証なしの一般的なOCIクライアントまでサポートすると断定できる記述ではない。自動化では「公開だから認証不要」と考えず、どのVercelチームとして認証しているのかを記録する。

匿名配布が要件なら、Vercelの認証情報が存在しないクリーンなクライアントで別途試験する。開発者のPCでpullできたという結果は、すでにログイン済みだったことを示しているだけかもしれない。

実行境界を分けて考える

公開VCRイメージをSandboxで使う経路は次のようになる。

開発者またはCI
  -> Dockerfile / Containerfile
  -> ローカルまたはCIのビルダー
  -> 元プロジェクトの書き込み権限でpush
  -> 1つのVercelプロジェクトが所有するVCR
  -> private / shared / publicの読み取り判定
  -> 利用側Vercelチームの認証
  -> Sandbox.create({ image })
  -> microVMのファイルシステム
  -> runCommand()で開始するアプリケーションプロセス

VCRリポジトリはプロジェクトに属する。同じチーム内の別プロジェクトから使う場合でも、公式文書では自分のチームへの共有が必要とされている。単純なリポジトリ名は認証中のプロジェクトを基準に解決され、別チームや別プロジェクトのイメージにはteam/project/repository:tagのようなチームスコープ参照を使う。

公開設定が広げるのはVCRの読み取り判定だけだ。利用側アプリケーションの認可、Sandboxのネットワーク設定、イメージ内部のプログラムの正しさは保証しない。

障害の所有者を見分ける

ビルダーはDockerfileとレイヤーを作る。元のVercelプロジェクトはリポジトリと可視性を所有する。VCRはOCIイメージを保管し、参照を解決する。Sandboxは選んだイメージから環境を起動する。最終的なexit codeと標準出力は、その環境で始めたプロセスが返す。

not_foundならチーム名、プロジェクト名、リポジトリ、タグ、digest、共有設定を調べる。Sandboxがreadyになった後のコマンド失敗なら、CPUアーキテクチャ、権限、ファイル、パッケージ、アプリケーション処理を確認する。外部通信の失敗はSandbox Firewallや接続先の認証も候補になる。全部を「公開リポジトリの不具合」と呼ぶと、必要な情報が消えてしまう。

現行のSandbox文書では、カスタムイメージにlinux/amd64が必要だ。また、DockerのENTRYPOINTとCMDは無視されると説明されている。起動処理はrunCommand()で明示する。この条件は公開・非公開の設定とは関係がない。

タグだけでなくdigestを固定する

公式の短い例では:latestが使われている。しかしタグは、同じ名前のまま別のmanifestへ移動できる。再現可能な環境が必要なら、team/project/repository@sha256:...というdigest参照を使う。Sandboxの文書もタグとdigestの両方をサポートしている。

Digestは内容の同一性を固定するが、安全性を証明しない。同じ脆弱なパッケージを正確に再実行することもできる。ビルド元の確認、脆弱性スキャン、承認済みdigestの管理、廃止手順は別に必要だ。

利用側で回帰プローブを実行する

次のTypeScriptは、変更可能なタグを拒否し、承認したdigestからSandboxを作る。さらにNode.jsのmajorと、ビルド時に/opt/image-versionへ書いた識別子を確認する。インストールコマンドだけではなく、実際のイメージ契約を試すコードだ。ただし、この記事の作業環境では実行していない。

import assert from 'node:assert/strict';
import { Sandbox } from '@vercel/sandbox';

const image = process.env.VCR_IMAGE_DIGEST;
assert.match(
  image ?? '',
  /^[a-z0-9-]+\/[a-z0-9-]+\/[a-z0-9._-]+@sha256:[a-f0-9]{64}$/,
  'team/project/repositoryのdigest参照を指定してください',
);

const sandbox = await Sandbox.create({ image: image! });

try {
  const probe = await sandbox.runCommand(
    'sh',
    '-lc',
    'node --version && cat /opt/image-version',
  );
  const stdout = await probe.stdout();
  const stderr = await probe.stderr();

  assert.equal(probe.exitCode, 0, stderr);
  assert.match(stdout, /^v24\./m);
  assert.ok(stdout.includes(process.env.EXPECTED_IMAGE_MARKER ?? ''));
} finally {
  await sandbox.stop();
}

正規表現は意図的に厳しくしている。実際の有効なslug文字に合わせた調整は必要かもしれない。重要なのはこの文字集合ではなく、本番経路で可変タグを拒否することだ。承認digestをユーザー入力から受け取るのではなく、デプロイ設定で管理する。

@vercel/sandbox 3.0.0を導入しただけでは、Vercelアカウントの権限やイメージ互換性は検証されない。SDK majorを上げるときは、Sandbox.create()の引数とコマンド結果APIを公式リファレンスで再確認する。

公開までを段階化する

最初にCIでイメージをbuildし、検査する。VCRへバージョンタグをpushしてdigestを保存する。リポジトリを指定チームだけに共有した状態で、利用予定プロジェクトから回帰プローブを動かす。成功後に公開へ切り替え、別チームのテスト主体から同じdigestをpull・実行できるかを確かめる。

後から非公開へ戻しても、すでに取得されたレイヤーは回収できない。秘密情報を含めてしまった場合は、その秘密を失効して再発行する。非公開化は将来の読み取りを止める操作であり、漏れたデータを消す操作ではない。

性能向上は実測せずに語れない

今回の発表には、pull時間、Sandbox起動時間、指定チーム共有との速度比較がない。公開にすると速くなるという主張は、公式資料からは導けない。カスタムイメージへツールを事前配置すれば実行時インストールを省ける、というSandbox文書の説明も、特定アプリの総時間を測った結果ではない。

イメージサイズ、レイヤーキャッシュ、リージョン、初回pull、プラットフォームの準備、起動後のコマンドが遅延へ影響しうる。全体を1本のタイマーで囲むだけでは、どこが変わったか分からない。

観測できる時刻を残す

create_requested、sandbox_ready、probe_started、probe_first_output、probe_completed、sandbox_stoppedを記録する。同じdigestで初回と連続10回を分け、各試行にdigest、SDK版、CLI版、リージョン、コマンド、exit codeを紐づける。

比較対象にはVercel Managed Imageと従来の実行時インストール方式を置く。初回と繰り返しのp50・p95、失敗率、image_not_readyやnot_foundの件数を分ける。Vercelがダウンロード専用の計測値を公開していないなら、Sandbox.create()全体を「download time」と呼ばない。その区間には参照解決、認可、イメージ準備、microVM起動が含まれる可能性がある。

出力の同等性も確認する。小さいイメージが速く起動しても、必要なバイナリが欠けていれば改善ではない。上のversionとmarkerは最低限の確認であり、実際のアプリではHTTP smoke testや主要機能の回帰試験を追加する。

主張の判定と安全な言い換え

確認済み: VCRは標準で非公開。公開すると、すべてのVercelチームがリポジトリ内の全イメージとタグを読み取れるが、pushと削除はできない。

条件付きで正しい: 「誰でも使える」はVercelアカウントとチームの範囲でのみ正しい。「認証されたVercelチームがpullできる」と書くほうが正確だ。

確認済み: 公開範囲は選んだタグではなくリポジトリ全体。公開前に全タグとレイヤーを検査する。

条件付きで正しい: カスタムイメージは実行時インストールを省ける。準備時間が短くなるかは初回と繰り返しを分けて測る。

未確認: 公開リポジトリのほうが指定チーム共有より速い。今回の公式資料にも、この調査にも測定根拠はない。

言い過ぎ: 「読み取り専用だから安全」は、レイヤーの情報漏えいとコード実行を無視している。読み取り専用は改変を制限するが、内容の安全性は保証しない。

公開リポジトリは、100チームを超えて共通のSandbox基盤を配る場面で便利だ。社内専用イメージなら非公開か指定チーム共有を初期選択にする。公開が必要なら、専用リポジトリへ分離し、digestを固定し、別チームからpullと実行を試した後で開く。設定は短いコマンドで変えられるが、信頼するかどうかは利用側が決めなければならない。

公式資料

공유

댓글 (0)

첫 댓글을 남겨주세요.