中国领先的IT技术网站
|
|

Linux容器的进化

随着.NET开发者现在已经可以无障碍地使用Docker之类的Linux容器,就让我们直接来看看如何以正确的方式来配置一个容器吧。

作者:陈峻译来源:51CTO|2017-03-02 11:25

沙龙活动 | 去哪儿、陌陌、ThoughtWorks在自动化运维中的实践!10.28不见不散!


 【51CTO.com快译】自从.NET技术已经能够在Linux上运行(如同在Windows和macOS上那样)以来,全世界的Linux容器和microservices(微服务)都向.NET开发者开放了。由于.NET拥有着超大量的开发者群体,长期的成功开发案例和令人印象深刻的性能参数,它为全世界的Linux容器向着原本以Windows为中心的开发者阵营的扩展,提供了一个很好的机会。


虽然在Linux容器里运行.NET代码时,有一些不容错过的运行方面的细微差别,而这似乎显得有点冒进,但我还是会脱口而出那句“去吧,皮卡丘!”这可比要把一些代码推到镜像里去完成可容易多了。毕竟,一切都能快速实现,而且是恰到好处地,不是吗?


当然,让你的.NET代码能运行在Linux容器中可并不是一个微不足道的事,正如一个古老的谚语所言:“让它先工作起来,然后再让它能快速地工作。”


快速,在我们这里是指其在构建、启动镜像以及镜像内代码的性能等方面所花费的时间。本文将涉及前两个部分——构建和启动镜像的时间。我们将从一个简单的.NET程序开始,来运行基于容器的应用,然后观察镜像随其变小的进化,并导致构建和加载时间的缩短。


至于代码优化则是另外一方面的话题了,市面上有很多书可供您参考。

