← logs

Firecracker 上の Ubuntu 24.04 で Docker を動かすまでに詰まったこと

2026年5月12日

Firecracker microVM 上の Ubuntu 24.04 rootfs で Docker を動かすために、iptables backend と Docker daemon 設定を調整した記録

この記事に出てくる HITSC 関連の用語や構成は、執筆時点の検証用基盤に関するものです。現在動作しているコンテストを指すものではなく、実装や設計の進行に伴い変更されている場合があります。

HITSC / hitto Troubleshooting Contest は、Firecracker microVM 上に実際の Linux 問題環境を用意するために作っている検証用基盤である。

これまでの検証では Ubuntu 24.04 rootfs に systemdsshnginx などを入れて、SSH で入れる単一 VM 問題を動かしていた。次に ICTSC 系の問題でよく出てくる Docker / iptables 問題を作るため、Firecracker guest 内で Docker を動かせる rootfs を作ることにした。

最終的には Docker 入り rootfs を作成し、Firecracker 上で docker run-p 8080:8080 の port publish まで確認できた。

/var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-docker-base.ext4

この記事では、Docker rootfs を作る途中で実際に詰まったところと、最終的に入れた設定をまとめる。

前提

worker VM は Proxmox 上の AlmaLinux 9 VM で、その中で Firecracker を動かしている。

host:
  Proxmox VM
  AlmaLinux 9
  /dev/kvm available

Firecracker:
  firecracker v1.15.1

kernel:
  /var/lib/hitsc-firecracker/kernels/vmlinux-6.1-firecracker
  -> /var/lib/hitsc-firecracker/kernels/vmlinux-6.1.155-firecracker

base rootfs:
  /var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-base.ext4

Docker 入り rootfs は、既存の Ubuntu 24.04 base rootfs をコピーして作る。

input:
  /var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-base.ext4

output:
  /var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-docker-base.ext4

ビルドスクリプトは以下。

tools/build-ubuntu-24.04-docker-rootfs.sh

インストールする主な package は以下。

DOCKER_PACKAGES=(
  docker.io
  containerd
  runc
  iptables
  nftables
  uidmap
)

最初のビルドは成功した

まずは普通に docker.io を入れる rootfs を作った。

sudo tools/build-ubuntu-24.04-docker-rootfs.sh

ビルド自体は成功した。

created /var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-docker-base.ext4
base rootfs: /var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-base.ext4
size: 6G

静的にも Docker 関連の binary や service は入っていた。

/usr/bin/docker
/usr/bin/containerd
/usr/sbin/iptables
/etc/systemd/system/multi-user.target.wants/docker.service
/etc/systemd/system/multi-user.target.wants/containerd.service

この時点では「Docker 入り rootfs はできた」と見える。

しかし Firecracker で実際に起動してみると、dockerd は起動していなかった。

Firecracker 上で起動してみる

検証用に一時的な docker-smoke-001 問題を作った。

HITSC の ticket で起動すると、Firecracker VM 自体は起動した。

{
  "ticket": "tkt-20260530-114015-72976f",
  "ticket_status": "ACTIVE",
  "instance_status": "RUNNING",
  "namespace": "hnc74867c3ae",
  "guest_ip": "172.16.253.2",
  "pid": 251731
}

SSH port も開いていた。

SSH_TCP_OK

guest に SSH して kernel を見ると、Firecracker 向けの 6.1 kernel で起動していた。

Linux hitsc-worker-01 6.1.155+ #1 SMP PREEMPT_DYNAMIC Thu Dec 18 15:17:16 UTC 2025 x86_64 GNU/Linux

しかし Docker は失敗していた。

failed

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

ここで大事なのは、Firecracker VM 自体、SSH、networkd、systemd は動いていること。

つまり問題は以下ではなさそうだった。

- Firecracker が起動していない
- TAP や network namespace が壊れている
- guest に SSH できない
- rootfs 全体が壊れている

焦点は dockerd がなぜ落ちるかに絞られた。

containerd は起動しているが Docker だけ落ちる

guest 内で service status を見る。

● containerd.service - containerd container runtime
     Active: active (running)

× docker.service - Docker Application Container Engine
     Active: failed (Result: exit-code)

containerd は生きている。

一方、docker.service は 3 回 restart して失敗していた。

