.NET程序在Linux容器中的演变-阿里云开发者社区

开发者社区> 猫饭先生> 正文

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

简介: 本文讲的是.NET程序在Linux容器中的演变【编者的话】Linux容器技术已被开发人员所熟知,现在.NET程序可以跑在Docker容器中,这为以Windows中心的开发人员带来了好处。
+关注继续查看
本文讲的是.NET程序在Linux容器中的演变【编者的话】Linux容器技术已被开发人员所熟知,现在.NET程序可以跑在Docker容器中,这为以Windows中心的开发人员带来了好处。

【上海站|3天烧脑式微服务架构训练营】培训内容包括:DevOps、微服务、Spring Cloud、Eureka、Ribbon、Feign、Hystrix、Zuul、Spring Cloud Config、Spring Cloud Sleuth等。

本文将首先讨论镜像的构建时间和启动时间,接着会将一个简单的.NET程序运行在基于容器的应用上,然后观察镜像大小的变化,最终缩短镜像的构建和加载时间。此外,代码优化是本文的另一个主题。 

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

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

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

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

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

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

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

短暂的停留

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

我们可从这个代码库中下载源码,并制作第一个Dockerfile(Dockerfile.attempt1),接着使用以下命令构建镜像:
#  docker  build  -t  attempt1   -f   Dockerfile.attempt1   .

然后在容器中运行镜像:
#  docker  run   -d   -p   5000:5000   --name   attempt1   attempt1

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

数字

第一次构建镜像,一共耗时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文件,注释掉下图中红框的内容,这告诉编译器此文件为一个平台构建。您可以在下图中看到它:
02.jpg

接下来,我们使用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构建的镜像大小对比图:
03.jpg

总结

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

原文链接:The Evolution of a Linux Container(译者:Jack)

===========================================
译者介绍

Jack,开源软件爱好者,研究方向是云计算PaaS平台与深度学习,现积极活跃于Docker,Kubernetes,Tensorflow社区。

原文发布时间为:2017-03-17

本文作者:Jack

本文来自云栖社区合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。

原文标题:.NET程序在Linux容器中的演变

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
4067 0
Kubernetes容器运行时接口-CRI
Kubernetes提供了多种容器开放接口用于对接不同的后端来提供资源,如提供计算资源的容器运行时接口(Container Runtime Interface, CRI),提供网络资源的容器网络接口(Container Network Interface, CNI),提供提供存储资源的容器存储接口(Container Storage Interface, CSI)。
647 0
容器服务-Kubernetes知识图谱
1. 概述  容器服务 kubernetes知识图谱,部分内容参考网上一知识图谱,更加结合阿里云容器服务。https://www.processon.com/view/link/5ac64532e4b00dc8a02f05eb#map 2.
1786 0
Kubernetes容器运行时接口-CRI
Kubernetes提供了多种容器开放接口用于对接不同的后端来提供资源,如提供计算资源的容器运行时接口(Container Runtime Interface, CRI),提供网络资源的容器网络接口(Container Network Interface, CNI),提供提供存储资源的容器存储接口(Container Storage Interface, CSI)。这篇作为这系列的开篇,主要介绍了kubelet的CRI接口实现。
393 0
Kubernetes必备知识: 容器接口CRI
每种容器运行时各有所长,许多用户都希望Kubernetes支持更多的运行时。在Kubernetes 1.5发布版里,我们引入了CRI–一个能让kubelet无需编译就可以支持多种容器运行时的插件接口。CRI包含了一组protocol buffers,gRPC API,相关的库,以及在活跃开发下的额外规范和工具。CRI目前是Alpha版本。 支持可替换的容器运行时在Kubernetes中概念中并非首次。在1.3发布版里,我们介绍了rktnetes项目,它可以让rkt容器引擎作为Docker容器运行时的一个备选。然而,不管是Docker还是Rkt都需要通过内部、不太稳定的接口直接集成到kubele
225 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4478 0
在阿里云Kubernetes容器服务上打造TensorFlow实验室
利用Jupyter开发TensorFLow也是许多数据科学家的首选,但是如何能够快速从零搭建一套这样的环境,并且配置GPU的使用,同时支持最新的TensorFLow版本, 对于数据科学家来说既是复杂的,同时也是浪费精力的。
4674 0
+关注
350
文章
1300
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载