如何在 Nano Server Docker 容器(特别是构建 1809)内使用 PowerShell 连接到远程系统?

nam*_*ple 6 powershell powershell-remoting docker dockerfile nano-server

背景

\n\n

我有一个 .NET 应用程序,我想将其安装在 Nano Server Docker 容器内,特别是构建 1809。该应用程序基本上是一个 REST 服务器,它将接收 REST 请求,并根据 JSON 的内容调用特定的 PowerShell cmdlet在特定的远程系统上并以 JSON 格式返回结果。

\n\n

我能够创建一个安装了 PowerShell 和 .NET Core 的 Nano Server Docker 容器。然而,我最终意识到容器上没有安装 WinRM,因此无法调用远程 PowerShell cmdlet。

\n\n

我的主机系统是 Windows Server 2019 Datacenter,版本 1809(操作系统内部版本 17763.379)。我正在使用适用于 Windows 的 Docker Desktop(版本 2.0.0.3)并启用了 Windows 容器。

\n\n

Dockerfile

\n\n

这是我正在使用的 Dockerfile。我通过结合此处此处的部分 Dockerfile 创建了它。

\n\n
# escape=`\n# Args used by from statements must be defined here:\nARG fromTag=1809\nARG InstallerVersion=nanoserver\nARG InstallerRepo=mcr.microsoft.com/powershell\nARG NanoServerRepo=mcr.microsoft.com/windows/nanoserver\n\n# Use server core as an installer container to extract PowerShell,\n# As this is a multi-stage build, this stage will eventually be thrown away\nFROM ${InstallerRepo}:$InstallerVersion  AS installer-env\n\n# Arguments for installing PowerShell, must be defined in the container they are used\nARG PS_VERSION=6.2.0\n\nARG PS_PACKAGE_URL=https://github.com/PowerShell/PowerShell/releases/download/v$PS_VERSION/PowerShell-$PS_VERSION-win-x64.zip\n\nSHELL ["pwsh", "-Command", "$ErrorActionPreference = \'Stop\'; $ProgressPreference = \'SilentlyContinue\';"]\n\nARG PS_PACKAGE_URL_BASE64\n\nRUN Write-host "Verifying valid Version..."; `\n    if (!($env:PS_VERSION -match \'^\\d+\\.\\d+\\.\\d+(-\\w+(\\.\\d+)?)?$\' )) { `\n        throw (\'PS_Version ({0}) must match the regex "^\\d+\\.\\d+\\.\\d+(-\\w+(\\.\\d+)?)?$"\' -f $env:PS_VERSION) `\n    } `\n    $ProgressPreference = \'SilentlyContinue\'; `\n    if($env:PS_PACKAGE_URL_BASE64){ `\n        Write-host "decoding: $env:PS_PACKAGE_URL_BASE64" ;`\n        $url = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($env:PS_PACKAGE_URL_BASE64)) `\n    } else { `\n        Write-host "using url: $env:PS_PACKAGE_URL" ;`\n        $url = $env:PS_PACKAGE_URL `\n    } `\n    Write-host "downloading: $url"; `\n    [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; `\n    New-Item -ItemType Directory /installer > $null ; `\n    Invoke-WebRequest -Uri $url -outfile /installer/powershell.zip -verbose; `\n    Expand-Archive /installer/powershell.zip -DestinationPath \\PowerShell\n\n# -------------------------------------------------------------------------------------------------------------------------------------------------------\n# Retrieve .NET Core SDK\nUSER ContainerAdministrator\nENV DOTNET_SDK_VERSION 2.2.401\n\nRUN 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; `\n    $dotnet_sha512 = \'ed83eb5606912cd78d7696fbdc8e8074afa95fda84eec57b078d7371848ad15fe91aaf521b85e77c69b844a7b036a2c0b7b6cac87a8e356643980d96b689af93\'; `\n    if ((Get-FileHash dotnet.zip -Algorithm sha512).Hash -ne $dotnet_sha512) { `\n        Write-Host \'CHECKSUM VERIFICATION FAILED!\'; `\n        exit 1; `\n    }; `\n    `\n    Expand-Archive dotnet.zip -DestinationPath dotnet; `\n    Remove-Item -Force dotnet.zip\n# -------------------------------------------------------------------------------------------------------------------------------------------------------\n\n# Install PowerShell into NanoServer\nFROM ${NanoServerRepo}:${fromTag}\n\n# Copy PowerShell Core from the installer container\nENV ProgramFiles="C:\\Program Files" `\n    # set a fixed location for the Module analysis cache\n    LOCALAPPDATA="C:\\Users\\ContainerAdministrator\\AppData\\Local" `\n    PSModuleAnalysisCachePath="$LOCALAPPDATA\\Microsoft\\Windows\\PowerShell\\docker\\ModuleAnalysisCache" `\n    # Persist %PSCORE% ENV variable for user convenience\n    PSCORE="$ProgramFiles\\PowerShell\\pwsh.exe" `\n    # Set the default windows path so we can use it\n    WindowsPATH="C:\\Windows\\system32;C:\\Windows"\n\n# Set the path\nENV PATH="$WindowsPATH;C:\\Program Files\\PowerShell;C:\\Program Files\\dotnet;"\n\nCOPY --from=installer-env ["\\\\PowerShell\\\\", "$ProgramFiles\\\\PowerShell"]\n\n# intialize powershell module cache\nRUN pwsh `\n        -NoLogo `\n        -NoProfile `\n        -Command " `\n          $stopTime = (get-date).AddMinutes(15); `\n          $ErrorActionPreference = \'Stop\' ; `\n          $ProgressPreference = \'SilentlyContinue\' ; `\n          while(!(Test-Path -Path $env:PSModuleAnalysisCachePath)) {  `\n            Write-Host "\'Waiting for $env:PSModuleAnalysisCachePath\'" ; `\n            if((get-date) -gt $stopTime) { throw \'timout expired\'} `\n            Start-Sleep -Seconds 6 ; `\n          }"\n\n# -------------------------------------------------------------------------------------------------------------------------------------------------------\nCOPY --from=installer-env ["/dotnet", "/Program Files/dotnet"]\n\n# -------------------------------------------------------------------------------------------------------------------------------------------------------\n\nUSER ContainerAdministrator\nEXPOSE 80/tcp\nEXPOSE 5985/tcp\nEXPOSE 5986/tcp\nEXPOSE 7777/tcp\nEXPOSE 7778/tcp\n\nCMD ["pwsh.exe"]\n
Run Code Online (Sandbox Code Playgroud)\n\n

