Mon, Sep 12, 2016

Hyper-Vコンテナ(Nano Server)でunzipしたいならjarを使え

Hyper-Vコンテナ(Nano Server)でunzipしたいならjarを使え

Nano Serverでunzipしたかっただけだったのに、妙に苦労した話。


Nano Serverとは

Nano Serverは、Windows Server 2016で追加されるWindows Serverの新たなインストール形式で、Server Coreよりさらに機能を絞り、リモートで管理するクラウドホストやWebサーバ向けにに特化したもの。

Server Coreが数GBくらいなのに対し、Nano Serverは数百MBととても軽量で、それゆえ起動が速くセキュア。

unzipとは

unzipとは、zipファイルを解凍する、ただそれだけのこと。

ただそれだけのことで、基本的な機能だと思うのだが、Windowsはこれをコマンドラインで実行する方法をつい最近まで正式に提供していなかった。

Nano Serverでunzip

Windows 10のHyper-V Containersの上でPcap4JのビルドとテストをするDockerイメージをビルドしたくて、そのための依存ライブラリなどをインストールする処理をDockerfileに書いていて、ADDでzipをダウンロードしたところまではいいんだけど、このzipどうやって解凍してやろうかとなった。 (Dockerホストに置いたものをコンテナにADDするのはなんか格好悪いから無しで。Dockerfile裸一貫で実現したい。)

Windows 10のHyper-V Containersは、現時点でNano Serverしかサポートしていないのが厳しい点。Server Coreだったら楽だったのに。


以下、いろいろ試したことを書く。

正攻法: Expand-Archive

PowerShellの v5 で実装されたExpand-Archiveというコマンドレットでzipを解凍できる。 Nano ServerのPowerShellのバージョンを確認したら 5.1 だったのでこれでいけるかと思った。

C:\>powershell -command "$PSVersionTable.PSVersion"

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      14284  1000


したらこのエラー。

Add-Type : Cannot find path 'C:\System.IO.Compression.FileSystem.dll' because it does not exist.
At
C:\windows\system32\windowspowershell\v1.0\Modules\Microsoft.PowerShell.Archive\Microsoft.PowerShell.Archive.psm1:914
char:5
+     Add-Type -AssemblyName System.IO.Compression.FileSystem
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\System.IO.Compression.FileSystem.dll:String) [Add-Type], ItemNotFoun
   dException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.AddTypeCommand

どうもPowerShellの 5.1 以降には、.NET FrameworkベースのDesktop Editionと、そこから機能を絞った.NET CoreベースのCore Editionがあり、Nano ServerのはCore Editionなんだそうな

Expand-ArchiveはSystem.IO.Compression.FileSystem.dllの中のZipFileクラスに依存しているんだけど、.NET CoreにはSystem.IO.Compression.FileSystem.dllが含まれていないっぽい。

ShellオブジェクトのCopyHere

PowerShellでのunzip方法を調べたらStack Overflowにいくつか載っていた。 Expand-Archiveと、System.IO.Compression.ZipFileを直接使う方法と、Shellオブジェクト(COMオブジェクト)のCopyHereメソッドを使う方法。

最初の2つはCore Editionでは使えないことが分かっているので、3つめにトライ。 こんなの↓

$shell = New-Object -ComObject shell.application
$zip = $shell.NameSpace("C:\a.zip")
MkDir("C:\a")
foreach ($item in $zip.items()) {
  $shell.Namespace("C:\a").CopyHere($item)
}

調べたらこの方法はMicrosoftから非推奨にされていることが分かったんだけど、一応やってみる。

したら以下のエラー。

new-object : Retrieving the COM class factory for component with CLSID {00000000-0000-0000-0000-000000000000} failed
due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).

この方法で利用しようとしているのはWindows shell、つまりWindows Explorerの機能らしく、そうであればまあGUIがないNano Serverで動かないのも当然か。

サードパーティツールに頼る

Stack Overflowの別の質問にサードパーティツールに頼る方法がいくつか紹介されていた。

ここで挙げられていたもののうち、7-ZipFreebyte ZipInfo-ZIPは、配布形式がダメ。

7-Zipのインストーラをコンテナで実行してみたら、「The subsystem needed to support the image type is not present.」というエラー。7-Zipにはzipで配布されているものもあるんだけど、皮肉としか思えない。

Freebyte ZipやInfo-ZIPの自己解凍ファイルもコンテナ内では動いてくれない。

一方、pkunzipはコマンドが裸で配布されているので行けるかと思ったが、実行したら「The system cannot execute the specified program.」なるエラー。 よく見たらこれ16bitアプリケーション。Nano Serverは32bitアプリすら実行できないというのに。

jarに託された最後の希望

上に貼ったStack Overflowの質問にはjarコマンドを使う方法も挙げられていたが、JDKなんてどうせインストールできないとあきらめていた。

が、ふと思い立ってDocker Hubを検索してみたら、OpenJDK入りのNano Serverをアップしてくれている人がいた。

pullしてrunしてみたら確かにJDKがインストールされていた。

C:\>java -version
openjdk version "1.8.0_102-1-ojdkbuild"
OpenJDK Runtime Environment (build 1.8.0_102-1-ojdkbuild-b14)
OpenJDK 64-Bit Server VM (build 25.102-b14, mixed mode)

で、無事"C:\Program Files\Java\bin\jar.exe" xf hoge.zipのようにしてunzipできた。

ここまで1日かかった。