docker.service: Scheduled restart job, restart counter is at 3.
docker.service: Start request repeated too quickly.
docker.service: Failed with result 'exit-code'.
Failed to start docker.service - Docker Application Container Engine.

この時点では、container runtime 全体ではなく、Docker daemon の network 初期化あたりが怪しい。

iptables-nft が nf_tables を要求して失敗していた

journalctl -b -u docker を見ると、原因がかなりはっきり出ていた。

failed to start daemon:
Error initializing network controller:
error obtaining controller instance:
failed to register "bridge" driver:
failed to create NAT chain DOCKER:
iptables failed:
iptables --wait -t nat -N DOCKER:
iptables: Failed to initialize nft: Protocol not supported

Docker は起動時に bridge network 用の NAT chain を作ろうとする。

Ubuntu 24.04 で iptables package を入れると、default では iptables-nft backend が選ばれる。実際、失敗している guest で確認するとこうだった。

/etc/alternatives/iptables -> /usr/sbin/xtables-nft-multi

しかし今回使っている Firecracker CI kernel では、guest 側で nf_tables が使えない。

その結果、Docker が iptables-nft 経由で nat table を触ろうとして、以下で落ちていた。

iptables: Failed to initialize nft: Protocol not supported

これは HITSC の namespace や TAP の問題ではなく、guest kernel と guest userland の iptables backend の不一致だった。

修正1: iptables-legacy へ切り替える

まず起動中 guest で、iptablesip6tables を legacy backend へ切り替えて試した。

update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
systemctl reset-failed docker
systemctl restart docker

結果、Docker daemon は起動した。

active
29.1.3

ここまでで 1 つ目の問題は解決した。

ただし Docker daemon が起動しただけで、コンテナ起動まで成功したわけではなかった。

docker run で raw table が無い問題に当たった

次に、host 側で作った小さな container rootfs を guest へ送り、Docker image として import した。

外部 registry への依存を避けるため、検証では docker pull は使わなかった。

docker import /root/hitsc-docker-smoke.tar hitsc-smoke:latest

そして container を起動しようとすると、今度は別の iptables エラーが出た。

docker: Error response from daemon:
failed to set up container networking:
failed to create endpoint epic_kilby on network bridge:
Unable to enable DIRECT ACCESS FILTERING - DROP rule:
iptables --wait -t raw -A PREROUTING -d 172.17.0.2 ! -i docker0 -j DROP:
iptables v1.8.10 (legacy): can't initialize iptables table `raw':
Table does not exist (do you need to insmod?)

今度は iptables-nft ではない。

iptables-legacy へ切り替えたあとに、legacy の raw table が無いと言われている。

can't initialize iptables table `raw': Table does not exist

Docker 29 は、bridge network 上の container IP へ直接アクセスされる経路を制御するため、direct access filtering の DROP rule を raw table へ入れようとしていた。

しかし今回の Firecracker CI kernel では、その raw table を使えない。

つまり 2 段階で詰まっていた。

1. iptables-nft backend が nf_tables を要求して dockerd 起動に失敗
2. iptables-legacy に切り替えた後、Docker 29 が raw table を要求して container 起動に失敗

修正2: allow-direct-routing を有効化する

Docker daemon の設定に allow-direct-routing を入れて再起動した。

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "allow-direct-routing": true
}

これにより、この環境では Docker が raw table へ direct access filtering 用の DROP rule を入れようとしなくなり、container network を作れるようになった。

起動中 guest で確認した結果は以下。

active
HITSC DOCKER OK
hitsc-http 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp

namespace 内の worker 側から guest の published port へ curl しても応答した。

HITSC DOCKER OK

HITSC の score も PASS した。

{
  "result": "PASS",
  "score": 100,
  "stdout": "docker container port 8080 is reachable\n",
  "message": "docker container port 8080 is reachable"
}

テンプレート側へ反映する

起動中 guest で手動修正して成功しただけでは、rootfs template としては不十分である。

そこで tools/build-ubuntu-24.04-docker-rootfs.sh に、iptables legacy への切り替えを反映した。

for alternative in iptables ip6tables; do
  if [[ -x "${ROOT_DIR}/usr/sbin/${alternative}-legacy" ]]; then
    chroot_run update-alternatives --set "${alternative}" "/usr/sbin/${alternative}-legacy"
  fi
