はじめに
コンテナを起動するとき、--name
を指定しないとランダムでコンテナ名がつけらえます。下記でいうところのgreat_wescoff
です。
$ docker run alpine:latest
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0772fae09ab1 alpine:latest "/bin/sh" 16 seconds ago Exited (0) 15 seconds ago great_wescoff
ふと、この名前がどのように生成されているのか気になりました。
調べ方
オープンソースなのでそれ見れば答えがわかりますが、自分なりに調べてみて最後に答え合わせを行おうと思います。
調べ方は、docker run
をたくさん実行して、(ほぼ)全通りの名前のパターンを抽出し、それを眺めて法則性を見つけて行こうと思います。
$ docker run -d alpine:latest
何かしらのイメージを実行します。実行するイメージはなんでもいいのですがなるべく軽量なものを選びました。
-d
オプションは、コンテナをバックグラウンドで実行してくれるコマンドです。
ですが今回はその用途でなくコンテナIDを出力するために使います。
$ docker ps -a -f "id=$cid" --format "{{.Names}}"
先ほど出力したコンテナID(cid)を用いてコンテナ名を抽出します。
-a
オプションで(実行中出ないものも含めた)全てのコンテナを表示させて、
-f
オプションでコンテナIDでフィルタリングを行い、
--format
で表示形式をカスタマイズします。
$ docker rm $cid
コンテナ名が抽出できたらあとは用無しなので削除します。
いざ実行
スクリプト
上記のdockerコマンドを組み合わせてshellscriptを作成します。
names
がコンテナ名のリストを格納しているファイルです。
1000件コンテナ名を取得して、1000件とも今まで蓄積してきたコンテナ名と重複してたら終了します。それまで無限ループします。
total=0
while true; do
for i in `seq 1000`; do
cid=$(docker run -d alpine:latest)
docker ps -a -f "id=$cid" --format "{{.Names}}" >> names
docker rm $cid > /dev/null
done
sort -u names -o names
after_wc=$(cat names | wc -l | tr -d ' ')
if [ "$total" = "$after_wc" ]; then
exit
else
echo "continue (find new name: $(($after_wc-$total)))"
total=$after_wc
fi
done
1日後、、、
処理が終わっていなかったので、少し高速化することにしました。一応この時点で、1エポックごとに新名が500件ほどに下がってきていたので、どこかで頭打ちにはなりそうです。
やはりdockerコマンドを実行している部分がネックなので、ある程度並列実行させることにしました。
total=0
get_name() {
for i in `seq 100`; do
cid=$(docker run -d alpine:latest)
docker ps -a -f "id=$cid" --format "{{.Names}}" >> names
docker rm $cid > /dev/null
done
}
export -f get_name
while true; do
seq 10 | xargs -I ZZ -P 10 sh -c get_name
sort -u names -o names
after_wc=$(cat names | wc -l | tr -d ' ')
if [ "$total" = "$after_wc" ]; then
exit
else
echo "continue (find new name: $(($after_wc-$total)))"
total=$after_wc
fi
done
名前を取得する部分を関数化させて10並列で実行させます。並列数は多ければ処理も高速化できることになりますが、PCスペックの関係か以下のエラーが頻出して、最悪の場合だとdockerが機能しなくなってしまいました。
Error response from daemon: You cannot remove a running container 54161ca2bea3d750a3e873a329a760af990dfcc115cb13ecec322cb2f3eff85a. Stop the container before attempting removal or force remove
ERRO[0003] error waiting for container: context canceled
100回繰り返しを10並列にしたこのバージョンでもたまに発生します。
2日後、、、
結局このループは終わりませんでしたが、1エポックごとに新名が10個ほどしか見つからなくなってきたので、打ち切りました。ここで法則性を見つけていきます。
解析
仰々しく解析などといっていますが、抽出されたコンテナ名を眺めてこんな感じの法則ありそう、と考えるだけです。
見つかった名前の数は25422
個。これにあと数百個は見つかっていない名前がありそうです。名前には、必ずアンダーバー(_
)が入っていて、前後の単語を区切る文字のようです。この単語をリストアップしてみます。また、前後でこの単語は重複していないようですので、別々にリストアップします。
sed -e 's/_.*//g' names > names_first
uniq names_first names_first_uniq
後方の単語はソートもしないといけないので、上記の抽出スクリプトで使っていたコマンドを流用。
sed -e 's/.*_//g' names > names_second
sort -u names_second -o names_second_uniq
まだ出現していない単語がないとして、__これらの108個×350個の単語を組み合わせてコンテナ名は作られている__のでないかと推測します。
そうなると、組み合わせは37800
個になりますので、スクリプトも非効率で試行回数が足りていなかったかなと反省です。
答え合わせ
上記で抽出した単語を頼りに、ソースコードを追いました。
大枠は捉えられていたようですが、予想と違いました。__108個×237個の単語の組み合わせと、引数として与えられるリトライ数によっては、後ろにランダムで0~9の数字が付与される__でした。
(ただし、boring
とwozniak
の組み合わせはのぞく)
これを受けて、私が抽出した350個の後方の単語のうち数字が付与されているものを除くとピッタリ237個になります。つまり候補の単語は前後全て抽出できていたようです。
しかし、ランダムで付与される数字も含めて、理論上の組み合わせの総数は(108*237-1)*11=281545
となり(しかも数字が付与される確率自体が低そう)、途方もないので打ち切って正解でした。
以上になります。ふと気になったことではありますが、調べているうちに(特にshellについて)勉強になることが多くなんだかんだ有意義な調査でした。