しばたテックブログ

気分で書いている技術ブログです。

Windows 10のコンテナーとDockerを試す

Anniversary Update(1607)が来てWindows 10でもHyper-Vコンテナーの機能が使える様になったので早速試してみました。

注意事項

本エントリは2016/08/05に公開後、2016/09/28に最新の情報に基づいて全面的に書き直しています。

2016/10/18 追記

Docker for Windows 1.12.2 Bata26以降で、Docker for Windowsにdockerd.exeが同梱される様になりました。
(現時点ではまだBeta Channelのみの配信ですが...)

stknohg.hatenablog.jp

Docker for Windowsとの併用を考えているのであればこちらをインストールしてしまう方が楽かもしれません。

以降の手順は、Docker for Windowsを使わない、Hyper-Vコンテナーの機能を単体で使う場合のものとなります。

手順

基本的にはMSDNのQuick Start*1の内容に従って試しています。

msdn.microsoft.com

このQuick StartではGithubからDocker 1.13の開発版をダウンロードしていますが、開発版は日々更新され不安定なビルドもあるため(実際この書き直しをする際のビルドではdocker runするとハングする事象に遭遇してしまいました...)今のところ確実に利用できるDocker 1.12.1を使う様にしています。

エントリ公開当時に発生した問題

公式に提供されているDocker 1.12だとdocker run時にエラーが出てしまいます。

エラーメッセージはこんな感じで、

Error response from daemon: container [コンテナID] encountered an error during CreateContainer failed in Win32: A connection could not be established with the Virtual Machine hosting the Container. (0xc0370108)

といったメッセージが出てしまいます。

