Docker: 将DotNetCore应用程序容器化并部署到IIS站点

简介

通常我们可以直接使用镜像microsoft/dotnet 作为dotnetcore应用程序的基础镜像,可以直接使用Kerstrel作为Web服务器。但实际情况不总是这样,本文先介绍如何使用powershell将AspNetCore程序部署到IIS,然后在window docker环境下将其打包成windows docker image。

使用PowerShell部署ASP.NetCore程序到IIS

首先我们要引入PowerShell中的WebAdministration模块,这样就可以对IIS进行相关的操作了

Import-Module WebAdministration

在IIS中运行ASP.NetCore程序,先要安装AspNetCoreModule,需要使用DotNetCore.WindowsHosting安装

# 如果你的程序是dotnetcore 2.0就安装这个
Invoke-WebRequest -Uri "https://aka.ms/dotnetcore.2.0.0-windowshosting" -OutFile "DotNetCore.WindowsHosting.exe"

# 安装此exe时,实际上它会弹出一个框要求你点击安装按钮,但这里肯定是希望能silent安装, ArgumentList 就是起这个作用的; -Wait 意思是让这个程序安装完成后再执行下一个程序
Start-Process "DotNetCore.WindowsHosting.exe"  -Wait  -ArgumentList '/S', '/v', '/qn' -passthru

# 如果你的程序是dotnetcore 2.1就安装这个
Invoke-WebRequest -Uri "https://download.microsoft.com/download/9/3/E/93ED35C8-57B9-4D50-AE32-0330111B38E8/dotnet-hosting-2.1.1-win.exe" -OutFile "dotnet-hosting-2.1.1-win.exe"

Start-Process "dotnet-hosting-2.1.1-win.exe" -Wait  -ArgumentList '/S', '/v', '/qn' -passthru

安装完DotNetCore.WindowsHosting之后,需要重启IIS服务

Invoke-Expression "net stop was /y"
Invoke-Expression "net start w3svc"

可以使用以下命令来检测ASPNetCoreModule是否已安装

Get-WebGlobalModule -Name AspNetCoreModule -ErrorAction Ignore

接下来就是要对程序部署,IIS设置

# 创建一个应用程序池,名称为TestApp
New-Item -path IIS:\AppPools\TestApp

# 把应用程序池的.Net版本设置为无托管代码
Set-ItemProperty -Path IIS:\AppPools\TestApp -Name managedRuntimeVersion -Value ''

# 创建一个网站
New-Website -name TestSite -PhysicalPath "C:\TestSite" -ApplicationPool TestApp -Port 80

# 访问网站
Invoke-WebRequest -uri http://localhost -usebasicparsing

安装SSL证书

# 增加HTTPS 绑定到 "*.container.com" 通配的证书

$pwd = ConvertTo-SecureString -String "123123123132" -Force -AsPlainText

Import-PfxCertificate -FilePath "c:\\install\\wildcard.pfx" -CertStoreLocation 'Cert:\LocalMachine\My' -Password $pwd -Verbose

Import-PfxCertificate -FilePath "c:\\install\\wildcard.pfx" -CertStoreLocation 'Cert:\LocalMachine\Root' -Password $pwd -Verbose

$cert = Get-ChildItem cert:\LocalMachine\MY | Where-Object { $_.FriendlyName -like "container.com" }

$binding = New-WebBinding -Name $websiteName -Protocol 'https' -Port 443 -IPAddress '*'

$NewBinding = Get-WebBinding -protocol https

$NewBinding.AddSSLCertificate("$($cert.getcerthashstring())", "MY")

其他powershell命令(这里暂时不使用)

#New-WebBinding -Name "mygod.stage.xxx.com" -IPAddress "*" -Port 80 -HostHeader "mygod.stage.accenture.com" -SslFlags 0 -Protocol "http"
#New-WebBinding -Name "mygod-api.stage.xxx.com" -IPAddress "*" -Port 80 -HostHeader "mygod-api.stage.accenture.com" -SslFlags 0 -Protocol "http"
#Set-WebBinding -Name "mygod.stage.xxx.com" -BindingInformation "*:443:" -PropertyName "HostHeader" -Value "mygod.stage.xxx.com"

#Get-WebBinding
#New-Item -Path "D:\apps\" -Name "myapi" -ItemType "directory"

#Get-Item(Join-Path 'IIS:AppPools\' 'mygod.stage.xxx.com') | select -ExpandProperty processModel | select -expand identityType
#Set-ItemProperty IIS:\AppPools\mygod.stage.xxx.com -name processModel.identityType -Value SpecificUser 
#Set-ItemProperty IIS:\AppPools\mygod.stage.xxx.com -name processModel.userName -Value $username
#Set-ItemProperty IIS:\AppPools\mygod.stage.xxx.com -name processModel.password -Value $password

