Docker: busybox のイメージサイズはなぜ小さい

まあ、このあたりは初心者なので新しい発見が多いわけですが…

この前も使った busybox ですが、サイズがやたらと小さいことに気づきました。

$ docker images
 REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
 busybox       latest    c55b0f125dc6   11 hours ago   1.24MB

えっ、busybox って一通りの基本コマンドが揃っていたような、と思ったわけです。普通 /bin とかこのサイズで収まらないでしょう、と。で、調べてみるとそもそも busybox って十徳ナイフのようなコマンドとして組み込み用に存在していてそれを Docker 用イメージにしたのですね。

Linux の man ページには以下のようにあります。

NAME
        BusyBox - The Swiss Army Knife of Embedded Linux

つまり、複数のコマンドを一つの実行ファイルにし共有できるところを共有・最適化することによりこのサイズを実現しているわけです。そして起動時にどのような名前で呼ばれたかによって動作を変えると。

試しに busybox を起動して中身を確認してみます。

$ docker run -i -t busybox:latest
/ # ls -li /bin
total 449684
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 [
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 [[
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 acpid
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 add-shell
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 addgroup
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 adduser
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 adjtimex
  84754 -rwxr-xr-x  400 root     root       1149184 May  3 21:57 ar
(...省略)

ls に ‘-i’ オプションをつけて inode 番号を表示するようにして /bin の中身を見ました。上の例で一番最初の数字が inode 番号ですが、これはファイルシステム上のどのファイル実体を指しているかを表しています。このように 400 の実行ファイルの inode 番号が同じに表示され (注)、これらの実行コマンドファイルの実体が同じものであることがわかります。ちなみにこの 400 という数はオーナー (root) の前に表示されているリンク数 (400) からもわかりますね。

busybox のサイズの小ささと共に 400 もの機能をひとまとめにしていることに感嘆するわけです。

(注: /bin の中で getconf だけ別の inode 番号を持っていました)

参考: https://busybox.net/

WSL 2 複数インスタンス同一 IP アドレス

WSL 2 で複数の distribution (ここではこれを WSL の「インスタンス」と呼ぶことにします) を導入して同時に起動すると同じ IP アドレスを使うのです。ちょっと直感に反していたのでまとめておきます。

まず前提として WSL の複数インスタンスを使うという話ですが、wsl.exe の export/import 機能を使えば同じバージョン Ubuntu でも複数のインスタンスを作成できます。ここでは元々の名前の Ubuntu というインスタンスとそれを export/import して作った Hetarena というインスタンスがあるものとして説明します。

その 2つ、Ubuntu と Hetarena を同時に起動して ifconfig で IP アドレスを確認すると、あら不思議、同じ IP アドレスを持っています。さらに Ubuntu で Apache を起動して Hetarena で netstat で listening しているポートを確認すると、なんと 80/TCP をリッスンしているではないですか!

さらにさらに Ubuntu で mysql を起動して Hetarena から以下のコマンドを打ってみると何と接続できて Ubuntu のデータベースの内容を確認することができます。

mysql -u myid -D mydb -h 127.0.0.1 -p

ちなみにホスト指定を ‘-h localhost’ とすると Unix ソケット経由の接続を試みこれはエラーとなります。ファイルシステムは明らかにインスタンス毎に独立しているし、ps コマンドを打ってみても他方のプロセスが見えるということはなさそうです。

ググってみると関連した issue が見つかり、そこで関係者により以下のように説明されていました。

All of the WSL 2 distros run on the same virtual machine, which has a singular virtualized networking interface controller.

https://github.com/microsoft/WSL/issues/4304

「WSL 2 の複数ディストロは同一 VM 上で動作する」って訳してもチンプンカンプンになりそうですが、面白い実装ですね。

WSL 2 のデフォルトユーザー指定 (2つめのインスタンス用)

Visual Studio Code で WSL 2 と連携してろくに設定せずに Terminal を開くと root でシェルが起動してしまったりします。それでは困るので以下のコマンドでデフォルトユーザーを設定することになります。

ubuntu config --default-user blogger323

参考: https://docs.microsoft.com/en-us/windows/wsl/wsl-config#change-the-default-user-for-a-distribution

ところが、です。私の場合は以下の記事を参考にしてディストリビューションの位置を変えたり 2つめの WSL インスタンスを作ったりしています。

https://stackoverflow.com/questions/38779801/move-wsl-bash-on-windows-root-filesystem-to-another-hard-drive

私の場合、Ubuntu を export 後、Hetarena という名前で import して 2つめのインスタンスを作りました。さて、この 2つめのインスタンスのデフォルトユーザーを変えるにはどうしたらよいでしょう?

hetarena config --default-user blogger323

の様なことをやっても当然のごとく “Command Not Found” なわけです。

ググっても答えは見つからず、結局レジストリ内を検索して見つけ出しました。以下のキーに Linux uid を設定することでデフォルトユーザーを指定できます。

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss\{distro-id}\DefaultUid

ここで distro-id はディストリビューション (WSL インスタンス) に割り当てられた ID です。DefaultUid と同じ階層に DistributionName というキーがあるのでこれで見分けることができます。

ちなみにレジストリキーでググって後から以下の Issue を見つけることができました。私は試していないですが、Linux ファイルシステムの設定ファイルに書く方法もあるようです。(追記: Microsoft のドキュメントを改めて確認すると記載があったのでこちらの方が正規の方法ですね!)

https://github.com/microsoft/WSL/issues/3974

Anaconda と Visual Studio Code

Windows で Anaconda を使用して主に Python でコーディングしたりしています。Notebook 中心でスクリプトファイルをいじるときは Windows 用 Python 付属の IDLE を少し使うみたいな感じだったのですが、最近思い立って Visual Studio Code 環境に移行しつつあります。で、このエントリは Anaconda 用に Visual Studio Code を設定したときの覚え書きです。Python Extension をインストールした後あたりからの話です。

Python interpreter の選択

公式 Tutorial にも書いてあるしエディター上でもメッセージが出たりしますが、まずは Python interpreter を選択しなければなりません。ここで Anaconda 環境の python.exe を選択します。

conda.bat のあるところにパスを通しておく

作成したスクリプト実行時はまず conda コマンドが起動され、選択されている python.exe に応じた仮想環境が activate されてスクリプトが実行されるわけですが、この conda が見つからずエラーになってしまいます。Visual Studio Code のドキュメントをみると python.condapath を設定するとあるのですが、これが効きません。

ググってみると、この設定での成功例は見当たらないし、関連ありそうな Issue は Open のままっぽいので、素直にパスを設定することにしました。Windows 「システムの詳細設定」から環境変数で Path に “C:\ProgramData\Anaconda3\Scripts” を追加しています。

conda init powershell を実行する

Visual Studio Code 内で起動するターミナルではデフォルトで PowerShell が起動されるようです。Anaconda プロンプトを起動後、以下のコマンドを実行し Anaconda 関連初期化処理をPowerShell 用初期化スクリプト (profile.ps1) に追加します。

conda init powershell

PowerShell 実行ポリシー変更

デフォルト設定のままだと初期化用の profile.ps1 が実行許可されずエラーとなってしまうため、PowerShell Execution Policy を緩くします。ここではローカルのスクリプトは署名がなくても実行できるようにします。(RemoteSigned: 詳細はこちら)

以下を管理者権限で起動した PowerShell 上で実行します。

 Set-ExecutionPolicy RemoteSigned 

以上で Python スクリプトを Visual Studio Code から起動できるようになったはずです。

ということをもろもろ確認しながら書いていたらここの回答そのままじゃん、と見つけて思ってしまいました。やはり探すべきは stack overflow ですよね。

Docker で WSL 2 エンジン有効化後、既存 Hyper-V ボリュームアクセス不可問題

まあ、当たり前と言えば当たり前なんですけどね。私は Docker に関して一般ユーザーなので、 ‘Use the WSL 2 based engine’ を有効化したとたんにこれまで使用できたボリュームにアクセスできなくなって焦りました。そんなときどうするかという話です。今更という感じの話題かも知れませんが…。

シナリオとしては以下の通りです。

  1. これまで Docker Desktop for Windows を Hyper-V backend で使用してきた。
  2. メリットが多いというので WSL 2 backend に切り替えた。
  3. 切り替えた後は既存ボリュームにアクセスできない!

さて、この時どうするかなのですが、まず焦らず もう一度 Hyper-V backend に戻します。backend を切り替えるだけでは Hyper-V 上の Docker サービス用 VM が削除されたりするわけではないので Docker Desktop で設定を元に戻せばこれまでの image や volume がまた使用できるようになります。

続いて volume の内容をエクスポートします。以下のように busybox を使用して Windows のファイルシステムと Docker ボリュームをマウントした後、ボリュームの内容を tar.gz ファイル on Windows ファイルシステムとしてエクスポートします。

(ボリュームを確認)
C:\>docker volume ls
 DRIVER    VOLUME NAME
 local     myvolume
(ボリュームをエクスポート)
C:\>docker run -v myvolume:/src -v C:\Temp:/dest -i -t busybox
 / # tar -zcvf /dest/volume-backup.tgz src
 / # exit

C:\>

あとは WSL 2 backend に切り替え新規ボリュームを作成してから .tgz を戻せばよいです。私の場合は WSL の Linux ファイルシステム上に適当に展開して ‘-v /home/blogger323/dockervol:/mnt’ のように bind mount するようにしました。

ちなみにですが、WSL 2 backend に切り替えた後のボリュームは以下のパスで Windows からアクセス可能です。

\\wsl$\docker-desktop-data\version-pack-data\community\docker\volumes

参考情報:

Migrating existing containers from Hyper-V to WSL2 technology

追記: リソース消費量を抑えるには (2021.5.2)

WSL 2 を Docker エンジンとして選択すると、Docker 用の WSL distribution として以下のように docker-desktop、docker-desktop-data の 2つが新たに作成されます。

C:\>wsl.exe --list -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2 

上のリストで Ubuntu は元から使っていた distribution ですが、WSL integration の設定でこの Ubuntu と Docker を統合すると Docker を使っているときはこれら 3つの WSL 環境が起動することになります。本文では WSL integration の設定を行う想定で bind mount するとしましたが、WSL 用メモリの使用量は多くなりがち (参考)なので統合せず Docker volume を作ることで起動する WSL 環境を docker 関連の 2つのみにするという選択肢もありだと思います。

その場合も、本文最後に書いたパスを用いて Windows から直接 volume 内のファイルにアクセスすることが可能です。

更に追記 (2021.5.15):
よく考えると Docker Desktop を使わずに WSL で Linux 版 Docker を動かせばよいような気がしてきました。

WSL 2 で Windows から Linux マシンをどう参照するか問題

WSL 2 に mysql や apache を入れて Windows のプログラムから接続したくなることがあります。さてどうしましょうか。

build 18950 以上であれば localhost 指定で OK

Windows 10 build 18950 以上であれば Windows のループバックアドレス (localhost) のポートから自動的に Linux マシンにポートフォワードしてくれます。ですので Linux で Apache を動かして Windows のブラウザから http://localhost/ と指定してアクセスすることができます。

参考記事: https://www.atmarkit.co.jp/ait/articles/1909/09/news020.html

生のアドレスでアクセスしたい時もある

ただ、Web アプリ関連でリダイレクト等が絡むと localhost を使った URL では動かないケースがあるようです。私もそのあたりが原因でハマりかけたので開発時は Linux マシンのアドレスを使ってアクセスしています。ここで困るのが Linux マシンの IP アドレスを固定できないことです。調べた限りでは方法がなさそうです。

そこで策として考えられるのが、Windows の hosts ファイルを Linux マシン起動後に更新してホスト名を固定する方法です。世の中にはこれを Windows サービス化された方がいらっしゃいます。

https://github.com/shayne/go-wsl2-host

私の場合はこれだけのためにサービスを増やすのも大ごとのように感じてしまい、以下のような簡単なスクリプトを作って WSL 起動後に Windows 管理者権限で実行しています。元々 hosts ファイルはいじっておらず空のままだったので完全に置き換える動作にしていますが、ちょっと変えれば静的レコード+動的レコードみたいな使い方もできると思います。ご参考まで。

Linux 側 IP アドレス表示スクリプト

$ cat /usr/local/bin/genhosts
#!/bin/sh
IP_ADDR=hostname -I
while read -r line; do
  echo $IP_ADDR $line
done < /usr/local/etc/wslhosts

Windows 側から参照したいホスト名を記述するファイル

hetarena.com 開発環境の例ですw

$ cat /usr/local/etc/wslhosts
hetarena-dev.com
en.hetarena-dev.com
ura.hetarena-dev.com

Windows 側スクリプトファイル

以下の内容の .cmd ファイルを作って WSL 起動後に管理者権限で実行します。

wsl.exe genhosts > C:\Windows\System32\drivers\etc\hosts 

すると以下のような hosts ファイルが生成されます。

172.27.43.123 hetarena-dev.com
172.27.43.123 en.hetarena-dev.com
172.27.43.123 ura.hetarena-dev.com

これで Windows ブラウザから http://hetarena-dev.com を参照したりすることができます。

その他

  • WSL 2 のファイルシステムを参照したいときは ‘\\WSL$\’ を Explorer から開けばよいですね。
  • 逆に Linux から Windows を参照する場合は “ホスト名.mshome.net” というホスト名が使用できます。

関連情報: WSL2 Set static ip? 
https://github.com/microsoft/WSL/issues/4210

hetarena.com rebooted!

いやはや。
3年超放置してしまいましたが、また細々と更新していきます。

この間色々ありましたが、突然部門がなくなったり、転職したものの上司と合わなかったり、家庭も子供が手がかかったりでそこそこ大変な日々を過ごしておりました。 もろもろの私生活をすべて記事にできるかわからないのですが、また IT系、趣味系は問題ないと思いますので記事を増やして行きたいと思います。

放置していた間、暖かいコメントくださった皆様、ありがとうございました。ポジティブな気分になれました!

Happy New Year!

昨年は春頃から仕事が忙しくなり、ブログもその他もろもろも放置してしまいました。 ブログに関して、今年はきちんと月1本は記事を書いていきたいです。

今考えているネタとしては以下の様な感じ。

  • 40歳を超えてからの転職
  • アジアの仕事と英語

けっしてブログを引退したわけではないです。 今年も細々と続けていきます。

バトスピ: 指定アタック後にブロックされない効果

いきなりバトルスピリッツです。 すみません、もともと方向性も何もない備忘録ブログだったりするので。

と言っても子供がハマっているのに付き合っている状態です。 ブースターパックを開けると、強いカードは全部子供のものになるので、余った黄色で頑張っています。 黄色はうまく使えればそこまで弱くないのでしょうが、萌え系カードということもあって余るのです。 決して私の趣味ではありません、はい。

まあ、負けてばかりなのが嫌で、ちょっとシングル買いをしたりもしますが…w

というわけで、今回は子供がカードダスナビに電話で質問した内容です。 備忘録&誰かのためになればと。

質問

ホワイトフォックスに蛇星鞭サビクがブレイヴした状態で、サビクのブレイヴ時効果を使って相手の疲労状態スピリットに指定アタックしたとします。 この後、アルティメットトリガーがヒットし、相手のスピリットからブロックされない効果が発揮したとき、どうなるのでしょう?

ひょっとして、そのままライフ削れる? (by 子供)

答え

この場合、相手のスピリットからブロックされないので、指定アタックが無効となります。 なので、アルティメットが居ればアルティメットでブロックすることができます。

子供の反応

まあ、アルティメットが居たらクロスアルティメットトリガーで手札に戻すけどね (ニヤリ)。

父はいつまで経っても各カードの効果が頭に入らず、子供の記憶力のよさにバカ親ぶりを発揮している今日この頃です。

Minified JavaScript と Source Maps、とか

あっと言う間に 2015年が始まって 1ヶ月半が経過してしまいました。 本業がバタバタしているのもあってこれが今年初のエントリだったりします。

今回は PhpStorm を使った開発関連の小ネタを 2つほど。 PhpStorm といいつつ PHP には関係ない部分なので WebStorm でもオッケーなはずです。

Minified JavaScript とデバッグ

WordPress プラグインの開発に PhpStorm を使っているわけですが、いわゆる .min.js ファイルを作って圧縮しつつ、ソースファイルを使ってデバッグするための方法を探していました。 Source Maps ファイルを使えば良いということはわかっていたのですが、今一つうまく動かないのでちょっと気合を入れて調べてみました。

すると日本語で答えが見つかったので感動しました!

重要なのは Closure Compiler に以下のオプションを与える部分です。

--create_source_map $FileNameWithoutExtension$.min.js.map 
--output_wrapper "%output%//@ sourceMappingURL=$FileNameWithoutExtension$.min.js.map"

こちらの記事でも説明されていますが、圧縮して作った .min.js の最後に Source Maps ファイルの URL を埋め込まなければなりません。 以下のような内容を最後に入れるのです。

//@ sourceMappingURL=standard-widget-extensions.min.js.map

これを行うために –output_wrapper オプションを使って出力の最後にソースマップ用 URL を追加しています。

これで圧縮前の JavaScript ソースを使ってデバッグできるようになりました。 英語も含めて探したのですが、見つかったのは先の日本語記事のみ。 バシャログさん、有益な情報ありがとうございます。

ちなみに YUI Compressor では Source Maps 出力用オプションを見つけることができず、UglifyJS は調べてません。

Git merge と PhpStorm

WordPress プラグインは一人で開発しているので、なるべくマージが発生しないようにしてきましたし、やむを得ない場合はできるだけコンフリクトが生じないようにしてきました。 ところが最近、開発を進めてだいぶコードをいじった後に、リリースしているバージョンの微修正版を出さなければならない状況が生じました。 もう仕方ないのでブランチを切ってみました。 そして、恐る恐る PhpStorm 上で git merge をしてみました。

そしたら、思ったより簡単。 PhpStorm (WebStorm) はマージツールとしてとても扱いやすいです。 下の画面の様にマージ結果を中心に 3つ並べて「×」や「>>」、「< <」を使って操作することができます。 merge (phpstorm)

以下の記事に従えばコマンドライン git のマージツールとして PhpStorm (WebStrom) を登録することもできます。

結び

今年も相変わらずマイペースにブログを続けていこうと思いますので、よろしくお願いします。