在AWS上构建你的第一台机器

作者/ Willi Richer

机器学习和机器人学博士,目前任职于微软Bing搜索核心研发团队。他从事多种机器学习领域的研究,包括主动学习和统计机器翻译。

当你有很多数据、需要进行很多计算的时候,你可能会开始渴求更多的计算资源。亚马逊http://aws.amazon.com允许你按小时租用计算资源。因此,你可以访问到大量的计算资源,而不需要预先购买大量机器(包括管理基础设施的费用)。在这个市场里还有其他的竞争者,但亚马逊是最大的玩家。

亚马逊Web服务(Amazon Web Services,AWS)是一组庞大的服务。我们只关注其中的Elastic Compute ClusterEC2)服务。这个服务提供给你虚拟机和磁盘空间,它们可以很快被分配和释放。

它一共有三种使用模式:保留模式,你可以预先支付,来获得更廉价的按小时的访问;每小时固定率;根据整体计算市场(需求少的时候,费用也低,但需求多的时候,价钱就会提高)提供变化的比率。

如果想测试的话,你可以在免费层级(free tier)中使用一台机器。它允许你试验一下这个系统,熟悉一下接口,等等。然而,这是一台CPU非常慢的机器。所以,我们并不建议用它进行过多的计算。

在整体系统的上层,有几种类型的机器,它们的费用不同;从单核到多核大内存系统,或者甚至是图形处理单元(Graphical Processing Unit,GPU)。我们之后会看到,你可以得到几台比较廉价的机器,并构建你自己的虚拟集群。你还可以选择Linux或是Windows服务器,其中Linux会便宜一点。在本文里,我们是在Linux上运行我们的例子的,但多数东西在Windows机器上也可以使用。

这些资源可以通过Web界面来进行管理。但也可以在程序中通过脚本来分配虚拟机,设置磁盘,以及所有Web界面所能做的操作。事实上,在Web界面频繁变化的同时,编程接口更为稳定。由于服务的引入,整体构架也保持着稳定。

通过传统的用户名/密码就可以访问AWS服务,尽管亚马逊把用户名叫做公钥,把密码叫做私钥。这样做是为了使访问Web接口的用户名和密码分开。事实上,你可以生成很多公/私钥对,并给予它们不同的权限。对于比较大的团队来说(一个能访问所有Web界面的高级用户可以为权限较少的开发者创建密钥),这样做是有益处的。

注意 亚马逊区域

Amazon.com有几个区域。它们与世界的物理区域相对应:美国西海岸、一些亚洲地点、南美区,以及欧洲区。如果你想迁移数据,最好使迁移的出发地和目的地比较靠近。此外,如果你正在处理用户的信息,并且要迁移到另一个管辖区去,那就可能会出现监管问题。在这种情况下,一定要让一个知情律师对欧洲客户数据迁移到美国的影响进行检查;反之亦然。

亚马逊Web服务是一个非常宏大的课题,市面上有各种可以涵盖AWS全部内容的书。我们只是要给你一个整体印象,告诉你AWS有什么,能做什么。基于实践精神,我们先看一些例子,但我们不会穷尽所有可能。

构建你的第一台机器

第一步是到http://aws.amazon.com/创建一个账号,这里的步骤跟其他在线服务类似。如果你想要的是比一台低端机器更好的机器,那你需要一张信用卡。在这个例子里,我们会使用一些机器,如果你想在上面运行程序的话,可能要花费一些钱。如果你还没准备掏出信用卡,那应该先阅读本文,了解一下AWS可以提供什么服务。然后,你可以做出更明智的选择,决定是否要进行注册。

一旦注册了AWS并登录,你会看到它的控制台。在这里,你可以看到AWS提供的多种服务:

我们选择并点击EC2(最左列的第二项,在本文撰写之时,界面是这样显示的;亚马逊经常会进行小修小改,所以你看到的可能有些不同)。我们现在看看EC2的管理控制台,如下图所示:

