Kubernetes-无服务器应用手册-全-

Kubernetes 无服务器应用手册(全)

原文:zh.annas-archive.org/md5/8919C4FA258132C529A8BB4FA8603A2F

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

Kubernetes 是近年来突出的技术之一;它已被所有主要公共云提供商采用为容器集群和编排平台,并迅速成为行业标准。

再加上 Kubernetes 是开源的,您就拥有了在多个公共和私有提供商上托管自己的平台即服务或 PaaS 的完美基础;您甚至可以在笔记本电脑上运行它,并且由于其设计,您将在所有平台上获得一致的体验。

它的设计也使其成为运行无服务器函数的理想平台。在本书中,我们将研究几个可以部署在 Kubernetes 上并与之集成的平台,这意味着我们不仅拥有 PaaS,还有一个强大的 FaaS 平台在您的 Kubernetes 环境中运行。

本书适合对象

本书主要面向运维工程师、云架构师和开发人员,他们希望在 Kubernetes 集群上托管他们的无服务器函数。

本书内容

第一章,无服务器景观,解释了什么是无服务器。此外,我们将在公共云上使用 AWS Lambda 和 Azure Functions 来运行无服务器函数,获得一些实际经验。

第二章,Kubernetes 简介,讨论了 Kubernetes 是什么,它解决了什么问题,并且还回顾了它的背景,从谷歌的内部工程工具到开源强大工具。

第三章,在本地安装 Kubernetes,解释了如何通过实践经验来使用 Kubernetes。我们将使用 Minikube 安装本地单节点 Kubernetes 集群,并使用命令行客户端与其交互。

第四章,介绍 Kubeless 函数,解释了在本地运行 Kubernetes 后如何使用 Kubeless 启动第一个无服务器函数。

第五章,使用 Funktion 进行无服务器应用,解释了使用 Funktion 来调用无服务器函数的一种略有不同的方法。

第六章,在云中安装 Kubernetes,介绍了在 DigitalOcean、AWS、Google Cloud 和 Microsoft Azure 上启动集群的过程,以及在本地使用 Kubernetes 进行一些实践经验后的操作。

第七章,Apache OpenWhisk 和 Kubernetes,解释了如何在我们新推出的云 Kubernetes 集群上启动、配置和使用最初由 IBM 开发的无服务器平台 Apache OpenWhisk。

第八章,使用 Fission 启动应用程序,介绍了部署 Fission 的过程,这是 Kubernetes 的流行无服务器框架,并附带了一些示例函数。

第九章,了解 OpenFaaS,介绍了 OpenFaaS。虽然它首先是一个用于 Docker 的函数即服务框架,但也可以部署在 Kubernetes 之上。

第十章,无服务器考虑因素,讨论了安全最佳实践以及如何监视您的 Kubernetes 集群。

第十一章,运行无服务器工作负载,解释了 Kubernetes 生态系统的快速发展以及您如何跟上。我们还讨论了您应该使用哪些工具,以及为什么您希望将无服务器函数部署在 Kubernetes 上。

为了充分利用本书

操作系统

  • macOS High Sierra

  • Ubuntu 17.04

  • Windows 10 专业版

软件

在本书中,我们将安装几个命令行工具;每个工具都将在各章节中提供安装说明和其要求的详细信息。请注意,虽然提供了 Windows 系统的说明,但我们将主要使用最初设计为在 Linux/Unix 系统上运行的工具,如 Ubuntu 17.04 和 macOS High Sierra,并且本书将偏向这些系统。虽然在撰写时已尽最大努力验证这些工具在基于 Windows 的系统上的运行情况,但由于一些工具是实验性构建的,我们无法保证它们在更新的系统上仍然能够正常工作,因此,我建议使用 Linux 或 Unix 系统。

硬件:

  • Windows 10 专业版和 Ubuntu 17.04 系统要求

  • 使用 2011 年或之后推出的处理器(CPU),核心速度为 1.3 GHz 或更快,除了基于LlanoBobcat微架构的英特尔 Atom 处理器或 AMD 处理器

  • 最低 4 GB RAM,建议使用 8 GB RAM 或更多

  • Apple Mac 系统要求

  • iMac:2009 年底或更新

  • MacBook/MacBook(Retina):2009 年底或更新

  • MacBook Pro:2010 年中期或更新

  • MacBook Air:2010 年末或更新版本

  • Mac mini:2010 年中期或更新版本

  • Mac Pro:2010 年中期或更新版本

至少可以访问以下公共云服务之一

下载示例代码文件

您可以从www.packtpub.com的帐户中下载本书的示例代码文件。如果您在其他地方购买了本书,您可以访问www.packtpub.com/support并注册,文件将直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. www.packtpub.com登录或注册。

  2. 选择“支持”选项卡。

  3. 单击“代码下载和勘误”。

  4. 在搜索框中输入书名,然后按照屏幕上的说明操作。

下载文件后,请确保使用最新版本的以下软件解压缩文件夹:

  • WinRAR/7-Zip for Windows

  • Zipeg/iZip/UnRarX for Mac

  • 7-Zip/PeaZip for Linux

本书的代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/Kubernetes-for-Serverless-Applications。我们还有其他丰富的图书和视频的代码包可供下载,网址为github.com/PacktPublishing/。请查看!

下载彩色图像

我们还提供了一个 PDF 文件,其中包含本书中使用的屏幕截图/图表的彩色图像。您可以在这里下载:www.packtpub.com/sites/default/files/downloads/KubernetesforServerlessApplications_ColorImages.pdf

使用的约定

本书中使用了许多文本约定。

CodeInText:指示文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。这是一个例子:“这包含一个名为index.html的单个文件。”

一段代码设置如下:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: cli-hello-world
  labels:
    app: nginx

当我们希望引起您对代码块的特定部分的注意时,相关的行或项目以粗体设置:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: cli-hello-world
  labels:
    app: nginx

任何命令行输入或输出都将按照以下方式编写:

$ brew cask install minikube

粗体:表示一个新术语,一个重要的词,或者你在屏幕上看到的词。例如,菜单或对话框中的单词会在文本中以这种方式出现。这是一个例子:“在页面底部,您将有一个按钮,可以让您为您的帐户创建访问令牌和访问令牌密钥。”

警告或重要提示会以这种方式出现。提示和技巧会以这种方式出现。

第一章:无服务器景观

欢迎来到《用于无服务器应用的 Kubernetes》的第一章。在本章中,我们将讨论以下内容:

  • 我们所说的无服务器和函数作为服务是什么意思?

  • 有哪些服务?

  • 亚马逊网络服务的 Lambda 的一个例子。

  • Azure Functions 的一个例子

  • 使用无服务器工具包

  • 我们可以使用无服务器和函数作为服务解决什么问题?

我认为重要的是我们首先要解决房间里的大象,那就是无服务器这个术语。

无服务器和函数作为服务

当你对某人说无服务器时,他们首先得出的结论是你在没有任何服务器的情况下运行你的代码。

如果你使用我们将在本章后面讨论的公共云服务之一,这可能是一个相当合理的结论。然而,当在你自己的环境中运行时,你无法避免必须在某种服务器上运行。

在我们讨论无服务器和函数作为服务的含义之前,我们应该讨论我们是如何到达这里的。和我一起工作的人无疑会告诉你,我经常使用“宠物与牛群”这个类比,因为这是一个很容易解释现代云基础设施与更传统方法之间差异的方式。

宠物、牛群、鸡、昆虫和雪花

我第一次接触“宠物与牛群”这个类比是在 2012 年,当时 Randy Bias 发布了一份幻灯片。这张幻灯片是在 Randy Bias 在云扩展会议上关于开放和可扩展云的架构的演讲中使用的。在演讲的最后,他介绍了宠物与牛群的概念,Randy 将其归因于当时在微软担任工程师的 Bill Baker。

幻灯片主要讨论的是扩展而不是升级;让我们更详细地讨论一下,并讨论自五年前首次进行演示以来所做的一些补充。

Randy 的幻灯片可以在www.slideshare.net/randybias/architectures-for-open-and-scalable-clouds找到。

宠物

宠物通常是我们作为系统管理员花时间照顾的东西。它们是传统的裸金属服务器或虚拟机:

  • 我们像给宠物起名字一样给每台服务器起名字。例如,app-server01.domain.comdatabase-server01.domain.com

  • 当我们的宠物生病时,你会把它们带到兽医那里。这很像你作为系统管理员重新启动服务器,检查日志,并更换服务器的故障组件,以确保它正常运行。

  • 你多年来一直密切关注你的宠物,就像对待服务器一样。你监视问题,修补它们,备份它们,并确保它们有充分的文档记录。

运行宠物并没有太大问题。然而,你会发现你大部分的时间都花在照顾它们上——如果你有几十台服务器,这可能还可以接受,但如果你有几百台服务器,情况就开始变得难以管理了。

牛更能代表你应该在公共云中运行的实例类型,比如亚马逊网络服务AWS)或微软 Azure,在那里你启用了自动扩展。

  • 你的牛群里有很多牛,你不给它们起名字;相反,它们被编号和标记,这样你就可以追踪它们。在你的实例集群中,你也可能有太多实例需要命名,所以像牛一样,你给它们编号和标记。例如,一个实例可以被称为ip123067099123.domain.com,并标记为app-server

  • 当你的牛群中的一头生病时,你会射杀它,如果你的牛群需要,你会替换它。同样地,如果你集群中的一个实例开始出现问题,它会被自动终止并替换为一个副本。

  • 你不指望牛群中的牛能活得像宠物一样长久,同样地,你也不指望你的实例的正常运行时间能以年为单位。

  • 你的牛群生活在一个牧场里,你从远处观察它,就像你不监视集群中的单个实例一样;相反,你监视集群的整体健康状况。如果你的集群需要额外的资源,你会启动更多的实例,当你不再需要资源时,实例会被自动终止,使你回到期望的状态。

2015 年,Bernard Golden 在一篇名为《云计算:宠物、牛和鸡?》的博客文章中,将鸡引入到宠物与牛的比喻中。Bernard 建议将鸡作为描述容器的一个好术语,与宠物和牛并列:

  • 鸡比牛更有效率;你可以把更多的鸡放在你的牛群所占用的同样空间里。同样地,你可以在你的集群中放更多的容器,因为你可以在每个实例上启动多个容器。

  • 每只鸡在饲养时需要的资源比你的牧群成员少。同样,容器比实例需要的资源更少,它们只需要几秒钟就可以启动,并且可以配置为消耗更少的 CPU 和 RAM。

  • 鸡的寿命远低于你的牧群成员。虽然集群实例的正常运行时间可能是几小时到几天,但容器的寿命可能只有几分钟。

不幸的是,伯纳德的原始博客文章已经不再可用。然而,The New Stack 已经重新发布了一篇版本。你可以在thenewstack.io/pets-and-cattle-symbolize-servers-so-what-does-that-make-containers-chickens/找到重新发布的版本。

昆虫

与动物主题保持一致,埃里克·约翰逊为 RackSpace 撰写了一篇介绍昆虫的博客文章。这个术语被用来描述无服务器和函数即服务。

昆虫的寿命远低于鸡;事实上,一些昆虫只有几小时的寿命。这符合无服务器和函数即服务的特点,因为它们的寿命只有几秒钟。

在本章的后面,我们将看一下来自 AWS 和微软 Azure 的公共云服务,这些服务的计费是以毫秒为单位,而不是小时或分钟。

埃里克的博客文章可以在blog.rackspace.com/pets-cattle-and-nowinsects/找到。

雪花

大约在兰迪·拜斯提到宠物与牛群的讲话时,马丁·福勒写了一篇名为SnowflakeServer的博客文章。这篇文章描述了每个系统管理员的噩梦:

  • 每片雪花都是独一无二的,无法复制。就像办公室里那台由几年前离开的那个人建造而没有记录的服务器一样。

  • 雪花是脆弱的。就像那台服务器一样——当你不得不登录诊断问题时,你会很害怕,你绝对不会想重新启动它,因为它可能永远不会再次启动。

马丁的帖子可以在martinfowler.com/bliki/SnowflakeServer.html找到。

总结

一旦我解释了宠物、牛群、鸡、昆虫和雪花,我总结道:

“那些拥有宠物的组织正在慢慢将他们的基础设施变得更像。那些已经将他们的基础设施运行为的人正在向转变,以充分利用他们的资源。那些运行的人将会考虑将他们的应用程序转变为昆虫,通过将他们的应用程序完全解耦成可单独执行的组件来完成。”

最后我这样说:

“没有人想要或者应该运行雪花。”

在这本书中,我们将讨论昆虫,我会假设你对覆盖牛和鸡的服务和概念有一些了解。

无服务器和昆虫

如前所述,使用“无服务器”这个词会给人一种不需要服务器的印象。无服务器是用来描述一种执行模型的术语。

在执行这个模型时,作为最终用户的你不需要担心你的代码在哪台服务器上执行,因为所有的决策都是由抽象出来的,与你无关——这并不意味着你真的不需要任何服务器。

现在有一些公共云服务提供了如此多的服务器管理抽象,以至于可以编写一个不依赖于任何用户部署服务的应用程序,并且云提供商将管理执行代码所需的计算资源。

通常,这些服务,我们将在下一节中看到,是按每秒执行代码所使用的资源计费的。

那么这个解释如何与昆虫类比呢?

假设我有一个网站,允许用户上传照片。一旦照片上传,它们就会被裁剪,创建几种不同的尺寸,用于在网站上显示缩略图和移动优化版本。

在宠物和牛的世界中,这将由一个 24/7 开机等待用户上传图像的服务器来处理。现在这台服务器可能不只是执行这一个功能;然而,如果几个用户都决定上传十几张照片,那么这将在执行该功能的服务器上引起负载问题。

我们可以采用鸡的方法,跨多台主机运行多个容器来分发负载。然而,这些容器很可能也会全天候运行;它们将会监视上传以进行处理。这种方法可以让我们水平扩展容器的数量来处理请求的激增。

使用昆虫的方法,我们根本不需要运行任何服务。相反,函数应该由上传过程触发。一旦触发,函数将运行,保存处理过的图像,然后终止。作为开发人员,你不需要关心服务是如何被调用或在哪里执行的,只要最终得到处理过的图像即可。

公共云服务

在我们深入探讨本书的核心主题并开始使用 Kubernetes 之前,我们应该看看其他选择;毕竟,我们将在接下来的章节中涵盖的服务几乎都是基于这些服务的。

三大主要的公共云提供商都提供无服务器服务:

这些服务都支持几种不同的代码框架。对于本书的目的,我们不会过多地研究代码框架,因为使用这些框架是一个基于你的代码的设计决策。

我们将研究这两种服务,AWS 的 Lambda 和微软 Azure 的 Functions。

AWS Lambda

我们要看的第一个服务是 AWS 的 AWS Lambda。该服务的标语非常简单:

“无需考虑服务器即可运行代码。”

现在,那些之前使用过 AWS 的人可能会认为这个标语听起来很像 AWS 的弹性 Beanstalk 服务。该服务会检查你的代码库,然后以高度可扩展和冗余的配置部署它。通常,这是大多数人从宠物到牲畜的第一步,因为它抽象了 AWS 服务的配置,提供了可扩展性和高可用性。

在我们开始启动一个 hello world 示例之前,我们将需要一个 AWS 账户和其命令行工具安装。

先决条件

首先,您需要一个 AWS 账户。如果您没有账户,可以在aws.amazon.com/注册一个账户:

虽然单击“创建免费账户”,然后按照屏幕上的说明将为您提供 12 个月的免费访问多项服务,但您仍然需要提供信用卡或借记卡详细信息,并且可能会产生费用。

有关 AWS 免费套餐的更多信息,请参阅aws.amazon.com/free/。此页面让您了解 12 个月免费服务涵盖的实例大小和服务,以及其他服务的永久优惠,其中包括 AWS Lambda。

一旦您拥有 AWS 账户,您应该使用 AWS 身份和访问管理IAM)服务创建一个用户。该用户可以拥有管理员权限,您应该使用该用户访问 AWS 控制台和 API。

有关创建 IAM 用户的更多详细信息,请参阅以下页面:

不建议使用 AWS 根账户启动服务和访问 API;如果凭据落入错误的手中,您可能会失去对账户的所有访问权限。使用 IAM 而不是您的根账户,并且您还应该使用多因素身份验证锁定根账户,这意味着您将始终可以访问您的 AWS 账户。

最后一个先决条件是您需要访问 AWS 命令行客户端,我将使用 macOS,但该客户端也适用于 Linux 和 Windows。有关如何安装和配置 AWS 命令行客户端的信息,请参阅:

在配置 AWS CLI 时,请确保将默认区域配置为您将在 AWS Web 控制台中访问的区域,因为没有比在 CLI 中运行命令然后在 Web 控制台中看不到结果更令人困惑的事情了。

安装后,您可以通过运行以下命令来测试您是否可以从命令行客户端访问 AWS Lambda:

$ aws lambda list-functions

这应该返回一个空的函数列表,就像下面的截图中所示:

现在我们已经设置、创建并使用非根用户登录了帐户,并且已经安装和配置了 AWS CLI,我们可以开始启动我们的第一个无服务器函数了。

创建 Lambda 函数

在 AWS 控制台中,单击屏幕左上角的“服务”菜单,然后通过使用过滤框或单击列表中的服务来选择 Lambda。当您首次转到 AWS 控制台中的 Lambda 服务页面时,您将看到一个欢迎页面:

单击“创建函数”按钮将直接进入启动我们的第一个无服务器函数的过程。

创建函数有四个步骤;我们需要做的第一件事是选择一个蓝图:

对于基本的 hello world 函数,我们将使用一个名为hello-world-python的预构建模板;将其输入到过滤器中,您将看到两个结果,一个是 Python 2.7,另一个使用 Python 3.6:

选择hello-world-python,然后单击“导出”将为您提供下载用于函数的代码的选项,该代码位于lambda_function.py文件中,以及 Lambda 在第 3 步中使用的模板。这可以在template.yaml文件中找到。

代码本身非常基本,就像你想象的那样。它除了返回传递给它的值之外什么也不做。如果您没有跟随,lambda_function.py文件的内容如下:

from __future__ import print_function

import json

print('Loading function')

def lambda_handler(event, context):
  #print("Received event: " + json.dumps(event, indent=2))
  print("value1 = " + event['key1'])
  print("value2 = " + event['key2'])
  print("value3 = " + event['key3'])
  return event['key1'] # Echo back the first key value
  #raise Exception('Something went wrong')

template.yaml文件包含以下内容:

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: A starter AWS Lambda function.
Resources:
  helloworldpython:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: lambda_function.lambda_handler
      Runtime: python2.7
      CodeUri: .
      Description: A starter AWS Lambda function.
      MemorySize: 128
      Timeout: 3
      Role: !<tag:yaml.org,2002:js/undefined> ''

正如您所看到的,模板文件配置了Runtime和一些合理的MemorySizeTimeout值。

要继续到第 2 步,请单击函数名称,对我们来说是hello-world-python,您将进入页面,可以选择如何触发函数:

我们暂时不打算使用触发器,我们将在下一个启动的函数中更详细地了解这些内容;所以现在,请单击“下一步”。

第 3 步是我们配置函数的地方。这里有很多信息要输入,但幸运的是,我们需要输入的许多细节已经从我们之前查看的模板中预填充,如下截图所示:

我们需要输入的详细信息如下:带有的是必填项,斜体中的信息*是预填充的,可以保持不变。

以下列表显示了所有表单字段及其应输入的内容:

  • 基本信息:

  • 名称:myFirstFunction

  • 描述:一个起始的 AWS Lambda 函数

  • 运行时:Python 2.7

  • Lambda 函数代码:

  • 代码输入类型:这包含了函数的代码,无需编辑

  • 启用加密助手:不选中

  • 环境变量:留空

  • Lambda 函数处理程序和角色:

  • 处理程序:lambda_function.lambda_handler

  • 角色:保持选择“从模板创建新角色”

  • 角色名称:myFirstFunctionRole

  • 策略模板:我们不需要为此函数使用策略模板,保持空白

将标签和高级设置保持默认值。输入前述信息后,单击“下一步”按钮,进入第 4 步,这是函数创建之前的最后一步。

查看页面上的详细信息。如果您确认所有信息都已正确输入,请单击页面底部的“创建函数”按钮;如果需要更改任何信息,请单击“上一步”按钮。

几秒钟后,您将收到一条消息,确认您的函数已创建:

在上述截图中,有一个“测试”按钮。单击此按钮将允许您调用函数。在这里,您可以自定义发送到函数的值。如下截图所示,我已更改了key1key2的值:

编辑完输入后,点击“保存并测试”将存储您更新的输入,然后调用该函数:

点击执行结果消息中的“详细信息”将显示函数被调用的结果以及使用的资源:

START RequestId: 36b2103a-90bc-11e7-a32a-171ef5562e33 Version: $LATEST
value1 = hello world
value2 = this is my first serverless function
value3 = value3
END RequestId: 36b2103a-90bc-11e7-a32a-171ef5562e33

具有36b2103a-90bc-11e7-a32a-171ef5562e33 ID 的请求的报告如下:

  • 持续时间:0.26 毫秒

  • 计费持续时间:100 毫秒

  • 内存大小:128 MB

  • 最大内存使用:19 MB

如您所见,函数运行需要0.26 毫秒,我们被收取了最低持续时间100 毫秒。函数最多可以消耗128 MB的 RAM,但在执行过程中我们只使用了19 MB

返回到命令行,再次运行以下命令会显示我们的函数现在已列出:

$ aws lambda list-functions

上述命令的输出如下:

我们也可以通过运行以下命令从命令行调用我们的函数:

$ aws lambda invoke \
 --invocation-type RequestResponse \
 --function-name myFirstFunction \
 --log-type Tail \
 --payload '{"key1":"hello", "key2":"world", "key3":"again"}' \
 outputfile.txt 

如您从上述命令中所见,aws lambda invoke命令需要几个标志:

  • --invocation-type:有三种调用类型:

  • RequestResponse:这是默认选项;它发送请求,在我们的情况下在命令的--payload部分中定义。一旦请求被发出,客户端就会等待响应。

  • 事件:这会发送请求并触发事件。客户端不等待响应,而是会收到一个事件 ID。

  • DryRun:这会调用函数,但实际上不执行它——这在测试用于调用函数的详细信息是否具有正确的权限时非常有用。

  • --function-name:这是我们要调用的函数的名称。

  • --log-type:目前只有一个选项,Tail。这返回--payload的结果,这是我们要发送给函数的数据;通常这将是 JSON。

  • outputfile.txt:命令的最后部分定义了我们要存储命令输出的位置;在我们的情况下,这是一个名为outputfile.txt的文件,它被存储在当前工作目录中。

在从命令行调用命令时,您应该会得到以下结果:

返回到 AWS 控制台并保持在myFirstFunction页面上,点击“监控”将呈现有关函数的一些基本统计信息:

从前面的图表中可以看出,有关您的函数被调用的次数、所需时间以及是否存在任何错误的详细信息。

单击 CloudWatch 中的查看日志将打开一个列出myFirstFunction日志流的新标签页。单击日志流的名称将带您到一个页面,该页面会显示每次函数被调用的结果,包括在 AWS 控制台中进行测试以及从命令行客户端进行调用。

监控页面和日志在调试 Lambda 函数时非常有用。

微软 Azure Functions

接下来,我们将看一下微软的无服务器服务 Azure Functions。微软将这项服务描述为:

"Azure Functions 是一个解决方案,可以轻松在云中运行小段代码或“函数”。您可以仅编写您需要解决的问题的代码,而不必担心整个应用程序或运行它的基础架构。"

与 Lambda 一样,您的 Function 可以通过多种方式被调用。在这个快速演示中,我们将部署一个通过 HTTP 请求调用的 Function。

先决条件

您需要一个 Azure 账户来跟随这个示例。如果您没有账户,可以在azure.microsoft.com/上注册一个免费账户:

在撰写本文时,微软向所有新账户提供了 200 美元的 Azure 服务信用额度,就像 AWS 一样,有几项服务有免费套餐。

虽然您可以获得 200 美元的信用额度,但您仍需要提供信用卡详细信息以进行验证。有关免费套餐中的服务和限制的更多信息,请参阅azure.microsoft.com/en-gb/free/pricing-offers/

创建一个 Function 应用程序

我们将使用基于 Web 的控制面板来创建我们的第一个 Function 应用程序。一旦您拥有了账户,您应该会看到类似以下页面:

关于微软 Azure 控制面板的一件事是,它可以水平滚动,因此如果您在页面上迷失了方向,通常可以通过向右滚动找回需要的位置。

如前面的屏幕截图所示,有相当多的选项。要开始创建您的第一个函数,您应该在左侧菜单顶部单击“+新建”。

从这里,您将进入 Azure 市场。单击计算,然后在特色市场项目列表中,您应该看到函数应用程序。单击此处,您将进入一个表单,询问您想要创建的函数的一些基本信息:

  • 应用程序名称:随意命名;在我的案例中,我将其命名为russ-test-version。这必须是一个唯一的名称,如果您想要的应用程序名称已经被另一个用户使用,您将收到一条消息,告知您所选的应用程序名称不可用。

  • 订阅:选择要在其中启动您的函数的 Azure 订阅。

  • 资源组:在输入应用程序名称时,这将自动填充。

  • 托管计划:将其保留为默认选项。

  • 位置:选择离您最近的地区。

  • 存储:这将根据您提供的应用程序名称自动填充,为了我们的目的,请保留选择“创建新”。

  • 固定到仪表板:选中此项,因为这将使我们能够快速找到我们创建的函数。

如果您没有在您的帐户中跟随,我的完成表格看起来像下面的屏幕截图:

填写完表格后,单击表单底部的“创建”按钮,您将被带回到您的仪表板。您将收到一个通知,告知您的函数正在部署,如下图右侧的框中所示:

单击仪表板中的方框或顶部菜单中的通知(带有数字 1 的铃铛图标)将带您到概述页面;在这里,您可以查看部署的状态:

部署后,您应该有一个空的函数应用程序,可以准备将代码部署到其中:

要部署一些测试代码,您需要在左侧菜单中的函数旁边单击“+”图标;这将带您到以下页面:

选择 Webhook + API 和 CSharp 后,单击“创建此函数”;这将向您的函数应用程序添加以下代码:

using System.Net;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info("C# HTTP trigger function processed a request.");

    // parse query parameter
    string name = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    // Set name to query string or body data
    name = name ?? data?.name;

    return name == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass
        a name on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

这段代码简单地读取变量name,它通过 URL 传递,然后作为Hello <name>打印回给用户。

我们可以通过单击页面顶部的“运行”按钮来测试这一点。这将执行我们的函数,并为您提供输出和日志:

测试运行的日志如下:

2017-09-09T15:28:08 Welcome, you are now connected to log-streaming service.2017-09-09T15:29:07.145 Function started (Id=4db505c2-5a94-4ab4-8e12-c45d29e9cf9c)2017-09-09T15:29:07.145 C# HTTP trigger function processed a request.2017-09-09T15:29:07.176 Function completed (Success, Id=4db505c2-5a94-4ab4-8e12-c45d29e9cf9c, Duration=28ms)

您还可以通过单击左侧菜单中的“监视”来查看有关函数应用的更多信息。从以下屏幕截图中可以看出,我们有关于函数调用次数的详细信息,以及每次执行的状态和持续时间:

有关函数应用调用的更详细信息,您可以启用 Azure 应用程序洞察,并且有关此服务的更多信息,请参阅azure.microsoft.com/en-gb/services/application-insights/

能够在 Azure 仪表板的安全环境中进行测试是很好的,但是如何直接访问您的函数应用呢?

如果单击 HttpTriggerCSharp1,它将带您回到您的代码,在代码块上方,您将有一个按钮,上面写着“获取函数 URL”,单击此按钮将弹出一个包含 URL 的覆盖框。复制这个:

对我来说,URL 是:

https://russ-test-function.azurewebsites.net/api/HttpTriggerCSharp1?code=2kIZUVH8biwHjM3qzNYqwwaP6O6gPxSTHuybdNZaD36cq3HptD5OUw==

前面的 URL 将不再起作用,因为函数已被移除;它仅用于说明目的,您应该用您的 URL 替换它。

为了在命令行上与 URL 交互,我将使用 HTTPie,这是一个命令行 HTTP 客户端。有关 HTTPie 的更多详细信息,请参阅项目主页httpie.org/

使用以下命令在命令行上调用该 URL:

$ http "https://russ-test-function.azurewebsites.net/api/HttpTriggerCSharp1?code=2kIZUVH8biwHjM3qzNYqwwaP6O6gPxSTHuybdNZaD36cq3HptD5OUw=="

这给我们带来了以下结果:

从返回的内容中可以看出,我们的函数应用返回了 HttpStatusCode BadRequest 消息。这是因为我们没有传递name变量。为了做到这一点,我们需要更新我们的命令为:

$ http "https://russ-test-function.azurewebsites.net/api/HttpTriggerCSharp1?code=2kIZUVH8biwHjM3qzNYqwwaP6O6gPxSTHuybdNZaD36cq3HptD5OUw==&name=kubernetes_for_serverless_applications"

正如您所期望的那样,这将返回正确的消息:

您还可以在浏览器中输入 URL 并查看消息:

无服务器工具包

在完成本章之前,我们将看一下无服务器工具包。这是一个旨在在不同的云提供商之间部署无服务器函数时提供一致体验的应用程序。您可以在serverless.com/.找到服务的主页。

从主页上可以看到,它支持 AWS 和 Microsoft Azure,以及 Google Cloud 平台和 IBM OpenWhisk。您还会注意到有一个注册按钮;单击此按钮并按照屏幕提示创建您的帐户。

注册后,您将收到一些非常简单的关于如何安装工具和部署第一个应用程序的说明;让我们现在遵循这些。首先,我们需要通过运行来安装命令行工具:

$ npm install serverless -g

安装将需要几分钟,一旦安装完成,您应该能够运行:

$ serverless version

这将确认上一个命令安装的版本:

现在命令行工具已安装并且我们已确认可以在没有任何错误的情况下获取版本号,我们需要登录。要做到这一点,请运行:

$ serverless login

此命令将打开一个浏览器窗口,并带您到登录页面,您需要选择要使用的帐户:

如前面的屏幕截图所示,它知道我上次使用 GitHub 帐户登录到无服务器,因此单击这将生成一个验证码:

将代码粘贴到终端提示符中,然后按键盘上的Enter键将您登录:

现在我们已经登录,我们可以创建我们的第一个项目,这将是另一个hello-world应用程序。

要在 AWS 中启动我们的hello-world函数,我们必须首先创建一个文件夹来保存无服务器工具包创建的工件,并切换到该文件夹;我在我的“桌面”上创建了一个文件夹,使用:

$ mkdir ~/Desktop/Serverless
$ cd ~/Desktop/Serverless

要生成启动我们的hello-world应用程序所需的文件,我们需要运行:

$ serverless create --template hello-world

这将返回以下消息:

在我的编辑器中打开serverless.yml,我可以看到以下内容(我已删除了注释):

service: serverless-hello-world
provider:
  name: aws
  runtime: nodejs6.10
functions:
  helloWorld:
    handler: handler.helloWorld
    # The `events` block defines how to trigger the handler.helloWorld code
    events:
      - http:
          path: hello-world
          method: get
          cors: true

我将服务更新为russ-test-serverless-hello-world;您也应该选择一个独特的名称。一旦我保存了更新的serverless.yml文件,我就运行了:

$ serverless deploy

您可能已经猜到,这将hello-world应用程序部署到了 AWS:

使用 HTTPie 访问终端 URL:

$ http --body "https://5rwwylyo4k.execute-api.us-east-1.amazonaws.com/dev/hello-world"

这将返回以下 JSON:

{
    "input": {
        "body": null,
        "headers": {
            "Accept": "*/*",
            "Accept-Encoding": "gzip, deflate",
            "CloudFront-Forwarded-Proto": "https",
            "CloudFront-Is-Desktop-Viewer": "true",
            "CloudFront-Is-Mobile-Viewer": "false",
            "CloudFront-Is-SmartTV-Viewer": "false",
            "CloudFront-Is-Tablet-Viewer": "false",
            "CloudFront-Viewer-Country": "GB",
            "Host": "5rwwylyo4k.execute-api.us-east-1.amazonaws.com",
            "User-Agent": "HTTPie/0.9.9",
            "Via": "1.1 dd12e7e803f596deb3908675a4e017be.cloudfront.net
             (CloudFront)",
            "X-Amz-Cf-Id": "bBd_ChGfOA2lEBz2YQDPPawOYlHQKYpA-
             XSsYvVonXzYAypQFuuBJw==",
            "X-Amzn-Trace-Id": "Root=1-59b417ff-5139be7f77b5b7a152750cc3",
            "X-Forwarded-For": "109.154.205.250, 54.240.147.50",
            "X-Forwarded-Port": "443",
            "X-Forwarded-Proto": "https"
        },
        "httpMethod": "GET",
        "isBase64Encoded": false,
        "path": "/hello-world",
        "pathParameters": null,
        "queryStringParameters": null,
        "requestContext": {
            "accountId": "687011238589",
            "apiId": "5rwwylyo4k",
            "httpMethod": "GET",
            "identity": {
                "accessKey": null,
                "accountId": null,
                "apiKey": "",
                "caller": null,
                "cognitoAuthenticationProvider": null,
                "cognitoAuthenticationType": null,
                "cognitoIdentityId": null,
                "cognitoIdentityPoolId": null,
                "sourceIp": "109.154.205.250",
                "user": null,
                "userAgent": "HTTPie/0.9.9",
                "userArn": null
            },
            "path": "/dev/hello-world",
            "requestId": "b3248e19-957c-11e7-b373-8baee2f1651c",
            "resourceId": "zusllt",
            "resourcePath": "/hello-world",
            "stage": "dev"
        },
        "resource": "/hello-world",
        "stageVariables": null
    },
    "message": "Go Serverless v1.0! Your function executed successfully!"
}

