Pcap4Jが動くHyper-VコンテナをWindows 10上でビルドしようとしたけど3合目あたりで息絶えた話。

Hyper-V Containersとは

Hyper-V Containersは、MicrosoftによるWindowsネイティブなコンテナ技術であるWindows Containersの一種で、これによるコンテナは、同じくWindows Containersの一種であるWindows Server Containersのものに比べて、より厳密に隔離されている分、起動コストが高い。

実体はDockerそのもので、コンテナイメージはDocker Hubからpullできるし、コンテナの操作や管理はdockerコマンドでやる。(昔はコンテナ操作用PowerShellコマンドレットもあったが、不評だったので廃止したようだ。) ソースもLinuxとWindowsで一本化されている。

Windows 10のAnniversary Updateで正式にリリースされたが、なんだかあまり注目されていない気がする。

Docker for Windowsとは全く別物なので注意。

Hyper-V Containersのインストール (on VMware Player)

自前のPCが5年前に買ったdynabookでWindows 10をサポートしていないので、VMware PlayerのVM上のWindows 10にHyper-V Containersをインストールしてみる。

VMは、Windows 7に入れたVMware Workstation 11.1.0 build-2496824に付属の VMware Player 7.1.0 build-2496824で作ったもの。 VMのバージョンは11.0。 2CPUでメモリは2.5GB。 ネットワークインターフェースはNAT。 このVMを、Hyper-Vが使えるように設定しておく

この記事にしたがい、Windows 10の評価版をダウンロード。 今公開されている評価版はAnniversary Update適用済みのバージョン1607で、Hyper-V Containersをサポートしている。

これをさっき作ったVMにインストール。

Windows 10を起動し、以下、Windows Containers on Windows 10に従って進める。

  1. containers機能有効化

    PowerShellプロンプトを管理者権限でひらき、以下のコマンドでcontainers機能を有効化。

    Enable-WindowsOptionalFeature -Online -FeatureName containers -All

    1分程度経つと再起動を促されるので再起動。

  2. Hyper-V機能有効化

    再度PowerShellプロンプトを管理者権限で開いて、以下のコマンドでHyper-Vを有効化。

    Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

    1分程度経つと再起動を促されるので再起動。

  3. OpLocks無効化

    現在のHyper-Vコンテナは、安定性を上げるためにOpLocksという機能を無効にすべきらしい。 再度PowerShellプロンプトを管理者権限で開いて、以下のコマンドを実行する。

    Set-ItemProperty -Path 'HKLM:SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers' -Name VSmbDisableOplocks -Type DWord -Value 1 -Force
  4. Dockerインストール

    同じPowerShellプロンプトで以下のコマンドを実行してDocker(EngineとClient)のアーカイブをダウンロード。

    Invoke-WebRequest "https://master.dockerproject.org/windows/amd64/docker-1.13.0-dev.zip" -OutFile "$env:TEMP\docker-1.13.0-dev.zip" -UseBasicParsing

    ダウンロードしたアーカイブを解凍。

    Expand-Archive -Path "$env:TEMP\docker-1.13.0-dev.zip" -DestinationPath $env:ProgramFiles

    ここまででDockerがC:\Program Files\docker\に入るので、このパスを環境変数PATHに追加。

    PATHの変更を反映させるために再度PowerShellプロンプトを管理者権限で開いて、以下のコマンドでDockerデーモンをサービスに登録。

    dockerd --register-service

    Dockerサービスを起動。

    Start-Service Docker

    (Dockerサービスは自動起動に設定されているので、OS再起動時は上記Start-Serviceは不要。)

    これでDockerが使えるようになった。

    PS C:\Windows\system32>docker version
    Client:
     Version:      1.13.0-dev
     API version:  1.25
     Go version:   go1.7.1
     Git commit:   130db0a
     Built:        Sat Sep 10 13:25:48 2016
     OS/Arch:      windows/amd64
    
    Server:
     Version:      1.13.0-dev
     API version:  1.25
     Go version:   go1.7.1
     Git commit:   130db0a
     Built:        Sat Sep 10 13:25:48 2016
     OS/Arch:      windows/amd64
  5. コンテナイメージダウンロード

    どうもDockerコマンドの実行には管理者権限が必要なようなので、このまま管理者権限のPowerShellプロンプトで続ける。

    docker pullでNano Serverのコンテナイメージをダウンロード。

    PS C:\Windows\system32>docker pull microsoft/nanoserver

    docker imagesで確認。

    PS C:\Windows\system32>docker images
    REPOSITORY             TAG                 IMAGE ID            CREATED             SIZE
    microsoft/nanoserver   latest              3a703c6e97a2        12 weeks ago        970 MB

    試しにコンテナ起動。

    PS C:\Windows\system32>docker run -it microsoft/nanoserver cmd

    起動はかなり遅い。1分近くかかった。ともあれちゃんと起動した。

    test_container.png