在右上角,你可以选择你的区域(见美国区域信息框)。注意,你只能看到所选区域的信息。因此,如果选择了错误的区域(或者在不同区域都有机器在运行),这些内容可能不会出现(在同其他程序员的聊天中得知,这似乎是使用EC2 Web管理控制台的一个常见陷阱)。

用EC2的说法,一个正在运行的服务器叫做一个实例(instance)。所以现在,我们要选择Launch Instance。现在,遵循典型惯例。选择Amazon Linux选项(如果你熟悉下列Linux发行版本之一,如Red Hat、SuSe或Ubuntu,可以选择其中之一,但配置将会有些不同)。我们从T1.micro类型的一个实例开始。这是最小的机器,并且是免费的。接受所有默认选项,直到进入下面这个输入密钥对后出现的界面:

我们选择名字awskeys作为密钥对。然后点击Create & Download your Key Pair按钮,下载awskeys.pem文件。这是Secure SheelSSH),将使你可以登录云机器。要把文件保存在安全的地方!接受完剩下的默认选项,你的实例就启动了。

现在你需要等待一小会儿,等实例出现。最终,这个实例会用绿色显示,状态为“运行中”。右击并选择Connect选项,你会知道如何连接。下面是一个标准SSH命令:

ssh -i awskeys.pem ec2-user@ec2-54-244-194-143.us-west-2.compute.amazonaws.com

因此,我们会调用ssh命令,并把之前下载的密钥文件传进去,作为身份信息(用-i选项)。我们会以用户ec2-user来登录地址为ec2-54-244-194-143.us-west-2. conompute.amazonaws.com的机器。当然,这个地址跟你的不同。如果在实例中选择另一个版本,用户名也会改变。不管怎样,Web界面会给你正确的信息。

最后,如果是在一个Unix风格的操作系统上(包括Mac OS)运行,你需要通过下面的代码调整它的权限:

chmod 600 awskeys.pem

这个命令会给当前用户设置读/写权限。否则,SSH会给你一个令人厌恶的警告。

现在,你应该可以登录主机了。如果一切顺利,你会看到下面的标语:

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2013.03-release-notes/
There are 1 security update(s) out of 3 total update(s) available
Run "sudo yum update" to apply all updates.

这是一个常规的Linux框,你拥有sudo权限;如果在前面加上sudo,你就可以像高级用户那样运行任何命令。你可以运行更新(update)命令让机器加速。

1. 在亚马逊Linux上安装Python包

如果你要使用另一个分发版本,可以用另一个分发版本的知识来安装Python、NumPy或者其他。在这里,我们采用的是标准亚马逊分发版本。从安装几个基本的Python包开始,如下所示:

sudo yum -y install python-devel python-dev python-pip numpy scipy
python-matplotlib

要对mahotas进行编译,还需要一个C++编译器:

sudo yum -y install gcc-c++

在这个系统里,pip被安装成python-pip。为方便起见,我们用pip来更新它自己。然后我们用pip安装必要的程序包:

sudo pip-python install -U pip
sudo pip install scikit-learn jug mahotas

在这里,你可以像使用pip那样安装任何其他程序包。

2. 在我们的云主机上运行Jug

现在,我们可以去下载数据和代码了。如下所示:

wget FIXME-LET'S-BUILD-A-TAR-GZ-PACKAGE
tar xzf chapter10.tar.gz
cd chapter10

然后,进行如下操作:

jug execute

它工作得还可以,但我们需要等很长时间才能得到结果。我们的免费级机器(t1.micro类型)是单核的,不是很快。所以我们要升级机器。

我们回到EC2控制台,在运行实例上右击,会出现一个弹窗。我们需要先停掉这个实例。在虚拟机上停止一个实例的运行就如同把它的电源关掉。你可以在任何时候停止你的机器。这时,你不再为它们承担费用(你仍然在使用磁盘空间,这也是有代价的,会分开计算费用)。

