李加庆 译 分布式实验室

.NET 和 Docker结合使用_Jav



许多与我交流的开发者要么在积极地使用 Docker,要么计划在他们的环境中采用容器技术。容器是我们这个行业的一个重要趋势,.NET 是其中的一部分。 Microsoft 和 Docker 一直在合作,以便在开发 .NET 应用时,使用 Docker 可以有一个良好的体验。


Docker生态系统始于 Linux 技术。你可以在 Linux 和 .NET Core 上使用同其他开发平台一样的 Docker 工作流。.NET 团队经常会在 Docker Hub 的 microsoft/dotnet 仓库中更新 Debian 镜像。


Windows 团队最近发布了 Windows Server 2016,并更新了 Windows 10,而且在 Windows 上支持了容器。现在 .NET Core和 .NET Framework 都可以与 Windows 容器结合使用。


上述支持在使用 Docker 构建和打包 .NET 应用的方式上提供了很多选择。这篇文章介绍了其中的一些选择,并提供了入门信息,即使你是个 Docker 新手也没关系。




为什么选择容器?

.NET 和 Docker结合使用_Jav_02




下面列出了开发人员迁移到容器的关键原因:


  • 一致性:容器包括应用程序及其所有依赖项。不管是在计算机、本地环境或是云端,应用程序都执行相同的代码。

  • 轻量级:通过使用基于主机操作系统的最小量级抽象,并在容器之间共享公共资源,容器可以快速启动并使用最少量的 RAM。

  • 共享性:容器镜像可以通过 Docker Hub、Docker Store 和私有 Docker 仓库(如 Azure 镜像仓库)轻松实现分享。

  • 简单而强大:DockerFile 格式(容器镜像的 recipe)是一种可以实现强大场景的简单格式:优雅地将操作系统和容器的特定命令结合,并且还可以创建 Docker 镜像层。


想象一下,五年前如果有人在面试中告诉你,他们非常关心一致性,所以需要经常与应用一起部署操作系统。当时你可能不会雇佣他们。然而,这正是目前 Docker 的典型应用场景!


例如,如果需要为 .NET 团队添加容器的测试用例。我们会将基础设施的重要部分转移到容器上,以便从容器的优势中受益,而不是简单地“在 Docker 容器中测试 .NET”。这种方法为我们提供了双重优势:在容器中运行 .NET 的高度信心以及更加高效的整体流程和更低的成本。 在这个案例中,我们每年为基础设施实验室节省了一个人力的时间成本和每年10万美元的云服务机器成本。我很高兴我们能够节省这些费用。




.NET 和 Docker结合使用_Jav_03





使用 Docker 和 .NET 应用程序的最典型场景是用于生产环境部署和托管。 事实证明,生产环境部署只是一种场景,而针对其他场景 Docker 同样有用。 这些场景虽然不是 .NET 特有的,但应同样适用于大多数开发平台。


  • 低摩擦安装——在机器上无需安装任何东西,只需下载一个预装 .NET 的 Docker 镜像,你就可以尝试使用 .NET。

  • 在容器中开发——你可以在一致的环境中开发,开发环境和生产环境非常相似(这样可以避免一些诸如开发机中的全局状态等此类的问题)。Visual Studio Tools for Docker 甚至可以直接从 Visual Studio 中启动容器。

  • 在容器中测试——你可以在容器中进行测试,减少由于环境配置错误或上次测试遗留的其他更改导致的失败。

  • 在容器中构建——你可以在容器中构建代码,无需为多个环境正确配置共享的构建机器,而是转为 “BYOC”(自带容器)的方法。

  • 在测试环境、staging 环境、生产环境以及其他环境中部署——你可以在所有环境部中部署同一个镜像,减少由于配置不同导致的失败。通常只通过外部配置(例如,注入的环境变量)来更改镜像的行为。




.NET 和 Docker结合使用_Jav_04