#Set-ItemProperty IIS:\AppPools\mygod.stage.xxx.com -name processModel.identityType -Value SpecificUser
#Set-ItemProperty IIS:\AppPools\mygod.stage.xxx.com -name processModel.userName -Value ""
#Set-ItemProperty IIS:\AppPools\mygod.stage.xxx.com -name processModel.password -Value ""

#cd D:\apps\webapi\
#(type 'appsettings.json') -replace ('VD120761','VLN120761')|out-file appsettings.json
#(type 'appsettings.json') -replace ('MultiSubnetFailover=False','MultiSubnetFailover=True')|out-file appsettings.json
#Invoke-Expression "net stop was /y"
#Invoke-Expression "net start w3svc"
#cat .\appsettings.json

#set-location D:\apps\webapi
#dotnet .\myapi.dll calculate-mapcount

给当前网站的物理路径设置权限

$path = "C:\inetpub\wwwroot"

$acl = Get-Acl $path

$permission = 'IIS_IUSRS', 'FullControl', 'ContainerInherit, ObjectInherit', 'None', 'Allow';

$permission = 'Everyone', 'FullControl', 'ContainerInherit, ObjectInherit', 'None', 'Allow';

$rule = New-Object -TypeName System.Security.AccessControl.FileSystemAccessRule -ArgumentList $permission;

$acl.SetAccessRule($rule);

$acl | Set-Acl -Path $path;

创建一个EventLog试试

New-EventLog -Source "WSR" -LogName "Application"

在本地可以使用域名进行Https访问

在C:\Windows\System32\drivers\etc\hosts中增加一条记录,以便用DNS name进行访问

$fqdn = "test.container.com";
$hostPath = 'C:\Windows\System32\drivers\etc\hosts';
$hostFile = Get-Content $hostPath;
$hostEntry = "127.0.0.1 `t $fqdn";
Add-content -path $hostPath -value $hostEntry
ipconfig /flushdns

Dockerfile (使用Muti-stage build的方式)

# 下面的 escape在window系统非常有用,它用以定义在Dockerfile中使用转义字符,Dockerfile中,escape默认为\,这里转义为`,而且必须放在第一行,参考https://blog.csdn.net/chengqiuming/article/details/79007598

# escape=`
# 这里本可以使用如下镜像,用Kerstrel作为WEB服务器,但本文不这样做
#FROM microsoft/dotnet:2.1-aspnetcore-runtime-nanoserver-sac2016 AS base

# 这里使用官方的Windows Server Core作为基础镜像
FROM microsoft/windowsservercore:latest AS base

# 这里是这个build镜像,专用于build我们的源代码
#FROM microsoft/dotnet:2.1-sdk-nanoserver-1803 AS build
FROM microsoft/dotnet:2.1-sdk-nanoserver-sac2016 AS build
WORKDIR /src

COPY ["./webapi.csproj", "."]

RUN dotnet restore "webapi.csproj"

COPY . .

RUN dotnet build "webapi.csproj" -c Release -o /app

# 这里发部程序
FROM build AS publish
RUN dotnet publish "webapi.csproj" -c Release -o /app

FROM base AS final
# 这里在镜像中启用powershell脚本环境
SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

LABEL name="WSR" `
      release-date="2019-1-4"

# Disable DNS cache so container addresses always fetched from Docker
#RUN Set-ItemProperty -path 'HKLM:\SYSTEM\CurrentControlSet\Services\Dnscache\Parameters' -Name ServerPriorityTimeLimit -Value 0 -Type DWord

# Activate Windows features based on the app's requirements.
#RUN Add-WindowsFeature Web-App-Dev, Web-Net-Ext45, Web-Asp-Net45, Web-ISAPI-Ext, Web-ISAPI-Filter, NET-Framework-45-ASPNET

# 将上面使用powershell脚本Copy到镜像中
RUN New-Item -ItemType Directory C:\install;
COPY .\install-website.ps1    C:\install

# DONT TOUCH - Paths used during CI build after solution MSBUILD.exe
# COPY .\bin\Release\PublishOutput  C:\inetpub\wwwroot\
COPY --from=publish /app C:\inetpub\wwwroot\

RUN 'Log written in docker build' >> 'C:\Logs\docker-build.log';

#这里就是我们上面使用powershell命令安装dotnetcore程序到IIS的脚本
ENTRYPOINT  ["powershell.exe", "C:\\install\\install-website.ps1"]

# 这里可以对你的程序作一些健康检查
# HEALTHCHECK --interval=60s --timeout=60s --start-period=100s --retries=3 CMD C:\install\healthcheck.bat

问题与总结

1.在用COPY命令复制文件时报了一个错

OPY failed: CreateFile \\?\C:\ProgramData\Docker\tmp\docker-builder832533802\mysolution.sln: The system cannot find the file specified.

找了很久,原因是源路径没有设置对,要仔细检查你当前运行的路径和COPY的前半段相对路径组合在一起看是否能找到你需要COPY的源文件。

你可能感兴趣的:(Docker,DotNet)