ICTSC2025 本戦 問題解説: [3025] やるのかやらないのか

問題文

概要

あなたの同僚は、Docker コンテナ上にある HTTP サーバが動作する VM guest に対して独自の最適化を施した。ところが、その作業による影響のためか guest の再起動後から HTTP サーバに接続できなくなってしまった。さらに、guest のログインコンソールにユーザ名とパスワードを入力しても、認証に失敗しログインできない。これらの原因を突き止めて、問題を解決してほしい。

guest は host に SSH でアクセスの上 $ ./launch.sh を実行して起動できる。また、guest には、ユーザ user、パスワードは ictsc2025 でログインできるようになっている。ただし、ログインコンソールには初期状態において不具合がある。

制約

  • host の /home/user/launch.sh に変更を加えてはならない
  • host 上にある guest のストレージ /home/user/guest.shrunk.qcow2 の内容に対して guest 外から変更を加えてはならない
    • 例えば、host 上でコマンドを実行し、当該ファイルで表現される filesystem の内容に変更を加えてはならない
  • guest 上で TCP 80 番ポートにて listen する新たなプログラムを起動する、または自動起動するような新たな設定を含めてはならない
  • guest の /home/user/app 以下に変更を加えてはならない
  • guest の /home/user/app 以下で指定されていない Docker イメージを pull してはならない
  • 回答には初期状態で示される 2 つの問題の原因およびそれらに対する解決策をそれぞれ明記すること

初期状態

  • $ ./launch.sh を実行して一定時間待機した後に得られる VM guest のログインコンソールからログインできない
  • guest が起動している間に host 上で $ curl -v 10.30.25.1 を実行してもステータスコード 200 の応答が得られない

終了状態

  • $ ./launch.sh を実行して一定時間待機した後に得られる VM guest のログインコンソールからユーザ user でログインし、シェルを介した操作ができる
  • $ ./launch.sh の実行開始後に guest のシリアルコンソールに対して一切の入力を行わないまま一定時間待機した後に host 上で $ curl -v 10.30.25.1 を実行するとステータスコード 200 の応答が得られる

解説

この問題は、ログインコンソールを表示する ttyS0 に対して 2 つの agetty が重複して起動していること、また guest 上で動作する Linux カーネルが Linux namespaces 機能をサポートしていないことの 2 つからなる。

ログインコンソールの問題

  • host 上で $ ./launch.sh を実行して guest を起動した後のログイン画面で文字入力がおかしくなり、ログインできない
  • 解決策
    • $ ./launch.sh を起動した直後に表示される extlinux の画面で Tab キーを押下し、linux のオプションにて末尾に init=/bin/shsingle を指定して root ユーザのシェルに入る
    • # mount -o remount,rw / 等により、root filesystem を読み書き可能な形でマウントしなおす
    • # vi /etc/inittab などで、ttyS0 に関して重複した記述のうちどちらかを削除する
    • # mount -o remount,ro / 等により、root filesystem を読み取り専用に戻す
    • # poweroff -f で VM を終了させる
      • PID 1 のシェルを終了させるとカーネルパニックする。この場合、host で以下のどちらかを行って VM guest を強制的に停止する
        • $ sudo killall qemu-system-x86_64
        • $ telnet localhost 4444 の後、(qemu) のプロンプトが表示されたら quit を入力する
  • 注意点
    • ホストで qemu-nbd を使って qcow2 のパーティションをマウントすることでも似た作業はできるが、今回はそれで qcow2 に変更を加えてしまうと制約に反することに注意
      • 制約上、それを用いて qcow2 の中身を閲覧することは可

Docker サービスの問題

  • /var/log 以下にある Docker 関連のログを閲覧すると、Docker daemon が起動できていないことがわかる
  • Docker daemon は Linux namespaces 機能を活用するソフトウェアである。試しに $ unshare -U で user namespace の作成を試みると invalid arguments というエラーで失敗する
  • /boot/config-3025customized または home directory にある Linux カーネルの .config を見ると、CONFIG_NAMESPACES=n となっていることがわかる。すなわち、「独自の最適化」では Linux namespaces 機能が無効化されており、これが Docker daemon の実行を妨げていることがわかる
  • 解決策
    • $ doas apk add linux-virt を実行し、Linux namespaces 機能が有効な Linux カーネルをインストールする
      • $ doas apk add linux-lts でも可
      • home directory にある Linux カーネルの .config を修正してから $ make && sudo make install modules_install を実行することも可能ではあるが、コンテスト中に回答するための戦略としては向かない
    • guest を再起動すると nginx のコンテナが自動起動する
      • host から $ curl 10.30.25.1 -v を実行してアクセスできることを確認する

