摘要:在云栖TechDay34期:DockerCon2017最新的技术解读中,阿里巴巴技术专家谭林华为大家介绍了Docker的最新特性以及与传统场景相比,这些新特性所具有的优势和所能够解决的问题。
以下内容根据演讲嘉宾现场视频以及速记整理而成。
演讲嘉宾介绍:
谭林华(花名:霖华),阿里云容器服务团队技术专家,具有多年PaaS产品研发经验,在平台设计、基础架构等方面具有深厚的功底,Docker技术实践者,目前负责阿里云容器服务镜像构建等功能。
本次将分享关于Docker新特性如下的几个点:
首先看一下多阶段构建,构建镜像了解Docker的同学应该比较熟悉。现在想象一个在Java语言下构建镜像的场景,这个场景下开发者提交代码,然后一个构建模块,之后就去拉取GitHub或者其他源代码仓库的代码,最后执行构建。像对这种静态语言的构建而言,其实过程会稍微麻烦一点,比如说要有编译器对它进行编译,然后跑单元测试,然后打包,打包到最后就生成了War包或者Jar包,最后推到Registry中去。这个过程有一个问题就是如果把所有的内容都放在一个Dockerfile里面,源代码就会包含在镜像里面,所以这里有源代码泄露的风险。还有就是具体的生产环境其实是不需要那些编译器源代码以及测试框架或者打包框架的,最终可能只需要一个简单的运行环境就可以了,这样会导致镜像变得特别大,所以这个方案不是特别好。
再来看一下Docker在5月份出来的一个最新的17.05版本,这个版本中引入了multi-stage build,也就是多阶段构建。它的思路就是把刚刚提到的在构建镜像的过程拆成两个阶段,这两个阶段都会产生镜像,第一个阶段就是去执行编译、测试然后打包,得到一个镜像,对于第一阶段得到的镜像,并不会使用这个镜像的全部内容;在第二阶段,可以把第一个阶段的Jar包拷贝到第二个阶段,这样的好处就是没有源代码泄露的风险,因为最终打包到生产环境的只有最终编译的字节码文件。同时整个镜像也变得很小,因为它里面没有包含源代码也没有包含一些编译器的软件、测试框架和打包工具,所以这个方案是比较完美的。
然后看一下它最终的实现,上图展现的主要是设计思路,最终实现是通过加入一些简单的语法来支持这个功能的。下图显示的就是一个Dockerfile,它分为两个构建阶段,两个构建阶段都会生成镜像,然后第二个构建镜像是通过引用第一个构建镜像的方式,把第一个构建镜像里面的Jar包打到第二个镜像里面。主要使用了copy命令,这里面有个from参数,from参数引用了第一个构建镜像的名称,Dockerfile增加了一个新的语法就是AS命令,上面这个红框中from指令就相当于给这个构建阶段取了一个名字叫build-env,然后在第二个构建阶段里面就引用它,相当于把第一个构建镜像里面target目录下面的app.jar拷贝到当前镜像的工作目录中,这样的好处就是最终只包含了运行环境和部署到生产环境的Jar包,所以整个镜像也变小了,也没有代码泄露的风险。
下图展现了在Docker 1.13中引入的一些资源管理命令,比如docker system df,它可以查看整个容器集群里面资源的使用情况,包括镜像的使用情况以及数据卷的使用情况。还有就是以前要删除镜像或者删除容器可能都比较麻烦,需要指定镜像ID或容器ID,现在它提供了docker system prune的命令,可以很方便地一键清理所有没有被使用或者没有被引用的资源,这些资源包括所有停止的容器,还有就是所有没有被引用的镜像,以及数据卷和网络资源对象。没有被引用的镜像是什么意思呢?这里用了dangling这个英文单词,稍后会具体介绍。还有就是在一键清理这些资源的同时,可以针对某个特定的资源进行清理,也就是第三条命令,可以清理所有没有被使用的容器镜像、网络或者数据卷。
我们来了解一下dangling的含义,在17.05的时候,其实dangling的含义发生了一点点变化。之前dangling的含义指的是同时没有版本和名称的镜像,这个镜像版本指的是就是镜像tag,像这种情况它会认为是dangling的镜像,所以在使用prune的时候会把它删掉。现在dangling的含义也包括有镜像名称,但是没有镜像版本的镜像。这种情况最常见于要构建相同tag的镜像时,新构建的镜像会把老的覆盖掉,最后它的镜像tag会变为none,所以这种情况在prune中删掉也是合理的。
接下来介绍一下Docker Secrets,Secrets这个特性的引进还算比较早,在1.13版本就引入了。大家可以理解一下在引入Docker Secrets之前是怎么样传入一些密码这样的敏感信息的,比如下图这里就是一个简单的compose文件,这个文件里面就是通过环境变量的方式把密码传进去,而这样的方式有一个问题就是不是很安全,还有就是也不适合权限管理,因为有些程序员可能不需要知道密码里面具体的内容,只需要去部署这个compose文件,所以这里有这样的两个缺点。
Docker Secrets试图解决这两个问题,首先它的整个密码传输的过程都是通过TLS加密的,就是用户如果要把它密文通过Docker Secrets的方式保存的话,它最后会保存到中心化的存储上,然后当某个服务要引用Docker Secrets的时候,它会通过TLS的方式传输到各个节点上,并且已经是以内存文件系统的形式挂载到容器上。
下图展示的是具体的Compose模板的例子。在这个Compose模板里边其实并没有Secrets密码的明文保存,例如这个地方定义了Secrets,声明Secrets的引用是来自于外部,包括这个wp_db_password和root_db_password,并且声明了它的使用,还有就是它具体的使用方法,最后所有的Docker Secrets对象都会挂载到容器里面的run/secrets目录下面,这个目录是固定的,后边还要加上Secrets的名称。
Docker 17.05还加入了Docker Service Logs。之前在查看容器的日志或者任务的日志的时候,比如在排查问题时,除非用了别的工具,要不然经常是需要挨个任务或者挨个容器进行查看,Docker Service Logs就试图解决这个问题。这个 Service Logs通过服务维度把所有任务的日志列出来,例如下图中就表明了Redis服务的很多任务。然后将它整个日志会按时间排序聚合起来,这样的好处就是聚合该服务下所有日志后,当进行调试时就可以方便地查找这些日志。还有就是当需要把这些日志保存到一个集中的地方的时候,通过这个接口也可以直接导到别的地方,而不用把每个容器的日志再收集一遍。
再来看一下swarm mode下Service的另外一个特性,就是Service Create和Service Update这两个命令。之前使用的Docker Create和Docker Update都是异步的,就是创建或者更新完资源之后,这个命令调用完之后会立马返回。只有通过Service ls或者Service ps的方式来察看这个任务是处于运行状态还是失败状态。但是现在它加了一个参数叫detach,detach等于false就表明这个命令是在同步的状态下执行的,所以它会同步地返回,这指的是它创建的所有任务会显示状态,然后一直等到它们处于Running状态或者运行失败状态时,才会最终返回停止。下图是两个示例,就是跑了一个Redis服务,它会等所有任务返回之后再停止。
17.05版本的另一个比较有亮点的特性就是拓扑结构感知的调度。关于调度这块,先来看一下swarm mode之前的调度方式。假设在两个可用区下面一共有三个节点,可用区1有两个节点,可用区2有一个节点,如果使用Service Create命令创建一个Web APP的时候,指定下面有六个任务,所以Service Create默认使用的是spread的调度策略,它会在每个节点上平均分配容器,所以最终导致的结果就是可用区1有四个容器,可用区2有两个容器。但是事实上为保证可用性,通常会倾向于每个可用区都有一半的容器,也就是可用区1和可用区2各有三个,这样当可用区1坏掉之后,可用区2能有三个容器来提供服务和支持,特别是在一些流量比较高的场景下,三个容器比两个容器会有更高的可用性支持。所以在这种情况下用传统的swarm调度方式是比较麻烦的,如果默认用spread调度方式就只能够每个节点两个容器分配,还有一种方式就是用Constrain,Constrain存在一个问题就是必须标识每个节点,比如说指定把三个容器部署到node 3,然后再把剩下的三个容器部署到node 1和node 2,这种方式其实跟按照可用区调度是不符合的。
那么引入的新特性是怎么做这件事情的呢?在Service Create的时候增加了一个placement-pref参数,这个参数有spread等于后面的label,这里的意思是什么呢?就是说采用的调度策略还是跟之前一样用spread,由于均分的调度策略,但是具体调度是按照某个label表示的可用区来调度。这个集群会对于下面所有具有labels.az的节点,查看它们的label有几个值,比如说现在有两个值az1和az2,会按照这两个值来均分整个的调度策略,最终的结果就会在az1上调度三个容器,然后az2也调度三个容器,就达到了按照可用区域来均分容器的目的。
最后来看一下Docker在健康检查这块引入的改动,健康检查其实是已经存在比较久了,在1.12中就引入了。先来了解一下健康检查使用的命令,健康检查有两种设置的方式,可以在Dockerfile中就是构建镜像的过程中设置默认的健康检查,然后通过健康检查的命令来执行,所以通过Shell命令支持的方式,就可以通过各种方式来检测应用程序和容器的健康性。还有就是在Compose file里面可以对一些检查的参数进行细粒度的调整,例如健康检查的间隔时间以及超时时间,还有就是连续出现多少次失败才认为是不健康的。在17.05加了一个参数Start Period,举个例子解释这个启动参数的意义所在,就是比如配置好MySQL数据库启动起来大概要个二、三十秒,它的健康检查间隔时间可能需要5秒或3秒,这种场景就比较适合用Start Period,这样的健康检查比较符合刚刚说的MySQL启动的特性,当然还有一些其他类似的程序。
接下来看一个具体的例子,下图展示的是对Redis做健康检查,就是有一个文件叫docker-healthcheck,是用这个文件来检查Redis。Redis的客户端发一个ping的命令,当服务器端返回PONG时健康检查就通过了,否则健康检查失败。下面是健康检查涉及的Dockerfile,就是最下面这一行。
以下内容根据演讲嘉宾现场视频以及速记整理而成。
演讲嘉宾介绍:
谭林华(花名:霖华),阿里云容器服务团队技术专家,具有多年PaaS产品研发经验,在平台设计、基础架构等方面具有深厚的功底,Docker技术实践者,目前负责阿里云容器服务镜像构建等功能。
本次将分享关于Docker新特性如下的几个点:
- 多阶段构建
- 资源管理
- Docker secrets
- swarm mode
- 健康检查
首先看一下多阶段构建,构建镜像了解Docker的同学应该比较熟悉。现在想象一个在Java语言下构建镜像的场景,这个场景下开发者提交代码,然后一个构建模块,之后就去拉取GitHub或者其他源代码仓库的代码,最后执行构建。像对这种静态语言的构建而言,其实过程会稍微麻烦一点,比如说要有编译器对它进行编译,然后跑单元测试,然后打包,打包到最后就生成了War包或者Jar包,最后推到Registry中去。这个过程有一个问题就是如果把所有的内容都放在一个Dockerfile里面,源代码就会包含在镜像里面,所以这里有源代码泄露的风险。还有就是具体的生产环境其实是不需要那些编译器源代码以及测试框架或者打包框架的,最终可能只需要一个简单的运行环境就可以了,这样会导致镜像变得特别大,所以这个方案不是特别好。
健康检查有什么用呢,之前提到可以通过docker ps来查看任务或者容器是否处于健康状态,但是现在比如这里的Redis它后面会有一个状态告诉你这个容器是否健康。同时这个健康检查还会跟swarm mode调度结合在一起,这是什么意思呢?就是说如果swarm根据你设置的健康检查指令发现任务失败了,它就会认为这个任务健康检查不通过,但是这个任务还在运行中,它会主动地把运行中的这个任务退出,然后再新起一个任务,比如你的任务指定了一定要始终保持三个拷贝,然后它就始终会有三个拷贝在运行状态,还有就是它在启动时会保证去检查健康状况。