一旦你的机器停止了,change instance type选项就会出现。你可以选择一个更强有力的实例,例如,一个c1.xlarge实例,它是8核的。这个机器仍然处于关闭状态,你需要重新打开它(这跟重启机器是一样的)。

提示 AWS提供了几种价钱不同的实例类型。由于引入了更强有力的选项,信息经常会不断修正,同时价格也会发生变化,所以在这里我们没法给你很多具体细节(一般来说会变得更便宜);然而,你可以在亚马逊的网站上找到最新的信息。

我们需要等待实例再次出现。一旦它出来了,右击Connet获得连接信息。几乎可以肯定,链接信息已经改变了。在你更改实例类型的时候,你的实例会获得一个新分配的地址。

提示 你可以用亚马逊的Elastic IPs功能给实例分配一个固定IP。你可以在EC2控制台的左边看到这个选项。

通过使用8核机器,你可以同时运行8个jug进程,如下面这些代码所示:

jug execute &
jug execute &
(repeat to get 8 jobs going)

jug status可以查看到8个作业是否在运行。完成作业之后(现在应该很快就可以实现),你可以停止机器,并再次降级为t1.micro实例,以节省花销;微型实例是免费的,而这个特大号的机器的价钱是每小时0.58美元(这是2013年4月的价钱——到AWS网站上可以看到信息)。

用starcluster自动创建集群

正像你所了解到的,我们可以通过Web界面创建机器,但你很快就会发现,这个操作单调枯燥,而且容易出错。幸运的是,亚马逊有一个API。你可以写脚本自动执行之前讨论过的所有操作。更好的是,其他人已经开发了一些工具。你用AWS要进行的很多过程都可以用这些工具自动化执行。

MIT的一个团队正好开发了这样一个工具,叫做starcluster。它恰好是一个Python包,所以你可以用Python工具把它安装上。

sudo pip install starcluster

你可以在亚马逊主机上,或你的本地机器上运行这个代码。这两种方式都行。

我们需要指定我们的集群是什么样的。通过编辑配置文件即可实现。下面的代码生成了一个模板配置文件:

starcluster help

然后,我们选择选项在-/.starcluster/config中生成配置文件。做完之后,我们就可以手动编辑这个文件。

提示 不同的密钥

在使用AWS的时候,有三种密钥。

  • 标准用户名/密码组合,用于登录网站。

  • SSH密钥系统,它是一个用文件实现的公/私钥系统;通过你的公钥,可以登录远程主机。

  • AWS访问钥/密钥系统,这只是某种形式的用户名/密码。它允许你在同一个账户下拥有多个用户(包括为每个用户添加不同权限)。

要想查看访问/私钥,我们回到AWS控制台,在右上角点击我们的名字;然后选择Security Credentials。现在,屏幕的下方应该会出现形如AAKIIT7HHF6IUSN3OCAA的访问钥,本文将以它为例。

现在编辑配置文件。这是一个标准的.ini文件:一个文本文件,每个部分以方括号和它们的名字开始。选项的格式是name=value。第一部分是aws info,你应该把你的密钥粘贴过来:

[aws info]
AWS_ACCESS_KEY_ID = AAKIIT7HHF6IUSN3OCAA
AWS_SECRET_ACCESS_KEY = <your secret key>

现在来到一个有趣的部分:定义一个集群。starcluster允许你定义任意多个不同的集群。文件的开始有一个叫做smallcluster的集群。它在cluster smallcluster部分里定义。把它编辑为:

[cluster mycluster]
KEYNAME = mykey
CLUSTER_SIZE = 16

它把节点个数从默认的2改为16。我们还可以指定每个节点的实例类型和初始映像(记住,映像是指你使用的是什么操作系统,安装了什么软件)。starcluster有一些预设的映像,但你也可以构建自己的映像。

我们需要创建一个新的ssh密钥,如下所示:

