在AWS上运行Docker:一个更好的开发&测试体验

时间:15-02-08 栏目:亚马逊 作者:爱说云网 评论:0 点击: 1,497 次

假如你正在为一个跟踪健康和健身的移动应用编写REST API。一开始你在你的笔记本上的开发环境中编写代码,在运行了所有单元测试并成功通过后,你将所有代码提交到Git,并且通知QA工程师测试。然而,当QA工程师认真地将最新版本代码部署到测试环境后却发现,这个新开发的REST程序往往连前几分钟的测试都通不过。

为什么会发生这样的情况?你明明已经完整的运行了单元测试,而代码移交给QA工程师之前又没发生任何问题。在与QA工程师一起奋斗数个小时后,你发现测试环境使用了一个过时版本的第三方库,而正是这个原因导致了你的REST程序无法正常运行。

在软件开发过程中,这个问题并不稀奇,开发、测试、演示、生产环境中的微小的区别都会引起各种各样的问题,而之前的处理流程已经适应不了现在应用程序快速的构建和部署流程。我们需要的是将开发环境与测试环境无缝对接,减少人工干涉以及配置。

AWS为开发者提供了自动化构建可靠及高效的开发环境。类似Amazon EC2和AWS CloudFormation等服务都允许开发者们通过代码的方式管理基础设施。通过CloudFormation服务,AWS资源可以使用JSON做预分配。CloudFormation模板可以在应用程序代码中正确的进行描述,通过EC2的自动化能力,用户可以快速和可靠地新建及结束某个环境。正是基于这个原因,AWS非常适合开发和测试工作。

类似Docker之类的容器技术让资源配置声明的理念成为现实。类似CloudFormation提供给EC2实例的功能,Docker为容器建立提供了一个非常实用的声明语法。同时,Docker容器并不依赖任何虚拟化平台,或者一个专用的操作系统。容器的运行仅仅需要一个Linux内核,这就意味着它几乎可以运行在任何环境之下——不管是笔记本或者是EC2实例。
Docker容器的架构如下图所示:

Docker容器使用了一个被称为Libcontainer的执行环境,它为不同的Linux内核隔离特性提供了一个接口,类似命名空间及控制组。这种架构允许多个容器在共享同一个Linux内核的情况下完全隔离地运行。鉴于Docker容器并不需要一个专用的操作系统,因此它比虚拟机更加的便捷和轻量。
Docker平台架构由下图一系列组件组成:

Docker客户端并不与运行的容器直接通信,取而代之,它通过TCP Sockets或REST与Docker守护进程通信,而守护进程将与主机上的容器直接通信。同时,Docker客户端并不需要与守护进程安装在同一台主机上。

在使用Docker时有3个理念必须理解:镜像(image)、注册表(registry)和容器(container)。

镜像,用于建立容器组件,它是个只读模板,使用它可以发布一个以上的容器实例。理论上说,它非常类似于AMI。

Registry用于储存镜像,既可以在本地,也可以在远程。当我们发布一个容器时,Docker首先会在本地Registry上搜索镜像。如果在本地Registry上没有发现,它随后会搜索远程公用的Registry,也就是DockerHub。如果在DockerHub发现所需镜像,Docker会将它下载到本地注册表,并使用它来发布所需容器。DockerHub非常类似于GitHub,我们可以使用它来建立公用或私有镜像资源。鉴于这个属性,有效及安全的镜像发布将非常便捷。

可以这么说,容器运行在一个镜像的实例上,Docker使用容器来执行和运行打包在镜像中的软件。

你也可以为一个正在运行的容器建立一个Docker镜像,类似为一个EC2实例建立AMI。举个例子,用户可以发布一个容器,并使用类似APT或者YUM的包管理器安装大量的软件,然后将更新提交到一个新的Docker镜像。

但是这里还存在更有效和灵活的途径来建立镜像,那就是使用Dockerfile,它允许声明式的镜像定义。Dockerfile语法由一系列的命令组成,我们可以用之安装和配置镜像中包括的各种组件。写一个Dockerfile就像茶余饭后使用UserData配置一个EC2实例那么简单。类似一个CloudFormation模板,Dockerfile可以使用一个版本控制系统进行跟踪和发布,你可以将Dockerfile比作一个镜像的建立文件。

