Windowsの上のDockerの上のLinuxの上でDockerを動かしてみたかった
概要
この記事は結論としてできなかったことを供養するためのものです。
Dockerをいじりはじめて1週間なのもありいろいろ気が付いたこともあったので、一緒に笑い飛ばしていただけると幸いです。
気づき
先日、Windows ContainerをDockerで利用できるようにしました。
この時、Hello World代わりに作成したDockerfileを見ていて気になったことがありました。
FROM mcr.microsoft.com/powershell:windowsservercore-ltsc2022
# powershellイメージでは「C:\Program Files\PowerShell\pwsh.exe」がpowershellの実行ファイル
SHELL ["pwsh.exe", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
# test用のユーザーを作成
RUN net user testuser /ADD
RUN NET LOCALGROUP Administrators /add testuser
USER testuser
CMD whoami
今回はpwsh.exe
(コンテナにおけるpowershell.exe
)を指していますが、ここをWindowsコマンドに変更しても実行できることを私は知っていました。
FROM mcr.microsoft.com/powershell:windowsservercore-ltsc2022
# cmd.exeに変更してみても動きは変わらない
SHELL ["cmd", "/c"]
# test用のユーザーを作成
RUN net user testuser /ADD
RUN NET LOCALGROUP Administrators /add testuser
USER testuser
CMD whoami
このことからSHELL
とは、ここではRUN
で実行するコマンドのプレフィックス(お約束的に前置する)コマンドと考えていました。
そうであるならば、RUNとは「SHELLに引数を追加して実行する」コマンドでしかありません。
実行できるかは別として、PostgreSQLのpsql -c
(コマンドラインから直接SQLを実行するオプション)などもSHELL
に書けちゃうんだろうなぁと予想。
# PostgreSQLをSHELLとして指定(test_dbはデータベース名)
SHELL ["psql", "test_db", "-c"]
# SQLが実行できるのでは……?(test_tableはテーブル名)
RUN "\
create table test_table ( col varchar(100) );\
insert test_table ( col ) values ('dummy');\
"
これが本当なら1ファイルに初期化処理が収まるのでなかなか便利そうです、未検証ですが。
外部ファイル化するのが一般的かなと思いますが、状況によってはレイヤーも減らせそうなので謎の魅力があります、未検証ですが。
すると気になりませんか。
他に同じようにSHELL
へ共通化することで便利に使えそうなコマンドがないか。
そんなに便利に使えるコマンドなんてあるわけが――
wsl.exe --exec xxxxx
docker exec FFFFFFFFFFFF xxxxx
――ありました、一番身近な奴が。
そういえば私はまだWSLをインストールしないでDockerを触っています。
特に忌避する理由もなかったのですが、どうせなら仮想環境内で完結できるならそれに越したこともなさそうです。
しかしそもそも仮想環境だとはいえ、コンテナー上に仮想環境を構築することはできるのでしょうか?
Windowsコンテナとイメージの選択
とりあえず、まずはWSLを使えることを確認しなければ話になりません。
Windowsコンテナでのベースとなるイメージにはいくつか派生がありますが、大きく分けて4種類あります。
イメージ名称 | |
---|---|
mcr.microsoft.com/windows/nanoserver | Windowsコンテナを実行する最低限をサポートしたイメージ |
mcr.microsoft.com/windows/servercore | Windowsのコマンドをある程度サポートしたイメージ |
mcr.microsoft.com/windows/server | 完全なWindows APIをサポートしたイメージ |
mcr.microsoft.com/windows | windows/serverの旧版。Windows Server2019までをサポートしている |
下に行けば行くほどサポートは厚くなりますが、容量が大きくなります。
上に行けば容量は軽くなりますが、nanoserverまでいくとwhoami
コマンドがインストールされていないなどプログラムをインストール、実行してもらうのに困る可能性が高くなります。
まずは容量重視でnanoserverを検討してみましょう。
nanoserverでのWSL事情
勘の良い方はお気づきかもしれませんがnanoserverにはWSLがインストールされていません。
通常ならC:\Windows\System32
にインストールされているはずなので、Sytem32フォルダの中身を確認してみましょう。
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
RUN dir /b C:\Windows\System32\*.exe
実行結果 | |||
---|---|---|---|
AggregatorHost.exe | ARP.EXE | attrib.exe | auditpol.exe |
autochk.exe | bcdedit.exe | CertEnrollCtrl.exe | CExecSvc.exe |
chkdsk.exe | cmd.exe | conhost.exe | convertvhd.exe |
csrss.exe | curl.exe | DeviceCensus.exe | DiskSnapshot.exe |
diskusage.exe | dllhost.exe | dtdump.exe | expand.exe |
findstr.exe | finger.exe | fltMC.exe | HOSTNAME.EXE |
icacls.exe | ipconfig.exe | lodctr.exe | lsass.exe |
mountvol.exe | MRINFO.EXE | MuiUnattend.exe | net.exe |
net1.exe | netsh.exe | NETSTAT.EXE | NIHOST.exe |
NSRC.exe | ntoskrnl.exe | pacjsworker.exe | PATHPING.EXE |
PING.EXE | rdrleakdiag.exe | reg.exe | regsvr32.exe |
ROUTE.EXE | runexehelper.exe | sc.exe | schtasks.exe |
services.exe | setx.exe | smss.exe | sort.exe |
svchost.exe | tar.exe | taskhostw.exe | TCPSVCS.EXE |
TRACERT.EXE | ucsvc.exe | unlodctr.exe | userinit.exe |
WerFault.exe | WerFaultSecure.exe | wermgr.exe | wevtutil.exe |
wininit.exe | wpr.exe | WUDFCompanionHost.exe | WUDFHost.exe |
xcopy.exe |
実行ファイルに限って実行すると69ファイルが見つかりました。
手元にあるWindows 11 Proで同じコマンドdir /b C:\Windows\System32\*.exe
を実行すると660ファイル見つかったので、通常のPCにくらべてずいぶんと機能が限られていることが分かります。
ちなみに本来であればwhere
コマンドでインストールされているかは検証できます。
しかし一覧の通りwhere
コマンドはインストールされていません。
したがって今回はdir
コマンドで確認せざるをえませんでした。
他にも使いそうなコマンドすらもちょくちょくないのでnanoserverを使用する際は注意したいですね。
閑話休題。
ないのであれば用意すればいいだけの話です。
面白いことに、WSLは様々なインストール方法が存在しています。
- Microsoft StoreでWSLをダウンロードする
- Windowsの機能の有効化または無効化から「Linux用Windowsサブシステム」を有効化にする
-
dism
コマンドを実行する - PowerSellから有効化する
- 直接WSLをインストールする
Microsoft Storeからインストールする
まずは1つ目。
インストールするためには当然、Microsoft Storeを起動する必要があります。
そういえばGUIがDockerで使用できるという話は聞いたことがあるので、きっと起動できるなら操作できるでしょう。
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
USER ContainerAdministrator
CMD start ms-windows-store:
docker run
してdocker ps -a
……っと。
ステータスが「Exited」。つまり起動失敗ですね。
ここで気が付いたのですが、コマンドを実行したいだけならCMD
を書く必要はなくdocker exec
で構いません。
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
USER ContainerAdministrator
修正したDockerfileからコンテナを作成するとうまく起動したのでdocker.exe exec
でコマンドを実行してみましょう。
The system cannot execute the specified program.(指定されたプログラムを実行できません)
GUIが起動できないのか、Microsoft Storeが存在しないのか、あるいはms-windows-store:
がないのかはわかりませんが実行できないようです。
そういえば:
がつく名称というのは確かプロトコルとして扱われたような気がします。
Windowsではプロトコルをプログラムと関連付けすることができるので、実体がどこにインストールされているのかネット検索したところC:\Windows\System32\WSReset.exe
がそうなのだとわかりました。
……先ほどのリストにはありませんね。
WSResetをインストールするということも考えてみましたが、通常はOSのインストールや修復をする
方法しか見つからなかったので、この方法はできないということがわかりました。
それと別にMicrosoft Storeにはwinget
というコマンドも存在します。
こちらはオプションで特定のアプリをダウンロードすることができるものですが、画像の通りユーザーフォルダのAppDataにインストールされているものです。
ですが、今回ログインしているContainerAdministrator(Windowsコンテナの標準管理者アカウント)のフォルダにはマイドキュメントしか存在しません。
つまるところMicrosoft Storeはインストールされていないのでしょう。
これは他のユーザーでも同様でした。
失敗!
「Windowsの機能の有効化または無効化」を変更する
「Windowsの機能の有効化または無効化」は基本的にコントロールパネルから起動するものですが、その実態はOptionalFeatures.exe
という実行ファイルです。
OptionalFeatures
にオプションを入れて設定する方法はわかりませんでしたが、GUIから操作ができるなら設定を変更するだけでいけそうです。
じゃあOptionalFeatures.exe
のパスはどこなのかというと……
やはり先ほどのリストにはありません。
次!
dism
コマンドを実行する
dism
コマンドとは「Windowsイメージを変更するためのツール」……らしいです。
説明はよくわかりませんが、要はシステムの有効、無効を切り替える機能を持つツールで、コマンドプロンプトで以下を実行すればWSLを有効化できるんだそうです。
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
で、そのdism
がどこにあるのかというと……
はい、解散!
PowerSellから有効化する
いえ、まだ解散するのは早いです。
ここまではコマンドプロンプトから起動するという発想が間違っていたのです。
最初に話していたとおり、WindowsのCUIはPowerShellもあります。
PowerShellはもう一覧を見ていただくとわかる通りインストールされていないのですが、すでにPowerShellがインストールされているnanoserverというのが存在しています。
ちなみにPowerShellはLinuxでもインストール可能で、上記のDocker Hubではインストール済みのLinux系OSのイメージをダウンロードすることもできます。
「LinuxコンテナからDockerを利用しているがWindowsに慣れすぎていてBashは無理!」という方は一度試してみるのもいいのではないでしょうか。
さて、ではさっそくWSLを有効にしてみましょう。
FROM mcr.microsoft.com/powershell:lts-nanoserver-ltsc2022
SHELL ["pwsh.exe", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
USER ContainerAdministrator
RUN Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
……はい、わかってました。
「Windowsの機能の有効化または無効化」の実行ファイルがなかった時点で実行できないのは明らかだったので、このコマンドが失敗してしまうのは予測できていました。
でも、あきらめていません。
PowerShellにはInstall-Module
というコマンドがあります。
このコマンドはPowerShell Galleryからモジュール(関数をパッケージしたようなもの)をダウンロードするものです。
そしてみつけました、WSLのパッケージ。
さっそくDockerfileに含めて実行してみましょう。
FROM mcr.microsoft.com/powershell:lts-nanoserver-ltsc2022
SHELL ["pwsh.exe", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
USER ContainerAdministrator
RUN Install-Module -Name Wsl -Verbose -Force -SkipPublisherCheck
なんだかさきほどまでと実行結果が違います。
なんか行けそうな気がします。
行けそうな気がするのですが……どうすればいいんでしょう?
PowerShellのモジュールは、インストールするとモジュールに内包されている命令が使えるようになるはずです。
FunctionsToExport = @("Get-WslDistribution",
"Set-WslDistribution",
"Stop-WslDistribution",
"Remove-WslDistribution",
"Import-WslDistribution",
"Export-WslDistribution",
"Invoke-WslCommand",
"Enter-WslDistribution",
"Stop-Wsl",
"Get-WslVersion",
"Get-WslDistributionOnline")
これは後で見つけたPowerShell Galleryと対応しているGitHubのプロジェクトのページ。
その中にあったWsl.psd1
の一部抜粋なのですが、モジュールをインストールしたのにいずれも使用できませんでした。
それもそのはず。
このモジュール、readme.md
を読めば分かったことなのですがwsl.exe
をラッパーするものでした。
つまりwsl.exe
が存在していることが前提。
インストールされていないnanoserverにおいては全く無意味です。
ちくしょーめ。
直接WSLをインストールする
さて、最終手段。WSLを直接インストールしてみましょう。
実はWSL、GitHub上にプロジェクトが存在していてインストーラも公開されています。
ここからmsiを拝借してDockerfileと同じフォルダに置いてみました。
FROM mcr.microsoft.com/windows/nanoserver:ltsc2022
USER ContainerAdministrator
RUN mkdir "C:\temp"
COPY "wsl.2.0.14.0.x64.msi" "C:\temp"
WORKDIR "C:\temp"
RUN start wsl.2.0.14.0.x64.msi
こんな感じでしょうか。
さっそく実行してみましょう。
……エラーですね。
すっかり忘れていましたが、コマンドプロンプトでmsi拡張子のインストーラを実行するためにはmsiexec
というコマンドから呼び出す必要があります。
ですのでオプションまで含めると以下の通りに修正するとインストーラは実行できるようになるでしょう。
- start wsl.2.0.14.0.x64.msi
+ msiexec.exe /i wsl.2.0.14.0.x64.msi /L*V wsllogs.txt
本来なら。
ここまで読んでいただいた方はオチがもうお分かりかと思いますが、この処理は失敗します。
それはなぜか。
msiexec
は本来、C:\Windows\System32
にインストールされているはずで、nanoserverにはmsiexec
がインストールされていないからです。
完全に詰みまみた。
失礼、詰みました。
nanoserverに関するオチ、あとから気が付いたこと
この記事を執筆するのに整理と検索のし直しをしていたら、以下の記事を見つけました。
- GUIに関する機能やAPIはサポートされない
- Windows OSでアプリやドライバーのインストールによく使われていたMSI形式のインストーラは利用できない
ここまでにやろうとしていたことがすべて集約されていました。
「Windowsの機能を駆使」してnanoserverを拡張しようとするのは誤りだったということですね。
残念。
ちなみにIISの構築や、
VMの起動はできるそうです。だったらコンテナの起動もできそうなものですけど……
フルサイズのWindowsコンテナを使う
さてここまでnanoserverについて言及してきましたが、ここでそもそもの前提を変えてみます。
イメージの大きさを気にしていろいろ犠牲にしてしまったのがいけないのです。
WSLをインストールする
素直にwsl
が実行可能な環境でwsl --install
をしてみましょう。
FROM mcr.microsoft.com/windows/server:ltsc2022
USER ContainerAdministrator
Dockerfileは管理者権限を得るためのユーザー選択のみです。
イメージを作成したのち、WSLが存在しているのか確認してみます。
nanoserverと異なり、where
コマンドもwsl
コマンドもコンテナにインストールされているので簡単に確かめることができました。
次にWSLのバージョンを確認してみます。
wsl --version
を実行したのにも関わらず、コマンドのヘルプ情報が表示されました。
WSL2が使えるバージョンであれば次のようにバージョン情報が表示されるはずなのに、です。
これは古いWSL(WSL1)の挙動で、--version
オプションがないので未定義時の処理が行われた(=ヘルプが表示された)というわけです。
したがってDockerを使用するためには、WSL2を使えるバージョンに上げる必要があります。
docker exec
でwsl --install
を実行してみましょう。
おや、おやおやおや……?
Errorが表示されるまでの間にラグはあったのですが失敗してしまいました。
きっといくつかの処理、たとえばWSLのダウンロードには成功したのでしょう。
エラーコード0x800F081F
は調べてみると.NET Framework 3.5に関するエラーのようです。
.NET Frameworkを含むイメージで試してみる
ならば.NET Frameworkが使用できるイメージからコンテナを作りましょう。
しかしこのコンテナ。
作成元がWindowsCoreになっています。つまりフルサイズのWindowsコンテナに比べてサポートされているソフトウェアが少ない。なんだか嫌な予感がします。
ま、まあコンテナを作って実行してみましょうか。
FROM mcr.microsoft.com/dotnet/framework/runtime:4.8.1
USER ContainerAdministrator
はい、読めてた。読めてました。
この「Class not registered」、ここでは省略しますが純粋なWindowsCoreでも発生しました。
つまるところ実行するには何かしら足りないんですね。
これはもうツンダツンダです。お手上げです。
結論
「Windowsの上のDockerの上のLinuxの上でDocker」を動かしてみたかったのですが、Dockerにたどり着くどころかWSLを実装することができませんでした。
ここまでの説明の中に含めていませんが、nanoserverで試してみたことをフルサイズのWindowsコンテナでも試してみたりすると今度は再起動を要求するエラーでRUN
やCMD
が失敗したりしています。
もう思いつく限りは試してみたので「現状は不可能である」というのを結論とします。
できた方はぜひアンサー記事を書いていただけると嬉しいです。
それはそれとしてここで思い出したのですが、Hyper-Vの仮想環境にHyper-Vの仮想環境を入れ子で起動するためにはSet-VMProcessor
コマンドを一番外側の環境で実行する必要があります。
しかしこのコマンド、オプションに仮想環境の名称を指定する必要があり、Hyper-Vアーキテクチャを利用して稼働するWSLには名称がないので指定することができません。
Hyper-Vで起動中のWindowsでWSLを実行することは可能なので関係のない推測の可能性もありますが、仮にWSLを実行することができたとしても次の段階、入れ子のDockerを導入する段階でエラーになっていた可能性もあります。
面白そうなことが実行できないのは少々悔しいですが、Microsoftがいろいろな派生イメージを用意してくれていること、Dockerの上でのコマンドの挙動など得られた知識は多かったので無駄な一日にはならなかったと思います。
さて、次は何をしてみましょうか。
Discussion