dockerコンテナが仮想化する7つの名前空間の分離と統合方法について

generated by ChatGPT

## 0. 動作環境

```bash
user@USER:~/work/docker$ uname -a
Linux USER 5.15.153.1-microsoft-standard-WSL2 #1 SMP Fri Mar 29 23:14:13 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
```

## 1. Dockerコンテナによる仮想化

Dockerコンテナは、Linuxの名前空間機能を利用して、以下の名前空間（namespace）を分離することができます。これにより、コンテナがホスト環境や他のコンテナから独立して動作することが可能になります。

1. **PID（プロセスID）名前空間**:
   - プロセスIDの空間を分離します。各コンテナは自身のプロセスツリーを持ち、他のコンテナやホストのプロセスに影響されません。これにより、コンテナ内で実行されるプロセスはホストや他のコンテナから見えず、逆も同様です。

2. **ネットワーク名前空間**:
   - ネットワークスタック（インターフェース、IPアドレス、ルーティングテーブルなど）を分離します。各コンテナは独自の仮想ネットワークインターフェースを持ち、ホストや他のコンテナと隔離されたネットワーク空間で動作します。

3. **UTS（UNIX Timesharing System）名前空間**:
   - ホスト名とNIS（Network Information System）ドメイン名を分離します。これにより、各コンテナは独自のホスト名とドメイン名を設定でき、ホストの名前に影響を与えません。

4. **マウント名前空間**:
   - ファイルシステムのマウントポイントを分離します。コンテナは独自のファイルシステムビューを持ち、ホストや他のコンテナとは異なるマウントポイントを使用できます。これにより、コンテナ内でのファイル操作がホストに影響を及ぼしません。

5. **IPC（Interprocess Communication）名前空間**:
   - プロセス間通信のリソース（セマフォ、メッセージキュー、共有メモリ）を分離します。これにより、コンテナ内のプロセス間通信は他のコンテナやホストから分離され、影響を受けません。

6. **ユーザー名前空間**:
   - ユーザーおよびグループIDを分離します。コンテナ内ではホストと異なるユーザーID/グループIDマッピングが可能で、特権の分離を実現します。これにより、コンテナ内での管理者権限がホストに影響を及ぼさないように設定できます。

7. **Cgroup名前空間**:
   - コントロールグループを分離し、リソースの割り当てと制限（CPU、メモリ、I/Oなど）を個々のコンテナに対して独立して管理できます。

これらの名前空間の分離により、コンテナはホストシステムや他のコンテナと独立して、安全かつ効率的に動作することが可能となります。

## 2. PID名前空間の分離と統合

### 1. 適当なサンプルイメージの作成

まず、`ubuntu`ベースで`bash`を起動するシンプルなコンテナイメージを作成します。

#### Dockerfileの内容
```Dockerfile
# Dockerfile
FROM ubuntu:latest

RUN apt update && apt install -y iproute2

CMD ["bash"]# Dockerfile
```

この`Dockerfile`を使ってイメージをビルドします。

#### イメージのビルドコマンド
```bash
docker build -t my-bash-container .
```

### 2. 分離されたプロセス空間でコンテナを起動する

このコマンドでコンテナを起動すると、プロセス空間がホストと分離され、コンテナ内部で`bash`が起動します。

#### 分離された状態での起動コマンド
```bash
docker run --rm -it --name separated-bash-container my-bash-container
```

### 3. プロセス空間をホストと統合した状態でコンテナを起動する

プロセス空間をホストと共有するためには、`--pid=host`オプションを使ってコンテナを起動します。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --pid=host --name integrated-bash-container my-bash-container
```
```bash
root@6cbfe32375ce:/# ps
    PID TTY          TIME CMD
  18217 pts/0    00:00:00 bash
  18249 pts/0    00:00:00 ps