那么在运动健身移动应用的打造中,Docker又会起到什么样的作用?应用程序架构由下图中的组件构成:

首先,我们需要建立一个Docker镜像,用于发布运行中REST程序的容器。我们可以在笔记本上测试我们的代码,而QA工程师则可以使用这个镜像在EC2实例上对应用程序进行测试。REST程序使用Ruby和Sinatra框架编写,因此它们需要被封装到容器中。我们将使用Amazon DynamoDB作为后端,因此,为了保证应用程序在AWS内外都可以使用,Docker镜像同样需要封装DynamoDB数据库。这样一来,Dockerfile的代码可能如下所示:

FROM ubuntu:14.04
MAINTAINER Nate Slater
RUN apt-get update && apt-get install -y curl wget default-jre git
RUN adduser --home /home/sinatra --disabled-password --gecos '' sinatra
RUN adduser sinatra sudo
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
USER sinatra
RUN curl -sSL https://get.rvm.io | bash-s stable
RUN /bin/bash -l -c "source /home/sinatra/.rvm/scripts/rvm"
RUN /bin/bash -l -c "rvm install 2.1.2"
RUN /bin/bash -l -c "gem install sinatra"
RUN /bin/bash -l -c "gem install thin"
RUN /bin/bash -l -c "gem install aws-sdk"
RUN wget -O /home/sinatra/dynamodb_local.tar.gz https://s3-us-west
-2.amazonaws.com/dynamodb-local/dynamodb_local_2013-12-12.tar.gz
RUN tar -C /home/sinatra -xvzf /home/sinatra/dynamodb_local.tar.gz
DockefFile的内容不再解释了,RUN关键字用以执行命令。默认情况下,命令执行在超级用户权限下。鉴于需要使用RVM来安装Ruby,我们需要使用USER关键字来转换到Sinatra用户权限,因此Ruby相关文件会安装到用户目录下。从USER命令生效起,随后的RUN命令都是使用Sinatra用户权限来执行。这同样意味着,当容器发布后,它也是以Sinatra用户权限来执行命令的。

Docker守护进程负责管理镜像与运行容器,而Docker客户端通常被用以将命令发送到守护进程。因此在使用上文Dockerfile建立镜像时,我们需要执行这个客户端命令:

$ docker build --tag=”aws_activate/sinatra:v1" .
在docker.io网站上,我们可以发现完整的Docker客户端命令说明文档。下面,我们着重看一下建立镜像所使用的命令。Tag选项用于在镜像上建立识别符,其典型值是owner/repository:version。这样一来,我们可以轻易的识别镜像中所包含的内容,并且可以从注册表中轻易的发现这个镜像的所有权。

在执行build命令后,我们可以在Dockerfile中使用声明来拥有一个配置好的镜像。Dockerfile如下:

$ docker imagesREPOSITORY TAG IMAGE ID CREATED
VIRTUAL SIZE
aws_activate/sinatra v1 84b6d4a5a22b
36 hours ago 942.2 MB
ubuntu 14.04 96864a7d2df3
6 days ago 205.1 MB
毫无疑问,我们可以看到Docker建立好了我们所需的镜像,并给它分配tag中指定的所有权,同时还会拥有一个唯一的镜像ID。现在,我们就可以通过新建立好的镜像来发布容器:

$ docker run -it aws_activate/sinatra:v1 /bin/bash
运行这个命令后,容器将成功发布,同时我们将进入Bash shell。在Bash shell中,我们可以像与Linux服务器一样与容器交互。鉴于我们建立的是一个Web应用程序,我们会从Git repository中克隆最新版本到容器,用以运行我们的单元测试,并做好给QA传送的准备。当代码被克隆到容器之后,并且做好了被测试的准备,我们会将运行容器中所做的更新克隆到一个新的镜像。为了完成这个步骤,我们需要确定容器的ID:

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b9d03d60ba89 aws_activate/sinatra:v1 "/bin/bash" 11 minutes ago Up
11 minutes nostalgic_davinci
下一步,我们运行提交命令:

