App Service における Docker User Namespace remapping issues について

12 minute read

はじめに

お世話になっております。App Service サポート担当の押田です。

本記事は 2022 年 6 月 30 日に公開されました Docker User Namespace remapping issues の日本語訳です。 翻訳内容には細心の注意を払っておりますが、もし原文内容と齟齬がある場合は、原文の内容を優先くださいませ。

Docker にはユーザ名前空間名前空間の再割り当て(userns とも呼ばれます)の概念があります。これを使用すると、コンテナ内のユーザーをホストマシン上の特権のないユーザーに再割当てできます。

Dockerfile で明示的に定義されたユーザーが、許可された ID 範囲の外の UID を持っている場合や、ファイル自体が大きな UID を持っている場合があります。これは、最終的にイメージがどのように構築されるか(ベースイメージ、サードパーティイメージなど)に依存するため、意図せずに発生する可能性があります。

許可された範囲外の UID が割当てされた場合、以下に示すようなエラーが発生する場合があります。

詳細については こちら のオフィシャルドキュメントもご参照ください。

重要事項

ユーザー名前空間の概念は、典型的な Linux の仕組みです。この記事で説明されているエラーは、App Service や Azure の問題ではありません。これは、ユーザー名前空間と再割当てをサポートし、マシン上の設定範囲外の ID を作成しようとしているマシンでも再現できます。

注意: App Service において、ID の範囲の変更はできません。非常に大きな ID の解決は、最終的にはイメージの開発者または管理者にて行う必要があります。

エラー

エラーの種類

ユーザー、グループまたはファイルが大きな UID または、GUID に再割当てされる場合、以下のようなエラーを目にする可能性があります。

  • failed to register layer: Error processing tar file(exit status 1): Container ID 1000000 cannot be mapped to a host IDErr: 0, Message: failed to register layer: Error processing tar file(exit status 1): Container ID 1000000 cannot be mapped to a host ID
  • OCI runtime create failed: container_linux.go:380: starting container process caused: setup user: cannot set uid to unmapped user in user namespace: unknown

様々なパターンのメッセージがありますが、根本原因としては、uid を ユーザー名前空間に再割当てできないことに起因します。

エラーの発生箇所