```


### 各オプションの意味
- `--rm`: コンテナが終了したときに自動的に削除されます。
- `-it`: コンテナをインタラクティブに実行し、ターミナルをアタッチします（標準入力を受け付ける）。
- `--name`: コンテナの名前を指定します。
- `--pid=host`: ホストのプロセス空間を共有します（統合された状態）。

これで、`bash`が起動するサンプルコンテナをホストと分離された状態、または統合された状態で実行することができます。

## 3. ネットワーク名前空間の分離と統合
ネットワーク空間の分離と統合を確認するために、`my-bash-container`イメージを使用してコンテナを起動するコマンドを示します。分離されたネットワーク空間ではコンテナがホストとは異なるネットワークスタックを持ち、統合された場合はホストのネットワークスタックを共有します。


### 1. ネットワーク空間が分離された状態での起動

デフォルトでコンテナのネットワークはホストから分離されています。コンテナ内部から`ip a`や`ifconfig`コマンドを使って確認することで、独自のネットワークインターフェースがあることを確認できます。

#### 分離された状態での起動コマンド
```bash
docker run --rm -it --name separated-network-container my-bash-container
```

このコマンドでコンテナ内に入った後、ネットワークインターフェースを確認して、ホストとは異なるIPアドレスやインターフェースが設定されていることを確認します。

```bash
# コンテナ内で実行
ip a
```

### 2. ネットワーク空間がホストと統合された状態での起動

ネットワーク空間をホストと共有するには、`--network host`オプションを使用します。これにより、コンテナはホストのネットワークインターフェースを直接使用し、ホストと同じネットワーク設定が適用されます。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --network host --name integrated-network-container my-bash-container
```

このコマンドでコンテナ内に入った後、再びネットワークインターフェースを確認することで、ホストと同じネットワーク設定で動作していることを確認できます。

```bash
# コンテナ内で実行
ip a
```

### 各コマンドの確認ポイント

- **分離された状態**: コンテナ内のIPアドレスやインターフェースがホストとは異なることを確認できます（例: `eth0`に異なるIPアドレスが付与されている）。
  
- **統合された状態**: コンテナ内でホストのネットワーク設定が見えることを確認できます（例: ホストのインターフェース名やIPアドレスが表示される）。

これらのコマンドを実行することで、ネットワーク空間の分離・統合がどのように機能するかを確認できます。

## 4. UTS（UNIX Timesharing System）名前空間の分離と統合

UTS（UNIX Timesharing System）名前空間の分離と統合を確認するために、`my-bash-container`イメージを使用してコンテナを起動する方法を紹介します。UTS名前空間はホスト名とNISドメイン名の分離を管理します。UTS名前空間を分離することで、コンテナは独自のホスト名を持ち、ホストの設定に影響を与えません。統合された状態ではホスト名を共有します。

### 1. UTS名前空間が分離された状態での起動

デフォルトでコンテナのUTS名前空間はホストから分離されています。コンテナ内部で`hostname`コマンドを使用して確認することで、独自のホスト名があることを確認できます。

#### 分離された状態での起動コマンド
```bash
docker run --rm -it --name separated-uts-container my-bash-container
```

このコマンドでコンテナ内に入った後、ホスト名を確認します。

```bash
# コンテナ内で実行
hostname
```

コンテナ内のホスト名は、コンテナ名（例: `separated-uts-container`）やランダムに割り当てられたホスト名が表示され、ホストマシンのホスト名とは異なります。

### 2. UTS名前空間がホストと統合された状態での起動

