この記事に出てくる HITSC 関連の用語や構成は、執筆時点の検証用基盤に関するものです。現在動作しているコンテストを指すものではなく、実装や設計の進行に伴い変更されている場合があります。
HITSC / hitto Troubleshooting Contest は、Firecracker microVM 上に実際の Linux 問題環境を用意するために作っている検証用基盤である。
これまでの検証では Ubuntu 24.04 rootfs に systemd、ssh、nginx などを入れて、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 で、iptables と ip6tables を 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.json へ allow-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 は、サーバー・ネットワークのトラブルシューティングや運用技術をチーム単位で競う学生向けコンテストである。