PS C:\> docker run -it microsoft/nanoserver cmd
C:\Program Files\docker\docker.exe: Error response from daemon: container 7ea19efb3c9f31092d5a4fcd9dd0115a21839cecb0bfaf
3bc99406d46a38667f encountered an error during CreateContainer failed in Win32: A connection could not be established wi
th the Virtual Machine hosting the Container. (0xc0370108) extra info: {"SystemType":"Container","Name":"7ea19efb3c9f310
92d5a4fcd9dd0115a21839cecb0bfaf3bc99406d46a38667f","Owner":"docker","IsDummy":false,"VolumePath":"","IgnoreFlushesDuring
Boot":true,"LayerFolderPath":"C:\\ProgramData\\docker\\windowsfilter\\7ea19efb3c9f31092d5a4fcd9dd0115a21839cecb0bfaf3bc9
9406d46a38667f","Layers":[{"ID":"7e26e071-3393-5a50-9d0f-623ee586e644","Path":"C:\\ProgramData\\docker\\windowsfilter\\2
4dafad04655d4bd2338442ac12a5c4409883eed90e32af9e80abc3fbd79b833"}],"HostName":"7ea19efb3c9f","MappedDirectories":[],"San
dboxPath":"C:\\ProgramData\\docker\\windowsfilter","HvPartition":true,"EndpointList":["3b6b30c2-c474-4f12-8602-9abc55c0d
10e"],"HvRuntime":{"ImagePath":"C:\\ProgramData\\docker\\windowsfilter\\24dafad04655d4bd2338442ac12a5c4409883eed90e32af9
e80abc3fbd79b833\\UtilityVM"},"Servicing":false}.

StackOverflowに同様の問題が報告されており、エントリ公開当初はこの内容を一部反映していました。

stackoverflow.com

検証環境

評価環境には日本語版のWindows 10 Enterprise 評価版(1607 Build 14393)を使いました。
初期セットアップが終わって最低限のネットワーク設定済みの状態で試しています。

Windows 10のコンテナーとDockerを試す

以降の手順は基本的に要管理者権限です。

1. 機能の追加

最初にHyper-Vコンテナーの機能を追加します。
GUIからやっても良いですが、Quick Startの内容に従ってPowerShellから追加します。

# 機能の追加
Enable-WindowsOptionalFeature -Online -FeatureName Containers -All
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

# 普通にやると自動で再起動するので -NoRestart を付けた方が良いかもしれません。  
#Enable-WindowsOptionalFeature -Online -FeatureName Containers -All -NoRestart
#Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All -NoRestart
#Restart-Computer -Force

Windows 10 は Hyper-V コンテナーにのみ対応しているためHyper-Vも有効にする必要があります。

以前にDockerを試してレジストリの変更をした場合は以下のコマンドでもとに戻しておく必要があります。
(今回が初回の場合は何もしなくてOKです)

# 以前の問題に対応するためのレジストリの変更の取り消し
Set-ItemProperty -Path 'HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers' -Name VSmbDisableOplocks -Type DWord -Value 0 -Force

# 以下のレジストリ変更をした場合に戻す必要があります
#Set-ItemProperty -Path 'HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers' -Name VSmbDisableOplocks -Type DWord -Value 1 -Force

2. Dockerのインストール

最初に述べた通りDockerはVer.1.13の開発版ではなく、GitHub上にあるVer.1.12.1をインストールします。

PowerShellから以下のコマンドを実行し、$env:ProgramFiles\docker\docker.exedockerd.exedocker-proxy.exeを配置します。

# ダウンロード
Invoke-WebRequest "https://get.docker.com/builds/Windows/x86_64/docker-1.12.1.zip" -OutFile "$env:TEMP\docker-1.12.1.zip" -UseBasicParsing
# 解凍
Expand-Archive -Path "$env:TEMP\docker-1.12.1.zip" -DestinationPath $env:ProgramFiles

【2017/03/17追記】

現在の最新バージョンは17.03です。
各バージョンはGitHubのReleaseで公開されていますので、ここから必要なバージョンをダウンロードするのがよさそうです。

# 64Bit版をダウンロードする例
Invoke-WebRequest -Uri "https://get.docker.com/builds/Windows/x86_64/docker-17.03.0-ce.zip" -OutFile "$env:TEMP\docker-17.03.0-ce.zip" -UseBasicParsing
Expand-Archive -Path "$env:TEMP\docker-17.03.0-ce.zip" -DestinationPath $env:ProgramFiles

【追記ここまで】


配置後、$env:ProgramFiles\docker\をPATHに追加します。

# PATHの設定
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";$env:ProgramFiles\docker\", [EnvironmentVariableTarget]::Machine)

ここで一旦コンソールを再起動して環境変数を更新します。
続けてdockerd.exeをサービスに登録し起動します。

# サービス登録
dockerd --register-service
# サービス起動
Start-Service docker

これでDockerのインストールは完了です。
バージョンを確認するとこんな感じになります。

f:id:stknohg:20160928003134p:plain

3. Dockerを試す(コンテナイメージのPull)

ここからはQuick Startの内容に従ってNano ServerのコンテナイメージをカスタマイズしてHello Worldまでやってみます。

最初にDocker Hubにあるmicrosoft/nanoserverをPullします。

docker pull microsoft/nanoserver

f:id:stknohg:20160928003240p:plain

Pullし終わったらdocker imagesでイメージを確認してみます。

docker images

結果はこんな感じになります。

f:id:stknohg:20160928003303p:plain

3. Dockerを試す(コンテナのデプロイ)

Pullしたmicrosoft/nanoserverイメージをrunしてコンテナを起動します。
docker runのオプションの-itは対話モード(interactive + tty)になります。

ここではQuick Startと少し内容を変えてcmd.exeでなくpowershell.exeを起動する様にしています。

docker run -it microsoft/nanoserver powershell

実行するとコンテナが起動し、PowerShellで対話できる様になります。

f:id:stknohg:20160928003334p:plain

f:id:stknohg:20160928003339p:plain

$PSVersionTableを確認してみるとちゃんとNano Serverであることがわかります。
ここから背景色がおかしくなってますが、まあ、そんなものでしょう...

f:id:stknohg:20160928003354p:plain

Hello Worldするためのスクリプトを仕込みます。
次のコマンドでC:\helloworld.ps1を作ります。

Add-Content C:\helloworld.ps1 'Write-Host "Hello World"'

結果以下の様になっていればOKです。

f:id:stknohg:20160928003417p:plain

最後はexitして終了します。

次にこの変更をCommitして新たなイメージを作ります。
docker ps -aで先ほどのコンテナを確認します。

docker ps -a

結果は以下の様になり、今回の場合はコンテナIDは1258d515295eです。

f:id:stknohg:20160928003441p:plain

docker commitで変更をコミットします。
コマンドの書式は以下となります。

docker commit <コンテナID> <新イメージ名>

今回はコンテナIDをdocker ps -lqで取得しています。
-lは前回実行したコンテナを取得、-qはIDのみ取得になります。

$Id = docker ps -lq
docker commit $Id helloworld

実行するとこんな感じになります。

f:id:stknohg:20160928003853p:plain

docker imagesで確認すると新しいイメージhelloworldができていることがわかります。

f:id:stknohg:20160928003908p:plain

4. Dockerを試す(コンテナの実行)

最後にイメージhelloworldを実行します。

docker run --rm helloworld powershell c:\helloworld.ps1

f:id:stknohg:20160928003926p:plain

ちゃんと新しいイメージからコンテナが実行されHello Worldが表示されました。

最後に

とりあえずこんな感じです。

WindowsコンテナーのイメージはDocker Hubで公開されていますので他のイメージを使って色々すると楽しいでしょう。

次はこのコンテナーの機能とDocker for Windowsが共存可能なのか調べていきたいと思い(当時)以下の記事を書きましたので参考にしてください。

stknohg.hatenablog.jp

*1:日本語訳もあるのですが若干情報が古い様です...

PowerShellの起動時に読まれるps1xmlファイルについて

最高に誰得なエントリです。

PowerShellの起動時に読まれるps1xmlファイル

stknohg.hatenablog.jp

で触れましたが、PowerShell 1.0~5.0ではpowershell.exeの起動時に$PSHOMEにある*.format.ps1xml*.types.ps1xmlを読み込み、オブジェクトの書式設定やオブジェクトの型に対する追加情報を設定します。

そして、先の記事の以下の引用についてなのですが、

これらのファイルは V2 のサイド バイ サイド インストールをサポートするためにまだ存在するので、ファイルの内容を変更した場合、V5 には影響がなく、影響を受けるのは V2 だけです。 これらのファイルの内容を変更するシナリオはサポートされていないことに注意してください。

ここを読んだ時に最初に「ps1xmlファイルは変更不可では?」という疑問がありました。

というのも$PSHOMEにあるps1xmlファイルは署名付きで改ざんできないはずという記憶があったからです。

ps1xmlファイルの署名

ps1xmlファイルの署名を改めて調べてみました。

結果、PowerShell 1.0 ~ 4.0では$PSHOMEにあるps1xmlファイルはすべて署名付きでした。
下図はPowerShell 4.0のtypes.ps1xmlの例です。最後に署名がついているのがわかります。

f:id:stknohg:20160802184318p:plain

これがPowerShell 5.0では署名がなくなりました。

f:id:stknohg:20160802184328p:plain

PowerShell 5.0で完全に自由にps1xmlファイルを変更可能になったという感じです。

無視されるps1xmlファイルの署名

実は、署名はついてはいるのですが、組み込み済みのスクリプトの署名を検証するのは実行ポリシーがAllSignedの場合のみなので、事実上$PSHOMEにあるps1xmlファイルは改変可能だったりします。*1

ただ、$PSHOMEにあるps1xmlファイルだけは特別扱いされているのではないか?(ps1xmlファイルは実行ポリシーの如何に関わらず常に署名が検証されるのでは?)と思い、試しに適当なps1xmlファイルを変更してみました。

その結果、予想とは真逆にPowerShell 2.0からは実行ポリシーがAllSignedでもps1xmlの署名は一切検証されないことがわかりました。

勿論ユーザーが自分で作ったカスタムps1xmlファイルはAllSignedであればちゃんと署名の検証がなされます。
$PSHOMEにあるps1xmlファイルだけが一切検証されません。

以下の図はPowerShell 4.0の環境でFileSystem.format.ps1xmlを変更してみたものです。実行ポリシーがAllSignedGet-AuthenticodeSignatureの結果がHashMismatchと検証に失敗しているにも関わらずPowerShellはエラー無く実行できてしまっています。
ps1xmlに対する変更も普通に反映されました。

f:id:stknohg:20160802184455p:plain

PowerShell 1.0では実行ポリシーがAllSignedであれば署名の検証が行われ、PowerShellの起動時にエラーが出ます。

f:id:stknohg:20160802184634p:plain

ps1xmlファイルのアクセス権

で、事実上改変可能とはいえ署名を一切見ないというのはセキュリティ的に如何なものかと思ったのでもう少し調べてみました。

するとPowerShell 1.0と2.0以降では$PSHOMEにあるps1xmlファイルのアクセス権の付き方がまるで別物になっている事がわかりました。

PowerShell 1.0ではAdministratorsであればps1xmlファイルに対してフルアクセス権を持っています。
下図はtypes.ps1xmlの場合です。

f:id:stknohg:20160802185345p:plain

これがPowerShell 2.0以降では、TrustedInstallerがファイルの所有権とフルアクセス権を持っておりその他のユーザーはAdministratorsであってもReadOnlyとなっています。

f:id:stknohg:20160802185811p:plain

この状態からだと一度所有権を取らないとアクセス権の変更ができず中身を変えることもできません。

どうやらPowerShell 2.0からは署名の検証をしない代わりにファイルのアクセス権を強化することでps1xmlファイルを保護する方針に変わったと思われます。(もしかしたら単純にOSのアクセス権強化なだけかもしれませんが...)

まとめ

ここまでの話をまとめると以下の様になります。

PowerShell 1.0

  • PowerShell 1.0では$PSHOMEにあるps1xmlファイルの署名は検証の対象となります。(実行ポリシーがAllSignedの場合のみ)
  • $PSHOMEにあるps1xmlファイルはAdministratorsであれば変更できます。

PowerShell 2.0 ~ 5.0

  • PowerShell 2.0 ~ 5.0では$PSHOMEにあるps1xmlファイルの署名は一切検証されません。
  • ユーザーが自作したps1xmlファイルの署名は検証の対象となります。(実行ポリシーがAllSignedの場合のみ)
  • $PSHOMEにあるps1xmlファイルはAdministratorでも基本的に変更できません。(ファイルの所有権を変えれば変更可能になる)

PowerShell 5.1 ~

  • PowerShell 5.1からは$PSHOMEにあるps1xmlファイルは使われなくなります。

最後に

ここからは完全に私見です。

PowerShellがリリースされた当初は実行ポリシーがAllSignedの運用が広まり普通に署名付きスクリプトが使われる未来を想定していたのでは?という気がします。*2
しかしながら、現実には実行ポリシーはRemoteSignedで運用されることが大勢となりps1xmlファイルの署名が意味を成さなくなったためアクセス権を強化することによる保護に切り替えたのではないかと思います。
加えてPowerShell起動時のパフォーマンス向上のためにps1xmlファイルの署名の検証がされなくなり、最終的にはファイルを使うこと自体を止めたのだと思います。

最高に誰得なエントリでしたが何気なく調べた結果が結構面白いことになったので私は満足です。

*1:大抵の場合RemoteSignedで運用しているでしょうし...

*2:まあ、その割には署名を付ける機能が貧弱でしたが...

WMF 5.1の新機能をざっくり説明する - 4. PowerShell エンジンの機能強化 編

今回は、

  1. 新シナリオと機能 編
  2. バグ修正 編
  3. コンソール機能強化 編

の続きです。

今回の対象範囲

WMF 5.1 の PowerShell エンジン機能強化 | Microsoft Docs

についてわかる範囲で補足を入れていく感じにします。

パフォーマンス

パフォーマンスの改善について

いくつかの重要な部分でパフォーマンスが向上しました。
* 起動
* ForEach-Object や Where-Object などのコマンドレットに対するパイプライン処理が、約 50% 速くなりました

については、仮想マシン上の検証環境で試してる限りでは「気持ち早くなったかな?」という程度でした。
ベンチマーク結果については環境に依存する部分もあるでしょうし、軽く捉えておけば良いかと思います。

起動時の処理について

そして、Noteにさらっと重要なことが書いてあります。

起動に関連する 1 つの変更が、一部のサポートされていないシナリオに影響を与える可能性があります。
PowerShell は $pshome*.ps1xml ファイルを読み込まなくなりました。これらのファイルは、XML ファイルの処理でファイルと CPU にオーバーヘッドが発生しないよう、C# に変換されています。

$pshome\*.ps1xmlは要はpowershell.exeと同じフォルダに置かれたXMLファイルで以下の二種類あります。

  • *.format.ps1xml - オブジェクトの表示書式の設定
  • *.types.ps1xml(typesv3.ps1xml) - オブジェクトの型に対する追加情報

フォルダ構成としてはこんな感じ。個別のファイルの説明はしません(

f:id:stknohg:20160731184210p:plain

パフォーマンス向上のためにXMLファイルの読み込みを止め、その部分をC#のコードにしたとの事なのですが、ざっと調べた限りではC#のコードがどこにあるのかわかりませんでした。
(単純にXMLがリソースになったという感じではありませんでした。継続して調べていきたいですね...)

変換されたc#のコードはSystem.Management.Automation.dll中のSystem.Management.Automation.Runspaces名前空間に各ps1xmlファイルと対になるクラスになっていました。
例えばFileSystem.format.ps1xmlであればSystem.Management.Automation.Runspaces.FileSystem_format_Ps1Xmlクラスになっています。

なお、起動時に*.ps1xml読まなくなっただけで*.ps1xmlが使われなくなったわけではないのでご注意ください。
*.format.ps1xmlファイルとUpdate-FormatDataコマンドレットを使った表示のカスタマイズや、*.types.ps1mxlUpdate-TypeDataを使ったオブジェクトのカスタマイズはこれまで通り利用可能です。

最後に、

これらのファイルは V2 のサイド バイ サイド インストールをサポートするためにまだ存在するので、ファイルの内容を変更した場合、V5 には影響がなく、影響を受けるのは V2 だけです。 これらのファイルの内容を変更するシナリオはサポートされていないことに注意してください。

とある様にこの変更の影響を受けるのはPowerShell 5.1のみとなります。

powershell.exe -version 2.0

とバージョン指定してPowerShell 2.0を実行したときは*.ps1xmlは読み込まれます。
(ちなみに3.0~5.0は読み込まれませんでした。起動時にロードする.NET Frameworkの違いによるものでしょう。)

簡単な例で試してみます。
FileSystem.fromat.ps1xmlを以下の図の様に修正し、Get-ChildItem(ls)したときに表示されるファイルのLastWriteTimeを和暦に変えるカスタマイズをしてみました。

f:id:stknohg:20160731184704p:plain

PowerShell 5.1を起動してもこの変更は読み込まれず、Get-ChildItem(ls)の結果は変わりませんが、

f:id:stknohg:20160731184955p:plain

powershell.exe -version 2.0とした場合は表示が変わることが確認できます。

f:id:stknohg:20160731185006p:plain