UTS名前空間をホストと共有するには、`--uts=host`オプションを使用します。これにより、コンテナはホストのUTS名前空間を共有し、同じホスト名を持ちます。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --uts=host --name integrated-uts-container my-bash-container
```

このコマンドでコンテナ内に入った後、再びホスト名を確認します。

```bash
# コンテナ内で実行
hostname
```

### 各コマンドの確認ポイント

- **分離された状態**: コンテナ内のホスト名がホストマシンと異なることを確認します。コンテナは独自のホスト名を持っているため、変更してもホストには影響しません。

- **統合された状態**: コンテナ内でホストのホスト名がそのまま見えることを確認します。ホスト名を変更すると、ホストにも影響があります。

これらのコマンドを使うことで、UTS名前空間の分離と統合がどのように動作するかを確認できます。

```bash
user@USER:~/work/docker$ hostname
USER
user@USER:~/work/docker$ docker run --rm -it --name separated-uts-container my-bash-container
root@1831cc4efa3e:/# hostname
1831cc4efa3e
user@USER:~/work/docker$ docker run --rm -it --uts=host --name integrated-uts-container my-bash-container
root@USER:/# hostname
USER
```

## 5. マウント名前空間の分離と統合

マウント名前空間の分離と統合を確認するために、`my-bash-container`イメージを使用してコンテナを起動するコマンドを紹介します。マウント名前空間を分離すると、コンテナ内でのファイルシステムの変更（マウントやアンマウント）はホストに影響を与えません。逆に、マウント名前空間をホストと共有すると、コンテナ内で行ったマウントやアンマウントがホストにも影響を与えます。

### 1. マウント名前空間が分離された状態での起動

デフォルトでDockerコンテナのマウント名前空間はホストから分離されています。コンテナ内でのファイルシステムの変更は、ホストには影響しません。

#### 分離された状態での起動コマンド
```bash
docker run --rm -it --name separated-mount-container my-bash-container
```

このコマンドでコンテナ内に入った後、`mount`コマンドや`ls /mnt`などのコマンドを使ってマウントポイントを確認することで、独自のマウント空間があることを確認します。

```bash
user@USER:~/work/docker$ docker run --rm -it --name separated-mount-container my-bash-container
root@8069a840010f:/# ls /dev
console  core  fd  full  mqueue  null  ptmx  pts  random  shm  stderr  stdin  stdout  tty  urandom  zero# コンテナ内で実行
mount
# または特定のディレクトリを確認
ls /mnt
```

### 2. マウント名前空間がホストと統合された状態での起動

マウント名前空間をホストと共有するためには、`--mount`オプションで特定の共有設定をするのではなく、`--pid=host`や`--uts=host`と異なり、Dockerでは直接のオプション指定がないため、`--privileged`モードを使って起動します。このモードで起動すると、マウント操作がホストに影響するようになります。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --privileged --name integrated-mount-container my-bash-container
```

このコマンドでコンテナ内に入った後、同じように`mount`コマンドを使用してマウントポイントを確認し、ホストと統合されたファイルシステムが見えるかどうかを確認します。また、コンテナ内でファイルシステムのマウントやアンマウントを行うことで、ホスト側への影響を直接確認することが可能です。

```bash
user@USER:~/work/docker$ sudo docker run --rm -it --privileged --name integrated-mount-container my-bash-container
root@80e7adcb99f7:/# ls /dev/
（ホストのデバイスが見える）
```

### 各コマンドの確認ポイント

- **分離された状態**: コンテナ内で行ったマウント操作はホストに反映されず、独自のファイルシステムを持っていることを確認できます。

- **統合された状態**: コンテナ内で行ったマウントやアンマウント操作がホストに影響を与えることを確認します。このため、注意して操作する必要があります。

これらのコマンドを使うことで、マウント名前空間の分離と統合がどのように機能するかを確認できます。

`--privileged`モードは、Dockerコンテナに通常のコンテナよりも高い権限を与えるオプションです。このモードを有効にすることで、コンテナはホストに近いレベルのアクセス権を持ち、さまざまな制限が解除されます。以下は`--privileged`モードの主な特徴とその影響です。

#### `--privileged`モードの主な特徴

1. **すべてのケーパビリティ（Capabilities）が付与される**:
   - Linuxには「ケーパビリティ」と呼ばれる機能があり、特定の特権操作（例えば、ネットワークインターフェースの設定変更やシステムクロックの変更など）を許可する細かな制御が可能です。`--privileged`モードでは、これらのケーパビリティがすべてコンテナに付与され、通常のコンテナよりも多くのシステム操作が可能になります。

2. **デバイスへのアクセスが許可される**:
   - `--privileged`モードでは、コンテナからホストのデバイス（例えば、USBデバイスやシリアルポートなど）へのアクセスが可能になります。通常のコンテナではアクセスできないデバイスも操作できるようになります。

3. **セキュリティ制限の緩和**:
   - 通常、コンテナはAppArmor、SELinux、Seccompなどのセキュリティプロファイルで保護されていますが、`--privileged`モードではこれらの制限が解除されるか、緩和されます。これにより、コンテナはホストに対してほぼ制限なしで操作できます。