done

さらに /etc/docker/daemon.jsonallow-direct-routing を書く。

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "allow-direct-routing": true
}

作り直した rootfs を静的に確認する。

/var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-docker-base.ext4

debugfs で確認した結果、daemon.json は期待通りだった。

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "allow-direct-routing": true
}

iptables alternative も legacy になっている。

Fast link dest: "/usr/sbin/iptables-legacy"

途中で踏んだもう一つの罠

一度、最終版 rootfs を作り直した後の fresh 検証で、また Docker が失敗した。

ログは同じだった。

iptables: Failed to initialize nft: Protocol not supported

おかしいと思って確認すると、永続 rootfs 自体は修正済みだった。

/var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-docker-base.ext4
  daemon.json: allow-direct-routing あり
  iptables: iptables-legacy

しかし、一時問題側の rootfs.base.ext4 は古いままだった。

/tmp/hitsc-docker-smoke-data.../problems/docker-smoke-001/rootfs.base.ext4
  daemon.json: allow-direct-routing なし
  iptables: xtables-nft-multi

原因は、検証用 problem template をコピーし直すときに、既存ファイルが残っていたことだった。

修正済みの永続 rootfs から検証用 rootfs.base.ext4 を作り直すには、既存ファイルを消してからコピーする必要がある。

rm -f "$PROBLEM_DIR/rootfs.base.ext4"
cp --reflink=auto /var/lib/hitsc-firecracker/rootfs/ubuntu-24.04-docker-base.ext4 \
  "$PROBLEM_DIR/rootfs.base.ext4"

この後、fresh ticket で再検証して成功した。

最終確認

修正後の rootfs から新しい ticket を発行して起動した。

{
  "ticket": "tkt-20260530-115134-98f758",
  "ticket_status": "ACTIVE",
  "instance_status": "RUNNING",
  "namespace": "hn01b69ebad0",
  "guest_ip": "172.16.253.2",
  "pid": 252945
}

guest 内では Docker が起動している。

active
29.1.3
/usr/sbin/xtables-legacy-multi

daemon.json も期待通り。

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "allow-direct-routing": true
}

container の実行も成功した。

HITSC DOCKER OK

port publish も成功した。

hitsc-http 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp

HITSC namespace 内から curl できた。

HITSC DOCKER OK

score も PASS。

{
  "id": "score-20260530-115525-650a664e",
  "ticket_id": "tkt-20260530-115134-98f758",
  "instance_id": "tkt-20260530-115134-98f758-g1",
  "result": "PASS",
  "score": 100,
  "exit_code": 0,
  "stdout": "docker container port 8080 is reachable\n",
  "message": "docker container port 8080 is reachable",
  "started_at": "2026-05-30T20:55:25+09:00",
  "finished_at": "2026-05-30T20:55:25+09:00"
}

今回分かったこと

Firecracker 上で Docker を動かす場合、「Ubuntu に Docker package を入れる」だけでは足りない。

Docker は guest kernel の network / filtering 機能にかなり依存する。

今回の問題は Docker package や systemd service ではなく、guest kernel と iptables backend の組み合わせだった。

Firecracker CI kernel:
  nf_tables が使えない
  legacy raw table も使えない

Ubuntu 24.04 docker.io:
  default では iptables-nft を使う

Docker 29:
  direct access filtering で raw table を使おうとする

そのため、HITSC の Docker-capable rootfs では MVP として以下の方針を取る。

iptables:
  iptables-legacy / ip6tables-legacy に固定

Docker daemon:
  allow-direct-routing: true

scope:
  Firecracker guest 内の Docker 問題用 rootfs

これは本番汎用サーバーの標準設定というより、現在の Firecracker CI kernel で Docker 問題を動かすための互換設定である。

HITSC の設計では ticket ごとに network namespace を分け、guest への参加者アクセスや外部公開は HITSC 側で制御する想定である。したがって、Docker guest 内のこの設定は MVP の練習環境としては許容できる。

ただし、将来的に Docker 問題をより本番サーバーに近づけるなら、guest kernel 側を見直した方がよい。

参考

ICTSC は、サーバー・ネットワークのトラブルシューティングや運用技術をチーム単位で競う学生向けコンテストである。