在浏览器中输入终端 URL(在我的情况下,我正在使用 Safari)会显示原始输出:

转到serverless deploy命令末尾提到的 URL,可以概览您使用 serverless 部署到 Lambda 的函数:

通过转到console.aws.amazon.com/打开 AWS 控制台,从服务菜单中选择 Lambda,然后切换到您的函数启动的区域;这应该会显示您的函数:

此时,您可能会想,“我的帐户是如何启动的?我没有提供任何凭据!” 无服务器工具旨在使用与我们在启动第一个 Lambda 函数之前安装的 AWS CLI 相同的凭据-这些凭据可以在您的计算机上的~/.aws/credentials找到。

要删除函数,只需运行:

$ serverless remove

这将删除无服务器工具包在您的 AWS 帐户中创建的所有内容。

有关如何使用无服务器工具包启动 Azure 函数的更多信息,请参阅快速入门指南,该指南可以在serverless.com/framework/docs/providers/azure/guide/quick-start/找到。

无服务器和函数作为服务解决的问题

尽管到目前为止我们只启动了最基本的应用程序,但我希望您开始看到使用无服务器如何有助于开发您的应用程序。

想象一下,你有一个 JavaScript 应用程序,它托管在像亚马逊的 S3 服务这样的对象存储中。你的应用程序可以用 React(facebook.github.io/react/)或 Angular(angular.io/)编写,这两种技术都允许你使用 JSON 加载外部数据。这些数据可以通过无服务器函数请求和传递 - 结合这些技术可以创建一个应用程序,不仅没有单点故障,而且在使用公共云服务时,是一个真正的按需付费应用程序。

由于无服务器函数被执行然后立即终止,你不应该担心它在哪里或者如何执行,只要它执行了。这意味着你的应用程序理论上应该是可伸缩的,也比传统的基于服务器的应用程序更容错。

例如,如果在调用你的一个函数时出现问题,例如,如果它崩溃了或者有资源问题,并且你知道下次调用函数时它将被重新启动,你不需要担心你的代码在有问题的服务器上执行。

总结

在本章中,我们快速了解了什么是无服务器,并在 AWS 和 Microsoft Azure 中启动和交互了无服务器函数,还使用了一个名为无服务器的第三方工具,在 AWS 中创建了一个无服务器函数。

到目前为止,你可能已经注意到我们还没有提到 Kubernetes,对于一本名为用于无服务器应用程序的 Kubernetes的书来说,这可能有点奇怪。不过不用担心,在下一章中我们将更详细地了解 Kubernetes,一切将变得清晰起来。

第二章:Kubernetes 简介

正如前一章末尾提到的,本章将讨论 Kubernetes。我们将讨论:

  • Kubernetes 的简要历史-它从哪里来?

  • 它是如何运作的?

  • Kubernetes 的用例是什么,谁在使用它?

  • 为什么要在服务器上运行无服务器?

Kubernetes 的简要历史

在讨论 Kubernetes 的起源之前,我们应该快速讨论一下 Kubernetes 是什么。它的发音是koo-ber-net****-eez,有时被称为K8sKubernetes是希腊语,意思是船长或船舵手,考虑到 Kubernetes 的设计目的,这个名字非常贴切。该项目的网站位于kubernetes.io/,描述了它是:

“用于自动化部署、扩展和管理容器化应用的开源系统。”

该项目起源于谷歌内部的一个名为Borg的项目。在 Docker 引起轰动之前,谷歌长期使用容器技术。

控制组

谷歌自己的容器之旅始于 2006 年,当时他们的两名工程师开始了控制组cgroups)项目。这是 Linux 内核的一个功能,可以隔离诸如 RAM、CPU、网络和磁盘 I/O 等资源,以供一组进程使用。cgroups 最初是在 2007 年发布的,在 2008 年初,该功能被合并到 Linux 内核主线版本 2.6.24 中。

您可以在kernelnewbies.org/Linux_2_6_24找到 Linux 内核 2.6.24 版本的发布说明。您可以在重要事项列表中的第 10 点找到有关 cgroups 引入的信息,其中讨论了允许 cgroups 连接到内核的框架。

lmctfy

几年后的 2013 年 10 月,谷歌发布了他们自己的容器系统的开源版本,名为lmctfy,实际上是Let Me Contain That For You的缩写。这个工具实际上是他们在自己的服务器上使用的,用于运行 Linux 应用容器,它被设计为 LXC 的替代品。

lmctfy、LXC 和 Docker 都占据着同样的领域。为此,谷歌实际上在 2015 年停止了 lmctfy 的所有开发。该项目的 GitHub 页面上有一则声明,谷歌一直在与 Docker 合作,他们正在将 lmctfy 的核心概念移植到 libcontainer 中。

Borg

这就是 Borg 项目的由来。谷歌大量使用容器,我说的是大量。2014 年 5 月,谷歌的 Joe Beda 在 Gluecon 上做了一个名为大规模容器的演讲。演讲中有一些引人注目的引用,比如:

“谷歌所有的东西都在容器中运行。”

而最常谈论的一个是:

“我们每周启动超过 20 亿个容器。”

这相当于每秒大约 3000 个,在演讲中提到,这个数字不包括任何长时间运行的容器。

虽然 Joe 详细介绍了谷歌当时如何使用容器,但他并没有直接提到 Borg 项目;相反,它只是被称为一个集群调度器。

演讲的最后一个要点是题为声明式优于命令式的幻灯片,介绍了以下概念:

  • 命令式:在那台服务器上启动这个容器

  • 声明式:运行 100 个此容器的副本,目标是同时最多有 2 个任务处于停机状态

这个概念解释了谷歌是如何能够每周启动超过 20 亿个容器,而不必真正管理超过 20 亿个容器。

直到 2015 年谷歌发表了一篇名为《谷歌 Borg 的大规模集群管理》的论文,我们才真正了解到了 Joe Beda 在前一年提到的集群调度器的实践和设计决策。

论文讨论了谷歌内部的工具 Borg 是如何运行成千上万的作业的,这些作业几乎构成了谷歌所有应用程序的集群,这些集群由成千上万台机器组成。

然后它揭示了像 Google Mail、Google Docs 和 Google Search 这样的面向客户的服务也都是从 Borg 管理的集群中提供的,以及他们自己的内部工具。它详细介绍了用户可以使用的作业规范语言,以声明他们期望的状态,使用户能够轻松部署他们的应用程序,而不必担心在谷歌基础设施中部署应用程序所需的所有步骤。

我建议阅读这篇论文,因为它很好地概述了谷歌是如何处理自己的容器服务的。

另外,如果你在想,Borg 是以《星际迷航:下一代》电视剧中的外星种族命名的。

项目七

2014 年,Joe Beda、Brendan Burns 和 Craig McLuckie 加入了 Brian Grant 和 Tim Hockin 参与了第七号项目。

这个项目以《星际迷航》中的角色“第七号九”命名,旨在制作一个更友好的 Borg 版本。在第一次提交时,该项目已经有了一个外部名称,即 Kubernetes。

你可以在github.com/kubernetes/kubernetes/commit/2c4b3a562ce34cddc3f8218a2c4d11c7310e6d56看到第一次提交,第一个真正稳定的版本是在四个月后发布的,可以在github.com/kubernetes/kubernetes/releases/tag/v0.4找到。

最初,Kubernetes 的目标是将谷歌从 Borg 和运行其大型容器集群中学到的一切开源化,作为吸引客户使用谷歌自己的公共云平台的一种方式——这就是为什么你可能仍然会在项目的原始 GitHub 页面上找到对该项目的引用github.com/GoogleCloudPlatform/kubernetes/

然而,到了 2015 年 7 月的 1.0 版本发布时,谷歌已经意识到它已经远远超出了这个范畴,他们加入了 Linux 基金会、Twitter、英特尔、Docker 和 VMware(举几个例子),共同组建了云原生计算基金会。作为这一新合作的一部分,谷歌将 Kubernetes 项目捐赠为新组织的基础。

此后,其他项目也加入了 Kubernetes,比如:

  • Prometheus(prometheus.io/),最初由 SoundCloud 开发,是一个可用于存储指标的时间序列数据库。

  • Fluentd(www.fluentd.org/)是一个数据收集器,允许你从许多不同的来源获取数据,对其进行过滤或规范化,然后将其路由到诸如 Elasticsearch、MongoDB 或 Hadoop(举几个例子)这样的存储引擎。

  • containerd(containerd.io/)是一个由 Docker 最初开发的开源容器运行时,用于实现 Open Container Initiative 标准。

  • CoreDNS(coredns.io/)是一个完全基于插件构建的 DNS 服务,这意味着你可以创建传统上配置极其复杂的 DNS 服务。

除此之外,像 AWS、微软、红帽和甲骨文这样的新成员都在支持和为基金会的项目提供资源。

Kubernetes 概述

现在我们对 Kubernetes 的起源有了一个概念,我们应该逐步了解构成典型 Kubernetes 集群的所有不同组件。

Kubernetes 本身是用 Go 编写的。虽然项目的 GitHub 页面显示该项目目前 84.9%是 Go,其余的 5.8%是 HTML,4.7%是 Python,3.3%是 Shell(其余是配置/规范文件等),都是文档和辅助脚本。

Go 是一种由 Google 开发并开源的编程语言,谷歌将其描述为一种快速、静态类型、编译语言,感觉像一种动态类型、解释语言。更多信息,请参见golang.org/

组件

Kubernetes 有两个主要的服务器角色:主服务器和节点;每个角色都由多个组件组成。

主服务器是集群的大脑,它们决定 pod(在下一节中更多介绍)在集群内部部署的位置,并且对集群的健康状况以及 pod 本身的健康状况进行操作和查看。

主服务器的核心组件包括:

  • kube-apiserver:这是您的 Kubernetes 控制面板的前端;无论您使用什么来管理您的集群,它都将直接与此 API 服务通信。

  • etcdetcd是 Kubernetes 用来存储集群状态的分布式键值存储。

  • kube-controller-manager:此服务在后台工作,以维护您的集群。它查找加入和离开集群的节点,确保正在运行正确数量的 pod,并且它们健康等等。

  • cloud-controller-manager:这项服务是 Kubernetes 的新功能。它与kube-controller-manager一起工作,其目的是与 AWS、Google Cloud 和 Microsoft Azure 等云提供商的 API 进行交互。它执行的任务示例可能是,如果要从集群中删除一个节点,它将检查您的云服务 API,看看节点是否仍然存在。如果存在,则可能会出现问题;如果不存在,则很可能是因为缩放事件而删除了节点。

  • kube-scheduler:根据一系列规则、利用率和可用性选择 pod 应该在哪里启动。

接下来我们有节点。一旦部署,主节点与安装在节点上的组件进行交互,以在集群内实现变化;这些是您的 pod 运行的地方。

组成节点的组件有:

  • kubelet:这是在节点上运行的主要组件。它负责接受来自主服务器的指令并报告回去。

  • kube-proxy:这项服务有助于集群通信。它充当节点上所有网络流量的基本代理,并能够配置 TCP/UDP 转发或充当 TCP/UDP 轮询负载均衡器到多个后端。

  • dockerrkt:这些是节点上实际的容器引擎。kubelet服务与它们交互,以启动和管理运行在集群节点上的容器。在接下来的章节中,我们将看到运行这两种节点的示例。

  • supervisord:这个进程管理器和监视器维护着节点上其他服务的可用性,比如kubeletdockerrkt

  • fluentd:这项服务有助于集群级别的日志记录。

你可能已经注意到,这些服务中唯一提到容器的是dockerrkt。Kubernetes 实际上并不直接与您的容器交互;相反,它与一个 pod 通信。

Pods 和服务

正如前面提到的,Kubernetes 不部署容器;相反,它启动 pod。在其最简单的形式中,一个 pod 实际上可以是一个单一的容器;然而,通常一个 pod 由多个容器、存储和网络组成。

以下内容仅供说明,不是一个实际的例子;我们将在下一章中通过一个实际的例子来进行讲解。

把一个 pod 想象成一个完整的应用程序;例如,如果你运行一个简单的 web 应用程序,它可能只运行一个 NGINX 容器——这个 pod 的定义文件将看起来像下面这样:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 8080

正如你所看到的,我们提供了关于我们的 pod 的一些简单元数据,这种情况下只是名称,这样我们就可以识别它。然后我们定义了一个单一的容器,它正在运行来自 Docker hub 的最新 NGINX 镜像,并且端口8080是开放的。

就目前而言,这个 pod 是相当无用的,因为我们只打算显示一个欢迎页面。接下来,我们需要添加一个卷来存储我们网站的数据。为了做到这一点,我们的 pod 定义文件将如下所示:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - mountPath: /srv/www
      name: web-data
      readOnly: true
    ports:
    - containerPort: 8080
 volumes:
 - name: web-data
 emptyDir: {} 

正如你所看到的,我们现在正在创建一个名为web-data的卷,并将其以只读方式挂载到/srv/www,这是我们 NGINX 容器上的默认网站根目录。但这还是有点毫无意义,因为我们的卷是空的,这意味着我们的所有访问者将只看到一个 404 页面。

让我们添加第二个容器,它将从 Amazon S3 存储桶同步我们网站的 HTML:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - mountPath: /srv/www
      name: web-data
      readOnly: true
    ports:
    - containerPort: 8080
  - name: sync
    image: ocasta/sync-s3:latest
    volumeMounts:
    - mountPath: /data
      name: web-data
      readOnly: false
    env:
    - ACCESS_KEY: "awskey"
      SECRET_KEY: "aws_secret"
      S3_PATH: "s3://my-awesome-website/"
      SYNC_FROM_S3: "true"
  volumes:
  - name: web-data
    emptyDir: {}

现在我们有两个容器:一个是 NGINX,现在还有一个运行s3 sync命令的容器(github.com/ocastastudios/docker-sync-s3/)。这将把我们网站的所有数据从名为my-awesome-website的 Amazon S3 存储桶复制到与 NGINX 容器共享的卷中。这意味着我们现在有一个网站;请注意,这一次,因为我们想要写入卷,我们不会将其挂载为只读。

到目前为止,你可能会想到自己;我们有一个从 Amazon S3 存储桶部署的网站服务的 pod,这一切都是真实的。然而,我们还没有完全完成。我们有一个正在运行的 pod,但我们需要将该 pod 暴露给网络,以便在浏览器中访问它。

为了做到这一点,我们需要启动一个服务。对于我们的示例,服务文件看起来可能是这样的:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

正如你所看到的,服务定义看起来与 pod 的定义类似。我们使用元数据部分设置名称。然后我们选择我们的 NGINX pod,并将端口80映射到端口8080,这是我们的 pod 正在侦听的端口。

如前所述,当我们启动第一个 Kubernetes 集群时,我们将在下一章更详细地讨论这个问题,但现在,这应该让你对 Kubernetes 的运作方式有一个很好的了解。

工作负载

在上一节中,我们看了 pod 和服务。虽然这些可以手动启动,但你也可以使用控制器来管理你的 pod。这些控制器允许执行不同类型的工作负载。我们将快速看一下不同类型的控制器,还讨论何时使用它们。

副本集

ReplicaSet 可用于启动和维护相同 pod 的多个副本。例如,使用我们在上一节中讨论的 NGINX pod,我们可以创建一个 ReplicaSet,启动三个相同 pod 的副本。然后可以在这三个 pod 之间进行负载均衡。

我们的三个 pod 可以分布在多个主机上,这意味着,如果一个主机因任何原因消失,将导致我们的一个 pod 停止服务,它将自动在健康节点上被替换。你还可以使用 ReplicaSet 来自动和手动地添加和删除 pod。

部署

您可能会认为使用 ReplicaSet 可以进行滚动升级和回滚。不幸的是,ReplicaSets 只能复制相同版本的 pod;幸运的是,这就是部署的用武之地。

部署控制器旨在更新 ReplicaSet 或 pod。让我们以 NGINX 为例。正如您从以下定义中所看到的,我们有 3 个副本都在运行 NGINX 版本 1.9.14

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.9.14
        ports:
        - containerPort: 80

kubectl 是 Kubernetes 的命令行客户端;我们将在下一章中更详细地讨论这个问题。

我们可以使用以下命令进行部署:

$ kubectl create -f nginx-deployment.yaml

现在假设我们想要更新使用的 NGINX 图像的版本。我们只需要运行以下命令:

$ kubectl set image deployment/nginx-deployment nginx=nginx:1.13.5 deployment "nginx-deployment" image updated

这将逐个更新每个 pod,直到所有的 pod 都运行新版本的 NGINX。

StatefulSets

这个控制器是 Kubernetes 中的新功能,旨在取代 PetSets。正如您可能从名称中猜到的那样,pod 作为部署的一部分维护其状态。它们被设计为具有:

  • 在整个 pod 生命周期中保持一致的唯一网络标识符

  • 持久存储

  • 按照您定义的顺序执行的优雅部署和扩展

  • 用户定义和控制的自动滚动更新

因此,虽然名称有所变化,但您应该将 StatefulSets 视为宠物,将 ReplicaSets 视为牲畜。

Kubernetes 使用案例

正如我们在本章中已经提到的,Kubernetes 几乎可以在任何地方运行,从您的本地机器(我们将在下一章中介绍),到您的本地硬件或虚拟机基础设施,甚至可能跨越 AWS、Microsoft Azure 或 Google Cloud 的数百个公共云实例。事实上,您甚至可以在 Kubernetes 集群中跨多个环境。

这意味着无论您在何处运行应用程序,都会获得一致的体验,但也可以利用底层平台的功能,比如负载平衡、持久存储和自动扩展,而无需真正设计应用程序以意识到它是在运行,比如在 AWS 或 Microsoft Azure 上。

阅读成功案例时你会注意到的一个共同点是,人们谈论的是不被锁定在一个特定的供应商上。由于 Kubernetes 是开源的,他们不会被任何许可成本所限制。如果他们遇到问题或想要添加功能,他们可以直接深入源代码进行更改;他们也可以通过拉取请求将他们所做的任何更改贡献回项目中。

另外,正如前面讨论的,使用 Kubernetes 使他们不会被锁定在任何一个特定的平台供应商或架构上。这是因为可以合理地假设 Kubernetes 在安装在其他平台时会以完全相同的方式运行。因此,突然之间,您可以相对轻松地将您的应用程序在不同提供商之间移动。

另一个常见的用例是运维团队将 Kubernetes 用作基础设施即服务(IaaS)平台。这使他们能够通过 API、Web 和 CLI 向开发人员提供资源,这意味着他们可以轻松地融入自己的工作流程。它还为本地开发提供了一个一致的环境,从暂存或用户验收测试(UAT)到最终在生产环境中运行他们的应用程序。

这也是为什么使用 Kubernetes 执行无服务器工作负载是一个好主意的部分原因。您不会被任何一个提供商锁定,比如 AWS 或 Microsoft Azure。事实上,您应该把 Kubernetes 看作是一个云平台,就像我们在第一章中看到的那些;它有一个基于 Web 的控制台,一个 API 和一个命令行客户端。

参考资料

关于 Kubernetes 的几个案例研究,用户详细介绍了他们在使用 Kubernetes 过程中的经历:

还有来自以下内容的讨论、采访和演示:

最后,您可以在www.cncf.io/了解更多关于 Cloud Native Computing Foundation 的信息。

总结

在这一章中,我们谈到了 Kubernetes 的起源,并介绍了一些其使用案例。我们还了解了一些基本功能。

在下一章中,我们将通过在本地安装 Minikube 来亲自体验 Kubernetes。一旦我们安装好了本地的 Kubernetes,我们就可以继续进行第四章,“介绍 Kubeless 功能”,在那里我们将开始在 Kubernetes 上部署我们的第一个无服务器函数。

第三章:本地安装 Kubernetes

在本章中,我们将看看如何使用 Minikube 快速搭建本地的 Kubernetes 安装。一旦我们的本地 Kubernetes 安装运行起来,我们将学习一些基本功能,并讨论在本地运行 Kubernetes 的局限性。我们将学习在以下平台上安装 Kubernetes:

  • macOS 10.13 High Sierra

  • Windows 10 专业版

  • Ubuntu 17.04

在我们开始安装之前,让我们快速看一下我们将使用的工具来部署我们的本地 Kubernetes 集群。

关于 Minikube

当你阅读上一章时,你可能会想到 Kubernetes 看起来很复杂。有很多组件需要配置,而且不仅需要配置,还需要监控和管理。

我记得当我最初看 Kubernetes 时,它刚发布不久,安装说明非常长,而且事情有点儿棘手。

在安装过程的开始阶段误读了一步,你可能会在安装过程的后期陷入麻烦——这让我想起了以前杂志上会包含游戏代码清单的情形。如果你在任何地方打错字,那么事情要么根本不起作用,要么会出现意外崩溃。

随着 Kubernetes 的成熟,安装过程也在不断改进。相当快地,一些辅助脚本被开发出来,以帮助在各种平台上启动 Kubernetes;Minikube 就是其中之一。

它的工作就是创建一个本地的 Kubernetes 节点。考虑到 Kubernetes 支持的功能范围,它有令人惊讶的多种功能,比如:

  • DNS,NodePorts 和 Ingress

  • ConfigMaps 和 Secrets

  • 容器运行时的选择;你可以使用 Docker 或 rkt

  • 通过hostPath持久卷

  • 仪表板

通常需要公共云提供商(如 AWS,Microsoft Azure 或 Google Cloud)或多个主机的 Kubernetes 功能是不受支持的。其中一些功能包括:

  • 负载均衡器

  • 高级调度策略

这是因为 Minikube 只在本地 PC 上的虚拟机上启动单个节点。但这不应该限制你;请记住,你只会想要在 Minikube 上进行开发,并且不应该使用它构建生产服务。还有很多其他工具,将在第六章中介绍,在云中安装 Kubernetes,更适合在公共云或其他供应商中启动生产就绪的 Kubernetes 集群。

Minikube 由两个核心组件组成:

  • libmachine:这个来自 Docker 的库用于在主机上提供虚拟机。它是 Docker Machine 以及 Docker for macOS 和 Docker for Windows 的核心组件。

  • localkube:这个库是由 Redspread(现在是 CoreOS 的一部分)开发并捐赠给 Minikube 项目的,它负责在启动虚拟机后部署和维护 Kubernetes 节点。

不再讨论 Minikube 能做什么,我们应该看看如何安装它,然后讨论如何与它交互。

安装 Minikube

我们将看看如何在介绍中提到的三种不同操作系统上安装 Minikube。一旦安装完成,与 Minikube 交互的过程大部分是一致的,这意味着,虽然我在示例中使用的是 macOS,但相同的命令也适用于 Windows 和 Linux。考虑到早期 Kubernetes 安装和配置过程的复杂性,你会惊讶地发现现在的过程是多么简单。

macOS 10.13 High Sierra

要在 macOS 上安装 Minikube,你首先必须安装 Homebrew 和 Cask。

Homebrew 是 macOS 的基于命令行的软件包管理器。Homebrew 用于安装命令行工具和 Cask,Cask 是一个用于管理桌面应用程序的附加组件。它非常有用,可以管理 macOS 应用商店中不可用的软件,同时也可以避免你在自己的机器上手动编译软件。

如果你还没有安装 Homebrew,你可以通过运行以下命令来安装它:

$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装完成后,你需要通过运行以下命令来安装 Cask:

$ brew install cask

如果你已经安装了 Homebrew 和 Cask,那么你应该确保一切都是最新的,并且准备好使用以下命令运行:

$ brew update
$ brew doctor

一旦 Homebrew 和 Cask 准备好,你可以通过运行以下命令来安装 Minikube:

$ brew cask install minikube

首先会下载依赖项,然后安装 Minikube:

该过程不到一分钟,安装完成后,您应该能够执行以下操作:

$ minikube version

这将显示当前版本;在我的情况下,这是v0.22.2。我们现在已经安装并准备好使用 Minikube 了。

Windows 10 专业版

与我们在 macOS 上安装 Minikube 的方式类似,我们将使用一个包管理器;这次叫做 Chocolatey。

Chocolatey 是 Windows 的一个包管理器,类似于 macOS 上的 Homebrew。它使您能够从命令行安装软件,并支持 PowerShell 和cmd.exe。我们将使用 PowerShell。

如果您没有安装 Chocolatey,可以在以管理员权限启动的 PowerShell 控制台中运行以下命令:

以下命令是一行,而不是多行。另外,由于我们使用Set-ExecutionPolicy Bypass来运行安装命令,您将被询问是否确定。由于我们直接从 Chocolatey 网站通过 HTTPS 运行脚本,您应该能够信任该脚本并回答是。

$ Set-ExecutionPolicy Bypass; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

安装了 Chocolatey 后,您可以通过运行以下命令来安装 Minikube:

$ choco install minikube

这将下载并安装依赖项,然后安装 Minikube。当您被要求确认是否要运行脚本时,请回答是:

安装后,您将能够运行以下命令:

$ minikube version

这将返回安装的 Minikube 版本;对我来说,这是v0.22.2

Ubuntu 17.04

与 macOS 和 Windows 版本不同,我们将不会使用包管理器在 Ubuntu 17.04 上安装 Minikube。相反,我们将直接从项目页面下载二进制文件。要做到这一点,只需运行以下命令:

$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.22.2/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/

Minikube 将被下载,将设置执行权限,并将移动到/usr/local/bin/,以便在系统路径中。

现在 Minikube 已安装,我们需要下载kubectl。在 macOS 和 Windows 安装过程中,这是由包管理器处理的;幸运的是,这个过程与我们刚刚运行以安装 Minikube 的命令几乎相同:

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl && chmod +x ./kubectl && sudo mv ./kubectl /usr/local/bin/kubectl

安装后,您应该能够再次运行以下命令来确认安装的 Minikube 版本:

$ minikube version

当我运行该命令时,它返回v0.22.2,如下截图所示:

Hypervisors

Minikube 支持多种不同的 hypervisors。 Hypervisor 是一个用于启动虚拟机的进程;它将虚拟机的操作系统与您自己的操作系统隔离开来,同时允许它共享 CPU、RAM 和磁盘空间等资源。

Minikube 默认支持以下 hypervisors:

  • Hyper-V(Windows 10):这是本机 hypervisor;它适用于 Windows 10 专业版和 Windows 服务器

  • KVM(Ubuntu 17.04):这是本机 Linux hypervisor,在大多数现代发行版的 Linux 内核中运行

  • VirtualBox(macOS,Windows 10 和 Ubuntu 17.04):由 Oracle 发布,VirtualBox 是一个开源的 x86 hypervisor,可以在大量操作系统上运行

  • VMware Fusion(macOS):Fusion 提供了一个经过优化的 macOS hypervisor,其最大优势是能够在 macOS 上运行和公开 Windows 应用程序

  • xhyve(macOS):这是 macOS 上的本机 hypervisor;就像 Linux 上的 KVM 一样,它内置在内核中

从列表中可以看出,在本章中我们涵盖的三种操作系统中,只有 VirtualBox 得到支持。因此,它是 Minikube 支持的默认 hypervisor。如果您已经安装了 VirtualBox,可以运行以下与您选择的操作系统相关的命令。

对于 macOS,我们可以使用 Homebrew 和 Cask 来安装 VirtualBox:

$ brew cask install virtualbox

同样,对于 Windows 10,您可以使用 Chocolatey 来安装 VirtualBox:

如果启用了 Hyper-V,则无法在 Windows 10 上使用 VirtualBox。如果您希望跟随操作,请在继续之前禁用 Hyper-V。

$ choco install virtualbox

最后,对于 Ubuntu 17.04,您需要运行以下命令来添加存储库和密钥:

$ wget -q http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc -O- | sudo apt-key add -
$ sudo sh -c 'echo "deb http://download.virtualbox.org/virtualbox/debian $(lsb_release -sc) contrib" >> /etc/apt/sources.list'

然后运行以下命令来加载我们之前添加的存储库并安装软件包:

$ sudo apt-get update
$ sudo apt-get install virtualbox-5.1

现在您应该能够在列出的软件程序中看到 Virtualbox。

启动 Minikube

要完成我们的安装,我们需要启动 Minikube。要做到这一点,请运行以下命令:

$ minikube start

在 macOS 上,您应该看到类似于这样的东西:

如您所见,用于创建虚拟机的 ISO 已经下载。虚拟机启动,我们将用于对我们的单节点集群进行身份验证的证书被生成,最后kubectl被配置为使用我们本地 Kubernetes 集群的详细信息。

在 Windows 10 上运行相同的命令将得到完全相同的步骤:

另外,正如您可能已经猜到的那样,在 Ubuntu 17.04 上运行会得到相同的结果。运行以下命令:

$ minikube status

您将收到一条消息,确认一切正常运行,并且 kubectl 已正确配置以与您的 Kubernetes 集群通信:

如果您打开 VirtualBox,您应该会看到您的 Minikube 虚拟机正在运行;例如,当我在 Windows 10 上打开 VirtualBox 时就是这种情况:

尽管我们在三种不同的操作系统上启动了 Minikube,除了初始安装之外,您已经可以体验我们在第二章中讨论的内容了:没有供应商锁定和一致的体验,而且这是在我们开始使用新安装的 Kubernetes 集群之前。

Minikube 命令

到目前为止,我们已经使用了 minikube startminikube status 命令来启动我们的单节点 Kubernetes 集群,并检查一切是否按预期运行。在我们开始与 Kubernetes 交互之前,我想介绍一些更基本的 Minikube 命令。

停止和删除

由于我们将我们的单节点 Kubernetes 集群作为虚拟机在您的主机上运行,您可能不希望它一直运行,占用资源。

有两种选项可以实现这一点,第一种是 minikube stop。这个命令将停止您的节点,并保持虚拟机完整。如果您计划在下次通过运行 minikube start 启动节点时继续之前的工作,您应该使用这个命令。

虽然 minikube stop 命令会停止您的虚拟机在主机上使用 CPU 和 RAM 资源,但用于托管虚拟机的硬盘映像仍将存在于您的机器上。虽然新启动的集群不会占用主机硬盘上太多空间,在我的 macOS 安装中大约为 650 MB;一旦您开始使用集群,您可能会发现这个空间至少会翻倍。

这就是我们下一个命令发挥作用的地方。minikube delete 命令将完全删除集群,包括所有虚拟机文件,释放主机机器上使用的空间。

在写作时,运行minikube delete将立即删除您的虚拟机,无论其是否正在运行。不会有提示询问您是否确定,也没有从该命令返回的方法(除非您有备份),因此请确保谨慎使用此命令。

当您再次运行minikube start时,您的集群将从头开始启动,就像我们在上一节中首次体验到的那样。

环境

接下来,我们有一些命令,显示有关虚拟机的信息,以及 Minikube 在您的设备上配置的环境。

首先,我们有一个非常简单的命令minikube ip。这个命令只是返回虚拟机的 IP 地址。如果您想通过脚本与集群交互,这将非常有用。您可以包含命令的输出,以引用集群的当前 IP 地址,而无需在脚本中硬编码实际的 IP 地址。

我们要看的下一个命令是minikube docker-env。运行此命令应该会在屏幕上打印出类似以下输出:

$ minikube docker-env
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.101:2376"
export DOCKER_CERT_PATH="/Users/russ/.minikube/certs"
export DOCKER_API_VERSION="1.23"
# Run this command to configure your shell:
# eval $(minikube docker-env)

输出的作用是允许您(如果已安装)配置本地 Docker 客户端与 Minikube 虚拟机上的 Docker 安装进行通信。然而,这样做也有一个缺点。目前作为 Minikube 虚拟机镜像的一部分分发的 Docker 版本略落后于当前版本。您可以通过运行eval $(minikube docker-env),然后docker version来查看这一点。当我运行这两个命令时,得到了以下结果:

$ eval $(minikube docker-env)
$ docker version
Client:
 Version: 17.06.2-ce
 API version: 1.23
 Go version: go1.8.3
 Git commit: cec0b72
 Built: Tue Sep 5 20:12:06 2017
 OS/Arch: darwin/amd64

Server:
 Version: 1.12.6
 API version: 1.24 (minimum version )
 Go version: go1.6.4
 Git commit: 78d1802
 Built: Wed Jan 11 00:23:16 2017
 OS/Arch: linux/amd64
 Experimental: false

从输出中可以看出,写作时 Minikube 使用的 Docker 版本比我在 macOS 上安装的最新稳定版本要落后两个版本。在本书涵盖的内容范围内,运行旧版本的 Docker 并不是问题,也不需要担心,因为我们不会直接与其交互。

虚拟机访问和日志

您可以通过 SSH 登录到 Minikube 虚拟机。在安装过程中,生成了一个 SSH 密钥,并在启动时与虚拟机共享。您可以通过运行minikube ssh-key来检查此密钥的位置。这将返回密钥的私钥部分的路径。您可以将其与其他命令结合使用,在 macOS 或 Ubuntu 上运行以下命令来 SSH 登录到虚拟机:

$ ssh docker@$(minikube ip) -i $(minikube ssh-key)