4. **名前空間の制限が解除される**:
   - 一部の名前空間（例えば、マウント名前空間など）でホストとの統合が可能になります。これにより、コンテナ内でのマウント操作がホストに直接影響を与えることができます。

5. **カーネル機能の利用**:
   - コンテナは通常アクセスできないカーネル機能やリソースにもアクセス可能になります。例えば、システムコールの制限が緩和され、特定のカーネルモジュールを操作することができます。

#### `--privileged`モードの使用例と注意点

- **使用例**:
  - ハードウェアアクセスが必要なアプリケーション（USBデバイスを操作するコンテナなど）。
  - ネットワーク設定やシステム設定を操作する必要があるコンテナ。
  - 特定の開発やデバッグ環境での使用。

- **注意点**:
  - **セキュリティリスク**: `--privileged`モードではコンテナがホストとほぼ同等の権限を持つため、セキュリティリスクが非常に高くなります。悪意のある操作や設定ミスがホスト全体に影響を及ぼす可能性があります。
  - **ホストへの影響**: コンテナ内での操作が直接ホストに影響を与えるため、注意が必要です。

`--privileged`モードは、強力な機能ですがセキュリティリスクも高いため、使用は最小限に抑え、信頼できる環境でのみ使用することが推奨されます。

## 6. IPC名前空間の分離と統合
IPC（Interprocess Communication）名前空間の分離と統合を確認するために、`my-bash-container`イメージを使用してコンテナを起動するコマンドを示します。IPC名前空間は、プロセス間通信（セマフォ、メッセージキュー、共有メモリなど）を分離する役割を持っています。分離された状態では、コンテナのIPCリソースは他のコンテナやホストと共有されません。統合された状態では、コンテナがホストのIPCリソースを共有します。


### 0. 確認用メッセージキューの作成
ホスト側で適当なメッセージキューを作成するには、Linuxの`ipcs`コマンドと`ipcmk`コマンドを使用できます。以下は、ホスト側でメッセージキューを作成する手順です。

### メッセージキューの作成手順

1. **メッセージキューの作成**:
   `ipcmk`コマンドを使ってメッセージキューを作成します。このコマンドは`util-linux`パッケージに含まれているため、インストールされていない場合はインストールが必要です。

   ```bash
   # メッセージキューを作成
   ipcmk -Q
   ```

2. **メッセージキューの確認**:
   `ipcs`コマンドを使って、作成したメッセージキューが正しく作成されたかを確認します。

   ```bash
   # メッセージキューの一覧を表示
   user@USER:~/work/docker$ ipcs
   ------ Message Queues --------
   key        msqid      owner      perms      used-bytes   messages    
   0x29a1a440 0          user      644        0            0           
   
   ------ Shared Memory Segments --------
   key        shmid      owner      perms      bytes      nattch     status      
   
   ------ Semaphore Arrays --------
   key        semid      owner      perms      nsems     
   ```

### コマンドの説明

- `ipcmk -Q`: メッセージキューを作成します。作成後、メッセージキューIDが表示されます。
- `ipcs -q`: 現在のシステムに存在するすべてのメッセージキューを表示します。

この手順を行うと、ホスト上でメッセージキューが作成され、IDが付与されます。このIDを用いて、コンテナからのアクセスやホスト側でのメッセージの送受信を行うことができます。

### 1. IPC名前空間が分離された状態での起動

デフォルトでDockerコンテナのIPC名前空間はホストから分離されています。コンテナ内で使用されるIPCリソース（例えば共有メモリ）はホストから独立しています。

#### 分離された状態での起動コマンド
```bash
docker run --rm -it --name separated-ipc-container my-bash-container
```

このコマンドでコンテナ内に入った後、IPCリソース（例えば共有メモリセグメント）を確認するために`ipcs`コマンドを使用します。コンテナ内で作成されたIPCリソースはホストから見えず、また、ホストで作成されたリソースもコンテナからは見えません。