横空出世且如此鲜活!
试想一个非常简单的例子:微服务仅给出一个HTTP 类型的“Hello world”响应。也就是说,您将浏览器指向一个URL,并获得包含有主机名的一个非常简单的响应。记住这是一个简单的例子,我登录到了Linux虚拟机(VM)里,从其repo(请参见https://github.com/donschenck/dotnet_docker_msa)中这些下载代码。


就像面对任何新技术的开发者那样,在这种情况下,我想尽快在Linux容器中启动并运行该应用程序。因此我迅速地构造了一个Dockerfile(请参见repo中的Dockerfile.attempt1)并使用如下命令构建了一个镜像:


docker build -t attempt1 -f Dockerfile.attempt1 .


在构建成功完成后,我当然是兴奋的。而当我能用如下命令在容器中运行镜像时,我甚至有些激动了:


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


我将浏览器指向正确的URL,它也是我虚拟机的IP地址。请看下面的截图:

Linux容器的进化



一些数字
我在第一次构建该镜像的时候花了高达95秒的时间,其原因在于需要下载一整个490 MB大小的且安装了.NET SDK的红帽子企业版(Red Hat Enterprise Linux,RHEL)的镜像。这也导致了整个镜像的大小为659 MB。


公平地说,后续的构建将会更快些,因为docker格式的容器镜像,现在已经存在我的机器上且可用了。此后,我又改变了其源代码,并再次运行了构建过程,这次只花了大约50秒的时间,并产生了相同大小为659 MB的镜像。


其实镜像的大小还是很重要的。虽然您的机器上所使用的存储空间现如今已经是比较便宜的了,但它仍然是一种有限的商品。特别是在您定期使用容器的时侯,那些过期且被遗忘的镜像很容易就“静静”地是待在那里占用消耗了空间。因此您很容易在不小心之间就很快地填满了磁盘的空间。


那么,如何才能让镜像变得更小呢?

删除一些不需要的部分
将一个简单的命令行选项添加到dotnet restore命令中,是会有所帮助的。我使用的是:dotnet restore --no-cache (请参见Dockerfile.attempt2)来消除任何缓存,它将镜像的大小降至608.6 MB,也就是缩减了50.6 MB,即省下了百分之七以上的空间。但我对此并不满意,肯定还有更多的办法。

在构建镜像前先构建应用程序
我意识到:当我每次在一个容器里运行镜像的时候,应用程序都会构建.NET的应用。虽然这花去的1.6秒显然不是什么一个大块的时间,但是我还是觉得这有些浪费。我通过在dotnetretore之间插入命令build,并在构建容器镜像之前先构建其应用程序,这将会使得容器的启动要快得多。这个结果显示在Dockerfile.attempt3中。当然,这是以消耗存储空间为代价的,其镜像的大小升至为610.2 MB了。不过,dotnet build是无论如何都必须运行的,我们不妨现在多花点时间,而以后每次启动容器之后都能因此获益了。

Dotnet的发布
接下来,我们的焦点转移到一个问题上:既然容器是一个运行环境,那么为什么不使用dotnet publish命令使得在其被推入镜像之前就发布代码呢?如果我这样做了的话,我就不需要在自己的容器里预先安装好.NET了。毕竟,发布是允许您构建一个单独的(或“独立”)应用程序,而该应用程序可以在任何地方运行。这才是dotnet publish 的真正用途!而这将是镜像大小和启动时间上的巨大的成功。

我修改了project.json文件,用以支持发布。其间,我删除掉了用以告诉编译器去构建一个平台那段命令行(实际上是将它注释掉了)。您可以在下面的截屏上看到:

Linux容器的进化

接下来,我通过使用发布命令发布了代码:dotnet publish -c Release -r rheh.7.2-x64。此举将所有已编译的部分,包括所有运行时所必要的部分,都放到了一个我可以复制到镜像的文件夹里。当然,它可以变得更好一些:由于我不需要.NET 的预安装,所以我可以使用一个没有.NETRHEL基本版本。而之所以这样做,是因为此举肯定会节省更多的空间。
为了将各个部分推到镜像之中,我使用了以下是Dockerfile (请参见repo中的Dockerfile.attempt4):

Linux容器的进化

值得注意的是:两个yum install命令将会安装一些能使得.NET需要在RHEL之上的先决条件。目前尚无绕过这个问题的办法。但是,毕竟也不是什么大不了的。我运行了docker build,结果生成的镜像大小居然是694.6 MB!这到底发生了什么事呢?

谁会需要缓存呢?
原来,两个yum install指令也为未来的yum install命令构建了缓存。所以如果我能在每个命令之后立即清除缓存的话,就会好很多。以下就是我的第五次迭代Dockerfile,请参见Dockerfile.attempt5

Linux容器的进化

我们将运行了docker build与这个Dockerfile所产生的镜像文件大小相比较,可以看到,这次只有293.7 MB, 比第一次的尝试结果少了55%以上。此处该有鼓声。

该堆积的是命令而不是杯子
反映在文件Dockerfile中的, 我最后的修改是堆积式的yum install命令,如下:

Linux容器的进化

可见,由此产生的镜像大小为257.5 MB,相比我的第一次尝试,结果可是少了60%以上。最后,请让我以如下图表的形式来和您重温这次所提及的各次尝试:

Linux容器的进化


总结
当我们探索新技术和新模式时,我们必须小心,不要把我们早期的结果与我们最佳实践与努力相混淆。虽然早期的成功会带来了兴奋和鼓励,但它也可能使我们停下前进的脚步。我们应该勤奋、不断尝试并且总对改进建议保持开放的心态。

【原标题】The Evolution of a Linux Container (作者: Don Schenck)
原文链接:https://dzone.com/articles/the-evolution-of-a-linux-container

【51CTO译稿,合作站点转载请注明原文译者和出处为51CTO.com】

【编辑推荐】

  1. 总编下午茶:挑战者心态能否帮助微软重回云计算巅峰?
  2. 2017年云计算成本将会更低
  3. 解读云计算霸主AWS
  4. 阻碍云计算在数据中心发展的五大因素
  5. 阿里云联合英特尔国内首推Skylake版云计算产品 性能提升60%
【责任编辑:刘晓旭 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

Microsoft SQL Server 2005技术内幕:T-SQL程序设

SQL Server 2005微软官方权威参考手册。 是Inside Microsoft SQL Server 2005系列书中的第一本,SQL Server类的顶尖之作。 全球公认SQL S...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊
× Python最火的编程语言