这将动态生成虚拟机的 IP 地址和私钥路径:

然而,Minikube 还有一个命令可以为您运行这个命令,并且在所有平台上都受支持。运行minikube ssh将直接将您登录到虚拟机作为 Docker 用户,如下面的终端输出所示:

我们要快速查看的最后一个命令是minikube logs。这会显示localkube实例生成的所有日志:

这些日志用于帮助调试您的 Minikube 安装中的问题。它们不包含任何用户数据,这意味着您不能使用它们来帮助跟踪您启动的服务或 pod 的任何问题。

你好世界

现在我们的单节点 Kubernetes 集群已经运行起来了,使用 Minikube,我们可以尝试启动一个服务。我们将首先使用仪表板,然后再转向命令行客户端。

仪表板

每个 Minikube 安装都带有一个基于 Web 的仪表板。这可以通过运行minikube dashboard来访问,它会立即在您的默认浏览器中打开仪表板:

点击页面左上角的+创建按钮,将带您到一个表单,让您部署一个容器化应用程序。

在部署容器化应用页面上,您会找到几个选项。保持启用下面的指定应用程序详细信息选项,填写如下:

  • 应用名称: dashboard-hello-world

  • 容器镜像: nginx:latest

  • Pod 数量: 1

  • 服务: 外部

  • 端口: 8080

  • 目标端口: 80

  • 协议: TCP

对于我们的目的,我们不需要填写在“显示高级选项”下找到的任何选项。只需点击表单底部的“部署”按钮。过一会儿,您的仪表板应该显示您有一个部署、pod、ReplicaSet 和服务,所有这些都带有dashboard-hello-world的名称:

您可以通过运行以下命令查看服务:

$ minikube service dashboard-hello-world

这将返回以下消息:

Opening kubernetes service default/dashboard-hello-world in default browser...

打开您的浏览器,在那里您应该看到默认的 NGINX 页面:

虽然这只是一个非常基本的例子,但它确实展示了使用仪表板启动简单应用程序有多简单。现在让我们看看如何转移到命令行。

命令行

在上一章中,我们简要介绍了如何使用 YAML 或 JSON 文件来定义您的 pod、ReplicaSets 和服务。让我们使用kubectl来启动一个与前一个应用程序相同的应用程序。

首先,我们需要一个要启动的文件;您可以在本书的代码包和 GitHub 存储库的Chapter03文件夹中找到名为cli-hello-world.yml的副本:

apiVersion: v1
kind: Service
metadata:
  name: cli-hello-world
spec:
  selector:
    app: cli-hello-world
  type: NodePort
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 80
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: cli-hello-world
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cli-hello-world
  template:
    metadata:
      labels:
        app: cli-hello-world
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

您可能已经注意到,虽然这是一个单独的文件,但实际上我们有两个不同的部分。第一个启动外部服务,在端口8000上公开它,以便与我们在上一节使用仪表板启动的外部服务不发生冲突。第二部分定义了 pod 和复制集;这与我们使用仪表板启动的内容非常相似。

要启动应用程序,我们只需要运行以下命令:

$ kubectl apply -f cli-hello-world.yml

您几乎立即会收到已创建服务和部署的确认:

service "cli-hello-world" created
deployment "cli-hello-world" created

创建后,您应该能够运行以下命令在浏览器中打开应用程序:

$ minikube service cli-hello-world

再次,您应该会看到默认的 NGINX 页面。

我相信当我们打开仪表板时,您点击了页面左侧可以找到的菜单项。所有这些信息也可以在命令行中找到,所以让我们简要地看一下我们可以使用的一些命令来了解有关我们集群的更多信息。

您将要运行的更常见的命令之一是kubectl get。这将获取 pod、ReplicaSets 和服务的列表,以及更多内容。运行以下命令应该给我们一个类似于仪表板概述的视图:

$ kubectl get pods
$ kubectl get replicasets
$ kubectl get services
$ kubectl get secrets

正如您从以下终端输出中所看到的,所有内容都列出了其当前状态:

您可以获得很多选项;例如,尝试运行这个:

$ kubectl get endpoints
$ kubectl get events
$ kubectl get storageclasses

只运行kubectl get将列出您可以使用的所有不同参数。现在我们有了完整的 pod 名称,在我的情况下是cli-hello-world-3678853705-f41d2,我们可以通过运行kubectl describe命令来了解更多关于它的细节。例如,我运行了这个:

$ kubectl describe pods/cli-hello-world-3678853705-f41d2

当您在本地运行命令时,请更新 pod 名称以反映您自己的名称。Kubernetes 在启动时为每个 pod 添加一个唯一 ID,以确保您可以在任何给定的主机上运行多个相同的 pod。

我得到了以下信息:

Name: cli-hello-world-3678853705-f41d2
Namespace: default
Node: minikube/192.168.99.100
Start Time: Sun, 08 Oct 2017 10:41:06 +0100
Labels: app=cli-hello-world
 pod-template-hash=3678853705
Annotations: kubernetes.io/created-by={"kind":"SerializedReference","apiVersion":"v1","reference":{"kind":"ReplicaSet","namespace":"default","name":"cli-hello-world-3678853705","uid":"ce7b2030-ac0c-11e7-9136-08002...
Status: Running
IP: 172.17.0.5
Created By: ReplicaSet/cli-hello-world-3678853705
Controlled By: ReplicaSet/cli-hello-world-3678853705
Containers:
 nginx:
 Container ID: docker://0eec13c8340b7c206bc900a6e783122cf6210561072b286bda10d225ffb3c658
 Image: nginx:latest
 Image ID: docker-pullable://nginx@sha256:af32e714a9cc3157157374e68c818b05ebe9e0737aac06b55a09da374209a8f9
 Port: 80/TCP
 State: Running
 Started: Sun, 08 Oct 2017 10:41:09 +0100
 Ready: True
 Restart Count: 0
 Environment: <none>
 Mounts:
 /var/run/secrets/kubernetes.io/serviceaccount from default-token-v563p (ro)
Conditions:
 Type Status
 Initialized True
 Ready True
 PodScheduled True
Volumes:
 default-token-v563p:
 Type: Secret (a volume populated by a Secret)
 SecretName: default-token-v563p
 Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: <none>
Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal Scheduled 31m default-scheduler Successfully assigned cli-hello-world-3678853705-f41d2 to minikube
 Normal SuccessfulMountVolume 31m kubelet, minikube MountVolume.SetUp succeeded for volume "default-token-v563p"
 Normal Pulling 31m kubelet, minikube pulling image "nginx:latest"
 Normal Pulled 31m kubelet, minikube Successfully pulled image "nginx:latest"
 Normal Created 31m kubelet, minikube Created container
 Normal Started 31m kubelet, minikube Started container

您可以使用 kubectl describe 查找几乎可以使用 kubectl get 列出的所有信息,例如:

$ kubectl describe services/cli-hello-world
$ kubectl describe replicasets/cli-hello-world-3678853705
$ kubectl describe storageclasses/standard

同样,您可以通过仅运行kubectl describe来了解更多信息。在接下来的章节中,我们将介绍更多命令,以便在本书结束时,您将能够充分利用kubectl

在完成本章之前,我希望我们能够快速看一下如何将存储从本地机器挂载到 Minikube 虚拟机内部,然后再挂载到我们的 pod 内部。

您将在Chapter03文件夹中找到一个名为html的文件夹。其中包含一个名为index.html的单个文件。在Chapter03文件夹中运行以下命令将挂载 HTML 到虚拟机内部:

$ minikube mount ./html:/data/html

您可以从运行命令后显示的消息中看到这一点:

在撰写本文时,已知在 Windows 10 主机上使用minikube mount命令存在一个已知的 bug,请参阅以下 GitHub 问题以获取更多信息github.com/kubernetes/minikube/issues/1473github.com/kubernetes/minikube/issues/2072

您需要保持此进程运行,因此在本节的其余部分中打开一个新的终端或 PowerShell 窗口以供使用。

运行以下命令:

$ minikube ssh
$ ls -lhat /data/html/
$ exit

这些命令将使您登录到 Minikube 虚拟机,获取/data/html/的目录列表,然后退出虚拟机:

如您所见,我们的index.html文件在/data/html/中的集群节点上可用。返回到Chapter03文件夹,您应该会看到一个名为cli-hello-world-storage.yml的文件。其中包含使用此挂载文件夹的服务和部署信息。

服务部分看起来与本节中先前使用的很相似;但是,在部署部分有一个额外的内容:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: cli-hello-world-storage
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cli-hello-world-storage
  template:
    metadata:
      labels:
        app: cli-hello-world-storage
    spec:
      volumes:
      - name: html
        hostPath:
          path: /data/html
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: /usr/share/nginx/html
          name: html

正如您所看到的,在部署的spec部分中,我们现在正在定义一个名为htmlvolume,然后在容器部分中,我们正在使用mountPath选项将名为html的卷挂载到/usr/share/nginx/html,这是我们在容器中使用的 NGINX 容器映像的默认网页根目录。

使用kubectl apply命令启动您的应用程序,然后使用minikube service命令在浏览器中打开服务:

$ kubectl apply -f cli-hello-world-storage.yml
$ minikube service cli-hello-world-storage

您应该看到以下页面:

如果您在本地机器上的html文件夹中编辑index.html,当您刷新浏览器窗口时,更改将立即反映出来。

在我们进入下一章之前,我们应该删除本章中使用的 Minikube 虚拟机,以便我们从头开始。首先,我们有一个进程,它正在保持我们主机机器上的html文件夹挂载。要终止此进程,请返回到终端或 PowerShell 并按下Ctrl + C;这将向进程发送终止信号并将您返回到命令行。然后我们可以运行:

$ minikube delete

这将删除当前的虚拟机,这意味着当我们下次启动 Minikube 时,它将从头开始。

参考资料

有关本章中使用的工具的更多信息,请访问它们的项目页面:

总结

在本章中,我们使用 Minikube 在本地机器上安装了单节点 Kubernetes 集群;我们看了如何在 macOS、Windows 10 和 Ubuntu Linux 上实现这一点。一旦安装完成,我们发现无论我们的本地机器运行哪个操作系统,我们都可以以完全相同的方式与我们的单节点 Kubernetes 集群进行交互。

然后,我们首次启动了 Pods、ReplicaSets 和服务,使用了 Kubernetes 仪表板和名为kubectl的 Kubernetes 命令行客户端。

在下一章中,我们将在我们目前在本地运行的单节点 Kubernetes 集群上启动我们的第一个无服务器工具,名为 Kubeless。

第四章:介绍 Kubeless 功能

现在我们的 Kubernetes 安装已经运行起来了,我们可以开始运行我们的第一个无服务器应用程序;我们将通过一些示例来安装和运行 Kubeless。我们将涵盖以下主题:

  • 安装 Kubeless

  • Kubeless 概述

  • 使用 Kubeless 运行我们的第一个函数-“hello world”示例

  • 更高级的示例-发布推文

  • 无服务器插件

让我们开始在我们的三个目标操作系统上安装 Kubeless。

安装 Kubeless

Kubeless 有两个组件;第一个是在 Kubernetes 上运行的堆栈,第二部分是您用来与 Kubeless 集群交互的命令行客户端。

我们首先将看看如何让 Kubeless 在 Kubernetes 上运行起来。一旦运行起来,我们将看看如何在我们的三个目标操作系统上安装命令客户端。

Kubeless Kubernetes 集群

我们将在上一章中安装和配置的单节点 Minikube 集群上安装 Kubeless。我们需要做的第一件事是确保我们从一个干净的 Kubernetes 安装开始。为此,我们只需要运行以下两个命令:

请记住,运行minikube delete命令将立即删除当前正在运行的虚拟机,而不会发出警告,这意味着您的 Minikube 单节点集群上当前活动的所有内容都将丢失。

$ minikube delete
$ minikube start

现在我们的新的单节点 Kubernetes 集群已经运行起来了,我们需要通过运行以下命令为 Kubeless 创建一个命名空间:

$ kubectl create ns kubeless

然后通过运行以下命令安装 Kubeless 本身:

在撰写本文时,Kubeless 的当前版本是 v0.2.3。您可以在项目的 GitHub 发布页面github.com/kubeless/kubeless/releases上检查最新版本。要安装更新版本,只需在以下 URL 中使用更新的版本号,但请注意,不同版本之间的输出可能会有所不同。

$ kubectl create -f https://github.com/kubeless/kubeless/releases/download/v0.2.3/kubeless-v0.2.3.yaml

如您所见,这将创建并启动在您的单节点 Kubernetes 集群上运行 Kubeless 所需的所有组件:

一切启动需要一些时间。您可以通过运行以下命令来检查每个组件的状态:

$ kubectl get pods -n kubeless
$ kubectl get deployment -n kubeless
$ kubectl get statefulset -n kubeless

这应该会显示类似以下输出:

或者,您也可以使用 Kubernetes 仪表板来检查状态。要做到这一点,运行以下命令打开仪表板:

$ minikube dashboard

当仪表板首次打开时,它被配置为显示默认命名空间,因为我们执行的第一个命令创建了一个名为kubeless的新命名空间。我们需要切换到kubeless命名空间以查看其中部署的 Pods、Deployments 和 Stateful Sets。

一旦您更改了命名空间,您应该在以下页面上看到一些内容:

正如您所看到的,我们只用两个命令就部署了一组相当复杂的服务。所有繁重的工作和复杂性都已完全抽象化。

命令行客户端

现在 Kubeless 已经安装在我们的单节点 Kubernetes 集群上,我们可以考虑安装命令行客户端;这是我们将与我们的 Kubeless 集群进行交互的方式。

macOS 10.13 High Sierra

由于我们已经在上一章安装了 Homebrew,我们将使用brew命令来安装 Kubeless。为此,我们需要添加 Kubeless tap;tap 是一个包含软件安装说明的第三方存储库。一旦 tap 被添加,我们就可以以与我们在第二章中安装 Minikube 相同的方式安装 Kubeless。

要安装 tap,然后安装 Kubeless 命令行客户端,请运行以下两个命令:

$ brew tap kubeless/tap
$ brew install kubeless

安装完成后,您可以通过运行以下命令来检查已安装的客户端的版本:

$ kubeless version

如果这返回的客户端版本与您安装的软件不同,不要太担心;这不应该是一个问题。

Windows 10 专业版

不幸的是,Kubeless 没有可用的 Chocolatey 安装程序,因此我们必须手动下载和解压可执行文件。要在 PowerShell 中执行此操作,请运行以下命令:

$ Invoke-WebRequest -Uri https://github.com/kubeless/kubeless/releases/download/v0.2.3/kubeless_windows-amd64.zip -Outfile C:\Temp\kubeless_windows-amd64.zip
$ expand-archive -path 'C:\Temp\kubeless_windows-amd64.zip' -destinationpath 'C:\Temp\kubeless_windows-amd64'
$ Move-Item C:\Temp\kubeless_windows-amd64\bundles\kubeless_windows-amd64\kubeless.exe .\

或者,您可以从 Kubeless 发布页面下载kubeless_windows-amd64.zip文件。下载后,解压.zip文件,并将kubeless.exe文件放在我们可以执行它的位置。从包含您的kubeless.exe文件的文件夹运行以下命令:

$ ./kubeless version

这将返回命令行客户端的版本。

Ubuntu 17.04

就像 Windows 10 版本的 Kubeless 命令行客户端一样,我们需要下载发布版本,解压缩并将可执行文件移动到指定位置。要做到这一点,请运行以下命令:

$ curl -Lo /tmp/kubeless.zip https://github.com/kubeless/kubeless/releases/download/v0.2.3/kubeless_linux-amd64.zip
$ unzip /tmp/kubeless.zip -d /tmp
$ chmod +x /tmp/bundles/kubeless_linux-amd64/kubeless
$ sudo mv /tmp/bundles/kubeless_linux-amd64/kubeless /usr/local/bin/

最后,为了检查可执行文件是否按预期工作,请运行:

$ kubeless version

我们已经准备好在我们的 Ubuntu Linux 主机上使用 Kubeless。

Kubeless Web 界面

在我们继续之前,我们还可以安装 Kubeless 的 Web 界面。就像 Kubeless 本身一样,只需运行以下命令即可轻松安装:

$ kubectl create -f https://raw.githubusercontent.com/kubeless/kubeless-ui/master/k8s.yaml

然后,您可以使用 Minikube 运行以下命令在浏览器中打开服务:

$ minikube service ui --namespace kubeless

从上述命令中可以看出,由于ui服务已部署在kubeless命名空间中,我们需要通过传递--namespace标志来让 Minikube 知道这是服务的访问位置。Kubeless Web 界面可能需要几分钟才能启动,但当它启动时,您应该会看到一个类似以下内容的页面:

Kubeless 概述

在开始使用 Kubeless 部署无服务器函数之前,我们应该花点时间来了解一下我们刚刚安装的内容,并查看在使用 Kubeless 命令行客户端时可用的命令。

正如我们已经提到的,安装过程非常简单——在我们的单节点 Kubernetes 集群上安装 Kubeless 时,安装过程基本上是一样的,即使我们在由多个节点组成的 Kubernetes 上安装它也是如此。

那么,什么是 Kubeless 呢?

Kubeless 是一个支持在 Kubernetes 集群上部署无服务器函数的框架,它允许您使用 HTTP 和事件触发器来执行 Python、Node.js 或 Ruby 代码。该框架是使用核心 Kubernetes 功能构建的,如部署、服务、ConfigMaps 等。这使得 Kubeless 的代码库很小,并且意味着开发人员不必重复大量的调度逻辑,因为它已经存在于 Kubernetes 核心中。

它通过利用 Kubernetes 控制器来工作。使用控制器,Kubeless 开发人员已扩展了 Kubernetes API,以在 Kubernetes 中添加一个函数对象。 Kubeless 控制器作为部署在 Kubernetes 集群中运行,其主要工作是监视函数端点的调用。当调用端点时,将执行包含函数代码的运行时;这些是预构建的 Docker 镜像,用于包装您的函数,使用 ConfigMaps 注入到 Kubernetes 集群中的 Apache Kafka 消费者或 HTTP 服务器中,您可以像调用任何其他网页一样调用它们。

Apache Kafka 是一个分布式流平台,让您可以发布和订阅信息流。在我们的情况下,这个信息流是触发的事件,Kubeless 控制器订阅了这个事件。

所有这些意味着我们可以在我们运行的单节点集群中获得与我们在第一章 无服务器景观中涵盖的 AWS 和 Microsoft Azure 的无服务器服务类似的体验,包括我们本地运行的 Kubernetes 集群。

谁创造了 Kubeless?

Kubeless 是由 Bitnami(bitnami.com/)创建的,它是他们编写并开源支持将应用程序轻松部署到 Kubernetes 集群的几个项目之一。

Bitnami 多年来一直是分发预打包的开源和商业支持许可应用的领导者,在撰写本文时有超过 140 个应用程序,以可预测和一致的方式跨多个不同平台和公共云进行分发和支持,因此支持和开发 Kubernetes 对他们来说是一个自然的选择。

他们是 Helm 的核心贡献者,与微软和谷歌一起,Helm 是由 Cloud Native Computing Foundation 论坛维护的 Kubernetes 的包管理器,我们知道来自第二章 Kubernetes 简介

您可以在kubeless.io/找到 Kubeless 网站。

Kubeless 命令

Kubeless 命令行客户端有几个命令。在我们使用 Kubeless 在 Kubernetes 上启动我们的第一个无服务器函数之前,我们应该快速讨论一些我们将要使用的命令。

我们将要使用的最常见的命令是kubeless function。这允许我们部署删除编辑列出函数。此外,我们可以通过使用call执行我们的函数,并检查日志

接下来,我们有kubeless ingress;使用此命令,我们可以创建删除列出到我们函数的路由。

最后,我们还将看一下kubeless topic;与ingress一样,它允许我们创建删除列出主题,以及向主题发布消息。

Hello world

首先,我们将看一下部署两个非常简单的 hello world 函数。第一个简单地打印Hello World!,第二个接受输入,然后将其显示回给你。

基本示例

首先,我们需要我们的函数。静态的 hello-world 函数需要以下三行 Python 代码:

import json
def handler():
    return "Hello World!"

将前面的代码放在名为hello.py的文件中,该文件也可以在伴随本书的 GitHub 存储库的Chapter04/hello-world文件夹中找到。

现在我们有了我们的函数,我们可以通过运行以下命令将其部署到默认命名空间中:

$ kubeless function deploy hello \
 --from-file hello.py
 --handler hello.handler \
 --runtime python2.7 \
  --trigger-http

此命令创建一个名为hello的函数,使用文件hello.py。每当执行名为hello.handler的函数时,我们使用python2.7运行时,并且我们的函数被设置为由http请求触发。

您可能已经注意到,当您运行命令时,没有任何反馈,所以要检查函数是否已创建,您可以运行以下命令:

$ kubeless function ls

在前面的命令中有几列:

  • 名称:这是函数的名称

  • 命名空间:函数部署到的命名空间的名称

  • 处理程序:要运行的处理程序的名称—在我们的情况下,处理程序只是处理程序,因此它正在调用hello-world.handler

  • 运行时:Kubeless 支持的每种语言都有单独的运行时

  • 类型:这是函数被调用的方式,在我们的情况下这是 HTTP

  • 主题:如果我们订阅消息队列,这将是我们要观察消息的主题

另外,正如前一节中提到的,Kubeless 将函数对象添加到 Kubernetes 中。您可以运行以下命令来检查我们的函数是否被列在函数对象中:

$ kubectl get functions

通过这些命令运行应该会给您一些类似以下结果:

现在我们的函数已部署,我们可以执行它。要运行此操作,请运行:

$ kubeless function call hello

这将产生以下结果:

我们调用函数的另一种方式是使用 Kubeless Web 界面。通过运行以下命令打开它:

$ minikube service ui --namespace kubeless

打开后,您应该在左侧列出函数hello。单击hello将显示函数中的代码,并且右侧有一个标有 RUN FUNCTION 的按钮;单击此按钮将执行hello函数并返回Hello World!

我们与函数交互的最终方式是创建 Ingress 规则;但是,在执行此操作之前,我们必须在 Minikube 中启用 Ingress 插件。要执行此操作,请运行以下命令:

$ minikube addons enable ingress

现在 Ingress 插件已启用,我们需要使用 Kubeless 创建 Ingress 路由。要执行此操作,我们只需要运行以下命令:

$ kubeless ingress create --function hello hello-ingress

我们需要知道 Kubeless 创建的主机,以便访问我们的服务。要执行此操作,请运行以下命令:

$ kubeless ingress ls

这将提供有关我们创建的 Ingress 路由的信息,包括我们将能够使用的主机来访问该服务。对我来说,这是http://hello.192.168.99.100.nip.io/

nip.io是一个简单且免费的 DNS 服务,允许您创建 DNS 记录将您的主机映射到 IP 地址。Kubeless 使用此服务创建有效的主机以路由到您的服务。

在我的浏览器中打开此 URL 返回Hello World!,通过 HTTPie 运行它也是如此,我们在第一章中介绍了 HTTPie,您可以从以下终端输出中看到:

现在我们的第一个函数已经运行起来了,让我们看看如何创建一个可以传递并打印数据的函数。

读取数据的示例

我们的新函数代码仍然非常简单:

import json

def handler(context):
    print context.json
    return context.json

此代码的全部作用只是接收我们发布的 JSON 并将其显示给我们。将其放入名为hello-name.py的文件中,或者使用 GitHub 存储库中Chapter04/hello-world/文件夹中的文件。一旦有了文件,您可以通过运行以下命令创建函数:

$ kubeless function deploy hello-name \
 --from-file hello-name.py \
 --handler hello-name.handler \
 --runtime python2.7 \
 --trigger-http

部署函数后,通过运行以下命令检查是否已创建:

$ kubeless function ls

您应该看到列出了两个函数,hellohello-name。现在我们已经创建了新函数,可以通过运行以下命令来调用它:

$ kubeless function call hello-name --data '{ "name": "Russ" }'

请注意,这次我们使用--data标志将数据传递给函数。运行所有命令后,您应该看到类似以下终端输出:

在使用 Web 界面调用函数时,我们还需要传递数据。要做到这一点,再次打开界面,运行:

$ minikube service ui --namespace kubeless

打开后,点击hello-name函数。在点击 RUN FUNCTION 按钮之前,使用下拉菜单将 GET 更改为 POST,并在请求表单中输入以下内容:

{ "name": "Russ" }

现在,点击 RUN FUNCTION 按钮。这将返回与kubeless function call命令相同的结果:

我们还可以通过配置 Ingress 路由直接与服务交互:

$ kubeless ingress create --function hello-name hello-name-ingress
$ kubeless ingress list

这将为我们的两个函数提供 URL:

与我们的第一个示例不同,转到hello-name的 URL,对我来说是http://hello-name.192.168.99.100.nip.io/,将会显示错误:500 内部服务器错误(或在 Kubeless 的后续版本中,显示 504 网关超时):

为什么会这样,尽管我们使用kubeless function call命令和 Kubeless Web 界面调用时都没有错误?

通过简单地将 URL 输入到浏览器中,我们没有发布任何数据供函数返回;这就是为什么会生成错误的原因。我们可以通过检查日志来确认这一点。要做到这一点,刷新浏览器中的页面几次,然后运行以下命令:

$ kubeless function logs hello-name

您应该看到类似以下的内容:

前面日志输出的第一行是内部健康检查,它是成功的,因为生成了200状态,您可以在GET之后看到。接下来的几行包含我们要查找的错误;正如您所看到的,我们得到了Traceback,然后是以下内容:TypeError: handler() takes exactly 1 argument (0 given)。这意味着函数期望传递数据,但没有传递。下一行是来自我们浏览器的请求;正如您在GET之后看到的,有一个500的状态。

那么,我们如何与需要 POST 数据而不是 GET 的函数进行交互呢?在 macOS 和 Linux 命令行上,您可以通过几种方式实现这一点,但在 Windows 上,您将不得不运行其他东西。与其通过不同的示例来工作,我要安装一个名为 Postman 的软件。这个桌面软件适用于我们在书中涵盖的所有三种操作系统,并且它将为我们与hello-name函数以及我们启动的任何其他函数进行交互提供一个很好的图形界面。

要在 macOS 10.13 High Sierra 上使用 Homebrew 安装 Postman,只需运行:

$ brew cask install postman

Postman 有一个 Chocolatey 软件包,因此如果您使用的是 Windows 10 专业版,可以运行:

$ choco install postman

要在 Ubuntu 17.04 上安装 Postman,我们需要运行一些额外的步骤。首先,我们需要下载、解压缩并移动文件到指定位置,确保清理和移动我们需要的文件。为此,请运行以下命令:

$ wget https://dl.pstmn.io/download/latest/linux64 -O postman.tar.gz
$ sudo tar -xzf postman.tar.gz -C /opt
$ rm postman.tar.gz
$ sudo ln -s /opt/Postman/Postman /usr/bin/postman

现在我们已经将文件放在了正确的位置,我们可以通过运行以下命令为它们创建一个桌面启动器:

$ cat > ~/.local/share/applications/postman.desktop <<EOL
[Desktop Entry]
Encoding=UTF-8
Name=Postman
Exec=postman
Icon=/opt/Postman/resources/app/assets/icon.png
Terminal=false
Type=Application 
Categories=Development;
EOL

创建了启动器后,您应该在已安装软件列表中看到一个 Postman 图标出现。

现在我们已经安装了 Postman,打开它,您将看到一个屏幕,询问您是否要注册。如果您愿意注册或不注册,完全取决于您;该服务是免费的,如果您需要测试向 API 发送数据,您会发现它非常有用。一旦您通过了注册或登录选项,您将看到一个类似以下的屏幕:

点击 BUILDING BLOCKS 下的 Request 选项;这将带您进入保存对话框。在这里,输入hello-name的请求名称,然后点击+Create Collection。在这里,创建一个名为Kubeless的集合,然后点击 Save to Kubeless 按钮。

首先,使用下拉菜单将 GET 更改为 POST,然后在标有输入请求 URL 的空格中输入http://hello-name.192.168.99.100.nip.io(或者如果不同的话,输入您的 URL)。现在我们已经定义了我们将要发布我们的数据,我们需要实际给 Postman 传递需要传递给我们的函数的数据。

要输入数据,请单击 Body,然后选择原始选项。当您选择原始选项时,输入字段将发生变化,您应该看到单词 Text 和旁边的下拉图标。单击这个图标,然后选中 JSON(application/json)选项。一旦更改,输入以下内容到主字段中:

{
  "name": "Russ" 
}

现在 Postman 已经配置为将 JSON 数据发送到我们的函数,您可以单击发送。这将发布我们定义的数据,然后在屏幕底部显示结果,以及 HTTP 状态和请求执行所需的时间,就像下面的截图一样:

单击保存按钮将保存设置,如果您想重新运行它们的话。

在我们继续下一节之前,我们应该整理一下我们的函数。要做到这一点,我们只需要运行:

$ kubeless ingress delete hello
$ kubeless function delete hello
$ kubeless ingress delete hello-name
$ kubeless function delete hello-name

这将删除我们的两个 hello world 函数和 Ingress 路由。您还可以在 Kubeless web 界面和 Kubernetes 仪表板中再次检查是否已删除了所有内容;您可以通过运行以下命令打开它们:

$ minikube service ui --namespace kubeless
$ minikube dashboard

如果您发现任何剩余的内容,无论是hello还是hello-name,您都可以从仪表板中删除服务、pod,甚至 Ingress 路由。

Twitter 示例

Kubeless GitHub 账户有一些更多的示例应用程序,这些应用程序不仅可以打印静态内容或转发您发送的数据。在这个例子中,我们将看看如何创建一个可以发布到 Twitter 账户的函数。

Twitter API

在我们看如何启动函数之前,我们需要为我们的函数生成密钥,以便对 Twitter 进行身份验证,然后发布到您的账户。为此,您需要以下内容:

  • Twitter 账户

  • 与账户注册的手机号码

如果您有它们,那么前往 Twitter 应用程序页面apps.twitter.com/将为您呈现一个表格(应用程序详情)-我使用了以下信息。然而,一些字段需要对您来说是唯一的;这些字段用*标记:

  • 名称*:MedialGlassesKubeless

  • 描述:使用 Kubeless 测试发布到 Twitter

  • 网站*:https://media-glass.es/

  • 回调 URL:留空

  • 开发者协议:同意协议

填写完上述信息后,点击“创建 Twitter 应用”按钮。创建应用程序后,您将被带到一个页面,允许您管理您的应用程序。页面上的一个选项卡是“密钥和访问令牌”;点击这个选项卡将显示您的消费者密钥(API 密钥)和消费者秘钥(API 秘钥)—请记下这些信息。

在页面底部,您将有一个按钮,允许您为您的帐户创建访问令牌和访问令牌秘钥;点击按钮将生成这些令牌—再次,请记下这些信息。

虽然以下示例将包含我生成的密钥,但它们已被撤销,您应该使用您自己的密钥。此外,由于它们允许对您的 Twitter 帐户进行读写访问,将它们存储在 GitHub、Gists 或其他版本控制软件等公开可访问的地方可能导致第三方未经您的许可就完全访问您的 Twitter 帐户。

将秘密添加到 Kubernetes

现在我们已经配置了 Twitter 应用程序,并且拥有了发布推文所需的所有令牌,我们需要将它们添加到 Kubernetes 中。Kubernetes 允许您定义秘密;这些是您的应用程序需要使用的 API 密钥和令牌等变量。但是,您可能不希望将它们放在源代码控制下或嵌入到您的应用程序中,因为相同代码的各种部署使用不同的密钥与 API 进行交互—例如,代码的开发版本使用与生产版本不同的 API 凭据。

要添加前一节中记下的令牌,您只需要运行以下命令,用您的令牌和密钥替换大写的占位符:

$ kubectl create secret generic twitter \
 --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的秘密,其中包含我们传递给命令的四个不同的键和令牌。您可以通过运行以下命令来列出这些秘密:

$ kubectl get secret

这将列出您的 Kubernetes 集群中的所有秘密,如下面终端输出所示:

这里有默认的 Kubernetes 服务账户令牌,包含三个项目,以及我们的twitter秘密,其中包含四个键和令牌。您也可以在 Kubernetes 仪表板中查看秘密:

从前面的屏幕截图中可以看出,您还可以通过单击眼睛图标来显示秘密。

Twitter 函数

现在我们的环境准备好了,我们可以部署函数了。为此,我们需要两个文件;第一个是requirements.txt文件,其中只包含两行:

python-twitter
kubernetes==2.0.0

requirements.txt文件让 Python 知道要与我们的代码一起部署的外部库。在我们的文件中,我们使用twitter库,以便可以轻松地发布推文,还使用kubernetes库来解码我们在上一节中创建的秘密。使用这些库意味着我们的代码非常简化,因为所有的繁重工作都发生在我们的核心函数之外。函数的代码如下:

import base64
import twitter

from kubernetes import client, config

config.load_incluster_config()

v1=client.CoreV1Api()

for secrets in v1.list_secret_for_all_namespaces().items:
    if secrets.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)

def handler(context):
    msg = context.json
    status = api.PostUpdate(msg['tweet'])

将此内容放入名为tweet.py的文件中。与以前一样,requirements.txttweet.py文件都可以在 GitHub 存储库Chapter04/twitter/中找到。

部署函数的命令在部署命令中有一个附加项。由于我们现在正在加载外部库,我们需要让 Kubeless 知道我们想要使用requirements.txt文件,方法是添加--dependencies标志:

$ kubeless function deploy twitter \
 --from-file tweet.py \
 --handler tweet.handler \
 --runtime python2.7 \
 --trigger-http \
 --dependencies requirements.txt

从以下终端输出中可以看出,在运行kubeless function list命令时现在列出了依赖项:

现在我们的函数已经部署,我们可以开始发推文了。要发送我们的第一条推文,您只需运行以下命令:

$ kubeless function call twitter --data '{"tweet": "Testing twitter function from Kubeless!"}'

您将不会收到任何反馈,但如果您转到您的 Twitter 帐户,您应该会看到这条推文:

您还可以使用 Postman 发送推文。首先,通过运行以下命令创建一个 Ingress 路由:

$ kubeless ingress create --function twitter twitter-ingress
$ kubeless ingress list

这将创建路由并给我们提供访问函数所需的主机:

现在我们可以打开 Postman,并且像以前一样配置它,但是这个文件将以下内容作为发布内容:

{
  "tweet": "Testing twitter function from Kubeless using @postmanclient!"
}

单击发送将发布推文,并且与使用kubeless function call命令调用函数时一样,不会给我们任何反馈:

检查 Twitter 应该会显示第二条推文,这次提到了@postmanclient。您可以在以下 URL 查看我的两条测试推文:

再次,在继续下一部分之前,我们应该删除我们的函数并整理一下:

$ kubeless function delete twitter
$ kubeless ingress delete twitter-ingress
$ kubectl delete secret twitter

另外,如果需要的话,你应该返回apps.twitter.com/并删除或撤销你的应用程序或令牌。

Kubeless 无服务器插件

回到第一章,无服务器景观,我们安装了 Serverless 框架来部署 AWS Lambda 函数;无服务器也适用于 Kubeless。

如果你还没有安装无服务器,这里是如何在我们正在涵盖的三个操作系统上安装它的快速回顾。

尽管已经尽一切努力确保以下说明适用于所有支持的平台,但由于插件所需的一些依赖项的兼容性问题,Kubeless 无服务器插件在基于 Windows 的操作系统上的运行成功程度有所不同。

对于 macOS 10.13 High Sierra,运行以下命令使用 Homebrew 安装 Node.js:

$ brew install node

如果你正在使用 Windows 10 专业版,你可以通过运行 Chocolatey 来安装 Node.js:

$ choco install nodejs

最后,如果你使用的是 Ubuntu 17.04,你可以使用以下命令安装 Node.js:

$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
$ sudo apt-get install -y nodejs

现在最新版本的 Node.js 已安装,我们可以使用Node Package Manager (NPM)通过运行以下命令来安装无服务器:

$ npm install -g serverless

一旦无服务器安装完成,你可以使用以下命令登录:

$ serverless login

现在无服务器已安装,我们可以通过运行以下命令启动演示 Kubeless 函数:

$ serverless create --template kubeless-python --path new-project
$ cd new-project
$ npm install

如果你没有跟着做,运行这些命令会得到以下输出:

这安装了 Kubeless 无服务器插件并创建了定义我们函数的serverless.yml文件。其中包含以下内容:

service: new-project

provider:
  name: kubeless
  runtime: python2.7

plugins:
  - serverless-kubeless

functions:
  hello:
    handler: handler.hello

正如你所看到的,这段代码告诉无服务器我们正在使用 Kubeless,并且它应该使用 Kubeless 插件。它还定义了一个名为hello的函数和处理程序。该函数可以在handler.py文件中找到。这包含以下代码,与我们在本章前面看过的 hello-world 示例非常相似:

import json

def hello(request):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": request.json
    }

    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response

