.NET程序在Linux容器中的演变

原文链接:https://dzone.com/articles/the-evolution-of-a-linux-container

现在,.NET开发人员可以无障碍地使用如Docker这样的Linux容器,那么让我们来尝试如何以正确的方式配置一个容器。

可能,文章的标题改成“Linux容器开发人员的演变”会更好。由于.NET可在Linux(以及Windows和macOS)上运行,所以整个世界的Linux容器和微服务已经开放给了.NET开发人员。

有着大量的开发人员,长期的运行记录和优异性能指标的.NET,现在给以Windows为中心的开发人员提供了一个使用Linux容器的机会。

虽然在Linux容器中尝试运行.NET代码是诱人的,同时也会产生一些细微差别,但是这样做是不会错的。你可以很容易地将一些.NET代码推送到镜像中。

毕竟,一切都发生的这么快,一定都很好。 对不对?

事实并非如此。让.NET代码运行在Linux容器中并不是一件简单的事情,但请记住:“先让它工作,然后让它工作得很快。”

在下面的例子中,上文说的“很快”指的是构建镜像所需的时间,启动镜像所需的时间和镜像内部代码的性能。本文将首先讨论镜像的构建时间和启动时间,接着会将一个简单的.NET程序运行在基于容器的应用上,然后观察镜像大小的变化,最终缩短镜像的构建和加载时间。此外,代码优化是本文的另一个主题。

短暂的停留

考虑一个非常简单的微服务示例,它只给出一个“Hello world”类型的HTTP响应。也就是说,当在浏览器中填写URL,你就会得到一个包括主机名的Web页面。

