man pages on a linux docker image あるいは「マニュアル専用 docker」

昔からの言い方の「オンラインマニュアル」って、今では「オフラインマニュアル」だよな。

当初の Unix が man コマンドで読めるマニュアルを「オンラインマニュアル」と呼んでいたのは、紙のマニュアルとの対比。「メール≒e-mail」の話と同じよな。

まぁそれはいいんだけれども。MSYS にオンラインマニュアルがなくったって泣くもんかというネタを書いたけれども、Docker もであった。

Dockerfile に CMD も ENTRYPOINT も書かないでおけば、コンテナ内にインストール済のコマンドは原則としてそのまま使えるので、そのノリで考えるなら「man コマンドが使える」はずなのである。けれども、docker 内の linux は基本的に「CUI でのインタラクティブ性は無駄無駄無駄ぁ」という思想なので、意図的にオンラインマニュアル一式が「動作しない」ようにカスタマイズされている。マニュアルページがインストールされないというだけでなく、「minimize」というツールまで作って、文字通り動作しないようになってる:

1 This system has been minimized by removing packages and content that are
2 not required on a system that users do not log into.
3 
4 To restore this content, including manpages, you can run the 'unminimize'
5 command. You will still need to ensure the 'man-db' package is installed.

unminimize しろと言われたからしてみるも、ダメみたい。

この「docker で man を使えない」ことが困ることなのかと言われれば、これは「普通はちっとも困らない」でいい。MSYS 単独だけで困るのとは違い、docker on Windows は、WSL/WSL2 が必須なので、必要ならば WSL/WSL2 の linux で対応するコマンドのマニュアルを引けばいい。

けれども「普通でないケース」があってな。Docker 初めの 1.3 歩の中で GUI コマンドで遊んでみてる例を書いた。この延長で、「sopwith」という懐かしいレトロゲームを動かせたのね。けれどもこやつ、操作方法が全くわからんの。そしてオンラインマニュアルが「あればある」ことを知り…、こういうのはインストールしたその環境にマニュアルもセットになってた方が良かろう、と。(というか、マニュアルを引きたいだけのために WSL のインスタンスで sopwith を全く同じ時間をかけて二重インストールするのはあまりに馬鹿げてる。)

un/minimize がどこまでのことをしやがるのかの詳細については知らないが、とにかく「不必要なものはなるべく入れない」ポリシーが強く働いていて、少なくとも「パッケージマネージャに対するインストール物除外設定」として man ページ除外が設定されているみたい。その除外設定の解除方法が、例によって stackoverflow の回答に書かれてた(方法つったってコメントアウトしてるだけだけどね):

/usr/share/man だけ除外してるとは限らんと思うのだが、そこだけ扱ってて、賢くないとは思う。けどまぁ。
1 sed -i 's:^path-exclude=/usr/share/man:#path-exclude=/usr/share/man:' \
2         /etc/dpkg/dpkg.cfg.d/excludes

これは apt に対する指示なのかな。実際言われた通りに設定を書き換えてみると、除外されていた man ページがインストールされるようになった。

で、「man ページがインストールされる」だけでは当然ダメで、「マニュアルシステム一式」が必要なわけなんだけれども、「man コマンドがリプレイスされちゃう」のだもの、だったら「オレさまman」をブチこんじまった方が早そうだと:

man
 1 #! /bin/sh
 2 #
 3 # A minumal emulation of UNIX's `man' command, for minimizing system
 4 # such as a linux system on docker container. This can only do rendering
 5 # each target page.
 6 #
 7 MP=${MANPATH:-"/usr/share/man"}
 8 PAGER=${MANPAGER:-${PAGER:-less}}
 9 section=""
10 target=""
11 while test $# -gt 0; do
12     case "$1" in 
13     *)
14         if test -z "${section}" ; then
15             section=$1
16         else
17             target=$1
18         fi
19         shift
20         ;;
21     esac
22 done
23 if test -z "${target}" ; then
24     target=${section}
25     section=""
26 fi
27 #
28 for r in `echo ${MP} | sed 's@:@ @g'` ; do
29     for page in `find ${r} -name "${target}.*" -type f | egrep "\.${section}"` ; do
30         zcat ${page} | nroff -man | ${PAGER}
31         exit 0
32     done
33 done
Dockerfile
 1 FROM ubuntu:22.04
 2 
 3 RUN sed -i 's:^path-exclude=/usr/share/man:#path-exclude=/usr/share/man:' \
 4         /etc/dpkg/dpkg.cfg.d/excludes
 5 
 6 RUN apt-get update && \
 7     apt-get -y upgrade
 8 
 9 RUN apt-get install -y groff
10 RUN apt-get install -y manpages
11 RUN apt-get install -y manpages-posix
12 RUN apt-get install -y less
13 
14 RUN apt-get install -y sopwith  # このレトロゲームのマニュアルが欲しかったのだ。
15 
16 COPY man /usr/bin
17 RUN chmod a+x /usr/bin/man

この sopwith on docker を動かすための前提となる話は Docker 初めの 1.3 歩を見てもらうとして。この Dockerfile の場合たとえばワタシの場合:

ワタシの Windows 内で「X Window System on Windows」が「認証不要」状態で動いてる。
1 [me@host: ~]$ docker build -t sopwith
2 [me@host: ~]$ docker run --rm -it -e DISPLAY=172.23.32.1:0 sopwith //usr/games/sopwith

という具合に実行出来て、こんな感じ:

なんかこのゲーム、かすかに記憶にあるんだよなぁ? リアルタイムで遊んだか、もしかして?

で、「オレさま man」を仕込んであるので、マニュアルが引ける:

1 [me@host: ~]$ docker run --rm -it sopwith man sopwith


コンソールウィンドウのタイトルにも注目してもらえたらと思う。

なお、PAGER を man の中に仕込んだが、これをせずに Windows 側の MSYS の less にパイプしようとすると「誰がキー入力を受け取るのか」に関する混同が起こって不愉快なことになる。以下を試みてみよ:

1 [me@host: ~]$ docker run --rm -it -e MANPAGER=cat sopwith man sopwith | less

今回のこれをさ、真剣に必要とする人はかなり稀だとは思うのね。言った通り「普通は即座に別の linux に出向いて試せる」から。Windows ユーザでさえ、だよ、Docker Desktop for Windowsは WSL/WSL2 が前提なので。その WSL/WSL2 上の本物の linux でマニュアルを引ければいい。

ただ、「on Windows では、純粋に Docker しか使わん」という人も中にはいるかもしれない。WSL/WSL2 そのものを直接使うことは別に必須事項ではないからね、WSL/WSL2 上で動いてるのが Docker だけ、という人もいてもおかしくはないと思う。そうした場合に、「オンラインマニュアルだけに期待する Docker コンテナ」というのも、あるいは考え方としてはあるかもしれんな、と思った、てハナシ。そしてそのためには、今回ワタシがやったような迂回技がどうしても必要、てことね。

ちなみにワタシの man はほんとにミニマルで、本物の man コマンドはキーワード検索や appropos 検索が出来たりするんだけれど、それらはさすがに本物の man システムに頼らないと実現出来ない、少なくともこういうシェススクリプトだけで賄おうとする範囲内に収めたいならば。こんなものは諦めてしまうのが肝要。



Related Posts