+
+OpenFaaS 需要一个[Kubernetes](https://kubernetes.io)集群来运行。你可以使用一个单节点集群或多节点集群,不管是在你的笔记本电脑上还是在云端。
+
+任何 OpenFaaS 函数的基本原件都是一个 Docker 镜像,它是使用`faas-cli`工具链构建的。
+
+## 前提条件
+
+让我们来安装 Docker、OpenFaaS CLI 以及设置 Kubernetes。
+
+### Docker
+
+适用于 Mac
+
+- [Docker CE for Mac Edge Edition](https://store.docker.com/editions/community/docker-ce-desktop-mac)
+
+适用于 Windows
+
+- 仅使用 Windows 10 Pro 或企业版
+- 安装[Docker CE for Windows](https://store.docker.com/editions/community/docker-ce-desktop-windows)
+
+> 请确保通过使用 Windows 任务栏通知区的 Docker 菜单来使用**Linux**容器的 Docker 守护程序。
+
+- 安装[Git Bash](https://git-scm.com/downloads)
+
+当你安装 git bash 时,选择以下选项:`install UNIX commands`和`use true-type font`。
+
+> 注意:请在所有步骤中使用*Git Bash*:不要试图使用*PowerShell*、*WSL*或*Bash for Windows*。
+
+Linux - Ubuntu 或 Debian
+
+- Docker CE for Linux
+
+> 你可以从[Docker Store](
+
+在开始本实验室之前,为你的文件创建一个新的文件夹。由于本实验室是建立在先前的实验室基础上的,因此请复制 lab5。
+
+```plain
+$ cp -r lab5 lab10\
+ && cd lab10
+```
+
+## 使用secret
+
+[实验室 5](./lab5.md)研究了`issue-bot`如何从环境变量(`auth_token`)获得 GitHub 的个人访问令牌。 另一种方法是使用**机密**来存储敏感信息。
+
+来自 Docker 文档。
+> ... secret是一团数据,如密码、SSH 私钥、SSL 证书或其他数据,不应通过网络传输或未经加密存储在 Docker 文件或应用程序的源代码中。
+
+这是一个比环境变量更安全的选择。环境变量更容易使用,但最适合于非保密的配置项目。 似乎很适合用于存储`auth_token`值。
+
+请参阅[docs](https://docs.openfaas.com/reference/secrets/)中关于secret的更多信息和它的设计。
+
+### 创建一个secret
+
+> secret名称必须遵循 DNS-1123 惯例,由小写字母数字字符或'-'组成,并且必须以一个字母数字字符开始和结束
+
+从一个终端运行以下命令。
+
+```plain
+$ echo -n
+
+## 前言
+
+用于微服务的传统认证策略与函数的工作原理完全相同。在这个实验室中,我们将讨论使用共享secret和基于哈希的消息验证码(HMAC)的几种可用方法之一。有关其他认证策略和想法,请参见。[openfaas-function-auth](https://github.com/openfaas-incubator/openfaas-function-auth/blob/master/README.md)
+
+这绝不是一个广泛的清单,安全和认证是一个复杂的领域,最好留给专家使用经过试验的方法。
+
+## 准备好你的环境
+
+在开始这个实验之前,创建一个新的文件夹
+
+```bash
+mkdir -p lab11\`bash
+ && cd lab11
+```
+
+也要确保你的`faas-cli'版本是`0.7.4'或以上,使用以下命令。
+
+```plain
+$ faas-cli version
+```
+
+## 什么是 HMAC
+
+如果没有任何形式的认证或信任,我们的函数可能会暴露给任何能猜到其 URL 的人。如果我们的函数可以在互联网或本地网络上访问,那么它们就可能被坏的行为者调用。默认情况下,函数会对任何请求做出响应。然而,如果我们想控制对函数的访问,我们可以使用基于哈希的消息验证码(HMAC)来验证信息的来源。
+
+来自[alexellis/hmac](https://github.com/alexellis/hmac)。
+> HMAC 使用发送方/接收方提前共享的对称密钥。发送方在想要传输信息时将产生一个哈希值--该数据与有效载荷一起发送。然后,收件人将用共享密钥签署有效载荷,如果哈希值匹配,则假定有效载荷来自发件人。
+
+这样我们就可以避免我们的函数被无效的甚至是危险的信息所调用。
+
+## 使用 HMAC
+
+我们将使用 faas-cli 提供的`--sign`标志来包含一个头,其中包含使用我们用`--key`标志提供的共享密钥创建的散列信息。
+
+> 注意: `--sign`和`--key`必须同时存在。
+
+让我们首先通过部署`-env`函数来检查该标志的作用,该函数将打印函数中可访问的所有环境变量。
+
+```bash
+$ faas-cli deploy --name env --fprocess="env" --image="function/alpine:new"
+```
+
+* 调用不带`--sign`标志的函数。
+
+```plain
+$ echo "The message" | faas-cli invoke env
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/bin
+HOSTNAME=d2c1a2cb20c2
+fprocess=env
+HOME=/root
+Http_X_Call_Id=b84947c6-2970-4fcf-ba3b-66dde6943999
+Http_X_Forwarded_For=10.255.0.2:34974
+Http_X_Forwarded_Host=127.0.0.1:8080
+Http_Content_Length=0
+Http_Accept_Encoding=gzip
+Http_Content_Type=text/plain
+Http_User_Agent=Go-http-client/1.1
+Http_X_Start_Time=1538725657952768349
+...
+```
+
+* 再次调用该函数,但这次有`--sign`标志。
+
+```plain
+$ echo -n "The message" | faas-cli invoke env --sign=HMAC --key=cookie
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+HOSTNAME=d2c1a2cb20c2
+fprocess=env
+HOME=/root
+Http_User_Agent=Go-http-client/1.1
+Http_Content_Length=0
+Http_Accept_Encoding=gzip
+...
+Http_Hmac=sha1=9239edfe20185eafd7a5513c303b03d207d22f64
+...
+```
+
+我们看到`HMAC`被作为环境变量`Http_Hmac`提供。生成的值是用钥匙`cookie`签名后的`消息`的哈希值,然后用散列方法`sha1`进行预处理。
+
+## HMAC 在行动
+
+为了我们的目的,我们将创建一个新的 Python 3 函数。让我们把它叫做`hmac-protected`。
+
+```bash
+$ faas-cli new --lang python3 hmac-protected --prefix="
+
+## 安装最新的 `kubectl`
+
+使用下面的说明或[官方文档](https://kubernetes.io/docs/tasks/tools/install-kubectl/)为你的操作系统安装`kubectl`。
+
+- Linux
+
+```sh
+export VER=$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)
+curl -LO https://storage.googleapis.com/kubernetes-release/release/$VER/bin/linux/amd64/kubectl
+chmod +x kubectl
+mv kubectl /usr/local/bin/
+```
+
+- MacOS
+
+```sh
+export VER=$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)
+curl -LO https://storage.googleapis.com/kubernetes-release/release/$VER/bin/darwin/amd64/kubectl
+chmod +x kubectl
+mv kubectl /usr/local/bin/
+```
+
+- Windows
+
+```sh
+export VER=$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)
+curl -LO https://storage.googleapis.com/kubernetes-release/release/$VER/bin/windows/amd64/kubectl.exe
+chmod +x kubectl.exe
+mkdir -p $HOME/bin/
+mv kubectl $HOME/bin/
+```
+
+## 设置一个 Kubernetes 集群
+
+你可以在使用 Kubernetes 的同时按照实验进行操作,但你可能需要沿途做一些小改动。网关的服务地址从`http://gateway:8080`改为`http://gateway.openfaas:8080`。这些差异已经尽可能地被记录下来,每个实验室都提供了替代方案。
+
+### 在你的笔记本电脑上创建一个本地集群
+
+#### _k3s 使用 k3d_
+
+如果你的电脑上有 Docker,那么你可以使用 Rancher 实验室托管的`k3d`工具。它安装了一个轻量级的 Kubernetes 版本,叫做`k3s`,并在 Docker 容器中运行,这意味着它可以在任何有 Docker 的电脑上运行。
+
+- [安装 k3d](https://github.com/rancher/k3d)
+
+- 启动一个集群
+
+1. `k3d cluster create CLUSTER_NAME`创建一个新的单节点集群(=1 个运行 k3s 的容器+1 个负载均衡器容器)
+2. 2.kubectl 的上下文会自动更新,你可以用`kubectl config get-contexts`来检查。
+3. 执行一些命令,如`kubectl get pods --all-namespaces`。
+ 如果你想删除默认集群`k3d cluster delete CLUSTER_NAME`。
+
+#### _Docker for Mac_
+
+- [安装 Docker for Mac](https://docs.docker.com/v17.12/docker-for-mac/install/)
+
+> 请注意,Kubernetes 仅在 Docker for Mac 17.12 CE 及以上版本中可用。
+
+#### _使用 Minikube_
+
+- 要安装 Minikube,请根据你的平台从[最新版本](https://github.com/kubernetes/minikube/releases)下载适当的安装程序。
+
+- 现在运行 Minikube
+
+```sh
+minikube start
+```
+
+minikube 虚拟机通过一个仅限主机的 IP 地址暴露给主机系统。用`minikube ip`检查这个 IP。
+这是你以后将用于网关 URL 的 IP。
+
+> 注意:Minikube 还需要一个 Hypervisor,如 VirtualBox 或 Hyperkit(在 MacOS 上)。按照 minikube 的说明和文件
+
+### 在云上创建一个远程集群
+
+你可以在云端创建一个远程集群,享受与本地开发一样的体验,同时节省 RAM/CPU 和电池。运行一个集群 1-2 天的费用是最低的。
+
+#### _在 DigitalOcean 的 Kubernetes 服务上运行_
+
+你可以使用免费点数通过 DigitalOcean 的用户界面创建一个集群。
+
+然后 DigitalOcean 的仪表板将指导你如何配置你的`kubectl`和`KUBECONFIG`文件,以便在实验室中使用。
+
+- [申请你的免费点数--30 天内有 50 美元的点数。](https://m.do.co/c/8d4e75e9886f)
+
+即使你已经申请了免费学分,一个 2-3 个节点的集群 24-48 小时的运行费用也是可以忽略不计的。
+
+*点击仪表板左侧面板上的 Kubernetes*,然后点击 `启用有限访问`
+
+*一旦登录,点击 Kubernetes*菜单项并创建一个集群。
+
+建议使用最新的 Kubernetes 版本,并选择离你最近的数据中心区域,以尽量减少延时。
+
+- 在 `添加节点池`下
+
+使用 2 个 4GB / 2vCPU
+
+> 注意:如果需要,你可以在以后添加更多的容量
+
+- 下载[doctl](https://github.com/digitalocean/doctl#installing-doctl)CLI 并把它放在你的路径中。
+
+- 在您的 DigitalOcean 仪表板上创建一个[API 密钥](https://cloud.digitalocean.com/account/api/tokens/new)
+
+追踪您的 API 密钥(将其复制到剪贴板)。
+
+- 认证 CLI
+
+```sh
+doctl auth init
+```
+
+粘贴你的 API 密钥
+
+- 现在获得集群的名称。
+
+```sh
+$ doctl k8s cluster list
+GUID workshop-lon1 nyc1 1.13.5-do.1 provisioning workshop-lon1-1
+```
+
+- 保存一个配置文件,使`kubectl`指向新集群。
+
+```sh
+doctl k8s cluster kubeconfig save workshop-lon1
+```
+
+现在你需要切换你的 Kubernetes 上下文以指向新的集群。
+
+用`kubectl config get-contexts`找到集群名称,如果它没有突出显示,则输入`kubectl config set-context
+
+在开始这个实验之前,创建一个新的文件夹。
+
+```sh
+$ mkdir -p lab2 \
+ && cd lab2
+```
+
+## 使用 UI 门户
+
+现在你可以测试一下 OpenFaaS 的用户界面了。
+
+如果你已经设置了一个`$OPENFAAS_URL`,那么就可以得到这个 URL,然后点击它。
+
+```sh
+echo $OPENFAAS_URL
+http://127.0.0.1:31112
+```
+
+如果你没有设置"$OPENFAAS_URL",那么默认情况下是这样的。[http://127.0.0.1:8080](http://127.0.0.1:8080).
+
+我们可以部署一些样本函数,然后用它们来测试一下。
+
+```sh
+faas-cli deploy -f https://raw.githubusercontent.com/openfaas/faas/master/stack.yml
+```
+
+
+
+你可以在用户界面中试用它们,比如将 Markdown 代码转换为 HTML 的 Markdown 函数。
+
+在*Request*字段中键入以下内容。
+
+```sh
+## The **OpenFaaS** _workshop_
+```
+
+现在点击*Invoke*,看到响应出现在屏幕的下半部分。
+
+即。
+
+```sh
+
+
+在开始这个实验之前,为你的文件创建一个新的文件夹。
+
+```sh
+$ mkdir -p lab3\`s
+ && cd lab3
+```
+
+## 创建一个新的函数
+
+有两种方法来创建一个新的函数。
+
+* 使用一个内置的或社区的代码模板建立一个函数(默认情况下)
+* 使用一个现有的二进制文件并将其作为你的函数(高级)
+
+### 构建或生成一个新函数
+
+在用模板创建新函数之前,请确保你从 GitHub 上提取了[模板](https://github.com/openfaas/templates)。
+
+```sh
+$ faas-cli template pull
+
+Fetch templates from repository: https://github.com/openfaas/templates.git
+ Attempting to expand templates from https://github.com/openfaas/templates.git
+2021/08/25 15:58:10 Fetched 13 template(s) : [csharp dockerfile go java11 java11-vert-x node node12 node14 php7 python python3 python3-debian ruby] from https://github.com/openfaas/templates.git
+```
+
+之后,要想知道哪些语言是可用的,请键入。
+
+```sh
+$ faas-cli new --list
+Languages available as templates:
+- csharp
+- dockerfile
+- go
+- java11
+- java11-vert-x
+- node
+- node12
+- node14
+- php7
+- python
+- python3
+- python3-debian
+- ruby
+```
+
+或者创建一个包含`Dockerfile`的文件夹,然后在 YAML 文件中选择 `Dockerfile`语言类型。
+
+在这一点上,你可以为 Python、Python 3、Ruby、Go、Node、CSharp 等创建一个新函数。
+
+* 关于我们的例子的说明
+
+我们这次研讨会的所有例子都经过了 OpenFaaS 社区对*Python 3*的全面测试,但也应该与*Python 2.7*兼容。
+
+如果你喜欢使用 Python 2.7 而不是 Python 3,那么把`faas-cli new --lang python3`换成`faas-cli new --lang python`。
+
+### Python 中的 Hello world
+
+我们将在 Python 中创建一个 hello-world 函数,然后转到也使用额外依赖的东西上。
+
+* 构建函数的脚手架
+
+```sh
+faas-cli new --lang python3 hello-openfaas --prefix="
+
+在开始本实验之前,为你的文件创建一个新的文件夹。由于本实验是建立在早期实验的基础上的,所以请复制 lab3。
+
+```plain
+$ cp -r lab3 lab4\
+ && cd lab4
+```
+
+## 通过环境变量注入配置
+
+能够控制一个函数在运行时的行为方式是很有用的,我们至少可以通过两种方式来实现这一点。
+
+### 在部署时
+
+* 在部署时设置环境变量
+
+我们在[Lab 3](./lab3.md)中用`write_debug`做了这个 - 你也可以在这里设置任何你想要的自定义环境变量 - 例如,如果你想为你的*hello world*函数配置一种语言,你可以引入一个`spoken_language`变量。
+
+### 使用 HTTP 上下文--querystring / headers
+
+* 使用 querystring 和 HTTP headers
+
+另一个更动态的、可以在每个请求层面上改变的选项是使用查询字符串和 HTTP 头信息,两者都可以通过`faas-cli`或`curl`传递。
+
+这些头信息通过环境变量暴露出来,所以它们很容易在你的函数中被使用。所以任何头信息都以`Http_`为前缀,所有`-`连字符都被替换成`_`下划线。
+
+让我们用 querystring 和一个列出所有环境变量的函数来试试。
+
+* 使用 BusyBox 的内置命令,部署一个打印环境变量的函数。
+
+```plain
+faas-cli deploy --name env --fprocess="env" --image="function/alpine:latest"
+```
+
+* 用一个查询字符串调用该函数。
+
+```sh
+$ echo "" | faas-cli invoke env --query workshop=1
+PATH=/usr/local/bin:/usr/local/bin:/usr/bin:/sbin:/bin
+HOSTNAME=05e8db360c5a
+fprocess=env
+HOME=/root
+Http_Connection=close
+Http_Content_Type=text/plain
+Http_X_Call_Id=cdbed396-a20a-43fe-9123-1d5a122c976d
+Http_X_Forwarded_For=10.255.0.2
+Http_X_Start_Time=1519729562486546741
+Http_User_Agent=Go-http-client/1.1
+Http_Accept_Encoding=gzip
+Http_Method=POST
+Http_ContentLength=-1
+Http_Path=/
+...
+Http_Query=workshop=1
+...
+```
+
+在 Python 代码中,你会输入`os.getenv("Http_Query")`。
+
+* 将路径附加到你的函数 URL 上
+
+用以下方法调用 env 函数。
+
+```sh
+$ curl -X GET $OPENFAAS_URL/function/env/some/path -d ""
+PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/bin
+HOSTNAME=fae2ac4b75f9
+fprocess=env
+HOME=/root
+Http_X_Forwarded_Host=127.0.0.1:8080
+Http_X_Start_Time=1539370471902481800
+Http_Accept_Encoding=gzip
+Http_User_Agent=curl/7.54.0
+Http_Accept=*/*
+Http_X_Forwarded_For=10.255.0.2:60460
+Http_X_Call_Id=bb86b4fb-641b-463d-ae45-af68c1aa0d42
+Http_Method=GET
+Http_ContentLength=0
+...
+Http_Path=/some/path
+...
+```
+
+正如你所看到的,`Http_Path`头包含你的路径。
+如果你想在你的代码中使用它,只要用`os.getenv("Http_Path")`来获取它。
+
+* 现在用标头来调用它。
+
+```sh
+$ curl $OPENFAAS_URL/function/env --header "X-Output-Mode: json" -d ""
+PATH=/usr/local/bin:/usr/local/bin:/usr/bin:/sbin:/bin
+HOSTNAME=05e8db360c5a
+fprocess=env
+HOME=/root
+Http_X_Call_Id=8e597bcf-614f-4ca5-8f2e-f345d660db5e
+Http_X_Forwarded_For=10.255.0.2
+Http_X_Start_Time=1519729577415481886
+Http_Accept=*/*
+Http_Accept_Encoding=gzip
+Http_Connection=close
+Http_User_Agent=curl/7.55.1
+Http_Method=GET
+Http_ContentLength=0
+Http_Path=/
+...
+Http_X_Output_Mode=json
+...
+```
+
+在 Python 代码中,你会输入`os.getenv("Http_X_Output_Mode")'。
+
+你可以看到所有其他的 HTTP 上下文也被提供了,比如当 `Http_Method`是 `POST`时的 `Content-Length`,`User_Agent`,Cookies 和其他你期望从 HTTP 请求中看到的东西。
+
+## 安全:只读文件系统
+
+OpenFaaS 可以使用的容器安全特性之一是使我们执行环境的根文件系统只读的能力。如果一个函数被破坏,这可以减少攻击面。
+
+生成一个函数,将文件保存到函数的文件系统中。
+
+```sh
+faas-cli new --lang python3 ingest-file --prefix=your-name
+```
+
+更新处理程序。
+
+```python
+import os
+import time
+
+def handle(req):
+ # Read the path or a default from environment variable
+ path = os.getenv("save_path", "/home/app/")
+
+ # generate a name using the current timestamp
+ t = time.time()
+ file_name = path + str(t)
+
+ # write a file
+ with open(file_name, "w") as f:
+ f.write(req)
+ f.close()
+
+ return file_name
+```
+
+建立这个例子。
+
+```sh
+faas-cli up -f ingest-file.yml
+```
+
+调用该例子。
+
+```sh
+echo "Hello function" > message.txt
+
+cat message.txt | faas-cli invoke -f ingest-file.yml ingest-file
+```
+
+该文件将被写入`/home/app`路径中。
+
+现在编辑 ingest-file.yml 并使该函数为只读。
+
+```yaml
+...
+functions:
+ ingest-file:
+ lang: python3
+ handler: ./ingest-file
+ image: alexellis2/ingest-file:latest
+ readonly_root_filesystem: true
+```
+
+> 也请参见。[YAML 参考](https://docs.openfaas.com/reference/yaml/#function-read-only-root-filesystem)
+
+再次部署。
+
+```sh
+faas-cli up -f ingest-file.yml
+```
+
+现在这将会失败。
+
+```sh
+echo "Hello function" > message.txt
+
+cat message.txt | faas-cli invoke -f ingest-file.yml ingest-file
+```
+
+请看错误。
+
+```sh
+Server returned unexpected status code: 500 - exit status 1
+Traceback (most recent call last):
+ File "index.py", line 19, in Hostname: 64767782518c
+ +Platform: linux +Arch: x64 +CPU count: 4 +Uptime: 1121466
+``` + +现在你会看到 NodeInfo 函数的输出被装饰成 HTML 标签,例如。``.
+
+另一个客户端函数链的例子可能是调用一个生成图像的函数,然后将该图像发送到另一个添加水印的函数中。
+
+### 从另一个函数中调用一个函数
+
+从另一个函数中调用一个函数的最简单方法是通过 OpenFaaS *API 网关*通过 HTTP 进行调用。这个调用不需要知道外部域名或 IP 地址,它可以简单地通过 DNS 条目将 API 网关称为`gateway`。
+
+当从一个函数访问 API 网关等服务时,最好的做法是使用环境变量来配置主机名,这很重要,有两个原因--名称可能会改变,在 Kubernetes 中有时需要一个后缀。
+
+优点。
+
+* 函数之间可以直接利用对方
+* 低延迟,因为函数可以在同一网络上相互访问
+
+缺点。
+
+* 需要一个代码库来进行 HTTP 请求
+
+例子。
+
+在[实验室 3](./lab3.md)中,我们介绍了 request 模块,并使用它来调用一个远程 API,以获得国际空间站上的一个宇航员的名字。我们可以使用同样的技术来调用部署在 OpenFaaS 上的另一个函数。
+
+* 使用用户界面,进入*函数商店*并部署*情感分析*函数。
+
+或者使用 CLI。
+
+```plain
+faas-cli store deploy SentimentAnalysis
+```
+
+情感分析函数将告诉你任何句子的主观性和极性(积极性评级)。该函数的结果是以 JSON 格式显示的,如下面的例子。
+
+```sh
+$ echo -n "California is great, it's always sunny there." | faas-cli invoke sentimentanalysis
+{"polarity": 0.8, "sentence_count": 1, "subjectivity": 0.75}
+```
+
+因此,结果显示我们的测试句子既非常主观(75%)又非常积极(80%)。这两个字段的值总是在`-1.00`和`1.00`之间。
+
+下面的代码可以用来调用*情绪分析*函数或任何其他函数。
+
+给网关主机加上`openfaas`命名空间的后缀。
+
+```python
+ r = requests.get("http://gateway.openfaas:8080/function/sentimentanalysis", text= test_sentence)
+```
+
+或者通过一个环境变量。
+
+```python
+ gateway_hostname = os.getenv("gateway_hostname", "gateway.openfaas") # uses a default of "gateway.openfaas" for when "gateway_hostname" is not set
+ test_sentence = "California is great, it's always sunny there."
+ r = requests.get("http://" + gateway_hostname + ":8080/function/sentimentanalysis", data= test_sentence)
+```
+
+由于结果总是以 JSON 格式出现,我们可以利用辅助函数`.json()`来转换响应。
+
+```python
+ result = r.json()
+ if result["polarity"] > 0.45:
+ return "That was probably positive"
+ else:
+ return "That was neutral or negative"
+```
+
+现在,在 Python 中创建一个新的函数,并将其全部整合起来
+
+```python
+import os
+import requests
+import sys
+
+def handle(req):
+ """handle a request to the function
+ Args:
+ req (str): request body
+ """
+
+ gateway_hostname = os.getenv("gateway_hostname", "gateway.openfaas") # uses a default of "gateway" for when "gateway_hostname" is not set
+
+ test_sentence = req
+
+ r = requests.get("http://" + gateway_hostname + ":8080/function/sentimentanalysis", data= test_sentence)
+
+ if r.status_code != 200:
+ sys.exit("Error with sentimentanalysis, expected: %d, got: %d\n" % (200, r.status_code))
+
+ result = r.json()
+ if result["polarity"] > 0.45:
+ return "That was probably positive"
+ else:
+ return "That was neutral or negative"
+```
+
+* 记得在你的`requirements.txt`文件中加入`requests`。
+
+注意:你不需要修改或改变 SentimentAnalysis 函数的源代码,我们已经部署了它并将通过 API 网关访问它。
+
+现在转到[实验室 5](lab5.md)。
diff --git a/translations/cn/lab5.md b/translations/cn/lab5.md
new file mode 100644
index 0000000..923a6b2
--- /dev/null
+++ b/translations/cn/lab5.md
@@ -0,0 +1,414 @@
+# Lab 5 - 创建一个 GitHub 机器人
+
+
+ Text:
+
+
+
+在开始这个实验之前,为你的文件创建一个新的文件夹。
+
+```sh
+$ mkdir -p lab5\
+ && cd lab5
+```
+
+我们将使用 OpenFaaS 的函数来创建一个名为 `issue-bot`的 GitHub 机器人。
+
+问题机器人的工作是通过分析 `描述` 字段的情绪来分流新的问题,然后它将应用一个*积极*或*审查*的标签。这将有助于维护者在繁忙的工作中,可以优先考虑哪些问题需要首先处理。
+
+问题机器人的图示](./diagram/issue-bot.png)
+
+## 获取一个 GitHub 账户
+
+* 注册一个[GitHub 账户](https://github.com),如果你还没有一个账户。
+
+* 创建一个新的仓库,并将其称为*bot-test*。
+
+注意:我们将只使用这个仓库作为创建问题的测试场所。你不需要在那里提交任何代码。
+
+## 建立一个带有入口的隧道
+
+你需要接收来自 GitHub 的 webhooks。幸运的是,inlets 让这一切变得非常快速和简单。它可以按月或按年订阅,所以如果你不确定是否全年都需要它,你可以只付一个月的钱。
+
+inlets 有一个叫做 inlets-operator 的 Kubernetes 集成。你可以用它来设置 LoadBalancers 或带有 TLS 的 Ingress。它的工作原理是为你创建一个云虚拟机,并在那里运行一个隧道服务器,然后为你运行一个隧道客户端作为一个 Pod,你就可以获得传入流量。
+
+在你喜欢的云提供商(如 DigitalOcean)的 API 页面下创建一个写入访问令牌,然后将内容保存到`digital-ocean-api-token.txt`。
+
+设置完订阅后,将你的密钥保存到`$HOME/.inlets/LICENSE`,然后运行以下程序。
+
+```bash
+arkade install inlets-operator \
+ --provider digitalocean \
+ --region lon1 \
+ --token-file $HOME/digital-ocean-api-token.txt
+```
+
+这将部署 inlets-operator,并指示它在 DigitalOcean 上为你的隧道服务器配置新的主机到伦敦地区。其他供应商和地区也可以使用,[更多信息请见文档](https://docs.inlets.dev/reference/inlets-operator/)。
+
+## 用网关的公共 IP 登录你的网关
+
+用信息检索你的网关密码,从。
+
+```bash
+arkade info openfaas
+```
+
+LoadBalancer 的公共 IP 大约需要 10-30 秒才能出现。
+
+```bash
+kubectl get svc -n openfaas gateway-external
+NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
+gateway-external LoadBalancer 10.96.29.46
+
+在开始这个实验之前,为你的文件创建一个新的文件夹。
+
+```plain
+$ mkdir -p lab6\
+ && cd lab6
+```
+
+## 从一个函数中生成并返回基本的 HTML
+
+函数可以返回 HTML,并将`Content-Type`设置为`text/html`。因此,函数返回的 HTML 可以通过浏览器进行渲染。让我们创建一个简单的函数,生成并返回一个基本的 HTML。
+
+```plain
+$ faas-cli new --lang python3 show-html --prefix="Hi, from your function!
'
+
+ return html
+```
+
+这将返回 HTML 给调用者。 还有一件事我们应该做的是设置响应的`Content-Type'。我们100%确定这个函数将返回一个HTML,所以`Content-Type`应该总是`text/html`。我们可以利用`show-html.yml`文件中的`environment`部分来设置。
+
+编辑`show-html.yml`。
+
+```yaml
+provider:
+ name: openfaas
+ gateway: http://127.0.0.1:8080
+
+functions:
+ show-html:
+ lang: python3
+ handler: ./show-html
+ image: Here's a new page!
+
+
+```
+
+现在把你的`handler.py`改为以下内容。
+
+```python
+import os
+
+def handle(req):
+ """handle a request to the function
+ Args:
+ req (str): request body
+ """
+
+ dirname = os.path.dirname(__file__)
+ path = os.path.join(dirname, 'html', 'new.html')
+
+ with(open(path, 'r')) as file:
+ html = file.read()
+
+ return html
+
+```
+
+现在构建、推送和部署该函数。
+
+```plain
+$ faas-cli up -f show-html.yml
+```
+
+打开你的浏览器,访问http://127.0.0.1:8080/function/show-html。你应该看到一个 `这里有一个新的页面!` 在浏览器中呈现的 HTML 页面。
+
+现在我们要为这个函数的 URL 添加一个路径。
+
+在`html`文件夹中添加新的`list.html`文件,内容如下。
+
+```html
+
+
+
+
+ This is a list!
+
+
+
+
+```
+
+将你的`handler.py`编辑成以下内容。
+
+```python
+import os
+
+def handle(req):
+
+ path = os.environ['Http_Path']
+ pathArr = path.split("/")
+ pageName = pathArr[1]
+
+ dirname = os.path.dirname(__file__)
+ page = os.path.join(dirname, 'html', pageName + '.html')
+
+ with(open(page, 'r')) as file:
+ html = file.read()
+
+ return html
+```
+
+构建、推送和部署该函数。
+
+```plain
+$ faas-cli up -f show-html.yml
+```
+
+现在在http://127.0.0.1:8080/function/show-html/new 或 http://127.0.0.1:8080/function/show-html/list 上打开你的网页。
+这将输出。
+```html
+Here's a new page!
+```
+and
+```html
+This is a list!
+
+
+```
+
+## 读取查询字符串并返回不同的 HTML
+
+现在我们已经了解了如何通过函数来提供 HTML,让我们动态地改变通过查询字符串提供的 HTML。正如我们在[实验室 4](./lab4.md)中学到的,查询字符串可以通过一个叫做`Http_Query`的环境变量来检索。假设我们做了一个看起来像这样的查询。
+
+ http://127.0.0.1:8080/function/show-html?action=new
+
+查询字符串是`action=new`,因此`Http_Query`的值将是`action=new`。我们也可以使用`urllib.parse`包中的`parse_qs`函数,轻松解析这个查询字符串。
+
+我们的函数的目录结构看起来是这样的。
+
+```plain
+├── show-html
+│ ├── __init__.py
+│ ├── handler.py
+│ ├── html
+│ │ ├── list.html
+│ │ └── new.html
+│ └── requirements.txt
+└── show-html.yml
+```
+
+
+改变你的`handler.py`。
+
+```python
+import os
+from urllib.parse import parse_qs
+
+def handle(req):
+ """handle a request to the function
+ Args:
+ req (str): request body
+ """
+
+ query = os.environ['Http_Query']
+ params = parse_qs(query)
+ action = params['action'][0]
+
+ dirname = os.path.dirname(__file__)
+ path = os.path.join(dirname, 'html', action + '.html')
+
+ with(open(path, 'r')) as file:
+ html = file.read()
+
+ return html
+```
+
+现在构建、推送和部署该函数。
+
+```plain
+$ faas-cli up -f show-html.yml
+```
+
+打开你的浏览器,首先访问。
+
+http://127.0.0.1:8080/function/show-html?action=new
+
+你应该看到 `这里有一个新的页面!`就像你在上一节看到的那样。现在访问。
+
+http://127.0.0.1:8080/function/show-html?action=list
+
+你应该看到一个显示列表的 HTML。
+
+## 与其他函数协作
+
+最后,让我们看看如何利用 JavaScript 和 Ajax 的优势,从 HTML 函数中与另一个函数(例如*figlet*函数)协作。
+
+首先,让我们再创建一个名为`figlet.html`的 HTML 文件。所以现在的结构应该是这样的。
+
+```plain
+├── show-html
+│ ├── __init__.py
+│ ├── handler.py
+│ ├── html
+│ │ ├── figlet.html
+│ │ ├── list.html
+│ │ └── new.html
+│ └── requirements.txt
+└── show-html.yml
+```
+
+编辑`figlet.html`。
+
+```html
+
+
+
+
+ Figlet
+
+
+在开始这个实验之前,为你的文件创建一个新的文件夹。
+
+```plain
+$ mkdir -p lab7 \
+ && cd lab7
+```
+
+## 同步与异步地调用一个函数
+
+当你同步调用一个函数时,一个连接会通过网关连接到你的函数,并且在整个执行过程中保持开放。同步调用是*阻塞的,所以你应该看到你的客户端暂停,变得不活跃,直到该函数完成其任务。
+
+* 网关使用的路由是。`/function/
+
+在开始这个实验之前,为你的文件创建一个新的文件夹。
+
+```plain
+$ mkdir -p lab8 \
+ && cd lab8
+```
+
+## 用`read_timeout`扩展超时时间
+
+*timeout*对应于一个函数可以运行多长时间,直到被执行。它对防止分布式系统中的误操作很重要。
+
+有几个地方可以为你的函数配置超时,在每个地方都可以通过使用环境变量来完成。
+
+* 函数超时
+
+* `read_timeout` - 允许函数通过 HTTP 读取一个请求的时间
+* `write_timeout` - 允许函数在 HTTP 上写一个响应的时间
+* `exec_timeout` - 一个函数在被终止前可以运行的最大时间。
+
+API 网关的默认时间是 20 秒,所以我们来测试一下在一个函数上设置一个更短的超时时间。
+
+```plain
+$ faas-cli new --lang python3 sleep-for --prefix="
+
+## 自动缩放函数的应用
+
+正如[文档](http://docs.openfaas.com/architecture/autoscaling/)中描述的那样,OpenFaaS 带有自动扩展函数。在这个实验室中,我们将看看自动扩展是如何运作的。
+
+### 前提条件
+
+* 在完成了[Lab 1](./lab1.md)中对 OpenFaaS 的设置后,你将拥有触发自动扩展所需的一切。
+
+* 多个工具可以用来创建足够的流量来触发自动扩展 - 在这个例子中,`curl'将被使用,因为它很容易在 Mac 和 Linux 上使用,并在 Windows 上与 Git Bash 打包。
+
+### 自动扩展的背景
+
+开箱即用的 OpenFaaS 是这样配置的,它将根据 Prometheus 测量的 `每秒请求`指标进行自动扩展。 这个指标是在流量通过 API 网关的时候捕获的。如果超过了定义的 `每秒请求`的阈值,AlertManager 就会启动。这个阈值应该被重新配置为适合生产使用的水平,因为在这个例子中,为了演示,它被设置为一个低值。
+
+> 在[文档网站](http://docs.openfaas.com/architecture/autoscaling/)中找到更多关于自动缩放的信息。
+
+每次警报被 AlertManager 触发时,API 网关将把你的函数的一定数量的副本添加到集群中。OpenFaaS 有两个配置选项,允许指定副本的起始/最低数量,也允许停止副本的最大数量。
+
+你可以通过设置`com.openfaas.scale.min`来控制函数的最小副本量,目前默认值为`1`。
+
+你可以通过设置`com.openfaas.scale.max`来控制一个函数可以产生的最大副本量,目前默认值是`20`。
+
+> 注意: 如果你把`com.openfaas.scale.min`和`com.openfaas.scale.max`设置成相同的值,你就会禁用自动缩放函数。
+
+### 查看 Prometheus
+
+你需要运行这个端口转发命令,以便能够在`http://127.0.0.1:9090`访问 Prometheus。
+```plain
+$ kubectl port-forward deployment/prometheus 9090:9090 -n openfaas
+```
+
+现在添加一个所有成功调用部署的函数的图。我们可以通过执行`rate( gateway_function_invocation_total{code="200"} [20s])`作为查询来实现。导致一个看起来像这样的页面。
+
+ 
+
+ 继续打开一个新的标签,在其中使用`http://127.0.0.1:9090/alerts`导航到警报部分。在这个页面上,你以后可以看到什么时候超过了 `每秒请求` 的阈值。
+
+ 
+
+### 触发缩放的 Go 函数
+
+首先是 Alex Ellis 的 `echo-fn`函数。
+
+```bash
+$ git clone https://github.com/alexellis/echo-fn \
+ && cd echo-fn \
+ && faas-cli template store pull golang-http \
+ && faas-cli deploy \
+ --label com.openfaas.scale.max=10 \
+ --label com.openfaas.scale.min=1
+```
+
+现在检查用户界面,看什么时候 `go-echo`函数从 `不准备`变成 `准备`。你也可以用`faas-cli describe go-echo`来检查。
+
+使用这个脚本反复调用 `go-echo` 函数,直到你看到副本数从 1 变成 5,以此类推。你可以在 Prometheus 中通过添加`gateway_service_count'的图表或在选择该函数的情况下查看 API 网关来监控这个值。
+
+```bash
+$ for i in {0..10000};
+do
+ echo -n "Post $i" | faas-cli invoke go-echo && echo;
+done;
+```
+
+> 注意:如果你在 Kubernetes 上运行,使用`$OPENFAAS_URL`而不是`http://127.0.0.1:8080`。
+
+### 监控警报
+
+现在你应该可以看到,在之前创建的图表中,`go-echo`函数的调用量有所增加。移动到你打开警报页面的标签。一段时间后,你应该开始看到 `APIHighInvocationRate`的状态(和颜色)变为 "待定",然后再次变为 "发射"。你也可以使用`$ faas-cli list`或通过[ui](http://127.0.0.1:8080)看到自动缩放的情况。
+
+ 
+
+现在你可以使用`$ docker service ps go-echo`来验证`go-echo`的新副本是否已经启动。
+
+现在停止 bash 脚本,你会看到副本的数量在几秒钟后回到 1 个副本。
+
+### 疑难解答
+
+如果你认为你的自动扩展没有被触发,那么请检查以下内容。
+
+* 普罗米修斯中的警报页面 - 这应该是红色/粉色的,并显示 `FIRING` - 即在http://127.0.0.1:9090/alerts。
+* 检查核心服务的日志,即网关、Prometheus / AlertManager。
+
+为了获得核心服务的日志,运行`docker service ls`,然后`docker service logs