我们可从这个代码库中(https://github.com/donschenck/dotnet_docker_msa)下载源码,并制作第一个Dockerfile(Dockerfile.attempt1),接着使用以下命令构建镜像:

#  docker  build  -t  attempt1   -f   Dockerfile.attempt1   .

然后在容器中运行镜像:

#  docker  run   -d   -p   5000:5000   --name   attempt1   attempt1

将浏览器的URL指向主机的IP地址,情况如下:

数字

第一次构建镜像,一共耗时95秒。其中,下载红帽企业Linux(简称RHEL)镜像与安装.NET SDK,这些文件一共490MB。最终,镜像大小为659MB。

一般而言,镜像的后续构建将更快,因为Docker化的镜像已经在主机上可用。改变源码后,我们再次运行构建。这一次构建镜像,大约耗时50秒,得到了相同大小的镜像,也是659MB。

镜像的大小很重要。因为镜像使用操作系统的存储空间,虽然空间便宜,但它仍然是有限的商品。当定期使用容器时,我们很容易忽略过时的镜像,然而它仍然在占用磁盘。如果你不注意的话,磁盘空间将很快用尽。

如何使镜像尽可能的小?

移除镜像不需要的部分

使用命令dotnet restore --no-cache可以消除任何缓存,这样镜像的大小下降到608.6MB,减少了50.6 MB,同比缩小超过7%。

在构建镜像之前构建应用

应用是在容器中运行镜像时构建.NET程序的。这耗时大约1.6秒——虽然时间不长,但却是在浪费时间。

在恢复之前插入的dotnet build命令,并在构建镜像之前构建应用,这样的话容器将会更快地启动。这个结果可在Dockerfile.attempt3中实现。

与此同时,镜像大小却增加到610.2MB,而我们还得运行dotnet build,虽然现在花这个时间,但却可在每次启动容器时受益。

运行Dotnet Publish命令

因为容器是一个运行时环境,那我们为什么不使用dotnet publish命令发布代码,然后把代码放入镜像呢?如果这样做的话,我们就没必要在镜像中安装.NET程序了。毕竟,我们需要的是一个可在任何地方独立运行的应用。

使用dotnet发布代码,会减少镜像大小和缩短容器启动时间。更改project.json文件,注释掉下图中红框的内容,这告诉编译器此文件为一个平台构建。您可以在下图中看到它:

接下来,我们使用dotnet publish -c Release -r rheh.7.2-x64发布代码,这会把所有的编译文件和运行时文件,放入一个文件夹,我们把此文件夹复制到镜像中。

因为我们不再需要安装.NET程序,只要一个包含RHEL文件的基础镜像即可,这样就减少了镜像的大小。这是Dockerfile的第四次迭代——Dockerfile.attempt4:

FROM registry.access.redhat.com/rhel7
RUN yum install -y libunwind
RUN yum install -y libicu
ADD bin/Release/netcoreapp1.0/rhel.7.2-x64/publish/. /opt/app-root/src/
WORKDIR /opt/app-root/src/
EXPOSE 5000
CMD ["/bin/bash", "-c", "/opt/app-root/src/dotnet_docker_msa"]

请注意,yum install命令将安装一些.NET需要的依赖文件,然后运行docker build命令,最终生成一个694.6MB的镜像。

谁需要缓存?

多次运行yum install命令,前一次操作将为后一次构建缓存。如果在每个yum install命令之后,我们立即清除缓存,效果将会很好。下面是Dockerfile的第五次迭代———Dockerfile.attempt5:

FROM registry.access.redhat.com/rhel7
RUN yum install -y libunwind && yum clean all
RUN yum install -y libicu && yum clean all
ADD bin/Release/netcoreapp1.0/rhel.7.2-x64/publish/. /opt/app-root/src/
WORKDIR /opt/app-root/src/
EXPOSE 5000
CMD ["/bin/bash", "-c", "/opt/app-root/src/dotnet_docker_msa"]

基于Dockerfile.attempt5构建的镜像,其大小减少到293.7MB,这比第一次构建缩小了55%。

堆叠命令

对Dockerfile做最后更改,我们需要堆叠yum install命令,具体内容如下所示:

FROM registry.access.redhat.com/rhel7

RUN yum install -y libunwind libicu && yum clean all

ADD bin/Release/netcoreapp1.0/rhel.7.2-x64/publish/. /opt/app-root/src/

WORKDIR /opt/app-root/src/

EXPOSE 5000

CMD ["/bin/bash", "-c", "/opt/app-root/src/dotnet_docker_msa"]

最终得到的镜像大小为257.5MB,这比第一次构建缩小了60%。

下面是各个Dockerfile构建的镜像大小对比图:

总结

在探索新技术与新模式时,我们不能将早期的结果与最优做法相混淆。虽然早期的成功会给我们带来兴奋和鼓励,但它也可能使我们丧失进步的动力。勤奋,然后不断尝试,并且始终接受改进的建议,会帮助我们走的更远。

时间: 2024-08-02 11:02:47

.NET程序在Linux容器中的演变的相关文章

通过 Docker 实现在 Linux 容器中运行 Microsoft SQL Server 数据库

首先,我们需要输入以下命令来下载镜像(解压完成后大约1.35GB). docker pull microsoft/mssql-server-linux:2017-latest 运行“docker images”来进行确认. 运行容器 接下来,您需要运行它.为此,您需要接受许可协议,并为管理员帐户指定一个安全的密码.如果您想要开发者版本以外的东西,那么您也可以指定PID.还有很多其他的环境变量供您进行设置,您可以参考https://docs.microsoft.com/en-us/sql/linu

在Azure中运行Linux容器ASP.NET 5 跨平台应用程序

在之前一篇的博客中,介绍了如何通过Virtual Studio 2015的Docker扩展工具直接发布ASP.NET应用程序到Azure公有云中的Windows Server 2016 TP3的Windows容器中,这个并不难理解,毕竟Windows 服务器内核就是Windows运行.NET应用程序是必须的:不过这里的伏笔是ASP.NET 5(vNEXT)的开源项目中具备通过DNX(.NET Execution Environment运行环境)将Bootstrap .NET应用程序的编译系统,S

在Linux和Windows的Docker容器中运行ASP.NET Core

(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 译者序:其实过去这周我都在研究这方面的内容,结果周末有事没有来得及总结为文章,Scott Hanselman就捷足先登了.那么我就来翻译一下这篇文章,让更多的中文读者看到.当然Scott遇到的坑我也遇到了. 不过首先,对于不熟悉的朋友我还是来解释一下Linux容器和Windows容器的概念. 由于容器成为虚拟化和应用托管的一种不可避免的选项,Windows也开始为公众提供容器功能(其实微软具备和使用

在容器中安装新的程序

简介:在docker容器中安装新的程序. 在容器中安装新的程序 目标: 提示: 正确的命令: 在容器中安装新的程序 下一步我们要做的事情是在容器里面安装一个简单的程序(ping).我们之前下载的tutorial镜像是基于ubuntu的,所以你可以使用ubuntu的apt-get命令来安装ping程序:apt-get install -y ping. 备注:apt-get 命令执行完毕之后,容器就会停止,但对容器的改动不会丢失. 目标: 在learn/tutorial镜像里面安装ping程序. 提

通过 Linux 容器进行虚拟化

简介 Linux 容器是一种轻量级"虚拟化"方法,用于在单个控制主机上同时运行多个虚拟装置(容器).另一个可用来描述 Linux 容器所执行的操作的术语是"容器化". Linux 容器提供操作系统级别的虚拟化,其中的内核控制隔离的容器.容器通过内核控制组 (cgroup) 和内核命名空间进行隔离.通过 Xen 和 KVM 等其他完整虚拟化解决方案,虚拟化子系统可模拟完整的硬件环境. Apache Web 服务器就是一个 Linux 容器使用案例.通过 Xen 或 K

Docker容器中运行ASP.NET Core

在Linux和Windows的Docker容器中运行ASP.NET Core 译者序:其实过去这周我都在研究这方面的内容,结果周末有事没有来得及总结为文章,Scott Hanselman就捷足先登了.那么我就来翻译一下这篇文章,让更多的中文读者看到.当然Scott遇到的坑我也遇到了. 不过首先,对于不熟悉的朋友我还是来解释一下Linux容器和Windows容器的概念. 由于容器成为虚拟化和应用托管的一种不可避免的选项,Windows也开始为公众提供容器功能(其实微软具备和使用容器技术很久了).这

在 docker 容器中捕获信号

原文:在 docker 容器中捕获信号 我们可能都使用过 docker stop 命令来停止正在运行的容器,有时可能会使用 docker kill 命令强行关闭容器或者把某个信号传递给容器中的进程.这些操作的本质都是通过从主机向容器发送信号实现主机与容器中程序的交互.比如我们可以向容器中的应用发送一个重新加载信号,容器中的应用程序在接到信号后执行相应的处理程序完成重新加载配置文件的任务.本文将介绍在 docker 容器中捕获信号的基本知识. 信号(linux) 信号是一种进程间通信的形式.一个信

在 Docker 容器中运行应用程序

案例说明 运行 3 个容器,实现对网站的监控. 三个容器的说明: 容器 web: 创建自 nginx 映像,使用 80 端口,运行于后台,实现 web 服务. 容器 mailer: 该容器中运行一个 mailer 程序,运行于后台,当接收到事件后会向管理员发送邮件. 容器 agent: 该容器运行一个 watcher 程序,以交互模式运行,用于不断地监测 web 服务的运行情况,一旦出现故障会立即向 mailer 容器发送消息. 创建容器 创建并运行 web 容器 $ docker run --

Linux 系统中 Docker 容器安装及使用

Docker 简介 产生背景 项目的开发环境和部署环境不一致,部署环境配置难度大.集群技术的发展,集群的相同配置操作难度大. 基本理念 使用Go语言实现的云开源项目,"一次编译,处处运行",只需要一次配置环境,就可以在其他环境一键部署,软件即容器,虚拟化. 虚拟化技术 虚拟机:一种带环境安装的解决方案,模拟整套操作系统,笨重. 容器:将所有运行环境打包成互相隔离的容器,不进行硬件虚拟. 开发/运维(DevOps) 开发自运维.一次开发,处处运维. 官方资料 Docker Docker中