Linux容器的进化

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

 【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】

责任编辑:关崇 来源: 51CTO
相关推荐

2013-11-11 15:04:52

2018-05-14 12:30:37

数据驱动算法优化

2012-09-28 13:23:43

编程语言语言进化程序员

2023-02-07 13:36:24

功能代码

2022-05-24 12:34:32

Docker容器Linux容器进程

2021-12-25 23:25:04

LinuxDocker容器

2013-12-12 16:51:43

安卓进化AndroidGoogle

2012-09-11 10:23:24

Windows 8

2023-10-30 16:02:20

区块链元宇宙

2013-12-12 16:23:53

安卓进化AndroidGoogle

2019-08-29 08:13:28

2022-01-24 17:08:33

Linux容器Docker

2014-01-15 10:04:38

LXCLinux 容器Linux 内核

2009-03-04 12:16:13

2018-03-12 20:18:16

Linux容器安全

2017-11-28 09:21:53

LinuxWindowsDocker

2022-03-24 10:51:41

架构技术数据库

2019-02-28 22:14:27

云计算混合云公共云

2011-07-08 17:37:10

软件思想

2020-03-03 08:40:16

细腰架构进化
点赞
收藏

51CTO技术栈公众号