windows container 踩坑记

windows container 踩坑记

Intro

我们有一些服务是 dotnet framework 的,不能直接跑在 docker linux container 下面,最近一直在折腾把它部署在 windows container 下,折腾的有点恶心,记录一下。

Windows Container 介绍

windows container 踩坑记

Windows Container 是微软在 Windows 上的虚拟化实践,它可以提供操作系统级别的虚拟化。

通过我们说的容器化大多是指 Linux Container,基于 linux 的 container 实践,除此之外还有 windows container,如果你使用的是 windows 且使用过 Docker for Desktop,你也许会注意到 docker 右键的时候会有一个 “Switch to windows container” 的选项。

windows container 踩坑记

Windows container 架构:

windows container 踩坑记

Windows container 分为两大部分: windows container on windows(下文简称 Windows Container), linux container on windows(下文简称 Linux Container), 我们今天将要用到的是 Windows container.

上图所示的两种方式对应着 Docker for Desktop 里 Windows Container 和 Linux Container 两种 docker 容器化运行时,两种运行时不能同时使用,可以切换,切换过程中数据是不会丢失的,你不可以在 windows container 环境下操作 linux container 的镜像与容器,更不能在 linux container 环境 下操作 windows container 的镜像和容器,两者架构上不一致。

windows container 是相当于 docker 在 linux 下的原生实现,linux container 是通过 Hyper-V 托管了一个小型虚拟机以实现 linux 环境。

Windows 容器类型

你应该知道, 有两个不同的容器类型 (也称为运行时)。

Windows Server 容器通过进程和命名空间隔离技术提供应用程序隔离, 这就是这些容器也称为进程隔离的容器的原因。 Windows Server 容器与容器主机和该主机上运行的所有容器共享内核。 这些进程隔离的容器不提供敌意安全边界, 不应用于隔离不受信任的代码。 由于共享内核空间,这些容器要求具有相同的内核版本和配置。

Hyper-v 隔离通过在高度优化的虚拟机中运行每个容器来扩展 Windows Server 容器提供的隔离。 在此配置中, 容器主机不与同一主机上的其他容器共享其内核。 这些容器旨在托管敌对多租户,并且具有与虚拟机相同的安全保证。 由于这些容器不与主机上的主机或其他容器共享内核, 因此它们可以运行具有不同版本和配置 (受支持版本内) 的内核。 例如, Windows 10 上的所有 Windows 容器都使用 Hyper-v 隔离来利用 Windows Server 内核版本和配置

尽管两者在技术架构上有差异,但是 docker 的基本操作都是适用的,如果你的磁盘不够大网速不够好,不建议直接在自己电脑上尝试 windows container,windows container 大部分是基于 windows-sever 的镜像,动则十几个G,下载镜像都不一定能下载成功。

GetStarted

部署一个简单的 dotnet framework 应用到 windows 容器

docker pull microsoft/iis 拉一个 iis 的 docker 镜像

docker run --rm -p 8080:80 --name=iis microsoft/iis:latest

docker run --it iis powershell

看着上面的命令是不是感觉很熟悉啊,下面再看一个 Dockerfile

这是一个 asp.net 的应用的 dockerfile,来自微软的官方示例

FROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS build
WORKDIR /app # copy csproj and restore as distinct layers
COPY *.sln .
COPY aspnetapp/*.csproj ./aspnetapp/
COPY aspnetapp/*.config ./aspnetapp/
RUN nuget restore # copy everything else and build app
COPY aspnetapp/. ./aspnetapp/
WORKDIR /app/aspnetapp
RUN msbuild /p:Configuration=Release FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8 AS runtime
WORKDIR /inetpub/wwwroot
COPY --from=build /app/aspnetapp/. ./

踩的坑

项目是 AspNetCore 只是项目是 dotnet framework471 的,并且有引用几个 netstandard2.0 的项目,

  1. 在 msbuild 的时候出错,尚未找到解决方法,提了一个 issue 还没回复

    https://github.com/microsoft/dotnet-framework-docker/issues/315

    看到有人说要引用 “Microsoft.Net.Compilers” 这个包,在项目里加上了这个包的引用还是有问题,这个问题很神奇,本地 dotnet build/msbuild 都是正常的

    C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin\Roslyn\Microsoft.CSharp.Core.targets(59,5):
    error MSB6006: "csc.exe" exited with code -2146232576

    msbuild 有问题之后便想用 dotnet build 来编译,最初尝试安装 dotnet core 后来发现这个 framework 镜像里已经安装 dotnet core sdk,太好了不用再自己装了,后来用 dotnet build 代替了 msbuild

  2. host 应用

  • dotnet 不能执行 dotnet framework 生成的 exe,原本想着像在 linux 下面跑 dotnet core 一样,直接dotnet <xxx.dll>使用 kestrel 来托管,然而并不能直接运行,起初按错误提示以为要手动加一个 runtimeconfig.json 来指定框架类型及版本,后来才知道 runtimeconfig.json 只支持 dotnetcore,详见https://github.com/dotnet/core-setup/issues/7149
  • 后来还是放到 iis 下面,当时使用的是 microsoft/iis 这个镜像,发现放上去之后不能运行,报 500 错误,后来又装 dotnetcore-windows-hosting ,还是不行最终在 Event-Log 中发现没有framework471 ...
  • 后来使用 mcr.microsoft.com/dotnet/framework/aspnet:4.7.1 这个镜像,然后在安装 dotnetcore-windows-hosting 的时候又是一波多折。。最后先安装了 chocolatey , chocolatey 是 windows 上的一个包管理器,像管理nuget包一样管理你PC上的软件,然后使用 choco install dotnetcore-windowshosting -y 来安装 dotnetcore-windowshosting 模块。

Solution

最终使用的 Dockerfile 如下:

FROM mcr.microsoft.com/dotnet/framework/sdk:4.7.1 AS build

WORKDIR /app

# copy csproj and restore as distinct layers

COPY Projects/*.csproj ./Projects/
COPY nuget.config ./ RUN dotnet restore ./Projects/Projects.csproj # copy everything else and build app
COPY . . WORKDIR /app/Projects
RUN dotnet publish -c Release -o out FROM mcr.microsoft.com/dotnet/framework/aspnet:4.7.1 WORKDIR /inetpub/wwwroot # install choco
RUN Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
# install dotnetcore-windowshosting
RUN choco install dotnetcore-windowshosting -y RUN powershell -NoProfile -Command Remove-Item -Recurse C:\inetpub\wwwroot\* COPY --from=build /app/Projects/out/ ./

Memo

折腾起来真是麻烦,可以直接上 dotnetcore 还是直接上 dotnetcore 吧

Tips

windows container 里 RUN 是相当于在 powershell 中执行命令

Reference

上一篇:iOS自动化打包上传的踩坑记


下一篇:高达渐出现效果Shader