```bash
user@USER:~/work/docker$ docker run --rm -it --name separated-ipc-container my-bash-container
root@d9a757f95957:/# ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     
```

### 2. IPC名前空間がホストと統合された状態での起動

IPC名前空間をホストと共有するためには、`--ipc=host`オプションを使用します。これにより、コンテナはホストのIPC名前空間を使用し、ホストのプロセス間通信リソースを共有します。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --ipc=host --name integrated-ipc-container my-bash-container
```

このコマンドでコンテナ内に入った後、再び`ipcs`コマンドを使用してIPCリソースを確認します。ホストと同じIPCリソースが見えるようになります。

```bash
user@USER:~/work/docker$ docker run --rm -it --ipc=host --name integrated-ipc-container my-bash-container
root@e5ad97bd2345:/# ipcs

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x29a1a440 0          ubuntu     644        0            0           

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

------ Semaphore Arrays --------
key        semid      owner      perms      nsems     # コンテナ内で実行
ipcs
```

### 各コマンドの確認ポイント

- **分離された状態**: コンテナ内で作成したIPCリソース（共有メモリやメッセージキュー）はホストや他のコンテナからは見えませんし、アクセスもできません。

- **統合された状態**: コンテナ内で`ipcs`を実行すると、ホストで作成されたIPCリソースが見えます。また、コンテナ内でのIPC操作がホストのIPCリソースに影響を与えることを確認できます。

これらのコマンドを使用することで、IPC名前空間の分離と統合の効果を直接確認できます。

## 7. ユーザ名前空間の分離と統合

ユーザー名前空間の分離と統合を確認するために、`my-bash-container`イメージを使用してコンテナを起動するコマンドを紹介します。ユーザー名前空間を分離することで、コンテナ内のユーザーとホストのユーザーが異なるIDマッピングを持つようになり、特権分離が強化されます。統合された状態では、コンテナ内のユーザーとホストのユーザーが同じIDを共有します。

### 1. ユーザー名前空間が分離された状態での起動

デフォルトで、Dockerコンテナはユーザー名前空間を分離しませんが、`--userns=remap`オプションを使用してユーザー名前空間を有効にすることができます。このオプションを使うと、コンテナのユーザーはホスト上で異なるIDにマッピングされます。

#### 分離された状態での起動コマンド
```bash
# Dockerのユーザー名前空間リマッピングを有効にした状態で起動
docker run --rm -it --userns=remap --name separated-userns-container my-bash-container
```

このコマンドでコンテナ内に入った後、コンテナ内でのユーザーIDとグループIDを確認します。

```bash
# コンテナ内で実行
id
```

この出力により、コンテナ内のユーザーID（UID）とグループID（GID）がホストのものと異なることが確認できます。通常、UID 0（root）がホストの非特権ユーザーにマッピングされます。

### 2. ユーザー名前空間がホストと統合された状態での起動

ユーザー名前空間をホストと共有するためには、`--userns=host`オプションを使用します。これにより、コンテナ内のユーザーIDはホストのユーザーIDと一致します。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --userns=host --name integrated-userns-container my-bash-container
```

このコマンドでコンテナ内に入った後、再びユーザーIDとグループIDを確認します。

```bash
# コンテナ内で実行
id
```

### 各コマンドの確認ポイント

- **分離された状態**: コンテナ内のユーザーIDとグループIDがホストのIDとは異なっていることを確認します。例えば、コンテナ内のrootユーザーがホストの非特権ユーザーとしてマッピングされている場合があります。

- **統合された状態**: コンテナ内のユーザーIDとグループIDがホストのIDと一致することを確認します。これにより、コンテナ内のユーザー操作がホストと直接連動します。

これらのコマンドを使用して、ユーザー名前空間の分離と統合の効果を直接確認できます。ユーザー名前空間の分離は、セキュリティを強化し、ホストとコンテナ間の特権を適切に分離するために役立ちます。

`--userns=remap`オプションの利用については、Dockerの設定でユーザー名前空間のリマッピングが事前に設定されている必要があります。エラーメッセージ「`docker: --userns: invalid USER mode.`」は、Dockerのユーザー名前空間設定が正しく構成されていないことが原因で発生します。