现在我们有了示例函数,我们可以通过运行以下命令部署服务:

$ serverless deploy -v

服务部署完成后,最后一步是部署函数本身。要做到这一点,请运行:

$ serverless deploy function -f hello

使用无服务器本身来调用函数可能会导致以下错误,如果出现这种情况,不要担心:

$ serverless invoke --function hello --data '{"Kubeless": "Welcome!"}' -l

您仍然可以使用 Kubeless 访问该函数:

$ kubeless function list
$ kubeless function call hello --data '{"Kubeless": "Welcome!"}'

这将返回预期的结果:

要删除示例函数,请运行以下命令:

$ serverless remove

在完成本章之前,让我们看一个使用事件而不是 HTTP 的示例。在 GitHub 存储库的Chapter04/serverless-event/文件夹中,有一个监听事件的示例应用程序。

serverless.yml文件与之前的 HTTP 示例不同,除了处理程序外,还添加了一个包含触发器/主题的事件部分:

service: events

provider:
  name: kubeless
  runtime: python2.7

plugins:
  - serverless-kubeless

functions:
  events:
    handler: handler.events
    events:
      - trigger: 'hello_topic'

handler.py文件可能包含迄今为止我们所看到的最简单的代码:

def events(context):
    return context

要启动示例,只需从Chapter04/serverless-event/文件夹中运行以下命令:

$ npm install
$ serverless deploy -v
$ kubeless function list

从前面的终端输出中可以看出,我们有一个PubSub类型和一个hello_topic主题。现在我们可以通过运行以下命令在hello_topic主题中发布事件:

$ kubeless topic publish --topic hello_topic --data 'testing an event!'
$ kubeless topic publish --topic hello_topic --data 'and another event!'

最后,我们可以通过运行日志来检查这两个事件是否已经被处理:

$ serverless logs -f events

从以下输出中可以看出,事件已成功发布并由我们的测试函数处理:

在进入下一章之前,我们可以通过运行以下命令删除我们的 Kubeless Kubernetes 单节点集群:

$ minikube delete

摘要

在本章中,我们已经将 Kubeless 部署到了我们使用 Minikube 启动的单节点 Kubernetes 上。我们安装了 Kubernetes 命令行客户端和基于 Web 的界面。一旦集群部署并安装了工具,我们就在 Kubeless 安装上部署和执行函数。

在安装一个更有用的发布推文的函数之前,我们先安装了两个基本的测试函数。然后,我们看了一下如何使用 Serverless 框架与 Kubeless 进行交互。

在下一章中,我们将看一下一个名为Funktion的基于事件的框架。

第五章:使用 Funktion 进行无服务器应用程序

在我们继续在公共云中启动 Kubernetes 集群之前,我们将再看一个本地示例;这次我们将看一下 Funktion。我们将涵盖以下主题:

  • 介绍 Funktion

  • 安装和配置 Funktion

  • 使用 Funktion 运行我们的第一个函数

  • Twitter 流

在我们开始安装和配置 Funktion 之前,我们应该花点时间讨论一下它的功能,因为它与本书中涵盖的其他框架有些不同。

介绍 Funktion

Funktion 的标语将其描述为基于事件的 Kubernetes Lambda 编程。从表面上看,Funktion 似乎与 Kubeless 和我们在前几章讨论过的其他无服务器框架非常接近。然而,它有自己的特色,使其与我们正在研究的其他框架有所不同。

我们正在研究的大多数无服务器函数都支持两种基本事件类型:

  • HTTP:这是通过标准 HTTP 请求将数据传递给框架的地方;通常数据将被发布为 JSON 对象

  • 订阅:这是框架监听事件流中的主题的地方,例如,Kubeless 使用 Apache Kafka (kafka.apache.org/)

Funktion 扩展了事件类型的数量 - 实际上,它支持大约 200 种不同类型的事件。这是一个相当大的飞跃!它使用 Apache Camel (camel.apache.org/) 来实现这一点。Apache Camel 是一个开源的 Java 框架,作为开发人员的管道,允许他们摄取和发布数据。

为了让您了解 Apache Camel 和因此 Funktion 支持的一些事件流,以下是一些亮点:

  • AWS-SNS 支持与亚马逊的简单通知服务SNS)一起使用

  • Braintree 允许与 Braintree 支付网关服务进行交互

  • etcd 允许您与 etcd 键值存储进行交互

  • Facebook 开放了完整的 Facebook API

  • GitHub 允许您监听来自 GitHub 的事件

  • Kafka - 像 Kubeless 一样,您可以订阅 Kafka 流

  • Twitter 让您能够监听标签、帖子等

还有许多其他服务,如 LinkedIn、Slack、各种 SQL 和 NoSQL 数据库、来自 AWS 的 S3 的文件服务、Dropbox 和 Box 等等。

所有这些选择使其与我们一直在研究和将要研究的其他框架相比,成为一个非常好的选择。

Funktion 部署由几个不同的组件组成。首先是一个函数;这就是代码本身,由 Kubernetes ConfigMap 管理。

单独的函数本身并不是很有用,因为它只存在于 ConfigMap 中。因此,我们需要一个运行时,一个在调用时执行函数的 Kubernetes 部署。当 Funktion 操作员(稍后会详细介绍)检测到添加了新函数时,将自动创建运行时。

接下来,我们有一个连接器;这是一个事件源的表示,就像我们在本节前面讨论的那些一样——它包含有关事件类型、配置(如 API 凭据)以及数据搜索参数的信息。

然后我们有流程;这是一系列步骤,可以从调用函数的连接器中消耗事件。

最后,我们有Funktion操作员。这是在 Kubernetes 中运行的一个 pod,监视构成我们的 Funktion 部署的所有组件,如函数、运行时、连接器和流程。它负责创建提供 Funktion 功能的 Kubernetes 服务。

Funktion 是开源的,根据 Apache 许可证 2.0 发布;它是由 fabric8 开发的,fabric8 是 Red Hat 的 JBoss 中间件平台的上游项目。fabric8 本身是一个基于 Docker、Kubernetes 和 Jenkins 的面向 Java 的微服务平台。它也与 Red Hat 自己的 OpenShift 平台很好地配合。

现在我们对 Funktion 与其他框架的区别有了一些背景了,我们可以看看如何在我们的单节点 Kubernetes 集群上安装它。

安装和配置 Funktion

使用 Funktion 有三个步骤。首先,我们需要安装命令行。这是大部分部署和管理我们的 Funktion 部署的命令将被输入的地方。一旦命令行客户端安装完成,我们可以使用 Minikube 启动我们的单节点 Kubernetes 集群,然后使用 Funktion CLI 引导我们的环境。

命令行客户端

与我们正在介绍的许多框架一样,Funktion 是用 Go 语言编写的。这意味着我们的三个平台都有独立的可执行文件。

然而,在撰写本文时,无论是在 macOS 上使用 Homebrew 还是在 Windows 10 专业版上使用 Chocolatey,都没有可用的安装程序,这意味着我们将在所有三个平台上进行手动安装。

可从 GitHub 项目的发布页面上获取可执行文件,网址为github.com/funktionio/funktion/releases/。在撰写本文时,当前版本为 1.0.14,因此以下说明将涵盖该版本的安装;如果需要安装更新版本,请在以下命令中替换版本号。

让我们从如何在 macOS 上安装开始。

macOS 10.13 High Sierra

在 macOS 上安装很简单,因为该项目已发布了未压缩的独立可执行文件。我们只需要下载正确的软件包并使其可执行。要做到这一点,请运行以下命令:

$ curl -L https://github.com/funktionio/funktion/releases/download/v1.0.14/funktion-darwin-amd64 > /usr/local/bin/funktion
$ chmod +x /usr/local/bin/funktion

现在,命令行工具已安装,我们可以通过运行以下命令来测试它:

$ funktion version

Funktion 版本将返回如下:

如您所见,虽然安装过程非常简单,但软件包不在 Homebrew 中可用也有一个缺点。如果在 Homebrew 中可用,那么更新到较新版本将更容易,因为 Homebrew 会在您运行时负责检查和安装升级:

$ brew update
$ brew upgrade

目前,如果需要升级,您将不得不删除当前版本并下载新版本来替换它。

Windows 10 专业版

在 Windows 上安装 Funktion 命令行客户端的过程与 macOS 类似。首先,以管理员用户身份打开 PowerShell 窗口,方法是从任务栏中的 PowerShell 菜单中选择以管理员身份运行。一旦打开,您应该看到您在文件夹C:\WINDOWS\system32中;如果没有,请运行:

$ cd C:\WINDOWS\system32

一旦您在C:\WINDOWS\system32文件夹中,请运行以下命令:

$ Invoke-WebRequest -Uri https://github.com/funktionio/funktion/releases/download/v1.0.14/funktion-windows-amd64.exe -UseBasicParsing -OutFile funktion.exe

然后,您应该能够通过运行以下命令来检查已安装的 Funktion 命令行客户端的版本:

$ funktion version

Funktion 版本将返回如下:

同样,由于我们没有使用软件包管理器来安装 Funktion,因此如果要升级,您将不得不删除旧的可执行文件,然后重复安装过程,并确保更新 URL 中的版本号以反映您所需的版本。

Ubuntu 17.04

最后,我们有 Ubuntu 17.04。安装过程与我们为 macOS 执行的命令基本相同。但是,要确保我们下载正确的可执行文件,并且在/usr/local/bin文件夹的权限在操作系统之间略有不同时,我们还需要使用sudo命令:

$ sudo sh -c "curl -L https://github.com/funktionio/funktion/releases/download/v1.0.14/funktion-linux-amd64 > /usr/local/bin/funktion"
$ sudo chmod +x /usr/local/bin/funktion

下载并使其可执行后,您应该能够运行:

$ funktion version

你应该看到类似以下的内容:

现在我们在三个操作系统上都安装了命令行客户端,我们可以继续部署。

启动单节点 Kubernetes 集群

你可能已经注意到,我们再次发现自己处于一个位置,现在可以在任何操作系统上使用相同的命令。这意味着本章剩余的命令将能够在我们的三个目标操作系统上运行。

在使用 Minikube 启动我们的单节点 Kubernetes 集群之前,可以通过运行以下命令检查是否有任何更新。macOS 10.13 High Sierra 用户可以运行:

$ brew update
$ brew upgrade

然后,要检查和更新 Minikube,请运行以下命令,从以下开始:

$ brew cask outdated

这将向您呈现可以更新的软件包列表。如果 Minikube 在列表中,请运行以下命令:

$ brew cask reinstall minikube

Windows 10 专业版用户可以运行:

$ choco upgrade all

Ubuntu 17.04 用户需要检查第三章中的发布页面详细信息,在本地安装 Kubernetes,删除旧的二进制文件,并使用更新的版本重复安装过程。

一旦您检查了 Minikube 的更新,可以通过运行以下命令启动您的集群:

$ minikube start

根据第三章,在本地安装 Kubernetes和第四章,介绍 Kubeless 功能,这将启动单节点 Kubernetes 集群,并配置您的本地 Kubernetes 客户端与其交互。如果您已经更新了 Minikube,您可能还会注意到下载并安装了一个更新版本的 Kubernetes:

如果你已经升级了 Minikube,可以使用以下命令检查一切是否正常运行:

$ minikube status
$ kubectl get all
$ minikube dashboard

现在我们的单节点 Kubernetes 集群已经重新启动运行,Funktion 安装的最后阶段是引导部署。

引导 Funktion

安装 Funktion 非常简单,事实上,只需要一个命令:

$ funktion install platform

这将给出以下输出:

一两分钟后,您应该能够运行:

$ kubectl get pods
$ kubectl get deployments

上述命令将检查部署的状态:

您还应该能够在 Kubernetes 仪表板中看到 Pods 和 Deployments:

运行以下命令应该返回一个空列表:

$ funktion get function

这证明了 Funktion 命令行客户端可以连接到您新安装的 Funktion 部署并与其交互。

部署一个简单的函数

现在我们的 Funktion 部署已经运行起来了,我们可以看一下部署一个非常简单的 hello world 示例。在支持本书的 GitHub 存储库中的/Chapter05/hello-world/src文件夹中,您会找到一个名为hello.js的文件。这个文件包含以下代码:

module.exports = function(context, callback) {
  var name = context.request.query.name || context.request.body || "World";
  callback(200, "Hello " + name + "!!");
};

/Chapter05/hello-world/文件夹中运行以下命令将使用上述代码创建我们的第一个函数:

$ funktion create fn -f src/hello.js

输出应该如下所示:

从终端输出中可以看出,这创建了一个名为hellofunction。现在,我们运行以下命令:

$ funktion get function

这应该返回一些结果。从以下输出中可以看出,我们现在可以看到NAMEPODSURL列出:

我们可以运行以下命令来仅返回函数的URL,或在浏览器中打开它:

$ funktion url fn hello
$ funktion url fn hello -o

您应该看到以下结果:

打开的浏览器窗口显示如下。我相信您会同意这不是最令人兴奋的页面:

但它确实证明了我们的函数正在工作并显示内容。您可以通过运行以下命令来显示函数的日志:

$ funktion logs function hello

这将实时将日志内容流式传输到您的终端窗口。您可以通过刷新浏览器几次来查看,您应该看到您的页面请求与内部健康检查请求一起被记录。

现在我们已经创建了我们的第一个函数,我们可以安装一些连接器。要这样做,请运行以下命令:

$ funktion install connector http4 timer twitter

现在我们安装了一些连接器,我们可以创建一个流程。我们的第一个流程将使用定时器连接器:

$ funktion create flow timer://foo?period=5000 http://hello/

这将创建一个名为foo的流程,每5000毫秒执行一次,目标是我们称为hello的函数。要获取有关流程的信息,可以运行以下命令:

$ funktion get flow

您应该看到以下内容:

正如您所看到的,流程称为timer-foo1;我们在与其交互时需要使用此名称。例如,您可以通过运行以下命令来检查流程的日志:

$ funktion logs flow timer-foo1

或者在 Kubernetes 仪表板中,您可以找到名为timer-foo1的 pod,并在那里检查日志:

通过运行以下命令检查函数的日志:

$ funktion logs function hello

您应该看到每五秒有一个来自用户代理为Apache-HttpClient/4.5.2的客户端的页面请求。这是计时器流程:

要删除流程,只需运行:

$ funktion delete flow timer-foo1

这将删除运行连接器的 pod,并且您的函数将停止接收自动请求。

返回 Kubernetes 仪表板,单击 Config Maps 应该显示 Funktion 创建的所有内容的列表。正如您所看到的,Funktion 的大部分部分都有一个 ConfigMap:

单击hello的 Config Maps 将显示类似以下页面的内容:

正如您所看到的,这包含了我们函数的代码,并且它已自动检测到它是用 Node.js 编写的,还有它是从src文件夹部署的。

在查看更高级示例之前,还有一件可能会让您感兴趣的事情,那就是与Chrome Dev工具的集成。要做到这一点,请运行以下命令:

$ funktion debug fn hello

这将在前台打开一个进程,并为您提供一个 URL 放入 Google Chrome 中:

一旦您打开 Google Chrome 并指向您的函数,您可以执行诸如直接在浏览器中编辑代码之类的任务:

使用 Chrome Dev 工具进行的任何更改都将直接在 pod 内进行,并且如果重新启动 pod,这些更改将不会持久保存;这应该纯粹用于测试。

要删除我们的hello函数,我们只需要运行:

$ funktion delete function hello

这应该让我们得到一个干净的安装,准备进行更高级的示例。

Twitter 流

在上一节中我们安装了 Twitter 连接器,让我们看看如何配置它来拉取一些数据。首先,您可以通过运行以下命令查看连接器的所有可配置选项:

$ funktion edit connector twitter -l

你应该看到类似以下的终端输出:

如您所见,您可以配置代理,并提供accessTokenaccessTokenSecretconsumerKeyconsumerSecret。您应该从上一章中获得这些信息。如果没有,那么请使用第四章中的说明重新生成它们,介绍 Kubeless 功能

就像我将用来演示您需要运行的命令的令牌和密钥一样,前面截图中列出的详细信息是默认的虚拟占位符详细信息,不是有效的。

要使用您自己的详细信息更新连接器,请运行以下命令,并确保用您自己的详细信息替换它们:

$ funktion edit connector twitter \
 accessToken=1213091858-REJvMEEUeSoGA0WPKp7cv8BBTyTcDeRkHBr6Wpj \
 accessTokenSecret=WopER9tbSJtUtASEz62lI8HTCvhlYBvDHcuCIof5YzyGg \
 consumerKey=aKiWFB6Q7Ck5byHTWu3zHktDF \
 consumerSecret=uFPEszch9UuIlHt6nCxar8x1DSYqhWw8VELqp3pMPB571DwnDg

您应该收到连接器已更新的确认。现在,我们可以启动使用 Twitter 适配器的流程。为此,我们应该运行以下命令:

$ funktion create flow --name twitsearch "twitter://search?type=polling&keywords=kubernetes&delay=120s"
$ funktion get flows

我们将看到以下内容:

一旦您启动了 pod,您可以通过运行以下命令来检查日志:

$ funktion logs flow twitsearch

或者通过在仪表板中查看twitsearch pod 的日志:

如您所见,Camel 正在打印包含单词 Kubernetes 的一系列推文。您的应用程序可以订阅此流,并对推文进行处理。最后,运行以下命令将删除流程:

$ funktion delete flow twitsearch

然后,您可以使用minikube delete命令删除您的 Minikube 机器。

总结

在本章中,我们简要介绍了 Funktion。我们安装了命令行客户端,然后将其安装在我们的单节点 Kubernetes 集群上。部署后,我们启动了一个测试函数,并与其交互,然后使用其中的一个事件流来搜索包含 Kubernetes 的推文。

Funktion 仍处于早期开发阶段,目前拥有一个小而活跃的社区,他们在项目的 GitHub 页面上做出贡献。因此,在撰写本文时,还没有太多利用 Funktion 支持的 Apache Camel 的许多流程的完整应用实例。如果您计划编写任何摄取数据然后处理数据的应用程序,我建议您密切关注 Funktion。

在下一章中,我们将讨论如何将我们的 Kubernetes 集群从本地单节点扩展到托管在公共云上的多节点集群。

第六章:在云中安装 Kubernetes

到目前为止,我们一直在本地机器上运行 Kubernetes。这确实有一些缺点,其中之一是处理能力。我们将开始研究一些更复杂和强大的框架,因此我们需要一些额外的能力。因此,我们将尝试在几个不同的公共云上安装 Kubernetes,每次使用不同的工具:

  • 在 DigitalOcean 上启动 Kubernetes

  • 在 AWS 上启动 Kubernetes

  • 在 Microsoft Azure 上启动 Kubernetes

  • 在 Google 云平台上启动 Kubernetes

然后,我们将研究公共云提供商之间的差异,并尝试在其中一个平台上安装 Kubeless。

在 DigitalOcean 上启动 Kubernetes

我们将首先研究的公共云平台是 DigitalOcean。DigitalOcean 与我们将在接下来的章节中研究的三大云平台有所不同,因为它的功能较少。例如,在产品页面上,DigitalOcean 列出了八个功能,而 AWS 产品页面列出了十八个主要领域,每个领域又分为六个或更多的功能和服务。

不要因此而认为 DigitalOcean 比我们在本章中将要研究的其他公共云提供商差。

DigitalOcean 的优势在于它是一个非常简单易用的托管平台。通过其直观的 API 和命令行工具,支持服务和出色的管理界面,可以在不到一分钟内轻松启动功能强大但价格竞争力极强的虚拟机。

创建 Droplets

Droplets 是 DigitalOcean 对其计算资源的术语。对于我们的 Kubernetes,我们将启动三个 Ubuntu 17.04 Droplets,每个 Droplet 配备 1GB 的 RAM,1 个 CPU 和 30GB 的 SSD 存储。

在撰写本文时,这个由三个 Droplet 组成的集群每月大约需要花费 30 美元才能保持在线。如果您打算在需要时保持在线,那么这三个 Droplets 每小时的费用将是 0.045 美元。

在您创建任何 Droplets 之前,您需要一个帐户;您可以在cloud.digitalocean.com/registrations/new注册 DigitalOcean。注册后,在您进行任何其他操作之前,我建议您立即在您的帐户上启用双因素身份验证。您可以在帐户安全页面上启用此功能cloud.digitalocean.com/settings/security/

启用双因素身份验证将为您提供额外的安全级别,并帮助保护您的帐户免受任何未经授权的访问以及意外费用的影响。毕竟,您不希望有人登录并使用您的帐户创建 25 个最昂贵的 Droplets,并由您来支付账单。

双因素身份验证通过向您的帐户引入第二级身份验证来工作;通常这是一个由应用程序(如 Google Authenticator)在您的移动设备上生成的四位或六位代码,或者是由您尝试登录的服务发送的短信。这意味着即使您的密码被泄露,攻击者仍然需要访问您的移动设备或号码。

接下来,我们需要生成一个 SSH 密钥并将其上传到 DigitalOcean。如果您已经有一个带有 SSH 密钥的帐户,可以跳过此任务。如果您没有密钥,请按照给定的说明操作。

如果您使用的是 macOS High Sierra 或 Ubuntu 17.04,则可以运行以下命令:

$ ssh-keygen -t rsa

这将要求您选择一个位置来存储您新生成的私钥和公钥,以及一个密码。密码是可选的,但如果您的 SSH 密钥的私有部分落入错误的手中,它确实会增加另一层安全性:

生成密钥后,您需要记下密钥的公共部分。为此,请运行以下命令,并确保更新密钥路径以匹配您自己的路径:

$ cat /Users/russ/.ssh/id_rsa.pub

您应该看到类似以下的内容:

请确保不要分享或发布您的 SSH 密钥的私有部分(文件名不包含.pub)。这用于对公钥的公共部分进行身份验证。如果这落入错误的手中,他们将能够访问您的服务器/服务。

对于 Windows 10 专业版用户来说,你很可能正在使用 PuTTY 作为你的 SSH 客户端。如果你没有 PuTTY,你可以通过运行以下命令来安装它:

$ choco install putty

一旦 PuTTY 安装完成,你可以通过运行以下命令打开 PuTTYgen 程序:

$ PUTTYGEN.exe

打开后,点击生成并按照提示在空白区域移动你的光标。一秒钟后,你应该会生成一个密钥:

如前面的截图所示,你可以选择添加一个密码,这将用于解锁你的密钥的私有部分;再次强调,这是可选的。

点击保存公钥,也保存私钥,并记下公钥的内容。

现在你有了你的公钥,我们需要让 DigitalOcean 拥有一份副本。为此,转到安全页面,你可以在cloud.digitalocean.com/settings/security/找到它,然后点击添加 SSH 密钥。这将弹出一个对话框,要求你提供你的公钥内容并命名它。填写两个表单字段,然后点击添加 SSH 密钥按钮。

现在你已经为你的账户分配了一个 SSH 密钥,你可以使用它来创建你的 Droplets,并且无需密码即可访问它们。要创建你的 Droplets,点击屏幕右上角的创建按钮,然后从下拉菜单中选择 Droplets。

在 Droplet 创建页面上有几个选项:

  • 选择一个镜像:选择 Ubuntu 16.04 镜像

  • 选择一个大小:选择每月 10 美元的选项,其中包括 1GB、1CPU 和 30GB SSD

  • 添加块存储:保持不变

  • 选择数据中心区域:选择离你最近的区域;我选择了伦敦,因为我在英国

  • 选择附加选项:选择私人网络连接。

  • 添加你的 SSH 密钥:选择你的 SSH 密钥

  • 完成并创建:将 Droplets 的数量增加到3,现在保持主机名不变

填写完前面的部分后,点击页面底部的创建按钮。这将启动你的三个 Droplets,并向你反馈它们创建过程的进度。一旦它们启动,你应该会看到类似以下页面的内容:

如你所见,我有三个 Droplets,它们的 IP 地址,还有一条很好的激励性信息。现在我们可以开始使用kubeadm部署我们的 Kubernetes 集群。

使用 kubeadm 部署 Kubernetes

首先,我们需要登录到我们的三个 Droplets 中的一个;我们登录的第一台机器将是我们的 Kubernetes 主节点。

$ ssh root@139.59.180.255

登录后,以下两个命令检查软件包是否有更新并应用它们:

$ apt-get update
$ apt-get upgrade

现在我们已经是最新的了,我们可以安装先决条件软件包。要做到这一点,请运行以下命令:

$ apt-get install docker.io curl apt-transport-https

您可能会注意到,我们使用的是作为核心 Ubuntu 16.04 软件包存储库的一部分分发的 Docker 版本,而不是官方的 Docker 发布版。这是因为kubeadm不支持更新版本的 Docker,并且推荐的版本是 1.12。目前,Ubuntu 16.04 支持的 Docker 版本是 1.12.6。

现在我们已经安装了先决条件,我们可以通过运行以下命令来添加 Kubernetes 存储库:

$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
$ cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF

curl命令为存储库添加了 GPG 密钥,cat命令创建了存储库文件。现在存储库已经就位,我们需要通过运行以下命令更新我们的软件包列表并安装kubeadmkubeletkubectl

$ apt-get update
$ apt-get install kubelet kubeadm kubectl

安装完成后,您可以通过运行以下命令来检查已安装的kubeadm的版本:

$ kubeadm version

现在我们已经安装了所需的一切,我们可以通过运行以下命令来引导我们的 Kubernetes 主节点:

$ kubeadm init

这将需要几分钟的时间运行,并且您将得到一些非常冗长的输出,让您知道kubeadm已经完成了哪些任务:

完成后,您应该看到以下消息,但是带有您的令牌等:

记下底部的kubeadm join命令,我们很快会看到它。我们应该运行消息中提到的命令:

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

接下来,我们需要启用 Pod 网络。您可以选择几种选项,所有这些选项都为您的 Kubernetes 集群提供了多主机容器网络:

对于我们的安装,我们将使用 Weave Net。要安装它,只需运行以下命令:

$ export kubever=$(kubectl version | base64 | tr -d '\n')
$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$kubever"

如您所见,这使用了kubectl命令来部署 pod 网络。这意味着我们的基本 Kubernetes 集群已经运行起来了,尽管只在单个节点上。

为了准备其他两个集群节点,打开两者的 SSH 会话,并在两者上运行以下命令:

$ apt-get update
$ apt-get upgrade
$ apt-get install docker.io curl apt-transport-https
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
$ cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
$ apt-get update
$ apt-get install kubelet kubeadm kubectl

如您所见,这些是我们在主节点上执行的确切一组命令,使我们能够执行kubeadm命令的命令。您可能已经猜到,我们将运行我们初始化主节点时收到的kubeadm join命令,而不是运行kubeadm init。对我来说,该命令如下:

$ kubeadm join --token 0c74f5.4d5492bafe1e0bb9 139.59.180.255:6443 --discovery-token-ca-cert-hash sha256:3331ba91e4a3a887c99e59d792b9f031575619b4646f23d8fe2938dc50f89491

您需要运行收到的命令,因为令牌将绑定到您的主节点。在两个节点上运行该命令,您应该会看到类似以下终端输出的内容:

一旦您在剩余的两个节点上运行了该命令,请返回到您的主节点并运行以下命令:

$ kubectl get nodes

这应该返回您的 Kubernetes 集群中的节点列表:

如您所见,我们有一个由三个 Droplets 组成的集群。唯一的缺点是,目前我们必须登录到我们的主节点才能与我们的集群交互。幸运的是,这很容易解决,我们只需要下载集群admin.conf文件的副本。

要在 macOS High Sierra 或 Ubuntu 17.04 上执行此操作,请运行以下命令,确保将 IP 地址替换为您的主节点的 IP 地址:

$ scp root@139.59.180.255:/etc/kubernetes/admin.conf 

如果您使用的是 Windows 10 专业版,您将需要使用诸如 WinSCP 之类的程序。要安装它,请运行以下命令:

$ choco install winscp

安装后,通过输入WINSCP.exe来启动它,然后按照屏幕提示连接到您的主节点并下载admin.conf文件,该文件位于/etc/kubernetes/中。

一旦您有了admin.conf文件的副本,您就可以在本地运行以下命令来查看您的三节点 Kubernetes 集群:

$ kubectl --kubeconfig ./admin.conf get nodes

一旦我们确认可以使用本地的kubectl副本连接,我们应该将配置文件放在适当的位置,这样我们就不必每次使用--kubeconfig标志。要做到这一点,请运行以下命令(仅适用于 macOS 和 Ubuntu):

$ mv ~/.kube/config ~/.kube/config.mini
$mv admin.conf ~/.kube/config

现在运行以下命令:

$ kubectl get nodes

这应该显示您的三个 Droplets:

删除集群

要删除集群,只需登录到您的 DigitalOcean 控制面板,然后单击每个 Droplet 右侧的更多下拉菜单中的销毁链接。然后按照屏幕上的说明进行操作。确保销毁所有三个 Droplets,因为它们在线时会产生费用。

这是在低规格服务器上手动部署 Kubernetes。在接下来的几节中,我们将看看如何在其他公共云中部署 Kubernetes,首先是 AWS。

在 AWS 中启动 Kubernetes

我们可以使用几种工具在 AWS 上启动 Kubernetes 集群;我们将介绍一个叫做kube-aws的工具。不幸的是,kube-aws不支持基于 Windows 的机器,因此以下说明只适用于 macOS High Sierra 和 Ubuntu 17.04。

kube-aws是一个命令行工具,用于生成 AWS CloudFormation 模板,然后用于启动和管理 CoreOS 集群。然后将 Kubernetes 部署到 CoreOS 实例的集群中。

AWS CloudFormation 是亚马逊的本地脚本工具,允许您以编程方式启动 AWS 服务;它几乎涵盖了所有 AWS API。CoreOS 是一个专注于运行容器的操作系统。它的占用空间极小,并且设计为可以在云提供商上直接进行集群和配置。

设置

