Kubernetes是近年来突出的技术之一;它已被所有主要公共云提供商采用为容器集群和编排平台,并迅速成为行业标准。
再加上Kubernetes是开源的,您就拥有了在多个公共和私有提供商上托管自己的平台即服务或PaaS的完美基础;您甚至可以在笔记本电脑上运行它,并且由于其设计,您将在所有平台上获得一致的体验。
它的设计也使其成为运行无服务器函数的理想平台。在本书中,我们将研究几个可以部署在Kubernetes上并与之集成的平台,这意味着我们不仅拥有PaaS,还有一个强大的FaaS平台在您的Kubernetes环境中运行。
本书主要面向运维工程师、云架构师和开发人员,他们希望在Kubernetes集群上托管他们的无服务器函数。
第一章,无服务器景观,解释了什么是无服务器。此外,我们将在公共云上使用AWSLambda和AzureFunctions来运行无服务器函数,获得一些实际经验。
第二章,Kubernetes简介,讨论了Kubernetes是什么,它解决了什么问题,并且还回顾了它的背景,从谷歌的内部工程工具到开源强大工具。
第三章,在本地安装Kubernetes,解释了如何通过实践经验来使用Kubernetes。我们将使用Minikube安装本地单节点Kubernetes集群,并使用命令行客户端与其交互。
第四章,介绍Kubeless函数,解释了在本地运行Kubernetes后如何使用Kubeless启动第一个无服务器函数。
第五章,使用Funktion进行无服务器应用,解释了使用Funktion来调用无服务器函数的一种略有不同的方法。
第六章,在云中安装Kubernetes,介绍了在DigitalOcean、AWS、GoogleCloud和MicrosoftAzure上启动集群的过程,以及在本地使用Kubernetes进行一些实践经验后的操作。
第七章,ApacheOpenWhisk和Kubernetes,解释了如何在我们新推出的云Kubernetes集群上启动、配置和使用最初由IBM开发的无服务器平台ApacheOpenWhisk。
第八章,使用Fission启动应用程序,介绍了部署Fission的过程,这是Kubernetes的流行无服务器框架,并附带了一些示例函数。
第九章,了解OpenFaaS,介绍了OpenFaaS。虽然它首先是一个用于Docker的函数即服务框架,但也可以部署在Kubernetes之上。
第十章,无服务器考虑因素,讨论了安全最佳实践以及如何监视您的Kubernetes集群。
第十一章,运行无服务器工作负载,解释了Kubernetes生态系统的快速发展以及您如何跟上。我们还讨论了您应该使用哪些工具,以及为什么您希望将无服务器函数部署在Kubernetes上。
操作系统:
macOSHighSierra
Ubuntu17.04
Windows10专业版
软件:
在本书中,我们将安装几个命令行工具;每个工具都将在各章节中提供安装说明和其要求的详细信息。请注意,虽然提供了Windows系统的说明,但我们将主要使用最初设计为在Linux/Unix系统上运行的工具,如Ubuntu17.04和macOSHighSierra,并且本书将偏向这些系统。虽然在撰写时已尽最大努力验证这些工具在基于Windows的系统上的运行情况,但由于一些工具是实验性构建的,我们无法保证它们在更新的系统上仍然能够正常工作,因此,我建议使用Linux或Unix系统。
硬件:
Windows10专业版和Ubuntu17.04系统要求:
使用2011年或之后推出的处理器(CPU),核心速度为1.3GHz或更快,除了基于Llano和Bobcat微架构的英特尔Atom处理器或AMD处理器
最低4GBRAM,建议使用8GBRAM或更多
AppleMac系统要求:
iMac:2009年底或更新
MacBook/MacBook(Retina):2009年底或更新
MacBookPro:2010年中期或更新
MacBookAir:2010年末或更新版本
Macmini:2010年中期或更新版本
MacPro:2010年中期或更新版本
至少可以访问以下公共云服务之一:
您可以按照以下步骤下载代码文件:
选择“支持”选项卡。
单击“代码下载和勘误”。
在搜索框中输入书名,然后按照屏幕上的说明操作。
下载文件后,请确保使用最新版本的以下软件解压缩文件夹:
WinRAR/7-ZipforWindows
Zipeg/iZip/UnRarXforMac
7-Zip/PeaZipforLinux
本书中使用了许多文本约定。
CodeInText:指示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入和Twitter用户名。这是一个例子:“这包含一个名为index.html的单个文件。”
一段代码设置如下:
apiVersion:apps/v1beta1kind:Deploymentmetadata:name:cli-hello-worldlabels:app:nginx任何命令行输入或输出都将按照以下方式编写:
$brewcaskinstallminikube粗体:表示一个新术语,一个重要的词,或者你在屏幕上看到的词。例如,菜单或对话框中的单词会在文本中以这种方式出现。这是一个例子:“在页面底部,您将有一个按钮,可以让您为您的帐户创建访问令牌和访问令牌密钥。”
警告或重要提示会以这种方式出现。提示和技巧会以这种方式出现。
欢迎来到《用于无服务器应用的Kubernetes》的第一章。在本章中,我们将讨论以下内容:
我们所说的无服务器和函数作为服务是什么意思?
有哪些服务?
亚马逊网络服务的Lambda的一个例子。
AzureFunctions的一个例子
使用无服务器工具包
我们可以使用无服务器和函数作为服务解决什么问题?
我认为重要的是我们首先要解决房间里的大象,那就是无服务器这个术语。
当你对某人说无服务器时,他们首先得出的结论是你在没有任何服务器的情况下运行你的代码。
如果你使用我们将在本章后面讨论的公共云服务之一,这可能是一个相当合理的结论。然而,当在你自己的环境中运行时,你无法避免必须在某种服务器上运行。
在我们讨论无服务器和函数作为服务的含义之前,我们应该讨论我们是如何到达这里的。和我一起工作的人无疑会告诉你,我经常使用“宠物与牛群”这个类比,因为这是一个很容易解释现代云基础设施与更传统方法之间差异的方式。
我第一次接触“宠物与牛群”这个类比是在2012年,当时RandyBias发布了一份幻灯片。这张幻灯片是在RandyBias在云扩展会议上关于开放和可扩展云的架构的演讲中使用的。在演讲的最后,他介绍了宠物与牛群的概念,Randy将其归因于当时在微软担任工程师的BillBaker。
幻灯片主要讨论的是扩展而不是升级;让我们更详细地讨论一下,并讨论自五年前首次进行演示以来所做的一些补充。
我们像给宠物起名字一样给每台服务器起名字。例如,app-server01.domain.com和database-server01.domain.com。
当我们的宠物生病时,你会把它们带到兽医那里。这很像你作为系统管理员重新启动服务器,检查日志,并更换服务器的故障组件,以确保它正常运行。
牛更能代表你应该在公共云中运行的实例类型,比如亚马逊网络服务(AWS)或微软Azure,在那里你启用了自动扩展。
你的牛群里有很多牛,你不给它们起名字;相反,它们被编号和标记,这样你就可以追踪它们。在你的实例集群中,你也可能有太多实例需要命名,所以像牛一样,你给它们编号和标记。例如,一个实例可以被称为ip123067099123.domain.com,并标记为app-server。
当你的牛群中的一头生病时,你会射杀它,如果你的牛群需要,你会替换它。同样地,如果你集群中的一个实例开始出现问题,它会被自动终止并替换为一个副本。
你的牛群生活在一个牧场里,你从远处观察它,就像你不监视集群中的单个实例一样;相反,你监视集群的整体健康状况。如果你的集群需要额外的资源,你会启动更多的实例,当你不再需要资源时,实例会被自动终止,使你回到期望的状态。
鸡比牛更有效率;你可以把更多的鸡放在你的牛群所占用的同样空间里。同样地,你可以在你的集群中放更多的容器,因为你可以在每个实例上启动多个容器。
每只鸡在饲养时需要的资源比你的牧群成员少。同样,容器比实例需要的资源更少,它们只需要几秒钟就可以启动,并且可以配置为消耗更少的CPU和RAM。
昆虫的寿命远低于鸡;事实上,一些昆虫只有几小时的寿命。这符合无服务器和函数即服务的特点,因为它们的寿命只有几秒钟。
在本章的后面,我们将看一下来自AWS和微软Azure的公共云服务,这些服务的计费是以毫秒为单位,而不是小时或分钟。
每片雪花都是独一无二的,无法复制。就像办公室里那台由几年前离开的那个人建造而没有记录的服务器一样。
一旦我解释了宠物、牛群、鸡、昆虫和雪花,我总结道:
“那些拥有宠物的组织正在慢慢将他们的基础设施变得更像牛。那些已经将他们的基础设施运行为牛的人正在向鸡转变,以充分利用他们的资源。那些运行鸡的人将会考虑将他们的应用程序转变为昆虫,通过将他们的应用程序完全解耦成可单独执行的组件来完成。”
最后我这样说:
“没有人想要或者应该运行雪花。”
在这本书中,我们将讨论昆虫,我会假设你对覆盖牛和鸡的服务和概念有一些了解。
如前所述,使用“无服务器”这个词会给人一种不需要服务器的印象。无服务器是用来描述一种执行模型的术语。
在执行这个模型时,作为最终用户的你不需要担心你的代码在哪台服务器上执行,因为所有的决策都是由抽象出来的,与你无关——这并不意味着你真的不需要任何服务器。
现在有一些公共云服务提供了如此多的服务器管理抽象,以至于可以编写一个不依赖于任何用户部署服务的应用程序,并且云提供商将管理执行代码所需的计算资源。
通常,这些服务,我们将在下一节中看到,是按每秒执行代码所使用的资源计费的。
那么这个解释如何与昆虫类比呢?
假设我有一个网站,允许用户上传照片。一旦照片上传,它们就会被裁剪,创建几种不同的尺寸,用于在网站上显示缩略图和移动优化版本。
在宠物和牛的世界中,这将由一个24/7开机等待用户上传图像的服务器来处理。现在这台服务器可能不只是执行这一个功能;然而,如果几个用户都决定上传十几张照片,那么这将在执行该功能的服务器上引起负载问题。
我们可以采用鸡的方法,跨多台主机运行多个容器来分发负载。然而,这些容器很可能也会全天候运行;它们将会监视上传以进行处理。这种方法可以让我们水平扩展容器的数量来处理请求的激增。
使用昆虫的方法,我们根本不需要运行任何服务。相反,函数应该由上传过程触发。一旦触发,函数将运行,保存处理过的图像,然后终止。作为开发人员,你不需要关心服务是如何被调用或在哪里执行的,只要最终得到处理过的图像即可。
在我们深入探讨本书的核心主题并开始使用Kubernetes之前,我们应该看看其他选择;毕竟,我们将在接下来的章节中涵盖的服务几乎都是基于这些服务的。
三大主要的公共云提供商都提供无服务器服务:
这些服务都支持几种不同的代码框架。对于本书的目的,我们不会过多地研究代码框架,因为使用这些框架是一个基于你的代码的设计决策。
我们将研究这两种服务,AWS的Lambda和微软Azure的Functions。
我们要看的第一个服务是AWS的AWSLambda。该服务的标语非常简单:
“无需考虑服务器即可运行代码。”
现在,那些之前使用过AWS的人可能会认为这个标语听起来很像AWS的弹性Beanstalk服务。该服务会检查你的代码库,然后以高度可扩展和冗余的配置部署它。通常,这是大多数人从宠物到牲畜的第一步,因为它抽象了AWS服务的配置,提供了可扩展性和高可用性。
在我们开始启动一个helloworld示例之前,我们将需要一个AWS账户和其命令行工具安装。
虽然单击“创建免费账户”,然后按照屏幕上的说明将为您提供12个月的免费访问多项服务,但您仍然需要提供信用卡或借记卡详细信息,并且可能会产生费用。
一旦您拥有AWS账户,您应该使用AWS身份和访问管理(IAM)服务创建一个用户。该用户可以拥有管理员权限,您应该使用该用户访问AWS控制台和API。
有关创建IAM用户的更多详细信息,请参阅以下页面:
不建议使用AWS根账户启动服务和访问API;如果凭据落入错误的手中,您可能会失去对账户的所有访问权限。使用IAM而不是您的根账户,并且您还应该使用多因素身份验证锁定根账户,这意味着您将始终可以访问您的AWS账户。
最后一个先决条件是您需要访问AWS命令行客户端,我将使用macOS,但该客户端也适用于Linux和Windows。有关如何安装和配置AWS命令行客户端的信息,请参阅:
在配置AWSCLI时,请确保将默认区域配置为您将在AWSWeb控制台中访问的区域,因为没有比在CLI中运行命令然后在Web控制台中看不到结果更令人困惑的事情了。
安装后,您可以通过运行以下命令来测试您是否可以从命令行客户端访问AWSLambda:
$awslambdalist-functions这应该返回一个空的函数列表,就像下面的截图中所示:
在AWS控制台中,单击屏幕左上角的“服务”菜单,然后通过使用过滤框或单击列表中的服务来选择Lambda。当您首次转到AWS控制台中的Lambda服务页面时,您将看到一个欢迎页面:
单击“创建函数”按钮将直接进入启动我们的第一个无服务器函数的过程。
创建函数有四个步骤;我们需要做的第一件事是选择一个蓝图:
对于基本的helloworld函数,我们将使用一个名为hello-world-python的预构建模板;将其输入到过滤器中,您将看到两个结果,一个是Python2.7,另一个使用Python3.6:
选择hello-world-python,然后单击“导出”将为您提供下载用于函数的代码的选项,该代码位于lambda_function.py文件中,以及Lambda在第3步中使用的模板。这可以在template.yaml文件中找到。
代码本身非常基本,就像你想象的那样。它除了返回传递给它的值之外什么也不做。如果您没有跟随,lambda_function.py文件的内容如下:
from__future__importprint_functionimportjsonprint('Loadingfunction')deflambda_handler(event,context):#print("Receivedevent:"+json.dumps(event,indent=2))print("value1="+event['key1'])print("value2="+event['key2'])print("value3="+event['key3'])returnevent['key1']#Echobackthefirstkeyvalue#raiseException('Somethingwentwrong')template.yaml文件包含以下内容:
AWSTemplateFormatVersion:'2010-09-09'Transform:'AWS::Serverless-2016-10-31'Description:AstarterAWSLambdafunction.Resources:helloworldpython:Type:'AWS::Serverless::Function'Properties:Handler:lambda_function.lambda_handlerRuntime:python2.7CodeUri:.Description:AstarterAWSLambdafunction.MemorySize:128Timeout:3Role:!
要继续到第2步,请单击函数名称,对我们来说是hello-world-python,您将进入页面,可以选择如何触发函数:
我们暂时不打算使用触发器,我们将在下一个启动的函数中更详细地了解这些内容;所以现在,请单击“下一步”。
第3步是我们配置函数的地方。这里有很多信息要输入,但幸运的是,我们需要输入的许多细节已经从我们之前查看的模板中预填充,如下截图所示:
以下列表显示了所有表单字段及其应输入的内容:
基本信息:
名称:myFirstFunction
描述:一个起始的AWSLambda函数
运行时:Python2.7
Lambda函数代码:
代码输入类型:这包含了函数的代码,无需编辑
启用加密助手:不选中
环境变量:留空
Lambda函数处理程序和角色:
处理程序:lambda_function.lambda_handler
角色:保持选择“从模板创建新角色”
角色名称:myFirstFunctionRole
策略模板:我们不需要为此函数使用策略模板,保持空白
将标签和高级设置保持默认值。输入前述信息后,单击“下一步”按钮,进入第4步,这是函数创建之前的最后一步。
查看页面上的详细信息。如果您确认所有信息都已正确输入,请单击页面底部的“创建函数”按钮;如果需要更改任何信息,请单击“上一步”按钮。
几秒钟后,您将收到一条消息,确认您的函数已创建:
在上述截图中,有一个“测试”按钮。单击此按钮将允许您调用函数。在这里,您可以自定义发送到函数的值。如下截图所示,我已更改了key1和key2的值:
编辑完输入后,点击“保存并测试”将存储您更新的输入,然后调用该函数:
点击执行结果消息中的“详细信息”将显示函数被调用的结果以及使用的资源:
STARTRequestId:36b2103a-90bc-11e7-a32a-171ef5562e33Version:$LATESTvalue1=helloworldvalue2=thisismyfirstserverlessfunctionvalue3=value3ENDRequestId:36b2103a-90bc-11e7-a32a-171ef5562e33具有36b2103a-90bc-11e7-a32a-171ef5562e33ID的请求的报告如下:
内存大小:128MB
最大内存使用:19MB
返回到命令行,再次运行以下命令会显示我们的函数现在已列出:
$awslambdalist-functions上述命令的输出如下:
我们也可以通过运行以下命令从命令行调用我们的函数:
$awslambdainvoke\--invocation-typeRequestResponse\--function-namemyFirstFunction\--log-typeTail\--payload'{"key1":"hello","key2":"world","key3":"again"}'\outputfile.txt如您从上述命令中所见,awslambdainvoke命令需要几个标志:
--invocation-type:有三种调用类型:
RequestResponse:这是默认选项;它发送请求,在我们的情况下在命令的--payload部分中定义。一旦请求被发出,客户端就会等待响应。
事件:这会发送请求并触发事件。客户端不等待响应,而是会收到一个事件ID。
DryRun:这会调用函数,但实际上不执行它——这在测试用于调用函数的详细信息是否具有正确的权限时非常有用。
--function-name:这是我们要调用的函数的名称。
--log-type:目前只有一个选项,Tail。这返回--payload的结果,这是我们要发送给函数的数据;通常这将是JSON。
outputfile.txt:命令的最后部分定义了我们要存储命令输出的位置;在我们的情况下,这是一个名为outputfile.txt的文件,它被存储在当前工作目录中。
在从命令行调用命令时,您应该会得到以下结果:
返回到AWS控制台并保持在myFirstFunction页面上,点击“监控”将呈现有关函数的一些基本统计信息:
单击CloudWatch中的查看日志将打开一个列出myFirstFunction日志流的新标签页。单击日志流的名称将带您到一个页面,该页面会显示每次函数被调用的结果,包括在AWS控制台中进行测试以及从命令行客户端进行调用。
监控页面和日志在调试Lambda函数时非常有用。
接下来,我们将看一下微软的无服务器服务AzureFunctions。微软将这项服务描述为:
"AzureFunctions是一个解决方案,可以轻松在云中运行小段代码或“函数”。您可以仅编写您需要解决的问题的代码,而不必担心整个应用程序或运行它的基础架构。"
与Lambda一样,您的Function可以通过多种方式被调用。在这个快速演示中,我们将部署一个通过HTTP请求调用的Function。
在撰写本文时,微软向所有新账户提供了200美元的Azure服务信用额度,就像AWS一样,有几项服务有免费套餐。
我们将使用基于Web的控制面板来创建我们的第一个Function应用程序。一旦您拥有了账户,您应该会看到类似以下页面:
如前面的屏幕截图所示,有相当多的选项。要开始创建您的第一个函数,您应该在左侧菜单顶部单击“+新建”。
从这里,您将进入Azure市场。单击计算,然后在特色市场项目列表中,您应该看到函数应用程序。单击此处,您将进入一个表单,询问您想要创建的函数的一些基本信息:
应用程序名称:随意命名;在我的案例中,我将其命名为russ-test-version。这必须是一个唯一的名称,如果您想要的应用程序名称已经被另一个用户使用,您将收到一条消息,告知您所选的应用程序名称不可用。
订阅:选择要在其中启动您的函数的Azure订阅。
资源组:在输入应用程序名称时,这将自动填充。
托管计划:将其保留为默认选项。
位置:选择离您最近的地区。
存储:这将根据您提供的应用程序名称自动填充,为了我们的目的,请保留选择“创建新”。
固定到仪表板:选中此项,因为这将使我们能够快速找到我们创建的函数。
如果您没有在您的帐户中跟随,我的完成表格看起来像下面的屏幕截图:
填写完表格后,单击表单底部的“创建”按钮,您将被带回到您的仪表板。您将收到一个通知,告知您的函数正在部署,如下图右侧的框中所示:
单击仪表板中的方框或顶部菜单中的通知(带有数字1的铃铛图标)将带您到概述页面;在这里,您可以查看部署的状态:
部署后,您应该有一个空的函数应用程序,可以准备将代码部署到其中:
要部署一些测试代码,您需要在左侧菜单中的函数旁边单击“+”图标;这将带您到以下页面:
选择Webhook+API和CSharp后,单击“创建此函数”;这将向您的函数应用程序添加以下代码:
usingSystem.Net;publicstaticasyncTask
我们可以通过单击页面顶部的“运行”按钮来测试这一点。这将执行我们的函数,并为您提供输出和日志:
测试运行的日志如下:
能够在Azure仪表板的安全环境中进行测试是很好的,但是如何直接访问您的函数应用呢?
如果单击HttpTriggerCSharp1,它将带您回到您的代码,在代码块上方,您将有一个按钮,上面写着“获取函数URL”,单击此按钮将弹出一个包含URL的覆盖框。复制这个:
对我来说,URL是:
前面的URL将不再起作用,因为函数已被移除;它仅用于说明目的,您应该用您的URL替换它。
使用以下命令在命令行上调用该URL:
从返回的内容中可以看出,我们的函数应用返回了HttpStatusCodeBadRequest消息。这是因为我们没有传递name变量。为了做到这一点,我们需要更新我们的命令为:
您还可以在浏览器中输入URL并查看消息:
从主页上可以看到,它支持AWS和MicrosoftAzure,以及GoogleCloud平台和IBMOpenWhisk。您还会注意到有一个注册按钮;单击此按钮并按照屏幕提示创建您的帐户。
注册后,您将收到一些非常简单的关于如何安装工具和部署第一个应用程序的说明;让我们现在遵循这些。首先,我们需要通过运行来安装命令行工具:
$npminstallserverless-g安装将需要几分钟,一旦安装完成,您应该能够运行:
$serverlessversion这将确认上一个命令安装的版本:
要在AWS中启动我们的hello-world函数,我们必须首先创建一个文件夹来保存无服务器工具包创建的工件,并切换到该文件夹;我在我的“桌面”上创建了一个文件夹,使用:
$mkdir~/Desktop/Serverless$cd~/Desktop/Serverless要生成启动我们的hello-world应用程序所需的文件,我们需要运行:
$serverlesscreate--templatehello-world这将返回以下消息:
在我的编辑器中打开serverless.yml,我可以看到以下内容(我已删除了注释):
$serverlessdeploy您可能已经猜到,这将hello-world应用程序部署到了AWS:
使用HTTPie访问终端URL:
转到serverlessdeploy命令末尾提到的URL,可以概览您使用serverless部署到Lambda的函数:
此时,您可能会想,“我的帐户是如何启动的?我没有提供任何凭据!”无服务器工具旨在使用与我们在启动第一个Lambda函数之前安装的AWSCLI相同的凭据-这些凭据可以在您的计算机上的~/.aws/credentials找到。
要删除函数,只需运行:
$serverlessremove这将删除无服务器工具包在您的AWS帐户中创建的所有内容。
尽管到目前为止我们只启动了最基本的应用程序,但我希望您开始看到使用无服务器如何有助于开发您的应用程序。
由于无服务器函数被执行然后立即终止,你不应该担心它在哪里或者如何执行,只要它执行了。这意味着你的应用程序理论上应该是可伸缩的,也比传统的基于服务器的应用程序更容错。
例如,如果在调用你的一个函数时出现问题,例如,如果它崩溃了或者有资源问题,并且你知道下次调用函数时它将被重新启动,你不需要担心你的代码在有问题的服务器上执行。
在本章中,我们快速了解了什么是无服务器,并在AWS和MicrosoftAzure中启动和交互了无服务器函数,还使用了一个名为无服务器的第三方工具,在AWS中创建了一个无服务器函数。
到目前为止,你可能已经注意到我们还没有提到Kubernetes,对于一本名为用于无服务器应用程序的Kubernetes的书来说,这可能有点奇怪。不过不用担心,在下一章中我们将更详细地了解Kubernetes,一切将变得清晰起来。
正如前一章末尾提到的,本章将讨论Kubernetes。我们将讨论:
Kubernetes的简要历史-它从哪里来?
它是如何运作的?
Kubernetes的用例是什么,谁在使用它?
为什么要在服务器上运行无服务器?
“用于自动化部署、扩展和管理容器化应用的开源系统。”
该项目起源于谷歌内部的一个名为Borg的项目。在Docker引起轰动之前,谷歌长期使用容器技术。
谷歌自己的容器之旅始于2006年,当时他们的两名工程师开始了控制组(cgroups)项目。这是Linux内核的一个功能,可以隔离诸如RAM、CPU、网络和磁盘I/O等资源,以供一组进程使用。cgroups最初是在2007年发布的,在2008年初,该功能被合并到Linux内核主线版本2.6.24中。
几年后的2013年10月,谷歌发布了他们自己的容器系统的开源版本,名为lmctfy,实际上是LetMeContainThatForYou的缩写。这个工具实际上是他们在自己的服务器上使用的,用于运行Linux应用容器,它被设计为LXC的替代品。
这就是Borg项目的由来。谷歌大量使用容器,我说的是大量。2014年5月,谷歌的JoeBeda在Gluecon上做了一个名为大规模容器的演讲。演讲中有一些引人注目的引用,比如:
“谷歌所有的东西都在容器中运行。”
而最常谈论的一个是:
“我们每周启动超过20亿个容器。”
虽然Joe详细介绍了谷歌当时如何使用容器,但他并没有直接提到Borg项目;相反,它只是被称为一个集群调度器。
命令式:在那台服务器上启动这个容器
这个概念解释了谷歌是如何能够每周启动超过20亿个容器,而不必真正管理超过20亿个容器。
直到2015年谷歌发表了一篇名为《谷歌Borg的大规模集群管理》的论文,我们才真正了解到了JoeBeda在前一年提到的集群调度器的实践和设计决策。
论文讨论了谷歌内部的工具Borg是如何运行成千上万的作业的,这些作业几乎构成了谷歌所有应用程序的集群,这些集群由成千上万台机器组成。
我建议阅读这篇论文,因为它很好地概述了谷歌是如何处理自己的容器服务的。
另外,如果你在想,Borg是以《星际迷航:下一代》电视剧中的外星种族命名的。
2014年,JoeBeda、BrendanBurns和CraigMcLuckie加入了BrianGrant和TimHockin参与了第七号项目。
这个项目以《星际迷航》中的角色“第七号九”命名,旨在制作一个更友好的Borg版本。在第一次提交时,该项目已经有了一个外部名称,即Kubernetes。
然而,到了2015年7月的1.0版本发布时,谷歌已经意识到它已经远远超出了这个范畴,他们加入了Linux基金会、Twitter、英特尔、Docker和VMware(举几个例子),共同组建了云原生计算基金会。作为这一新合作的一部分,谷歌将Kubernetes项目捐赠为新组织的基础。
此后,其他项目也加入了Kubernetes,比如:
除此之外,像AWS、微软、红帽和甲骨文这样的新成员都在支持和为基金会的项目提供资源。
现在我们对Kubernetes的起源有了一个概念,我们应该逐步了解构成典型Kubernetes集群的所有不同组件。
Kubernetes本身是用Go编写的。虽然项目的GitHub页面显示该项目目前84.9%是Go,其余的5.8%是HTML,4.7%是Python,3.3%是Shell(其余是配置/规范文件等),都是文档和辅助脚本。
Kubernetes有两个主要的服务器角色:主服务器和节点;每个角色都由多个组件组成。
主服务器是集群的大脑,它们决定pod(在下一节中更多介绍)在集群内部部署的位置,并且对集群的健康状况以及pod本身的健康状况进行操作和查看。
主服务器的核心组件包括:
kube-apiserver:这是您的Kubernetes控制面板的前端;无论您使用什么来管理您的集群,它都将直接与此API服务通信。
etcd:etcd是Kubernetes用来存储集群状态的分布式键值存储。
kube-controller-manager:此服务在后台工作,以维护您的集群。它查找加入和离开集群的节点,确保正在运行正确数量的pod,并且它们健康等等。
cloud-controller-manager:这项服务是Kubernetes的新功能。它与kube-controller-manager一起工作,其目的是与AWS、GoogleCloud和MicrosoftAzure等云提供商的API进行交互。它执行的任务示例可能是,如果要从集群中删除一个节点,它将检查您的云服务API,看看节点是否仍然存在。如果存在,则可能会出现问题;如果不存在,则很可能是因为缩放事件而删除了节点。
kube-scheduler:根据一系列规则、利用率和可用性选择pod应该在哪里启动。
接下来我们有节点。一旦部署,主节点与安装在节点上的组件进行交互,以在集群内实现变化;这些是您的pod运行的地方。
组成节点的组件有:
kubelet:这是在节点上运行的主要组件。它负责接受来自主服务器的指令并报告回去。
kube-proxy:这项服务有助于集群通信。它充当节点上所有网络流量的基本代理,并能够配置TCP/UDP转发或充当TCP/UDP轮询负载均衡器到多个后端。
docker或rkt:这些是节点上实际的容器引擎。kubelet服务与它们交互,以启动和管理运行在集群节点上的容器。在接下来的章节中,我们将看到运行这两种节点的示例。
supervisord:这个进程管理器和监视器维护着节点上其他服务的可用性,比如kubelet、docker和rkt。
fluentd:这项服务有助于集群级别的日志记录。
你可能已经注意到,这些服务中唯一提到容器的是docker和rkt。Kubernetes实际上并不直接与您的容器交互;相反,它与一个pod通信。
正如前面提到的,Kubernetes不部署容器;相反,它启动pod。在其最简单的形式中,一个pod实际上可以是一个单一的容器;然而,通常一个pod由多个容器、存储和网络组成。
以下内容仅供说明,不是一个实际的例子;我们将在下一章中通过一个实际的例子来进行讲解。
把一个pod想象成一个完整的应用程序;例如,如果你运行一个简单的web应用程序,它可能只运行一个NGINX容器——这个pod的定义文件将看起来像下面这样:
apiVersion:v1kind:Podmetadata:name:nginxspec:containers:-name:nginximage:nginx:latestports:-containerPort:8080正如你所看到的,我们提供了关于我们的pod的一些简单元数据,这种情况下只是名称,这样我们就可以识别它。然后我们定义了一个单一的容器,它正在运行来自Dockerhub的最新NGINX镜像,并且端口8080是开放的。
就目前而言,这个pod是相当无用的,因为我们只打算显示一个欢迎页面。接下来,我们需要添加一个卷来存储我们网站的数据。为了做到这一点,我们的pod定义文件将如下所示:
apiVersion:v1kind:Podmetadata:name:nginxspec:containers:-name:nginximage:nginx:latestvolumeMounts:-mountPath:/srv/wwwname:web-datareadOnly:trueports:-containerPort:8080volumes:-name:web-dataemptyDir:{}正如你所看到的,我们现在正在创建一个名为web-data的卷,并将其以只读方式挂载到/srv/www,这是我们NGINX容器上的默认网站根目录。但这还是有点毫无意义,因为我们的卷是空的,这意味着我们的所有访问者将只看到一个404页面。
让我们添加第二个容器,它将从AmazonS3存储桶同步我们网站的HTML:
到目前为止,你可能会想到自己;我们有一个从AmazonS3存储桶部署的网站服务的pod,这一切都是真实的。然而,我们还没有完全完成。我们有一个正在运行的pod,但我们需要将该pod暴露给网络,以便在浏览器中访问它。
为了做到这一点,我们需要启动一个服务。对于我们的示例,服务文件看起来可能是这样的:
apiVersion:v1kind:Servicemetadata:name:nginx-servicespec:selector:app:nginxports:-protocol:TCPport:80targetPort:8080正如你所看到的,服务定义看起来与pod的定义类似。我们使用元数据部分设置名称。然后我们选择我们的NGINXpod,并将端口80映射到端口8080,这是我们的pod正在侦听的端口。
如前所述,当我们启动第一个Kubernetes集群时,我们将在下一章更详细地讨论这个问题,但现在,这应该让你对Kubernetes的运作方式有一个很好的了解。
在上一节中,我们看了pod和服务。虽然这些可以手动启动,但你也可以使用控制器来管理你的pod。这些控制器允许执行不同类型的工作负载。我们将快速看一下不同类型的控制器,还讨论何时使用它们。
ReplicaSet可用于启动和维护相同pod的多个副本。例如,使用我们在上一节中讨论的NGINXpod,我们可以创建一个ReplicaSet,启动三个相同pod的副本。然后可以在这三个pod之间进行负载均衡。
我们的三个pod可以分布在多个主机上,这意味着,如果一个主机因任何原因消失,将导致我们的一个pod停止服务,它将自动在健康节点上被替换。你还可以使用ReplicaSet来自动和手动地添加和删除pod。
您可能会认为使用ReplicaSet可以进行滚动升级和回滚。不幸的是,ReplicaSets只能复制相同版本的pod;幸运的是,这就是部署的用武之地。
部署控制器旨在更新ReplicaSet或pod。让我们以NGINX为例。正如您从以下定义中所看到的,我们有3个副本都在运行NGINX版本1.9.14:
apiVersion:apps/v1beta1kind:Deploymentmetadata:name:nginx-deploymentspec:replicas:3template:metadata:labels:app:nginxspec:containers:-name:nginximage:nginx:1.9.14ports:-containerPort:80kubectl是Kubernetes的命令行客户端;我们将在下一章中更详细地讨论这个问题。
我们可以使用以下命令进行部署:
$kubectlcreate-fnginx-deployment.yaml现在假设我们想要更新使用的NGINX图像的版本。我们只需要运行以下命令:
$kubectlsetimagedeployment/nginx-deploymentnginx=nginx:1.13.5deployment"nginx-deployment"imageupdated这将逐个更新每个pod,直到所有的pod都运行新版本的NGINX。
这个控制器是Kubernetes中的新功能,旨在取代PetSets。正如您可能从名称中猜到的那样,pod作为部署的一部分维护其状态。它们被设计为具有:
在整个pod生命周期中保持一致的唯一网络标识符
持久存储
按照您定义的顺序执行的优雅部署和扩展
用户定义和控制的自动滚动更新
因此,虽然名称有所变化,但您应该将StatefulSets视为宠物,将ReplicaSets视为牲畜。
正如我们在本章中已经提到的,Kubernetes几乎可以在任何地方运行,从您的本地机器(我们将在下一章中介绍),到您的本地硬件或虚拟机基础设施,甚至可能跨越AWS、MicrosoftAzure或GoogleCloud的数百个公共云实例。事实上,您甚至可以在Kubernetes集群中跨多个环境。
这意味着无论您在何处运行应用程序,都会获得一致的体验,但也可以利用底层平台的功能,比如负载平衡、持久存储和自动扩展,而无需真正设计应用程序以意识到它是在运行,比如在AWS或MicrosoftAzure上。
阅读成功案例时你会注意到的一个共同点是,人们谈论的是不被锁定在一个特定的供应商上。由于Kubernetes是开源的,他们不会被任何许可成本所限制。如果他们遇到问题或想要添加功能,他们可以直接深入源代码进行更改;他们也可以通过拉取请求将他们所做的任何更改贡献回项目中。
另外,正如前面讨论的,使用Kubernetes使他们不会被锁定在任何一个特定的平台供应商或架构上。这是因为可以合理地假设Kubernetes在安装在其他平台时会以完全相同的方式运行。因此,突然之间,您可以相对轻松地将您的应用程序在不同提供商之间移动。
另一个常见的用例是运维团队将Kubernetes用作基础设施即服务(IaaS)平台。这使他们能够通过API、Web和CLI向开发人员提供资源,这意味着他们可以轻松地融入自己的工作流程。它还为本地开发提供了一个一致的环境,从暂存或用户验收测试(UAT)到最终在生产环境中运行他们的应用程序。
这也是为什么使用Kubernetes执行无服务器工作负载是一个好主意的部分原因。您不会被任何一个提供商锁定,比如AWS或MicrosoftAzure。事实上,您应该把Kubernetes看作是一个云平台,就像我们在第一章中看到的那些;它有一个基于Web的控制台,一个API和一个命令行客户端。
关于Kubernetes的几个案例研究,用户详细介绍了他们在使用Kubernetes过程中的经历:
还有来自以下内容的讨论、采访和演示:
在这一章中,我们谈到了Kubernetes的起源,并介绍了一些其使用案例。我们还了解了一些基本功能。
在下一章中,我们将通过在本地安装Minikube来亲自体验Kubernetes。一旦我们安装好了本地的Kubernetes,我们就可以继续进行第四章,“介绍Kubeless功能”,在那里我们将开始在Kubernetes上部署我们的第一个无服务器函数。
在本章中,我们将看看如何使用Minikube快速搭建本地的Kubernetes安装。一旦我们的本地Kubernetes安装运行起来,我们将学习一些基本功能,并讨论在本地运行Kubernetes的局限性。我们将学习在以下平台上安装Kubernetes:
macOS10.13HighSierra
在我们开始安装之前,让我们快速看一下我们将使用的工具来部署我们的本地Kubernetes集群。
当你阅读上一章时,你可能会想到Kubernetes看起来很复杂。有很多组件需要配置,而且不仅需要配置,还需要监控和管理。
我记得当我最初看Kubernetes时,它刚发布不久,安装说明非常长,而且事情有点儿棘手。
在安装过程的开始阶段误读了一步,你可能会在安装过程的后期陷入麻烦——这让我想起了以前杂志上会包含游戏代码清单的情形。如果你在任何地方打错字,那么事情要么根本不起作用,要么会出现意外崩溃。
随着Kubernetes的成熟,安装过程也在不断改进。相当快地,一些辅助脚本被开发出来,以帮助在各种平台上启动Kubernetes;Minikube就是其中之一。
它的工作就是创建一个本地的Kubernetes节点。考虑到Kubernetes支持的功能范围,它有令人惊讶的多种功能,比如:
DNS,NodePorts和Ingress
ConfigMaps和Secrets
容器运行时的选择;你可以使用Docker或rkt
通过hostPath持久卷
仪表板
通常需要公共云提供商(如AWS,MicrosoftAzure或GoogleCloud)或多个主机的Kubernetes功能是不受支持的。其中一些功能包括:
负载均衡器
高级调度策略
这是因为Minikube只在本地PC上的虚拟机上启动单个节点。但这不应该限制你;请记住,你只会想要在Minikube上进行开发,并且不应该使用它构建生产服务。还有很多其他工具,将在第六章中介绍,在云中安装Kubernetes,更适合在公共云或其他供应商中启动生产就绪的Kubernetes集群。
Minikube由两个核心组件组成:
libmachine:这个来自Docker的库用于在主机上提供虚拟机。它是DockerMachine以及DockerformacOS和DockerforWindows的核心组件。
localkube:这个库是由Redspread(现在是CoreOS的一部分)开发并捐赠给Minikube项目的,它负责在启动虚拟机后部署和维护Kubernetes节点。
不再讨论Minikube能做什么,我们应该看看如何安装它,然后讨论如何与它交互。
我们将看看如何在介绍中提到的三种不同操作系统上安装Minikube。一旦安装完成,与Minikube交互的过程大部分是一致的,这意味着,虽然我在示例中使用的是macOS,但相同的命令也适用于Windows和Linux。考虑到早期Kubernetes安装和配置过程的复杂性,你会惊讶地发现现在的过程是多么简单。
要在macOS上安装Minikube,你首先必须安装Homebrew和Cask。
Homebrew是macOS的基于命令行的软件包管理器。Homebrew用于安装命令行工具和Cask,Cask是一个用于管理桌面应用程序的附加组件。它非常有用,可以管理macOS应用商店中不可用的软件,同时也可以避免你在自己的机器上手动编译软件。
如果你还没有安装Homebrew,你可以通过运行以下命令来安装它:
$brewinstallcask如果你已经安装了Homebrew和Cask,那么你应该确保一切都是最新的,并且准备好使用以下命令运行:
$brewupdate$brewdoctor一旦Homebrew和Cask准备好,你可以通过运行以下命令来安装Minikube:
$brewcaskinstallminikube首先会下载依赖项,然后安装Minikube:
该过程不到一分钟,安装完成后,您应该能够执行以下操作:
$minikubeversion这将显示当前版本;在我的情况下,这是v0.22.2。我们现在已经安装并准备好使用Minikube了。
与我们在macOS上安装Minikube的方式类似,我们将使用一个包管理器;这次叫做Chocolatey。
Chocolatey是Windows的一个包管理器,类似于macOS上的Homebrew。它使您能够从命令行安装软件,并支持PowerShell和cmd.exe。我们将使用PowerShell。
如果您没有安装Chocolatey,可以在以管理员权限启动的PowerShell控制台中运行以下命令:
以下命令是一行,而不是多行。另外,由于我们使用Set-ExecutionPolicyBypass来运行安装命令,您将被询问是否确定。由于我们直接从Chocolatey网站通过HTTPS运行脚本,您应该能够信任该脚本并回答是。
$chocoinstallminikube这将下载并安装依赖项,然后安装Minikube。当您被要求确认是否要运行脚本时,请回答是:
安装后,您将能够运行以下命令:
$minikubeversion这将返回安装的Minikube版本;对我来说,这是v0.22.2。
与macOS和Windows版本不同,我们将不会使用包管理器在Ubuntu17.04上安装Minikube。相反,我们将直接从项目页面下载二进制文件。要做到这一点,只需运行以下命令:
现在Minikube已安装,我们需要下载kubectl。在macOS和Windows安装过程中,这是由包管理器处理的;幸运的是,这个过程与我们刚刚运行以安装Minikube的命令几乎相同:
$minikubeversion当我运行该命令时,它返回v0.22.2,如下截图所示:
Minikube支持多种不同的hypervisors。Hypervisor是一个用于启动虚拟机的进程;它将虚拟机的操作系统与您自己的操作系统隔离开来,同时允许它共享CPU、RAM和磁盘空间等资源。
Minikube默认支持以下hypervisors:
Hyper-V(Windows10):这是本机hypervisor;它适用于Windows10专业版和Windows服务器
KVM(Ubuntu17.04):这是本机Linuxhypervisor,在大多数现代发行版的Linux内核中运行
VirtualBox(macOS,Windows10和Ubuntu17.04):由Oracle发布,VirtualBox是一个开源的x86hypervisor,可以在大量操作系统上运行
VMwareFusion(macOS):Fusion提供了一个经过优化的macOShypervisor,其最大优势是能够在macOS上运行和公开Windows应用程序
xhyve(macOS):这是macOS上的本机hypervisor;就像Linux上的KVM一样,它内置在内核中
对于macOS,我们可以使用Homebrew和Cask来安装VirtualBox:
$brewcaskinstallvirtualbox同样,对于Windows10,您可以使用Chocolatey来安装VirtualBox:
如果启用了Hyper-V,则无法在Windows10上使用VirtualBox。如果您希望跟随操作,请在继续之前禁用Hyper-V。
$chocoinstallvirtualbox最后,对于Ubuntu17.04,您需要运行以下命令来添加存储库和密钥:
$sudoapt-getupdate$sudoapt-getinstallvirtualbox-5.1现在您应该能够在列出的软件程序中看到Virtualbox。
要完成我们的安装,我们需要启动Minikube。要做到这一点,请运行以下命令:
$minikubestart在macOS上,您应该看到类似于这样的东西:
如您所见,用于创建虚拟机的ISO已经下载。虚拟机启动,我们将用于对我们的单节点集群进行身份验证的证书被生成,最后kubectl被配置为使用我们本地Kubernetes集群的详细信息。
在Windows10上运行相同的命令将得到完全相同的步骤:
另外,正如您可能已经猜到的那样,在Ubuntu17.04上运行会得到相同的结果。运行以下命令:
$minikubestatus您将收到一条消息,确认一切正常运行,并且kubectl已正确配置以与您的Kubernetes集群通信:
如果您打开VirtualBox,您应该会看到您的Minikube虚拟机正在运行;例如,当我在Windows10上打开VirtualBox时就是这种情况:
尽管我们在三种不同的操作系统上启动了Minikube,除了初始安装之外,您已经可以体验我们在第二章中讨论的内容了:没有供应商锁定和一致的体验,而且这是在我们开始使用新安装的Kubernetes集群之前。
到目前为止,我们已经使用了minikubestart和minikubestatus命令来启动我们的单节点Kubernetes集群,并检查一切是否按预期运行。在我们开始与Kubernetes交互之前,我想介绍一些更基本的Minikube命令。
由于我们将我们的单节点Kubernetes集群作为虚拟机在您的主机上运行,您可能不希望它一直运行,占用资源。
有两种选项可以实现这一点,第一种是minikubestop。这个命令将停止您的节点,并保持虚拟机完整。如果您计划在下次通过运行minikubestart启动节点时继续之前的工作,您应该使用这个命令。
虽然minikubestop命令会停止您的虚拟机在主机上使用CPU和RAM资源,但用于托管虚拟机的硬盘映像仍将存在于您的机器上。虽然新启动的集群不会占用主机硬盘上太多空间,在我的macOS安装中大约为650MB;一旦您开始使用集群,您可能会发现这个空间至少会翻倍。
这就是我们下一个命令发挥作用的地方。minikubedelete命令将完全删除集群,包括所有虚拟机文件,释放主机机器上使用的空间。
在写作时,运行minikubedelete将立即删除您的虚拟机,无论其是否正在运行。不会有提示询问您是否确定,也没有从该命令返回的方法(除非您有备份),因此请确保谨慎使用此命令。
当您再次运行minikubestart时,您的集群将从头开始启动,就像我们在上一节中首次体验到的那样。
接下来,我们有一些命令,显示有关虚拟机的信息,以及Minikube在您的设备上配置的环境。
首先,我们有一个非常简单的命令minikubeip。这个命令只是返回虚拟机的IP地址。如果您想通过脚本与集群交互,这将非常有用。您可以包含命令的输出,以引用集群的当前IP地址,而无需在脚本中硬编码实际的IP地址。
我们要看的下一个命令是minikubedocker-env。运行此命令应该会在屏幕上打印出类似以下输出:
$minikubedocker-envexportDOCKER_TLS_VERIFY="1"exportDOCKER_HOST="tcp://192.168.99.101:2376"exportDOCKER_CERT_PATH="/Users/russ/.minikube/certs"exportDOCKER_API_VERSION="1.23"#Runthiscommandtoconfigureyourshell:#eval$(minikubedocker-env)输出的作用是允许您(如果已安装)配置本地Docker客户端与Minikube虚拟机上的Docker安装进行通信。然而,这样做也有一个缺点。目前作为Minikube虚拟机镜像的一部分分发的Docker版本略落后于当前版本。您可以通过运行eval$(minikubedocker-env),然后dockerversion来查看这一点。当我运行这两个命令时,得到了以下结果:
$eval$(minikubedocker-env)$dockerversionClient:Version:17.06.2-ceAPIversion:1.23Goversion:go1.8.3Gitcommit:cec0b72Built:TueSep520:12:062017OS/Arch:darwin/amd64Server:Version:1.12.6APIversion:1.24(minimumversion)Goversion:go1.6.4Gitcommit:78d1802Built:WedJan1100:23:162017OS/Arch:linux/amd64Experimental:false从输出中可以看出,写作时Minikube使用的Docker版本比我在macOS上安装的最新稳定版本要落后两个版本。在本书涵盖的内容范围内,运行旧版本的Docker并不是问题,也不需要担心,因为我们不会直接与其交互。
$sshdocker@$(minikubeip)-i$(minikubessh-key)这将动态生成虚拟机的IP地址和私钥路径:
我们要快速查看的最后一个命令是minikubelogs。这会显示localkube实例生成的所有日志:
这些日志用于帮助调试您的Minikube安装中的问题。它们不包含任何用户数据,这意味着您不能使用它们来帮助跟踪您启动的服务或pod的任何问题。
现在我们的单节点Kubernetes集群已经运行起来了,使用Minikube,我们可以尝试启动一个服务。我们将首先使用仪表板,然后再转向命令行客户端。
每个Minikube安装都带有一个基于Web的仪表板。这可以通过运行minikubedashboard来访问,它会立即在您的默认浏览器中打开仪表板:
点击页面左上角的+创建按钮,将带您到一个表单,让您部署一个容器化应用程序。
在部署容器化应用页面上,您会找到几个选项。保持启用下面的指定应用程序详细信息选项,填写如下:
应用名称:dashboard-hello-world
容器镜像:nginx:latest
Pod数量:1
服务:外部
端口:8080
目标端口:80
协议:TCP
对于我们的目的,我们不需要填写在“显示高级选项”下找到的任何选项。只需点击表单底部的“部署”按钮。过一会儿,您的仪表板应该显示您有一个部署、pod、ReplicaSet和服务,所有这些都带有dashboard-hello-world的名称:
您可以通过运行以下命令查看服务:
$minikubeservicedashboard-hello-world这将返回以下消息:
Openingkubernetesservicedefault/dashboard-hello-worldindefaultbrowser...打开您的浏览器,在那里您应该看到默认的NGINX页面:
虽然这只是一个非常基本的例子,但它确实展示了使用仪表板启动简单应用程序有多简单。现在让我们看看如何转移到命令行。
在上一章中,我们简要介绍了如何使用YAML或JSON文件来定义您的pod、ReplicaSets和服务。让我们使用kubectl来启动一个与前一个应用程序相同的应用程序。
首先,我们需要一个要启动的文件;您可以在本书的代码包和GitHub存储库的Chapter03文件夹中找到名为cli-hello-world.yml的副本:
apiVersion:v1kind:Servicemetadata:name:cli-hello-worldspec:selector:app:cli-hello-worldtype:NodePortports:-protocol:TCPport:8000targetPort:80---apiVersion:apps/v1beta1kind:Deploymentmetadata:name:cli-hello-worldlabels:app:nginxspec:replicas:1selector:matchLabels:app:cli-hello-worldtemplate:metadata:labels:app:cli-hello-worldspec:containers:-name:nginximage:nginx:latestports:-containerPort:80您可能已经注意到,虽然这是一个单独的文件,但实际上我们有两个不同的部分。第一个启动外部服务,在端口8000上公开它,以便与我们在上一节使用仪表板启动的外部服务不发生冲突。第二部分定义了pod和复制集;这与我们使用仪表板启动的内容非常相似。
要启动应用程序,我们只需要运行以下命令:
$kubectlapply-fcli-hello-world.yml您几乎立即会收到已创建服务和部署的确认:
service"cli-hello-world"createddeployment"cli-hello-world"created创建后,您应该能够运行以下命令在浏览器中打开应用程序:
$minikubeservicecli-hello-world再次,您应该会看到默认的NGINX页面。
我相信当我们打开仪表板时,您点击了页面左侧可以找到的菜单项。所有这些信息也可以在命令行中找到,所以让我们简要地看一下我们可以使用的一些命令来了解有关我们集群的更多信息。
您将要运行的更常见的命令之一是kubectlget。这将获取pod、ReplicaSets和服务的列表,以及更多内容。运行以下命令应该给我们一个类似于仪表板概述的视图:
$kubectlgetpods$kubectlgetreplicasets$kubectlgetservices$kubectlgetsecrets正如您从以下终端输出中所看到的,所有内容都列出了其当前状态:
您可以获得很多选项;例如,尝试运行这个:
$kubectlgetendpoints$kubectlgetevents$kubectlgetstorageclasses只运行kubectlget将列出您可以使用的所有不同参数。现在我们有了完整的pod名称,在我的情况下是cli-hello-world-3678853705-f41d2,我们可以通过运行kubectldescribe命令来了解更多关于它的细节。例如,我运行了这个:
$kubectldescribepods/cli-hello-world-3678853705-f41d2当您在本地运行命令时,请更新pod名称以反映您自己的名称。Kubernetes在启动时为每个pod添加一个唯一ID,以确保您可以在任何给定的主机上运行多个相同的pod。
我得到了以下信息:
$kubectldescribeservices/cli-hello-world$kubectldescribereplicasets/cli-hello-world-3678853705$kubectldescribestorageclasses/standard同样,您可以通过仅运行kubectldescribe来了解更多信息。在接下来的章节中,我们将介绍更多命令,以便在本书结束时,您将能够充分利用kubectl。
在完成本章之前,我希望我们能够快速看一下如何将存储从本地机器挂载到Minikube虚拟机内部,然后再挂载到我们的pod内部。
您将在Chapter03文件夹中找到一个名为html的文件夹。其中包含一个名为index.html的单个文件。在Chapter03文件夹中运行以下命令将挂载HTML到虚拟机内部:
$minikubemount./html:/data/html您可以从运行命令后显示的消息中看到这一点:
您需要保持此进程运行,因此在本节的其余部分中打开一个新的终端或PowerShell窗口以供使用。
运行以下命令:
如您所见,我们的index.html文件在/data/html/中的集群节点上可用。返回到Chapter03文件夹,您应该会看到一个名为cli-hello-world-storage.yml的文件。其中包含使用此挂载文件夹的服务和部署信息。
服务部分看起来与本节中先前使用的很相似;但是,在部署部分有一个额外的内容:
apiVersion:apps/v1beta1kind:Deploymentmetadata:name:cli-hello-world-storagelabels:app:nginxspec:replicas:1selector:matchLabels:app:cli-hello-world-storagetemplate:metadata:labels:app:cli-hello-world-storagespec:volumes:-name:htmlhostPath:path:/data/htmlcontainers:-name:nginximage:nginx:latestports:-containerPort:80volumeMounts:-mountPath:/usr/share/nginx/htmlname:html正如您所看到的,在部署的spec部分中,我们现在正在定义一个名为html的volume,然后在容器部分中,我们正在使用mountPath选项将名为html的卷挂载到/usr/share/nginx/html,这是我们在容器中使用的NGINX容器映像的默认网页根目录。
使用kubectlapply命令启动您的应用程序,然后使用minikubeservice命令在浏览器中打开服务:
$kubectlapply-fcli-hello-world-storage.yml$minikubeservicecli-hello-world-storage您应该看到以下页面:
如果您在本地机器上的html文件夹中编辑index.html,当您刷新浏览器窗口时,更改将立即反映出来。
在我们进入下一章之前,我们应该删除本章中使用的Minikube虚拟机,以便我们从头开始。首先,我们有一个进程,它正在保持我们主机机器上的html文件夹挂载。要终止此进程,请返回到终端或PowerShell并按下Ctrl+C;这将向进程发送终止信号并将您返回到命令行。然后我们可以运行:
$minikubedelete这将删除当前的虚拟机,这意味着当我们下次启动Minikube时,它将从头开始。
有关本章中使用的工具的更多信息,请访问它们的项目页面:
在本章中,我们使用Minikube在本地机器上安装了单节点Kubernetes集群;我们看了如何在macOS、Windows10和UbuntuLinux上实现这一点。一旦安装完成,我们发现无论我们的本地机器运行哪个操作系统,我们都可以以完全相同的方式与我们的单节点Kubernetes集群进行交互。
然后,我们首次启动了Pods、ReplicaSets和服务,使用了Kubernetes仪表板和名为kubectl的Kubernetes命令行客户端。
在下一章中,我们将在我们目前在本地运行的单节点Kubernetes集群上启动我们的第一个无服务器工具,名为Kubeless。
现在我们的Kubernetes安装已经运行起来了,我们可以开始运行我们的第一个无服务器应用程序;我们将通过一些示例来安装和运行Kubeless。我们将涵盖以下主题:
安装Kubeless
Kubeless概述
使用Kubeless运行我们的第一个函数-“helloworld”示例
更高级的示例-发布推文
无服务器插件
让我们开始在我们的三个目标操作系统上安装Kubeless。
Kubeless有两个组件;第一个是在Kubernetes上运行的堆栈,第二部分是您用来与Kubeless集群交互的命令行客户端。
我们首先将看看如何让Kubeless在Kubernetes上运行起来。一旦运行起来,我们将看看如何在我们的三个目标操作系统上安装命令客户端。
我们将在上一章中安装和配置的单节点Minikube集群上安装Kubeless。我们需要做的第一件事是确保我们从一个干净的Kubernetes安装开始。为此,我们只需要运行以下两个命令:
请记住,运行minikubedelete命令将立即删除当前正在运行的虚拟机,而不会发出警告,这意味着您的Minikube单节点集群上当前活动的所有内容都将丢失。
$minikubedelete$minikubestart现在我们的新的单节点Kubernetes集群已经运行起来了,我们需要通过运行以下命令为Kubeless创建一个命名空间:
$kubectlcreatenskubeless然后通过运行以下命令安装Kubeless本身:
$kubectlgetpods-nkubeless$kubectlgetdeployment-nkubeless$kubectlgetstatefulset-nkubeless这应该会显示类似以下输出:
或者,您也可以使用Kubernetes仪表板来检查状态。要做到这一点,运行以下命令打开仪表板:
$minikubedashboard当仪表板首次打开时,它被配置为显示默认命名空间,因为我们执行的第一个命令创建了一个名为kubeless的新命名空间。我们需要切换到kubeless命名空间以查看其中部署的Pods、Deployments和StatefulSets。
一旦您更改了命名空间,您应该在以下页面上看到一些内容:
正如您所看到的,我们只用两个命令就部署了一组相当复杂的服务。所有繁重的工作和复杂性都已完全抽象化。
现在Kubeless已经安装在我们的单节点Kubernetes集群上,我们可以考虑安装命令行客户端;这是我们将与我们的Kubeless集群进行交互的方式。
由于我们已经在上一章安装了Homebrew,我们将使用brew命令来安装Kubeless。为此,我们需要添加Kubelesstap;tap是一个包含软件安装说明的第三方存储库。一旦tap被添加,我们就可以以与我们在第二章中安装Minikube相同的方式安装Kubeless。
要安装tap,然后安装Kubeless命令行客户端,请运行以下两个命令:
$brewtapkubeless/tap$brewinstallkubeless安装完成后,您可以通过运行以下命令来检查已安装的客户端的版本:
$kubelessversion如果这返回的客户端版本与您安装的软件不同,不要太担心;这不应该是一个问题。
不幸的是,Kubeless没有可用的Chocolatey安装程序,因此我们必须手动下载和解压可执行文件。要在PowerShell中执行此操作,请运行以下命令:
$./kubelessversion这将返回命令行客户端的版本。
就像Windows10版本的Kubeless命令行客户端一样,我们需要下载发布版本,解压缩并将可执行文件移动到指定位置。要做到这一点,请运行以下命令:
$kubelessversion我们已经准备好在我们的UbuntuLinux主机上使用Kubeless。
在我们继续之前,我们还可以安装Kubeless的Web界面。就像Kubeless本身一样,只需运行以下命令即可轻松安装:
$minikubeserviceui--namespacekubeless从上述命令中可以看出,由于ui服务已部署在kubeless命名空间中,我们需要通过传递--namespace标志来让Minikube知道这是服务的访问位置。KubelessWeb界面可能需要几分钟才能启动,但当它启动时,您应该会看到一个类似以下内容的页面:
正如我们已经提到的,安装过程非常简单——在我们的单节点Kubernetes集群上安装Kubeless时,安装过程基本上是一样的,即使我们在由多个节点组成的Kubernetes上安装它也是如此。
Kubeless是一个支持在Kubernetes集群上部署无服务器函数的框架,它允许您使用HTTP和事件触发器来执行Python、Node.js或Ruby代码。该框架是使用核心Kubernetes功能构建的,如部署、服务、ConfigMaps等。这使得Kubeless的代码库很小,并且意味着开发人员不必重复大量的调度逻辑,因为它已经存在于Kubernetes核心中。
它通过利用Kubernetes控制器来工作。使用控制器,Kubeless开发人员已扩展了KubernetesAPI,以在Kubernetes中添加一个函数对象。Kubeless控制器作为部署在Kubernetes集群中运行,其主要工作是监视函数端点的调用。当调用端点时,将执行包含函数代码的运行时;这些是预构建的Docker镜像,用于包装您的函数,使用ConfigMaps注入到Kubernetes集群中的ApacheKafka消费者或HTTP服务器中,您可以像调用任何其他网页一样调用它们。
ApacheKafka是一个分布式流平台,让您可以发布和订阅信息流。在我们的情况下,这个信息流是触发的事件,Kubeless控制器订阅了这个事件。
所有这些意味着我们可以在我们运行的单节点集群中获得与我们在第一章无服务器景观中涵盖的AWS和MicrosoftAzure的无服务器服务类似的体验,包括我们本地运行的Kubernetes集群。
Bitnami多年来一直是分发预打包的开源和商业支持许可应用的领导者,在撰写本文时有超过140个应用程序,以可预测和一致的方式跨多个不同平台和公共云进行分发和支持,因此支持和开发Kubernetes对他们来说是一个自然的选择。
他们是Helm的核心贡献者,与微软和谷歌一起,Helm是由CloudNativeComputingFoundation论坛维护的Kubernetes的包管理器,我们知道来自第二章Kubernetes简介。
Kubeless命令行客户端有几个命令。在我们使用Kubeless在Kubernetes上启动我们的第一个无服务器函数之前,我们应该快速讨论一些我们将要使用的命令。
我们将要使用的最常见的命令是kubelessfunction。这允许我们部署、删除、编辑和列出函数。此外,我们可以通过使用call执行我们的函数,并检查日志。
接下来,我们有kubelessingress;使用此命令,我们可以创建、删除和列出到我们函数的路由。
最后,我们还将看一下kubelesstopic;与ingress一样,它允许我们创建、删除和列出主题,以及向主题发布消息。
首先,我们将看一下部署两个非常简单的helloworld函数。第一个简单地打印HelloWorld!,第二个接受输入,然后将其显示回给你。
首先,我们需要我们的函数。静态的hello-world函数需要以下三行Python代码:
importjsondefhandler():return"HelloWorld!"将前面的代码放在名为hello.py的文件中,该文件也可以在伴随本书的GitHub存储库的Chapter04/hello-world文件夹中找到。
现在我们有了我们的函数,我们可以通过运行以下命令将其部署到默认命名空间中:
您可能已经注意到,当您运行命令时,没有任何反馈,所以要检查函数是否已创建,您可以运行以下命令:
$kubelessfunctionls在前面的命令中有几列:
名称:这是函数的名称
命名空间:函数部署到的命名空间的名称
处理程序:要运行的处理程序的名称—在我们的情况下,处理程序只是处理程序,因此它正在调用hello-world.handler
运行时:Kubeless支持的每种语言都有单独的运行时
类型:这是函数被调用的方式,在我们的情况下这是HTTP
主题:如果我们订阅消息队列,这将是我们要观察消息的主题
另外,正如前一节中提到的,Kubeless将函数对象添加到Kubernetes中。您可以运行以下命令来检查我们的函数是否被列在函数对象中:
$kubectlgetfunctions通过这些命令运行应该会给您一些类似以下结果:
现在我们的函数已部署,我们可以执行它。要运行此操作,请运行:
$kubelessfunctioncallhello这将产生以下结果:
我们调用函数的另一种方式是使用KubelessWeb界面。通过运行以下命令打开它:
$minikubeserviceui--namespacekubeless打开后,您应该在左侧列出函数hello。单击hello将显示函数中的代码,并且右侧有一个标有RUNFUNCTION的按钮;单击此按钮将执行hello函数并返回HelloWorld!:
我们与函数交互的最终方式是创建Ingress规则;但是,在执行此操作之前,我们必须在Minikube中启用Ingress插件。要执行此操作,请运行以下命令:
$minikubeaddonsenableingress现在Ingress插件已启用,我们需要使用Kubeless创建Ingress路由。要执行此操作,我们只需要运行以下命令:
$kubelessingresscreate--functionhellohello-ingress我们需要知道Kubeless创建的主机,以便访问我们的服务。要执行此操作,请运行以下命令:
nip.io是一个简单且免费的DNS服务,允许您创建DNS记录将您的主机映射到IP地址。Kubeless使用此服务创建有效的主机以路由到您的服务。
在我的浏览器中打开此URL返回HelloWorld!,通过HTTPie运行它也是如此,我们在第一章中介绍了HTTPie,您可以从以下终端输出中看到:
现在我们的第一个函数已经运行起来了,让我们看看如何创建一个可以传递并打印数据的函数。
我们的新函数代码仍然非常简单:
importjsondefhandler(context):printcontext.jsonreturncontext.json此代码的全部作用只是接收我们发布的JSON并将其显示给我们。将其放入名为hello-name.py的文件中,或者使用GitHub存储库中Chapter04/hello-world/文件夹中的文件。一旦有了文件,您可以通过运行以下命令创建函数:
$kubelessfunctionls您应该看到列出了两个函数,hello和hello-name。现在我们已经创建了新函数,可以通过运行以下命令来调用它:
$kubelessfunctioncallhello-name--data'{"name":"Russ"}'请注意,这次我们使用--data标志将数据传递给函数。运行所有命令后,您应该看到类似以下终端输出:
在使用Web界面调用函数时,我们还需要传递数据。要做到这一点,再次打开界面,运行:
$minikubeserviceui--namespacekubeless打开后,点击hello-name函数。在点击RUNFUNCTION按钮之前,使用下拉菜单将GET更改为POST,并在请求表单中输入以下内容:
{"name":"Russ"}现在,点击RUNFUNCTION按钮。这将返回与kubelessfunctioncall命令相同的结果:
我们还可以通过配置Ingress路由直接与服务交互:
$kubelessingresscreate--functionhello-namehello-name-ingress$kubelessingresslist这将为我们的两个函数提供URL:
为什么会这样,尽管我们使用kubelessfunctioncall命令和KubelessWeb界面调用时都没有错误?
通过简单地将URL输入到浏览器中,我们没有发布任何数据供函数返回;这就是为什么会生成错误的原因。我们可以通过检查日志来确认这一点。要做到这一点,刷新浏览器中的页面几次,然后运行以下命令:
$kubelessfunctionlogshello-name您应该看到类似以下的内容:
前面日志输出的第一行是内部健康检查,它是成功的,因为生成了200状态,您可以在GET之后看到。接下来的几行包含我们要查找的错误;正如您所看到的,我们得到了Traceback,然后是以下内容:TypeError:handler()takesexactly1argument(0given)。这意味着函数期望传递数据,但没有传递。下一行是来自我们浏览器的请求;正如您在GET之后看到的,有一个500的状态。
那么,我们如何与需要POST数据而不是GET的函数进行交互呢?在macOS和Linux命令行上,您可以通过几种方式实现这一点,但在Windows上,您将不得不运行其他东西。与其通过不同的示例来工作,我要安装一个名为Postman的软件。这个桌面软件适用于我们在书中涵盖的所有三种操作系统,并且它将为我们与hello-name函数以及我们启动的任何其他函数进行交互提供一个很好的图形界面。
要在macOS10.13HighSierra上使用Homebrew安装Postman,只需运行:
$brewcaskinstallpostmanPostman有一个Chocolatey软件包,因此如果您使用的是Windows10专业版,可以运行:
$chocoinstallpostman要在Ubuntu17.04上安装Postman,我们需要运行一些额外的步骤。首先,我们需要下载、解压缩并移动文件到指定位置,确保清理和移动我们需要的文件。为此,请运行以下命令:
$cat>~/.local/share/applications/postman.desktop< 点击BUILDINGBLOCKS下的Request选项;这将带您进入保存对话框。在这里,输入hello-name的请求名称,然后点击+CreateCollection。在这里,创建一个名为Kubeless的集合,然后点击SavetoKubeless按钮。 要输入数据,请单击Body,然后选择原始选项。当您选择原始选项时,输入字段将发生变化,您应该看到单词Text和旁边的下拉图标。单击这个图标,然后选中JSON(application/json)选项。一旦更改,输入以下内容到主字段中: 单击保存按钮将保存设置,如果您想重新运行它们的话。 在我们继续下一节之前,我们应该整理一下我们的函数。要做到这一点,我们只需要运行: $kubelessingressdeletehello$kubelessfunctiondeletehello$kubelessingressdeletehello-name$kubelessfunctiondeletehello-name这将删除我们的两个helloworld函数和Ingress路由。您还可以在Kubelessweb界面和Kubernetes仪表板中再次检查是否已删除了所有内容;您可以通过运行以下命令打开它们: $minikubeserviceui--namespacekubeless$minikubedashboard如果您发现任何剩余的内容,无论是hello还是hello-name,您都可以从仪表板中删除服务、pod,甚至Ingress路由。 KubelessGitHub账户有一些更多的示例应用程序,这些应用程序不仅可以打印静态内容或转发您发送的数据。在这个例子中,我们将看看如何创建一个可以发布到Twitter账户的函数。 在我们看如何启动函数之前,我们需要为我们的函数生成密钥,以便对Twitter进行身份验证,然后发布到您的账户。为此,您需要以下内容: Twitter账户 与账户注册的手机号码 名称*:MedialGlassesKubeless 描述:使用Kubeless测试发布到Twitter 回调URL:留空 开发者协议:同意协议 填写完上述信息后,点击“创建Twitter应用”按钮。创建应用程序后,您将被带到一个页面,允许您管理您的应用程序。页面上的一个选项卡是“密钥和访问令牌”;点击这个选项卡将显示您的消费者密钥(API密钥)和消费者秘钥(API秘钥)—请记下这些信息。 在页面底部,您将有一个按钮,允许您为您的帐户创建访问令牌和访问令牌秘钥;点击按钮将生成这些令牌—再次,请记下这些信息。 虽然以下示例将包含我生成的密钥,但它们已被撤销,您应该使用您自己的密钥。此外,由于它们允许对您的Twitter帐户进行读写访问,将它们存储在GitHub、Gists或其他版本控制软件等公开可访问的地方可能导致第三方未经您的许可就完全访问您的Twitter帐户。 现在我们已经配置了Twitter应用程序,并且拥有了发布推文所需的所有令牌,我们需要将它们添加到Kubernetes中。Kubernetes允许您定义秘密;这些是您的应用程序需要使用的API密钥和令牌等变量。但是,您可能不希望将它们放在源代码控制下或嵌入到您的应用程序中,因为相同代码的各种部署使用不同的密钥与API进行交互—例如,代码的开发版本使用与生产版本不同的API凭据。 要添加前一节中记下的令牌,您只需要运行以下命令,用您的令牌和密钥替换大写的占位符: $kubectlcreatesecretgenerictwitter\--from-literal=consumer_key=YOUR_CONSUMER_KEY\--from-literal=consumer_secret=YOUR_CONSUMER_SECRET\--from-literal=token_key=YOUR_TOKEN_KEY\--from-literal=token_secret=YOUR_TOKEN_SECRET对我来说,命令看起来像下面这样: 这样就创建了一个名为twitter的秘密,其中包含我们传递给命令的四个不同的键和令牌。您可以通过运行以下命令来列出这些秘密: $kubectlgetsecret这将列出您的Kubernetes集群中的所有秘密,如下面终端输出所示: 这里有默认的Kubernetes服务账户令牌,包含三个项目,以及我们的twitter秘密,其中包含四个键和令牌。您也可以在Kubernetes仪表板中查看秘密: 从前面的屏幕截图中可以看出,您还可以通过单击眼睛图标来显示秘密。 现在我们的环境准备好了,我们可以部署函数了。为此,我们需要两个文件;第一个是requirements.txt文件,其中只包含两行: python-twitterkubernetes==2.0.0requirements.txt文件让Python知道要与我们的代码一起部署的外部库。在我们的文件中,我们使用twitter库,以便可以轻松地发布推文,还使用kubernetes库来解码我们在上一节中创建的秘密。使用这些库意味着我们的代码非常简化,因为所有的繁重工作都发生在我们的核心函数之外。函数的代码如下: importbase64importtwitterfromkubernetesimportclient,configconfig.load_incluster_config()v1=client.CoreV1Api()forsecretsinv1.list_secret_for_all_namespaces().items:ifsecrets.metadata.name=='twitter':consumer_key=base64.b64decode(secrets.data['consumer_key'])consumer_secret=base64.b64decode(secrets.data['consumer_secret'])token_key=base64.b64decode(secrets.data['token_key'])token_secret=base64.b64decode(secrets.data['token_secret'])api=twitter.Api(consumer_key=consumer_key,consumer_secret=consumer_secret,access_token_key=token_key,access_token_secret=token_secret)defhandler(context):msg=context.jsonstatus=api.PostUpdate(msg['tweet'])将此内容放入名为tweet.py的文件中。与以前一样,requirements.txt和tweet.py文件都可以在GitHub存储库Chapter04/twitter/中找到。 部署函数的命令在部署命令中有一个附加项。由于我们现在正在加载外部库,我们需要让Kubeless知道我们想要使用requirements.txt文件,方法是添加--dependencies标志: 现在我们的函数已经部署,我们可以开始发推文了。要发送我们的第一条推文,您只需运行以下命令: $kubelessfunctioncalltwitter--data'{"tweet":"TestingtwitterfunctionfromKubeless!"}'您将不会收到任何反馈,但如果您转到您的Twitter帐户,您应该会看到这条推文: 您还可以使用Postman发送推文。首先,通过运行以下命令创建一个Ingress路由: $kubelessingresscreate--functiontwittertwitter-ingress$kubelessingresslist这将创建路由并给我们提供访问函数所需的主机: 现在我们可以打开Postman,并且像以前一样配置它,但是这个文件将以下内容作为发布内容: {"tweet":"TestingtwitterfunctionfromKubelessusing@postmanclient!"}单击发送将发布推文,并且与使用kubelessfunctioncall命令调用函数时一样,不会给我们任何反馈: 检查Twitter应该会显示第二条推文,这次提到了@postmanclient。您可以在以下URL查看我的两条测试推文: 再次,在继续下一部分之前,我们应该删除我们的函数并整理一下: 回到第一章,无服务器景观,我们安装了Serverless框架来部署AWSLambda函数;无服务器也适用于Kubeless。 如果你还没有安装无服务器,这里是如何在我们正在涵盖的三个操作系统上安装它的快速回顾。 尽管已经尽一切努力确保以下说明适用于所有支持的平台,但由于插件所需的一些依赖项的兼容性问题,Kubeless无服务器插件在基于Windows的操作系统上的运行成功程度有所不同。 对于macOS10.13HighSierra,运行以下命令使用Homebrew安装Node.js: $brewinstallnode如果你正在使用Windows10专业版,你可以通过运行Chocolatey来安装Node.js: $chocoinstallnodejs最后,如果你使用的是Ubuntu17.04,你可以使用以下命令安装Node.js: $serverlesslogin现在无服务器已安装,我们可以通过运行以下命令启动演示Kubeless函数: $serverlesscreate--templatekubeless-python--pathnew-project$cdnew-project$npminstall如果你没有跟着做,运行这些命令会得到以下输出: 这安装了Kubeless无服务器插件并创建了定义我们函数的serverless.yml文件。其中包含以下内容: service:new-projectprovider:name:kubelessruntime:python2.7plugins:-serverless-kubelessfunctions:hello:handler:handler.hello正如你所看到的,这段代码告诉无服务器我们正在使用Kubeless,并且它应该使用Kubeless插件。它还定义了一个名为hello的函数和处理程序。该函数可以在handler.py文件中找到。这包含以下代码,与我们在本章前面看过的hello-world示例非常相似: importjsondefhello(request):body={"message":"GoServerlessv1.0!Yourfunctionexecutedsuccessfully!","input":request.json}response={"statusCode":200,"body":json.dumps(body)}returnresponse现在我们有了示例函数,我们可以通过运行以下命令部署服务: $serverlessdeploy-v服务部署完成后,最后一步是部署函数本身。要做到这一点,请运行: $serverlessdeployfunction-fhello使用无服务器本身来调用函数可能会导致以下错误,如果出现这种情况,不要担心: 您仍然可以使用Kubeless访问该函数: $kubelessfunctionlist$kubelessfunctioncallhello--data'{"Kubeless":"Welcome!"}'这将返回预期的结果: 要删除示例函数,请运行以下命令: $serverlessremove在完成本章之前,让我们看一个使用事件而不是HTTP的示例。在GitHub存储库的Chapter04/serverless-event/文件夹中,有一个监听事件的示例应用程序。 serverless.yml文件与之前的HTTP示例不同,除了处理程序外,还添加了一个包含触发器/主题的事件部分: service:eventsprovider:name:kubelessruntime:python2.7plugins:-serverless-kubelessfunctions:events:handler:handler.eventsevents:-trigger:'hello_topic'handler.py文件可能包含迄今为止我们所看到的最简单的代码: defevents(context):returncontext要启动示例,只需从Chapter04/serverless-event/文件夹中运行以下命令: 从前面的终端输出中可以看出,我们有一个PubSub类型和一个hello_topic主题。现在我们可以通过运行以下命令在hello_topic主题中发布事件: $kubelesstopicpublish--topichello_topic--data'testinganevent!'$kubelesstopicpublish--topichello_topic--data'andanotherevent!'最后,我们可以通过运行日志来检查这两个事件是否已经被处理: $serverlesslogs-fevents从以下输出中可以看出,事件已成功发布并由我们的测试函数处理: 在进入下一章之前,我们可以通过运行以下命令删除我们的KubelessKubernetes单节点集群: $minikubedelete摘要在本章中,我们已经将Kubeless部署到了我们使用Minikube启动的单节点Kubernetes上。我们安装了Kubernetes命令行客户端和基于Web的界面。一旦集群部署并安装了工具,我们就在Kubeless安装上部署和执行函数。 在安装一个更有用的发布推文的函数之前,我们先安装了两个基本的测试函数。然后,我们看了一下如何使用Serverless框架与Kubeless进行交互。 在下一章中,我们将看一下一个名为Funktion的基于事件的框架。 在我们继续在公共云中启动Kubernetes集群之前,我们将再看一个本地示例;这次我们将看一下Funktion。我们将涵盖以下主题: 介绍Funktion 安装和配置Funktion 使用Funktion运行我们的第一个函数 Twitter流 Funktion的标语将其描述为基于事件的KubernetesLambda编程。从表面上看,Funktion似乎与Kubeless和我们在前几章讨论过的其他无服务器框架非常接近。然而,它有自己的特色,使其与我们正在研究的其他框架有所不同。 我们正在研究的大多数无服务器函数都支持两种基本事件类型: HTTP:这是通过标准HTTP请求将数据传递给框架的地方;通常数据将被发布为JSON对象 为了让您了解ApacheCamel和因此Funktion支持的一些事件流,以下是一些亮点: AWS-SNS支持与亚马逊的简单通知服务(SNS)一起使用 Braintree允许与Braintree支付网关服务进行交互 etcd允许您与etcd键值存储进行交互 Facebook开放了完整的FacebookAPI GitHub允许您监听来自GitHub的事件 Kafka-像Kubeless一样,您可以订阅Kafka流 Twitter让您能够监听标签、帖子等 还有许多其他服务,如LinkedIn、Slack、各种SQL和NoSQL数据库、来自AWS的S3的文件服务、Dropbox和Box等等。 所有这些选择使其与我们一直在研究和将要研究的其他框架相比,成为一个非常好的选择。 Funktion部署由几个不同的组件组成。首先是一个函数;这就是代码本身,由KubernetesConfigMap管理。 单独的函数本身并不是很有用,因为它只存在于ConfigMap中。因此,我们需要一个运行时,一个在调用时执行函数的Kubernetes部署。当Funktion操作员(稍后会详细介绍)检测到添加了新函数时,将自动创建运行时。 接下来,我们有一个连接器;这是一个事件源的表示,就像我们在本节前面讨论的那些一样——它包含有关事件类型、配置(如API凭据)以及数据搜索参数的信息。 然后我们有流程;这是一系列步骤,可以从调用函数的连接器中消耗事件。 最后,我们有Funktion操作员。这是在Kubernetes中运行的一个pod,监视构成我们的Funktion部署的所有组件,如函数、运行时、连接器和流程。它负责创建提供Funktion功能的Kubernetes服务。 Funktion是开源的,根据Apache许可证2.0发布;它是由fabric8开发的,fabric8是RedHat的JBoss中间件平台的上游项目。fabric8本身是一个基于Docker、Kubernetes和Jenkins的面向Java的微服务平台。它也与RedHat自己的OpenShift平台很好地配合。 现在我们对Funktion与其他框架的区别有了一些背景了,我们可以看看如何在我们的单节点Kubernetes集群上安装它。 使用Funktion有三个步骤。首先,我们需要安装命令行。这是大部分部署和管理我们的Funktion部署的命令将被输入的地方。一旦命令行客户端安装完成,我们可以使用Minikube启动我们的单节点Kubernetes集群,然后使用FunktionCLI引导我们的环境。 与我们正在介绍的许多框架一样,Funktion是用Go语言编写的。这意味着我们的三个平台都有独立的可执行文件。 然而,在撰写本文时,无论是在macOS上使用Homebrew还是在Windows10专业版上使用Chocolatey,都没有可用的安装程序,这意味着我们将在所有三个平台上进行手动安装。 让我们从如何在macOS上安装开始。 在macOS上安装很简单,因为该项目已发布了未压缩的独立可执行文件。我们只需要下载正确的软件包并使其可执行。要做到这一点,请运行以下命令: $funktionversionFunktion版本将返回如下: 如您所见,虽然安装过程非常简单,但软件包不在Homebrew中可用也有一个缺点。如果在Homebrew中可用,那么更新到较新版本将更容易,因为Homebrew会在您运行时负责检查和安装升级: $brewupdate$brewupgrade目前,如果需要升级,您将不得不删除当前版本并下载新版本来替换它。 在Windows上安装Funktion命令行客户端的过程与macOS类似。首先,以管理员用户身份打开PowerShell窗口,方法是从任务栏中的PowerShell菜单中选择以管理员身份运行。一旦打开,您应该看到您在文件夹C:\WINDOWS\system32中;如果没有,请运行: $cdC:\WINDOWS\system32一旦您在C:\WINDOWS\system32文件夹中,请运行以下命令: 同样,由于我们没有使用软件包管理器来安装Funktion,因此如果要升级,您将不得不删除旧的可执行文件,然后重复安装过程,并确保更新URL中的版本号以反映您所需的版本。 最后,我们有Ubuntu17.04。安装过程与我们为macOS执行的命令基本相同。但是,要确保我们下载正确的可执行文件,并且在/usr/local/bin文件夹的权限在操作系统之间略有不同时,我们还需要使用sudo命令: $funktionversion你应该看到类似以下的内容: 现在我们在三个操作系统上都安装了命令行客户端,我们可以继续部署。 你可能已经注意到,我们再次发现自己处于一个位置,现在可以在任何操作系统上使用相同的命令。这意味着本章剩余的命令将能够在我们的三个目标操作系统上运行。 在使用Minikube启动我们的单节点Kubernetes集群之前,可以通过运行以下命令检查是否有任何更新。macOS10.13HighSierra用户可以运行: $brewupdate$brewupgrade然后,要检查和更新Minikube,请运行以下命令,从以下开始: $brewcaskoutdated这将向您呈现可以更新的软件包列表。如果Minikube在列表中,请运行以下命令: $brewcaskreinstallminikubeWindows10专业版用户可以运行: $chocoupgradeallUbuntu17.04用户需要检查第三章中的发布页面详细信息,在本地安装Kubernetes,删除旧的二进制文件,并使用更新的版本重复安装过程。 一旦您检查了Minikube的更新,可以通过运行以下命令启动您的集群: $minikubestart根据第三章,在本地安装Kubernetes和第四章,介绍Kubeless功能,这将启动单节点Kubernetes集群,并配置您的本地Kubernetes客户端与其交互。如果您已经更新了Minikube,您可能还会注意到下载并安装了一个更新版本的Kubernetes: 如果你已经升级了Minikube,可以使用以下命令检查一切是否正常运行: $minikubestatus$kubectlgetall$minikubedashboard现在我们的单节点Kubernetes集群已经重新启动运行,Funktion安装的最后阶段是引导部署。 安装Funktion非常简单,事实上,只需要一个命令: $funktioninstallplatform这将给出以下输出: 一两分钟后,您应该能够运行: $kubectlgetpods$kubectlgetdeployments上述命令将检查部署的状态: 您还应该能够在Kubernetes仪表板中看到Pods和Deployments: 运行以下命令应该返回一个空列表: $funktiongetfunction这证明了Funktion命令行客户端可以连接到您新安装的Funktion部署并与其交互。 现在我们的Funktion部署已经运行起来了,我们可以看一下部署一个非常简单的helloworld示例。在支持本书的GitHub存储库中的/Chapter05/hello-world/src文件夹中,您会找到一个名为hello.js的文件。这个文件包含以下代码: module.exports=function(context,callback){varname=context.request.query.name||context.request.body||"World";callback(200,"Hello"+name+"!!");};在/Chapter05/hello-world/文件夹中运行以下命令将使用上述代码创建我们的第一个函数: $funktioncreatefn-fsrc/hello.js输出应该如下所示: 从终端输出中可以看出,这创建了一个名为hello的function。现在,我们运行以下命令: $funktiongetfunction这应该返回一些结果。从以下输出中可以看出,我们现在可以看到NAME,PODS和URL列出: 我们可以运行以下命令来仅返回函数的URL,或在浏览器中打开它: $funktionurlfnhello$funktionurlfnhello-o您应该看到以下结果: 打开的浏览器窗口显示如下。我相信您会同意这不是最令人兴奋的页面: 但它确实证明了我们的函数正在工作并显示内容。您可以通过运行以下命令来显示函数的日志: $funktionlogsfunctionhello这将实时将日志内容流式传输到您的终端窗口。您可以通过刷新浏览器几次来查看,您应该看到您的页面请求与内部健康检查请求一起被记录。 现在我们已经创建了我们的第一个函数,我们可以安装一些连接器。要这样做,请运行以下命令: $funktiongetflow您应该看到以下内容: 正如您所看到的,流程称为timer-foo1;我们在与其交互时需要使用此名称。例如,您可以通过运行以下命令来检查流程的日志: $funktionlogsflowtimer-foo1或者在Kubernetes仪表板中,您可以找到名为timer-foo1的pod,并在那里检查日志: 通过运行以下命令检查函数的日志: $funktionlogsfunctionhello您应该看到每五秒有一个来自用户代理为Apache-HttpClient/4.5.2的客户端的页面请求。这是计时器流程: 要删除流程,只需运行: $funktiondeleteflowtimer-foo1这将删除运行连接器的pod,并且您的函数将停止接收自动请求。 返回Kubernetes仪表板,单击ConfigMaps应该显示Funktion创建的所有内容的列表。正如您所看到的,Funktion的大部分部分都有一个ConfigMap: 单击hello的ConfigMaps将显示类似以下页面的内容: 正如您所看到的,这包含了我们函数的代码,并且它已自动检测到它是用Node.js编写的,还有它是从src文件夹部署的。 在查看更高级示例之前,还有一件可能会让您感兴趣的事情,那就是与ChromeDev工具的集成。要做到这一点,请运行以下命令: $funktiondebugfnhello这将在前台打开一个进程,并为您提供一个URL放入GoogleChrome中: 一旦您打开GoogleChrome并指向您的函数,您可以执行诸如直接在浏览器中编辑代码之类的任务: 要删除我们的hello函数,我们只需要运行: $funktiondeletefunctionhello这应该让我们得到一个干净的安装,准备进行更高级的示例。 在上一节中我们安装了Twitter连接器,让我们看看如何配置它来拉取一些数据。首先,您可以通过运行以下命令查看连接器的所有可配置选项: $funktioneditconnectortwitter-l你应该看到类似以下的终端输出: 如您所见,您可以配置代理,并提供accessToken、accessTokenSecret、consumerKey和consumerSecret。您应该从上一章中获得这些信息。如果没有,那么请使用第四章中的说明重新生成它们,介绍Kubeless功能。 就像我将用来演示您需要运行的命令的令牌和密钥一样,前面截图中列出的详细信息是默认的虚拟占位符详细信息,不是有效的。 要使用您自己的详细信息更新连接器,请运行以下命令,并确保用您自己的详细信息替换它们: $funktioneditconnectortwitter\accessToken=1213091858-REJvMEEUeSoGA0WPKp7cv8BBTyTcDeRkHBr6Wpj\accessTokenSecret=WopER9tbSJtUtASEz62lI8HTCvhlYBvDHcuCIof5YzyGg\consumerKey=aKiWFB6Q7Ck5byHTWu3zHktDF\consumerSecret=uFPEszch9UuIlHt6nCxar8x1DSYqhWw8VELqp3pMPB571DwnDg您应该收到连接器已更新的确认。现在,我们可以启动使用Twitter适配器的流程。为此,我们应该运行以下命令: $funktioncreateflow--nametwitsearch"twitter://searchtype=polling&keywords=kubernetes&delay=120s"$funktiongetflows我们将看到以下内容: 一旦您启动了pod,您可以通过运行以下命令来检查日志: $funktionlogsflowtwitsearch或者通过在仪表板中查看twitsearchpod的日志: 如您所见,Camel正在打印包含单词Kubernetes的一系列推文。您的应用程序可以订阅此流,并对推文进行处理。最后,运行以下命令将删除流程: $funktiondeleteflowtwitsearch然后,您可以使用minikubedelete命令删除您的Minikube机器。 在本章中,我们简要介绍了Funktion。我们安装了命令行客户端,然后将其安装在我们的单节点Kubernetes集群上。部署后,我们启动了一个测试函数,并与其交互,然后使用其中的一个事件流来搜索包含Kubernetes的推文。 在下一章中,我们将讨论如何将我们的Kubernetes集群从本地单节点扩展到托管在公共云上的多节点集群。 到目前为止,我们一直在本地机器上运行Kubernetes。这确实有一些缺点,其中之一是处理能力。我们将开始研究一些更复杂和强大的框架,因此我们需要一些额外的能力。因此,我们将尝试在几个不同的公共云上安装Kubernetes,每次使用不同的工具: 在DigitalOcean上启动Kubernetes 在AWS上启动Kubernetes 在MicrosoftAzure上启动Kubernetes 在Google云平台上启动Kubernetes 然后,我们将研究公共云提供商之间的差异,并尝试在其中一个平台上安装Kubeless。 我们将首先研究的公共云平台是DigitalOcean。DigitalOcean与我们将在接下来的章节中研究的三大云平台有所不同,因为它的功能较少。例如,在产品页面上,DigitalOcean列出了八个功能,而AWS产品页面列出了十八个主要领域,每个领域又分为六个或更多的功能和服务。 不要因此而认为DigitalOcean比我们在本章中将要研究的其他公共云提供商差。 DigitalOcean的优势在于它是一个非常简单易用的托管平台。通过其直观的API和命令行工具,支持服务和出色的管理界面,可以在不到一分钟内轻松启动功能强大但价格竞争力极强的虚拟机。 Droplets是DigitalOcean对其计算资源的术语。对于我们的Kubernetes,我们将启动三个Ubuntu17.04Droplets,每个Droplet配备1GB的RAM,1个CPU和30GB的SSD存储。 在撰写本文时,这个由三个Droplet组成的集群每月大约需要花费30美元才能保持在线。如果您打算在需要时保持在线,那么这三个Droplets每小时的费用将是0.045美元。 接下来,我们需要生成一个SSH密钥并将其上传到DigitalOcean。如果您已经有一个带有SSH密钥的帐户,可以跳过此任务。如果您没有密钥,请按照给定的说明操作。 如果您使用的是macOSHighSierra或Ubuntu17.04,则可以运行以下命令: $ssh-keygen-trsa这将要求您选择一个位置来存储您新生成的私钥和公钥,以及一个密码。密码是可选的,但如果您的SSH密钥的私有部分落入错误的手中,它确实会增加另一层安全性: 生成密钥后,您需要记下密钥的公共部分。为此,请运行以下命令,并确保更新密钥路径以匹配您自己的路径: $cat/Users/russ/.ssh/id_rsa.pub您应该看到类似以下的内容: 对于Windows10专业版用户来说,你很可能正在使用PuTTY作为你的SSH客户端。如果你没有PuTTY,你可以通过运行以下命令来安装它: $chocoinstallputty一旦PuTTY安装完成,你可以通过运行以下命令打开PuTTYgen程序: $PUTTYGEN.exe打开后,点击生成并按照提示在空白区域移动你的光标。一秒钟后,你应该会生成一个密钥: 如前面的截图所示,你可以选择添加一个密码,这将用于解锁你的密钥的私有部分;再次强调,这是可选的。 点击保存公钥,也保存私钥,并记下公钥的内容。 现在你已经为你的账户分配了一个SSH密钥,你可以使用它来创建你的Droplets,并且无需密码即可访问它们。要创建你的Droplets,点击屏幕右上角的创建按钮,然后从下拉菜单中选择Droplets。 在Droplet创建页面上有几个选项: 选择一个镜像:选择Ubuntu16.04镜像 选择一个大小:选择每月10美元的选项,其中包括1GB、1CPU和30GBSSD 添加块存储:保持不变 选择数据中心区域:选择离你最近的区域;我选择了伦敦,因为我在英国 选择附加选项:选择私人网络连接。 添加你的SSH密钥:选择你的SSH密钥 完成并创建:将Droplets的数量增加到3,现在保持主机名不变 填写完前面的部分后,点击页面底部的创建按钮。这将启动你的三个Droplets,并向你反馈它们创建过程的进度。一旦它们启动,你应该会看到类似以下页面的内容: 如你所见,我有三个Droplets,它们的IP地址,还有一条很好的激励性信息。现在我们可以开始使用kubeadm部署我们的Kubernetes集群。 $apt-getupdate$apt-getupgrade现在我们已经是最新的了,我们可以安装先决条件软件包。要做到这一点,请运行以下命令: 现在我们已经安装了先决条件,我们可以通过运行以下命令来添加Kubernetes存储库: $apt-getupdate$apt-getinstallkubeletkubeadmkubectl安装完成后,您可以通过运行以下命令来检查已安装的kubeadm的版本: $kubeadmversion现在我们已经安装了所需的一切,我们可以通过运行以下命令来引导我们的Kubernetes主节点: 完成后,您应该看到以下消息,但是带有您的令牌等: 记下底部的kubeadmjoin命令,我们很快会看到它。我们应该运行消息中提到的命令: $mkdir-p$HOME/.kube$sudocp-i/etc/kubernetes/admin.conf$HOME/.kube/config$sudochown$(id-u):$(id-g)$HOME/.kube/config接下来,我们需要启用Pod网络。您可以选择几种选项,所有这些选项都为您的Kubernetes集群提供了多主机容器网络: 对于我们的安装,我们将使用WeaveNet。要安装它,只需运行以下命令: 如您所见,这使用了kubectl命令来部署pod网络。这意味着我们的基本Kubernetes集群已经运行起来了,尽管只在单个节点上。 为了准备其他两个集群节点,打开两者的SSH会话,并在两者上运行以下命令: $kubeadmjoin--token0c74f5.4d5492bafe1e0bb9139.59.180.255:6443--discovery-token-ca-cert-hashsha256:3331ba91e4a3a887c99e59d792b9f031575619b4646f23d8fe2938dc50f89491您需要运行收到的命令,因为令牌将绑定到您的主节点。在两个节点上运行该命令,您应该会看到类似以下终端输出的内容: 一旦您在剩余的两个节点上运行了该命令,请返回到您的主节点并运行以下命令: $kubectlgetnodes这应该返回您的Kubernetes集群中的节点列表: 要在macOSHighSierra或Ubuntu17.04上执行此操作,请运行以下命令,确保将IP地址替换为您的主节点的IP地址: $scproot@139.59.180.255:/etc/kubernetes/admin.conf如果您使用的是Windows10专业版,您将需要使用诸如WinSCP之类的程序。要安装它,请运行以下命令: $chocoinstallwinscp安装后,通过输入WINSCP.exe来启动它,然后按照屏幕提示连接到您的主节点并下载admin.conf文件,该文件位于/etc/kubernetes/中。 一旦您有了admin.conf文件的副本,您就可以在本地运行以下命令来查看您的三节点Kubernetes集群: $kubectl--kubeconfig./admin.confgetnodes一旦我们确认可以使用本地的kubectl副本连接,我们应该将配置文件放在适当的位置,这样我们就不必每次使用--kubeconfig标志。要做到这一点,请运行以下命令(仅适用于macOS和Ubuntu): $mv~/.kube/config~/.kube/config.mini$mvadmin.conf~/.kube/config现在运行以下命令: $kubectlgetnodes这应该显示您的三个Droplets: 这是在低规格服务器上手动部署Kubernetes。在接下来的几节中,我们将看看如何在其他公共云中部署Kubernetes,首先是AWS。 我们可以使用几种工具在AWS上启动Kubernetes集群;我们将介绍一个叫做kube-aws的工具。不幸的是,kube-aws不支持基于Windows的机器,因此以下说明只适用于macOSHighSierra和Ubuntu17.04。 kube-aws是一个命令行工具,用于生成AWSCloudFormation模板,然后用于启动和管理CoreOS集群。然后将Kubernetes部署到CoreOS实例的集群中。 AWSCloudFormation是亚马逊的本地脚本工具,允许您以编程方式启动AWS服务;它几乎涵盖了所有AWSAPI。CoreOS是一个专注于运行容器的操作系统。它的占用空间极小,并且设计为可以在云提供商上直接进行集群和配置。 在第一章中,无服务器景观,我们看了一下创建Lambda函数。为了配置这个,我们安装了AWSCLI。我假设您仍然配置了这个,并且您配置的IAM用户具有管理员权限。您可以通过运行以下命令来测试: $awsec2describe-instances这应该返回类似以下内容: 现在我们在正确的区域,点击密钥对选项,在左侧菜单的NETWORK&SECURITY部分下可以找到。页面加载后,点击导入密钥对按钮,然后像DigitalOcean一样,输入您的密钥对的名称,并在其中输入您的id_rsa.pub文件的内容。 接下来,我们需要一个AWSKMS存储。要创建这个,运行以下命令,确保根据需要更新您的区域: $awskms--region=eu-west-1create-key--description="kube-awsassets"这将返回几个信息,包括一个Amazon资源名称(ARN)。记下这个信息以及KeyId: 接下来,我们需要一个AmazonS3存储桶。使用AWSCLI运行以下命令来创建一个,确保更新区域,并且使存储桶名称对您来说是唯一的: 现在我们已经导入了我们的公共SSH密钥,有了KMSARN和一个S3存储桶,我们只需要决定集群的DNS名称。 我将使用kube.mckendrick.io,因为我已经在AmazonRoute53DNS服务上托管了mckendrick.io。您应该选择一个可以在其上配置CNAME的域或子域,或者一个托管在Route53上的域。 现在我们已经掌握了基础知识,我们需要安装kube-aws二进制文件。要做到这一点,如果您正在运行macOSHighSierra,您只需要运行以下命令: $brewinstallkube-aws如果您正在运行UbuntuLinux17.04,您应该运行以下命令: 在我们开始创建集群配置之前,我们需要创建一个工作目录,因为将会创建一些工件。让我们创建一个名为kube-aws-cluster的文件夹并切换到它: $mkdirkube-aws-cluster$cdkube-aws-cluster现在我们在我们的工作目录中,我们可以创建我们的集群配置文件。要做到这一点,运行以下命令,确保用之前部分收集的信息替换值: 如果您没有使用Route53托管的域,删除--hosted-zone-id标志。 kube-awsinit\--cluster-name=kube-aws-cluster\--external-dns-name=kube.mckendrick.io\--hosted-zone-id=Z2WSA56Y5ICKTT\--region=eu-west-1\--availability-zone=eu-west-1a\--key-name=russ\--kms-key-arn="arn:aws:kms:eu-west-1:687011238589:key/2d54175d-41e1-4865-ac57-b3c40d0c4c3f"这将创建一个名为cluster.yaml的文件,这将是我们配置的基础: 接下来,我们需要创建将被我们的Kubernetes集群使用的证书。要做到这一点,请运行以下命令: 接下来,我们需要生成AWSCloudFormation模板。要做到这一点,请运行以下命令: $kube-awsrenderstack这将在名为stack-templates的文件夹中创建模板: 在您的工作目录中运行ls应该会显示已创建的几个文件和文件夹: 最后,我们可以运行以下命令来验证并上传文件到我们的S3存储桶,记得用您自己的存储桶名称更新命令: $kube-awsvalidate--s3-uris3://kube-aws-russ/kube-aws-cluster现在我们可以启动我们的集群。要做到这一点,只需运行以下命令,确保您更新存储桶名称: $kube-awsup--s3-uris3://kube-aws-russ/kube-aws-cluster这将开始使用AWSCloudFormation工具启动我们的集群: 这个过程将需要几分钟;您可以在AWS控制台的命令行上查看其进度。要在控制台中查看它,请转到服务菜单并选择CloudFormation。一旦打开,您应该会看到列出的一些堆栈;选择其中一个,然后单击事件选项卡: 从事件和资源选项卡中可以看出,后台有很多事情正在进行。有:IAM角色、VPC和网络、EC2实例、负载均衡器、DNS更新、自动缩放组等正在创建。 一旦完成,您应该会看到三个CloudFormation堆栈,一个主要的称为kube-aws-cluster,另外两个嵌套堆栈,一个称为kube-aws-cluster-Controlplane,另一个称为kube-aws-cluster-Nodepool1。两个嵌套堆栈都将在其名称后附加一个唯一的ID。您将在命令行上收到集群已启动的确认: 在我们的工作目录中运行以下命令将列出AWSKubernetes集群中的节点: 要启动商店,我们需要从工作目录中运行以下命令: $kubectl--kubeconfig=kubeconfig-nsock-shopgetpods等待每个pod获得运行状态,如下截图所示: 然后,我们应该能够访问我们的应用程序。要做到这一点,我们需要将其暴露给互联网。由于我们的集群在AWS中,我们可以使用以下命令启动一个弹性负载均衡器,并让它指向我们的应用程序: $kubectl--kubeconfig=kubeconfig-nsock-shopexposedeploymentfront-end--type=LoadBalancer--name=front-end-lb要获取有关我们的负载均衡器的信息,我们可以运行以下命令: 正如您所见,应用程序正在端口8079上暴露,但我们无法完全看到弹性负载均衡器的URL。要获得这个,我们可以运行以下命令: 输入您的URL应该显示以下页面: 要删除SockShop应用程序,只需运行: $kubectl--kubeconfig=kubeconfigdeletenamespacesock-shop这将删除我们创建的所有pod、服务和弹性负载均衡器。 让我们不要忘记,当集群正在运行时,它会花费我们的钱。要删除集群和CloudFormation脚本创建的所有服务,请运行以下命令: 我们还需要删除我们创建的S3存储桶和KMS;要做到这一点,请运行以下命令: $awss3rbs3://kube-aws-russ--force$awskms--region=eu-west-1disable-key--key-id2d54175d-41e1-4865-ac57-b3c40d0c4c3f您可以从您在本节早期创建KMS时所做的备注中找到--key-id。 在第一章中,《无服务器景观》,我们看了微软AzureFunctions;然而,我们没有进展到比Azureweb界面更多的地方来启动我们的Function。要使用Azure容器服务(AKS),我们需要安装Azure命令行客户端。 值得一提的是,AKS目前不支持Windows10PowerShellAzure工具。但是,如果您使用Windows,不用担心,因为命令行客户端的Linux版本可以通过Azureweb界面获得。 Azure命令行工具可以通过macOSHighSierra上的Homebrew获得,这样安装就像运行以下两个命令一样简单: $brewupdate$brewinstallazure-cliUbuntu17.04用户可以运行以下命令: 现在我们已经安装并连接到我们的账户的命令行工具,我们可以启动我们的Kubernetes集群。 首先,我们需要注册AKS服务。要做到这一点,运行以下命令: $azprovidershow-nMicrosoft.ContainerService一旦看到registrationState为Registered,您就可以开始了。要启动集群,我们首先需要创建一个资源组,然后创建集群。目前,AKS在ukwest或westus2都可用: $azgroupcreate--nameKubeResourceGroup--locationukwest$azakscreate--resource-groupKubeResourceGroup--nameAzureKubeCluster--agent-count1--generate-ssh-keys一旦您的集群启动,您可以运行以下命令配置您的本地kubectl副本以对集群进行身份验证: $azaksget-credentials--resource-groupKubeResourceGroup--nameAzureKubeCluster最后,您现在可以运行以下命令,开始与您的集群进行交互,就像与任何其他Kubernetes集群一样: 您会注意到我们只有一个节点;我们可以通过运行以下命令添加另外两个节点: 您应该能够在AzureWeb界面中看到所有已启动的资源: 现在我们的集群中有三个节点,让我们启动SockShopdemo应用程序。 这些命令与我们之前运行的命令略有不同,因为我们不必为kubectl提供配置文件: $kubectl-nsock-shopgetpods一旦所有的pod都在运行,您可以通过运行以下命令暴露应用程序: 要删除应用程序,我只需要运行: $azaksdelete--resource-groupKubeResourceGroup--nameAzureKubeCluster$azgroupdelete--nameKubeResourceGroup删除后,转到AzureWeb界面,并手动删除任何其他剩余的资源/服务。我们接下来要看的下一个和最后一个公共云是GoogleCloud。 所有三个操作系统都有安装程序。如果您使用的是macOSHighSierra,则可以使用Homebrew和Cask通过运行以下命令安装GoogleCloudSDK: $brewcaskinstallgoogle-cloud-sdkWindows10专业版用户可以使用Chocolatey并运行以下命令: $chocoinstallgcloudsdk最后,Ubuntu17.04用户需要运行以下命令: 回到您的终端,现在应该会提示您创建一个项目。出于测试目的,请回答是(y)并输入一个项目名称。这个项目名称必须对您来说是唯一的,所以可能需要尝试几次。如果一开始失败,您可以使用以下命令: $gcloudprojectscreateruss-kubernetes-cluster如您所见,我的项目名为russ-kubernetes-cluster。您应该在命令中引用您自己的项目名称。最后的步骤是将我们的新项目设置为默认项目以及设置区域。我使用了以下命令: $gcloudconfigsetprojectruss-kubernetes-cluster$gcloudconfigsetcompute/zoneus-central1-b现在我们已经安装了命令行工具,我们可以继续启动我们的集群。 您可以使用单个命令启动集群。以下命令将启动一个名为kube-cluster的集群: $gcloudcontainerclusterscreatekube-cluster当您第一次运行该命令时,可能会遇到一个错误,指出您的项目未启用Google容器API: 您可以通过按照错误中给出的链接并按照屏幕上的说明启用API来纠正此错误。如果您的项目没有与之关联的计费,您可能还会遇到错误: 一旦您启用了API并将您的项目链接到一个计费账户,您应该能够重新运行以下命令: 如您所见,kubectl的配置已经自动更新,这意味着我们可以运行以下命令来检查我们是否可以与新的集群通信: 您还应该能够在GoogleCloud网络界面的容器引擎部分看到您的集群: 现在我们的集群已经运行起来了,让我们再次启动SockShop应用程序。 与Azure一样,这次也不需要提供配置文件,所以我们只需要运行以下命令: 此外,在GoogleCloud网络界面中,单击发现与负载平衡还应该显示我们创建的负载均衡器: 单击界面中的链接,或将您的URL粘贴到浏览器中,应该会显示您熟悉的商店前台: 运行以下命令应该删除SockShop应用程序: $kubectldeletenamespacesock-shop运行Kubeless在删除GoogleCloud三节点Kubernetes集群之前,让我们快速回顾一下Kubeless。要部署Kubeless,请运行以下命令: $kubectlgetpods-nkubeless$kubectlgetdeployment-nkubeless$kubectlgetstatefulset-nkubeless您还可以在GoogleCloud网络界面的Google容器引擎部分检查工作负载和发现与负载平衡。一旦Kubeless部署完成,返回到本书附带的存储库中的/Chapter04/hello-world文件夹,并运行以下命令部署测试函数: $kubectlgetfunctions$kubelessfunctionls您可以通过运行以下命令调用该函数: 此外,您可以使用以下命令公开该函数: $kubectlexposedeploymenthello--type=LoadBalancer--name=hello-lb一旦负载均衡器创建完成,您可以运行以下命令确认IP地址和端口: $kubectlgetserviceshello-lb一旦您知道IP地址和端口,您可以在浏览器中打开该函数,或者使用curl或HTTPie查看该函数: 现在我们已经使用SockShop应用程序测试了我们的集群,并部署了一个Kubeless函数,我们应该考虑终止我们的集群。 要删除集群,只需运行以下命令: $gcloudcontainerclustersdeletekube-cluster它会问你是否确定,回答是,一两分钟后,您的集群将被删除。再次,您应该在GoogleCloud网页界面上仔细检查您的集群是否已被正确删除,以免产生任何意外费用。 在本章中,我们看了四个云提供商。前两个,DigitalOcean和AWS,目前不支持原生的Kubernetes,因此我们使用kubeadm和kube-aws来启动和配置我们的集群。对于MicrosoftAzure和GoogleCloud,我们使用他们的命令行工具来启动他们原生支持的Kubernetes服务。我相信您会同意,在撰写本文时,这两项服务比我们看过的前两项要友好得多。 一旦集群运行起来,与Kubernetes交互是一个相当一致的体验。当我们发出诸如kubectlexpose的命令时,我们实际上并不需要为集群运行的位置做出任何让步:Kubernetes知道它在哪里运行,并使用提供商的原生服务来启动负载均衡器,而无需我们干预任何特殊设置或考虑。 您可能会想知道为什么我们没有在DigitalOcean上启动SockShop应用程序。由于机器的规格相当低,应用程序运行非常缓慢,并且DigitalOcean是我们看过的四个提供商中唯一一个不支持Kubernetes当前不支持提供商的原生负载均衡服务。我相信这将在未来几个月内得到纠正。 此外,您可能会感到惊讶,AWS上没有原生的Kubernetes经验。在撰写本文时是这种情况;然而,有传言称自从AWS加入了云原生基金会后,他们正在努力开发原生的Kubernetes服务。 在下一章中,我们将介绍ApacheOpenWhisk,这是最初由IBM开发的开源无服务器云平台。 在本章中,我们将看看ApacheOpenWhisk。虽然不严格是一个仅限于Kubernetes的项目,比如Kubeless和Fission(这些将在下一章中介绍),但它可以部署并利用Kubernetes。 我们将看三个主要主题: ApacheOpenWhisk概述 使用Vagrant在本地运行ApacheOpenWhisk 在Kubernetes上运行ApacheOpenWhisk 让我们首先了解更多关于OpenWhisk。 ApacheOpenWhisk是一个开源的无服务器云计算平台,旨在以与本书其他章节中涵盖的所有工具类似的方式工作。ApacheOpenWhisk最初是IBM公共云服务Bluemix的FunctionsasaService部分,现在仍然是。 它在2016年12月发布了普遍可用版本。随着宣布的新闻稿中有一句来自Santander集团平台工程和架构负责人LuisEnriquez的引用,他是IBMCloudFunctions的一位客户,Luis说: “微服务和容器正在改变我们构建应用程序的方式,但由于无服务器,我们可以进一步推动这种转变,OpenWhisk为我们提供了处理强烈任务和工作负载意外高峰的即时基础设施,并且是我们转向实时和事件驱动架构的关键构建块。” 你可能已经注意到,这听起来很像AWS和MicrosoftAzureFunctions的Lambda——IBM的服务与竞争对手的区别在于IBM已经将OpenWhisk提交给了Apache孵化器,这是所有外部开发项目成为Apache软件基金会努力的一部分的入口。 Apache软件基金会成立于1999年,是一个慈善组织,负责监督和管理超过350个开源软件项目的开发和管理,这是为了公共利益。 那么为什么IBM要这样做呢?嗯,IBM不仅是Apache软件基金会的金牌赞助商,将其FunctionsasaService提供开源化对他们来说是有意义的,因为它是唯一一个可以避免供应商锁定的公共云提供商,因为你可以在本地或自己的硬件或虚拟机上运行ApacheOpenWhisk。 这使您可以自由地在任何地方运行和部署ApacheOpenWhisk。但是,如果您想像Santander集团一样进行规模化运行,那么您可以选择在IBM支持的企业级公共云上运行它。 我们首先将研究在本地运行ApacheOpenWhisk。我们将通过使用VirtualBox和Vagrant来实现这一点。 在启动本地ApacheOpenWhisk服务器之前,我们需要安装由HashiCorp开发的Vagrant。我能描述Vagrant的最好方式是作为一个开源的虚拟机管理器,您可以使用易于遵循的文本配置文件编写机器配置。 安装Vagrant非常简单。在macOS10.13HighSierra上,我们可以使用Homebrew和Cask: $brewcaskinstallvagrant如果您正在运行Windows10专业版,您可以使用Chocolatey并运行以下命令: $chocoinstallvagrant最后,如果您正在运行Ubuntu17.04,您可以通过运行以下命令直接从Ubuntu核心存储库安装Vagrant: $sudoapt-getupdate$sudoapt-getinstallvagrant请注意,Ubuntu提供的版本可能会比使用Homebrew和Chocolatey安装的版本稍微滞后;但是对于我们的目的,这不应该造成任何问题。 您可以通过运行以下命令测试Vagrant安装: 所有这些都是使用以下配置定义的: Vagrant.configure("2")do|config|config.vm.box="ubuntu/xenial64"end如果您打开Vagrantfile,您会注意到有很多配置选项,比如RAM和CPU分配,网络和脚本,这些脚本在虚拟机成功启动后执行。您可以运行以下命令以SSH连接到Vagrant虚拟机: $vagrantssh如果您正在运行Windows10专业版,则需要安装SSH客户端。当您执行上述命令时,Vagrant将为您提供一些选项。 运行以下命令将关闭您的虚拟机并将其删除: 我还建议通过运行清除您的工作文件夹: $cd../$rm-rfvagrant-test现在我们已经安装了Vagrant,并且快速查看了如何启动和与虚拟机交互,我们现在可以使用它来启动我们自己的本地安装ApacheOpenWhisk。 正如我们已经提到的,ApacheOpenWhisk附带一个Vagrantfile,其中包含从头开始部署本地ApacheOpenWhisk安装的所有命令。要下载ApacheOpenWhisk存储库并部署虚拟机,请运行以下命令: 正如您所看到的,它只有将近200行,这与上一节中我们测试Vagrantfile的三行有很大不同。Vagrantfile使用bash脚本和Ansible的组合来启动、安装和配置我们的ApacheOpenWhisk虚拟机。 在过程结束时,它将执行一个基本的helloworld检查,如下控制台输出所示: 在我们继续之前,请注意以wskpropertyset命令开头的输出。我们将需要这个来配置本地客户端,接下来我们将看到如何安装。 由于您的本地安装使用自签名SSL证书,当在浏览器中打开时,您可能会收到警告。您需要接受这些警告才能继续访问该网站。此过程因浏览器而异,因此您需要按照屏幕上的提示进行操作。 要在macOS10.13HighSierra上安装客户端,我们只需要运行以下命令: 要在Windows10专业版上下载,请运行以下命令。我建议从IBM下载,以避免自签名SSL证书和PowerShell的问题。为此,首先以管理员用户身份打开PowerShell窗口。您可以通过从任务栏中的PowerShell菜单中选择以管理员身份运行来执行此操作。打开后,您应该看到您在C:\WINDOWS\system32文件夹中;如果不是,则运行以下命令: $wskhelp最后,在Ubuntu17.04上,您需要运行以下命令: $wskhelp现在我们已经安装了客户端,我们需要对我们的安装进行身份验证。为此,请运行您在上一节末尾做的笔记中的命令,减去--namespaceguest部分。对我来说,这个命令是这样的: $wskpropertyset--apihost192.168.33.13--auth`vagrantssh--catopenwhisk/ansible/files/auth.guest`如果您不是从启动机器的文件夹运行vagrantssh命令,该命令将失败,因为它将无法找到您的机器配置。现在,您的本地客户端已对本地安装的ApacheOpenWhisk进行了身份验证,我们可以通过运行以下命令执行与自动安装相同的helloworld命令: $wsk-iactioninvoke/whisk.system/utils/echo-pmessagehello--result这应该返回以下终端输出的消息hello: 现在我们有了本地客户端,我们可以尝试下载和执行另一个示例。 现在,我们可以部署一个更复杂的解决方案,而不仅仅是使用内置的echo实用程序返回消息。与我们之前使用的helloworld脚本类似,我们将部署一个使用Node.js编写的函数,该函数接受输入并将其显示回给我们。 首先,让我们创建一个工作目录: functionmain(args){varmsg="youdidn'ttellmewhoyouare."if(args.name){msg=`hello${args.name}!`}return{body:`
`}}现在我们有了要部署的函数,首先我们需要创建一个包,然后创建一个暴露给Web的操作:
$wsk-ipackagecreate/guest/demo$wsk-iactioncreate/guest/demo/hellohello.js--webtrue现在我们已经创建了包和操作,您的终端应该看起来像以下内容:
这意味着您可以使用浏览器在以下URL调用您的函数:
您应该会看到以下页面:
您可以通过在macOS或Ubuntu上使用HTTPie来查看更多信息,方法是运行以下命令:
您可以通过运行以下命令列出软件包和操作,并删除它们:
完成本地安装后,您可以运行以下命令来停止和销毁虚拟机:
$vagrantdestroy请记住,您必须在openwhisk/tools/vagrant/文件夹中运行此命令,否则Vagrant将无法找到您的虚拟机配置。
现在我们已经在本地安装并与ApacheOpenWhisk进行了交互,让我们看看如何在公共云中的Kubernetes上部署它。
现在我们知道如何与ApacheOpenWhisk进行交互以及其基本概念,我们可以考虑在Kubernetes集群之上部署一个副本。为此,我将通过运行以下命令在GoogleCloud中启动一个三节点集群:
$gcloudcontainerclusterscreatekube-cluster一旦集群运行起来,您可以通过运行以下命令来检查是否可以看到三个节点:
现在我们有了我们的Kubernetes,我们可以继续进行ApacheOpenWhisk的部署。
在开始部署之前,所有在Kubernetes上部署ApacheOpenWhisk所需的配置都可以在GitHub上找到,因此我们应该通过运行以下命令克隆存储库。
$kubectlcreatenamespaceopenwhisk现在我们可以通过启动CouchDB来开始我们的部署。
要部署CouchDB,请从openwhisk-kube文件夹内运行以下命令:
$kubectlapply-fkubernetes/couchdb/couchdb.yml这将启动一个使用couchdb.yml文件中定义的参数运行CouchDB的pod。您可以通过获取pod的名称来检查部署是否正常。您可以通过运行以下命令来执行此操作:
$kubectl-nopenwhiskgetpods一旦您获得了名称,对我来说是couchdb-1146267775-v0sdm,然后您可以运行以下命令,确保更新pod的名称为您自己的:
$kubectl-nopenwhisklogscouchdb-1146267775-v0sdm在日志输出的最后,您应该看到以下消息:
现在我们的CouchDBpod正在运行,我们可以继续下一个,即Redis。
要启动Redispod,我们只需要运行以下命令:
$kubectlapply-fkubernetes/redis/redis.ymlAPI网关接下来我们有API网关;通过运行以下命令来启动它:
$kubectlapply-fkubernetes/apigateway/apigateway.ymlZooKeeper现在我们可以使用以下命令启动ApacheZooKeeper:
$kubectlapply-fkubernetes/zookeeper/zookeeper.yml卡夫卡现在是时候启动另一个Apache项目,Kafka了:
$kubectlapply-fkubernetes/kafka/kafka.yml此时,我们应该仔细检查我们启动的所有pod是否正在运行。要做到这一点,请运行以下命令:
$kubectl-nopenwhiskgetpods您应该看到couchdb,redis,apigateway,zookeeper和kafka的pod,所有这些pod都在没有记录重启并且READY列中为1/1运行:
接下来是控制器。这与我们部署的其他pod略有不同,因为它是以有状态的方式部署的:
$kubectlapply-fkubernetes/controller/controller.yml您应该看到已创建了一个StatefulSet而不是一个部署。
再次部署的下一个pod将是一个StatefulSet而不是一个部署。在部署pod之前,我们需要对kubernetes/invoker/invoker.yml文件进行轻微更改。这是因为,默认情况下,OpenWhisk假定您正在运行Ubuntu作为基本操作系统,而GoogleCloud不是。
要做到这一点,请在您选择的文本编辑器中打开kubernetes/invoker/invoker.yml并删除以下代码块:
-name:apparmorhostPath:path:"/usr/lib/x86_64-linux-gnu/libapparmor.so.1"还有另一个关于apparmor的参考资料需要删除。这次是在文件底部:
-name:apparmormountPath:"/usr/lib/x86_64-linux-gnu/libapparmor.so.1"一旦删除了引用apparmor的两个代码块,您可以通过运行以下命令部署invoker:
部署的最后一部分是NGINX容器。对于这个容器,我们需要做更多的工作,因为我们需要为我们的集群生成证书。为了生成证书,我们需要使用OpenSSL。这在Windows机器上默认情况下不安装,因此您可以使用以下命令使用Chocolatey安装OpenSSL:
$chocoinstallopenssl.light一旦安装了OpenSSL,您可以通过运行以下命令生成证书:
$mkdir-pcerts$opensslreq-x509-newkeyrsa:2048-keyoutcerts/key.pem-outcerts/cert.pem-nodes-subj"/CN=localhost"-days365一旦我们有了证书,我们需要使用kubernetes/nginx中的nginx.conf文件创建一个configmap。为此,请运行以下命令:
$kubectl-nopenwhiskcreateconfigmapnginx--from-file=kubernetes/nginx/nginx.conf现在我们需要上传生成的证书和密钥作为secret:
$kubectl-nopenwhiskcreatesecrettlsnginx--cert=certs/cert.pem--key=certs/key.pem一旦它们被上传,我们可以通过运行以下命令启动NGINXpod:
现在我们已经部署了所有的pod,您应该使用以下命令再次检查它们是否都在运行:
正如您所看到的,一切都在运行。只要数量不增加,您可以忽略任何重启。
现在我们已经部署了所有的pod,我们可以开始与我们的部署进行交互。首先,我们需要找出NGINXpod的外部IP地址。您可以通过运行以下命令找到有关pod的信息:
$kubectl-nopenwhiskdescribeservicenginx这是输出:
正如您所看到的,虽然端口是暴露的,但它们只在节点本身上暴露。由于节点位于私有地址上,我们将无法从本地客户端访问它们。要在外部暴露端口,我们需要创建一个负载均衡服务,运行以下命令来执行此操作:
$kubectl-nopenwhiskexposeservicenginx--type=LoadBalancer--name=front-end这将启动一个负载均衡器并暴露三个端口:80、443和8443。您可以通过运行以下命令找到外部IP地址的详细信息:
$kubectl-nopenwhiskdescribeservicefront-end在输出中,您会找到一行,上面写着LoadBalancerIngress,后面跟着一个IP地址:
正如您从先前显示的示例输出中看到的,我有一个IP地址35.188.204.73。这将被用作我与之交互的API端点。
现在我们已经获得了安装的IP地址,我们可以继续通过运行以下命令来配置认证令牌,确保您使用自己安装的IP地址进行更新:
这与前一节中的helloworld完全相同,所以我不会详细介绍。只需切换到您拥有hello.js文件的文件夹,并运行以下命令:
$wsk-ipackagecreate/guest/demo$wsk-iactioncreate/guest/demo/hellohello.js--webtrue一旦您运行了创建包和操作的命令,您将能够访问URL。对我来说,它是以下内容:
这显示了我们期望看到的页面:
再次,我们可以通过运行HTTPie来看到更多:
正如您所看到的,一旦您使用提供的文件部署了ApacheOpenWhisk,使用它是一个非常一致的体验。
在完成本章之前,我们应该删除我们的Kubernetes集群。要做到这一点,请运行以下命令:
在本章中,我们稍微偏离了目标,看了一下ApacheOpenWhisk。我们使用标准虚拟机部署了一个本地副本,然后我们转向部署到在GoogleCloud上运行的Kubernetes集群。
正如您所看到的,一旦部署完成,与ApacheOpenWhisk的交互是一致的体验,我们能够在两个安装中部署我们简单的hello-world应用程序,而无需进行任何修改。
虽然Kubernetes对ApacheOpenWhisk的支持仍处于起步阶段,但我们的偏离表明,不仅是为Kubernetes设计的框架,就像我们在前几章中看到的工具一样,它们将在Kubernetes之上运行,并提供一致的体验,而无需将您锁定在单一供应商或技术中。
在下一章中,我们将看到可能是最成熟的Kubernetes函数作为服务提供:Fission。
接下来我们将看一下Fission。Fission是一个快速增长的,基于Kubernetes的无服务器框架,而且在我们之前章节中看到的技术中,可能是最多才多艺的。在本章中,我们将涵盖:
谁构建了Fission?
安装先决条件
在本地安装、配置和运行Fission
命令概述
在云中安装、配置和运行Fission
部署一些示例Fission应用程序
到本章结束时,我们将在两个不同的目标环境中安装Fission,并且还将启动多个应用程序。
Fission是由Platform9开发的开源无服务器应用程序。它旨在在Kubernetes之上运行,并利用一些核心的Kubernetes功能。Platform9是一家托管服务提供商,其核心业务是部署、管理和支持专门从事OpenStack和Kubernetes的开源云。
OpenStack是一组开源组件,构成了一个完全功能的基础设施即服务产品。它提供计算、网络、块存储、对象存储、编排,甚至容器服务等功能。
该项目的目标是为多个不同的硬件供应商提供支持,从普通的x86硬件到专门的存储解决方案,使最终用户能够构建自己的AWS和MicrosoftAzure风格的产品。
随着AWSLambda和AzureFunctions等服务成熟到现在几乎在大多数企业中都很普遍,Platform9看到了提供自己的函数即服务的机会。
作为一家专门从事复杂开源解决方案的公司,他们为他们向社区贡献自己的工作是有意义的,因此他们以Apache许可证发布了Fission。
这可能看起来像一个奇怪的决定。然而,就像我们在上一章中介绍的OpenWhisk一样,Platform9为他们的客户以及任何想要开始部署函数即服务(FaaS)的人提供了一个坚实的基础来构建他们的应用程序。他们不仅给了人们在任何地方部署他们的工作负载的自由,还能够为安装和Fission平台提供支持服务。
Helm是Kubernetes的一个包管理器,是CloudNativeComputingFoundation的一部分,Bitnami、Google、Microsoft和Helm社区都为其开发做出了贡献。
要在macOSHighSierra上安装Helm,我们可以使用Homebrew;只需运行:
$brewinstallkubernetes-helm如果您正在运行UbuntuLinux,则可以使用安装脚本下载并安装Helm:
安装Helm的下一步需要您拥有一个运行中的Kubernetes集群,因为这是它的启动位置。我将在本章后面包括安装Helm的服务器组件Tiller的说明。
我们需要安装的最后一个命令行工具是Fission本身的工具。您可以通过在macOSHighSierra上运行以下命令来安装它:
运行以下命令应该显示当前安装的版本:
如前所述,我们还没有安装Tiller,因此我们可以安全地忽略关于无法连接到它的错误。
现在我们已经安装了先决条件,我们可以开始创建我们的第一个函数。为此,我们将使用Minikube。要启动单节点集群,我们只需要运行以下命令:
$minikubestart$kubectlgetnodes这应该启动您的Minikube集群,并确认您的本地版本已重新配置以与其通信:
一旦我们的集群运行并且可访问,我们需要通过安装Tiller来完成Helm安装。要做到这一点,我们需要运行以下命令:
$helminit您应该会看到类似以下消息:
Helm现在已配置好,我们可以使用它来部署Fission的远程组件。可以通过运行以下命令来完成:
Helm的输出非常详细。它将为您提供它创建的所有内容的概述,以及开发人员包含的任何附加说明。
输出的这部分包含了部署的基本细节:
NAME:lopsided-foxLASTDEPLOYED:SatDec910:52:192017NAMESPACE:fissionSTATUS:DEPLOYED接下来,我们会得到有关在Kubernetes中部署了什么的信息,从服务账户开始。这些提供运行pod的身份服务。这些允许Fission的各个组件与Kubernetes进行接口交互:
==>v1/ServiceAccountNAMESECRETSAGEfission-builder11mfission-fetcher11mfission-svc11m然后是绑定。这些为集群提供基于角色的身份验证(RBAC):
==>v1beta1/ClusterRoleBindingNAMEAGEfission-builder-crd1mfission-crd1mfission-fetcher-crd1m接下来是服务本身:
==>v1/ServiceNAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGEpoolmgrClusterIP10.0.0.134
==>v1beta1/DeploymentNAME.DESIREDCURRENTUP-TO-DATEAVAILABLEAGEtimer11111mpoolmgr11111minfluxdb11111mnats-streaming11111mcontroller11111mmqtrigger11111mrouter11101mstoragesvc11101mkubewatcher11111mbuildermgr11101m接下来,我们有了部署和服务的pod:
==>v1/Pod(related)NAMEREADYSTATUSRESTARTSAGElogger-zp65r1/1Running01mtimer-57f75c486f-9ktbk1/1Running21mpoolmgr-69fcff7d7-hbq461/1Running11minfluxdb-c5c6cfd86-wkwrs1/1Running01mnats-streaming-85b9898784-h6j2v1/1Running01mcontroller-5f964bc987-mmfrx1/1Running01mmqtrigger-c85dd79f7-vj5p71/1Running01mrouter-7cfff6794b-gn5pw0/1ContainerCreating01mstoragesvc-58d5c8f6-bnqc70/1ContainerCreating01mkubewatcher-6d784b9987-5wwhv1/1Running01mbuildermgr-7ff69c8bb-pvtbx0/1ContainerCreating01m然后我们有了命名空间:
==>v1/NamespaceNAME.STATUSAGEfission-builderActive1mfission-functionActive1m现在我们有了秘密。这些只是用于正在使用的数据库:
==>v1/SecretNAMETYPEDATAAGEinfluxdbOpaque21m我们接近尾声了:持久存储索赔。您可以看到,由于我们在本地启动,它只是使用VM上的一个文件夹,而不是创建外部存储:
==>v1/PersistentVolumeClaimNAME.STATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGEfission-storage-pvcBoundpvc-082cf8d5-dccf-11e7-bfe6-080027e101f58GiRWOstandard1m现在我们有了角色绑定:
==>v1beta1/RoleBindingNAMEAGEfission-function-admin1mfission-admin1m最后,我们有了守护进程集:
==>v1beta1/DaemonSetNAMEDESIREDCURRENTREADYUP-TO-DATEAVAILABLENODESELECTORAGElogger11111
笔记分为三个部分;第一部分提供了如何安装Fission命令行客户端的说明。由于我们已经在本章的前一部分中涵盖了这一点,我们可以忽略这一步。
接下来,在第二部分中,我们得到了关于需要设置的环境变量的说明,以便我们的本地Fission客户端可以与我们的Fission安装进行交互。要设置这些变量,请运行以下命令:
第三部分包含了一步一步的说明,说明如何运行一个helloworld函数;让我们现在来运行这些步骤。
首先,我们需要创建一个环境。为此,我们使用以下命令:
现在我们已经创建了环境,我们需要一个要部署的函数。运行以下命令(仅适用于macOS和Linux)来下载helloworld示例:
module.exports=asyncfunction(context){return{status:200,body:"Hello,world!\n"};}如您所见,这与我们在早期章节中运行的示例并没有太大不同。现在我们已经下载了一个函数,我们可以使用以下命令部署它:
$fissionfunctioncreate--namehello--envnodejs--code/tmp/hello.js我们快要完成了;最后一步是创建一个到我们函数的路由。要做到这一点,使用以下命令:
$fissionroutecreate--methodGET--url/hello--functionhello现在我们应该能够通过发出HTTP请求来调用我们的函数。您可以使用以下命令中的任一个来触发我们的函数:
现在我们已经有了一个基本的应用程序在运行,让我们来创建一些更复杂的东西。Fission附带了一个演示应用程序,充当留言板。您可以在伴随本书的GitHub存储库中的/Chapter08/guestbook/文件夹中找到我们将要部署的文件。
$kubectlcreate-fredis.yaml您可以从以下截图中看到,这创建了一个namespace、deployment和service。
现在我们需要创建一个环境来启动我们的函数。由于应用程序是用Python编写的,让我们运行以下命令:
$fissionenvcreate--namepython--imagefission/python-env前面命令的输出显示在以下截图中:
$fissionfunctioncreate--nameguestbook-get--envpython--codeget.py--url/guestbook--methodGET$fissionfunctioncreate--nameguestbook-add--envpython--codeadd.py--url/guestbook--methodPOST前面命令的输出可以在以下截图中看到:
您会注意到,用于添加函数的命令与我们在上一节中用于启动helloworld示例的命令有些不同。在之前的示例中,我们既添加了函数,又创建了路由。您可能还注意到,虽然我们创建了两个函数,但它们都绑定到了相同的路由/guestbook。现在不讨论这个问题,让我们启动应用程序并与之交互。
要打开留言板,请运行以下命令:
如果收到内部服务器错误,请不要担心,只需刷新页面并重新提交。查看页面的HTML源代码,您可能会注意到表单操作配置为将POST提交到/guestbook:
每当您的浏览器请求页面时,它都会发送一个GET请求。在我们的情况下,所有对/guestbook的GET请求都被路由到guestbook-get函数,这是get.py代码:
##HandlesGET/guestbook--returnsalistofitemsintheguestbook#withaformtoaddmore.#fromflaskimportcurrent_app,escapeimportredis#Connecttoredis.Thisisrunonlywhenthisfileisloaded;as#longasthepodisalive,theconnectionisreused.redisConnection=redis.StrictRedis(host='redis.guestbook',port=6379,db=0)defmain():messages=redisConnection.lrange('guestbook',0,-1)items=[("
- %s
Guestbook
%s