採点基準

  • 初期状態でコンソールからログインできない理由が agetty の重複起動によることを説明している +20 点
  • /etc/inittab を編集し、コンソールにログインできる +20 点
  • nginx のコンテナが自動起動しない原因として、Docker デーモンが起動しておらず、かつそれは Linux namespaces の機能が無効化されているからであることを /boot/config-3025customized または home directory にある Linux カーネルの .config を参照しながら述べている +100 点
    • namespaces 関連機能が使えないことを unshare コマンド等で確認したが kernel config を見ていない場合も可
  • Linux namespaces 機能を提供するカーネルをゲスト VM に導入する +40 点
  • ./launch.sh の実行開始後に guest のシリアルコンソールに対して一切の入力を行わないまま一定時間待機した後に host 上で $ curl 10.30.25.1 を実行し、guest HTTP サーバが動作することを確認する +20 点

講評

起動したホストにログインできないときはシングルユーザーモードに入って問題解決を試みることが多くの場合でファーストステップになります。例えば、セットアップしたホストで特権が使えるユーザすべてのパスワードを忘れたとき、root ユーザのシェルに入りパスワードの再設定を行うことがあります。
今回、あえて qemu-system-x86_64 を起動するシェルスクリプト launch.sh を用意したのは、ブートローダに対してインタラクティブな操作を行う部分が、実践的なトラブルシューティングの模倣の一つとして適当であると考えたためでした。実際、guest の内部から /etc/inittab に対する変更を行うために、guest をシングルユーザモードで起動する必要がありました。なお、この部分の記述の具体性が足りず減点される報告書が複数ありました。また、init=/bin/sh で guest を起動した状態で doas を実行する報告書がありましたが、この状態ではそもそも root ユーザのシェルが得られているため doas は不要であり、かつ実際には doas の実行が失敗する点に注意が必要です。

過去十数年間で Linux を使ったことがある方のほとんどは、どこかでコンテナ技術に触れたことがあると思いますが、その根底を支える存在の 1 つとして Linux namespaces が挙げられます。Alpine Linux は幸いなことに Linux namespaces を切っていても起動に成功し、かつ問題を解くために必要な操作が実行できたため、guest で実行する Linux ディストロとして採用したのでした。

Docker デーモンが起動しない問題の解決策として、ホームディレクトリにある Linux カーネルの設定を変更してビルドを開始するチームも複数ありましたが、問題環境の VM には 1 vCPU, 2 GB メモリしか割り当てていなかったことから、コンテスト開催中にビルドを完了させて満点を取ることは困難です。そのため、問題名「やるのかやらないのか」の答えは「(カーネルビルドを)やらない」を想定していました。なお、コンテスト開催中にカーネルビルドから linux-virt のインストールに切り替えて満点回答となったチームの報告書もありました。また関連して、linux-lts のインストール中に host の OOM Killer が qemu-system-x86_64 を kill するケースもあったようです。

満点の報告書を提出したチームは 3 組ありました。シングルユーザーモードの入り方と作業内容が細やかに記述されている報告書や、Linux namespaces 機能が無効化されているがために Docker コンテナが動作しないから有効な Linux カーネルをインストールすれば良い点を明確に記述している報告書があったときは、問題の意図が手に取るように読まれているように感じられ、大変嬉しかったです。対して、実際には実行できない手順や、問題環境と矛盾する記述のある報告書が幾つか見受けられた点は少し残念でした。AI の活用は好ましい一方で、使い手側が AI に振り回されないようにすることもまた 2020 年代後半では求められる技能の 1 つだと考えられました。

おまけ

CONFIG_NAMESPACES=n なカーネルは、大量の vCPU とメモリを保有するホスト上で VM を起動し、ビルドしてインストールしました。その後、qcow2 イメージを圧縮し、問題 VM 上に配置しなおして出題しました。