在第一章中,无服务器景观,我们看了一下创建 Lambda 函数。为了配置这个,我们安装了 AWS CLI。我假设您仍然配置了这个,并且您配置的 IAM 用户具有管理员权限。您可以通过运行以下命令来测试:

$ aws ec2 describe-instances

这应该返回类似以下内容:

我们需要将我们的 SSH 密钥导入 AWS。要做到这一点,打开 AWS 控制台(console.aws.amazon.com/)。登录后,从页面顶部的服务菜单中选择 EC2。一旦您进入 EC2 页面,请确保使用页面右上角的区域下拉菜单选择了正确的区域。我将使用欧盟(爱尔兰),也就是 eu-west-1。

现在我们在正确的区域,点击密钥对选项,在左侧菜单的 NETWORK & SECURITY 部分下可以找到。页面加载后,点击导入密钥对按钮,然后像 DigitalOcean 一样,输入您的密钥对的名称,并在其中输入您的id_rsa.pub文件的内容。

接下来,我们需要一个 AWS KMS 存储。要创建这个,运行以下命令,确保根据需要更新您的区域:

$ aws kms --region=eu-west-1 create-key --description="kube-aws assets"

这将返回几个信息,包括一个 Amazon 资源名称(ARN)。记下这个信息以及KeyId

接下来,我们需要一个 Amazon S3 存储桶。使用 AWS CLI 运行以下命令来创建一个,确保更新区域,并且使存储桶名称对您来说是唯一的:

$ aws s3api --region=eu-west-1 create-bucket --bucket kube-aws-russ --create-bucket-configuration LocationConstraint=eu-west-1

现在我们已经导入了我们的公共 SSH 密钥,有了 KMS ARN 和一个 S3 存储桶,我们只需要决定集群的 DNS 名称。

我将使用kube.mckendrick.io,因为我已经在 Amazon Route 53 DNS 服务上托管了mckendrick.io。您应该选择一个可以在其上配置 CNAME 的域或子域,或者一个托管在 Route 53 上的域。

现在我们已经掌握了基础知识,我们需要安装kube-aws二进制文件。要做到这一点,如果您正在运行 macOS High Sierra,您只需要运行以下命令:

$ brew install kube-aws

如果您正在运行 Ubuntu Linux 17.04,您应该运行以下命令:

$ cd /tmp
$ wget https://github.com/kubernetes-incubator/kube-aws/releases/download/v0.9.8/kube-aws-linux-amd64.tar.gz
$ tar zxvf kube-aws-linux-amd64.tar.gz
$ sudo mv linux-amd64/kube-aws /usr/local/bin
$ sudo chmod 755 /usr/local/bin/kube-aws

安装完成后,运行以下命令确认一切正常:

$ kube-aws version

在撰写本文时,当前版本为 0.9.8。您可以在发布页面上检查更新版本:github.com/kubernetes-incubator/kube-aws/releases/

使用 kube-aws 启动集群

在我们开始创建集群配置之前,我们需要创建一个工作目录,因为将会创建一些工件。让我们创建一个名为kube-aws-cluster的文件夹并切换到它:

$ mkdir kube-aws-cluster
$ cd kube-aws-cluster

现在我们在我们的工作目录中,我们可以创建我们的集群配置文件。要做到这一点,运行以下命令,确保用之前部分收集的信息替换值:

如果您没有使用 Route 53 托管的域,删除--hosted-zone-id标志。

kube-aws init \
 --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 集群使用的证书。要做到这一点,请运行以下命令:

$ kube-aws render credentials --generate-ca

接下来,我们需要生成 AWS CloudFormation 模板。要做到这一点,请运行以下命令:

$ kube-aws render stack

这将在名为stack-templates的文件夹中创建模板:

在您的工作目录中运行ls应该会显示已创建的几个文件和文件夹:

最后,我们可以运行以下命令来验证并上传文件到我们的 S3 存储桶,记得用您自己的存储桶名称更新命令:

$ kube-aws validate --s3-uri s3://kube-aws-russ/kube-aws-cluster

现在我们可以启动我们的集群。要做到这一点,只需运行以下命令,确保您更新存储桶名称:

$ kube-aws up --s3-uri s3://kube-aws-russ/kube-aws-cluster

这将开始使用 AWS CloudFormation 工具启动我们的集群:

这个过程将需要几分钟;您可以在 AWS 控制台的命令行上查看其进度。要在控制台中查看它,请转到服务菜单并选择 CloudFormation。一旦打开,您应该会看到列出的一些堆栈;选择其中一个,然后单击事件选项卡:

从事件和资源选项卡中可以看出,后台有很多事情正在进行。有:IAM 角色、VPC 和网络、EC2 实例、负载均衡器、DNS 更新、自动缩放组等正在创建。

一旦完成,您应该会看到三个 CloudFormation 堆栈,一个主要的称为kube-aws-cluster,另外两个嵌套堆栈,一个称为kube-aws-cluster-Controlplane,另一个称为kube-aws-cluster-Nodepool1。两个嵌套堆栈都将在其名称后附加一个唯一的 ID。您将在命令行上收到集群已启动的确认:

在我们的工作目录中运行以下命令将列出 AWS Kubernetes 集群中的节点:

$ kubectl --kubeconfig=kubeconfig get nodes

Sock Shop

要测试我们的部署,我们可以启动 Sock Shop。这是由 Weave 编写的演示微服务应用程序。您可以在以下项目页面找到它:microservices-demo.github.io/

要启动商店,我们需要从工作目录中运行以下命令:

$ kubectl --kubeconfig=kubeconfig create namespace sock-shop
$ kubectl --kubeconfig=kubeconfig apply -n sock-shop -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"

启动需要几分钟的时间;您可以通过运行以下命令来检查进度:

$ kubectl --kubeconfig=kubeconfig -n sock-shop get pods

等待每个 pod 获得运行状态,如下截图所示:

然后,我们应该能够访问我们的应用程序。要做到这一点,我们需要将其暴露给互联网。由于我们的集群在 AWS 中,我们可以使用以下命令启动一个弹性负载均衡器,并让它指向我们的应用程序:

$ kubectl --kubeconfig=kubeconfig -n sock-shop expose deployment front-end --type=LoadBalancer --name=front-end-lb

要获取有关我们的负载均衡器的信息,我们可以运行以下命令:

$ kubectl --kubeconfig=kubeconfig -n sock-shop get services front-end-lb

正如您所见,应用程序正在端口8079上暴露,但我们无法完全看到弹性负载均衡器的 URL。要获得这个,我们可以运行以下命令:

$ kubectl --kubeconfig=kubeconfig -n sock-shop describe services front-end-lb

现在我们知道了弹性负载均衡器的 URL,我们可以将其输入到浏览器中,以及端口。对我来说,完整的 URL 是http://a47ecf69fc71411e7974802a5d74b8ec-130999546.eu-west-1.elb.amazonaws.com:8079/(此 URL 已不再有效)。

输入您的 URL 应该显示以下页面:

要删除 Sock Shop 应用程序,只需运行:

$ kubectl --kubeconfig=kubeconfig delete namespace sock-shop

这将删除我们创建的所有 pod、服务和弹性负载均衡器。

删除集群

让我们不要忘记,当集群正在运行时,它会花费我们的钱。要删除集群和 CloudFormation 脚本创建的所有服务,请运行以下命令:

$ kube-aws destroy

您将收到确认,CloudFormation 堆栈正在被移除,并且这将需要几分钟的时间。我建议您在 AWS 控制台上的 CloudFormation 页面上进行双重检查,以确保在移除堆栈过程中没有出现任何错误,因为任何仍在运行的资源可能会产生费用。

我们还需要删除我们创建的 S3 存储桶和 KMS;要做到这一点,请运行以下命令:

$ aws s3 rb s3://kube-aws-russ --force
$ aws kms --region=eu-west-1 disable-key --key-id 2d54175d-41e1-4865-ac57-b3c40d0c4c3f

您可以从您在本节早期创建 KMS 时所做的备注中找到--key-id

虽然这次我们不必手动配置我们的集群,或者实际上登录到任何服务器,但启动我们的集群的过程仍然是非常手动的。对于我们的下一个公共云提供商 Microsoft Azure,我们将会看到更本地的部署。

在 Microsoft Azure 中启动 Kubernetes

在第一章中,《无服务器景观》,我们看了微软 Azure Functions;然而,我们没有进展到比 Azure web 界面更多的地方来启动我们的 Function。要使用Azure 容器服务AKS),我们需要安装 Azure 命令行客户端。

值得一提的是,AKS 目前不支持 Windows 10 PowerShell Azure 工具。但是,如果您使用 Windows,不用担心,因为命令行客户端的 Linux 版本可以通过 Azure web 界面获得。

准备 Azure 命令行工具

Azure 命令行工具可以通过 macOS High Sierra 上的 Homebrew 获得,这样安装就像运行以下两个命令一样简单:

$ brew update
$ brew install azure-cli

Ubuntu 17.04 用户可以运行以下命令:

$ echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ wheezy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
$ sudo apt-key adv --keyserver packages.microsoft.com --recv-keys 52E16F86FEE04B979B07E28DB02C46DF417A0893
$ sudo apt-get install apt-transport-https
$ sudo apt-get update && sudo apt-get install azure-cli

安装完成后,您需要登录您的账户。要做到这一点,运行以下命令:

$ az login

当您运行该命令时,您将获得一个 URL,即aka.ms/devicelogin还有一个要输入的代码。在浏览器中打开 URL 并输入代码:

登录后,关闭浏览器窗口并返回到命令行,在几秒钟后,您将收到确认消息,说明您已经以您在浏览器中登录的用户身份登录。您可以通过运行以下命令来再次检查:

$ az account show

如前所述,Windows 用户可以使用 Azure web 界面访问他们自己的 bash shell。要做到这一点,登录并点击顶部菜单栏中的>_ 图标,选择 bash shell,然后按照屏幕提示操作。在设置结束时,您应该看到类似以下内容:

现在我们已经安装并连接到我们的账户的命令行工具,我们可以启动我们的 Kubernetes 集群。

启动 AKS 集群

首先,我们需要注册 AKS 服务。要做到这一点,运行以下命令:

$ az provider register -n Microsoft.ContainerService

注册需要几分钟的时间。您可以通过运行以下命令来检查注册的状态:

$ az provider show -n Microsoft.ContainerService

一旦看到registrationStateRegistered,您就可以开始了。要启动集群,我们首先需要创建一个资源组,然后创建集群。目前,AKS 在ukwestwestus2都可用:

$ az group create --name KubeResourceGroup --location ukwest
$ az aks create --resource-group KubeResourceGroup --name AzureKubeCluster --agent-count 1 --generate-ssh-keys

一旦您的集群启动,您可以运行以下命令配置您的本地kubectl副本以对集群进行身份验证:

$ az aks get-credentials --resource-group KubeResourceGroup --name AzureKubeCluster

最后,您现在可以运行以下命令,开始与您的集群进行交互,就像与任何其他 Kubernetes 集群一样:

$ kubectl get nodes

您会注意到我们只有一个节点;我们可以通过运行以下命令添加另外两个节点:

$ az aks scale --resource-group KubeResourceGroup --name AzureKubeCluster --agent-count 3
$ kubectl get nodes

您应该能够在 Azure Web 界面中看到所有已启动的资源:

现在我们的集群中有三个节点,让我们启动Sock Shop demo应用程序。

袜店

这些命令与我们之前运行的命令略有不同,因为我们不必为kubectl提供配置文件:

$ kubectl create namespace sock-shop
$ kubectl apply -n sock-shop -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"

再次,您可以通过运行以下命令来检查 pod 的状态:

$ kubectl -n sock-shop get pods

一旦所有的 pod 都在运行,您可以通过运行以下命令暴露应用程序:

$ kubectl -n sock-shop expose deployment front-end --type=LoadBalancer --name=front-end-lb
$ kubectl -n sock-shop get services front-end-lb
$ kubectl -n sock-shop describe services front-end-lb

这应该给您一个端口和 IP 地址。从前面的输出中可以看出,这给了我一个 URL http://51.141.28.140:8079/,将其放入浏览器中显示了 Sock Shop 应用程序。

要删除应用程序,我只需要运行:

$ kubectl delete namespace sock-shop

删除集群

与其他云服务一样,当您的 AKS 节点在线时,将按小时收费。完成集群后,您只需删除资源组;这将删除所有相关的服务:

$ az aks delete --resource-group KubeResourceGroup --name AzureKubeCluster
$ az group delete --name KubeResourceGroup

删除后,转到 Azure Web 界面,并手动删除任何其他剩余的资源/服务。我们接下来要看的下一个和最后一个公共云是 Google Cloud。

在 Google Cloud 平台上启动 Kubernetes

正如您所期望的,Kubernetes 在 Google Cloud 上得到了原生支持。在继续之前,您需要一个帐户,您可以在cloud.google.com/注册。一旦您设置好了您的帐户,类似于本章中我们一直在研究的其他公共云平台,我们需要配置命令行工具。

安装命令行工具

所有三个操作系统都有安装程序。如果您使用的是 macOS High Sierra,则可以使用 Homebrew 和 Cask 通过运行以下命令安装 Google Cloud SDK:

$ brew cask install google-cloud-sdk

Windows 10 专业版用户可以使用 Chocolatey 并运行以下命令:

$ choco install gcloudsdk

最后,Ubuntu 17.04 用户需要运行以下命令:

$ export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)"
$ echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
$ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ sudo apt-get update && sudo apt-get install google-cloud-sdk

安装完成后,您需要通过运行以下命令登录到您的帐户:

$ gcloud init

这将打开您的浏览器,并要求您登录到您的 Google Cloud 帐户。登录后,您将被要求授予 Google Cloud SDK 访问您的帐户的权限。按照屏幕上的提示授予权限,您应该收到一条消息,确认您已经通过 Google Cloud SDK 进行了身份验证。

回到您的终端,现在应该会提示您创建一个项目。出于测试目的,请回答是(y)并输入一个项目名称。这个项目名称必须对您来说是唯一的,所以可能需要尝试几次。如果一开始失败,您可以使用以下命令:

$ gcloud projects create russ-kubernetes-cluster

如您所见,我的项目名为russ-kubernetes-cluster。您应该在命令中引用您自己的项目名称。最后的步骤是将我们的新项目设置为默认项目以及设置区域。我使用了以下命令:

$ gcloud config set project russ-kubernetes-cluster
$ gcloud config set compute/zone us-central1-b

现在我们已经安装了命令行工具,我们可以继续启动我们的集群。

启动 Google 容器集群

您可以使用单个命令启动集群。以下命令将启动一个名为kube-cluster的集群:

$ gcloud container clusters create kube-cluster

当您第一次运行该命令时,可能会遇到一个错误,指出您的项目未启用 Google 容器 API:

您可以通过按照错误中给出的链接并按照屏幕上的说明启用 API 来纠正此错误。如果您的项目没有与之关联的计费,您可能还会遇到错误:

要解决这个问题,请登录到 Google Cloud 网页界面console.cloud.google.com/,并从下拉列表中选择您的项目,该下拉列表位于 Google Cloud Platform 旁边。选择您的项目后,点击左侧菜单中的计费链接,并按照屏幕上的提示将您的项目链接到您的计费账户。

一旦您启用了 API 并将您的项目链接到一个计费账户,您应该能够重新运行以下命令:

$ gcloud container clusters create kube-cluster

这将需要几分钟的时间,但一旦完成,您应该会看到类似以下的内容:

如您所见,kubectl的配置已经自动更新,这意味着我们可以运行以下命令来检查我们是否可以与新的集群通信:

在运行此命令之前,请确保您的本地机器直接连接到互联网,并且您没有通过代理服务器或受到严格防火墙限制的连接,否则您可能无法使用kubectl proxy命令遇到困难。

您还应该能够在 Google Cloud 网络界面的容器引擎部分看到您的集群:

现在我们的集群已经运行起来了,让我们再次启动 Sock Shop 应用程序。

袜子店

与 Azure 一样,这次也不需要提供配置文件,所以我们只需要运行以下命令:

$ kubectl create namespace sock-shop
$ kubectl apply -n sock-shop -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"
$ kubectl -n sock-shop get pods
$ kubectl -n sock-shop expose deployment front-end --type=LoadBalancer --name=front-end-lb
$ kubectl -n sock-shop get services front-end-lb
$ kubectl -n sock-shop describe services front-end-lb

如您从以下截图中所见,IP 和端口给了我一个http://104.155.191.39:8079的 URL:

此外,在 Google Cloud 网络界面中,单击发现与负载平衡还应该显示我们创建的负载均衡器:

单击界面中的链接,或将您的 URL 粘贴到浏览器中,应该会显示您熟悉的商店前台:

运行以下命令应该删除 Sock Shop 应用程序:

$ kubectl delete namespace sock-shop

运行 Kubeless

在删除 Google Cloud 三节点 Kubernetes 集群之前,让我们快速回顾一下 Kubeless。要部署 Kubeless,请运行以下命令:

$ kubectl create ns kubeless
$ kubectl create -f https://github.com/kubeless/kubeless/releases/download/v0.2.3/kubeless-v0.2.3.yaml

部署后,您可以通过运行以下命令来检查状态:

$ kubectl get pods -n kubeless
$ kubectl get deployment -n kubeless
$ kubectl get statefulset -n kubeless

您还可以在 Google Cloud 网络界面的 Google 容器引擎部分检查工作负载和发现与负载平衡。一旦 Kubeless 部署完成,返回到本书附带的存储库中的/Chapter04/hello-world文件夹,并运行以下命令部署测试函数:

**$ kubeless function deploy hello \**
 **--from-file hello.py \**
 **--handler hello.handler \**
 **--runtime python2.7 \**
 **--trigger-http** 

部署后,您可以通过运行以下命令查看该函数:

$ kubectl get functions
$ kubeless function ls

您可以通过运行以下命令调用该函数:

$ kubeless function call hello 

此外,您可以使用以下命令公开该函数:

$ kubectl expose deployment hello --type=LoadBalancer --name=hello-lb

一旦负载均衡器创建完成,您可以运行以下命令确认 IP 地址和端口:

$ kubectl get services hello-lb

一旦您知道 IP 地址和端口,您可以在浏览器中打开该函数,或者使用 curl 或 HTTPie 查看该函数:

现在我们已经使用 Sock Shop 应用程序测试了我们的集群,并部署了一个 Kubeless 函数,我们应该考虑终止我们的集群。

删除集群

要删除集群,只需运行以下命令:

$ gcloud container clusters delete kube-cluster

它会问你是否确定,回答是,一两分钟后,您的集群将被删除。再次,您应该在 Google Cloud 网页界面上仔细检查您的集群是否已被正确删除,以免产生任何意外费用。

摘要

在本章中,我们看了四个云提供商。前两个,DigitalOcean 和 AWS,目前不支持原生的 Kubernetes,因此我们使用 kubeadmkube-aws 来启动和配置我们的集群。对于 Microsoft Azure 和 Google Cloud,我们使用他们的命令行工具来启动他们原生支持的 Kubernetes 服务。我相信您会同意,在撰写本文时,这两项服务比我们看过的前两项要友好得多。

一旦集群运行起来,与 Kubernetes 交互是一个相当一致的体验。当我们发出诸如 kubectl expose 的命令时,我们实际上并不需要为集群运行的位置做出任何让步:Kubernetes 知道它在哪里运行,并使用提供商的原生服务来启动负载均衡器,而无需我们干预任何特殊设置或考虑。

您可能会想知道为什么我们没有在 DigitalOcean 上启动 Sock Shop 应用程序。由于机器的规格相当低,应用程序运行非常缓慢,并且 DigitalOcean 是我们看过的四个提供商中唯一一个不支持 Kubernetes 当前不支持提供商的原生负载均衡服务。我相信这将在未来几个月内得到纠正。

此外,您可能会感到惊讶,AWS 上没有原生的 Kubernetes 经验。在撰写本文时是这种情况;然而,有传言称自从 AWS 加入了云原生基金会后,他们正在努力开发原生的 Kubernetes 服务。

在下一章中,我们将介绍 Apache OpenWhisk,这是最初由 IBM 开发的开源无服务器云平台。

第七章:Apache OpenWhisk 和 Kubernetes

在本章中,我们将看看 Apache OpenWhisk。虽然不严格是一个仅限于 Kubernetes 的项目,比如 Kubeless 和 Fission(这些将在下一章中介绍),但它可以部署并利用 Kubernetes。

我们将看三个主要主题:

  • Apache OpenWhisk 概述

  • 使用 Vagrant 在本地运行 Apache OpenWhisk

  • 在 Kubernetes 上运行 Apache OpenWhisk

让我们首先了解更多关于 OpenWhisk。

Apache OpenWhisk 概述

Apache OpenWhisk 是一个开源的无服务器云计算平台,旨在以与本书其他章节中涵盖的所有工具类似的方式工作。Apache OpenWhisk 最初是 IBM 公共云服务 Bluemix 的 Functions as a Service 部分,现在仍然是。

它在 2016 年 12 月发布了普遍可用版本。随着宣布的新闻稿中有一句来自 Santander 集团平台工程和架构负责人 Luis Enriquez 的引用,他是 IBM Cloud Functions 的一位客户,Luis 说:

“微服务和容器正在改变我们构建应用程序的方式,但由于无服务器,我们可以进一步推动这种转变,OpenWhisk 为我们提供了处理强烈任务和工作负载意外高峰的即时基础设施,并且是我们转向实时和事件驱动架构的关键构建块。”

你可能已经注意到,这听起来很像 AWS 和 Microsoft Azure Functions 的 Lambda——IBM 的服务与竞争对手的区别在于 IBM 已经将 OpenWhisk 提交给了 Apache 孵化器,这是所有外部开发项目成为 Apache 软件基金会努力的一部分的入口。

Apache 软件基金会成立于 1999 年,是一个慈善组织,负责监督和管理超过 350 个开源软件项目的开发和管理,这是为了公共利益。

那么为什么 IBM 要这样做呢?嗯,IBM 不仅是 Apache 软件基金会的金牌赞助商,将其 Functions as a Service 提供开源化对他们来说是有意义的,因为它是唯一一个可以避免供应商锁定的公共云提供商,因为你可以在本地或自己的硬件或虚拟机上运行 Apache OpenWhisk。

这使您可以自由地在任何地方运行和部署 Apache OpenWhisk。但是,如果您想像 Santander 集团一样进行规模化运行,那么您可以选择在 IBM 支持的企业级公共云上运行它。

在本地运行 Apache OpenWhisk

我们首先将研究在本地运行 Apache OpenWhisk。我们将通过使用 VirtualBox 和 Vagrant 来实现这一点。

安装 Vagrant

在启动本地 Apache OpenWhisk 服务器之前,我们需要安装由 HashiCorp 开发的 Vagrant。我能描述 Vagrant 的最好方式是作为一个开源的虚拟机管理器,您可以使用易于遵循的文本配置文件编写机器配置。

安装 Vagrant 非常简单。在 macOS 10.13 High Sierra 上,我们可以使用 Homebrew 和 Cask:

$ brew cask install vagrant

如果您正在运行 Windows 10 专业版,您可以使用 Chocolatey 并运行以下命令:

$ choco install vagrant

最后,如果您正在运行 Ubuntu 17.04,您可以通过运行以下命令直接从 Ubuntu 核心存储库安装 Vagrant:

$ sudo apt-get update
$ sudo apt-get install vagrant 

请注意,Ubuntu 提供的版本可能会比使用 Homebrew 和 Chocolatey 安装的版本稍微滞后;但是对于我们的目的,这不应该造成任何问题。

您可以通过运行以下命令测试 Vagrant 安装:

$ mkdir vagrant-test
$ cd vagrant-test
$ vagrant init ubuntu/xenial64
$ vagrant up

这些命令将在vagrant-test文件夹中创建一个基本的 Vagrantfile,该文件夹使用来自 Vagrant 网站(app.vagrantup.com/ubuntu/boxes/xenial64/)的官方 64 位 Ubuntu 16.04 LTS(Xenial)镜像,下载该镜像,使用 VirtualBox 启动虚拟机,配置网络,并在最终将当前文件夹挂载到虚拟机的/vagrant

所有这些都是使用以下配置定义的:

Vagrant.configure("2") do |config|
 config.vm.box = "ubuntu/xenial64"
end

如果您打开 Vagrantfile,您会注意到有很多配置选项,比如 RAM 和 CPU 分配,网络和脚本,这些脚本在虚拟机成功启动后执行。您可以运行以下命令以 SSH 连接到 Vagrant 虚拟机:

$ vagrant ssh

如果您正在运行 Windows 10 专业版,则需要安装 SSH 客户端。当您执行上述命令时,Vagrant 将为您提供一些选项。

运行以下命令将关闭您的虚拟机并将其删除:

$ vagrant destroy

我还建议通过运行清除您的工作文件夹:

$ cd ../
$ rm -rf vagrant-test

现在我们已经安装了 Vagrant,并且快速查看了如何启动和与虚拟机交互,我们现在可以使用它来启动我们自己的本地安装 Apache OpenWhisk。

下载和配置 Apache OpenWhisk

正如我们已经提到的,Apache OpenWhisk 附带一个 Vagrantfile,其中包含从头开始部署本地 Apache OpenWhisk 安装的所有命令。要下载 Apache OpenWhisk 存储库并部署虚拟机,请运行以下命令:

$ git clone --depth=1 https://github.com/apache/incubator-openwhisk.git openwhisk
$ cd openwhisk/tools/vagrant
$ ./hello

这个过程将花费最多 30 分钟,具体取决于您的互联网连接速度;您可以在以下 URL 找到 Vagrantfile 的副本:github.com/apache/incubator-openwhisk/blob/master/tools/vagrant/Vagrantfile

正如您所看到的,它只有将近 200 行,这与上一节中我们测试 Vagrantfile 的三行有很大不同。Vagrantfile 使用 bash 脚本和 Ansible 的组合来启动、安装和配置我们的 Apache OpenWhisk 虚拟机。

Ansible 是来自 Red Hat 的编排/配置工具。它允许您轻松地用人类可读的代码定义部署,无论是与 API 交互以启动基础设施,还是登录到服务器并执行任务来安装和配置软件堆栈。

在过程结束时,它将执行一个基本的 hello world 检查,如下控制台输出所示:

在我们继续之前,请注意以wsk property set命令开头的输出。我们将需要这个来配置本地客户端,接下来我们将看到如何安装。

安装 Apache OpenWhisk 客户端

每个 Apache OpenWhisk 都有一个用于 macOS、Windows 和 Linux 版本的 Apache OpenWhisk 客户端的下载页面。您可以从以下 URL 访问本地安装:192.168.33.13/cli/go/download/ 或 IBM:openwhisk.ng.bluemix.net/cli/go/download/

由于您的本地安装使用自签名 SSL 证书,当在浏览器中打开时,您可能会收到警告。您需要接受这些警告才能继续访问该网站。此过程因浏览器而异,因此您需要按照屏幕上的提示进行操作。

要在 macOS 10.13 High Sierra 上安装客户端,我们只需要运行以下命令:

$ curl -L --insecure https://192.168.33.13/cli/go/download/mac/amd64/wsk > /usr/local/bin/wsk
$ chmod +x /usr/local/bin/wsk
$ wsk help

这将使用curl下载二进制文件并忽略自签名证书。

要在 Windows 10 专业版上下载,请运行以下命令。我建议从 IBM 下载,以避免自签名 SSL 证书和 PowerShell 的问题。为此,首先以管理员用户身份打开 PowerShell 窗口。您可以通过从任务栏中的 PowerShell 菜单中选择以管理员身份运行来执行此操作。打开后,您应该看到您在C:\WINDOWS\system32文件夹中;如果不是,则运行以下命令:

$ cd C:\WINDOWS\system32
$ Invoke-WebRequest -Uri https://openwhisk.ng.bluemix.net/cli/go/download/windows/amd64/wsk.exe -UseBasicParsing -OutFile wsk.exe

与 macOS 版本一样,您可以通过运行以下命令来检查客户端是否已安装:

$ wsk help

最后,在 Ubuntu 17.04 上,您需要运行以下命令:

$ sudo sh -c "curl -L --insecure https://192.168.33.13/cli/go/download/linux/amd64/wsk > /usr/local/bin/wsk"
$ sudo chmod +x /usr/local/bin/wsk

一旦下载并设置为可执行,您应该能够运行:

$ wsk help

现在我们已经安装了客户端,我们需要对我们的安装进行身份验证。为此,请运行您在上一节末尾做的笔记中的命令,减去--namespace guest部分。对我来说,这个命令是这样的:

$ wsk property set --apihost 192.168.33.13 --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP

如果您没有做笔记,那么您可以通过从启动 Vagrant 虚拟机的文件夹运行以下命令来动态传递授权令牌,如下所示:

$ wsk property set --apihost 192.168.33.13 --auth `vagrant ssh -- cat openwhisk/ansible/files/auth.guest`

如果您不是从启动机器的文件夹运行vagrant ssh命令,该命令将失败,因为它将无法找到您的机器配置。现在,您的本地客户端已对本地安装的 Apache OpenWhisk 进行了身份验证,我们可以通过运行以下命令执行与自动安装相同的 hello world 命令:

$ wsk -i action invoke /whisk.system/utils/echo -p message hello --result

这应该返回以下终端输出的消息hello

现在我们有了本地客户端,我们可以尝试下载和执行另一个示例。

你好世界

现在,我们可以部署一个更复杂的解决方案,而不仅仅是使用内置的echo实用程序返回消息。与我们之前使用的 hello world 脚本类似,我们将部署一个使用 Node.js 编写的函数,该函数接受输入并将其显示回给我们。

首先,让我们创建一个工作目录:

$ mkdir openwhisk-http
$ cd openwhisk-http

现在我们有了一个工作目录,创建一个包含以下代码的文件,并将其命名为hello.js

function main(args) {
    var msg = "you didn't tell me who you are."
    if (args.name) {
        msg = `hello ${args.name}!`
    }
    return {body:
       `<html><body><h3><center>${msg}</center></h3></body></html>`}
}

现在我们有了要部署的函数,首先我们需要创建一个包,然后创建一个暴露给 Web 的操作:

$ wsk -i package create /guest/demo
$ wsk -i action create /guest/demo/hello hello.js --web true

现在我们已经创建了包和操作,您的终端应该看起来像以下内容:

这意味着您可以使用浏览器在以下 URL 调用您的函数:

192.168.33.13/api/v1/web/guest/demo/hello.http?name=Kubernetes%20for%20Serverless%20Applications

您应该会看到以下页面:

您可以通过在 macOS 或 Ubuntu 上使用 HTTPie 来查看更多信息,方法是运行以下命令:

$ http --verify=no https://192.168.33.13/api/v1/web/guest/demo/hello.http?name=Kubernetes%20for%20Serverless%20Applications

这将返回标头和输出:

您可以通过运行以下命令列出软件包和操作,并删除它们:

$ wsk -i list
$ wsk -i action delete /guest/demo/hello
$ wsk -i package delete /guest/demo

随意在本地安装 Apache OpenWhisk 上玩耍;您可以在 Awesome OpenWhisk 页面找到更多示例,网址为:github.com/apache/incubator-openwhisk-external-resources/

完成本地安装后,您可以运行以下命令来停止和销毁虚拟机:

$ vagrant destroy

请记住,您必须在openwhisk/tools/vagrant/文件夹中运行此命令,否则 Vagrant 将无法找到您的虚拟机配置。

现在我们已经在本地安装并与 Apache OpenWhisk 进行了交互,让我们看看如何在公共云中的 Kubernetes 上部署它。

在 Kubernetes 上运行 Apache OpenWhisk

现在我们知道如何与 Apache OpenWhisk 进行交互以及其基本概念,我们可以考虑在 Kubernetes 集群之上部署一个副本。为此,我将通过运行以下命令在 Google Cloud 中启动一个三节点集群:

$ gcloud container clusters create kube-cluster

一旦集群运行起来,您可以通过运行以下命令来检查是否可以看到三个节点:

$ kubectl get nodes

现在我们有了我们的 Kubernetes,我们可以继续进行 Apache OpenWhisk 的部署。

部署 OpenWhisk

在开始部署之前,所有在 Kubernetes 上部署 Apache OpenWhisk 所需的配置都可以在 GitHub 上找到,因此我们应该通过运行以下命令克隆存储库。

$ git clone --depth=1 https://github.com/apache/incubator-openwhisk-deploy-kube.git openwhisk-kube
$ cd openwhisk-kube

现在我们有了存储库的副本,我们可以开始部署运行 Apache OpenWhisk 所需的各个组件。首先,我们需要创建一个名为openwhisk的命名空间。要做到这一点,请运行以下命令:

$ kubectl create namespace openwhisk

现在我们可以通过启动 CouchDB 来开始我们的部署。

CouchDB

要部署 CouchDB,请从openwhisk-kube文件夹内运行以下命令:

$ kubectl apply -f kubernetes/couchdb/couchdb.yml

这将启动一个使用couchdb.yml文件中定义的参数运行 CouchDB 的 pod。您可以通过获取 pod 的名称来检查部署是否正常。您可以通过运行以下命令来执行此操作:

$ kubectl -n openwhisk get pods

一旦您获得了名称,对我来说是couchdb-1146267775-v0sdm,然后您可以运行以下命令,确保更新 pod 的名称为您自己的:

$ kubectl -n openwhisk logs couchdb-1146267775-v0sdm

在日志输出的最后,您应该看到以下消息:

现在我们的 CouchDB pod 正在运行,我们可以继续下一个,即 Redis。

