すぐに exec format error で終了するコンテナは、ケイパビリティの追加、デバイスのマウント、トンネル設定の編集という間違った発想を誘います。そのどれも問題には届きません。カーネルはバイナリをそもそも実行できておらず、コンテナではこれはほぼ常に、イメージがホストとは異なる CPU アーキテクチャを対象にしていることを意味します。ルーティング、認証情報、権限について変更できることはすべて、プログラムが起動した後に起きるものであり、どのプログラムも起動していません。

設定エラーのように見える起動メッセージ

exec フォーマットの失敗は、アプリケーションが設定を一つも読む前に、ローダーから出ます。そのため設定の警告もトンネルのログも残りません。それを書く生きたプロセスが存在しないからです。失敗は驚くほど速く、コンテナのログに一行も現れないうちに起きることもよくあります。同じ短いメッセージは、バイナリが別のアーキテクチャ向けでも、別の OS 向けでも、破損していても現れます。

実行環境とアーキテクチャを、正確なイメージ参照とともに記録し、その参照が実際に何を提供しているかを確認します。Docker Desktop ではコンテナのプラットフォームはノート PC ではなく Linux 仮想マシンに属するため、ホストのアーキテクチャだけでは誤解を招きます。この参照のインデックス項目一覧を読み、タグページのマーケティング文ではなく実体を確認してください。

実行コマンドの前にイメージのインデックスを読む

レジストリがマルチプラットフォームの参照を公開すると、タグはインデックスを指し、各エントリはそれぞれの digest を持つプラットフォームを示します。隣のタグではなく検討中の参照を調べ、中身を推測せずインデックスを読みます。

docker buildx imagetools inspect ghcr.io/example/vpn:1.4
docker image inspect --format '{{.Os}}/{{.Architecture}}' ghcr.io/example/vpn:1.4

最初のコマンドは、その参照が公開しているプラットフォームを一覧します。二番目は、すでにローカルにあるイメージのプラットフォームを報告し、ランタイムが実行するのはこちらです。エントリが一つだけの参照は、どのホストも同じバイナリを受け取ることを意味し、そのタグは一度もビルドされたことのないマシンには役立ちません。

タグはバリアントを選ぶのであって、作るのではない

マルチプラットフォームのインデックスは、ランタイムが一致するエントリを選べるようにするもので、これは本当に便利であり、しばしば可搬性と取り違えられます。選択は検索であって翻訳ではありません。バリアントがなければ、選ぶものは何もありません。したがって同じタグでも、二つのホストでは二つの異なるバイナリに解決され、一つのホストでは使えるものに解決されません。

digest による固定は曖昧さを取り除き、後からのレビューも可能にします。digest はテストされたバイトを正確に特定するからです。その digest をデプロイ記録とともに残し、たまたま最新だったタグに頼らないようにします。

エミュレーションは取り決めであり、翻訳層ではない

サポートされたランタイムによるエミュレーションは、一部の非ネイティブなユーザー空間ワークロードを実行できるため、あるアーキテクチャが別のアーキテクチャのイメージを決して実行できないという絶対的な主張は誤りです。また、一般的な解決策でもありません。エミュレートされた実行は性能特性が異なり、起動するだけの些細なプロセスは、トンネルのワークロードが許容できる振る舞いをするかどうかを何も教えてくれません。

Docker のマルチプラットフォームガイドは、ネイティブビルド、クロスコンパイル、エミュレーションを別々の戦略として扱っており、それが選択肢を読む正しい方法です。どれか一つを意図的に選び、等価だと仮定するのではなく、その下で VPN ワークロードを評価してください。

権限は別の問題を解決する

ネットワーク管理のケイパビリティを与えても、TUN デバイスを渡しても、互換性のない実行ファイルを動かすことはできません。これらは実行中のプロセスが使う実行時権限であり、ファイルをすでに拒否したローダーには見えません。

逆も同じくらい当てはまります。ネイティブのバイナリは、特権、デバイスアクセス、リゾルバ統合、トンネルが必要とする再起動ポリシーについて何も証明しないため、起動するコンテナはまだ動作するコンテナではありません。まずプラットフォームの契約を確立し、次に文書化されたオプションを伴う実行時の契約へ進みます。

イメージの digest、サポート対象、設定を同じ記録にまとめておきます。アーキテクチャを静かに変えるタグ更新は、以前の digest が書き留められていれば、唯一の症状が見慣れたエラーとデバッグに費やす新しい午後である場合より、ずっと捕まえやすくなります。

関連記事