$ docker commit -m “ready for testing” b9d03d60ba89
aws_activate/sinatra:v1.1
现在我们在本地注册表中会拥有一个新的容器:

$ docker imagesREPOSITORY TAG IMAGE ID CREATED
VIRTUAL SIZE
aws_activate/sinatra v1.1 40355be9eb8f
21 hours ago 947.5 MB
aws_activate/sinatra v1 84b6d4a5a22b
3 days ago 942.2 MB
ubuntu 14.04 96864a7d2df3
8 days ago 205.1 MB
Version 1.1版本镜像拥有服务我们REST端点所需的Sinatra应用程序。我们可以使用以下命令来运行Web应用程序:

$ docker run -d -w /home/sinatra -p 10001:4567
aws_activate/sinatra:v1.1 ./run_app.sh
上面命令告诉Docker需要做以下的工作:

从镜像aws_activate/sinatra:v1.1建立一个容器
(-d)表示以分离的形式运行容器
将工作路径设置为/home/sinatra (-w)
映射容器端口到主机端口4567——10001
在容器中执行一个叫做run_app.sh的shell script
这个shell script会在容器中启动DynamoDB,并且在4567下使用Thin网络服务器的模式发布Sinatra应用程序。现在,如果我们在运行这个Docker容器的笔记本浏览器中指向http://localhost:10001/activity/1,我们将看到以下结果:

{"activity_id":"1",
"user_id":" db430d35-92a0-49d6-ba79-0f37ea1b35f7",
"type":"meal",
"calories":100,
"date":"2014-09-26 15:33:58 +0000"}
我们的程序看起来运行良好——活动记录从本地DynamoDB中取出,并从Sinatra应用程序代码中以JSON的格式返回。

如果想让这个容器可以给QA工程师做进一步测试,我们可以将之推送给DockerHub这个公用的注册表。类似GitHub,DockerHub提供了公用和私有两个选项,可以满足这个容器不面向所有人的需求。

QA工程师将在EC2中运行这个实例,这就意味着我们将需要一个配置了Docker守护进程和客户端软件的EC2实例。假设需要使用CloudFormation启动一个EC2实例和CloudFormation表,我们可以借助CloudFormation AWS::EC2::Instance类型的UserData属性,使用Docker软件安装程序中的引导程序。CloudFormation中规定EC2实例的JSON文件可能拥有类似如下代码:

"DockerInstance": {
"Type": "AWS::EC2::Instance",
"Properties": {
"InstanceType": "t2.micro",
"ImageId": {"Fn::FindInMap" : ["RegionMap",{"Ref" :
"AWS::Region"}, "64"]},
"KeyName": {"Ref": "KeyName"},
"SubnetId": {"Ref": "SubnetId"},
"SecurityGroupIds": [{"Ref": "SecurityGroupId"}],
"Tags": [{"Key": "Name", "Value": "DockerHost"}],
"UserData": {"Fn::Base64":
"#include https://get.docker.io" }
}}
这样一来,如果QA工程师登入CloudFormation堆栈建立的EC2实例,镜像可以使用如下命令从远程的DockerHub注册表中取出:

$ docker pull aws_activate/sinatra:v1.1
这里从镜像中启动容器的命令和上文没太大的区别,有一个区别是环境变量会使用“-e”选项来设置,而Sinatra应用程序则会被配置为“test”环境。这个配置将使用区域端点(regional endpoint)来连接DynamoDB,而不是本地端点:

$ docker run -d -w /home/sinatra –e “RACK_ENV=test” -p 10001:4567
aws_activate/sinatra:v1.1 ./run_app.sh
到这里,QA工程师就可以通过HTTP在公共DNS(名称是EC2实例,端口号是10001)下访问REST端点。当然,前提你还需要设置一个安全组规则,并允许10001端口访问。如果发现任何bug,运行的容器可以提交到一个新的镜像,指定一个合适的版本号,并将之提交到注册表。容器的状态会被完整的保存,因此软件工程师可以便捷的复制QA中发现的问题,检查日志文件并且做常规的排错。