Redis

要启动 Redis pod,我们只需要运行以下命令:

$ kubectl apply -f kubernetes/redis/redis.yml

API 网关

接下来我们有 API 网关;通过运行以下命令来启动它:

$ kubectl apply -f kubernetes/apigateway/apigateway.yml

ZooKeeper

现在我们可以使用以下命令启动 Apache ZooKeeper:

$ kubectl apply -f kubernetes/zookeeper/zookeeper.yml

卡夫卡

现在是时候启动另一个 Apache 项目,Kafka 了:

$ kubectl apply -f kubernetes/kafka/kafka.yml

此时,我们应该仔细检查我们启动的所有 pod 是否正在运行。要做到这一点,请运行以下命令:

$ kubectl -n openwhisk get pods

您应该看到couchdbredisapigatewayzookeeperkafka的 pod,所有这些 pod 都在没有记录重启并且READY列中为1/1运行:

控制器

接下来是控制器。这与我们部署的其他 pod 略有不同,因为它是以有状态的方式部署的:

$ kubectl apply -f kubernetes/controller/controller.yml

您应该看到已创建了一个 StatefulSet 而不是一个部署。

调用者

再次部署的下一个 pod 将是一个 StatefulSet 而不是一个部署。在部署 pod 之前,我们需要对kubernetes/invoker/invoker.yml文件进行轻微更改。这是因为,默认情况下,OpenWhisk 假定您正在运行 Ubuntu 作为基本操作系统,而 Google Cloud 不是。

要做到这一点,请在您选择的文本编辑器中打开kubernetes/invoker/invoker.yml并删除以下代码块:

      - name: apparmor
        hostPath:
          path: "/usr/lib/x86_64-linux-gnu/libapparmor.so.1"

还有另一个关于apparmor的参考资料需要删除。这次是在文件底部:

        - name: apparmor
          mountPath: "/usr/lib/x86_64-linux-gnu/libapparmor.so.1"

一旦删除了引用apparmor的两个代码块,您可以通过运行以下命令部署invoker

$ kubectl apply -f kubernetes/invoker/invoker.yml

部署可能需要几分钟时间。

NGINX

部署的最后一部分是 NGINX 容器。对于这个容器,我们需要做更多的工作,因为我们需要为我们的集群生成证书。为了生成证书,我们需要使用 OpenSSL。这在 Windows 机器上默认情况下不安装,因此您可以使用以下命令使用 Chocolatey 安装 OpenSSL:

$ choco install openssl.light

一旦安装了 OpenSSL,您可以通过运行以下命令生成证书:

$ mkdir -p certs
$ openssl req -x509 -newkey rsa:2048 -keyout certs/key.pem -out certs/cert.pem -nodes -subj "/CN=localhost" -days 365

一旦我们有了证书,我们需要使用kubernetes/nginx中的nginx.conf文件创建一个configmap。为此,请运行以下命令:

$ kubectl -n openwhisk create configmap nginx --from-file=kubernetes/nginx/nginx.conf

现在我们需要上传生成的证书和密钥作为secret

$ kubectl -n openwhisk create secret tls nginx --cert=certs/cert.pem --key=certs/key.pem

一旦它们被上传,我们可以通过运行以下命令启动 NGINX pod:

$ kubectl apply -f kubernetes/nginx/nginx.yml

现在我们已经部署了所有的 pod,您应该使用以下命令再次检查它们是否都在运行:

$ kubectl -n openwhisk get pods

正如您所看到的,一切都在运行。只要数量不增加,您可以忽略任何重启。

配置 OpenWhisk

现在我们已经部署了所有的 pod,我们可以开始与我们的部署进行交互。首先,我们需要找出 NGINX pod 的外部 IP 地址。您可以通过运行以下命令找到有关 pod 的信息:

$ kubectl -n openwhisk describe service nginx

这是输出:

正如您所看到的,虽然端口是暴露的,但它们只在节点本身上暴露。由于节点位于私有地址上,我们将无法从本地客户端访问它们。要在外部暴露端口,我们需要创建一个负载均衡服务,运行以下命令来执行此操作:

$ kubectl -n openwhisk expose service nginx --type=LoadBalancer --name=front-end

这将启动一个负载均衡器并暴露三个端口:804438443。您可以通过运行以下命令找到外部 IP 地址的详细信息:

$ kubectl -n openwhisk describe service front-end

在输出中,您会找到一行,上面写着 Load Balancer Ingress,后面跟着一个 IP 地址:

正如您从先前显示的示例输出中看到的,我有一个 IP 地址35.188.204.73。这将被用作我与之交互的 API 端点。

现在我们已经获得了安装的 IP 地址,我们可以继续通过运行以下命令来配置认证令牌,确保您使用自己安装的 IP 地址进行更新:

$ wsk -i property set --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP --apihost https://35.188.204.73:443

配置完成后,我们可以运行我们的 hello-world 测试。

你好,世界

这与前一节中的 hello world 完全相同,所以我不会详细介绍。只需切换到您拥有hello.js文件的文件夹,并运行以下命令:

$ wsk -i package create /guest/demo
$ wsk -i action create /guest/demo/hello hello.js --web true

一旦您运行了创建包和操作的命令,您将能够访问 URL。对我来说,它是以下内容:

https://35.188.204.73/api/v1/web/guest/demo/hello.http?name=Kubernetes%20for%20Serverless%20Applications

这显示了我们期望看到的页面:

再次,我们可以通过运行 HTTPie 来看到更多:

$ http --verify=no https://35.188.204.73/api/v1/web/guest/demo/hello.http?name=Kubernetes%20for%20Serverless%20Applications

这显示了以下信息:

正如您所看到的,一旦您使用提供的文件部署了 Apache OpenWhisk,使用它是一个非常一致的体验。

在完成本章之前,我们应该删除我们的 Kubernetes 集群。要做到这一点,请运行以下命令:

$ gcloud container clusters delete kube-cluster

删除后,请务必检查您的 Google Cloud 控制面板console.cloud.google.com/,以确保没有剩余的资源,这可能会产生意外的成本。

摘要

在本章中,我们稍微偏离了目标,看了一下 Apache OpenWhisk。我们使用标准虚拟机部署了一个本地副本,然后我们转向部署到在 Google Cloud 上运行的 Kubernetes 集群。

正如您所看到的,一旦部署完成,与 Apache OpenWhisk 的交互是一致的体验,我们能够在两个安装中部署我们简单的 hello-world 应用程序,而无需进行任何修改。

虽然 Kubernetes 对 Apache OpenWhisk 的支持仍处于起步阶段,但我们的偏离表明,不仅是为 Kubernetes 设计的框架,就像我们在前几章中看到的工具一样,它们将在 Kubernetes 之上运行,并提供一致的体验,而无需将您锁定在单一供应商或技术中。

在下一章中,我们将看到可能是最成熟的 Kubernetes 函数作为服务提供:Fission。

第八章:使用 Fission 启动应用程序

接下来我们将看一下 Fission。Fission 是一个快速增长的,基于 Kubernetes 的无服务器框架,而且在我们之前章节中看到的技术中,可能是最多才多艺的。在本章中,我们将涵盖:

  • 谁构建了 Fission?

  • 安装先决条件

  • 在本地安装、配置和运行 Fission

  • 命令概述

  • 在云中安装、配置和运行 Fission

  • 部署一些示例 Fission 应用程序

到本章结束时,我们将在两个不同的目标环境中安装 Fission,并且还将启动多个应用程序。

Fission 概述

Fission 是由 Platform9 开发的开源无服务器应用程序。它旨在在 Kubernetes 之上运行,并利用一些核心的 Kubernetes 功能。Platform9 是一家托管服务提供商,其核心业务是部署、管理和支持专门从事 OpenStack 和 Kubernetes 的开源云。

OpenStack 是一组开源组件,构成了一个完全功能的基础设施即服务产品。它提供计算、网络、块存储、对象存储、编排,甚至容器服务等功能。

该项目的目标是为多个不同的硬件供应商提供支持,从普通的 x86 硬件到专门的存储解决方案,使最终用户能够构建自己的 AWS 和 Microsoft Azure 风格的产品。

随着 AWS Lambda 和 Azure Functions 等服务成熟到现在几乎在大多数企业中都很普遍,Platform9 看到了提供自己的函数即服务的机会。

作为一家专门从事复杂开源解决方案的公司,他们为他们向社区贡献自己的工作是有意义的,因此他们以 Apache 许可证发布了 Fission。

Apache 软件基金会的 Apache 2.0 许可证允许开发人员免费发布他们的软件,允许最终用户以任何目的使用该软件,并在不必担心版税的情况下修改/重新分发它。为了确保许可证不被违反,最终用户必须保留原始的版权声明和免责声明。

这可能看起来像一个奇怪的决定。然而,就像我们在上一章中介绍的 OpenWhisk 一样,Platform9 为他们的客户以及任何想要开始部署函数即服务FaaS)的人提供了一个坚实的基础来构建他们的应用程序。他们不仅给了人们在任何地方部署他们的工作负载的自由,还能够为安装和 Fission 平台提供支持服务。

安装先决条件

在我们本地或公共云中安装 Fission 之前,我们需要一些支持工具。第一个工具我们已经安装了,那就是 Kubernetes 命令行接口kubectl。我们还没有安装运行 Fission 所需的第二个工具:Helm (helm.sh/)。

安装 Helm

Helm 是 Kubernetes 的一个包管理器,是 Cloud Native Computing Foundation 的一部分,Bitnami、Google、Microsoft 和 Helm 社区都为其开发做出了贡献。

要在 macOS High Sierra 上安装 Helm,我们可以使用 Homebrew;只需运行:

$ brew install kubernetes-helm

如果您正在运行 Ubuntu Linux,则可以使用安装脚本下载并安装 Helm:

$ curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash

最后,Windows 10 专业版用户可以从 canary 存储库下载 Helm 的实验版本。该版本的直接下载链接为kubernetes-helm.storage.googleapis.com/helm-canary-windows-amd64.zip。由于这是一个实验版本,我建议直接运行它,而不要将其放在系统文件夹中。

安装 Helm 的下一步需要您拥有一个运行中的 Kubernetes 集群,因为这是它的启动位置。我将在本章后面包括安装 Helm 的服务器组件 Tiller 的说明。

安装 Fission CLI

我们需要安装的最后一个命令行工具是 Fission 本身的工具。您可以通过在 macOS High Sierra 上运行以下命令来安装它:

$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-osx && chmod +x fission && sudo mv fission /usr/local/bin/

对于 Ubuntu 17.04,您可以运行:

$ curl -Lo fission https://github.com/fission/fission/releases/download/0.3.0/fission-cli-linux && chmod +x fission && sudo mv fission /usr/local/bin/

最后,Windows 可执行文件可以从github.com/fission/fission/releases/download/0.3.0/fission-cli-windows.exe下载。我建议与 Helm 的可执行文件一起使用,而不是将其安装在System32文件夹中。

运行以下命令应该显示当前安装的版本:

$ helm version
$ fission --version

如前所述,我们还没有安装 Tiller,因此我们可以安全地忽略关于无法连接到它的错误。

在本地运行 Fission

现在我们已经安装了先决条件,我们可以开始创建我们的第一个函数。为此,我们将使用 Minikube。要启动单节点集群,我们只需要运行以下命令:

$ minikube start
$ kubectl get nodes

这应该启动您的 Minikube 集群,并确认您的本地版本已重新配置以与其通信:

一旦我们的集群运行并且可访问,我们需要通过安装 Tiller 来完成 Helm 安装。要做到这一点,我们需要运行以下命令:

$ helm init

您应该会看到类似以下消息:

使用 Helm 启动 Fission

Helm 现在已配置好,我们可以使用它来部署 Fission 的远程组件。可以通过运行以下命令来完成:

$ helm install --namespace fission --set serviceType=NodePort https://github.com/fission/fission/releases/download/0.4.0/fission-all-0.4.0.tgz

一两分钟后,您应该会收到 Fission 已启动的确认信息。

通过输出进行工作

Helm 的输出非常详细。它将为您提供它创建的所有内容的概述,以及开发人员包含的任何附加说明。

输出的这部分包含了部署的基本细节:

NAME: lopsided-fox
LAST DEPLOYED: Sat Dec 9 10:52:19 2017
NAMESPACE: fission
STATUS: DEPLOYED

接下来,我们会得到有关在 Kubernetes 中部署了什么的信息,从服务账户开始。这些提供运行 pod 的身份服务。这些允许 Fission 的各个组件与 Kubernetes 进行接口交互:

==> v1/ServiceAccount
NAME            SECRETS AGE
fission-builder 1       1m
fission-fetcher 1       1m
fission-svc     1       1m

然后是绑定。这些为集群提供基于角色的身份验证(RBAC):

==> v1beta1/ClusterRoleBinding
NAME                 AGE
fission-builder-crd  1m
fission-crd          1m
fission-fetcher-crd  1m

接下来是服务本身:

==> v1/Service
NAME           TYPE        CLUSTER-IP  EXTERNAL-IP PORT(S)        AGE
poolmgr        ClusterIP   10.0.0.134  <none>      80/TCP         1m
buildermgr     ClusterIP   10.0.0.212  <none>      80/TCP         1m
influxdb       ClusterIP   10.0.0.24   <none>      8086/TCP       1m
nats-streaming NodePort    10.0.0.161  <none>      4222:31316/TCP 1m
storagesvc     ClusterIP   10.0.0.157  <none>      80/TCP         1m
controller     NodePort    10.0.0.55   <none>      80:31313/TCP   1m
router         NodePort    10.0.0.106  <none>      80:31314/TCP   1m

现在我们有了部署详情。您可能会注意到,如下所示,一些 pod 仍在启动,这就是为什么它们显示为零可用的原因:

==> v1beta1/Deployment
NAME.           DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
timer           1       1       1          1         1m
poolmgr         1       1       1          1         1m
influxdb        1       1       1          1         1m
nats-streaming  1       1       1          1         1m
controller      1       1       1          1         1m
mqtrigger       1       1       1          1         1m
router          1       1       1          0         1m
storagesvc      1       1       1          0         1m
kubewatcher     1       1       1          1         1m
buildermgr      1       1       1          0         1m

接下来,我们有了部署和服务的 pod:

==> v1/Pod(related)
NAME                            READY STATUS            RESTARTS AGE
logger-zp65r                    1/1   Running           0        1m
timer-57f75c486f-9ktbk          1/1   Running           2        1m
poolmgr-69fcff7d7-hbq46         1/1   Running           1        1m
influxdb-c5c6cfd86-wkwrs        1/1   Running           0        1m
nats-streaming-85b9898784-h6j2v 1/1   Running           0        1m
controller-5f964bc987-mmfrx     1/1   Running           0        1m
mqtrigger-c85dd79f7-vj5p7       1/1   Running           0        1m
router-7cfff6794b-gn5pw         0/1   ContainerCreating 0        1m
storagesvc-58d5c8f6-bnqc7       0/1   ContainerCreating 0        1m
kubewatcher-6d784b9987-5wwhv    1/1   Running           0        1m
buildermgr-7ff69c8bb-pvtbx      0/1   ContainerCreating 0        1m

然后我们有了命名空间:

==> v1/Namespace
NAME.            STATUS AGE 
fission-builder  Active 1m
fission-function Active 1m

现在我们有了秘密。这些只是用于正在使用的数据库:

==> v1/Secret
NAME     TYPE   DATA AGE 
influxdb Opaque 2    1m

我们接近尾声了:持久存储索赔。您可以看到,由于我们在本地启动,它只是使用 VM 上的一个文件夹,而不是创建外部存储:

==> v1/PersistentVolumeClaim
NAME.               STATUS VOLUME                                   CAPACITY ACCESS MODES STORAGECLASS AGE
fission-storage-pvc Bound  pvc-082cf8d5-dccf-11e7-bfe6-080027e101f5 8Gi      RWO            standard     1m

现在我们有了角色绑定:

==> v1beta1/RoleBinding
NAME                   AGE
fission-function-admin 1m
fission-admin          1m

最后,我们有了守护进程集:

==> v1beta1/DaemonSet
NAME   DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
logger 1       1       1     1          1         <none>        1m

现在我们已经看到了我们的 Fission 安装的所有 Kubernetes 元素的概述,我们得到了如何与安装进行交互的说明。

启动我们的第一个函数

笔记分为三个部分;第一部分提供了如何安装 Fission 命令行客户端的说明。由于我们已经在本章的前一部分中涵盖了这一点,我们可以忽略这一步。

接下来,在第二部分中,我们得到了关于需要设置的环境变量的说明,以便我们的本地 Fission 客户端可以与我们的 Fission 安装进行交互。要设置这些变量,请运行以下命令:

$ export FISSION_URL=http://$(minikube ip):31313
$ export FISSION_ROUTER=$(minikube ip):31314 

export命令仅适用于 macOS High Sierra 和 Ubuntu 17.04。Windows 10 专业版用户将不得不运行以下命令:

$ for /f "delims=" %%a in ('minikube ip') do @set minikube_ip=%%a
$ set FISSION_URL=http://%minikube_ip%:31313
$ set FISSION_ROUTER=%minikube_ip%:31314

从这些命令中可以看出,我们的 Fission 安装知道它正在运行在 Minikube 安装上,并为我们提供了动态生成 Minikube 安装的 IP 地址的命令。

第三部分包含了一步一步的说明,说明如何运行一个 hello world 函数;让我们现在来运行这些步骤。

首先,我们需要创建一个环境。为此,我们使用以下命令:

$ fission env create --name nodejs --image fission/node-env

这个命令创建了一个名为nodejs的环境,然后指示 Fission 使用来自 Docker Hub 的 Docker 镜像fission/node-env——您可以在hub.docker.com/r/fission/node-env/找到这个镜像。

现在我们已经创建了环境,我们需要一个要部署的函数。运行以下命令(仅适用于 macOS 和 Linux)来下载 hello world 示例:

$ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > /tmp/hello.js

这将下载以下代码:

module.exports = async function(context) {
    return {
        status: 200,
        body: "Hello, world!\n"
    };
}

如您所见,这与我们在早期章节中运行的示例并没有太大不同。现在我们已经下载了一个函数,我们可以使用以下命令部署它:

$ fission function create --name hello --env nodejs --code /tmp/hello.js

我们快要完成了;最后一步是创建一个到我们函数的路由。要做到这一点,使用以下命令:

$ fission route create --method GET --url /hello --function hello

现在我们应该能够通过发出 HTTP 请求来调用我们的函数。您可以使用以下命令中的任一个来触发我们的函数:

$ curl http://$FISSION_ROUTER/hello
$ http http://$FISSION_ROUTER/hello 

对于 Windows 10 专业版,请使用以下命令在 IE 中打开示例:

$ explorer http://%FISSION_ROUTER%/hello 

HTTPie 将为您提供标头,以及以下输出:

一个留言板

现在我们已经有了一个基本的应用程序在运行,让我们来创建一些更复杂的东西。Fission 附带了一个演示应用程序,充当留言板。您可以在伴随本书的 GitHub 存储库中的/Chapter08/guestbook/文件夹中找到我们将要部署的文件。

启动应用程序的第一步是启动 Redis 部署;这将用于存储写入留言板的评论。要创建部署,请在/Chapter08/guestbook/文件夹中运行以下命令:

$ kubectl create -f redis.yaml

您可以从以下截图中看到,这创建了一个namespacedeploymentservice

现在我们需要创建一个环境来启动我们的函数。由于应用程序是用 Python 编写的,让我们运行以下命令:

$ fission env create --name python --image fission/python-env

前面命令的输出显示在以下截图中:

现在我们已经创建了两个函数,一个用于显示评论,一个用于写评论。要添加这些,请运行以下命令:

$ fission function create --name guestbook-get --env python --code get.py --url /guestbook --method GET
$ fission function create --name guestbook-add --env python --code add.py --url /guestbook --method POST

前面命令的输出可以在以下截图中看到:

您会注意到,用于添加函数的命令与我们在上一节中用于启动 hello world 示例的命令有些不同。在之前的示例中,我们既添加了函数,又创建了路由。您可能还注意到,虽然我们创建了两个函数,但它们都绑定到了相同的路由/guestbook。现在不讨论这个问题,让我们启动应用程序并与之交互。

要打开留言板,请运行以下命令:

$ open http://$FISSION_ROUTER/guestbook 

对于 Windows 10 专业版,请使用:

$ explorer http://%FISSION_ROUTER%/guestbook

这将在浏览器中打开一个空的留言板页面,如下截图所示:

现在让我们通过输入一些文本(比如Testing Fission)来添加评论,然后单击添加。刷新后,您应该看到您的评论已添加:

如果收到内部服务器错误,请不要担心,只需刷新页面并重新提交。查看页面的 HTML 源代码,您可能会注意到表单操作配置为将POST提交到/guestbook

<form action="/guestbook" method="POST">
  <input type="text" name="text">
  <button type="submit">Add</button>
</form>

如果您查看我们用于创建两个函数的命令,您会注意到两者都附有一个方法。guestbook-add,运行add.py,使用了POST方法,如下面的代码所示:

#
# Handles POST /guestbook -- adds item to guestbook 
#

from flask import request, redirect
import redis

# Connect to redis.
redisConnection = redis.StrictRedis(host='redis.guestbook', port=6379, db=0)

def main():
    # Read the item from POST params, add it to redis, and redirect
    # back to the list
    item = request.form['text']
    redisConnection.rpush('guestbook', item)
    return redirect('/guestbook', code=303)

该函数读取表单提交的数据,将评论推送到 Redis 数据库,然后将我们带回/guestbook303代码是在POST后重定向使用的状态代码。

每当您的浏览器请求页面时,它都会发送一个GET请求。在我们的情况下,所有对/guestbookGET请求都被路由到guestbook-get函数,这是get.py代码:

#
# Handles GET /guestbook -- returns a list of items in the guestbook
# with a form to add more.
#

from flask import current_app, escape
import redis

# Connect to redis. This is run only when this file is loaded; as
# long as the pod is alive, the connection is reused.
redisConnection = redis.StrictRedis(host='redis.guestbook', port=6379, db=0)

def main():
    messages = redisConnection.lrange('guestbook', 0, -1)

    items = [("<li>%s</li>" % escape(m.decode('utf-8'))) for m in messages]
    ul = "<ul>%s</ul>" % "\n".join(items)
    return """
      <html><body style="font-family:sans-serif;font-size:2rem;padding:40px">
          <h1>Guestbook</h1> 
          <form action="/guestbook" method="POST">
            <input type="text" name="text">
            <button type="submit">Add</button>
          </form>
          <hr/>
          %s
      </body></html>
      """ % ul

从上面的代码中可以看出,这会连接到 Redis 数据库,读取每个条目,将结果格式化为无序的 HTML 列表,然后将列表插入到水平线下方(<hr/>)。

Fission 命令

在我们将 Fission 安装移到公共云之前,我们应该更多地了解一下命令客户端。有几个顶级命令可用于管理我们的函数和路由。

fission function 命令

这基本上是您在使用 Fission 时将花费大部分时间的地方。函数命令是您创建、管理和删除函数的方式。您可以使用fission function <command>fission fn <command>

create 命令

我们已经使用过这个命令,所以不需要详细介绍。fission function create 命令有几个选项;最常见的是:

  • --name:这表示我们想要给我们的函数取什么名字。

  • --env:这表示我们想要在哪个环境中部署我们的函数。更多关于环境的内容请参见下一节。

  • --code:我们希望部署的代码的路径或 URL。

  • --url:我们希望我们的函数在哪个 URL 上可用。

  • --method:我们在前面 URL 上访问我们的函数的方式;这里的选项有GETPOSTPUTDELETEHEAD—如果您不使用--method但使用--url,它将始终默认为GET

正如我们在留言板示例中已经看到的,fission function create 命令看起来会像下面这样:

$ fission function create \
 --name guestbook-get \
 --env python \
 --code get.py \
 --url /guestbook \
 --method GET

获取选项

这个选项相当简单;运行fission function get将显示您选择的函数的源代码。它接受一个输入:--name。这是您希望显示源代码的函数的名称。

运行以下命令将显示 hello world 函数的源代码:

$ fission function get --name hello 

列出和获取元数据命令

以下两个命令有点类似:

$ fission function list

这个命令将列出当前安装的函数。列表中包括函数的名称、唯一 ID 以及函数部署的环境:

如果我们已经知道函数的名称,并且想要提醒自己它正在运行的环境,或者需要它的 UID,那么我们可以使用 fission function getmeta 命令,并传递函数的名称:

$ fission function getmeta --name hello

日志命令

虽然目前没有任何视图,但您可以使用 fission function logs 命令查看函数的日志。您可以传递一些不同的选项:

  • --name:这是您希望查看日志的函数的名称,这总是必需的

  • --follow:保持流打开,日志实时显示

  • --detail:添加更多详细输出

使用上述选项,命令将看起来像下面这样:

$ fission function logs --detail --follow --name hello

然而,正如前面提到的,目前没有太多可看的。

更新命令

fission function update 命令部署函数的更新版本。它使用与 fission function create 命令相同的选项。例如,如果我们想要更新我们的 hello world 函数以使用不同的源,我们将运行以下命令:

$ fission function update \
 --name hello \
 --env nodejs \
 --code hello-update.js \

删除命令

我们要看的最后一个命令是 fission function delete。这个命令相当不言自明。它删除函数,只接受一个参数,那就是 --name

在使用 fission function delete 时请小心;它不会以任何方式提示您,当您按下 Enter 时,您的函数将被删除。

要删除 hello world 函数,例如,我们只需运行以下命令:

$ fission function delete --name hello

如您所见,并且正如前面提到的,没有 您确定吗? 的提示,因此在使用命令时请小心。

fission environment 命令

下一个顶级命令是 environment。正如我们已经看到的,环境是我们的函数运行的地方,它们还定义了我们的函数在哪种语言中执行。在撰写本文时,Fission 支持 Node.js、Go、Python、PHP、Ruby、Perl 和 .NET C#。

创建命令

fission environment create 命令是我们已经使用过的一个命令。例如,当我们创建 guestbook 应用程序时,我们需要一个 Python 环境来运行我们的应用程序,所以我们运行了以下命令:

$ fission environment create \
 --name python \
 --image fission/python-env

图像的完整列表、要使用的 URL 和用于创建图像的 Dockerfile 如下:

语言 图像名称 源 URL
Python 2.7 fission/python-env github.com/fission/fission/tree/master/environments/python
Python 3.5 fission/python3-env github.com/fission/fission/tree/master/environments/python
Node.js fission/nodejs-env github.com/fission/fission/tree/master/environments/nodejs
.NET C# fission/dotnet-env github.com/fission/fission/tree/master/environments/dotnet
.NET 2.0 C# fission/dotnet20-env github.com/fission/fission/tree/master/environments/dotnet20
Go fission/go-runtime github.com/fission/fission/tree/master/environments/go
PHP fission/php7-env github.com/fission/fission/tree/master/environments/php7
Ruby fission/ruby-env github.com/fission/fission/tree/master/environments/ruby
Perl fission/perl-env github.com/fission/fission/tree/master/environments/perl

列出和获取命令

与函数命令一样,环境也有listget命令,它们的工作方式也相同。

$ fission environment list

运行上一个命令将列出所有配置的环境。

$ fission environment get --name nodejs

运行上一个命令将获取命名环境的详细信息。

删除命令

delete命令再次按预期工作(请记住它会在没有警告的情况下删除):

$ fission environment delete --name nodejs

此外,如果您的环境中有函数,它也将在没有警告的情况下被删除。但是,您的函数将保留,直到您手动删除它们。任何尝试调用没有环境的函数都将导致内部服务器错误。

在云中运行 Fission

现在我们知道了在本地运行 Fission 时启动和交互所涉及的内容,让我们看看在云中启动 Kubernetes,然后配置 Fission 在那里运行。

在本节的其余部分,我将仅提供 macOS High Sierra 和 Ubuntu 17.04 主机的说明,因为它们与我们将要运行的命令具有更高的兼容性。

启动 Kubernetes 集群

我将使用以下命令在 Google Cloud 中启动我的 Kubernetes:

$ gcloud container clusters create kube-cluster

前述命令的输出可以在以下截图中看到:

一旦启动,最多需要大约 5 分钟,您可以使用以下方法检查您的集群是否按预期运行:

$ kubectl get nodes

前述命令的输出可以在以下截图中看到:

现在我们的三节点集群已经运行起来了,并且我们的本地 Kubernetes 客户端正在与之交互,我们可以再次运行以下命令来部署 Helm 的 Kubernetes 端:

$ helm init

这将返回以下消息:

现在我们已经准备好了 Helm,我们可以继续启动 Fission。

安装 Fission

与之前一样,我们将使用 Helm 来安装 Fission。在本地安装 Fission 和在 Google Cloud、Microsoft Azure 或 AWS 等公共云上安装 Fission 之间唯一的区别是,我们不会使用--set serviceType=NodePort选项,而是直接运行以下命令:

$ helm install --namespace fission https://github.com/fission/fission/releases/download/0.4.0/fission-all-0.4.0.tgz

您可能会注意到这次运行速度要快得多,并且返回的信息与我们在本地单节点集群上启动 Fission 时非常相似。

您可能会注意到,这次您的安装有一个不同的名称:

NAME: orange-shark
LAST DEPLOYED: Sun Dec 10 13:46:02 2017
NAMESPACE: fission
STATUS: DEPLOYED

此名称用于在整个过程中引用安装,如您从 Google Cloud Web 控制台的工作负载页面中看到的屏幕截图所示:

在控制台中,点击“发现和负载均衡”将显示分配给您的安装的所有外部 IP 地址。由于我们传递了NodePort选项,因此已创建了外部负载均衡器:

在控制台中查看的最后一件事是存储页面。如您所见,外部块存储已创建并附加到您的安装中。这与我们在本地启动时不同,因为存储实际上是我们单台机器的存储:

回到命令行,你会注意到,Helm 再次给了我们关于如何完成本地 Fission 客户端配置的指令。然而,由于我们没有使用 Minikube,这次的指令略有不同。

这次设置FISSION_URLFISSION_ROUTER变量的命令使用kubectl来查询我们的安装,以找出负载均衡器的外部 IP 地址:

 $ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}')
 $ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}')

你可以通过运行以下命令来检查 URL:

$ echo $FISSION_URL
$ echo $FISSION_ROUTER

这应该会给你类似以下的输出:

现在我们已经安装了 Fission,并且我们的本地命令行客户端已配置为与我们的基于云的安装进行交互,我们可以通过运行以下命令快速重新运行 hello world 示例:

$ fission env create --name nodejs --image fission/node-env
$ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > /tmp/hello.js
$ fission function create --name hello --env nodejs --code /tmp/hello.js --url /hello --method GET

这应该会给你类似以下的输出:

一旦启动,你可以使用以下命令之一来调用该函数:

$ curl http://$FISSION_ROUTER/hello
$ http http://$FISSION_ROUTER/hello

这应该会给你类似以下的输出:

正如你已经看到的,就像我们所看到的所有技术一样,一旦安装,与公共云中的 Fission 交互和使用与在本地运行时并无不同。你真的不需要太在意外部访问等等,因为 Fission 和 Kubernetes 都已经为你解决了这个问题。

guestbook

在我们继续更高级的示例之前,让我们快速再次启动我们的 guestbook 应用程序。要做到这一点,切换到存储库中的/Chapter08/guestbook/文件夹,然后运行以下命令:

$ kubectl create -f redis.yaml
$ fission env create --name python --image fission/python-env
$ fission function create --name guestbook-get --env python --code get.py --url /guestbook --method GET
$ fission function create --name guestbook-add --env python --code add.py --url /guestbook --method POST
$ open http://$FISSION_ROUTER/guestbook

这应该会给你类似以下的输出:

这将启动应用程序,并且还会在浏览器中打开,你可以在其中添加评论:

更多示例

在我们结束本章之前,让我们看一些在 Fission 中运行的示例代码,首先是一个天气检查器。

天气

在存储库的/Chapter08/weather/文件夹中,你会找到weather.js。这是一个简单的 Node.js 函数,用于查询 Yahoo 天气 API 以返回给定位置的当前天气:

'use strict';

const rp = require('request-promise-native');

module.exports = async function (context) {
    const stringBody = JSON.stringify(context.request.body);
    const body = JSON.parse(stringBody);
    const location = body.location;

    if (!location) {
        return {
            status: 400,
            body: {
                text: 'You must provide a location.'
            }
        };
    }

    try {
        const response = await rp(`https://query.yahooapis.com/v1/public/yql?q=select item.condition from weather.forecast where woeid in (select woeid from geo.places(1) where text="${location}") and u="c"&format=json`);
        const condition = JSON.parse(response).query.results.channel.item.condition;
        const text = condition.text;
        const temperature = condition.temp;
        return {
            status: 200,
            body: {
                text: `It is ${temperature} celsius degrees in ${location} and ${text}`
            },
            headers: {
                'Content-Type': 'application/json'
            }
        };
    } catch (e) {
        console.error(e);
        return {
            status: 500,
            body: e
        };
    }
}

正如你从前面的代码中看到的,该函数接受 JSON 编码的数据,其中必须包含一个有效的位置。因此,我们需要使用POST路由部署该函数,并且如果没有传递位置数据,它会报错,所以我们还应该部署一个GET路由。要做到这一点,只需在/Chapter08/weather/文件夹中运行以下命令:

$ fission env create --name nodejs --image fission/node-env
$ fission function create --name weather --env nodejs --code weather.js --url /weather --method POST
$ fission route create --method GET --url /weather --function weather

