戳蓝字“CSDN云计算”关注我们哦!
技术头条:干货、简洁、多维全面。更多云计算精华知识尽在眼前,get要点、solve难题,统统不在话下!
当我们在大概七年前将buildpack技术开源的时候,我们就知道这项技术将大大简化应用的发布过程。当一个开发人员要跑一个:git push heroku master的时候,一个buildpack就可以保证所有的依赖以及编译工作都会作为发布的一部分被全权打理好。
一如之前所宣布,我们正在将buildpacks之所以如此成功的哲学理论运用到创立云原生buildpacks(CNB),一种再也不需要dockerfile并将源代码转换为Docker Image的标准。在这篇文档里,我们会简单地看一下CNB 是如何工作的,它是如何旨在解决许多现存于dockerfile上的问题, 以及你如何可以将它和最近发布的 buildpacks.io 项目 beta版本一起使用。作为这次发布的一部分,我们已经给 Ruby, Node.js, Java, Python, PHP, 以及Go建立好了可以使用CNB 工具的Heroku buildpacks builder image。
我们先从创造一个稻草人(假设模型)开始。我们会通过一些比较冗长无聊但却很根本且必须的步骤来创建一个Ruby on Rails 应用的Dockerfile。
抽象泄露:递增地来写一个Dockerfile
大多开发者以写一个dockerfile 来使用Docker,这个file会定义整个生成Docker image的build流程。比如说, 你手里有一个的Rails 项目,并且你希望将它以一个Docker Container来发布。你会需要从一个基本的Ruby Image 开始, 将其他额外需要的包加入进来,以此来让你的应用跑起来。如果你从未用过Docker,你可能要先学几样东西才能到这一步。
FROM ruby
RUN apt-get update -qq \
&& apt-get install -y nodejs libpq-dev build-essential
COPY . /app
WORKDIR /app
RUN bundle install
RUN bundle exec rake assets:precompile
EXPOSE 5000
CMD bin/rails s
除了Ruby以外,一个Rails应用的Docker image 也将会需要几个额外的 Apt包。要想跑用来预编译assets的必须要的工具, node js 的runtime一定要被包含在其中;为了要和PostgreSQL 交流, libpq-dev也是需要的;以及为了让gcc能够给Ruby gems 们build原生的插件(一个Ruby包管理器,类似Python的 pip),我们还会需要到build-essential。
一个Dockerfile 已经足够用来在生产环境跑一个简单的Rails应用,但是整个image会充满很多非相关的缓存文件夹。你或许会想去掉那些文件,以缩减整个image大小,但这只会对本地有序性build有用。
RUN apt-get update -qq \
&& apt-get install -y nodejs libpq-dev build-essential \
&& apt-get clean autoclean && apt-get autoremove -y \
&& rm -rf /var/lib/apt /var/lib/dpkg /var/lib/cache /var/lib/log
# ..
RUN bundle exec rake assets:precompile
&& rm -rf /app/tmp/cache/assets/
这便突出了使用Dockerfile在速度上的短板。它并不能适当利用那些缓存目录,只因为一次rebuild就会要把整个image从头build。然而,我们可以利用些聪明的小技巧来缓存依赖(dependency)的信息来加速builds。相比较每次复制整个应用,你可以如下选择性地加入文件。
ADD Gemfile /app/
ADD Gemfile.lock /app/
RUN bundle install
复制加入Gemfile 和Gemfile.lock 可以用来取代dockerfile的缓存机制,以此来防止因为仅你更改了一行代码而导致所有的缓存失效。
这些例子仅强调出了“几个” 当使用Dockerfile构建应用时的挑战和难点。而且你会在每次使用Dockerfile构建应用时都会重复遇见这些困难。多数情况下,最后你都会复制粘贴某个应用Dockerfile 的一部分到另外一个应用的Dockerfile,而这种行为会潜在地造成维护人员噩梦。
维护,是Dockerfile最大的短板。除了复制粘贴代码,它给你带来了一些如果你不使用Dockerfile是绝对不会关心的低等级问题。举例来说,和很多其他编程语言一样,Ruby有很多基本image 可以给你用来继承使用,而且每个image都有自己的大小和安全性考量。如果有一天,Rails的生态系统系要一个新的依赖(dependency),而这个新的依赖并不存在于你现有的Dockerfile,那你就要负责根据需要更新相关的设置。如果你把你的项目分拆成多个“微服务”(microservices),同时也意味着你需要在多个位置更新你的文件。
最终来说,Dockerfile是个具有漏洞性的抽象概念。它强制开发者们要小心运作上,以及平台的需要注意点,而在这之前这些需要注意点都已经被平台抽象打理了。要想写出一个好的Dockerfile,你就必须理解底层机制,以及产生image的每一步是如何工作的并以此来打理未来的更新。
所有的这些问题都源于Dockerfile缺少对应用的相对了解。如果没有你的应用相关或所使用架构的上下文相关信息,开发者如何开发和他们使用的工具就会产生巨大的不匹配现象。
像这样将运行和应用的关注点混合在一起就相当于给了一个希望尽量容易去写代码以及运输代码的开发者一个非常差的工具。基于此类不足之处,我们接下来看一下另外一种可以减少此类复杂度的替代办法。
向Buildpacks学习
如果你曾经使用Heroku来发布你的应用你就会知道,这就像在本地跑一个 git push heroku master 一样容易。而在幕后,一个 buildpack 就会收集依赖(retrieve dependencies),运行数据信息,打理缓存,并且给你的应用语言编译代码。举例来说,有一个Rails 应用。Ruby的buildpack就会安装Ruby和 bundler(一个Ruby的依赖管理应用)。你的gem(一系列包和文件的总称)依赖会被提取出来,你的资源代码会被编译,而且缓存也会被清理干净:
git push heroku master
remote: Compressing source files... done.
remote: Building source:
remote: -----> Ruby app detected
remote: -----> Compiling Ruby/Rails
remote: -----> Using Ruby version: ruby-2.6.0
remote: -----> Installing dependencies using bundler 1.15.2
remote: Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
...
remote: Bundle complete! 18 Gemfile dependencies, 61 gems now installed.
remote: Gems in the groups development and test were not installed.
remote: Bundled gems are installed into `./vendor/bundle`
remote: Removing bundler (1.15.2)
remote: Bundle completed (42.62s)
remote: Cleaning up the bundler cache.
...
remote: Asset precompilation completed (3.72s)
remote: Cleaning assets
remote: Running: rake assets:clean
remote: -----> Detecting rails configuration
remote: -----> Compressing...
remote: Done: 41.3M
remote: -----> Launching...
remote: Released v6
remote: https://myapp.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
一个Buildpack以识别你应用代码语言的行为惯例帮你自动处理了这些步骤。Builpacks的设计原则就是就是安装并设置所有需要的设置以帮你达到运行你的应用的目的。站在云原生Buildpacks的角度来说,我们希望利用Docker和现代container标准,有一个相似的系统来让我们的开发者更加注重于他们的应用,而不是拼凑一个build管道。
运行云原生Buildpacks
因为希望能够将 Buildpacks 的简洁性和可用性与container的优势结合起来,我们开发了云原生Buildpacks(CNB),它可以产出一个 “开放容器计划”兼容(OCI-Compliant)image, 并且这个image可以使用现存 的Docker 工具,以及更广阔的现存容器生态系统。
Buildpacks.io项目是一个让我们愿景成可能的开源工具之家。其中,第一个工具便是 pack build,其行为和git push heroku master非常相似。你可以在任意资源库下产生一个Docker image。以下便是一个在Rails应用上跑的Heroku云原生buildpack的例子。
和buildpack产生可执行的压缩文件(slug)很相似, 一个CNB会根据你现有项目的文件识别什么东西需要被安装。你不需要进行任何配置来识别确认你的应用的需求。因为buildpack 是应用感知的(app-aware),它能够精准地捕捉到你所使用的编程语言以及你应用的所需依赖,在build阶段也同样会有着合理的系统默认值来处理内存的使用和处理并发。
CNB 进程在产生最后image的进行的步骤和现存Heroku Buildpacks运作的不同阶段非常相似:
· CLI 侦测到你的项目使用的主要语言。比如说,如果你的源代码目录下有一个Gemfile,那么CNB就会将它识别为一个Ruby项目;如果有个pom.xml那么它就会被识别为一个Java项目,以此类推。
· 之后,执行环境会分析之前的build来确认其中是否有任何步骤可以在接下来build中使用。
· CNB开始跑build,下载所需的依赖以及将应用准备好可以在production当中运行。
· 最后,它会将以上build的结果以Docker image的形式导出。
产出一个image的底层幕后进程全权被buildpack所打理。如果这个进程需要被更新-比如有个漏洞被检测到-你可以很轻松地获取一个新的工具链并且用 pack rebase重新build一个image来更新你的image而不用从头开始build一个image, 整个过程花费不到一秒钟。和用Dockerfile在你每一个应用上rebuild相比(一个可能耗费几个小时的过程),这会节省大量的时间。
现在就来试一下云原生Buildpacks吧!
再也没有比现在更好的时机来尝试一下云原生Buildpacks(CNB)了。这个项目刚刚出了它第一个beta 版本, 它已经准备好被你使用,也希望到你的反馈!
如何开始呢?先下载 pack CLI 并且在你的应用源代码目录下使用我们任一buildpack(Ruby, Node.js, Java, Python, PHP, Go)。
$ pack build --builder heroku/buildpacks
来Slack上加入我们!如果你还想生成你自己的OCI Images,我们还有定义了使用细则的buildpack API文档 。
Heroku 一直以来都认为切合开发者的关注和需求是非常重要的:即他们应用的源代码。我们相信云原生Buildpacks 会因为以建立了基于容器的应用而帮助减少在运行维护上的复杂度,从而解放了开发者双手来专注于给他们的用户创作更多优秀的的功能和特性。
福利
扫描添加小编微信,备注“姓名+公司职位”,加入【云计算学习交流群】,和志同道合的朋友们共同打卡学习!
推荐阅读: