Rootless Docker 完全ガイド - 仕組み・セットアップ・注意点まとめ

Rootless Docker を使うと、root 権限なしで Docker デーモンを動かせるため、ホストへの影響を最小限に抑えたセキュアなコンテナ実行が実現できます。本記事では仕組みの基礎から実際のセットアップ・よくあるハマりどころまでを実務目線でまとめます。

Rootless Docker とは

通常の Docker デーモン (dockerd) は root ユーザーとして動作します。これはコンテナを特権なしで隔離するうえで必要な権限ですが、ホストシステムへの影響範囲が広くなるというリスクがあります。

Rootless Docker は、Docker デーモンおよびコンテナを一般ユーザー権限内で動作させる仕組みです。2020 年の Docker 20.10 から正式サポートされました。

通常の Docker:
  root  → dockerd → container

Rootless Docker:
  一般ユーザー → rootlesskit → dockerd → container

仕組み

rootlesskit

rootlesskit は「Linux の名前空間を使って、あたかも root であるかのような環境」を一般ユーザーに提供するラッパーツールです。Rootless Docker の根幹をなすコンポーネントで、dockerd はこの中で動作します。

User Namespace

Linux カーネルの user namespace により、コンテナ内の UID 0(root)をホスト上の一般ユーザー UID にマッピングします。

コンテナ内:  UID 0 (root)
                 ↕  /etc/subuid マッピング
ホスト上:    UID 1000 (alice)  →  UID 100000〜165535

マッピングの設定ファイル:

# /etc/subuid
alice:100000:65536

# /etc/subgid
alice:100000:65536

cgroup v2

cgroup v2 では、一般ユーザーが自身のプロセスに対してリソース制限を設定できます(delegation 機能)。Rootless Docker はこれを利用して、root なしでもメモリ・CPU 制限が効くようになっています。

cgroup v2 が有効かどうかは以下で確認できます。

# 方法1: cgroup.controllers ファイルの存在確認(推奨)
ls /sys/fs/cgroup/cgroup.controllers
# cpu cpuset hugetlb io memory pids  ← 表示されれば cgroup v2

# 方法2: マウント情報から確認
mount | grep cgroup2
# cgroup2 on /sys/fs/cgroup type cgroup2 ...  ← これが表示されれば有効

ネットワーク (slirp4netns / pasta)

root なしでは TUN/TAP デバイスを直接扱えないため、ユーザー空間のネットワークスタック実装が使われます。

  • slirp4netns: 従来の標準実装。NAT 方式でホストネットワークに接続。
  • pasta: より新しい実装で slirp4netns より高速。Docker 27 以降で推奨。

インストール・セットアップ

前提条件の確認

# カーネルバージョン (5.11+ 推奨)
uname -r

# cgroup v2 有効確認
cat /proc/1/cgroup
# 0::/ と表示されれば cgroup v2

# subuid/subgid の確認
grep $(whoami) /etc/subuid /etc/subgid

必要パッケージのインストール

sudo apt update
sudo apt install -y \
  uidmap \
  dbus-user-session \
  fuse-overlayfs \
  slirp4netns
パッケージ用途
uidmapnewuidmap / newgidmap コマンドを提供
dbus-user-sessionユーザーセッション用 D-Bus(systemd 連携に必要)
fuse-overlayfsoverlay2 ストレージドライバの代替
slirp4netnsユーザー空間ネットワーク

Rootless Docker のインストール

方法 1: 公式スクリプトを使う(推奨)

curl -fsSL https://get.docker.com/rootless | sh

スクリプト実行後、指示に従って環境変数を設定します。

# ~/.bashrc や ~/.zshrc に追記
export PATH=$HOME/bin:$PATH
# XDG_RUNTIME_DIR が設定されている場合
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
# 明示的なパスで書く場合(より確実)
# export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock

方法 2: Docker CE パッケージから移行する

# docker-ce-rootless-extras をインストール
sudo apt install -y docker-ce-rootless-extras

# Rootless モードのセットアップ実行(一般ユーザーで実行)
dockerd-rootless-setuptool.sh install

systemd ユーザーサービスとして登録

# サービス有効化・起動
systemctl --user enable docker
systemctl --user start docker

# ログイン不要で起動させる(サーバー用途)
sudo loginctl enable-linger $(whoami)

# 動作確認
systemctl --user status docker
docker info

動作テスト

docker run --rm hello-world
docker run --rm -p 8080:80 nginx

メリット

メリット説明
セキュリティ向上デーモン侵害時でもホスト root を奪取されにくい
マルチユーザー対応各ユーザーが独立した Docker 環境を持てる
CI 環境での利用特権不要のため SaaS CI などでも動作しやすい
コンテナランタイム侵害時の被害局所化コンテナエスケープが成功してもユーザー権限内にとどまる

デメリット・制限事項

デメリット内容
1024 以下のポートはバインドできないルーティングの工夫が必要(後述)
overlay2 が使えない環境があるfuse-overlayfs にフォールバック(速度低下)
cgroup v1 環境ではリソース制限が一部無効Ubuntu 22.04 以降は基本 v2 なので問題少ない
ネットワーク性能がやや低下slirp4netns/pasta 経由のオーバーヘッド
AppArmor / SELinux との相性ディストリビューションによって追加設定が必要

よくあるハマりどころ・注意点

1. 1024 以下のポートがバインドできない

Rootless コンテナはユーザー権限で動くため、-p 80:80 のような特権ポートバインドが失敗します。

# エラー例
docker run -p 80:80 nginx
# Error response from daemon: driver failed programming external connectivity

対策 1: ポート番号を変える(推奨)

docker run -p 8080:80 nginx

対策 2: net.ipv4.ip_unprivileged_port_start を変更する