Docker 命令

\n\n

以下是我用来创建和访问 Docker 容器的 Docker 命令(请注意,该目录包含一个具有上述内容的 Dockerfile):

\n\n
docker build C:\\powershell-nanoserver1809-with-dotnet-2.2.401\ndocker create -t --name NanoServerHelloWorld -h NanoServer -i <ID_RETURNED_FROM_PREVIOUS_COMMAND>\ndocker start -i NanoServerHelloWorld\n
Run Code Online (Sandbox Code Playgroud)\n\n

失败的 PowerShell 和 WinRM 命令

\n\n

在其他系统上,我可以使用以下 PowerShell 代码创建到远程系统的 CimSession,然后调用 PowerShell cmdlet:

\n\n
$u = "REMOTE_DOMAIN\\REMOTE_USERNAME";\n$pw = "REMOTE_PASSWORD";\n$secStr = New-Object -TypeName System.Security.SecureString;\n$pw.ToCharArray() | ForEach-Object {$secStr.AppendChar($_)};\n$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $u, $secStr;\n\n$Session = New-CimSession -ComputerName 172.27.0.114 -Authentication Negotiate -Credential $Cred -OperationTimeoutSec 900\n
Run Code Online (Sandbox Code Playgroud)\n\n

但在这个容器中我收到以下错误消息:

\n\n
New-CimSession : FAILED\nAt line:1 char:12\n+ $Session = New-CimSession -ComputerName 172.27.0.114 -Authentication  ...\n+            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n+ CategoryInfo          : NotSpecified: (:) [New-CimSession], CimException\n+ FullyQualifiedErrorId : Microsoft.Management.Infrastructure.CimException,Microsoft.Management.Infrastructure.CimCmdlets.NewCimSessionCommand\n
Run Code Online (Sandbox Code Playgroud)\n\n

此外,如果我尝试以任何方式配置 WinRM,我会得到以下信息(来自 cmd):

\n\n
C:\\>winrm set winrm/config/client @{TrustedHosts="*"}\n\'winrm\' is not recognized as an internal or external command,\noperable program or batch file.\n
Run Code Online (Sandbox Code Playgroud)\n\n

另外,如果我查看容器上的服务,我看不到 WinRM:

\n\n
PS C:\\> Get-Service\n\nStatus   Name               DisplayName\n------   ----               -----------\nRunning  cexecsvc           Container Execution Agent\nRunning  CryptSvc           Cryptographic Services\nRunning  DcomLaunch         DCOM Server Process Launcher\nRunning  Dhcp               DHCP Client\nRunning  DiagTrack          Connected User Experiences and Teleme\xe2\x80\xa6\nRunning  Dnscache           DNS Client\nRunning  EventLog           Windows Event Log\nStopped  KeyIso             CNG Key Isolation\nStopped  LanmanServer       Server\nRunning  LanmanWorkstation  Workstation\nStopped  lmhosts            TCP/IP NetBIOS Helper\nStopped  mpssvc             Windows Defender Firewall\nStopped  Netlogon           Netlogon\nStopped  NetSetupSvc        Network Setup Service\nRunning  nsi                Network Store Interface Service\nStopped  Power              Power\nRunning  ProfSvc            User Profile Service\nRunning  RpcEptMapper       RPC Endpoint Mapper\nRunning  RpcSs              Remote Procedure Call (RPC)\nRunning  SamSs              Security Accounts Manager\nRunning  Schedule           Task Scheduler\nStopped  seclogon           Secondary Logon\nRunning  SystemEventsBroker System Events Broker\nRunning  TimeBrokerSvc      Time Broker\nGet-Service : Service \'TrustedInstaller (TrustedInstaller)\' cannot be queried due to the following error:\nAt line:1 char:1\n+ Get-Service\n+ ~~~~~~~~~~~\n+ CategoryInfo          : PermissionDenied: (System.ServiceProcess.ServiceController:ServiceController) [Get-Service], ServiceCommandException\n+ FullyQualifiedErrorId : CouldNotGetServiceInfo,Microsoft.PowerShell.Commands.GetServiceCommand\n\nStopped  TrustedInstaller   TrustedInstaller\nRunning  UserManager        User Manager\nStopped  VaultSvc           Credential Manager\nStopped  WerSvc             Windows Error Reporting Service\nStopped  WinHttpAutoProxyS\xe2\x80\xa6 WinHTTP Web Proxy Auto-Discovery Serv\xe2\x80\xa6\nStopped  wisvc              Windows Insider Service\n
Run Code Online (Sandbox Code Playgroud)\n\n

问题

\n\n

有没有办法安装 WinRM 并在 Nano Server Docker 容器构建 1809 上运行?如果没有,是否有一些解决方法可以使用 PowerShell 连接到远程系统以调用 PowerShell cmdlet?

\n\n

也许我缺少一些特殊的 Docker 命令,或者其他一些可用的 Nano Server 映像具有此缺少的功能?

\n\n

首先十分感谢。

\n