如果你已经在终端输出中看到了我们最初为 hello world 示例创建并运行的环境,那么第一个命令可能会导致错误:

现在我们已经部署了我们的函数,可以通过运行以下两个命令之一来快速测试它:

$ http http://$FISSION_ROUTER/weather
$ curl http://$FISSION_ROUTER/weather

因为我们没有提供位置,所以你应该会看到以下消息:

这正是代码的预期行为。正如你所看到的,它返回了一个400错误和我们预期的消息。通过运行以下命令之一提供位置(我使用了英格兰诺丁汉)应该会告诉你天气情况:

$ http POST http://$FISSION_ROUTER/weather location="Nottingham, England"
$ curl -H "Content-Type: application/json" -X POST -d '{"location":"Nottingham, England"}' http://$FISSION_ROUTER/weather 

你可以从下面的终端输出中看到,它已经确认了我所在的地方目前天气并不是很好:

Slack

在这个例子中,我们将在当前 Kubernetes 安装的默认命名空间中每次创建或删除服务时发布一条消息。这些消息将通过名为 Slack 的 Webhook 发布到一个名为 Slack 的群组消息服务中。

Slack 是一个在线协作工具,允许团队在其中与聊天机器人和其他人进行交互。它提供免费和付费的服务,以及一个详尽的 API 供你的应用程序连接到你的聊天室。

我将假设你已经可以访问 Slack 工作空间,并且有权限向其添加应用程序。如果没有,那么你可以在slack.com/配置一个新的工作空间。

一旦你进入了你的工作空间,点击屏幕左上角的工作空间名称,然后从下拉列表中选择“管理应用程序”。这将带你进入 Slack 的应用程序目录。在这里,在页面顶部的搜索框中输入Incoming WebHooks,选择结果,然后点击“添加配置”按钮。

按照屏幕上的说明创建你选择的频道的 Webhook。我选择在随机频道发布我的更新,我还自定义了图标。在这个页面上,你还会找到一个 Webhook URL。我的(现在已被删除)是 https://hooks.slack.com/services/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

记下这一点,因为我们需要用它来更新代码。正如你在下面的代码中所看到的,你也可以在 /Chapter08/slack/ 仓库中找到,第三行需要用你的 Webhook 详细信息进行更新:

'use strict';

let https = require('https');

const slackWebhookPath = "/put/your/url/here"; // Something like "/services/XXX/YYY/zZz123"

function upcaseFirst(s) {
    return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
}

async function sendSlackMessage(msg) {
    let postData = `{"text": "${msg}"}`;
    let options = {
        hostname: "hooks.slack.com",
        path: slackWebhookPath,
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        }
    };

    return new Promise(function(resolve, reject) {
        let req = https.request(options, function(res) {
            console.log(`slack request status = ${res.statusCode}`);
            return resolve();
        });
        req.write(postData);
        req.end();
    });
}

module.exports = async function(context) {
    console.log(context.request.headers);

    let obj = context.request.body;
    let version = obj.metadata.resourceVersion;
    let eventType = context.request.get('X-Kubernetes-Event-Type');
    let objType = context.request.get('X-Kubernetes-Object-Type');

    let msg = `${upcaseFirst(eventType)} ${objType} ${obj.metadata.name}`;
    console.log(msg, version);

    if (eventType == 'DELETED' || eventType == 'ADDED') {
        console.log("sending event to slack")
        await sendSlackMessage(msg);
    }

    return {
        status: 200,
        body: ""
    }
}

为了做到这一点,粘贴https://hooks.slack.com后面的所有内容,包括斜杠(/)。对我来说,这是/services/T8F3CR4GG/B8FNRR3PC/wmLSDgS0fl5SGOcAgNjwr6pC

该行应该类似于以下内容:

const slackWebhookPath = "/services/T8F3CR4GG/B8FNRR3PC/wmLSDgS0fl5SGOcAgNjwr6pC"; // Something like "/services/XXX/YYY/zZz123"

确保文件名为 kubeEventsSlack.js,一旦你的 Webhook 详细信息在代码中,我们可以使用以下命令创建和启动函数:

$ fission function create --name kubeslack --env nodejs --code kubeEventsSlack.js

函数创建后,我们需要创建一些东西来触发它。以前,我们一直在使用 HTTP 调用来调用函数。不过这一次,我们希望在我们的 Kubernetes 集群中发生某些事情时触发函数。为此,我们需要创建一个观察。

为了做到这一点,运行以下命令:

$ fission watch create --function kubeslack --type service --ns default

fission watch 命令是我们尚未讨论过的内容,所以让我们花点时间了解一下更多关于它的信息。

作为我们 Fission 部署的一部分,有一个名为 kubewatcher 的服务。默认情况下,Fission 使用这个服务来通过观察 Kubernetes API 来帮助管理自身,但也向最终用户公开。用于创建之前观察的命令创建了一个观察者,它每次在默认命名空间中对服务进行更改时调用我们的函数(--function kubeslack)。我们还可以设置一个观察,以查找对 pods、deployments 等的更改,通过更改类型:

现在我们需要在默认命名空间中启动一个服务。为此,切换到 /Chapter03/ 文件夹并运行以下命令:

$ kubectl apply -f cli-hello-world.yml

然后,通过运行以下命令删除服务:

$ kubectl delete service cli-hello-world 

如果你检查 Slack,你应该会看到两条消息,确认一个名为 cli-hello-world 的服务已被添加和删除:

你应该几乎实时地看到这种情况发生,你可能还会看到有关在默认命名空间内启动其他服务的消息。

鲸鱼

接下来,也是最后一个例子,我们要看的是一个二进制环境。这个环境与我们一直在看的环境不同,因为它不包含编程语言。相反,我们将部署一个安装和配置名为cowsay的 Unix 工具的 bash 脚本。代码如下,并且位于/Chapter08/whale/文件夹中:

#!/bin/sh

if ! hash cowsay 2> /dev/null; then
    apk update > /dev/null
    apk add curl perl > /dev/null
    curl https://raw.githubusercontent.com/docker/whalesay/master/cowsay > /bin/cowsay 2> /dev/null
    chmod +x /bin/cowsay
    mkdir -p /usr/local/share/cows/
    curl https://raw.githubusercontent.com/docker/whalesay/master/docker.cow > /usr/local/share/cows/default.cow 2> /dev/null
fi

cowsay

如你所见,bash 脚本有两个部分。第一部分运行cowsay命令,如果出错,它将使用apk来安装curlperl。安装完成后,它会下载代码副本,并配置默认行为。然后在安装后运行cowsay命令。

也许你会想,APK 是什么,cowsay又是什么?由于部署到 Fission 环境中的容器运行的是 Alpine Linux,我们需要使用Alpine 软件包管理器APK)来安装我们代码运行所需的必要软件包。

Alpine Linux 是一个 Linux 发行版,在过去的两年里开始在更传统的 Ubuntu/CentOS 安装中获得了很多关注,这是因为它的体积。Alpine Linux 的基本安装只需 8MB 的空间。然而,尽管它很小,但它仍然和其他 Linux 发行版一样功能强大。它小巧的体积加上强大的功能,使其成为构建容器的完美操作系统。

cowsay是一个 Unix 命令,它会在一个来自牛的对话气泡中重复你给它的任何输入,因此得名cowsay。我们将安装 Docker 自己的版本cowsay,它使用的是鲸鱼而不是牛。要部署二进制函数,我们首先需要创建环境:

$ fission env create --name binary --image fission/binary-env

现在我们可以部署函数并创建POSTGET路由,以便我们可以访问它:

$ fission function create --name whalesay --env binary --deploy whalesay.sh --url /whale --method POST
$ fission route create --method GET --url /whale --function whalesay

上述命令的输出如下截图所示:

现在我们已经部署了我们的函数,我们可以使用以下之一来访问它:

$ http http://$FISSION_ROUTER/whale
$ curl http://$FISSION_ROUTER/whale

这将返回一个 ASCII 鲸鱼,如下终端输出所示:

你可能会注意到对话框中没有任何内容;那是因为我们需要POST一些东西。与之前的示例不同,我们启动的函数将简单地重复我们发布的任何内容。因此,如果我们POST一个 JSON 对象,它将返回 JSON 对象。因此,我们将只发布纯文本:

$ echo 'Hello from Whalesay !!!' | http POST http://$FISSION_ROUTER/whale
$ curl -X POST -H "Content-Type: text/plain" --data 'Hello from Whalesay !!!' http://$FISSION_ROUTER/whale

正如你可以从以下终端输出中看到的那样,这将返回我们发布的消息:

现在,你可能会认为这似乎是一个相当愚蠢的例子。然而,我们在这里所做的是获取 HTTP 请求的内容,并将其发布到 Linux 二进制文件中,然后使用我们发布的内容执行它。然后,我们通过 HTTP 请求返回运行命令的输出。

此时,您可能希望终止/关闭您已经启动以测试 Fission 的任何 Kubernetes 集群。

摘要

在本章中,我们已经研究了 Fission。我们使用 Helm 进行了安装,并在本地和 Google Cloud 上部署了它。我们还启动了几个测试应用程序,一些基本的,一些调用第三方服务来发布和返回信息。在安装和配置示例应用程序的过程中,我希望您开始看到 Fission 的用处以及它和其他无服务器技术如何集成到您自己的应用程序中。

当我开始写这一章时,我希望包括一些关于 Fission 工作流和 Fission UI 的部分。然而,在写作时,这两个附加组件都无法正常工作。现在,不要误会,Fission 是一种强大且易于使用的技术;然而,它非常新,并且仍在开发中,就像 Kubernetes 一样——这意味着在代码基础变得更加稳定之前,新版本中会有破坏性的更新。

例如,我们安装的 Fission 版本 0.4.0 是因为在写作时,最新版本的 Kubernetes 1.8 删除了ThirdPartyResources功能,并用CustomResourceDefinitions替换,这意味着旧版本的 Fission 将无法在当前版本的 Kubernetes 上运行。

我们将在剩下的章节中研究 Kubernetes 的发布周期以及这可能对您产生的影响。

第九章:查看 OpenFaaS

我们将要看的最后一个平台叫做 OpenFaaS。这是一个相对较新的项目,只支持 Kubernetes,所以我们不会详细介绍。然而,该项目正在获得很多关注和支持者,因此我认为我们有必要提到它。

在本章中,我们将看到:

  • 什么是 OpenFaaS,由谁制作?

  • 使用 Minikube 在本地安装 OpenFaaS

OpenFaaS 简介

OpenFaaS 是由 Alex Ellis 于 2016 年 12 月开始的,就在我写这篇文章的一年多前。最初,它是设计用于与 Docker swarm 一起工作的。

Docker swarm 是 Docker 自己的集群技术;它允许您将运行 Docker Engine 的一些主机连接在一起。从表面上看,Docker swarm 与 Kubernetes 非常相似。然而,当你深入研究这两种技术的工作原理时,你会发现它们不仅工作方式不同,而且设计目的也不同。

自 2016 年 12 月的第一次提交以来,OpenFaaS 在 Docker 世界中引起了很多关注。Ellis 和其他 OpenFaaS 社区成员曾在 DockerCon Europe、Moby Summit、KubeCon 和 Gluecon 以及过去 12 个月的众多聚会上发表讲话。OpenFaaS 还被列入了 InfoWorld Bossie Awards 2017,被评为 2017 年最佳云计算软件项目之一。

OpenFaaS 可能是功能最丰富的函数即服务提供商,它支持 Docker swarm,因此很合理地,软件的 Kubernetes 版本最终会发布——这个 Kubernetes 版本被称为faas-netes,它在 2017 年 7 月进行了第一次提交。

在本地运行 OpenFaaS

与其查看 Docker swarm 上的 OpenFaaS 并对比在 Kubernetes 和 Docker swarm 上运行服务,我们将直接深入并在 Kubernetes 上安装 OpenFaaS。就像我们涵盖的所有工具一样,我们需要一个命令行客户端,所以我们将从安装它开始。

OpenFaaS 命令行客户端

OpenFaaS 命令行客户端适用于我们的三个操作系统。让我们逐个安装它,从 macOS High Sierra 开始。

正如你可能已经猜到的那样,我们将使用 Homebrew 来完成这个任务。要安装客户端,只需运行以下命令:

$ brew install faas-cli

对于 Ubuntu 17.04,可以使用 OpenFaaS CLI 安装脚本来安装 CLI,您可以通过运行以下命令直接从 GitHub 运行:

$ curl -sL cli.openfaas.com | sudo sh

如果您运行的是旧版本,此脚本还将更新已安装的 faas-cli 版本。

要在 Windows 10 专业版上安装 CLI,首先以管理员用户身份打开 PowerShell 窗口;您可以通过从任务栏中的 PowerShell 菜单中选择以管理员身份运行来执行此操作。一旦打开,您应该看到您在C:\WINDOWS\system32文件夹中,如果没有,那么运行以下命令:

$ cd C:\WINDOWS\system32

一旦您在C:\WINDOWS\system32文件夹中,您可以通过运行以下命令下载 OpenFaaS CLI:

$ Invoke-WebRequest -Uri https://github.com/openfaas/faas-cli/releases/
download/0.5.1/faas-cli.exe -UseBasicParsing -OutFile faas-cli.exe

您可以在项目的 GitHub 发布页面上找到 OpenFaaS CLI 的最新版本详情,网址为github.com/openfaas/faas-cli/releases/

安装完成后,您应该能够运行以下命令来确认已安装的 CLI 的版本号:

$ faas-cli version

Docker

接下来,我们需要在本地安装 Docker。虽然我在本节开头说我们不会在 Docker swarm 上安装,但 OpenFaaS 仍然使用 Docker Engine,所以我们需要在本地安装它。

如果您正在运行 Ubuntu 17.04,可以通过运行以下命令使用 Docker 的安装脚本来安装 Docker:

$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh

要在 macOS High Sierra 上安装 Docker,可以使用 Homebrew 和 Cask 进行安装,运行:

$ brew cask install docker

对于 Windows 10 专业版用户,您可以使用 Chocolatey 并运行:

$ choco install docker-for-windows

安装了 Docker for macOS 和 Docker for Windows 之后,您需要打开 Docker 应用程序来完成安装。如果不这样做,Docker 将无法启动,我们稍后在本章中将使用 Docker 的示例也将无法工作。首次启动需要几分钟,因为它需要下载和配置一个小型虚拟机。

启动 Minikube 集群

现在我们已经安装了 OpenFaaS CLI,我们可以继续使用 Minikube 启动单节点 Kubernetes 集群。要做到这一点,请运行以下命令:

$ minikube start

这将启动集群并配置我们的本地 Kubernetes CLI 与之通信,除非您安装了 Windows 10 专业版,然后您可能会看到以下消息:

Docker for Windows 安装的一部分启用了 Hyper-V,正如我们在第三章中发现的,本地安装 Kubernetes,当我们最初安装 Minikube 时,您不能同时运行 VirtualBox 和 Hyper-V。为了解决这个问题,我们需要配置 Minikube 使用 Hyper-V 来支持虚拟化,而不是 VirtualBox。

为此,打开 Hyper-V Manager,选择 Virtual Switch Manager,然后创建一个新的外部虚拟交换机。如下截图中所示,将其命名为minikube,并确保选中“允许管理操作系统共享此网络适配器”复选框:

创建了虚拟交换机后,重新启动您的机器。一旦您的机器恢复在线,以管理员权限打开一个 PowerShell 窗口,并运行以下命令启动单节点 Kubernetes 集群:

$ minikube start --vm-driver=hyperv --hyperv-virtual-switch=minikube

在 Hyper-V Manager 中,您应该能够看到您的minikube虚拟机的状态为 Running,以及列为MobyLinuxVM的 Docker 虚拟机:

现在您应该可以继续进行其余的指令了。

使用 Helm 安装 OpenFaaS

现在我们已经启动了集群,我们需要安装 OpenFaaS 组件。我们将使用 Helm 来进行安装。如果您在上一章没有安装 Helm,请参考那里的安装说明。与 Helm 安装一样,我们需要初始化它,这将安装服务器端组件 Tiller。要做到这一点,请运行以下命令:

$ helm init

现在我们在本地单节点 Kubernetes 集群上配置了 Helm,我们需要从 GitHub 下载 faas-netes 代码库的副本。为此,请运行以下命令之一:

$ git clone https://github.com/openfaas/faas-netes
$ cd faas-netes

您还可以从github.com/openfaas/faas-netes下载 ZIP 文件的副本。

下载后,我们可以使用以下命令使用 Helm 安装 OpenFaaS:

$ helm upgrade --install --debug --reset-values --set async=false --set rbac=false openfaas openfaas/

这将返回相当多的输出,但您真正需要关注的部分只有最后一行,其中应该包含类似于以下内容:

一两分钟后,您可以通过运行输出末尾的命令来检查安装的状态:

$ kubectl --namespace=default get deployments -l "release=openfaas, app=openfaas"

您应该看到类似以下终端输出的内容:

现在我们已经安装并可用 OpenFaaS,我们可以尝试启动一个 hello world 示例。

你好世界!

与我们涵盖的其他服务一样,我们将运行一个快速的 hello world 示例。这将让您了解 OpenFaaS 和其他工具之间的区别。我们还将找出为什么需要安装 Docker。首先,运行以下命令:

$ mkdir hello
$ cd hello
$ faas-cli new --lang python hello

这将创建一个名为hello的文件夹,然后在其中创建一个名为hello.yml的文件和一些文件夹:

接下来,我们需要登录到 Docker Hub 帐户。要做到这一点,请运行以下命令:

$ docker login

如果您没有 Docker Hub 帐户,可以免费在hub.docker.com/注册一个:

在文本编辑器中打开hello.yml将显示以下内容:

provider:
  name: faas
  gateway: http://localhost:8080

functions:
  hello:
    lang: python
    handler: ./hello
    image: hello

编辑文件,使image读取your-dockerhub-username/hello。在我的情况下,这是russmckendrick/hello

编辑后,运行以下命令:

$ faas-cli build -f hello.yml

这将在本地构建一个包含/hello文件夹中代码的容器,使用您本地的 Docker Engine 安装:

现在我们已经构建了容器镜像,您可以通过运行以下命令来查看:

$ docker image ls

现在我们可以通过运行以下命令将图像推送到我们的 Docker Hub 帐户:

$ faas-cli push -f hello.yml

现在我们已经将包含我们函数的容器镜像上传到 Docker Hub,您可以在hub.docker.com/r/russmckendrick/hello/上看到:

我们可以启动我们的函数,但是首先我们需要将 Minikube VM 的 IP 地址放入一个变量中,这样当我们运行 OpenFaaS CLI 时,它就知道要连接到哪里:

$ export gw=http://$(minikube ip):31112

现在我们可以使用以下命令启动我们的函数:

$ faas-cli deploy -f hello.yml --gateway $gw

我们可以通过运行以下命令来调用该函数:

$ echo test | faas-cli invoke hello --gateway $gw

这应该返回单词test

我们还可以通过运行以下命令来检查函数的状态:

$ faas-cli list --gateway $gw

如您所见,我们有一个正在运行的函数副本,并且已被调用一次。在我们进入下一节之前,再运行几次该函数。

OpenFaaS UI 和 store

OpenFaaS 带有基于 Web 的用户界面,可以通过在 macOS 和 Ubuntu 上运行以下命令来访问:

$ open http://$(minikube ip):31112/

Windows 用户可以运行:

$ minikube service gateway-external

这将打开 OpenFaaS Web UI,在那里您应该看到我们在上一节中创建的hello函数。选择该函数,在请求正文表单字段中输入一些文本,然后点击调用将调用该函数,如下截图所示:

OpenFaaS UI 还包括一个商店,您可以直接将社区策划的函数部署到您的 OpenFaaS 安装中。要访问商店,请单击“部署新函数”按钮,然后您将看到可以部署的函数列表:

选择mememachine函数,然后点击 DEPLOY 按钮。部署后,您应该在hello函数下看到一个名为mememachine的函数,选择它,在请求正文表单字段中输入以下内容:

{"image": "http://i.imgflip.com/qiefv.jpg","top": "CREATING A MEME","bottom": "USING OPENFAAS"}

选择下载,然后点击调用:

一两秒后,将下载一个文件,打开它后,您应该看到类似以下截图的内容:

正如您所看到的,我们有成功宝宝的迷因,它在图像字段中以 URL i.imgflip.com/qiefv.jpg定义,并且我们传递给topbottom变量的两行文本。

该函数的源代码可以在github.com/faas-and-furious/openfaas-mememachine/找到。正如您所看到的,它是一个 Node.js 容器,用于下载图像,添加文本,然后返回新图像的内容。OpenFaaS 商店中大多数函数的源代码可以在FaaS and Furious GitHub 存储库中找到github.com/faas-and-furious/

Prometheus

当我们第一次推出 OpenFaaS 时,您可能已经注意到其中一个部署被称为 Prometheus。

Prometheus 是我们在前几章中讨论过的云原生计算基金会项目之一。最初由 SoundCloud 构建,它迅速成为基于容器的安装收集指标的新标准-您可以在项目网站prometheus.io/上找到更多信息。

这是记录我们在整个章节中一直在调用的 OpenFaaS 网关的一些基本统计信息;您可以通过运行以下两个命令之一来打开 Prometheus(请记住,open在 Windows 上不起作用):

$ open http://$(minikube ip):31119/
$ minikube service prometheus-external

打开后,您将看到一个空白页面。在文本框中输入以下内容:

gateway_function_invocation_total

单击“执行”按钮后,您将看到一个图表,其中详细说明了每个函数被调用的次数,以及每个函数调用的 HTTP 状态:

从上面的图表中可以看出,当我运行mememachine函数时出现了一些错误,因为镜像大小太大,导致函数崩溃。还有很多其他指标被捕获。我建议您点击一下,看看一些图表。

完成 OpenFaaS 安装后,可以通过运行以下命令来删除安装:

$ minikube stop
$ minikube delete

摘要

OpenFaaS 是一个快速增长的函数即服务平台,正如前面提到的,它得到了很多社区支持。它的独特之处在于它与 Docker 本地交互,用于构建和推送镜像到 Docker Hub,而我们之前使用的其他工具是使用 Kubernetes 将我们的函数注入到容器和运行时中。

这就是能够轻松分发容器镜像供 OpenFaaS 使用的优势,正如我们通过mememachine示例和 OpenFaaS 商店中的其他函数所演示的。

在本章中我们没有做的一件事是在公共云中启动 Kubernetes 集群并部署 OpenFaaS。其中一个原因是,为了能够访问它,我们必须通过公共 IP 地址使其对我们的主机机器可用,这将使我们的安装暴露给世界。在下一章中,我们将讨论在公共云中运行我们的 Kubernetes 集群时的安全考虑,以及其他事项。

第十章:无服务器考虑

在上一章的最后,我们提到了保护我们的无服务器安装的问题,以及开箱即用的安全性可能存在的缺陷。在本章中,我们将直面这个问题,讨论在部署 Kubernetes 上的无服务器函数服务时应该注意的事项,以及如何最好地监视您的集群。

我们将研究:

  • 安全最佳实践

  • 您如何监视您的 Kubernetes 集群?

让我们从讨论安全性开始。

安全最佳实践

在谈论安全最佳实践时,我们的最终目标应该是确保任何未经授权的第三方都无法访问我们的应用程序或基础架构的任何部分。

例如,我希望最终用户能够运行一个脚本,通过直接的 HTTP 请求,由网页或移动应用程序调用我的无服务器函数之一。但是,我不希望同一用户能够访问我的 Kubernetes 仪表板,例如。

现在,这可能看起来是一个非常明显的例子,但是,正如我们在过去几年中所看到的,开箱即用的配置并不总是考虑到这个最基本的安全要求。MongoDB 就是一个很好的例子。

在 2017 年 1 月、6 月和 9 月,有几家主要新闻媒体报道,大约有 99,000 个 MongoDB 安装暴露在互联网上;这些安装要么没有打补丁,要么配置不当。这导致第三方访问、复制和删除了其中的数据。

在某些情况下,犯罪分子会复制数据,从源数据库中删除数据,然后向数据库所有者发送勒索要求,要求安全返回已删除的数据。其他攻击者只是删除了数据库,并用一个名为PWNED_SECURE_YOUR_STUFF_SILLYDELETED_BECAUSE_YOU_DIDNT_PASSWORD_PROTECT_YOUR_MONGODB的空数据库替换它。您可以在以下推文中找到附加到赎金的示例:twitter.com/nmerrigan/status/818034565700849664

之前发推文的研究人员 Niall Merrigan 在另一条推文中指出,在一个早晨,受损的 MongoDB 安装数量从 12,000 增加到了大约 27,000。

微软等公司开始推广他们自己的 NoSQL 数据库服务,比如 Azure DocumentDB,发布了标题为“首先,安全是我们的首要任务”的博客文章,以及以下链接中的图片:azure.microsoft.com/en-in/blog/dear-mongodb-users-we-welcome-you-in-azure-documentdb/,微软将他们自己的 DocumentDB 标志和 MongoDB 标志放在生锈的锁和现代保险门上。

那么,这与保护我们的无服务器函数有什么关系呢?嗯,要回答这个问题,我们必须首先看一下 MongoDB 问题的根本原因。

许多受攻击的 MongoDB 版本最初被配置为绑定到0.0.0.0,这意味着服务附加到服务器上的所有 IP 地址。如果您的 MongoDB 安装是在仅在私有网络上运行的服务器上启动,这就不是问题,但是被攻击的安装并非如此,它们是在公共云中托管的,其中一些只提供外部 IP 地址。

现在,你可能会想,访问数据库肯定需要某种身份验证吧?嗯,你错了;在 MongoDB 仍然在所有网络接口(0.0.0.0)上监听的时候,身份验证是一个额外的配置步骤。这意味着,根据网站 Shodan 在 2015 年 7 月的数据,公共互联网上共有 595.2 TB 的 MongoDB 数据暴露在外,并且没有进行身份验证。

此外,你读对了日期,这是 2015 年的一个问题,很多安装仍然没有修补和配置正确。

那么,我们如何避免在我们的 Kubernetes 和服务器功能服务安装中出现这些基本配置问题呢?让我们先看看 Kubernetes 本身。

保护 Kubernetes

Kubernetes 默认情况下是相当安全的。提供 Kubernetes 的两家云服务提供商,Google Cloud 和 Microsoft Azure,工作方式类似。

一个管理节点部署在您的节点旁边;这个管理节点控制整个集群,并且默认情况下暴露给公共互联网和云服务提供商。我们可以通过使用以下命令启动一个集群来测试未经身份验证的用户看到的内容:

$ gcloud container clusters create kube

现在,默认情况下,此命令将启动集群,包括管理节点。用于验证您的本地kubectl副本与集群的所有证书都是在云上生成的,一旦集群启动,它将配置kubectl以获取连接所需的所有信息。如果您查看配置文件,可以在~/.kube/config中找到,您应该会看到类似以下内容:

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURERENDQWZTZ0F3SUJBZ0lSQUpEbDRydFJWSENjSlNhL2lxcVN4d0V3RFFZSktvWklodmNOQVFFTEJRQXcKTHpFdE1Dc0dBMVVFQXhNa1pUTmtaRFZtT1dJdE1UVTBPUzAwTlRoa0xXRmxZV010Tnpkak9HTTBOalV5Wm1aaQpNQjRYRFRFM01USXlOekV4TXpRek0xb1hEVEl5TVRJeU5qRXlNelF6TTFvd0x6RXRNQ3NHQTFVRUF4TWtaVE5rClpEVm1PV0l0TVRVME9TMDBOVIhaveEditedThisDoNotW0rryT0dNME5qVXlabVppTUlJQklqQU5CZ2txaGtpRzl3MEIKQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBb21pdGF4eE9DMzJwRE5nY3RLQkFKRXZhVjVBL1ZEMnByU0xYcnpsYwpOL1h1UFI2NWpVR0Z3emVNbkcvMHNrZXZoUklEUncvK3B0elNDSnR5WFhtNnUysdfsdfsdfsd4LzdHZmxSCmtnRWNPY1pZd2NzS3dIU1lRTXBQVE5Lek51b0JqcDlla0ltcnFSNW5qWHRrNU1DS0ROS2lWbVlwTVBBV2dCL1MKakRDYWpNcUxwZU5FdDlRVkluQVI3aUNTeFRHQkN5cE5ZRHd3R0ZOaFhka3B6b01rcUg2dDhmeXlSTEV1dkZTMgpJVFNOUzJsRVFPc2x4L1MxaklVVEVlSVlXclFBRlJrRGs2M2VoTnRhVzNubU0rMU9FUCtqT2ZJR3hYWVdtR29FCkgwRERBRmttRjNrcVEvR3JnbThDb3o0UWdLMlIzMEh0OVlYeUkvckxNSTF5dVFJREFRQUJveU13SVRBT0JnTsdfsdfsdhxdVCQU1DQWdRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQQphSnRrdGYyZWFrcVFNQldSV2MrSGJZUzNKYjlzZktTNWFGSW14a2duVkNpMHBRVXJGWEwzNEt3dk5raG9SQUlkCklBRVpmRTUwT2p3WFdjMnluVW1XL1dMeVU4K0pQZDNWWDBML0w1SW9oMGdud1c1NU4xK0dQQTZNRWZmSnltenAKVGE3U1NmbUJaTFIyemFaSGNGWDZxeThzMEhVRHN0L2hTQ0E1WFo5bHp6U1B0WkwxUTVpanhVVUkxbjFsS1p4dwpXTndxaDhtTFBmME1xWE9sejdMT1g2YmJsQ1B6cUcxRTdRdG5leUpXNk5oL2FmQkY5V2tnT1d3TWlBMlRFMHZ3CmkrMktzdCtWQ1JkaDlRSVEzUzQvMlRTVHJhMlRCMk9WOWpYY2tYckRaeXJaTThxMzBQQjlnay8zR29pajA4N1EKOWdleUJUNGRxWXZlT3NyWmNNMWlxUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    server: https://35.202.202.37
  name: gke_russ-kubernetes-cluster_us-central1-b_kube
contexts:
- context:
    cluster: gke_russ-kubernetes-cluster_us-central1-b_kube
    user: gke_russ-kubernetes-cluster_us-central1-b_kube
  name: gke_russ-kubernetes-cluster_us-central1-b_kube
current-context: gke_russ-kubernetes-cluster_us-central1-b_kube
kind: Config
preferences: {}
users:
- name: gke_russ-kubernetes-cluster_us-central1-b_kube
  user:
    auth-provider:
      config:
        cmd-args: config config-helper --format=json
        cmd-path: /usr/local/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/bin/gcloud
        expiry-key: '{.credential.token_expiry}'
        token-key: '{.credential.access_token}'
      name: gcp

正如您所看到的,certificate-authority-data部分中有一个证书存储。此证书用于验证您的集群,这意味着无论何时您运行诸如以下命令之类的命令,它都会返回预期的节点列表:

$ kubectl get nodes

节点将显示如下:

现在,打开您的~/.kube/config文件,并从certificate-authority-data部分中删除证书。这基本上会创建一个无效的证书,这意味着当您运行以下命令时,您将收到一个错误:

$ kubectl get nodes

错误将显示如下:

因此,除非您有正确证书的副本,否则无法连接到集群。不用担心,您仍然可以通过运行以下命令访问您的证书:

$ gcloud container clusters get-credentials kube

您将看到以下内容:

这个命令将连接到您的 Google Cloud 帐户,下载详细信息,并使用证书更新您的~/.kube/config文件。您可以通过运行以下命令测试新下载的凭据:

$ kubectl cluster-info

这将返回有关所有终端的详细信息:

您可能会注意到列表中的最后一个 URL 是用于 Kubernetes 仪表板的。那么它是如何保护的呢?

让我们尝试将 URL 输入到浏览器中并查看。我输入了https://35.202.202.37/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy(在您阅读此内容时,该 URL 将无法访问)到我的浏览器中,然后按回车键,立即收到一个证书警告;在接受了证书后,我看到了以下消息:

User "system:anonymous" cannot get services/proxy in the namespace "kube-system".: "No policy matched.\nUnknown user \"system:anonymous\""

这很好,因为这正是我们想要看到的——我们不希望未经身份验证的用户能够直接访问我们的仪表板。但是,我们如何访问它呢?我们没有用户名和密码,只有一个证书——即使我们有用户名和密码,我们会在哪里输入它们,考虑到我们从未被提示进行任何身份验证?

Kubernetes 有一个内置的代理服务器。启动时,代理服务器使用证书连接到您的 Kubernetes 集群。一旦连接,通过代理传递的所有流量都经过身份验证,您将能够使用服务。要启动代理,我们只需要运行以下命令:

$ kubectl proxy

您将看到代理启动如下:

这将在前台启动代理进程。从前面的终端输出中可以看到,代理正在本地机器的8001端口上监听。我们只需要替换 URL 的公共部分并将其放入浏览器中。所以在我的情况下,我更新如下:

https://35.202.202.37/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy

我改为如下所示:

http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy

这将直接带您进入仪表板:

到目前为止,我们已经证明了 Google Cloud 上的 Kubernetes 配置是安全的。Microsoft Azure 集群以类似的方式工作,例如,我们运行以下命令来更新一旦集群部署完成后的本地凭据:

$ az aks get-credentials --resource-group KubeResourceGroup --name AzureKubeCluster

在使用kubeadmkube-aws部署时,证书会生成并复制到我们的配置文件中。