现在可以在 Windows、MacOS 或 Linux 上使用 .NET 容器。 首先,你需要一个 Docker 客户端,最佳获取途径是 Docker.com。 如果是在 Windows 平台上,请下载 Docker for Windows(稳定版本)。 Docker 同时支持 Windows 和 Linux 容器。 如果你是 Azure 用户,可以查看 Docker Edition for Azure(https://docs.docker.com/docker-for-azure/)。


如果你是 Docker 新手,建议你在 Docker 文档中查看 Docker 入门(https://docs.docker.com/get-started/)部分,我也是从那入门的。该说明使用 Linux 容器,但如果你不是 Linux 用户,也不用担心,你可以找到与你使用的操作系统对应的 Docker 客户端使用说明,以及针对通用 Docker 概念和机制的指南。


接下来,可以尝试使用我们提供的 .NET 与 Docker 结合使用的示例。 这些示例应该可以帮助你开始使用 .NET 和 Docker。 我们创建了两组样本,因为 .NET Core 和 .NET Framework 之间的关键不同在于需要不同 Dockerfile 和其他组件。


  • .NET Core + Docker 示例(https://github.com/dotnet/dotnet-docker-samples)

  • .NET Framework + Docker 示例(https://github.com/microsoft/dotnet-framework-docker-samples)


我们 push 了一些镜像到 Docker hub,以便大家可以更加容易地使用它们:


  • .NET Core 示例镜像(https://hub.docker.com/r/microsoft/dotnet-samples/)

  • .NET Framework 示例镜像(https://hub.docker.com/r/microsoft/dotnet-framework-samples/)


通过使用示例镜像,你可以在机器上尝试 .NET,而无需安装任何东西( Docker 除外)。也就是说,我认为在本地环境中尝试示例仍然是充分体验和评估 .NET 与 Docker 的最佳实践。


例如,针对 .NET Core,可以尝试以下 Linux 镜像(如果使用Docker for Windows,请务必切换到 Linux 容器):





docker run microsoft/dotnet-samples




.NET 和 Docker结合使用_Jav_05






使用 .NET 与 Docker 的一个重要方面是要依赖 .NET 团队提供的 .NET 基础镜像。 为什么使用 .NET 基础镜像是一个好主意呢?至少有以下四个原因:


  • .NET 团队提供了基础镜像,所以你不必再造轮子。

  • .NET 团队会定期更新镜像,其中包括一些针对安全性的更新。

  • 当多个应用程序在同一台机器上使用相同镜像时,Docker 共享这些镜像的内存。镜像必须相同才能共享。

  • Docker 会扫描镜像来发现安全漏洞,可以提供有关环境的更多信息。


我们将 Docker 镜像发布在 Docker Hub 上几个不同的仓库中。 镜像分割很重要,这样可以在 Docker Hub 以及 docker search 命令中更容易找到它们。


  • microsoft/dotnet——适用于 Linux 和 Nano Server 的 .NET Core 运行时 和 SDK 镜像。

  • microsoft/aspnetcore — 适用于 Linux 和 Nano Server 的ASP.NET Core 镜像。

  • microsoft/aspnetcore-build——适用于 Linux 和 Nano Server 的 ASP.NET Core 镜像,用于构建应用程序。

  • microsoft/dotnet-framework——适用于 Windows Server Core 的 .NET Framework 3.5 和 4.6.2 镜像。

  • microsoft/aspnet——适用于 Windows Server Core 的 .NET Framework 3.5 和 4.6.2 ASP.NET 镜像。


我们也在 Docker Hub 上发布了示例,以便大家可以更加容易地使用。


  • microsoft/dotnet-samples——.NET Core 示例。

  • microsoft/dotnet-framework-samples——.NET Framework 示例。


基础镜像的使用说明及示例可以参照上述提供的 Docker Hub 链接。 如果你刚刚接触 Docker 分层概念,我们建议你阅读 Docker 关于理解镜像、容器和存储驱动(http://t.cn/RWZvwe4)的文档。




.NET 和 Docker结合使用_Jav_06






Docker 镜像(包括 .NET 镜像)由一个相对简单的 Dockerfile 格式文件定义。你可以在上述提供的 Docker Hub 仓库中找到每个镜像的 Dockerfile 文件的链接。


这些文件定义镜像内容和其他特征。在多数情况下,我们会研究其他平台对镜像的处理,并尝试遵循行业规范。也有一些情况下,我们会选择做一些最适用 .NET 应用程序的工作,这些工作可能会也可能不会适用于其他平台。 通常情况下,Docker 员工为其他平台创建初始化镜像,所以我们在 Docker Hub 上看到的自由“复制”模式实际上只是在追随 Docker。随着 Docker 规范的变化,我们将尽最大努力地将这些变化应用在 .NET 镜像上。


Dockerfile 源文件存储在 GitHub 上。 你可以关注我们关于镜像的进展。如果你愿意的话,也可以看看我们制作镜像的原因并参与讨论。




.NET 和 Docker结合使用_Jav_07






Docker 镜像具有隐藏 ID(例如,d99acb94e777),用于默认识别。 因为 ID 对用户来说并不是很有用,所以我们可以给 Docker 镜像打标签。标签通常是描述镜像用途的的友好名称,例如 “hello-world-app”。该机型在本地运行良好。


在 Docker Hub 上,仓库名称为镜像的名称,标签通过版本号或其他标志来消除仓库中镜像的歧义。




.NET 和 Docker结合使用_Jav_08





对于 .NET Core,标签用于描述以下三个维度的镜像差异:


  • .NET Core 版本——.NET Core 1.0, 1.1 and 2.0(撰写本文时)。

  • .NET Core 分布——.NET Core 运行时,.NET Core SDK, .NET Core dependencies 依赖项。

  • 操作系统基础镜像——Windows 和 Linux。


dotnet 仓库的一个重要选项是打上 latest 标签。我们决定 latest 标签总是指向最新的 .NET Core SDK 版本。例如,作为RTM时,latest 将更新指向 .NET Core SDK 2.0。


替代方案是将 latest 映射到 .NET Core 运行时。我们认为,SDK是你起步的最佳镜像,一旦你体验了较大的 SDK,就可以更容易地做出优化的选择。我们最近做出了一些改变 .NET Core Docker 镜像使用连拱 (multi-arch) 标签。 此改变可以让你减少对上述第三个维度的考量。这意味着你的 Dockerfile 文件不再需要定义要使用的操作系统。




.NET 和 Docker结合使用_Jav_09






对于.NET Framework,标签仅在一个维度上描述镜像差异:


  • .NET Framework 版本 — 3.5, 4.6.2


latest 标签对应最新版本的 .NET Framework。当镜像可用时,latest 将会更新并指向 4.7 版本。




.NET 和 Docker结合使用_Jav_10





关于如何构建仓库没有现成的硬性规定。下面这条是个不错的参考:


从仓库中执行 docker pull 拉取到的镜像,应当是一个意义明确的镜像,仓库中的其他镜像应当紧紧围绕与默认镜像相关的附加概念而存在。


仓库中的标签越多,其中某些镜像应当属于其他仓库的可能性越大。 这是我们正在讨论的东西,与 Docker 方面也在讨论这个问题。我们希望将 .NET Core 仓库分为两个或三个,这样便于用户使用。不幸的是,目前没有比较好的机制来找到其他相关联的库。我们希望这个问题可以得以解决,这样我们可以更加自由的分割仓库。




.NET 和 Docker结合使用_Jav_11





.NET 镜像经常更新,有时是安全性更新。可以通过执行 docker pull 下载这些更新,docker build 不会从服务器请求更新。


对于 Windows 镜像, 在 .NET 的 Dockerfile 定义中依赖了一个特定的基础镜像,具体可以参考microsoft/dotnet:1.0-runtime-nanoserver Dockerfile 中的第一行,如下:





FROM microsoft/nanoserver:10.0.14393.1198





在“补丁周二”(每个月的第二个星期二),Windows团队通常会发布补丁并更新其基础图像,.NET 镜像也会更新。


对于 Linux 镜像,.NET Dockerfile 中定义依赖了一个更通用的基础镜像,该镜会随时间发生变化。目前,.NET Core 选用 Debian Jessie,并向 Jessie 发布更新(例如8.6 到 8.7)。你可以在 microsoft / dotnet:1.0-runtime-deps Dockerfile Dockerfile 定义的第一行看到,如下所示:





FROM debian:jessie




Debian 仓库包括 Debian Stretch(Debian 9)的镜像,这是Debian Jessie(Debian 8)之后的一个版本。我们不会将使用 Debian Jessie 的 .NET Core 标签自动前滚到 Debian Stretch,但会像其他平台一样,为其创建一个新标签。




.NET 和 Docker结合使用_Jav_12






如果有工具可以将 VHD 转换为容器,岂不是让人很兴奋?


在 DockerCon 2017 上,一款名为 Image2Docker 的工具着实令人兴奋,它可以将 Windows VM 迁移到容器。该工具由 Docker 在 GitHub 上维护。


Image2Docker 的 PowerShell 模块可与 VHD,VHDX 或 WIM 镜像文件一起使用,并生成可构建 Docker 镜像的高保真 Dockerfile。该工具可以帮助你构建一个与 VHD 相同的 Docker 镜像,同时还能发挥 Docker 的优势。Image2Docker 虽然不会构建出最漂亮的 Dockerfile,但对于希望将 .NET Framework 应用迁移到容器的用户来说,这是一个不错的开始!


更多信息可参考 DockerCon 演讲之 Image2Docker(https://www.youtube.com/watch?v=YVfiK72Il5A),或者阅读 Docker 官方文章:使用 Image2Docker 将 ASP.NET 服务转换为 Docker(https://blog.docker.com/2016/12/convert-asp-net-web-servers-docker-image2docker/)。


: 运行 Image2Docker 需要安装 Windows版本的 Docker。




.NET 和 Docker结合使用_Jav_13





作为一个团队,我们至今已持续大约两年的时间专注于 .NET 与 Docker 的结合。期间,我们针对 Docker 的方案发生了很大变化,很大程度上是为了适应了 Docker 生态系统的快速变化。我们本身也大量使用 Docker。 我们将继续调整步伐来确保 .NET 是容器化应用程序的最佳平台之一。


在此,我想为在 Docker 工作的同事点赞。我们一直在与他们其中的很多同事合作,致力于 .NET 和 Docker 更好地结合。当我们提出可以让 .NET 开发人员更友好的改进时,Docker 的同事也很乐意接受建议。连拱标签和多级构建是我们早期参与而获益的一个良好示例。


原文链接:https://blogs.msdn.microsoft.com/dotnet/2017/05/25/using-net-and-docker-together/