# 一時的に変更
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80

# 恒久的に変更
echo "net.ipv4.ip_unprivileged_port_start=80" | sudo tee /etc/sysctl.d/99-rootless-docker.conf
sudo sysctl --system

2. overlay2 ストレージドライバが使えない

カーネルが unprivileged overlay を許可していない環境では fuse-overlayfs が使われます。

docker info | grep "Storage Driver"
# Storage Driver: fuse-overlayfs  ← overlay2 より遅い

# overlay2 を有効にするには(Ubuntu 22.04+)
echo '{ "storage-driver": "overlay2" }' > ~/.config/docker/daemon.json
systemctl --user restart docker

カーネル 5.11+ かつ Ubuntu 22.04 以降では設定なしで overlay2 が使えることが多いです。

3. cgroup v2 の delegate 設定

コンテナへのリソース制限(--memory, --cpus)が効かない場合は、systemd の delegate 設定を確認します。

# delegate が有効か確認
cat /sys/fs/cgroup/user.slice/user-$(id -u).slice/user@$(id -u).service/cgroup.controllers
# memory pids cpu io  ← これらが表示されれば OK

# 有効でない場合は /etc/systemd/system/user@.service.d/ に設定を追加
sudo mkdir -p /etc/systemd/system/user@.service.d/
cat <<EOF | sudo tee /etc/systemd/system/user@.service.d/delegate.conf
[Service]
Delegate=cpu cpuset io memory pids
EOF
sudo systemctl daemon-reload

4. DOCKER_HOST 環境変数の設定忘れ

Rootless Docker のソケットは /run/user/<UID>/docker.sock に配置されます。通常の /var/run/docker.sock ではないため、docker コマンドで接続できないことがあります。

# 症状: docker コマンドがエラーになる
docker ps
# Cannot connect to the Docker daemon at unix:///var/run/docker.sock

# 解決: DOCKER_HOST を設定する
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
docker ps  # 正常に動作する

5. ネットワーク名前解決が遅い

slirp4netns 経由では DNS ルックアップが遅くなることがあります。pasta へ切り替えると改善するケースがあります。

# pasta をインストール
sudo apt install -y pasta

# daemon.json で pasta を指定
cat <<EOF > ~/.config/docker/daemon.json
{
  "rootless-network-driver": "pasta"
}
EOF
systemctl --user restart docker

6. ボリュームマウント時の UID/GID ズレ

ホストのディレクトリをコンテナにマウントすると、User Namespace のマッピングにより「コンテナ内では書き込めない」問題が発生することがあります。

# 症状: マウントしたディレクトリに書き込めない
docker run -v $HOME/data:/app/data myimage
# permission denied: /app/data/output.txt

# 解決: コンテナ実行ユーザーを明示する
docker run -u $(id -u):$(id -g) -v $HOME/data:/app/data myimage

7. 公開ポートがローカルホスト限定になる

Rootless Docker では、-p 8080:80 のようにポートを公開してもデフォルトで 127.0.0.1 にしかバインドされず、他端末からアクセスできないことがあります。

# 他端末や VM からアクセスさせたい場合は 0.0.0.0 を明示する
docker run -p 0.0.0.0:8080:80 nginx

サーバー上で SSH ログアウト後もコンテナを動かし続けたい場合は loginctl enable-linger が必須です。

8. systemd linger 未設定でログアウト後にコンテナが止まる

sudo loginctl enable-linger $(whoami)

# 確認
loginctl show-user $(whoami) | grep Linger
# Linger=yes  ← これが表示されれば OK

業務での使いどころ

CI/CD 環境

GitHub Actions self-hosted runner や Jenkins エージェントで root なし Docker が使えます。

# GitHub Actions での例
jobs:
  build:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        env:
          DOCKER_HOST: unix://${{ env.XDG_RUNTIME_DIR }}/docker.sock
        run: docker build -t myapp .

マルチテナント開発サーバー

複数の開発者が同一ホストを共有する環境では、各ユーザーが独立した Docker 環境を持てるため、互いのコンテナが干渉しません。

# alice のコンテナは alice の権限内で動作
# bob のコンテナは bob の権限内で動作
# 互いのコンテナやイメージは見えない

セキュリティ要件が厳しい本番環境

コンテナランタイムへの攻撃が成功しても、ホスト root を奪取されない点が評価されています。ただし、本番投入前は十分な検証が必要です。

代替案との比較

ツールrootless 対応特徴
Rootless DockerDocker エコシステムとの互換性が高い
Podman rootlessデーモンレスで systemd ソケットアクティベーション対応
nerdctl + containerdOCI 準拠。rootless は nerdctl + bypass4netns で実現
Lima (macOS)macOS 向け。内部で rootless containerd を使用

Podman と Docker の使い分けの観点:

  • Podman が向く場合: デーモンプロセスを持ちたくない、systemd との統合を重視する、RHEL/Fedora 環境が多い
  • Rootless Docker が向く場合: 既存の Docker Compose や Docker Hub ワークフローをそのまま使いたい、Docker エコシステムへの依存が大きい

まとめ

Rootless Docker は「セキュリティを高めたいが Docker エコシステムを手放したくない」という場面に有効な選択肢です。

  • User Namespace + rootlesskit でユーザー権限内に Docker デーモンを閉じ込める
  • cgroup v2 delegate でリソース制限も一般ユーザーから設定可能
  • ポートや overlay2、DOCKER_HOST などのハマりどころは事前に把握しておく
  • Podman rootless とは互換性・デーモン有無などで使い分ける

Ubuntu 22.04+ ならほぼ追加設定なしで動作するため、まず開発環境で試してみることをおすすめします。

参考リンク