背景
我有一个 .NET 应用程序,我想安装在 Nano Server Docker 容器中,特别是构建 1809。该应用程序基本上是一个 REST 服务器,它将接收 REST 请求,并根据 JSON 的内容,调用特定的 PowerShell cmdlet在特定的远程系统上并以 JSON 格式返回结果。
我能够创建一个安装了 PowerShell 和 .NET Core 的 Nano Server Docker 容器。但是,我最终意识到容器上没有安装 WinRM,因此无法调用远程 PowerShell cmdlet。
我的主机系统是 Windows Server 2019 Datacenter,版本 1809(OS Build 17763.379)。我正在使用启用了 Windows 容器的 Docker Desktop for Windows(版本 2.0.0.3)。
Dockerfile
这是我正在使用的 Dockerfile。我通过组合此处和此处的 Dockerfile 部分来创建它。
# escape=`
# Args used by from statements must be defined here:
ARG fromTag=1809
ARG InstallerVersion=nanoserver
ARG InstallerRepo=mcr.microsoft.com/powershell
ARG NanoServerRepo=mcr.microsoft.com/windows/nanoserver
# Use server core as an installer container to extract PowerShell,
# As this is a multi-stage build, this stage will eventually be thrown away
FROM ${InstallerRepo}:$InstallerVersion AS installer-env
# Arguments for installing PowerShell, must be defined in the container they are used
ARG PS_VERSION=6.2.0
ARG PS_PACKAGE_URL=https://github.com/PowerShell/PowerShell/releases/download/v$PS_VERSION/PowerShell-$PS_VERSION-win-x64.zip
SHELL ["pwsh", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
ARG PS_PACKAGE_URL_BASE64
RUN Write-host "Verifying valid Version..."; `
if (!($env:PS_VERSION -match '^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$' )) { `
throw ('PS_Version ({0}) must match the regex "^\d+\.\d+\.\d+(-\w+(\.\d+)?)?$"' -f $env:PS_VERSION) `
} `
$ProgressPreference = 'SilentlyContinue'; `
if($env:PS_PACKAGE_URL_BASE64){ `
Write-host "decoding: $env:PS_PACKAGE_URL_BASE64" ;`
$url = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($env:PS_PACKAGE_URL_BASE64)) `
} else { `
Write-host "using url: $env:PS_PACKAGE_URL" ;`
$url = $env:PS_PACKAGE_URL `
} `
Write-host "downloading: $url"; `
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; `
New-Item -ItemType Directory /installer > $null ; `
Invoke-WebRequest -Uri $url -outfile /installer/powershell.zip -verbose; `
Expand-Archive /installer/powershell.zip -DestinationPath \PowerShell
# -------------------------------------------------------------------------------------------------------------------------------------------------------
# Retrieve .NET Core SDK
USER ContainerAdministrator
ENV DOTNET_SDK_VERSION 2.2.401
RUN Invoke-WebRequest -OutFile dotnet.zip https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$Env:DOTNET_SDK_VERSION/dotnet-sdk-$Env:DOTNET_SDK_VERSION-win-x64.zip; `
$dotnet_sha512 = 'ed83eb5606912cd78d7696fbdc8e8074afa95fda84eec57b078d7371848ad15fe91aaf521b85e77c69b844a7b036a2c0b7b6cac87a8e356643980d96b689af93'; `
if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { `
Write-Host 'CHECKSUM VERIFICATION FAILED!'; `
exit 1; `
}; `
`
Expand-Archive dotnet.zip -DestinationPath dotnet; `
Remove-Item -Force dotnet.zip
# -------------------------------------------------------------------------------------------------------------------------------------------------------
# Install PowerShell into NanoServer
FROM ${NanoServerRepo}:${fromTag}
# Copy PowerShell Core from the installer container
ENV ProgramFiles="C:\Program Files" `
# set a fixed location for the Module analysis cache
LOCALAPPDATA="C:\Users\ContainerAdministrator\AppData\Local" `
PSModuleAnalysisCachePath="$LOCALAPPDATA\Microsoft\Windows\PowerShell\docker\ModuleAnalysisCache" `
# Persist %PSCORE% ENV variable for user convenience
PSCORE="$ProgramFiles\PowerShell\pwsh.exe" `
# Set the default windows path so we can use it
WindowsPATH="C:\Windows\system32;C:\Windows"
# Set the path
ENV PATH="$WindowsPATH;C:\Program Files\PowerShell;C:\Program Files\dotnet;"
COPY --from=installer-env ["\\PowerShell\\", "$ProgramFiles\\PowerShell"]
# intialize powershell module cache
RUN pwsh `
-NoLogo `
-NoProfile `
-Command " `
$stopTime = (get-date).AddMinutes(15); `
$ErrorActionPreference = 'Stop' ; `
$ProgressPreference = 'SilentlyContinue' ; `
while(!(Test-Path -Path $env:PSModuleAnalysisCachePath)) { `
Write-Host "'Waiting for $env:PSModuleAnalysisCachePath'" ; `
if((get-date) -gt $stopTime) { throw 'timout expired'} `
Start-Sleep -Seconds 6 ; `
}"
# -------------------------------------------------------------------------------------------------------------------------------------------------------
COPY --from=installer-env ["/dotnet", "/Program Files/dotnet"]
# -------------------------------------------------------------------------------------------------------------------------------------------------------
USER ContainerAdministrator
EXPOSE 80/tcp
EXPOSE 5985/tcp
EXPOSE 5986/tcp
EXPOSE 7777/tcp
EXPOSE 7778/tcp
CMD ["pwsh.exe"]
码头工人命令
以下是我用来创建和访问 Docker 容器的 Docker 命令(注意该目录包含一个包含上述内容的单个 Dockerfile):
docker build C:\powershell-nanoserver1809-with-dotnet-2.2.401
docker create -t --name NanoServerHelloWorld -h NanoServer -i <ID_RETURNED_FROM_PREVIOUS_COMMAND>
docker start -i NanoServerHelloWorld
失败的 PowerShell 和 WinRM 命令
在其他系统上,我可以使用以下 PowerShell 代码为远程系统创建 CimSession,然后调用 PowerShell cmdlet:
$u = "REMOTE_DOMAIN\REMOTE_USERNAME";
$pw = "REMOTE_PASSWORD";
$secStr = New-Object -TypeName System.Security.SecureString;
$pw.ToCharArray() | ForEach-Object {$secStr.AppendChar($_)};
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $u, $secStr;
$Session = New-CimSession -ComputerName 172.27.0.114 -Authentication Negotiate -Credential $Cred -OperationTimeoutSec 900
但在这个容器中,我收到以下错误消息:
New-CimSession : FAILED
At line:1 char:12
+ $Session = New-CimSession -ComputerName 172.27.0.114 -Authentication ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [New-CimSession], CimException
+ FullyQualifiedErrorId : Microsoft.Management.Infrastructure.CimException,Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionCommand
此外,如果我尝试以任何方式配置 WinRM,我会得到以下信息(来自 cmd):
C:\>winrm set winrm/config/client @{TrustedHosts="*"}
'winrm' is not recognized as an internal or external command,
operable program or batch file.
另外,如果我查看容器上的服务,我看不到 WinRM:
PS C:\> Get-Service
Status Name DisplayName
------ ---- -----------
Running cexecsvc Container Execution Agent
Running CryptSvc Cryptographic Services
Running DcomLaunch DCOM Server Process Launcher
Running Dhcp DHCP Client
Running DiagTrack Connected User Experiences and Teleme…
Running Dnscache DNS Client
Running EventLog Windows Event Log
Stopped KeyIso CNG Key Isolation
Stopped LanmanServer Server
Running LanmanWorkstation Workstation
Stopped lmhosts TCP/IP NetBIOS Helper
Stopped mpssvc Windows Defender Firewall
Stopped Netlogon Netlogon
Stopped NetSetupSvc Network Setup Service
Running nsi Network Store Interface Service
Stopped Power Power
Running ProfSvc User Profile Service
Running RpcEptMapper RPC Endpoint Mapper
Running RpcSs Remote Procedure Call (RPC)
Running SamSs Security Accounts Manager
Running Schedule Task Scheduler
Stopped seclogon Secondary Logon
Running SystemEventsBroker System Events Broker
Running TimeBrokerSvc Time Broker
Get-Service : Service 'TrustedInstaller (TrustedInstaller)' cannot be queried due to the following error:
At line:1 char:1
+ Get-Service
+ ~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (System.ServiceProcess.ServiceController:ServiceController) [Get-Service], ServiceCommandException
+ FullyQualifiedErrorId : CouldNotGetServiceInfo,Microsoft.PowerShell.Commands.GetServiceCommand
Stopped TrustedInstaller TrustedInstaller
Running UserManager User Manager
Stopped VaultSvc Credential Manager
Stopped WerSvc Windows Error Reporting Service
Stopped WinHttpAutoProxyS… WinHTTP Web Proxy Auto-Discovery Serv…
Stopped wisvc Windows Insider Service
问题
有没有办法安装 WinRM 并在 Nano Server Docker 容器 build 1809 上工作?如果没有,是否有一些解决方法可以使用 PowerShell 连接到远程系统以调用 PowerShell cmdlet?
也许我缺少一些特殊的 Docker 命令,或者其他一些具有此缺失功能的 Nano Server 映像?
首先十分感谢。