### Dockerでユーザー名前空間リマッピングを設定する手順

1. **Dockerデーモンの設定ファイルの編集**:

   Dockerの設定ファイル（通常は`/etc/docker/daemon.json`）にユーザー名前空間リマッピングの設定を追加します。以下のように設定します。

   ```json
   {
       "userns-remap": "default"
   }
   ```

2. **Dockerデーモンの再起動**:

   設定を反映させるためにDockerデーモンを再起動します。

   ```bash
   sudo systemctl restart docker
   ```

3. **ユーザー名前空間の分離を確認するためのコマンド**:

   設定が完了したら、以下のコマンドを使用してユーザー名前空間をリマップした状態でコンテナを起動できます。

   ```bash
   docker run --rm -it --userns=default --name separated-userns-container my-bash-container
   ```

### ユーザー名前空間の設定が無効な場合の代替コマンド

もしリマッピングが複雑な場合や無効な場合は、基本的な確認のために、`--user`オプションを使ってUIDとGIDを明示的に設定することもできます。

```bash
docker run --rm -it --user 1000:1000 --name separated-userns-container my-bash-container
```

- `--user 1000:1000`: コンテナ内でUID 1000、GID 1000としてプロセスを実行します。この方法は完全なユーザー名前空間の分離を確認するものではありませんが、UIDの違いを確認するための簡易的な手法として役立ちます。

設定の有無やシステムのDockerバージョンによって動作が異なるため、`--userns=host`と同様の設定を含むかどうかの確認が必要です。

## 7. Cgroups名前空間の分離と統合 (参考)
※ 以下の内容はWSL2上では確認できず。参考程度まで。

Cgroups（Control Groups）名前空間の分離と統合を確認するために、`my-bash-container`イメージを使用してコンテナを起動するコマンドを紹介します。Cgroups名前空間は、コンテナのリソース（CPU、メモリ、I/Oなど）の割り当てと制限を管理するための仕組みです。Cgroups名前空間の分離により、各コンテナは自身のリソース制限を持ち、他のコンテナやホストの制限に影響されません。

Cgroups名前空間の管理はDockerの内部で行われるため、Dockerコマンドを通して分離状態と統合状態を確認する方法を紹介します。

### 1. Cgroups名前空間が分離された状態での起動

デフォルトでは、DockerはCgroups名前空間を分離して各コンテナを実行します。この状態では、コンテナ内で設定されたリソース制限はホストや他のコンテナに影響を与えません。

#### 分離された状態での起動コマンド
```bash
docker run --rm -it --name separated-cgroup-container my-bash-container
```

コンテナ内に入った後、Cgroupsのリソース制限を確認するには、以下のようなコマンドを実行します。

```bash
# コンテナ内で実行
cat /proc/self/cgroup
```

このコマンドで表示されるCgroups情報により、コンテナ固有のCgroups階層があることが確認できます。これがCgroups名前空間の分離を示しています。

### 2. Cgroups名前空間がホストと統合された状態での起動

Cgroups名前空間をホストと共有するためには、`--cgroupns=host`オプションを使用します。これにより、コンテナはホストのCgroups制御を直接利用します。

#### 統合された状態での起動コマンド
```bash
docker run --rm -it --cgroupns=host --name integrated-cgroup-container my-bash-container
```

このコマンドでコンテナ内に入った後、再びCgroups情報を確認します。

```bash
# コンテナ内で実行
cat /proc/self/cgroup
```

### 各コマンドの確認ポイント

- **分離された状態**: コンテナ内で確認したCgroups情報は、ホストとは異なる独自のCgroupsが設定されています。この状態では、コンテナのリソース使用がホストのCgroups設定に直接影響を与えることはありません。

- **統合された状態**: コンテナ内で確認したCgroups情報がホストと一致している場合、コンテナのリソース管理がホストと統合されていることを意味します。リソース制限や管理がホストと共有されます。

これらのコマンドを使用して、Cgroups名前空間の分離と統合の状態を確認できます。Cgroupsの分離は、各コンテナのリソース管理を独立させるため、セキュリティと効率の向上に役立ちます。