因此,到目前为止,我们已经学到了,默认情况下 Kubernetes 强制执行基于证书的身份验证来保护您的安装,这意味着您必须付出相当大的努力来错误配置您的安装,以至于您的安装暴露给世界。然而,这有一个例外。这与您的安装无关;它更多地涉及您如何管理kubectl配置文件。

永远不要在任何地方发布它(例如,将其提交到 GitHub,或与同事共享)。如果它落入错误的手中,那么不仅有人拥有您的证书副本,他们还拥有您的集群其他信息,这意味着他们只需将其放置在本地机器上,就可以自由地开始启动应用程序。此外,由于大多数基于云的 Kubernetes 安装可以访问您的云提供商来启动支持服务,如负载均衡器、存储,以及可能的额外节点,您可能会发现自己面临相当大的账单,以及一个受损的集群。

我在本节前面分享的kubectl配置已被编辑,使其无效——还有它配置连接的集群已被终止。

所以,现在我们知道我们的 Kubernetes 集群应该是安全的,那么我们所看到的无服务器函数服务呢?

保护无服务器服务

我们已经在我们的本地 Kubernetes 集群和云上安装并连接了我们的每个服务。到目前为止,我们还没有真正考虑过如何保护它们——这是我们在上一章末提出的问题。

以下部分将讨论每个工具在其默认配置中的安全性以及此配置可能给您带来的潜在风险。我不会详细讨论如何保护每个工具,但在适当的情况下,我会提供链接到文档。

OpenFaaS

让我们从 OpenFaaS 开始。我仍然在运行我的 Google Cloud 集群,所以我将使用前一章中克隆的faas-netes文件夹中的以下命令在那里部署 OpenFaaS:

$ kubectl apply -f ./faas.yml,monitoring.yml

如您所见,这次我只使用了kubectl而不是helm。我们可以通过运行以下命令来检查部署的服务:

$ kubectl get services

这将返回以下内容:

需要注意的一件事是,默认情况下 OpenFaaS 使用NodePort而不是负载均衡器来公开网关服务。没问题,你可能会想;我们可以使用以下命令来找到部署的名称并公开它:

$ kubectl get deployments

现在我们知道部署被称为网关,我们可以运行:

$ kubectl expose deployment gateway --type=LoadBalancer --name=gateway-lb

一两分钟后,运行以下命令应该给我们提供外部 IP 地址和端口:

$ kubectl get services

结果将如下所示:

在浏览器中输入外部 IP 地址和端口8080——在我的情况下是http://35.224.135.38:8080/ui/——不幸的是,我们直接进入了 OpenFaaS UI,无需进行身份验证。使用命令行界面也是一样。那么,你如何保护你的 OpenFaaS 安装呢?

在 OpenFaaS GitHub 存储库上有关使用代理服务(如 Traefik 和 Kong)的说明。

Kong 是一个开源的 API 网关,它增加了诸如流量控制、日志记录、数据转换、分析以及最重要的身份验证等功能。有关 Kong 社区版的更多信息,请参阅konghq.com/kong-community-edition/

Traefik(发音为 Traffic)是一个反向 HTTP 代理,它从头开始设计,与 Kubernetes 等容器编排工具一起工作。它不仅提供负载平衡,还支持基本的 HTTP 身份验证和 SSL 终止。要了解有关 Traefik 的更多信息,请访问其网站traefik.io/

这两种工具都可以配置为位于 OpenFaaS 安装的前端并拦截请求,并在配置时向最终用户呈现登录提示。您可以通过使用公共云服务中的网络工具将 OpenFaaS 安装限制在您的 IP 地址上来保护 OpenFaaS 安装。这样做的缺点是,根据应用程序调用函数的方式,您可能无法完全将其限制。

因此,如果只是部署 OpenFaaS,将暴露 Kubernetes 集群的部分内容,这意味着第三方可能会潜在地访问您的资源,如果您不对其进行安全保护。有关保护 OpenFaaS 集群的更多信息,请参阅官方文档github.com/openfaas/faas/tree/master/guide。或者,您可以使用 Stefan Prodan 的 openfaas-gke 安装文件,该文件可以在github.com/stefanprodan/openfaas-gke/找到。还可以使用kubectl proxy命令访问 OpenFaaS 安装;但是,这可能会限制其实用性。

使用 OpenFaaS 还存在另一个潜在的安全问题,如果您已经是 Docker 用户,这应该是您熟悉的问题。由于 OpenFaaS 使用 Docker 镜像和 Docker Hub 作为其主要交付方法,因此在推送镜像时需要小心,因为镜像可能潜在地包含密码详细信息、API 凭据、自定义代码和其他您可能不希望通过公共容器镜像存储库访问的信息。解决此问题的方法是使用私有存储库或私有 Docker 注册表。

请不要将任何内容视为负面;OpenFaaS 是一款优秀的软件,我相信随着时间的推移,社区将引入变化,以确保之前详细介绍的步骤不会成为 Kubernetes 托管版本初始配置的一部分。

Kubeless

接下来,让我们来看看 Kubeless。为了在我的 Google Cloud Kubernetes 集群中部署最新版本,我运行了以下命令:

$ export RELEASE=v0.3.0
$ kubectl create ns kubeless
$ kubectl create -f https://github.com/kubeless/kubeless/releases/download/$RELEASE/kubeless-$RELEASE.yaml

部署后,我运行了以下命令来查看哪些服务已经被暴露:

$ kubectl get services -n kubeless

如您从以下终端输出中所见,没有服务被公开暴露:

到目前为止,一切都很顺利。让我们快速启动一个测试函数并将其暴露出来。在/Chapter04/hello-world/文件夹中,我运行了以下命令:

$ kubeless function deploy hello --from-file hello.py --handler hello.handler --runtime python2.7 --trigger-http

这按预期创建了函数。运行以下命令确认函数可用且正在运行:

$ kubectl get function
$ kubeless function ls
$ kubeless function call hello

运行以下命令将该函数暴露给世界:

$ kubectl expose deployment hello --type=LoadBalancer --name=hello-lb

在短时间内,当hello-lb服务运行时,我可以看到一个 IP 地址:

$ kubectl get services

到目前为止,我们实际上并没有做任何事情来锁定我们的安装,那么它有多安全呢?对于这个问题的简短答案是非常安全,但是默认安装的 Kubeless 比默认安装的 OpenFaaS 更安全的原因是什么?

从表面上看,这两种技术在架构上是相似的;它们的服务器组件都部署在我们的 Kubernetes 集群上,并且我们使用本地机器上的命令行界面与这些组件进行交互。例如,我们为 Kubeless 使用了以下命令:

$ kubeless function deploy hello --from-file hello.py --handler hello.handler
--runtime python2.7 --trigger-http

在上一章中,我们使用以下命令来启动 OpenFaaS 中的函数:

$ export gw=http://$(minikube ip):31112
$ faas-cli deploy -f hello.yml --gateway $gw

正如您可能已经注意到的那样,在 Kubeless 的配置或使用过程中,我们从未不得不提供任何关于我们 Kubernetes 集群的详细信息,而在 OpenFaaS 中,我们必须明确告诉命令行界面我们 OpenFaaS 安装的 IP 地址和端口。

Kubeless 确切知道我们的集群在哪里,更重要的是,它在需要访问时进行身份验证。由于 Kubeless 是一个本地的 Kubernetes 框架,而不是安装在 Kubernetes 之上,它正在集成到我们的集群中,并添加额外的功能——在这种情况下是函数——并且正在使用其他 Kubernetes 技术,比如kubectl和自定义资源定义,来根据需要将我们函数的代码注入到运行时中,这意味着一切都包含在我们的 Kubernetes 集群中,与之的所有交互都是安全的。

这可以通过从~/.kube/config文件中删除证书,然后尝试列出函数来进行演示。您应该会看到以下错误:

所有这些都意味着您的 Kubeless 安装默认情况下是安全的。

功能

Funktion,像 Kubeless 一样,默认情况下是安全的,因为它与您的 Kubernetes 集群紧密集成,并添加了额外的功能,其命令行界面在kubectl的基础上进行调用。

Apache OpenWhisk

Apache OpenWhisk,像 OpenFaaS 一样,安装在您的 Kubernetes 集群之上,而不是完全集成自己。然而,正如我们在第七章中所介绍的那样,Apache OpenWhisk 和 Kubernetes,一旦服务暴露到公共互联网,CLI 需要配置以对安装进行身份验证。在那一章中,我们运行了以下命令来暴露服务并对客户端进行身份验证:

$ kubectl -n openwhisk expose service nginx --type=LoadBalancer --name=front-end
$ wsk -i property set --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP --apihost https://35.188.204.73:443

因此,再次强调,默认情况下此服务是安全的,假设您不发布或分享身份验证密钥。

Fission

在 Fission 安装期间,我们必须设置两个环境变量:

$ helm install --namespace fission https://github.com/fission/fission/releases/download/0.4.0/fission-all-0.4.0.tgz
$ export FISSION_URL=http://$(kubectl --namespace fission get svc controller -o=jsonpath='{..ip}')
$ export FISSION_ROUTER=$(kubectl --namespace fission get svc router -o=jsonpath='{..ip}')
$ fission env create --name nodejs --image fission/node-env
$ curl https://raw.githubusercontent.com/fission/fission/master/examples/nodejs/hello.js > hello.js
$ fission function create --name hello --env nodejs --code hello.js
$ fission route create --method GET --url /hello --function hello
$ curl http://$FISSION_ROUTER/hello

FISSION_URLFISSION_ROUTER各有一个变量。这可能意味着不是所有的东西都是安全的。首先,让我们看看当我们访问FISSION_URL时我们得到了什么:

正如您所看到的,我们得到了一个标识 Fission API 和版本号的响应。从~/.kube/config文件中删除证书,并运行以下命令:

$ fission function list

我们仍然可以与我们的 Fission 安装进行交互;这意味着默认情况下 Fission 没有身份验证,并且当我们使用推荐的安装程序时,API 默认暴露在互联网上:

正在进行工作,以使用更安全的默认设置来发布 Fission;您可以在以下 GitHub 问题中跟踪其进展:github.com/fission/fission/issues/22/

在那之前,建议您更新 Helm 图表,将控制器服务的serviceType设置为ClusterIP。从下面的输出中可以看到,它目前设置为LoadBalancer

一旦您配置服务使用ClusterIP,您可以使用kubectl内置的代理配置从本地主机到控制器的端口转发。执行此操作的命令看起来类似于以下内容:

$ kubectl -n fission port-forward $(kubectl -n fission get pod -o name|grep controller|cut -f2 -d'/') 8888

这意味着您的FISSION_URL将类似于http://localhost:1234,而不是一个没有身份验证的外部可访问的 URL。Fission 开发人员正在将这个解决方案集成到 Fission 中,并且它应该成为 2018 年早期版本的默认配置之一。

结论

正如您所看到的,当涉及到保护我们的无服务器安装时,我们有一个相当混杂的情况——我们涵盖的一些解决方案是默认安全的,而另一些解决方案,比如旧的默认 MongoDB 配置,需要更多的工作来保护它们并使其达到生产就绪状态。在永久部署本书中涵盖的任何工具之前,请确保您已经审查了每个工具暴露的内容以及如何最好地将其锁定。

监控 Kubernetes

在我们开始研究各种监控 Kubernetes 集群的方法之前,我们应该快速谈谈当涉及到一个可能有很多移动部分的工具时,我们所说的监控是什么意思。

传统上,监控服务器意味着密切关注固定服务器上运行的应用程序的可用性。为了做到这一点,我们的监控工具将汇总有关 CPU、RAM 和磁盘利用率的信息,以及正在运行的服务、进程数量以及服务和服务器本身的可用性。

我们将在特定阈值处设置触发器,这样,例如,如果 CPU 负载增加,我们可以登录到服务器并在所述 CPU 负载开始影响我们应用程序性能之前进行一些调查。

正如您所能想象的,监控 Kubernetes 集群与此有很大不同。按设计,集群中运行的应用程序应具有容错性和高可用性——事实上,我们在之前章节中运行的函数有时只有执行函数所需的寿命。

这改变了我们监控集群的方式,因为我们相信许多我们传统上要监控的事情将由 Kubernetes 本身处理,而不需要我们登录并采取预防措施。

考虑到这一点,我们不需要深入了解监视 Kubernetes 集群的细节——这可能需要一本完全不同的书。相反,我们将快速查看一下使用仪表板、Google Cloud 和 Microsoft Azure 来审查我们 Kubernetes 集群的服务指标的一些选项,因为这两者都原生支持 Kubernetes 集群。

仪表板

Kubernetes 仪表板不仅是管理集群的重要资源;它还为您提供了一个很好的视觉概述,显示您正在运行的内容以及当前的性能。

例如,在命名空间下拉菜单中选择所有命名空间,然后在左侧菜单的工作负载部分点击 Pods,将为您提供所有正在运行的 Pod 的列表,以及每个 Pod 当前使用 CPU 和 RAM 的详细情况:

点击一个 Pod——在这种情况下是 heapster——将为您提供该 Pod 中组成容器正在使用的资源的更详细的分解:

向下滚动将显示容器。在 heapster 的情况下,Pod 中有三个容器。从这里,您可以实时查看每个容器的日志:

可以想象,这是一个非常有用的功能,当您需要调试正在运行的容器时。

然而,您可能已经注意到在查看仪表板时,显示的 CPU 和 RAM 利用率仅为过去 15 分钟的数据——您无法深入挖掘或查看更早的数据。因此,有关当前运行服务的信息可以通过仪表板获得。

这使得仪表板非常适合登录并快速了解集群的概况——而且仪表板已经默认包含在大多数 Kubernetes 集群中,非常方便。

Google Cloud

接下来是 Google Cloud。从外表看,Google Cloud 控制台的 Kubernetes 部分看起来几乎与 Kubernetes 仪表板相似:

然而,正如您从前面的截图中所看到的,除了显示 OK 状态之外,它实际上并没有告诉您有关集群内部发生了什么。相反,您需要使用 Stackdriver,它可以从 Google Cloud 控制台的左侧菜单中访问。

Google Stackdriver 是一个 Google Cloud 服务,允许您记录来自多个来源的指标,包括 Google Cloud 服务、AWS,以及使用代理的个别服务器。该服务不是免费的;详细的成本分解可以在cloud.google.com/stackdriver/pricing找到。我们将使用免费试用版,但如果您已经使用过 Google Stackdriver,则以下步骤可能会产生费用。

当您首次进入 Stackdriver 时,将会被问到几个问题。通过这个过程,在结束时,您应该可以免费试用并收集来自您的 Kubernetes 集群的日志。几分钟后,您应该开始看到来自您的集群的信息显示在指标资源管理器中。从这里,您可以开始构建诸如以下仪表板之类的仪表板:

正如您从上述屏幕截图中所看到的,我们有选项查看超过 15 分钟的数据 - 实际上,仪表板显示了超过一个小时的数据,这就是集群的年龄。

Stackdriver 不仅可以让您访问有关您的集群的指标,还可以访问来自您的 Kubernetes 集群和容器本身的日志:

由于日志和指标被存储在集群之外,您还可以访问有关容器的历史信息。如果您在一个只活动几秒钟的容器中运行一个函数,您不仅可以看到该容器的 RAM 和 CPU 利用率,还可以访问整个容器的生命周期。

Stackdriver 的其他功能包括关于您整体使用情况的每日、每周和每月电子邮件报告,以及配置触发器的选项,用于当指标阈值被触发或日志文件中出现事件时通知您 - 您可以通过短信、电子邮件甚至聊天产品(如 Slack 或 Campfire)收到这些通知。

Microsoft Azure

与 Google Cloud 相比,Microsoft Azure 对您的 Kubernetes 集群的开箱即用的洞察力并不是很好。您无法看到集群内部的运行情况,虽然有可用的指标,但它们只适用于主机机器 - 例如,您可以在以下屏幕截图中看到 CPU 利用率:

同样,您可以使用以下命令启动 Kubernetes 仪表板(确保用您自己的资源组和名称替换):

$ az aks browse --resource-group KubeResourceGroup --name AzureKubeCluster

不过不用担心,还有容器监控解决方案;这是一个基于代理的系统,您可以部署在您的 Kubernetes 集群上,然后将信息反馈给 Azure 门户。

要部署它,您需要在 Azure 门户内搜索 Microsoft 的容器监控解决方案。单击“创建”按钮将要求您创建一个工作空间;我选择在与我的 Kubernetes 集群相同的资源组和区域中创建我的工作空间。确保选中“固定到仪表板”,然后单击“部署”。

这就有点复杂了,因为您需要获取工作空间 ID 和主密钥。这些信息深藏在一系列链接中。要获取它们,转到您的仪表板并选择您的工作空间—我的标记为 Containers(russ-monitor)。然后,单击“OMS 工作区”,然后单击“高级设置”。您应该看到类似以下屏幕截图的内容:

记下工作空间 ID 和主密钥(在上述屏幕截图中我的已模糊处理)。在本书附带的存储库的Chapter10文件夹中有一个名为oms-daemonset.yaml的文件;复制该文件并更新其中的env部分的值,以便使用您实际的工作空间 ID 和主密钥进行更新:

env:
  - name: WSID
    value: <WORKSPACE ID>
  - name: KEY
    value: <PRIMARY KEY>

更新文件后,从保存了更新后的oms-daemonset.yaml文件的同一文件夹中运行以下命令,将daemonset部署到您的集群中:

$ kubectl create -f oms-daemonset.yaml

部署后,您应该能够运行以下命令来确认一切是否按预期工作:

$ kubectl get daemonset

您应该在集群中的每个节点上看到一个daemonset。由于我的集群有三个节点,结果看起来像下面这样:

部署后,大约 15 分钟后,您应该能够重新访问您的工作空间并开始记录统计信息。以下屏幕截图给出了您对记录的信息的一个概念。

第一个屏幕显示了有关在您的 Kubernetes 集群中运行的容器数量的一些基本信息,以及 Kubernetes 记录的任何错误和事件:

向右滚动将向您显示有关集群的更多详细信息:

正如您所看到的,我们有信息在我集群中运行的两个命名空间中的 pod,然后我们有集群中的节点。在此之后,我们有所有已下载的映像,以及所有正在运行的容器的详细信息。

再次向右滚动将显示更多信息:

在这里,我们可以看到我们所有容器中的进程数量,我们选择的时间范围内的 CPU 和内存性能,最后,我们可以运行在我们收集的数据上的一些示例查询。单击链接将执行示例查询,然后,您将有选项将结果保存为 Microsoft Excel 文件或将数据导出到 Microsoft 的 Power BI 服务。

Power BI 是由 Microsoft 提供的业务分析服务。它允许您创建仪表板并对数据集进行一些相当复杂的计算,其中之一是将度量数据从 Kubernetes 集群导出到 Microsoft Azure 工作区。

正如您所看到的,我们已经从几乎没有信息到被我们集群的统计数据和日志所淹没。有关 Microsoft 的容器监视解决方案的更多信息,请参阅其产品页面docs.microsoft.com/en-us/azure/log-analytics/log-analytics-containers/

摘要

在本章中,我们讨论了我们的 Kubernetes 集群如何受到保护,以及如何保护我们在前几章中看到的每个无服务器工具的默认配置。我们已经看过三种方法,可以使用 Kubernetes 仪表板从我们的 Kubernetes 集群获取实时统计信息,并且还查看了 Google Cloud 和 Microsoft Azure 提供的监控工具,用于存储和查询来自您的集群的指标。

在下一章中,也是最后一章,我们将看看如何基于我们在前几章中学到的知识最好地在 Kubernetes 上运行无服务器工作负载。

第十一章:运行无服务器工作负载

在我们的最后一章中,我们将讨论一些不同的情景,您会希望在其中托管自己的无服务器工作负载,并在选择工具时要考虑什么。我们将从讨论使用一种仍处于萌芽阶段并且仍在积极开发中的技术的优缺点开始。

软件和平台的发展

我们在本书中看到的几乎所有技术目前都在开发中。正如我们已经讨论过的,一些项目正处于开发的早期阶段,而另一些则更加成熟。让我们先讨论 Kubernetes 本身。

Kubernetes

Kubernetes 目前正在积极开发,尽管它已经相当成熟。我在 2017 年 9 月初开始写这本书,现在,当我写下最后一章时,已经是 2017 年 12 月底了。

在这段时间内,一共发布了 48 个版本,如下截图所示:

这些更新涵盖了从维护版本 v1.5、v1.6 和 v1.7 到实际版本 v.1.8 和 v1.9,以及随后的维护版本,一直到 v1.10 的第一个 alpha 版本。在如此活跃的发布周期中,保持对发布的掌握有多容易呢?

嗯,考虑到发布的频率,情况并不像你想象的那么糟糕,尽管可能会变得复杂。正如您从表中看到的,每个 Kubernetes 发布都有一个主要版本、一个次要版本和一个补丁版本。例如,我写作时的当前版本是:

  • v1.6.13(较旧版本)

  • v1.7.11(较旧版本)

  • v1.8.6(当前版本)

  • v1.9.0(开发版本)

截至 2017 年 12 月 12 日,同一主要版本有四个次要版本正在积极开发和修补。Kubernetes 本身支持同时三个次要版本;即当前版本(v1.8)和两个较旧的版本(v1.6 和 v1.7)。这意味着:

  • 预计运行当前版本的主节点将与运行两个先前版本的节点一起工作。也就是说,您可以在集群中拥有一个 v1.8 主节点和混合的 v1.7 和 v1.6 节点。

  • 运行当前版本的主节点预计可以与一个版本落后和一个版本领先于当前版本的客户端(如 kubectl)一起工作;这意味着我们可以将我们的 v1.8 主节点与 v1.9 和 v1.10 客户端进行交互。

  • 建议无论您运行哪个版本,都始终运行最新的补丁版本,因为补丁通常包含关键的错误和安全修复程序。

这种支持模型意味着在 v1.6.13 版本中可能存在的功能在 v1.9.0 版本中可能不可用。每两个月左右发布一个新的次要版本,您有大约四个月的时间来计划更新,然后两个月的时间来执行它们——这可能意味着审查并可能更新已部署在您的集群中的现有应用程序,以确保它们没有使用最近版本中正在逐步淘汰的任何功能。

阅读发布说明变得非常宝贵,因为新的次要版本总是有一个升级前部分,确认自上一个版本发布以来可能存在的潜在破坏集群的变化。例如,当前的开发版本是 v1.9.0。我知道它将在大约两个月内成为当前版本,所以为了做好准备,我需要处理我的集群,并确保考虑到github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.9.md#before-upgrading中详细列出的所有变化。

功能只会在次要版本中添加、弃用和移除。补丁版本只是对现有功能的修补。我还建议阅读Kubernetes Depreciation Policy,其中解释了删除/禁用功能的规则。该政策可以在kubernetes.io/docs/reference/deprecation-policy/找到。

您可以通过运行以下命令列出可以使用 Minikube 部署的 Kubernetes 版本:

$ minikube get-k8s-versions

Google Cloud 支持的 Kubernetes 版本发布可以在cloud.google.com/kubernetes-engine/supported-versions找到。Microsoft Azure 支持所有当前版本;可以在 AKS 介绍博客文章中找到此支持的示例,网址为azure.microsoft.com/en-us/blog/introducing-azure-container-service-aks-managed-kubernetes-and-azure-container-registry-geo-replication/,示例显示了从 v1.7.7 实时升级到 v1.8.1。

无服务器工具

那么,Kubernetes 的持续发展周期如何影响我们一直在关注的无服务器工具的开发,以及这如何影响它们自己的开发周期?

首先,让我们看看工具的类型。在上一章中,当我们研究安全性时,我们发现基本上有两种类型的工具。第一种是在 Kubernetes 内添加和扩展功能的工具,例如 Kubeless 和 Funktion。第二种类型的工具通过基本上位于 Kubernetes 之上并进行 API 调用来消耗 Kubernetes 服务,例如 Apache OpenWhisk,Fission 和 OpenFaaS。

与 Kubernetes 密切耦合的工具将不仅需要根据 Kubernetes 规划其发布,还需要密切关注 Kubernetes 的发展路径,因为 Kubernetes 特别兴趣小组的决定将直接影响它们自己的路线图。

例如,2017 年 9 月,Kubeless 发布了一个更新,从使用ThirdPartyResourcesTPR)更改为CustomResourceDefinitionsCRD),因为 TPR 在 Kubernetes v.1.7 中已被弃用,并在 v1.8 中删除。

这意味着您选择的工具将需要一些研究。您应该问自己的问题是:

  • 我正在评估的工具是否与我将在集群中部署的 Kubernetes 版本兼容?如果有疑问,您可以通过在 Minikube 上进行一些测试安装来进行检查。

  • 是否有任何可能影响我的部署的未解决问题?在承诺使用该工具之前,我建议查看工具 GitHub 项目页面上的任何未解决问题;其中有没有问题听起来很熟悉,并且可能适用于您的安装?

  • 我正在考虑部署的工具是否在积极开发中,新版本发布频率如何?是否有社区支持该工具?查看 GitHub 上的发布页面;发布频率如何,是否有任何破坏性的发布?

  • 该工具有多安全?根据前一章,默认配置有多安全,使其安全会如何影响您使用该工具?

以下是一些有用的链接,应该可以帮助您回答之前的问题。

Kubeless

Kubeless 的有用链接如下:

Apache OpenWhisk

OpenWhisk 的有用链接如下:

Fission

Fission 的有用链接如下:

OpenFaaS

OpenFaaS 的有用链接如下:

Funktion

Funktion 的有用链接如下:

自从这本书开始编写以来,Funktion 已经被沙箱化。源代码仍然可供任何人使用,或者分叉他们自己的版本以继续开发。作者建议两种替代方案:Kubeless 或 Apache OpenWhisk。

未来发展

在技术领域,三个月是很长的时间。自我开始写这本书以来,Kubernetes 生态系统发生了一些变化;最显著的两个目前处于私人测试阶段,预计将在 2018 年初向公众开放使用。

第一个是使用 Minikube 在本地运行 Kubernetes 的替代方案,这源自一个意想不到的来源:Docker。在 2017 年的 DockerCon Europe 上宣布,Docker 将在 Docker for macOS 和 Docker for Windows 的社区版和企业版中支持 Kubernetes 和 Docker swarm。

您可以在www.docker.com/kubernetes找到有关即将发布的更多信息,或观看 Elton Stoneman 为该服务的介绍视频www.youtube.com/watch?v=jWupQjdjLN0

第二项服务毫不意外地是引入了Amazon 弹性容器服务 for Kubernetes服务,简称Amazon EKS。亚马逊在他们每年的 re:Invent 大会上宣布了这一点,正如您所期望的那样,它与其他 AWS 服务(如 Amazon VPC、IAM、弹性负载均衡和 AWS CloudTrail 等)有深度集成。您可以在aws.amazon.com/eks/找到有关该服务的更多信息,目前该服务处于私有测试阶段,也可以观看宣布视频www.youtube.com/watch?v=gUFtUU7qvSQ

为什么在 Kubernetes 上使用函数作为服务

在前几章中,我们谈到了无服务器函数和 Kubernetes 以及使用它们的优势:

  • Kubernetes:使用 Kubernetes 部署应用程序的最大用例是,它允许您开发一次,然后以一致的方式在多个平台上部署,无论是自托管的裸机服务器,还是在 VMWare、OpenStack、KVM、Hyper-V 等上运行虚拟机的私有云。对于谷歌云、微软 Azure 和现在的 AWS 等公共云提供商也是一样,它们都提供自己的本地托管 Kubernetes 服务,包括 Minikube 或即将发布的 Docker for macOS 或 Docker for Windows 版本。

  • 无服务器: 将应用程序的全部或部分部署为无服务器函数可以帮助它轻松扩展。突然间,你不需要担心你的虚拟机或容器是否有足够的资源来处理大量的传入连接,或者这些连接如何路由到你的应用程序。每个请求将被发送到一个个体或一组容器中,在那里你的请求将被处理——一旦完成,该容器将被终止或回收用于下一个请求。

  • Kubernetes 加无服务器: 正如前面提到的,应用程序的无服务器部分可以轻松扩展——这可以与 Kubernetes 结合,其中可以快速地手动或通过脚本添加额外的节点到你的集群中。一旦额外的资源成为集群的一部分,你的无服务器函数将自动安排在新资源上,而无需对应用程序路由或代码进行进一步的更改。

再加上你几乎可以在所有主要的公共云提供商中部署你的应用程序的知识,你将获得一致的体验,而不必调整你的代码以适应提供商自己的函数即服务提供,比如我们在第一章中讨论的那些。无服务器景观

你选择无服务器工具很可能取决于两个因素,第一个是你的应用程序是用什么语言编写的,例如,你的应用程序是用 Python、Node.js、PHP、Java、.NET 还是 Go 编写的?

第二个因素将是个人偏好。在阅读本书的章节时,你可能已经对哪种工具最适合你以及哪种工具适合你的开发工作流程和自己的工作方式形成了看法。安全等问题总是一个影响因素,但正如在上一章中讨论的那样,有办法克服这些问题。

固定点

到目前为止,我们一直在讨论许多可能是小的移动部分。那么大的固定点,比如数据库和文件存储,该如何与 Kubernetes 上的 FaaS 服务结合呢?

数据库

关于是否应该在容器中运行数据库服务仍在进行讨论——这基本上自从 Docker 开始获得关注以来就一直存在,不幸的是,没有一个简单的是或不是的答案。

每当我开始一个项目,我倾向于查看数据库的使用情况以及对应用程序整体性能的影响,然后从那里开始工作。

Kubernetes 允许您运行 PetSet;回想一下本书开头的宠物与牛的比喻。在 Kubernetes v1.5 中,随着该功能退出 alpha 版,它被称为 StatefulSet。该功能在 Kubernetes v1.9 中退出 beta 版。

请参见以下 GitHub 问题,讨论从 PetSet 更名为 StatefulSet 的更改github.com/kubernetes/kubernetes/issues/27430

StatefulSet 允许您运行传统上在 Kubernetes 等集群服务中运行起来相当困难的东西。通过使用 pod 和持久存储的组合,它基本上在 Kubernetes 集群中创建了一个固定点,其中:

  • 具有稳定的唯一网络标识符,如果 StatefulSet 需要在主机之间移动或者由于错误需要重新启动 pod,它将持续存在

  • 具有稳定的专用于 StatefulSet 的持久存储,用于存储数据库、配置等

  • 具有有序和优雅的部署和扩展、删除和终止,以及自动滚动更新,所有这些意味着您可以控制需要在启动、移动或关闭时进行控制的软件。

所有这些意味着在 Kubernetes 集群中托管数据库是完全可能的。这样做意味着您将能够在相同的命名空间内连接到您的数据库,但这种解决方案可能并非适用于所有情况。

例如,如果您有一个大型数据集,或者您的数据库需要被 Kubernetes 集群外的其他应用程序访问,那么您最好使用公共云提供商提供的本地数据库服务。这些服务包括:

尽管使用这些服务会使您暴露于一定程度的供应商锁定,因为您的大部分数据将位于 Kubernetes 集群之外,但这三个服务都提供开源数据库引擎,从应用程序的角度来看,这意味着它们仍然在使用相同的数据库服务,无论是在您的集群内托管还是作为您的公共云提供商服务之一。

有关 StatefulSets 的更多信息,我建议阅读 Kubernetes 网站上的以下两个示例:

请记住,直到 Kubernetes v1.9 版本之前,此功能都处于测试阶段,因此如果您的集群运行的是旧版本,可能需要查看文档。

存储

大多数现代应用程序应该不再存储在本地驱动器上生成的文件,而是应该使用对象存储。通常,对象提供了一个 API,允许应用程序将文件写入服务,并查询服务以查找文件的元数据,包括检索文件可以通过 HTTP 访问的 URL。

三大公共云提供商都提供对象存储:

Amazon S3 是它们中的鼻祖;很可能在过去 48 小时内,您已经访问过直接从 Amazon S3 提供的文件,或者间接地使用内容传送网络,其中 Amazon S3 是文件的来源。

如果您希望将应用程序保留在 Kubernetes 中,包括对象存储,不用担心,可以运行自己的对象存储;事实上,您可以运行一个与 Amazon S3 具有高度兼容性的对象存储,这意味着您的应用程序应该可以继续工作,几乎不需要修改。

Minio 是一个多云对象存储,可以部署到 Kubernetes 以及其他云和服务提供商;甚至可以使用 Minikube 在本地运行它。

有关 Kubernetes 上的 Minio 的更多信息,请参阅以下链接:www.minio.io/kubernetes.html

总结

所以,我们到了书的结尾。我们已经解释了什么是无服务器,并解决了在服务器上运行无服务器函数的混乱。

我们已经了解了 Kubernetes 是如何起步以及一些核心概念,以及如何使用 Kubernetes 提供的工具在本地和公共云中部署集群,还有云服务提供商的原生解决方案。

使用这些集群,我们通过了几个工具,它们都提供了函数即服务功能,要么通过扩展 Kubernetes 的新功能,要么通过利用 Kubernetes 的平台即服务功能并在其上安装自己。

然后我们讨论了这些部署可能存在的安全问题以及如何监视它们,然后讨论了我们如何努力保持领先的不断发展的技术,并且在开始在 Kubernetes 上部署无服务器函数时需要考虑的事项。

posted @ 2024-05-20 12:03  绝不原创的飞龙  阅读(4)  评论(0编辑  收藏  举报