Pcap4Jコンテナのビルド

Pcap4Jコンテナを、docker buildでビルドしてみる。 Dockerfileはとりあえず以前のものをちょっと書き変えただけのものを試す。

# escape=`

#
# Dockerfile for Pcap4J on Windows Nano Server
#

FROM microsoft/nanoserver
MAINTAINER Kaito Yamada <[email protected]>

# Install Chocolatey.
RUN mkdir C:\pcap4j
WORKDIR C:\\pcap4j
ADD https://chocolatey.org/install.ps1 install.ps1
RUN powershell .\install.ps1

# Install dependencies.
RUN choco install -y nmap jdk7 && `
    choco install -y maven -version 3.2.5

# Build Pcap4J.
RUN powershell -Command Invoke-WebRequest https://github.com/kaitoy/pcap4j/archive/v1.zip -OutFile pcap4j.zip && `
    powershell -Command Expand-Archive -Path pcap4j.zip -DestinationPath .
WORKDIR pcap4j-1
RUN powershell -Command "mvn -P distribution-assembly install 2>&1 | Add-Content -Path build.log -PassThru"

# Collect libraries.
RUN mkdir bin && `
    cd pcap4j-packetfactory-static && `
    mvn -DoutputDirectory=..\bin -Dmdep.stripVersion=true -DincludeScope=compile dependency:copy-dependencies && `
    mvn -DoutputDirectory=..\bin -Dmdep.stripVersion=true -DincludeGroupIds=ch.qos.logback dependency:copy-dependencies && `
    cd ../pcap4j-distribution && `
    mvn -DoutputDirectory=..\bin -Dmdep.stripVersion=true -DincludeArtifactIds=pcap4j-packetfactory-static,pcap4j-sample dependency:copy-dependencies

# Generate sample script. (C:\pcap4j\pcap4j-1\bin\capture.bat)
RUN echo @echo off > bin\capture.bat && `
    echo "%JAVA_HOME%\bin\java" -cp C:\pcap4j\pcap4j-1\bin\pcap4j-core.jar;C:\pcap4j\pcap4j-1\bin\pcap4j-packetfactory-static.jar;C:\pcap4j\pcap4j-1\bin\pcap4j-sample.jar;C:\pcap4j\pcap4j-1\bin\jna.jar;C:\pcap4j\pcap4j-1\bin\slf4j-api.jar;C:\pcap4j\pcap4j-1\bin\logback-classic.jar;C:\pcap4j\pcap4j-1\bin\logback-core.jar org.pcap4j.sample.GetNextPacketEx >> bin\capture.bat

escapeディレクティブが使えるようになっていたので使うようにしている。 (というか以前Windows Server 2016 TP5で試した時はescapeディレクティブをDockerfileの先頭に書かなかったのがだめだったってだけかもしれない。) WORKDIRのパスの区切りにはescapeディレクティブは利かない変な仕様。

Nano ServerでSystem.Net.WebClient使えない問題

このDockerfileでビルドしたら、Chocolateyのダウンロード・インストールスクリプトを実行するRUN powershell .\install.ps1のステップでSystem.Net.WebClientが見つからないというエラー。

new-object : Cannot find type [System.Net.WebClient]: verify that the assembly
containing this type is loaded.
At C:\pcap4j\install.ps1:84 char:17
+   $downloader = new-object System.Net.WebClient
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentExcepti
   on
    + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewOb
   jectCommand

Nano Serverに入っているPowerShellはCore Editionなる機能限定版で、System.Net.WebClientだけでなく、WebアクセスのためのAPIがいろいろ欠けているもよう

Hyper-V ContainersでServer Core使えない問題

Nano Serverめんどくさそうなので、Server Coreをpullする。

PS C:\Windows\system32>docker pull microsoft/windowsservercore

DockerfileのFROMmicrosoft/windowsservercoreに書き変えてビルドしたら、最初のRUNで以下のエラー。

container 4bc8d8d38993426fa7a3c76e4aabbe6a229cbd025754723ff396aec04ffbfa1d encountered an error during Start failed in Win32: The operating system of the container does not match the operating system of the host. (0xc0370101)

調べたら、Hyper-V ContainersはまだNano Serverしかサポートしていないようだ。

unzip難しい問題

Chocolateyのダウンロード・インストールスクリプトを実行するのはあきらめて、アーカイブを自分でダウンロードする方法を試す。