starcluster createkey mykey -o .ssh/mykey.rsa

现在我们已经配置了一个16节点的集群,并设置了密钥。让我们尝试一下。

starcluster start mycluster

这个操作可能需要几分钟,因为它要分配17台新机器。为什么是17台机器,而我们的集群只有16个节点?这是因为会有一个主节点。所有这些节点都使用同一个文件系统,所以在主节点上创建的任何东西,也可以在从属节点上看到。这也意味着我们可以在这些集群上应用Jug。

这些集群可以按照你想要的方式进行使用,但它们都预装了一个作业队列引擎,这种方式对于批处理作业比较理想。它的使用过程很简单。

  1. 登录主节点。

  2. 在主节点上准备好你的脚本(更好的方式是,在之前就把脚本准备好)。

  3. 把作业提交到队列。一个作业可以是任何一个Unix命令。调度程序会寻找空闲节点来运行你的作业。

  4. 等待作业结束。

  5. 从主节点上读取结果。你现在还可以杀掉所有从属节点,以节约费用。在任何时候都不要忘记你的系统的运行是长期的。否则,这会花费很大(意思是美元和美分)。

我们可以用一个命令登录到主节点上:

starcluster sshmaster mycluster

我们也可以看到我们之前用ssh命令生成的机器地址,但在使用前面那个命令的时候,地址是什么并不重要,因为starcluster已经在背后把这些都处理好了。

正如我们前面所说的,starcluster为集群提供了一个批处理排队系统;你可以写脚本执行你的操作,把作业放在队列里,这些作业就会在空闲节点上运行。

在这一点上,你需要在集群上重复安装必要的程序包。如果这是一个真实项目,我们可以创建一个脚本来执行所有这些初始化工作,但由于这是一个教程,你只需要再初始化一次。

我们可以像以前一样使用相同的jugfile.py系统,但现在,我们会在集群中进行调度,而不是直接在主节点上运行。首先,写一个很简单的脚本:

#!/usr/bin/env bash
jug execute jugfile.py

run-jugfile.sh调用它,并用chmod +x run-jugfile.sh赋予它可执行权限:

For c in 'seq 16'; do qsub run-jugfile.sh; done

这会创建16个任务,每个都会运行run-jugfile.sh脚本调用Jug。你仍然可以使用主节点。需要特别强调的是,你可以在任何时候运行jug status查看计算状态。事实上,Jug就是在这种环境下开发出来的,所以它在这个环境下工作得很好。

最后,计算结束,可以把所有节点都删掉了。但我们一定要把预期的结果保存在某个地方,并执行下述命令:

starcluster terminate mycluster

注意,终止操作将会销毁文件系统中的内容以及所有运行结果。当然,这个默认设置是可以更改的。你可以让集群把结果写在一个不是由starcluster分配并销毁的而你又可以访问的文件系统里。事实上,这些工具的灵活性非常大。不过这些高级操作并不适合在本文里展开。

starcluster有一个优秀的在线文档,见http://star.mit.edu/cluster/,你可以读到关于这个工具的更多信息。我们在这里只看到了一小部分功能,只使用了默认的设置。

 

如今,机器学习正在互联网上下掀起热潮,而Python则是非常适合开发机器学习系统的一门优秀语言。作为动态语言,它支持快速探索和实验,并且针对Python的机器学习算法库的数量也与日俱增。《机器学习系统设计》最大的特色,就是结合实例分析教会读者如何通过机器学习解决实际问题。举几个例子,我们会介绍怎么把StackOverflow的回答按质量高低进行分类,怎么知道某个音乐文件是爵士风格,还是重金属摇滚风格。另外,本书还涵盖了主题建模、购物习性分析及云计算等高级内容。总之,通过学习本书,读者可以掌握构建自己所需系统的各方面知识,并且学以致用,解决自己面临的现实问题。本文节选自《机器学习系统设计》