我们希望通过本文让用户对Docker有一个很好的认识,同时也认识到AWS和Docker的完美兼容。Docker的可移植性让它非常适合开发和测试,因为我们可以在多个团队中非常便捷的共享容器。EC2和CloudFormation完美的支撑了容器在AWS中的运行,但是AWS的便利绝不止于此。AWS ElasticBeanstalk,允许开发者将整个应用程序堆栈部署到Docker容器。经常关注本网站,你将看到更多关于AWS中运行Docker的博客。

相关文章

Docker入门教程:15个Docker进阶命令
views 2294
在之前的文章中,我们介绍了15个Docker命令,并分享了它们的使用经验。在这篇文章中,我们将学习另外的15个Docker命令。它们分别是: daemon: Docker daemon是一个用于管理容器的后台进程。一般情况下,守护进程是一个长期运行的用来处理请求的进程服务。-d参数用于运行后台进程。...
赞!Docker生态系统常用组件介绍 开启快乐开发之旅...
views 2140
Docker已经为开发者和管理员提供一个简单的平台来创建和部署可扩展的应用。本篇文章是介绍Docker生态系统的系列文章的第一篇在这个系列中,作者将探索Docker如何与其他组件整合在一起,并用它们提供的工具集来便捷地提供高可用性的分布式系统。本篇文章不仅从概念上介绍了容器化、服务发现和全局配置存储...
云自动化难驾驭?五步简化你的AWS自动化部署...
views 1685
亚马逊网络服务(AWS)客户需要经历五个步骤,才能驾驭云自动化。先要从只需点击操作的AWS Web控制台入手,最后是为云计算系统的声明式模型编写代码。 1. 熟悉AWS Web控制台 想尽快熟悉许多AWS服务,最简便的方法就是,在AWS Web控制台里面反复捣鼓和研究,看看发生什么情况。AW...
云计算之Docker:颠覆者还是昙花一现
views 1447
在云计算产业界,一场由一个技术掀起的革命正在悄悄上演:名不见经传的小公司横插进IT大佬构筑的云生态产业链,各路IT企业纷纷拥抱该技术并接纳该公司,云计算的构建方式和实施方式也即将发生或多或少的改变。这个技术就是Docker,这个公司便是dotCloud。 DotCloud本是家新创业小公司,基本无...
Amazon发布免费的AWS EC2 Container服务,且支持Docker...
views 1254
近日,在AWS re:Invent云计算大会上的第二场主题演讲中,Amazon的CTO Werner Vogels宣布发布AWS EC2 Container服务。目前,AWS EC2 Container服务预览版免费提供于开发者,开发者可以在官网进行注册使用。这是一项高度可扩展、高性能、免费的容器...
阿里云产品博客 » 阿里云ECS拥抱Docker
views 1235
现在我们很高兴地宣布您能使用阿里云ECS部署Docker容器应用了。您能在ECS上把应用打包成Docker镜像、运行Docker容器,从我们提供的镜像库中快速下载官方镜像,或者部署自己的私有镜像库,并和伙伴们分享和协作。 什么是Docker? Docker是一个开源项目,诞生于2013年初,最初是...
改善 Docker 镜像系统安全性的补救措施
views 1610
最近在使用Docker下载一个“官方”容器镜像时我看到这么一行提示:   ubuntu:14.04: The image you are pulling has been verified 我当时以为这和Docker极力推荐的镜像签名系统有关,所以并未深究。后来,在研究Docker镜...
云计算到底哪家强?
views 1877
从全球市场看,微软Azure+谷歌GCE+IBM的Softlayer+阿里云,四家的市场份额加起来也不及Amazon AWS。微软Azure强在哪? 技术储备。Amazon AWS虽然全球员工很多人,但是最核心的工程师也就几十个,而这种技术级别的在微软,可以说是一抓一大把。而且你看,现在的很多A...

声明: 本文由( 爱说云网 )原创编译,转载请保留链接: 在AWS上运行Docker:一个更好的开发&测试体验

在AWS上运行Docker:一个更好的开发&测试体验:等您坐沙发呢!

发表评论


读者排行