App Sericeでは、イメージの pull 実行時に発生します。これらのエラーが発生した場合、pull に失敗し、サイトアクセス時に Application Error :( が表示されたり、HTTP ステータス 502 が返却されることになります。

以下は、pull 失敗時のエラー例となります。

2022-07-01T00:02:29.030Z INFO  - 6f199d2c4d2f Extracting 32KB / 308KB
2022-07-01T00:02:29.930Z INFO  - 6f199d2c4d2f Extracting 192KB / 308KB
2022-07-01T00:02:30.500Z INFO  - 6f199d2c4d2f Extracting 224KB / 308KB
2022-07-01T00:02:32.193Z INFO  - 6f199d2c4d2f Extracting 256KB / 308KB
2022-07-01T00:02:34.247Z INFO  - 6f199d2c4d2f Extracting 308KB / 308KB
2022-07-01T00:02:35.021Z INFO  - 6f199d2c4d2f Extracting 308KB / 308KB
2022-07-01T00:02:48.845Z INFO  - 6f199d2c4d2f Pull complete
2022-07-01T00:02:48.874Z INFO  - d163dfb13dd8 Extracting 193B / 193B
2022-07-01T00:02:48.876Z INFO  - d163dfb13dd8 Extracting 193B / 193B
2022-07-01T00:02:49.002Z INFO  - d163dfb13dd8 Pull complete
2022-07-01T00:02:49.017Z INFO  - 8820d67a46eb Extracting 202B / 202B
2022-07-01T00:02:49.019Z INFO  - 8820d67a46eb Extracting 202B / 202B
2022-07-01T00:02:49.113Z INFO  -  
2022-07-01T00:02:49.114Z ERROR - failed to register layer: Error processing tar file(exit status 1): Container ID 1000000 cannot be mapped to a host IDErr: 0, Message: failed to register layer: Error processing tar file(exit status 1): Container ID 1000000 cannot be mapped to a host ID
2022-07-01T00:02:49.124Z INFO  - Pull Image failed, Time taken: 0 Minutes and 55 Seconds

これらのログは ログストリーム 機能や、Kudu にて _docker.log. で終わるファイル名のログファイルから確認することができます。

回避方法

ファイルの特定

次の操作は App Service 上ではなく、ローカル環境で実施する必要があります。

問題のあるコンテナイメージをローカル環境で実行し find / \( -uid 1000000 \) -ls 2>/dev/null を shell から実行します。

1000000 は実際に pull 失敗時のエラーメッセージに含まれる値に置き換えます。

理想的には、以下のように表示されるはずです。この場合、UID が大きすぎるユーザーが所有するファイルがあります。

# find / \( -uid 1000000 \)  -ls 2>/dev/null
  4758012      0 -rw-r--r--   1 veryhigh veryhigh        0 Jun 30 21:24 /var/www/html/file-with-high-id

注意: もし、大きな UID がコンテナ内の USER として定義されている場合、全てのファイルが該当のユーザーに所有されていると表示される場合があります。

ファイルの確認

次の操作は App Service 上ではなく、ローカル環境で実施する必要があります。

特定のファイルが特定できた場合、次のコマンドを実行することで、大きな ID マッピングが実行されていることがわかります。

ls -ln file-with-high-id

サンプル実行例:

# ls -ln file-with-high-id
-rw-r--r-- 1 1000000 1000000 0 Jun 30 21:24 file-with-high-id

ファイルまたはユーザーの修正

このためには、ファイルの所有権を変更する必要があります。例えばファイルに対しては Dockerfile 内で chown コマンドを利用します。 もし、以下のようにユーザーがイメージ内に定義されている場合、以下のように割り当てられるIDをセットする必要があります。

RUN groupadd veryhigh -g 1000000
RUN useradd -r -u 1000000 -g veryhigh veryhigh
RUN touch file-with-high-id
RUN chown veryhigh:veryhigh file-with-high-id

もし、イメージがサードパーティ製の場合は、UID を使用しているメンテナーに連絡してください。あるいは、サードパーティ製イメージをベースとし、問題のあるファイルあるいはユーザーを変更したイメージを作成することも可能です。

問題を解消した後に、イメージを再ビルドし、デプロイすることができます。この作業はローカルで実施する必要があります。

NPM 利用プロジェクトにおける ユーザー名前空間再割当てエラー

特定の NPM バージョンでは、node_modules インストール時に、非常に大きな ID をファイルの所有者/作成者として割り当てることがあります。これは npm 9 系において導入されました。npm コミュニティにおいてもこの問題に関連するディスカッションが以下の 3 つのスレッドで確認できます。 一読することをお勧めします。

npm 9 系を利用する以下の Dockerfile を App Service にデプロイした場合、ユーザー名前空間再割当てに関するエラーが発生します。

注意: npm 5系でも発生することもあります。

(Dockerfile):

FROM node:18.9.0-alpine3.15

WORKDIR /app
COPY package.json ./
RUN npm i -g npm@9.1.1 && \
    npm i

COPY . ./

EXPOSE 8080 

CMD [ "node", "/app/server.js" ]

App Service で発生するエラー:

ERROR - failed to register layer: Error processing tar file(exit status 1): Container ID 1516583083 cannot be mapped to a host IDErr: 0, Message: failed to register layer: Error processing tar file(exit status 1): Container ID 1516583083 cannot be mapped to a host ID

前述のアプローチと同様に、ローカル環境で調査する必要があります。 ただし、find コマンドでは、問題を起こしている UID を特定することができません。 ID 変更が発生しているためです。(詳細については前述のスレッド内に書かれています。)

NPM と node_modules に関連する問題がわかっているため、以下のアプローチで特定します。 実際には、利用するプロジェクト内の node_modules のパスに置き換えてください。

node_modules:

FILES=$(find /app/node_modules/ ! -user root)
ls -lrta $FILES

エラーに示される ID と一致する以下の所有者名を確認することができます。

-rw-r--r--    1 501      dialout       1490 Nov 29 17:18 /app/node_modules/cookie-signature/Readme.md
-rw-r--r--    1 501      dialout        695 Nov 29 17:18 /app/node_modules/cookie-signature/History.md
-rw-r--r--    1 501      dialout         29 Nov 29 17:18 /app/node_modules/cookie-signature/.npmignore
-rw-r--r--    1 15165830 root          1070 Nov 29 17:18 /app/node_modules/content-type/package.json
-rw-r--r--    1 15165830 root          4809 Nov 29 17:18 /app/node_modules/content-type/index.js
-rw-r--r--    1 15165830 root          2796 Nov 29 17:18 /app/node_modules/content-type/README.md
-rw-r--r--    1 15165830 root          1089 Nov 29 17:18 /app/node_modules/content-type/LICENSE
-rw-r--r--    1 15165830 root           436 Nov 29 17:18 /app/node_modules/content-type/HISTORY.md
-rw-r--r--    1 501      dialout        703 Nov 29 17:18 /app/node_modules/asynckit/stream.js
-rw-r--r--    1 501      dialout       1751 Nov 29 17:18 /app/node_modules/asynckit/serialOrdered.js
-rw-r--r--    1 501      dialout        501 Nov 29 17:18 /app/node_modules/asynckit/serial.js
-rw-r--r--    1 501      dialout       1017 Nov 29 17:18 /app/node_modules/asynckit/parallel.js

回避策としては, chown コマンドによってディレクトリ配下のファイルの所有者を変更します。

重要: chown を実行する場合、それは npm install を実行するレイヤと必ず同じレイヤで実行する必要があります。もし 2 つの異なるレイヤで実行された場合、所有者の変更は正しく動かず、名前空間再割当てに関するエラーは解消しません。

(Do)

RUN npm i -g npm@9.1.1 && \
    npm i && \
    find /app/node_modules/ ! -user root | xargs chown root:root

(Dont)

RUN npm i -g npm@9.1.1 && \
    npm i 

RUN find /app/node_modules/ ! -user root | xargs chown root:root

UID を特定できない場合はどうすると良いか?

コンテナを Pull し作成エラーとなった際に、エラーメッセージから UID が特定できない場合もあります。

常にそうなるわけではありませんが、同様のアプローチ で解決することができます。

なぜなら、 find /app/node_modules/ ! -user root を実行しているためです。例えば、ユーザーが 50x の範囲にあり、グループ dialout に含まれる node_module 関連のファイルが表示される場合があります。 ファイルシステム全体を検索すると、通常、これらはユーザー/グループとして、 rootnode 以外に表示される唯一のものです。 このようなシナリオの場合、50x ユーザーと dialout グループを root (または、許容範囲内にあるUID) に変更することで問題が解消します。これは、NPM インストール実行時にユーザー切り替えが起きているためとなります。NPM に関連する当セクションの先頭に記載したスレッドのリンクを参照ください。

2023 年 02 月 15 日時点の内容となります。
本記事の内容は予告なく変更される場合がございますので予めご了承ください。