これはhttps://chocolatey.org/api/v2/package/chocolatey/というWeb APIをたたいてアーカイブをダウンロードする方法だけど、このURLをADDに渡してもうまくいかなかったので、このWeb APIが最終的に呼ぶhttps://packages.chocolatey.org/chocolatey.0.10.0.nupkgADDするようにした。 これでダウンロードできるchocolatey.0.10.0.nupkgはzipファイルで、unzipするとインストールスクリプトが出てくる。

しかしこのunzipが曲者で、妙に苦労した話を最近書いた。

で、苦労して取り出したインストールスクリプトを実行したら、エラーがわんさと出ただけだった。 そんなこったろうと思ってはいたが。

どうせChocolateyをインストールできても、パッケージのインストールスクリプトがまた動かないんだろうから、もうChocolateyはあきらめる。

WoW64サポートしてない問題

Chocolateyを使わないようにDockerfileの前半を以下の様に書き変えた。

(snip)

FROM michaeltlombardi/nanoserveropenjdk
MAINTAINER Kaito Yamada <[email protected]>

RUN mkdir C:\pcap4j
WORKDIR C:\\pcap4j

# Install Maven
ADD http://archive.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.zip maven.zip
RUN jar xf maven.zip
RUN powershell -command $env:path += ';C:\pcap4j\apache-maven-3.3.9\bin'; setx PATH $env:path /M

# Install Npcap
ADD https://github.com/nmap/npcap/releases/download/v0.08-r7/npcap-0.08-r7.exe npcap.exe
RUN npcap.exe /S

# Build Pcap4J.

(snip)

(因みにこの時点で、PATHを設定するのにGetEnvironmentVariableSetEnvironmentVariableがうまく使えない問題を乗り越えている。Cannot find an overload for "GetEnvironmentVariable" and the argument count: "2".というエラーが出て、PowerShell Desktop Editionのものと仕様がちょっと違うようだったので、GetEnvironmentVariableSetEnvironmentVariableも使わないようにした。)

このDockerfileでビルドしたら、RUN npcap.exe /Sで以下のエラー。

The subsystem needed to support the image type is not present.

このsubsystemというのはどうもWoW64を指しているようで、Nano ServerがWoW64をサポートしていないのにnpcap.exeが32bitバイナリであることが問題のようであった。

ついでにMSIもサポートされていないことがわかった。大丈夫かこれ。

Nano Serverパッケージプロバイダバグってる問題

Nano Serverにもロールや機能の追加ができるらしいので、ひょっとしてこれで何か改善できないかと思って試した。

Nano Serverへのロール・機能の追加は、WindowsのパッケージマネジメントシステムであるPackageManagement (a.k.a. OneGet)を使ってやる。PowerShellでInstall-PackageProvider NanoServerPackageImport-PackageProvider NanoServerPackageを実行するとNano Serverのパッケージプロバイダが使えるようになり、Find-NanoServerPackageで利用できるパッケージの一覧が見れる。

はずなんだけど、Find-NanoServerPackageでエラー。

C:\pcap4j>powershell -command Find-NanoServerPackage
DownloadFile : Save-HTTPItem: Bits Transfer failed. Job State:  ExitCode = 255
At C:\Program Files\WindowsPowerShell\Modules\NanoServerPackage\0.1.1.0\NanoServerPackage.psm1:1294 char:9
+         DownloadFile -downloadURL $fullUrl `
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (https://nanoser...ServerIndex.txt:String) [DownloadFile], RuntimeException
    + FullyQualifiedErrorId : FailedToDownload,DownloadFile

Get-Content : Cannot find drive. A drive with the name 'CleanUp' does not exist.
At C:\Program Files\WindowsPowerShell\Modules\NanoServerPackage\0.1.1.0\NanoServerPackage.psm1:674 char:26
+     $searchFileContent = Get-Content $searchFile
+                          ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (CleanUp:String) [Get-Content], DriveNotFoundException
    + FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetContentCommand

Get-Content : Cannot find path
'C:\Users\ContainerAdministrator\AppData\Local\NanoServerPackageProvider\searchNanoPackageIndex.txt' because it does not exist.
At C:\Program Files\WindowsPowerShell\Modules\NanoServerPackage\0.1.1.0\NanoServerPackage.psm1:674 char:26
+     $searchFileContent = Get-Content $searchFile
+                          ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (C:\Users\Contai...ackageIndex.txt:String) [Get-Content], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

NanoServerPackageのIssuesにこのエラーが登録されていた。1か月放置されてる。


パトラッシュ、僕はもう疲れたよ。