<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Serverless | 伪架构师</title>
    <link>/tags/serverless/</link>
      <atom:link href="/tags/serverless/index.xml" rel="self" type="application/rss+xml" />
    <description>Serverless</description>
    <generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>zh</language><lastBuildDate>Tue, 16 Apr 2019 03:38:07 +0800</lastBuildDate>
    <image>
      <url>/img/logo-wide.png</url>
      <title>Serverless</title>
      <link>/tags/serverless/</link>
    </image>
    
    <item>
      <title>Google Cloud Run 一瞥</title>
      <link>/post/a-first-look-at-google-cloud-run/</link>
      <pubDate>Tue, 16 Apr 2019 03:38:07 +0800</pubDate>
      <guid>/post/a-first-look-at-google-cloud-run/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://thenewstack.io/a-first-look-at-google-cloud-run/&#34; target=&#34;_blank&#34;&gt;A First Look at Google Cloud Run&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://thenewstack.io/author/marko-anastasov/&#34; target=&#34;_blank&#34;&gt;Marko Anastasov&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Google 在 &lt;a href=&#34;https://cloud.withgoogle.com/next/sf&#34; target=&#34;_blank&#34;&gt;Cloud Next&amp;rsquo;19&lt;/a&gt; 上发布了基于 Docker 容器的的 Serverless 新方案。目前可以肯定的是，这是 Serverless 的重要进步——在 Cloud Run 上进行部署比在 Kubernetes 上运行容器简单多了。而且和 Lambda 不同，这一方案没有语言绑定的问题。&lt;/p&gt;

&lt;h2 id=&#34;什么是-google-cloud-run&#34;&gt;什么是 Google Cloud Run&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;https://cloud.google.com/run/&#34; target=&#34;_blank&#34;&gt;Google Cloud Run&lt;/a&gt; 是一个全托管平台，它以无状态、自动伸缩的 HTTP 服务的形式运行 Docker 容器镜像。&lt;/p&gt;

&lt;p&gt;Cloud Run 和第一代 Serverless 平台（例如 AWS Lambda、&lt;a href=&#34;https://cloud.google.com/functions/&#34; target=&#34;_blank&#34;&gt;Google Cloud functions&lt;/a&gt; 或 Azure Functions）不同，它允许你运行任意的应用，提供多个端点；而无需使用特定接口来运行小规模的函数。&lt;/p&gt;

&lt;p&gt;Cloud Run 的基础来自于 &lt;a href=&#34;https://www.knative.dev/&#34; target=&#34;_blank&#34;&gt;Knative&lt;/a&gt;，因此这一方案有可能被移植到其它的托管 Kubernetes 平台。&lt;/p&gt;

&lt;h2 id=&#34;我的项目能够在-google-cloud-run-上运行么&#34;&gt;我的项目能够在 Google Cloud Run 上运行么？&lt;/h2&gt;

&lt;p&gt;Google 发表了 &lt;a href=&#34;https://cloud.google.com/run/docs/reference/container-contract&#34; target=&#34;_blank&#34;&gt;容器运行时契约&lt;/a&gt;，其中说明了对容器的要求：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;容器是 64 位 Linux 平台；&lt;/li&gt;
&lt;li&gt;在 8080 端口监听 HTTP 请求；&lt;/li&gt;
&lt;li&gt;最多使用 2G 内存；&lt;/li&gt;
&lt;li&gt;容器实例必须在收到请求之后的 4 分钟内启动 HTTP 服务器；&lt;/li&gt;
&lt;li&gt;应用应该能够适应自动从 0 到多个运行实例的容器环境；&lt;/li&gt;
&lt;li&gt;所有的运算都应该是无状态的，限制在一个请求之内。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;只要你的项目能够符合上面的条件，不论使用什么语言开发，都能够在 Cloud Run 上面运行。&lt;/p&gt;

&lt;p&gt;Cloud Run 目前还在 Beta 阶段，因此这些需求可能会发生变动。&lt;/p&gt;

&lt;h2 id=&#34;上手流程&#34;&gt;上手流程&lt;/h2&gt;

&lt;p&gt;对于熟悉 Docker 或者 Heroku 传统 PaaS 解决方案的人来说，Cloud Run 应该很易上手。&lt;/p&gt;

&lt;p&gt;应用打包成 Docker 之后，剩下的任务包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;把镜像推送到 Google 镜像库。&lt;/li&gt;
&lt;li&gt;运行 &lt;code&gt;gcloud beta run deploy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;只要几分钟，Cloud Run 就会使用一个可定制、可开放的域名启动新应用了。&lt;/p&gt;

&lt;h3 id=&#34;示例-使用-semaphore-进行持续部署&#34;&gt;示例：使用 Semaphore 进行持续部署&lt;/h3&gt;

&lt;p&gt;下面的例子中，我们使用 &lt;a href=&#34;https://semaphoreci.com/&#34; target=&#34;_blank&#34;&gt;Semaphore&lt;/a&gt; 要为一个微服务配置 &lt;a href=&#34;https://thenewstack.io/why-cloud-native-success-depends-on-high-velocity-ci-cd/&#34; target=&#34;_blank&#34;&gt;Serverless CI/CD Pipeline&lt;/a&gt;，其中包含如下环节：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;运行自动测试；&lt;/li&gt;
&lt;li&gt;构建 Docker 容器；&lt;/li&gt;
&lt;li&gt;将容器镜像推入 Google 镜像库；&lt;/li&gt;
&lt;li&gt;提供到 Cloud Run 预备环境的一键部署；&lt;/li&gt;
&lt;li&gt;在对 Master 分支的构建成功之后；自动部署到 Cloud Run 生产环境。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;images/dac19d8b-semaphore1-1024x533.png&#34; alt=&#34;semaphore&#34; /&gt;&lt;/p&gt;

&lt;p&gt;可以在 &lt;a href=&#34;https://github.com/semaphoreci-demos/semaphore-demo-cloud-run&#34; target=&#34;_blank&#34;&gt;Github&lt;/a&gt; 上找到相关的全部代码。&lt;/p&gt;

&lt;h3 id=&#34;启用-cloud-run&#34;&gt;启用 Cloud Run&lt;/h3&gt;

&lt;p&gt;官方的&lt;a href=&#34;https://cloud.google.com/run/docs/quickstarts/build-and-deploy&#34; target=&#34;_blank&#34;&gt;快速启动指南&lt;/a&gt;提供了一个 Cloud Run 的上手教程。&lt;/p&gt;

&lt;p&gt;第一步是：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在你的账号中&lt;a href=&#34;http://console.cloud.google.com/apis/library/run.googleapis.com&#34; target=&#34;_blank&#34;&gt;启用 Cloud Run API&lt;/a&gt;；&lt;/li&gt;
&lt;li&gt;安装 Google Cloud SDK；&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;gcloud components install beta&lt;/code&gt; 安装 Beta 组件。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;应用容器化&#34;&gt;应用容器化&lt;/h3&gt;

&lt;p&gt;下面的 Dockerfile 将一个简单的 Sinatra 应用打包：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-dockerfile&#34;&gt;FROM ruby:2.5
RUN apt-get update -qq &amp;amp;amp;&amp;amp;amp; apt-get install -y build-essential
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install --without development test
ADD . $APP_HOME
EXPOSE 8080
CMD [&amp;quot;bundle&amp;quot;, &amp;quot;exec&amp;quot;, &amp;quot;rackup&amp;quot;, &amp;quot;--host&amp;quot;, &amp;quot;0.0.0.0&amp;quot;, &amp;quot;-p&amp;quot;, &amp;quot;8080&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意如果使用你自己的 Dockerfile，必须开放 8080 端口，否则可能会看到如下错误：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/4759c604-screen-shot-2019-04-10-at-22.38.51.png&#34; alt=&#34;execption&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;登录-google-cloud-和-gcr&#34;&gt;登录 Google Cloud 和 GCR&lt;/h3&gt;

&lt;p&gt;要在 CI/CD Pipeline 中自动地将&lt;a href=&#34;https://cloud.google.com/container-registry/docs/pushing-and-pulling&#34; target=&#34;_blank&#34;&gt;镜像推送到 GCR&lt;/a&gt;，需要在 Semaphore 中登录到 Google Cloud。为了安全起见，需要在 Semaphore 中根据 Google Cloud Service account 的认证密钥创建一个 Secret。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://cloud.google.com/iam/docs/creating-managing-service-account-keys#iam-service-account-keys-list-gcloud&#34; target=&#34;_blank&#34;&gt;获取认证密钥&lt;/a&gt;之后，在 Semaphore 中用 Secret 的形式上传到 Semaphore。假设文件名是 &lt;code&gt;.secrets.gcp.json&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sem create secret google-cloud-stg --file ~/Downloads/account-name-27f3a5bcea2d.json:.secrets.gcp.json
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;定义分发管线&#34;&gt;定义分发管线&lt;/h3&gt;

&lt;p&gt;接下来就可以编写一个 Pipeline 来构建、标记并推送镜像到 GCR 了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;# .semaphore/docker-build.yml
# This pipeline runs after semaphore.yml
version: v1.0
name: Docker build
agent:
  machine:
    # Use a machine type with more RAM and CPU power for faster container
    # builds:
    type: e1-standard-4
    os_image: ubuntu1804
blocks:
  - name: Build
    task:
      # Mount a secret which defines an authentication key file.
      # For info on creating secrets, see:
      # - https://docs.semaphoreci.com/article/66-environment-variables-and-secrets
      # - https://docs.semaphoreci.com/article/72-google-container-registry-gcr
      secrets:
        - name: google-cloud-stg
      jobs:
      - name: Docker build
        commands:
          # Authenticate using the file injected from the secret
          - gcloud auth activate-service-account --key-file=.secrets.gcp.json
          # Configure access to container registry, silence confirmation prompts with -q
          - gcloud auth configure-docker -q

          - checkout

          # Tag your images with gcr.io/ACCOUNT_PROJECT_NAME/SERVICE_NAME pattern
          # Use Git SHA to produce unique artifacts
          - docker build -t &amp;quot;gcr.io/semaphore2-stg/semaphore-demo-cloud-run:${SEMAPHORE_GIT_SHA:0:7}&amp;quot; .
          - docker push &amp;quot;gcr.io/semaphore2-stg/semaphore-demo-cloud-run:${SEMAPHORE_GIT_SHA:0:7}&amp;quot;

promotions:
  # Deployment to staging can be trigger manually:
  - name: Deploy to staging
    pipeline_file: deploy-staging.yml

  # Automatically deploy to production on successful builds on master branch:
  - name: Deploy to production
    pipeline_file: deploy-production.yml
    auto_promote_on:
      - result: passed
        branch:
          - master
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在 &lt;code&gt;deploy-staging.yml&lt;/code&gt; 和 &lt;code&gt;deploy-production.yml&lt;/code&gt; 中包含了同样的步骤，区别只是服务的名称。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;# .semaphore/deploy-production.yml
# This pipeline runs after docker-build.yml
version: v1.0
name: Deploy to production
agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu1804
blocks:
  - name: Deploy to production
    task:
      secrets:
        - name: google-cloud-stg
      jobs:
      - name: run deploy
        commands:
          - gcloud auth activate-service-account --key-file=.secrets.gcp.json
          - gcloud auth configure-docker -q
          
          # Deploy to Cloud Run, using flags to avoid interactive prompt
          # See https://cloud.google.com/sdk/gcloud/reference/beta/run/deploy
          - gcloud beta run deploy markoci-demo-cloud-run --project semaphore2-stg --image gcr.io/semaphore2-stg/markoci-demo-cloud-run:${SEMAPHORE_GIT_SHA:0:7} --region us-central1
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;上线运行&#34;&gt;上线运行&lt;/h3&gt;

&lt;p&gt;在本地终端或者 Semaphore 作业的日志中，最后一行会包含一个应用运行的网址：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;https://semaphore-demo-cloud-run-ud2bmvsmda-uc.a.run.app.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用浏览器打开这个网址会看到：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/08fefc9a-screen-shot-2019-04-10-at-22.30.52.png&#34; alt=&#34;forbidden&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这是因为还没有完成最后一步：在 Google Cloud Run 控制台中&lt;a href=&#34;https://cloud.google.com/run/docs/securing/managing-access#making_a_service_public&#34; target=&#34;_blank&#34;&gt;开放服务&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;完成之后的浏览页面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/011b5a8f-semaphore2-1024x627.png&#34; alt=&#34;hello&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;整装待发&#34;&gt;整装待发&lt;/h3&gt;

&lt;p&gt;希望本文能够引起你使用 &lt;a href=&#34;https://thenewstack.io/why-a-well-oiled-ci-cd-pipeline-makes-for-a-happy-devops-team/&#34; target=&#34;_blank&#34;&gt;CI/CD Pipeline&lt;/a&gt; 在 Google Cloud Run 上构建和发布应用的兴趣。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Knative 0.5 发布</title>
      <link>/post/knative-0.50-release/</link>
      <pubDate>Thu, 11 Apr 2019 22:37:55 +0800</pubDate>
      <guid>/post/knative-0.50-release/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://medium.com/knative/announcing-knative-v0-5-release-cfe646ca8e30?nsukey=DmMa%2BnItgi6DqPeCf9uQWn%2BXbGw%2FmuvNjKfVx80KhnL0s9C1pHst%2BzTVnCy8Iax3n9bUNJjrkL9O0TDyf42Qi8z9p0XW4heqtF%2FgPlJrJo%2BNs5qBOkyV%2FyH3Gc%2FjOCwm57VEZYyZNZcFB796ezQwZnbUN06%2FvuNEMR7BpUCETzg9VPlHVOAPpHF6qAkOlfxPCH7iM5Kw1Y7FaL2zLKXXcg%3D%3D&#34; target=&#34;_blank&#34;&gt;Announcing Knative v0.5 Release&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://medium.com/@mchmarny_google&#34; target=&#34;_blank&#34;&gt;Mark Chmarny&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;再一次激动地宣布 &lt;a href=&#34;https://www.knative.dev/&#34; target=&#34;_blank&#34;&gt;Knative&lt;/a&gt; 的新版本发布了。Knative 是一个能帮助开发者在 Kubernetes 基础之上，构建、部署和管理现代 Serverless 工作负载的平台。&lt;/p&gt;

&lt;p&gt;更加频繁和符合预期的发布节奏，让我们有机会能够从真实世界中获得更快的反馈，这种节奏当然也意味着更小的、更多的功能进展。也不全是这样，Knative v0.5 中，Eventing 系统有了长足的进步。其中的 Trigger 和 Broker 对象的引入，让开发者基于 Knative 构建事件驱动系统时能够得到更好更强的开发体验。&lt;/p&gt;

&lt;p&gt;除了 Eventing，这个版本的 Knative 还增加了监控指标，提高了自动伸缩、队列代理以及 Istio 遥测的可观察性，下面做一个介绍，并对部分变更做出一点深入讲解。&lt;/p&gt;

&lt;h2 id=&#34;eventing&#34;&gt;Eventing&lt;/h2&gt;

&lt;p&gt;在 &lt;strong&gt;Eventing&lt;/strong&gt; 架构中加入了 &lt;strong&gt;Trigger&lt;/strong&gt; 和 &lt;strong&gt;Broker&lt;/strong&gt; 对象，开发者能够轻松的构建出复杂且健壮的事件驱动应用。通过对 &lt;strong&gt;Producing&lt;/strong&gt; 和 &lt;strong&gt;Consuming&lt;/strong&gt; 服务的解耦，对路由配置的需求大大降低。我们相信，社区将会使用这一新能力构建出新的事件和创新的解决方案。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;eventing-object-model.png&#34; alt=&#34;Knative Eventing Object Model&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trigger&lt;/strong&gt;：开发者不再需要手工的对事件进行转换并路由给下游的 Knative 服务。只要定义一个简单的事件触发器，选择源事件（可以使用任何方式进行过滤），然后发送到消费方服务即可。这一对象会给开发者的体验带来很大简化。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Broker&lt;/strong&gt;：事件 Broker 充当了事件 Hub 的角色，所有的消息都会发送给它。开发者和用户简单的编写服务或者配置事件源发送时间给 Broker，Broker 会处理其它工作。消费方服务只需要创建一个触发器，从 Broker 中接收它们感兴趣的事件即可。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;新的事件源&lt;/strong&gt;：Kanative 中加入了 Kafka 的事件源，将 Kafka 生态系统的丰富功能带给了 Knative 和 Kubernetes。&lt;/p&gt;

&lt;h2 id=&#34;自动伸缩&#34;&gt;自动伸缩&lt;/h2&gt;

&lt;p&gt;自动伸缩功能增强，在重度工作负载的情况下，能够更平顺、更高效的完成伸缩动作。加入了更多的自动伸缩指标，增强了可观察性。&lt;/p&gt;

&lt;h2 id=&#34;核心-api&#34;&gt;核心 API&lt;/h2&gt;

&lt;p&gt;新版本中，具名子路由的 URL 暴露在 Service 和 Route 资源的状态之中，就不用再猜测如何标记流量的分配状况了。这是 &lt;code&gt;v1beta1 task force&lt;/code&gt; 中的第一个变化。后续版本中将会看到更多这方面的更新。&lt;/p&gt;

&lt;p&gt;另外我们的 Webhook 中，很多缺省值都可以使用名为 &lt;code&gt;config-defaults&lt;/code&gt; 的 Configmap 进行配置了。另外，在我们的控制器遇到内部错误时，会通过 Kubernetes 事件系统提供更好的可见性。最后，我们还扩展了我们的一致性测试，其中包含了对 &lt;code&gt;securityContext&lt;/code&gt; 和 &lt;code&gt;metadata.generateName&lt;/code&gt; 的支持。&lt;/p&gt;

&lt;h2 id=&#34;网络&#34;&gt;网络&lt;/h2&gt;

&lt;p&gt;这一版本中，针对 gRPC 服务的冷启动和客户端的认证头处理有了很大提升。&lt;/p&gt;

&lt;p&gt;Knative 0.5 的完整发布公告中包含了新功能和问题修复的完整列表，分别在 &lt;a href=&#34;https://github.com/knative/serving/releases/tag/v0.5.0&#34; target=&#34;_blank&#34;&gt;Serving&lt;/a&gt;、&lt;a href=&#34;https://github.com/knative/build/releases/tag/v0.5.0&#34; target=&#34;_blank&#34;&gt;Build&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/knative/eventing/releases/tag/v0.5.0&#34; target=&#34;_blank&#34;&gt;Eventing&lt;/a&gt; 代码库中。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>发现 Serverless 应用中的隐形成本</title>
      <link>/post/finding-serverless-hidden-costs/</link>
      <pubDate>Tue, 05 Feb 2019 16:18:33 +0800</pubDate>
      <guid>/post/finding-serverless-hidden-costs/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://thenewstack.io/finding-serverless-hidden-costs/&#34; target=&#34;_blank&#34;&gt;Finding Serverless’ Hidden Costs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://thenewstack.io/author/nitzan-shapira/&#34; target=&#34;_blank&#34;&gt;Nitzan Shapira&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;提到 Serverless 这一概念，除了无需自行维护基础设施之外，另外一个亮点就是按使用付费。&lt;/p&gt;

&lt;p&gt;像 &lt;a href=&#34;https://aws.amazon.com/lambda/&#34; target=&#34;_blank&#34;&gt;AWS Lambda&lt;/a&gt; 这样的服务，代码每运行 100 毫秒，就需要&lt;a href=&#34;https://thenewstack.io/finding-serverless-hidden-costs/&#34; target=&#34;_blank&#34;&gt;支付&lt;/a&gt;一定的费用。例如 512 MB 内存的 Lambda 的（每 100 毫秒）费率是 $0.000000834。最好的一点是，如果代码没有运行，就无需支付费用——在一些大的组织机构中，如果服务器的使用率低于 20%，这种无服务器的方式会在财务上带来巨大的成本节约。&lt;a href=&#34;https://read.acloud.guru/how-going-serverless-helped-us-reduce-costs-by-70-255adb87b093&#34; target=&#34;_blank&#34;&gt;企业已经注意到了这一点&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;这有问题么？没有。这个技术很有意思。然而新技术总是有风险的，必须做出识别和防范。特拉维夫 AWS Community Day 上，&lt;a href=&#34;https://www.youtube.com/watch?v=rVCx8dHidGg&#34; target=&#34;_blank&#34;&gt;The Real Cost of Pay-Per-Use in Serverless&lt;/a&gt; 这篇谈话中，&lt;a href=&#34;https://twitter.com/ranrib&#34; target=&#34;_blank&#34;&gt;Ran Ribenzaft&lt;/a&gt; 说到了 Serverless 技术中几个方面的成本问题。下文将会说到其中的一部分。&lt;/p&gt;

&lt;h2 id=&#34;服务硬件配置是否越低越好&#34;&gt;服务硬件配置是否越低越好&lt;/h2&gt;

&lt;p&gt;部署一个无服务器函数的时候，第一个选择往往就是内存和 CPU。Lambda 中，这两个参数是绑定的一系列固定选择。更大的内存就对应着更高的价格。这么看来，选择较低的内存会省钱对么？不是的，过低的内存可能带来以下问题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;更低的内存配置一般也对应着更低的 CPU 配置，也就意味着更长的运行时间，甚至可能造成超时。超时的严重程度可以与台式机的关机相提并论，是大家非常不愿见到的情况。&lt;/li&gt;
&lt;li&gt;更长的运行时间当然也代表着更高的成本。所以降低内存配置可能会取得一个相反的效果。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;在 &lt;a href=&#34;https://epsagon.com/blog/how-to-make-lambda-faster-memory-performance-benchmark/&#34; target=&#34;_blank&#34;&gt;AWS Lambda 的性能测试&lt;/a&gt;中，发现一个令人惊奇的结果：性能和价格同步上升——直到某点之后，配置的提高不再带来性能的提升。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn.thenewstack.io/media/2019/01/c4413b04-epsagon1.png&#34; alt=&#34;Price vs Performance&#34; /&gt;&lt;/p&gt;

&lt;p&gt;所以想要一次确定 CPU 和内存的最佳方案是不太实际的。函数自身也会发生变更和升级，对配置的需要也有发生变化的肯能。&lt;/p&gt;

&lt;p&gt;所以该怎么办？&lt;/p&gt;

&lt;h3 id=&#34;对执行时间和内存用量进行监控&#34;&gt;对执行时间和内存用量进行监控&lt;/h3&gt;

&lt;p&gt;一般来说在复杂系统中，在你不知道你不知道的情况下，会发生最严重的问题。可观测性是一个关键点——不仅是检测异常和性能瓶颈，还要知道函数的内存消耗和执行时间。最有用的方式之一就是，在问题发生之后的告警之外，尝试预测问题发生的可能。可以通过静态规则以及用峰值和常规行为进行对比的方式来进行这种尝试。&lt;/p&gt;

&lt;h3 id=&#34;昂贵的-api-调用&#34;&gt;昂贵的 API 调用&lt;/h3&gt;

&lt;p&gt;一个主要的观察结果就是，性能和账单是直接相关的。代码运行越慢、就要付出更多。无服务器的总成本由两个因素组成：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;运行业务逻辑的时间&lt;/li&gt;
&lt;li&gt;等待 API 调用返回的时间&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&#34;https://epsagon.com/blog/the-importance-and-impact-of-apis-in-serverless/&#34; target=&#34;_blank&#34;&gt;The Importance and Impact of APIs in Serverless&lt;/a&gt; 的演示中，对一个 &lt;a href=&#34;https://auth0.com/&#34; target=&#34;_blank&#34;&gt;Auth0&lt;/a&gt; 这样的常见服务的简单调用，可能会消耗 Lambda 函数中的 80% 的时间。这并不是说不应该使用第三方 API，正好相反，创建完全无服务器的可伸缩应用，使用第三方应用是一个关键。然而应该对自己的选择有深入了解，相对于传统应用，对工具的配置在无服务器应用中可能会产生更大的影响。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/7120925f-epsagon2.png&#34; alt=&#34;api calling&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;让账单变得可以预测&#34;&gt;让账单变得可以预测&lt;/h2&gt;

&lt;p&gt;虽然从传统上来说，云计算的账单很复杂且难于理解，通常还是可以预测的——买一千个虚拟机，一般是大约知道可能发生的费用的。在 Lambda 以及一些像 &lt;a href=&#34;https://aws.amazon.com/rds/aurora/serverless/&#34; target=&#34;_blank&#34;&gt;Amazon Aurora Serverless&lt;/a&gt; 这样的新服务来说，账单变得更加动态，更难预测。&lt;/p&gt;

&lt;h3 id=&#34;预测成本&#34;&gt;预测成本&lt;/h3&gt;

&lt;p&gt;成本预估是控制支出的有效方法。根据目前的花费情况，可以预测本月的最终成本。成本预测是一种简单有效的防止超支的方式。简单的预测公式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;月度支出 = 当前支出*（本月天数/当前日期）
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例如本月有 30 天，今天是 15 号，本月已经消费了 $200，那么就可以预计到月底的花费应该是 $400。这种预测对于单独的函数或者整体成本都是有效的，在 Epsagon，函数视图中包含了每个函数的预测。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/21a6eabb-epsagon3.png&#34; alt=&#34;Epsagon 3&#34; /&gt;&lt;/p&gt;

&lt;h3 id=&#34;成本监控&#34;&gt;成本监控&lt;/h3&gt;

&lt;p&gt;正如使用性能监控工具来保障应用的正常运行一样，无服务器函数也需要对成本进行监控。&lt;a href=&#34;https://epsagon.com/&#34; target=&#34;_blank&#34;&gt;Epsagon&lt;/a&gt; 的案例研究中，我们分享了一个&lt;a href=&#34;https://www.youtube.com/watch?v=MZmw-bb02Uo&#34; target=&#34;_blank&#34;&gt;故事&lt;/a&gt;，其中一个 Lambda 函数有严重的问题，它会以很高的并发方式运行，最终导致了每月 $12k 的成本。幸运的是，我们对自己的系统进行了监控。另外还发生过大公司发现过&lt;a href=&#34;https://thenewstack.io/serverless-pricing-estimating-consumption-costs/&#34; target=&#34;_blank&#34;&gt;价值 $50,000 的 bug&lt;/a&gt; 的问题，也多亏了 Epsagon 的监控功能。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://cdn.thenewstack.io/media/2019/01/c0624db5-epsagon4.png&#34; alt=&#34;epsagon 4&#34; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://epsagon.com/blog/how-much-does-aws-lambda-cost/&#34; target=&#34;_blank&#34;&gt;Lambda 成本计算器&lt;/a&gt;是一个开源工具，能帮助用户理解函数的可能花费。&lt;/p&gt;

&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;

&lt;p&gt;按使用付费的方式是一个绝妙的概念，让无服务器应用能够大幅降低成本。但是性能问题可能直接影响月度账单，是一个值得注意的问题。&lt;/p&gt;

&lt;p&gt;成本的预测和监控能够降低意外高额账单的风险。特别需要指出的是，无服务器应用中的 API 应该小心使用并注意监控，因为它有成为主要的性能和成本瓶颈的可能。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>在两个半公有云上实现 Github Webhook</title>
      <link>/post/announce-serverless-tranlation-workflow-for-k8s-istio/</link>
      <pubDate>Wed, 16 Jan 2019 02:51:47 +0800</pubDate>
      <guid>/post/announce-serverless-tranlation-workflow-for-k8s-istio/</guid>
      <description>

&lt;h2 id=&#34;背景&#34;&gt;背景&lt;/h2&gt;

&lt;p&gt;&lt;a href=&#34;http://www.servicemesher.com&#34; target=&#34;_blank&#34;&gt;Service Mesher&lt;/a&gt; 社区牵头启动 &lt;a href=&#34;https://istio.io/zh&#34; target=&#34;_blank&#34;&gt;Istio&lt;/a&gt; 文档翻译工作之后，为降低维护工作量，我们开发了一个 &lt;a href=&#34;https://github.com/servicemesher/webhook&#34; target=&#34;_blank&#34;&gt;Github Webhook 项目&lt;/a&gt;，用 Github Issue 的方式对社区翻译工作流程提供自动化支持。同时也开发了一个 Chatbot 来完成任务的维护工作。&lt;/p&gt;

&lt;p&gt;在上海 KubeCon 上，经过和 &lt;a href=&#34;https://kubernetes.io/zh/&#34; target=&#34;_blank&#34;&gt;Kubernetes&lt;/a&gt; 文档工作组进行一番交流之后，决定将这一套方法推行到 Kubernetes 文档的本地化工作之中。&lt;/p&gt;

&lt;p&gt;经过一番准备之后，两个项目用相似的 Flask 代码，以在 VPS 上运行的 Docker Image 的形式支撑了两个本地化工作组的工作流程。&lt;/p&gt;

&lt;p&gt;然而两组代码始终是一个隐患，并且工作流程固化在代码之中，也给流程改进带来很大阻碍；另外使用高配 Linode 运行 Webhook 是个非常奢侈的事情 : smile :。因此也就有了利用公有云 Free Tier 提供 Webhook 响应的想法。&lt;/p&gt;

&lt;p&gt;未解决这些问题，新建了 &lt;a href=&#34;https://github.com/fleeto/issueflow&#34; target=&#34;_blank&#34;&gt;Webhook 项目&lt;/a&gt;，经过对代码的修改，将流程定制工作全部转移到配置文件之中，并将流程处理代码进行了固化，在此基础上，分别实现了 Flask、AWS Lambda 以及 GCP Function 的三个版本。&lt;/p&gt;

&lt;h2 id=&#34;aws-lambda&#34;&gt;AWS Lambda&lt;/h2&gt;

&lt;h3 id=&#34;入口代码&#34;&gt;入口代码&lt;/h3&gt;

&lt;p&gt;Lambda 版本的 Webhook，使用 &lt;a href=&#34;https://github.com/fleeto/issueflow/blob/lambda/lambda.py&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;lambda.py&lt;/code&gt;&lt;/a&gt; 作为入口文件，入口函数为 &lt;code&gt;webhook&lt;/code&gt;，在创建 Lambda 的页面中，可以指定 &lt;code&gt;lambda.webhook&lt;/code&gt; 为入口。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;def webhook(event, context):&lt;/code&gt; 中的 &lt;code&gt;event&lt;/code&gt; 参数中包含了请求数据，&lt;code&gt;context&lt;/code&gt; 顾名思义，包含 Lambda 的上下文信息。例如用下面的代码获取 METHOD、POST Data 以及 Header（强烈鄙视标头这个译法）：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;if event[&amp;quot;httpMethod&amp;quot;] != &amp;quot;POST&amp;quot;:
    return {
        &amp;quot;isBase64Encoded&amp;quot;: &amp;quot;false&amp;quot;,
        &amp;quot;statusCode&amp;quot;: 405,
        &amp;quot;headers&amp;quot;: {},
        &amp;quot;body&amp;quot;: &amp;quot;Method Not Allowed!&amp;quot;
    }

data = json.loads(event[&amp;quot;body&amp;quot;])
event_type = event[&amp;quot;headers&amp;quot;][&amp;quot;X-GitHub-Event&amp;quot;]
event_id = event[&amp;quot;headers&amp;quot;][&amp;quot;X-GitHub-Delivery&amp;quot;]
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;日志&#34;&gt;日志&lt;/h3&gt;

&lt;p&gt;如下代码可以将日志写入 CloudWatch Log。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;logger = logging.getLogger()
logger.setLevel(int(LOG_LEVEL))
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;需要注意的两个问题：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch Log 不属于 Free Tier。因此可以考虑使用 S3 存储文件的方式来完成日志记录。&lt;/li&gt;
&lt;li&gt;AWS 为 Lambda 分配的缺省权限中不包含 Log 的内容，需要在 IAM 中进行授权。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&#34;返回&#34;&gt;返回&lt;/h3&gt;

&lt;p&gt;选择 API Gateway 作为 Lambda 触发器，其返回内容需要是一个固定的 JSON 格式，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;return {
    &amp;quot;isBase64Encoded&amp;quot;: &amp;quot;false&amp;quot;,
    &amp;quot;statusCode&amp;quot;: 405,
    &amp;quot;headers&amp;quot;: {},
    &amp;quot;body&amp;quot;: &amp;quot;Method Not Allowed!&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;部署&#34;&gt;部署&lt;/h3&gt;

&lt;p&gt;Lambda 没有为 Python 提供依赖处理功能，需要自行下载依赖包，并统一打包为 ZIP 文件上传，代码中提供了 &lt;a href=&#34;https://github.com/fleeto/issueflow/blob/lambda/build.sh&#34; target=&#34;_blank&#34;&gt;build.sh&lt;/a&gt;，用于生成发布包。&lt;/p&gt;

&lt;h2 id=&#34;gcp-function&#34;&gt;GCP Function&lt;/h2&gt;

&lt;h3 id=&#34;入口代码-1&#34;&gt;入口代码&lt;/h3&gt;

&lt;p&gt;GCP Function 版本的 Webhook 以 &lt;a href=&#34;https://github.com/fleeto/issueflow/blob/gcp-function/main.py&#34; target=&#34;_blank&#34;&gt;&lt;code&gt;main.py&lt;/code&gt;&lt;/a&gt; 为入口，这是强制规定。可以指定入口函数，我在这里指定使用 &lt;code&gt;webhook&lt;/code&gt; 入口，其中的 &lt;code&gt;request&lt;/code&gt; 参数实际上就是 &lt;a href=&#34;http://flask.pocoo.org/docs/1.0/&#34; target=&#34;_blank&#34;&gt;Flask&lt;/a&gt; 的 Request 对象。因此可以很方便的查找文档。&lt;/p&gt;

&lt;h3 id=&#34;日志-1&#34;&gt;日志&lt;/h3&gt;

&lt;p&gt;这里的日志稍嫌复杂，但是和 AWS 不同的是，StackDriver Log 是免费的，因此可以忍。&lt;/p&gt;

&lt;p&gt;创建 ServiceAccount：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;gcloud iam service-accounts \
create [account] --project [project-id]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;为新账号赋权：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;gcloud projects add-iam-policy-binding [project-id] \
--member &amp;quot;serviceAccount:[account]@[project-id].iam.gserviceaccount.com&amp;quot; \
--role &amp;quot;roles/owner&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;获取账号文件：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;gcloud iam service-accounts keys create permission.json \
--iam-account [account]@[project-id].iam.gserviceaccount.com
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;应用中需要定义 &lt;code&gt;GOOGLE_APPLICATION_CREDENTIALS&lt;/code&gt; 环境变量，指定上传的 permission.json 文件的位置。&lt;/p&gt;

&lt;p&gt;日志需要使用 Google 自己的库来完成：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from google.cloud import logging
...

logging_client = logging.Client()
log_name = &amp;quot;github-webhook-{}&amp;quot;.format(WORKFLOW)
logger = logging_client.logger(log_name)
...
logger.log_struct(
    {&amp;quot;workflow&amp;quot;: WORKFLOW, &amp;quot;admins&amp;quot;: ADMINS}
)
...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code&gt;requirments.txt&lt;/code&gt; 中需要加入如下依赖：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;google-cloud
google-cloud-logging
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&#34;返回-1&#34;&gt;返回&lt;/h3&gt;

&lt;p&gt;返回值无需像 Lambda 一样特别处理，直接 &lt;code&gt;return&lt;/code&gt; 即可。&lt;/p&gt;

&lt;h3 id=&#34;部署-1&#34;&gt;部署&lt;/h3&gt;

&lt;p&gt;GCP Function 提供了依赖处理能力，只需要在 &lt;code&gt;requirements.txt&lt;/code&gt; 中写明依赖包即可。无需下载上传大量的依赖包文件。&lt;/p&gt;

&lt;h2 id=&#34;azure-function&#34;&gt;Azure Function&lt;/h2&gt;

&lt;p&gt;Azure 提供了 func cli 来完成初始化工作，并通过 VS Code 提供了 Azure Function 的开发支持。&lt;strong&gt;然而 func cli 只支持 Python 3.6.x，测试未能完成。&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&#34;一点对比&#34;&gt;一点对比&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GCP Function 的 HTTP 触发器没有提供对网址的定义功能。&lt;/li&gt;
&lt;li&gt;AWS 日志不免费提供，但是比 GCP 更方便。&lt;/li&gt;
&lt;li&gt;AWS 没有提供 Python 的依赖处理。&lt;/li&gt;
&lt;li&gt;GCP Function 部署似乎有一点延迟，不会立即生效。&lt;/li&gt;
&lt;li&gt;AWS Lambda 的默认超时时间为 3 秒，对很多任务来说，可能无法顺利完成。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>Knative v0.2 发布</title>
      <link>/post/announcing-knative-v0.2-release/</link>
      <pubDate>Thu, 15 Nov 2018 00:40:06 +0800</pubDate>
      <guid>/post/announcing-knative-v0.2-release/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://medium.com/knative/https-medium-com-knative-v0-2-963f276af58e&#34; target=&#34;_blank&#34;&gt;Announcing Knative v0.2 Release&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://medium.com/@mchmarny_google&#34; target=&#34;_blank&#34;&gt;Mark Chmarny&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;增强的插接能力、自动伸缩、稳定性和性能。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Knative 是一组用于在 Kubernetes 上构建现代应用的一组中间件组件，在此宣布，0.2 版本已经面世。从七月份该项目启动至今，0.2 版本是第一次显著更新。这次更新是整个 Knative 社区的几个月里的努力成果，体现了我们从日益增多的 Knative 部署数量中学到的新知识。&lt;/p&gt;

&lt;p&gt;0.2 版本中最令人激动的更新就是包含了 &lt;code&gt;eventing&lt;/code&gt;。Eventing 组件补充了另外两大组件：Build 和 Serving 留下的空白。我们希望社区会使用这一新功能来开发新的事件和解决方案。&lt;/p&gt;

&lt;p&gt;Serving 组件的内部实现做出了很大改进，新的内部 API 将 Knative 划分为了子系统，从而加入了对可插接网络、自动伸缩以及缓存的支持。&lt;/p&gt;

&lt;p&gt;完整的发布说明可以在 &lt;a href=&#34;https://github.com/knative/serving/releases/tag/v0.2.1&#34; target=&#34;_blank&#34;&gt;Serving&lt;/a&gt;、&lt;a href=&#34;https://github.com/knative/build/releases/tag/v0.2.0&#34; target=&#34;_blank&#34;&gt;Build&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/knative/eventing/releases/tag/v0.2.0&#34; target=&#34;_blank&#34;&gt;Eventing&lt;/a&gt; 仓库中找到。下面着重说一下其中的亮点：&lt;/p&gt;

&lt;h2 id=&#34;新的事件资源模型&#34;&gt;新的事件资源模型&lt;/h2&gt;

&lt;p&gt;Eventing 在资源模型方面发生了很大变化，将源码融入 CRD 之中。Eventing 现已采用 Kubernetes 的表尊概念，这样一来就可以更好的实现校验、清晰的接口以及 RBAC 支持。新架构使用了以 Channel 和订阅为中心的简化的对象模型。&lt;/p&gt;

&lt;h2 id=&#34;松耦合&#34;&gt;松耦合&lt;/h2&gt;

&lt;p&gt;在 v0.1 期间我们收到的正面评价之一就是在定义 Knative 组件安装时候的选择能力。v0.2 中 Knative 增强了这种能力，操作者可以独立安装 Build、Serving 以及 Eventing 组件。Knative 组件之间的松耦合使得第三方集成成为了可能，例如使用一个非 Knative 的 Build 来和 Serving 进行配合，或者用 Eventing 把事件分发给非 Serving 的部署。我们很高兴社区选择了这一方向。&lt;/p&gt;

&lt;h2 id=&#34;可插接的子系统&#34;&gt;可插接的子系统&lt;/h2&gt;

&lt;p&gt;我们还接到了很多希望能够定制 Knative 的反馈。目标就是支持某种程度的插接能力，所以 v0.2 中定制能力也有了显著提高。我们加入了三个新的内部 API，来把我们的核心资源模型从实现模块中分离出来：网络、自动伸缩和缓存。网络让开发人员可以用不同的 Ingress 实现来替代我们的 Istio 依赖。自动伸缩让开发人员能够把我们的自动伸缩机制替换为自己的方案。缓存让开发人员可以使用镜像缓存策略来在集群中进行镜像的提前载入（这是一个纯粹的扩展点，并没有内置绑定方案）。&lt;/p&gt;

&lt;h2 id=&#34;冷启动改进&#34;&gt;冷启动改进&lt;/h2&gt;

&lt;p&gt;决定冷启动性能的两个决定因素就是 Sidecar 注入和镜像拉取造成的延时。Istio 的 1.0.2 版本中在减少 Envoy 编程时间方面有了进步。另外我们还允许安装无 Sidecar 注入的 Knative。为了帮助用户降低镜像拉取延迟，我们开放了新的扩展点，称为 &amp;ldquo;Image&amp;rdquo; 资源（knative/caching 的一部分），其中包含了一个预载入镜像控制器所需要的所有信息。社区对缓存组件的建设让我们非常激动。&lt;/p&gt;

&lt;h2 id=&#34;自动伸缩&#34;&gt;自动伸缩&lt;/h2&gt;

&lt;p&gt;以前的自动伸缩器是每个版本一个，现在替换为单一的共享控制器。新的自动伸缩器和之前的版本具备同样的逻辑，但是进化到纯粹的指标驱动（包含 0-&amp;gt;1-&amp;gt;0），去掉了无用的 Revision servingState 字段。我们用一个整数 &lt;code&gt;ContainerConcurrency&lt;/code&gt; 字段替换了 &lt;code&gt;ConcurrencyModel&lt;/code&gt;。这样在一些情况下就可以限制并行数量为 1 以外的数值（例如限制线程池）。&lt;/p&gt;

&lt;h2 id=&#34;build&#34;&gt;Build&lt;/h2&gt;

&lt;p&gt;Knative Build 做了很多增强，包括新的 &lt;code&gt;ClusterBuildTemplate&lt;/code&gt; 资源。用户可以一次性安装一组 &lt;code&gt;BuildTemplates&lt;/code&gt;，而不是每个命名空间一次。Build 模板参数可以对构建步骤所用的镜像名称进行设置。Build Spec 的新功能包括用户可以指定 Build 一级的超时、节点选择器以及亲和性。另外 Build 状态得到了扩展，会报告每个步骤和 Build 的等候时间。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>在 Knative 中进行应用程序的构建和部署</title>
      <link>/post/building-and-deploying-applications-to-knative/</link>
      <pubDate>Tue, 13 Nov 2018 00:10:38 +0800</pubDate>
      <guid>/post/building-and-deploying-applications-to-knative/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://starkandwayne.com/blog/building-and-deploying-applications-to-knative/&#34; target=&#34;_blank&#34;&gt;Building and deploying applications to Knative&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://twitter.com/starkandwayne&#34; target=&#34;_blank&#34;&gt;Dr Nic Williams&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Knative 有三个高级子系统：Serving 用来协调服务 Pod 的自动伸缩以及路由；Build 提供了将代码转换为镜像的工具链；Eventing 则会使用事件的发布订阅来触发松耦合服务。&lt;/p&gt;

&lt;p&gt;前一篇文章中我们将一个构件好的容器镜像发布到了 Knative Serving 中。&lt;/p&gt;

&lt;p&gt;本篇文章将使用 Knative Build 把我们的应用通过 Dockerfile 以及 Cloud Foundry buildpack 在发布过程中转化为容器镜像。我们还会尝试从本地文件系统以及远程 Git 仓库中进行部署的方式。&lt;/p&gt;

&lt;p&gt;前面的操作都很简单，看得出在 Kubernetes 环境中安装 Knative、运行现有容器镜像、使用 curl 与应用进行交互都不难。总结一下就是：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl install [--node-ports] [--exclude-monitoring]
knctl deploy --service &amp;lt;service-name&amp;gt; --image &amp;lt;image-name&amp;gt;
knctl curl --service &amp;lt;service-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;后面的两步 - 把镜像部署为 Kubernetes 并运行，为每个部署进行版本化处理，使用 HTTP 路由进行交互，都是 &lt;a href=&#34;https://github.com/knative/serving/blob/master/docs/spec/overview.md&#34; target=&#34;_blank&#34;&gt;Serving 子系统&lt;/a&gt;的功劳。&lt;/p&gt;

&lt;p&gt;Knative 还支持用于构建容器镜像的弹性子系统，构建生成的镜像将会以 Kubernetes Pod 的形式运行。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/knative/docs/tree/master/build&#34; target=&#34;_blank&#34;&gt;Knative Build&lt;/a&gt; 子系统非常有弹性。我们会探讨集中用例：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;上传一个本地目录，使用 Dockerfile 进行构建。&lt;/li&gt;
&lt;li&gt;上传一个本地目录，使用 Buildpack 进行构建。&lt;/li&gt;
&lt;li&gt;用上面两种方式来构建来自于远程 Git 仓库的代码。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;命名空间&#34;&gt;命名空间&lt;/h2&gt;

&lt;p&gt;前一篇文章中我们在每个 &lt;code&gt;knctl&lt;/code&gt; 命令中使用了 &lt;code&gt;--namespace helloworld&lt;/code&gt; 参数来显式的指定命名空间。对我来说这样显得更清晰。可能有别的用户希望设置一个缺省命名空间，从而能够缩短命令。&lt;/p&gt;

&lt;p&gt;可以给 &lt;code&gt;knctl&lt;/code&gt; 配置一个当前命名空间：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kubectl create ns my-simple-app
export KNCTL_NAMESPACE=my-simple-app
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;所有的 &lt;code&gt;knctl&lt;/code&gt; 命令都会使用这一命名空间。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;$ knctl service list
Services in namespace &#39;my-simple-app&#39;

Name  Domain  Annotations  Age

0 services
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以在 &lt;code&gt;kubectl&lt;/code&gt; 命令中复用 &lt;code&gt;$KNCTL_NAMESPACE&lt;/code&gt;：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kubectl get pods -n $KNCTL_NAMESPACE&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&#34;上传一个带有-dockerfile-的本地目录&#34;&gt;上传一个带有 Dockerfile 的本地目录&lt;/h2&gt;

&lt;p&gt;在所有 Knative Build 的示例中，都会产生一个副产品——容器镜像。这些镜像必须放在什么地方，例如 &lt;a href=&#34;https://hub.docker.com/&#34; target=&#34;_blank&#34;&gt;Docker Hub&lt;/a&gt;、&lt;a href=&#34;https://cloud.google.com/container-registry/&#34; target=&#34;_blank&#34;&gt;GCP 容器库&lt;/a&gt;、&lt;a href=&#34;https://azure.microsoft.com/en-au/services/container-registry/&#34; target=&#34;_blank&#34;&gt;Azure 容器库&lt;/a&gt;，或者借助 &lt;a href=&#34;https://goharbor.io/&#34; target=&#34;_blank&#34;&gt;Harbor&lt;/a&gt; 之类的软件自建的私库。&lt;/p&gt;

&lt;p&gt;我们需要在每个应用所在的 Kubernetes 命名空间中给 Knative 配置一个镜像库，然后 &lt;code&gt;knctl basic-auth-secret create&lt;/code&gt; 给 Knative 配置一个 Secret。&lt;/p&gt;

&lt;p&gt;以 Docker Hub 为例，使用 &lt;code&gt;--docker-hub&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl basic-auth-secret create -s registry --docker-hub -u &amp;lt;username&amp;gt; -p &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;GCP 容器仓库可以使用 &lt;code&gt;--gcr&lt;/code&gt; 选项：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl basic-auth-secret create -s registry --gcr -u &amp;lt;username&amp;gt; -p &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其它私库可以使用 &lt;code&gt;--type&lt;/code&gt; 和 &lt;code&gt;--url&lt;/code&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl basic-auth-secret create -s registry --type docker --url https://registry.domain.com/ -u &amp;lt;username&amp;gt; -p &amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下一步把镜像库 Secret 映射到 &lt;a href=&#34;https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/&#34; target=&#34;_blank&#34;&gt;Kubernetes Service account&lt;/a&gt;，它会在 Knative Build 的 Pod 中提供上面的登录信息。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl service-account create --service-account build -s registry
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个操作会体现在 Kubernetes 的 Service account 中：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl get serviceaccount -n $KNCTL_NAMESPACE
NAME      SECRETS   AGE
build     2         37s
default   1         3h
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样我们就准备好使用 Knative Build 创建新容器镜像所需的认证凭据了。&lt;/p&gt;

&lt;p&gt;Clone 一个 Go 应用作为样例，并从它的本地目录推送到 Docker hub：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;git clone https://github.com/cppforlife/simple-app
cd simple-app

DOCKER_IMAGE=index.docker.io/&amp;lt;your hub.docker.com org or user&amp;gt;/knative-simple-app

knctl deploy \
    --service simple-app \
    --directory=$PWD \
    --service-account build \
    --image ${DOCKER_IMAGE:?required} \
    --env SIMPLE_MSG=&amp;quot;Built from local directory using Dockerfile&amp;quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;blockquote&gt;
&lt;p&gt;容器的显式命名事实上只是从 Build 到 Serve 的工作过程中的一个中间步骤的副产品，但是必须提供。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;knctl deploy&lt;/code&gt; 的输出大概是这样的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;Name  simple-app

Waiting for new revision to be created...

Tagging new revision &#39;simple-app-00001&#39; as &#39;latest&#39;

Tagging new revision &#39;simple-app-00001&#39; as &#39;previous&#39;

[2018-10-15T13:18:31+10:00] Uploading source code...

[2018-10-15T13:19:59+10:00] Finished uploading source code...

Watching build logs...

build-step-build-and-push | INFO[0000] Downloading base image golang:1.10.1
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:20:01.547607       1 metadata.go:142] while reading &#39;google-dockercfg&#39; metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:20:01.550268       1 metadata.go:159] while reading &#39;google-dockercfg-url&#39; metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg-url
build-step-build-and-push | INFO[0001] Executing 0 build triggers
build-step-build-and-push | INFO[0001] Extracting layer 0
build-step-build-and-push | INFO[0003] Extracting layer 1
build-step-build-and-push | INFO[0004] Extracting layer 2
build-step-build-and-push | INFO[0004] Extracting layer 3
build-step-build-and-push | INFO[0007] Extracting layer 4
build-step-build-and-push | INFO[0010] Extracting layer 5
build-step-build-and-push | INFO[0015] Extracting layer 6
build-step-build-and-push | INFO[0015] Taking snapshot of full filesystem...
build-step-build-and-push | INFO[0027] WORKDIR /go/src/github.com/mchmarny/simple-app/
build-step-build-and-push | INFO[0027] cmd: workdir
build-step-build-and-push | INFO[0027] Changed working directory to /go/src/github.com/mchmarny/simple-app/
build-step-build-and-push | INFO[0027] Creating directory /go/src/github.com/mchmarny/simple-app/
build-step-build-and-push | INFO[0027] COPY . .
build-step-build-and-push | INFO[0027] RUN CGO_ENABLED=0 GOOS=linux go build -v -o app
build-step-build-and-push | INFO[0027] cmd: /bin/sh
build-step-build-and-push | INFO[0027] args: [-c CGO_ENABLED=0 GOOS=linux go build -v -o app]
build-step-build-and-push | net
build-step-build-and-push | vendor/golang_org/x/net/lex/httplex
build-step-build-and-push | vendor/golang_org/x/net/proxy
build-step-build-and-push | net/textproto
build-step-build-and-push | crypto/x509
build-step-build-and-push | crypto/tls
build-step-build-and-push | net/http/httptrace
build-step-build-and-push | net/http
build-step-build-and-push | github.com/mchmarny/simple-app
build-step-build-and-push | INFO[0030] Taking snapshot of full filesystem...
build-step-build-and-push | INFO[0034] Storing source image from stage 0 at path /kaniko/stages/0
build-step-build-and-push | INFO[0038] trying to extract to /kaniko/0
build-step-build-and-push | INFO[0038] Extracting layer 0
build-step-build-and-push | INFO[0040] Extracting layer 1
build-step-build-and-push | INFO[0041] Extracting layer 2
build-step-build-and-push | INFO[0041] Extracting layer 3
build-step-build-and-push | INFO[0043] Extracting layer 4
build-step-build-and-push | INFO[0046] Extracting layer 5
build-step-build-and-push | INFO[0051] Extracting layer 6
build-step-build-and-push | INFO[0051] Extracting layer 7
build-step-build-and-push | INFO[0051] Deleting filesystem...
build-step-build-and-push | INFO[0053] No base image, nothing to extract
build-step-build-and-push | INFO[0053] Taking snapshot of full filesystem...
build-step-build-and-push | INFO[0062] COPY --from=0 /go/src/github.com/mchmarny/simple-app/app .
build-step-build-and-push | INFO[0063] Taking snapshot of files...
build-step-build-and-push | INFO[0063] EXPOSE 8080
build-step-build-and-push | INFO[0063] cmd: EXPOSE
build-step-build-and-push | INFO[0063] Adding exposed port: 8080/tcp
build-step-build-and-push | INFO[0063] ENTRYPOINT [&amp;quot;/app&amp;quot;]
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:21:04.751338       1 metadata.go:142] while reading &#39;google-dockercfg&#39; metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg
build-step-build-and-push | ERROR: logging before flag.Parse: E1015 03:21:04.753927       1 metadata.go:159] while reading &#39;google-dockercfg-url&#39; metadata: http status code: 404 while fetching url http://metadata.google.internal./computeMetadata/v1/instance/attributes/google-dockercfg-url
build-step-build-and-push | 2018/10/15 03:21:06 pushed blob sha256:72a682eea3309941d5e8e6f993a07ae4d33a413b8b7fa2762f8e969310b5996a
build-step-build-and-push | 2018/10/15 03:21:07 pushed blob sha256:9c24aa788ba416c5e1e631d8af3e3115519ad7ca0f659ac10f40682524c6d9cd
build-step-build-and-push | 2018/10/15 03:21:07 index.docker.io/drnic/knative-simple-app:latest: digest: sha256:b5823ead77d9544998b5bc844f049d1a7dfb0aefe7461b74b3e4f67fb5481fa1 size: 428
nop | Nothing to push

Succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;knative-build-的调试&#34;&gt;Knative Build 的调试&lt;/h2&gt;

&lt;p&gt;目前 &lt;code&gt;knctl deploy&lt;/code&gt; 没有显示任何来自 Knative Build 系统的内部错误或者警告。只需要看着 &lt;code&gt;Waiting for new revision to be created...&lt;/code&gt; 坐享其成就可以了。&lt;/p&gt;

&lt;p&gt;一个调试方法就是使用 &lt;a href=&#34;https://github.com/boz/kail&#34; target=&#34;_blank&#34;&gt;kail&lt;/a&gt; 工具处理来自 Knative Build 子系统的消息：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kail -n knative-build
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样就会看到大量的日志，可以再其中查找错误信息，例如 &lt;code&gt;&amp;quot;msg&amp;quot;:&amp;quot;Failed the resource specific validation{error 25 0 serviceaccounts \&amp;quot;build\&amp;quot; not found}&amp;quot;&lt;/code&gt;。&lt;/p&gt;

&lt;h2 id=&#34;使用-buildpack-进行构建&#34;&gt;使用 Buildpack 进行构建&lt;/h2&gt;

&lt;p&gt;我本人很喜欢 Cloud Foundry 和 Heroku 的镜像构建方式，幸运的是，Knative Build 通过&lt;a href=&#34;https://github.com/knative/build-templates/tree/master/buildpack&#34; target=&#34;_blank&#34;&gt;自定义构建模板&lt;/a&gt;的方式提供了这种支持。&lt;/p&gt;

&lt;p&gt;首先用 &lt;code&gt;buildpack&lt;/code&gt; 这个名字在活动命名空间中注册一个&lt;a href=&#34;https://github.com/knative/build-templates/blob/master/buildpack/buildpack.yaml&#34; target=&#34;_blank&#34;&gt;构建模板&lt;/a&gt;：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kubectl -n $KNCTL_NAMESPACE apply -f \
https://raw.githubusercontent.com/knative/build-templates/master/buildpack/buildpack.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;加入 &lt;code&gt;--template buildpack&lt;/code&gt; 就可以使用这一自定义模板了。构建模板所需的附加环境变量都可以用 &lt;code&gt;--template-env NAME=value&lt;/code&gt; 的方式进行植入。&lt;/p&gt;

&lt;p&gt;例如 Cloud Foundry Go Buildpack 需要 &lt;code&gt;$GOPACKNAME&lt;/code&gt;（&lt;a href=&#34;https://docs.cloudfoundry.org/buildpacks/go/index.html#pushing_Apps_with_native_Go_vendoring&#34; target=&#34;_blank&#34;&gt;参考文档&lt;/a&gt;）：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl deploy \
    --service simple-app \
    --directory=$PWD \
    --service-account build \
    --image ${DOCKER_IMAGE:?required} \
    --env SIMPLE_MSG=&amp;quot;Built from local directory using Buildpack template&amp;quot; \
    --template buildpack \
    --template-env GOPACKAGENAME=main
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;输出内容和 Cloud Foundry buildpack 是一致的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;Name  simple-app

Waiting for new revision (after revision &#39;simple-app-00001&#39;) to be created...

Tagging new revision &#39;simple-app-00002&#39; as &#39;latest&#39;

Tagging older revision &#39;simple-app-00001&#39; as &#39;previous&#39;

[2018-10-15T13:40:41+10:00] Uploading source code...

[2018-10-15T13:42:08+10:00] Finished uploading source code...

Watching build logs...

build-step-build | -----&amp;gt; Go Buildpack version 1.8.26
build-step-build | -----&amp;gt; Installing godep 80
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/godep/godep-v80-linux-x64-cflinuxfs2-06cdb761.tgz]
build-step-build | -----&amp;gt; Installing glide 0.13.1
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/glide/glide-v0.13.1-linux-x64-cflinuxfs2-aab48c6b.tgz]
build-step-build | -----&amp;gt; Installing dep 0.5.0
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/dep/dep-v0.5.0-linux-x64-cflinuxfs2-52c14116.tgz]
build-step-build | -----&amp;gt; Installing go 1.8.7
build-step-build |        Download [https://buildpacks.cloudfoundry.org/dependencies/go/go1.8.7.linux-amd64-cflinuxfs2-fff10274.tar.gz]
build-step-build |        **WARNING** Installing package &#39;.&#39; (default)
build-step-build | -----&amp;gt; Running: go install -tags cloudfoundry -buildmode pie .
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:1124eb40dd68654b8ca8f5d9ec7e439988a4be752a58c8f4e06d60ab1589abdb
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:6be38da025345ffb57d1ddfcdc5a2bc052be5b9491825f648b49913d51e41acb
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:a5733e6358eec8957e81b1eb93d48ef94d649d65c69a6b1ac49f616a34a74ac1
build-step-export | 2018/10/15 03:47:58 mounted blob: sha256:21324a9f04e76c93078f3a782e3198d2dded46e4ec77958ddd64f701aecb69c0
build-step-export | 2018/10/15 03:47:59 pushed blob sha256:efa2d34b82bc07588a1a8fd4526322257408109547ee089a792b3f51c383f8e6
build-step-export | 2018/10/15 03:47:59 pushed blob sha256:d495696b33936c79216ec8178726b9fbe915fafbffdd0911a7fdabce4297d9a4
build-step-export | 2018/10/15 03:48:00 index.docker.io/drnic/knative-simple-app:latest: digest: sha256:e5ef1d4d255b4bcbb38d4b43bb6302423c33e6eeabd0e20d5fda4e5ce4c46668 size: 1082
nop | Nothing to push
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;现在就能看到应用已经部署成功：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl curl -s simple-app
&amp;lt;h1&amp;gt;Built from local directory using Buildpack template&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;私有-git-secret&#34;&gt;私有 Git Secret&lt;/h2&gt;

&lt;p&gt;前面两节我们从本地上传了源码然后构建了 Docker 镜像（使用 Dockerfile 或 Cloud Foundry buildpack），最后运行应用。&lt;/p&gt;

&lt;p&gt;Knative 还能从 Git 仓库获取源码（正式的说法是，Knative Build 只支持从 Git 仓库获取源码，本地代码的支持是 &lt;code&gt;knctl&lt;/code&gt; 提供的）。&lt;/p&gt;

&lt;p&gt;让 Knative Build 获取 Git 仓库中的代码，需要用 &lt;code&gt;--git-url&lt;/code&gt; 和 &lt;code&gt;--git-revision&lt;/code&gt; 来取代 &lt;code&gt;--directory=$PWD&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;如果你的 Git 仓库是私有的，那就还需要在 Service account（在上面的例子中的 &lt;code&gt;build&lt;/code&gt;）里包含 Git ssh 凭据。&lt;code&gt;knctl ssh-auth-secret create&lt;/code&gt; 能够协助用户创建一个 kubernetes.io/ssh-auth secret。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl ssh-auth-secret create --secret git --github --private-key &amp;quot;$(cat ~/.ssh/id_rsa)&amp;quot;
Name  git
Type  kubernetes.io/ssh-auth

$ kubectl get secrets -n $KNCTL_NAMESPACE
NAME                  TYPE                                  DATA   AGE
...
git                   kubernetes.io/ssh-auth                1      5m
registry              kubernetes.io/basic-auth              2      3h
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;现在需要把 &lt;code&gt;git&lt;/code&gt; secret 加入到我们的 &lt;code&gt;build&lt;/code&gt; Service account 之中了。&lt;/p&gt;

&lt;p&gt;在成文之时，&lt;code&gt;knctl&lt;/code&gt; 还没有提供 &lt;code&gt;knctl serviceaccounts update&lt;/code&gt; 这样的命令，所以需要删除重新创建：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kubectl delete serviceaccounts -n $KNCTL_NAMESPACE build
knctl service-account create --service-account build -s registry -s git
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;从-git-部署&#34;&gt;从 Git 部署&lt;/h2&gt;

&lt;p&gt;用 &lt;code&gt;--git-url&lt;/code&gt; 和 &lt;code&gt;--git-revision&lt;/code&gt; 替代 &lt;code&gt;--directory&lt;/code&gt; 来进行 Git 部署：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;knctl deploy \
    --service simple-app \
    --git-url git@github.com:cppforlife/simple-app.git \
    --git-revision master \
    --service-account build \
    --image ${DOCKER_IMAGE:?required} \
    --env SIMPLE_MSG=&amp;quot;Built from Git repo using Buildpack template&amp;quot; \
    --template buildpack \
    --template-env GOPACKAGENAME=main
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;knctl deploy&lt;/code&gt; 命令在 Knative 的基础上提供了创建新镜像的良好体验，可以从本地目录或者 Git 仓库开始，使用 Dockerfile 或 Cloud Foundry buildpack 进行构建，并支持不同的镜像仓库。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>在 Knative 上部署 12 要素应用程序</title>
      <link>/post/deploying-12-factor-apps-to-knative/</link>
      <pubDate>Mon, 12 Nov 2018 00:35:24 +0800</pubDate>
      <guid>/post/deploying-12-factor-apps-to-knative/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://starkandwayne.com/blog/introduction-to-knative/&#34; target=&#34;_blank&#34;&gt;Deploying 12 Factor Apps to Knative&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://twitter.com/starkandwayne&#34; target=&#34;_blank&#34;&gt;Dr Nic Williams&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;我尝试按照本文内容在 Azure 上使用 ACS-Engine 部署的 Kubernetes 上进行了测试，版本部分的案例得到了相反结果，不过还是按照原文发了出来，作为一个获取感性认识的入门还是不错的。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Google Next18 活动中，Google 宣称将会把 GKE 上的无服务器插件以 &lt;a href=&#34;https://github.com/knative/&#34; target=&#34;_blank&#34;&gt;Knative&lt;/a&gt; 的名称进行开源。当时它被描述为无服务器平台的构建块，由此推断，Knative 可能需要 Google、Pivotal 或者 RedHat 的协助才能使用。这可能是开源的古怪时机。从我最初的摸索来看，Knative 能工作；当我把 Heroku/Cloud Foundry buildpacks 加入进来之后，整个系统变得越来越像 Heroku/Cloud Foundry，相对于原始 Kubernetes，我更加了解和喜爱这一系统。&lt;/p&gt;

&lt;p&gt;本文中我们会将 Knative 安装到你自己的 Kubernetes 集群中（&lt;code&gt;knctl install&lt;/code&gt;），然后用 Knative 做些有趣的事情（&lt;code&gt;knctl deploy&lt;/code&gt;）。&lt;/p&gt;

&lt;p&gt;Knative 能够为在 Kubernetes 集群上运行无状态应用的运维人员带来很多惊喜。对我来说，最引人入胜的一点就是伸缩性：在高负载时候进行扩容，没人喜欢你的应用了，就会一直缩容到 0。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/cppforlife/knctl/releases&#34; target=&#34;_blank&#34;&gt;下载和安装&lt;/a&gt; Knative 客户端工具 &lt;code&gt;knctl&lt;/code&gt;，然后就可以在你的 Kubernetes 上部署 Knative，然后发布你的应用了。&lt;/p&gt;

&lt;p&gt;在 MacOS 中，可以利用我们的 Homebrew tap 进行安装：&lt;/p&gt;

&lt;p&gt;&lt;code&gt;brew install starkandwayne/kubernetes/knctl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;这里我假设你再使用 Minikube。在 Minikube 中，可以使用 Node Port 代替 Load balancer：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;minikube start --memory=8192 --cpus=3 \
  --kubernetes-version=v1.11.3 \
  --vm-driver=hyperkit \
  --bootstrapper=kubeadm

knctl install --node-ports --exclude-monitoring
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以参考 &lt;a href=&#34;https://github.com/knative/docs/tree/master/install&#34; target=&#34;_blank&#34;&gt;Knative 文档&lt;/a&gt;来获取在其它类型 Kubernetes 集群上进行部署的要点。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;knctl install&lt;/code&gt; 命令可能要花上几分钟，甚至是十分钟或者更多。这个过程中需要下载大概一打镜像。不管是互联网带宽还是镜像尺寸都可能有变化，所以坐下放松一会，或者出去走走也好。&lt;/p&gt;

&lt;p&gt;如果 &lt;code&gt;knctl install&lt;/code&gt; 失败了，可能是你的 Internet 比较慢，Docker 镜像在命令超时之前还没能完成下载。运行 &lt;code&gt;kubectl get pods --all-namespaces&lt;/code&gt; 直到所有 Pod 都在运行，然后再次运行 &lt;code&gt;knctl install&lt;/code&gt; 命令继续完成安装过程。&lt;/p&gt;

&lt;p&gt;现在你的 Kubernetes 就是无服务器架构了，不错吧。&lt;/p&gt;

&lt;p&gt;可以运行 &lt;code&gt;kubectl get pods --all-namespaces&lt;/code&gt; 看看原始的 Knative 的 Pod 们。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;$ kubectl get pods --all-namespaces
NAMESPACE         NAME                                        READY   STATUS      RESTARTS   AGE
istio-system      istio-citadel-7d8f9748c5-zgm9x              1/1     Running     0          21m
istio-system      istio-cleanup-secrets-j4pkx                 0/1     Completed   0          21m
istio-system      istio-egressgateway-676c8546c5-dnwsd        1/1     Running     0          21m
istio-system      istio-galley-5669f7c9b-g774b                1/1     Running     0          21m
istio-system      istio-ingressgateway-5475685bbb-q5f2x       1/1     Running     0          21m
istio-system      istio-pilot-5795d6d695-9klrz                2/2     Running     0          21m
istio-system      istio-policy-7f945bf487-2wh88               2/2     Running     0          21m
istio-system      istio-sidecar-injector-d96cd9459-7knkm      1/1     Running     0          21m
istio-system      istio-statsd-prom-bridge-549d687fd9-lcmb7   1/1     Running     0          21m
istio-system      istio-telemetry-6c587bdbc4-t4jql            2/2     Running     0          21m
istio-system      knative-ingressgateway-7f4477dd99-n9wz2     1/1     Running     0          4m
knative-build     build-controller-7dcc4b7544-rkgwb           1/1     Running     0          4m
knative-build     build-webhook-fb6484576-sr4fk               1/1     Running     0          4m
knative-serving   activator-77d46b585d-b6g8n                  2/2     Running     0          4m
knative-serving   controller-85768cfd45-t8ktc                 1/1     Running     0          4m
knative-serving   webhook-56dd548f8-hjw44                     1/1     Running     0          4m
...
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;部署预构建的应用&#34;&gt;部署预构建的应用&lt;/h2&gt;

&lt;p&gt;接下来我们试试用一个现有的 Docker 镜像来作为自动伸缩的无状态应用运行到 Knative 上，在当前 Kubernetes 命令空间中：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;kubectl create ns helloworld

knctl deploy \
      --namespace helloworld \
      --service hello \
      --image gcr.io/knative-samples/helloworld-go \
      --env TARGET=Rev1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;命令执行后会输出一些信息，表明 &lt;code&gt;hello&lt;/code&gt; 服务已经创建，它的第一个版本 &lt;code&gt;hello-00001&lt;/code&gt; 已经创建，并且被标记为 &lt;code&gt;latest&lt;/code&gt; 和 &lt;code&gt;previous&lt;/code&gt;（第一个版本）。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-plain&#34;&gt;Name  hello

Waiting for new revision to be created...

Tagging new revision &#39;hello-00001&#39; as &#39;latest&#39;

Tagging new revision &#39;hello-00001&#39; as &#39;previous&#39;

Succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以用一个 curl 请求，发送到 Knative 的路由层，来调用我们的 &lt;code&gt;hello&lt;/code&gt; 服务：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl curl --namespace helloworld --service hello
Running: curl &#39;-H&#39; &#39;Host: hello.helloworld.example.com&#39; &#39;http://192.168.64.8:32380&#39;

Hello World: Rev1!

Succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果没有马上显示 &lt;code&gt;Hello World: Rev1!&lt;/code&gt;，可能是因为你的系统还在下载应用镜像、可以稍后重试。&lt;/p&gt;

&lt;p&gt;我使用的是 Minikube 中的 NodePort Ingress，这意味着我不能设置漂亮的 DNS 路由。以后我会讨论一下公共负载均衡、DNS、Knative 路由以及 &lt;code&gt;https://github.com/cppforlife/kwt&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;knctl deploy&lt;/code&gt; 之后，我们的 Kubernetes 用单 Pod 的形式运行这一服务：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl get pods --namespace helloworld
NAME                                      READY   STATUS    RESTARTS   AGE
hello-00001-deployment-5864685cbc-v8r7n   3/3     Running   0          15s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Knative 中，服务的版本是一个重要特性。我们可以看到我们的唯一版本的服务正在处理 100% 的流量：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl revisions list --namespace helloworld --service hello
Revisions for service &#39;hello&#39;

Name         Tags      Allocated Traffic %  Serving State  Annotations  Age
hello-00001  latest    100%                 Active         -            3m
             previous

1 revisions
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下一步我们使用 &lt;code&gt;knctl deploy&lt;/code&gt; 创建一个新的版本，然后把所有流量分配到新版本：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl deploy \
      --namespace helloworld \
      --service hello \
      --image gcr.io/knative-samples/helloworld-go \
      --env TARGET=Rev2

Name  hello

Waiting for new revision (after revision &#39;hello-00001&#39;) to be created...

Tagging new revision &#39;hello-00002&#39; as &#39;latest&#39;

Tagging older revision &#39;hello-00001&#39; as &#39;previous&#39;

Succeeded
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;现在请求被发送到了我们的新版本：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl revisions list --namespace helloworld --service hello
Revisions for service &#39;hello&#39;

Name         Tags      Allocated Traffic %  Serving State  Annotations  Age
hello-00002  latest    100%                 Active         -            1m
hello-00001  previous  0%                   Active         -            10m

$ knctl curl --namespace helloworld --service hello
Running: curl &#39;-H&#39; &#39;Host: hello.helloworld.example.com&#39; &#39;http://192.168.64.8:32380&#39;

Hello World: Rev2!
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;部署第二个版本之后，起初两个版本的 Pod 都会持续运行：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl get pods --namespace helloworld
NAME                                      READY   STATUS    RESTARTS   AGE
hello-00001-deployment-5864685cbc-v8r7n   3/3     Running   0          1m
hello-00002-deployment-7874bf89b8-4b4k5   3/3     Running   0          29s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们会看到 Knative 自动将没有路由指向的版本缩容到 0。&lt;/p&gt;

&lt;h2 id=&#34;路由&#34;&gt;路由&lt;/h2&gt;

&lt;p&gt;下图展示了路由到服务某版本的过程中所涉及到的 &lt;a href=&#34;https://github.com/knative/serving/blob/master/docs/spec/overview.md&#34; target=&#34;_blank&#34;&gt;Knative Serving&lt;/a&gt; 模块：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/object_model.png&#34; alt=&#34;Object Model&#34; /&gt;&lt;/p&gt;

&lt;p&gt;版本是代码、依赖和配置的的只读快照。没有被路由引用的版本会被放弃，其中的 Kubernetes 资源会被删除。&lt;/p&gt;

&lt;p&gt;我们可以看到当前的路由：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl routes list --namespace helloworld
Routes in namespace &#39;helloworld&#39;

Name   Traffic         All Traffic Assigned  Ready  Domain                        Age
hello  100% -&amp;gt; hello:  true                  true   hello.helloworld.example.com  2h
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&#34;缩容至-0&#34;&gt;缩容至 0&lt;/h2&gt;

&lt;p&gt;如果离开五分钟再回来，你会发现 &lt;code&gt;hello-00002&lt;/code&gt; Pod 正在被终结或者已经不见了：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ kubectl get pods --namespace helloworld
NAME                                      READY   STATUS        RESTARTS   AGE
hello-00001-deployment-5864685cbc-v8r7n   3/3     Running       0          6m
hello-00002-deployment-7874bf89b8-4b4k5   2/3     Terminating   0          5m
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下一次 &lt;code&gt;knctl curl&lt;/code&gt;，Knative 会动态的启动一个 Pod 来满足这一请求。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&#34;language-shell&#34;&gt;$ knctl curl --namespace helloworld --service hello
$ kubectl get pods --namespace helloworld
NAME                                      READY   STATUS    RESTARTS   AGE
hello-00001-deployment-5864685cbc-v8r7n   3/3     Running   0          8m
hello-00002-deployment-7874bf89b8-kfbm2   3/3     Running   0          10s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我还不太清楚为什么 &lt;code&gt;hello-00001-deployment-...&lt;/code&gt; Pod 没有被缩容和终结。&lt;/p&gt;

&lt;h2 id=&#34;未完待续&#34;&gt;未完待续&lt;/h2&gt;

&lt;p&gt;后续文章中将会尝试：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Knative Build 组件：使用 &lt;code&gt;Dockerfile&lt;/code&gt; 或者 Cloud Foundry buildpack 自动从定制代码构建容器镜像（代码可以保存本地或者 Git 仓库之中）。&lt;/li&gt;
&lt;li&gt;为 Kubernetes 的负载均衡设置 DNS，从而为 Knative 路由和服务提供公共 URL。&lt;/li&gt;
&lt;li&gt;在不同版本之间进行流量分割（例如 10% 到最新版本，90% 到前一版本；然后将 100% 分配给新版本，老版本流量降低到 0%）。&lt;/li&gt;
&lt;li&gt;Knative Eventing：在应用中进行 &lt;a href=&#34;https://github.com/cloudevents/spec&#34; target=&#34;_blank&#34;&gt;CloudEvents&lt;/a&gt;的绑定和分发。让你的服务更加“无服务器”。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;社区&#34;&gt;社区&lt;/h2&gt;

&lt;p&gt;Knative 核心团队有自己的 &lt;a href=&#34;https://knative.slack.com/&#34; target=&#34;_blank&#34;&gt;Knative Slack&lt;/a&gt;，可以在 &lt;a href=&#34;https://slack.knative.dev&#34; target=&#34;_blank&#34;&gt;https://slack.knative.dev&lt;/a&gt; 申请加入。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://groups.google.com/forum/#!forum/knative-dev&#34; target=&#34;_blank&#34;&gt;knative-dev Group&lt;/a&gt; 中包含了总结和提议。&lt;/p&gt;

&lt;h2 id=&#34;鸣谢&#34;&gt;鸣谢&lt;/h2&gt;

&lt;p&gt;感谢 Google 的 &lt;a href=&#34;https://m.chmarny.com/&#34; target=&#34;_blank&#34;&gt;Mark Chmarny&lt;/a&gt;，在 2018 Spring One 上首先回应了我的问题。&lt;/p&gt;

&lt;p&gt;感谢 Pivotal 的 &lt;a href=&#34;https://github.com/cppforlife&#34; target=&#34;_blank&#34;&gt;Dmitriy Kalinin&lt;/a&gt; 花时间帮助我将 Knative 运行起来，并给我展示了他的 &lt;a href=&#34;https://github.com/cppforlife/knctl&#34; target=&#34;_blank&#34;&gt;knctl Knative CLI&lt;/a&gt;，以及 &lt;a href=&#34;https://github.com/cppforlife/kwt&#34; target=&#34;_blank&#34;&gt;kwt Kubernetes Workstation Tools&lt;/a&gt;。相对于 YAML + &lt;code&gt;kubectl&lt;/code&gt; 组合来说，一个好用的客户端工具更能够帮助 Knative 走向实用。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Knative：在 Kubernetes 上构建可移植 Serverless 平台</title>
      <link>/post/knative-enables-portable-serverless/</link>
      <pubDate>Thu, 20 Sep 2018 17:13:03 +0800</pubDate>
      <guid>/post/knative-enables-portable-serverless/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://thenewstack.io/knative-enables-portable-serverless-platforms-on-kubernetes-for-any-cloud/&#34; target=&#34;_blank&#34;&gt;Knative Enables Portable Serverless Platforms on Kubernetes, for Any Cloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://thenewstack.io/author/dan-baskette/&#34; target=&#34;_blank&#34;&gt;Dan Baskette&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://kubernetes.io/&#34; target=&#34;_blank&#34;&gt;Kubernetes&lt;/a&gt; 目前如日中天，这一项目不仅在容器编排方面独占鳌头，还给基础设施自动化进程提供了可实践的原语。&lt;/p&gt;

&lt;p&gt;但是我们注意到，开发团队在进行基于 Kubernetes 的应用部署时常有困扰。Kubernetes 毕竟只会推送容器——要想推送应用代码或者 Function，很明显就不是 Kubernetes 的能力所在了。&lt;/p&gt;

&lt;p&gt;这样一来，就有不少厂商以 K8S 作为基础设施，展开了高级抽象方面的竞争。这也是 &lt;a href=&#34;http://github.com/knative&#34; target=&#34;_blank&#34;&gt;Knative&lt;/a&gt; 的着眼点。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://twitter.com/kelseyhightower&#34; target=&#34;_blank&#34;&gt;Kelsey Hightower&lt;/a&gt;：Kubernetes 是一个用来构建平台的平台。它是起跑线，不是目的地。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&#34;function-是值得注意的下一次抽象&#34;&gt;Function 是值得注意的下一次抽象&lt;/h2&gt;

&lt;p&gt;读者可能已经注意到分布式系统世界的新晋成员：FaaS（Functions/Serverless）。Function 是一种新的抽象方式，让开发人员能够轻松的运行部署代码片段，并具备根据事件进行伸缩的能力。&lt;/p&gt;

&lt;p&gt;这对开发人员来说是非常有吸引力的。为什么？把所有的基础设施和应用启动之前的事件处理都抽象之后，开发人员能够完全专注于解决如何使用 Function 的代码处理事件的问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://storage.googleapis.com/cdn.thenewstack.io/media/2018/07/26b7e88b-danb1.png&#34; alt=&#34;抽象策略&#34; /&gt;&lt;/p&gt;

&lt;p&gt;天下自然是没有免费的午餐了，FaaS 的问题在哪里呢？&lt;/p&gt;

&lt;p&gt;市场上有很多 FaaS 方案，每个都是独一无二的，他们有自己的 Function 触发方式，自己的可接受事件格式范围，独特的伸缩功能以及从各自角度触发，为开发人员作出的各种抽象。&lt;/p&gt;

&lt;p&gt;如果有要用到的抽象，可以寄希望于云供应商将其打包并为你提供服务。Azure Functions、Lambda 以及 Google Cloud Function 就是这样工作的：根据事件运行 Function 代码，按需伸缩。这类产品按照用量进行计费——根据调用量收取费用。&lt;/p&gt;

&lt;p&gt;开源社区的开发者们也加入了 FaaS 的盛宴，OpenFaaS、Fission、Kubeless 以及 Project Riff 这些项目都是构建在 Kubernetes 之上的 FaaS。这一共同的基础，也有了大同小异的产品。&lt;/p&gt;

&lt;p&gt;在三个核心领域，每个项目都有些许差异：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;都有自己的构建服务，也就是把 Function 进行容器式构建和部署的功能。&lt;/li&gt;
&lt;li&gt;都有一种按调用需要进行扩容（或者缩容）的实现。&lt;/li&gt;
&lt;li&gt;都提供了根据事件调用 Function 的能力，事件可能是 HTTP 或者是事件中间件的发布、订阅方式。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这些细微差异会造成平台采用的巨大障碍。在企业开发者眼里，这一领域功能破碎，竞品众多。所以只能静观其变。&lt;/p&gt;

&lt;h2 id=&#34;knative-适时出现&#34;&gt;Knative 适时出现&lt;/h2&gt;

&lt;p&gt;Google 看到这种碎片化的现状，也注意到了开发人员在 Kubernetes 上进行 Function 开发的过程中对通用工具集的需求。&lt;a href=&#34;http://pivotal.io/knative&#34; target=&#34;_blank&#34;&gt;Knative&lt;/a&gt; 就是基于这种需求产生的。&lt;/p&gt;

&lt;p&gt;Knative 是一个开源软件层，帮助云服务供应商和企业平台在任意云上为开发者提供 Serverless 体验。&lt;/p&gt;

&lt;p&gt;Pivotal 也身在其中，不但向 Knative 贡献了来自 &lt;a href=&#34;https://projectriff.io/&#34; target=&#34;_blank&#34;&gt;riff&lt;/a&gt; 项目的事件模型，还和 Google 一起，在其它方面贡献了开发人员和代码。我们为这一项目的未来欢欣鼓舞，将 riff 和 Knative 结合在一起，酝酿成我们的新项目 &lt;a href=&#34;https://pivotal.io/platform/pivotal-function-service&#34; target=&#34;_blank&#34;&gt;Pivotal Function Service&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;所以对于 Knative 来说，还需要知道点什么呢？这个项目使用 Kubernetes 作为容器编排层。它使用大家熟知的 Kubernetes 对象（Pod、Replica Set 以及 Deployment）构建应用。Istio？是的，Knative 使用 Istio 来进行网格内的路由以及 Ingress 入口管理。&lt;/p&gt;

&lt;p&gt;但是仅仅有 Kubernetes 和 Istio 还是不够的。因此 Knative 同时还引入了三个松耦合的组件，协同对外提供一个完整的 Serverless 平台：Build、Eventing 以及 Serving。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build：提供了一个可插入模型，用于从源码构建容器。&lt;/li&gt;
&lt;li&gt;Eventing：让应用或者 Function 发布到或订阅事件流，事件流包括 Google Cloud Pub/Sub 以及 Apache Kafka。&lt;/li&gt;
&lt;li&gt;Serving：为应用或 Function 提供运行和扩容以及缩容至 0 的能力。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上述元素融合形成的 Knative 又有何神通？它提供了一种较为简化的部署和运行 function 的方式，包括这些模式：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;从源码构建应用和 Function。&lt;/li&gt;
&lt;li&gt;包含多种构建方法（Cloud Foundry Buildpacks、Bazel、Kaniko、Dockerfiles，并可以扩展支持其他方式）。&lt;/li&gt;
&lt;li&gt;开发者能够轻松部署新的（可路由的）应用和 Function。&lt;/li&gt;
&lt;li&gt;允许应用的不间断升级。&lt;/li&gt;
&lt;li&gt;应用实例的自动伸缩。&lt;/li&gt;
&lt;li&gt;把事件绑定到 Function、应用或者容器上。&lt;/li&gt;
&lt;li&gt;当发生 HTTP 请求时触发 Function。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;稍微深入一点看看这几个组件。&lt;/p&gt;

&lt;h2 id=&#34;build-源码到容器的弹性和可扩展过程&#34;&gt;Build：源码到容器的弹性和可扩展过程&lt;/h2&gt;

&lt;p&gt;开发人员编写源码。Kubernetes 操作容器。如何完成联动？Cloud Foundry 使用 buildpack 来完成这一场景。Knative 提供一个插件模型来完成从代码到容器的构建过程。这一模型通过 CRD 实现，也就是一组 Kubernetes API 对象。这种方式提供了一个构建块，能够作为一个 CI/CD 之类的更大系统的一部分，完成源码的构建。&lt;/p&gt;

&lt;p&gt;Knative 的 Build 组件包含 4 个主要组成部分。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;描述如何获取待构建的源码。位置在 &lt;code&gt;/workspace&lt;/code&gt; 卷中存储，这个内容会在后面的步骤中沿用。通常情况下，源码会保存在 git gcs 之类的版本控制系统中，也可以用自定义容器来访问源码。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;步骤或模板&lt;/strong&gt;：这是构建容器的实际工作。这个过程简单说来就是根据 Build 规范完成一系列步骤。换句话说，这一过程由一组可插接构建器组成，被设计用来从源码构建容器，目前这个模型支持五种构建模板，提供了可共享的构建过程：&lt;a href=&#34;https://docs.cloudfoundry.org/buildpacks/&#34; target=&#34;_blank&#34;&gt;Cloud Foundry Buildpacks&lt;/a&gt;、&lt;a href=&#34;https://cloud.google.com/container-builder/docs/&#34; target=&#34;_blank&#34;&gt;Google Container Builder&lt;/a&gt;、Bazel、Kaniko 以及 Jib。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Account&lt;/strong&gt;：用来运行构建过程的账号。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储卷&lt;/strong&gt;：可以定义多个卷，来提供对构建步骤的支持。这些卷可以有很多用途，例如共享 Secret 或者在多个步骤间提供缓存。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&#34;serving-按需伸缩以及版本为基础的高级运维&#34;&gt;Serving：按需伸缩以及版本为基础的高级运维&lt;/h2&gt;

&lt;p&gt;自动化升级了开发者的工作流。Serving 的自动化范围覆盖了从容器到运行中的 Function 部分。它还提供了容器的快速部署，以及根据进入请求完成扩容到 N 或缩容到 0 的能力支持。Istio 在版本之间进行路由，这使得不间断升级、蓝绿部署、金丝雀测试以及回滚都成为了可能。&lt;/p&gt;

&lt;p&gt;Serving 包含了四个 CRD：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;管理应用和 Function 的生命周期以及提供控制点。它可以处理对象的生成，来保障应用或者 Function 的任何版本更新都具备网络路由、配置以及版本支持。&lt;/li&gt;
&lt;li&gt;代码和配置的固定快照。一个版本会引用一个容器以及创建这个容器所需的内容。历史中可以包含多个版本，这样就能够支持一些蓝绿部署或者回退之类的高级运维工作。&lt;/li&gt;
&lt;li&gt;网络端点到一或多个应用版本的映射。&lt;/li&gt;
&lt;li&gt;定义了部署的最新版本以及各版本的状态。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;images/7346ed16-danb2.png&#34; alt=&#34;serving&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;eventing-把订阅-发布操作进行抽象-简化开发人员工作&#34;&gt;Eventing：把订阅/发布操作进行抽象，简化开发人员工作&lt;/h2&gt;

&lt;p&gt;Function 的基本存在价值就是用来响应事件。FaaS 项目和受管服务的区别就是事件的接收以及消费方式。Knative Eventing 组件用来对事件系统的后端进行抽象，从而解放开发人员。开发人员无需了解消息平台、不用关注数据复制等问题。&lt;/p&gt;

&lt;p&gt;Knative 提供了 CRD 用于事件的生产和消费。Eventing 组件由两类 CRD 组成：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Channel&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;发布/订阅模型中发布者发送消息的目标。一般来说，Channel 是一组位置用于获取或存储事件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bus&lt;/strong&gt;：Channel 的后端。这是为事件提供消息平台支持的底层，可以是 Google Cloud PubSub、Apache Kafka 以及 RabbitMQ 等。&lt;/li&gt;
&lt;li&gt;这些结合起来告知 Knative 服务，特定 Channel 的消息会被哪个应用或者 Function 消费。这是应用和 Function 的入口地址。&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feeds&lt;/strong&gt;：事件携带的附件。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&#34;images/88636989-danb3.png&#34; alt=&#34;eventing&#34; /&gt;&lt;/p&gt;

&lt;h2 id=&#34;试用你能掌控的最高级抽象&#34;&gt;试用你能掌控的最高级抽象&lt;/h2&gt;

&lt;p&gt;Knative 是一个全新事物，但是已经有了很多资源可供学习和参考。企业开发软件数量暴涨，意味着典型情况下，公司都会谋求试用应用平台、容器编排以及 Function。Pivotal 希望在所有不同抽象中驱动开源软件的发展。Cloud Foundry、Kubernetes 以及 Knative 会成为大公司的软件构建和运行过程中的主要推手。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/knative/docs&#34; target=&#34;_blank&#34;&gt;Knative 文档&lt;/a&gt;是该项目的主要信息源。每个组件都在仓库中有自己的一席之地，让用户可以跟进最新进展。&lt;/li&gt;
&lt;li&gt;可以阅读 &lt;a href=&#34;https://content.pivotal.io/blog&#34; target=&#34;_blank&#34;&gt;Pivotal 博客&lt;/a&gt;，Ryan Morgan 在其中发布了关于 Pivotal 在 Knative 项目中贡献的相关内容，会涉及企业应用 Serverless 的更多案例。&lt;/li&gt;
&lt;li&gt;在 Google Cloud 也有很多资料：

&lt;ul&gt;
&lt;li&gt;Knative &lt;a href=&#34;https://cloud.google.com/knative&#34; target=&#34;_blank&#34;&gt;概览页面&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Knative &lt;a href=&#34;https://cloudplatform.googleblog.com/2018/07/bringing-the-best-of-serverless-to-you.html&#34; target=&#34;_blank&#34;&gt;博客&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;如果想要知道 &lt;a href=&#34;https://projectriff.io/&#34; target=&#34;_blank&#34;&gt;riff 项目&lt;/a&gt; 的信息，官方网站是最好的起步地点。其中包含了所有的文档和对 &lt;a href=&#34;https://github.com/projectriff/riff&#34; target=&#34;_blank&#34;&gt;riff 仓库&lt;/a&gt;的引用。&lt;/li&gt;
&lt;li&gt;想要了解更多？&lt;a href=&#34;https://springoneplatform.io/2018/sessions&#34; target=&#34;_blank&#34;&gt;SpringOne 平台有一套 Serverless 课程&lt;/a&gt;。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    <item>
      <title>如何在企业中尝试无服务器技术</title>
      <link>/post/serverless-101-how-to-get-serverless-started-in-the-enterprise/</link>
      <pubDate>Thu, 07 Jun 2018 02:06:09 +0800</pubDate>
      <guid>/post/serverless-101-how-to-get-serverless-started-in-the-enterprise/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://thenewstack.io/serverless-101-how-to-get-serverless-started-in-the-enterprise/&#34; target=&#34;_blank&#34;&gt;Serverless 101: How to Get Serverless Started in the Enterprise&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://thenewstack.io/author/michelle_gienow/&#34; target=&#34;_blank&#34;&gt;Michelle Gienow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;起初，世上有了&lt;a href=&#34;https://en.wikipedia.org/wiki/Bare-metal_server&#34; target=&#34;_blank&#34;&gt;裸机&lt;/a&gt;，这很好。&lt;/p&gt;

&lt;p&gt;独占的服务器很快、很可靠，也很安全——毕竟是独占。但是平心而论，这时候的配置和扩展非常麻烦。对于敏捷和弹性的渴望，催生了&lt;a href=&#34;https://en.wikipedia.org/wiki/Virtual_machine&#34; target=&#34;_blank&#34;&gt;虚拟机&lt;/a&gt;，云供应商们适时推出了&lt;a href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_a_service&#34; target=&#34;_blank&#34;&gt;IaaS（基础设施即服务）&lt;/a&gt;，云端+自助的模式也就自然而然的随之面世了。IaaS 的沃土中，产生了 &lt;a href=&#34;https://aws.amazon.com/&#34; target=&#34;_blank&#34;&gt;Amazon Web Services&lt;/a&gt;、编排技术、以及&lt;a href=&#34;https://en.wikipedia.org/wiki/Infrastructure_as_Code&#34; target=&#34;_blank&#34;&gt;基础设置即代码 (IaC: infrastructure as code)&lt;/a&gt;；容器化初露端倪、&lt;a href=&#34;https://en.wikipedia.org/wiki/Platform_as_a_service&#34; target=&#34;_blank&#34;&gt;PaaS（平台即服务）&lt;/a&gt;也应运而生。当然了，一切都很好，更好了。只不过，开发者们大声疾呼，他们需要语言无关的服务端点、水平伸缩以及实时的服务计费。&lt;/p&gt;

&lt;p&gt;祈祷有时候是有效的，这世界得到了一份大礼：无服务器计算，也被称为 &lt;a href=&#34;https://en.wikipedia.org/wiki/Function_as_a_service&#34; target=&#34;_blank&#34;&gt;Function as a Service (FaaS)&lt;/a&gt;。运行时部分只做执行，不保存数据。换个说法就是，AWS、&lt;a href=&#34;https://cloud.google.com/&#34; target=&#34;_blank&#34;&gt;Google Cloud&lt;/a&gt; 或者 &lt;a href=&#34;https://azure.microsoft.com/en-us/&#34; target=&#34;_blank&#34;&gt;Microsoft Azure&lt;/a&gt; 负责动态的管理资源的分配和分布。&lt;/p&gt;

&lt;p&gt;从前使用的基于预测的预付费服务方式已经不适合无服务器计算，取而代之的是根据实际用量进行的即时计费方案。2018 年开始，我们开始将其视为基础设施。&lt;/p&gt;

&lt;h2 id=&#34;并非魔法&#34;&gt;并非魔法&lt;/h2&gt;

&lt;p&gt;首先，这个名字很有误导性：无服务器计算不是魔法，并不是由神秘的月光驱动的——服务器还是需要的。&lt;/p&gt;

&lt;p&gt;这个说法大行其道的原因是，这一架构隐藏了服务器管理以及容量规划和决策等工作。无服务器计算中所谓的“无服务器”，是针对用户和开发者来说的，对他们来说，再也不用花力气、甚至不用知道基础设施了——服务器已经成为一个抽象概念了。无服务器架构中的代码可以和微服务这样的传统的代码部署一起使用——或者可以完全使用无服务器架构来构建应用，就完全不需要关注服务器的问题了。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;无服务器架构的真正价值在于时间效率&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&#34;https://bitnami.com/&#34; target=&#34;_blank&#34;&gt;Bitnami&lt;/a&gt; 的云计算高级总监 &lt;a href=&#34;https://ch.linkedin.com/in/sebastien-goasguen-382b7b21&#34; target=&#34;_blank&#34;&gt;Sebastien Goasguen&lt;/a&gt; 说：“我认为这是一种最小化的 PaaS，一种类似胶水的软件”。“无服务器架构中的关键动作就是——从云中发生了事件，触发了函数的执行。”，Goasguen 描述了 AWS 中的一个场景：把图片保存到存储设施中，接下来调用一个函数来进行图片的缩放。无服务器系统中会处理把这些代码自动的注入到运行时环境中（服务器或者容器），然后进行公开，让函数可以被外部调用。&lt;/p&gt;

&lt;h2 id=&#34;不是魔法是什么&#34;&gt;不是魔法是什么？&lt;/h2&gt;

&lt;p&gt;传统云计算和无服务器计算的主要区别来自于客户：没人愿意为自己没使用的资源买单。从前我们需要对容量和资源需求进行预测，然后根据预测来采购基础设施——可能是自有也可能是公有云。我们前面的例子中，就是要在 AWS 启动服务器，令其保持就绪，这样才能随时使用这一服务来完成图片的缩放任务——只有在这时候才真正的调用了这一函数。&lt;/p&gt;

&lt;p&gt;无服务器计算服务把你的函数作为输入，执行其中的逻辑，然后返回函数的输出，最后关掉。用户只会收到函数执行期间的消费账单。&lt;/p&gt;

&lt;p&gt;随用随付，只为实际消费买单，这是个伟大创新。然而 Goasguen 和其他一些 Cloud Native 专家强调，无服务器技术的真正价值不在于成本效率，而在于时间效率 。&lt;/p&gt;

&lt;h2 id=&#34;好像时光机&#34;&gt;好像时光机？&lt;/h2&gt;

&lt;p&gt;是的有点像。或者更像一个通向未来的 Portal，无服务器技术让客户专注的用各种 AI、机器学习等神奇的新技术构建应用，而不是为了跟随时代步伐，永远的建设和翻新基础设施。&lt;/p&gt;

&lt;p&gt;无服务器技术的另外一个时光机的特性就是缩短从代码开发到生产运行的时间。简单到了一句话：“这是代码，拿去运行吧”，几乎没有基础设施方面的拖拖拉拉。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.oracle.com/index.html&#34; target=&#34;_blank&#34;&gt;Oracle&lt;/a&gt; 的无服务器技术副总裁 &lt;a href=&#34;https://www.linkedin.com/in/chadarimura&#34; target=&#34;_blank&#34;&gt;Chad Arimura&lt;/a&gt; 说：“基本思路，开发人员把编写好的代码推送给无服务器服务提供商，其余的部分交给服务商完成。”，另外他还补充说，常见的依赖内容，例如服务器和存储，也会在无服务器计算的环境中提供无缝支持。&lt;/p&gt;

&lt;p&gt;“幕后有专家团队和大量的自动化工具来运维这些系统，从而把开发者从这些问题中解脱出来”，Arimura 接下来说，“无服务器的魔术一般的体验，让这一概念的热度持续上升。“&lt;/p&gt;

&lt;h2 id=&#34;感受-faas-平台的力量&#34;&gt;感受 FaaS 平台的力量&lt;/h2&gt;

&lt;p&gt;Docker 简化了分布式应用的打包和依赖管理，Kubernetes 让企业能够在生产环境中运行这些应用，但是他们的易用性还是不够。Bitnami 的 Goasguen 说：”Dockerfile、基础设施的细节、Kubernetes 的清单——这些对开发思维的受众来说还是稍显复杂的。“&lt;/p&gt;

&lt;p&gt;无服务器计算的核心，就是 FaaS 平台。具体点说就是 &lt;a href=&#34;https://aws.amazon.com/lambda/&#34; target=&#34;_blank&#34;&gt;AWS Lambda&lt;/a&gt; 或者 &lt;a href=&#34;https://cloud.google.com/functions/&#34; target=&#34;_blank&#34;&gt;Google Cloud Function&lt;/a&gt;，所做的事情：资源管理、负载均衡以及多线程处理；而开发者只需要为雇主专注完成代码即可。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://www.iguazio.com/&#34; target=&#34;_blank&#34;&gt;iguaz.io&lt;/a&gt;  是一个致力于优化云端性能的持续数据平台，其创始人和 CTO &lt;a href=&#34;https://il.linkedin.com/in/yaronh&#34; target=&#34;_blank&#34;&gt;Yaron Haviv&lt;/a&gt; 提出：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;无服务器平台接收函数代码（FaaS 中的 &amp;ldquo;F&amp;rdquo;），为这些代码提供所有的依赖支持（库、内存、配置等），然后构建一个容器化的应用包（通常是 Docker 格式）。&lt;/li&gt;
&lt;li&gt;函数的触发方，可以是其他的平台服务，例如对象存储或者数据库；也可以是一个外部的 HTTP 请求。无服务器平台会把请求转发给一个可用的函数微服务上去。如果没有可用的实例，就部署一个——也就是冷启动。&lt;/li&gt;
&lt;li&gt;无服务器平台需要处理好微服务的故障恢复、按需伸缩、日志和监控，并在代码变更后执行滚动更新。开发者除了函数本身，所有其他问题都无需关注。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&#34;无服务器架构的不足&#34;&gt;无服务器架构的不足&lt;/h2&gt;

&lt;p&gt;无服务器并非银弹，还是有一些不足的。有专家说，云服务商经常会缩减一些使用率不高的环境的资源，他们还会限制用户的资源总量，这样就产生一个矛盾——高性能和高延迟同时出现。另外云服务商的监控、排错和安全都会有自己的思路，可能让用户觉得很古怪。这些问题的根源就是：公有云是不受客户控制的。&lt;/p&gt;

&lt;p&gt;Amazon Lambda 跟无服务器计算是同时进步的，提供了一个多数人都可以接受的模型。 因为 Lambda 是无服务器技术的开创者，所以他的不足也自然而然的被视作无服务器技术的不足：缓慢的冷启动、低性能、短命的函数以及千篇一律的触发器。目前认为这些限制是所有无服务器平台的通病，然而实际上，这是实现过程的一些选择问题。我们应该注意一些新的无服务器平台，例如 &lt;a href=&#34;https://nuclio.io/&#34; target=&#34;_blank&#34;&gt;Nuclio&lt;/a&gt;，他提供了更丰富的用例。这些平台的限制更少，提供更好的性能，同时能够运行在多种云甚至是私有云上。&lt;/p&gt;

&lt;p&gt;”极客们喜欢这个，但是企业还在试水——他们连 Docker/K8S 还没用上呢，就来到无服务器时代了，“，Haviv 说。”和任何新技术一样，我们需要首先争取到早期试用者——他们通常更敏捷、更能承受风险，然后才能接近大众。而要获得早期试用者，需要建立信任、看到证据、解决性能、安全等方面的疑难等等。“&lt;/p&gt;

&lt;p&gt;显然无服务器技术正在日新月异的向前发展，肯定无法十全十美。虽然下面的话不一定是什么缺点，但却是能让董事们无法安坐在 Aeron 椅子的一个问题：”数字化转型总在被新技术打断，因此企业必须更加敏捷和并正视风险，“Haviv 指出。&lt;/p&gt;

&lt;p&gt;”有趣之处在于，Docker/K8S 的抽象是非常复杂的，而无服务器技术虽然更加新潮，却更加简单、更易接受。“&lt;/p&gt;

&lt;h2 id=&#34;我的公司是否适合无服务器技术&#34;&gt;我的公司是否适合无服务器技术？&lt;/h2&gt;

&lt;p&gt;暂时不提企业适合无服务器技术是好是坏，我们先来看看领导厂商。&lt;/p&gt;

&lt;p&gt;Oracle 的 Arimura 认为：”字面上来说，所有组织和公司，只要需要编写软件的，就都适合使用无服务器技术“，”目前的文化和云原生之间的距离越远，就越难应用无服务器技术“，换句话说，如果一个公司没有使用公有云、也没有在内部试用 Kubernetes 或者 Docker 这样的新技术，那么无服务器可能就不是一个合适的尝试。&lt;/p&gt;

&lt;p&gt;”这是一个新架构，需要不同的思维方式。最简单的例子：如果一个单体应用拆分为十个微服务，再分为一百个函数，这些单元都具有自己的发布周期和复杂的依赖关系。这很明显需要成熟稳定的持续构建、交付以及自动化系统的支撑。“，Arimura 说，”敏捷创新是无服务器技术的必要支撑，否则造成的害处可能要多余带来的益处。“&lt;/p&gt;

&lt;p&gt;”DevOps 的目标不是 NoOps，这完全是个错误思路，无服务器技术更加需要 DevOps 的支持。“&lt;/p&gt;

&lt;p&gt;来自 Bitnami 的 Goasguen 补充，采用无服务器技术（尤其是 AWS Lambda）的大多数公司都是以开发者为中心的，他们在此之前已经在应用 AWS，现在使用无服务器计算把服务连接在一起。所以机会就是，如果现在还没有使用 AWS，那么你不需要无服务器技术，然而你仍然应该保持跟踪，开始评估，识别企业中可能存在的事件源，看看如何使用这些事件构建完整的应用管线。&lt;/p&gt;

&lt;h2 id=&#34;企业如何试水无服务器技术&#34;&gt;企业如何试水无服务器技术？&lt;/h2&gt;

&lt;p&gt;”不要把一个单体应用整体转化为微服务或者函数“，Arimura 建议，”使用公司的最重要项目来学习新架构是不明智的，尤其是在公司的文化还在 DevOps 的尝试阶段。“&lt;/p&gt;

&lt;p&gt;从小处入手，例如一点自动化任务，或者一些市场活动以及一些事件驱动的用例等。&lt;/p&gt;

&lt;p&gt;New Stack 的无服务器技术系列，将协助你的公司探索无服务器的新领域。&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>无服务器架构</title>
      <link>/post/serverless/</link>
      <pubDate>Wed, 30 May 2018 17:11:14 +0800</pubDate>
      <guid>/post/serverless/</guid>
      <description>

&lt;p&gt;原文：&lt;a href=&#34;https://martinfowler.com/articles/serverless.html&#34; target=&#34;_blank&#34;&gt;Serverless Architectures&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;作者：&lt;a href=&#34;https://www.symphonia.io/bios/#mike-roberts&#34; target=&#34;_blank&#34;&gt;Mike Roberts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;h2&gt;目录&lt;/h2&gt;
HAHAHUGOSHORTCODE-TOC0-HBHB&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;无服务器架构是一种应用设计方法，这种方法在 FaaS 平台之上，在受管理的临时容器中，把第三方 BaaS 服务以及客制代码结合起来提供服务。贯彻这种思路，以及单页应用等相关概念，能能够大大降低对保持开机的服务器的需求。无服务器架构能够显著降低运维成本、复杂性以及交付时间；但随之而来的，他增加了对供应商以及相对稚嫩的支持服务能力的依赖。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;无服务器运算&lt;/strong&gt;，或者简单说&lt;strong&gt;无服务器&lt;/strong&gt;，是软件架构界的一个新热点。三大云——亚马逊、谷歌以及微软都在无服务器方面下了重注，我们会看到很多的书籍、开源项目、会议以及软件供应商，都不约而同的关注这一主题。但是什么是无服务器？是否值得重视？为什么值得重视？本文中我希望能在这一问题方面给读者一些启迪。&lt;/p&gt;

&lt;p&gt;我们首先来解答“无服务器运算是什么”的问题，然后可以研究一下这一方法的得与失。&lt;/p&gt;

&lt;h2 id=&#34;无服务器是什么&#34;&gt;无服务器是什么&lt;/h2&gt;

&lt;p&gt;和软件方面的其他新浪潮一样，目前对无服务器还没有一个清晰的描述。对于初学者，它包含了两个不同但重叠的领域：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;无服务器最先用于描述一种主要或者全部由云端第三方应用和服务构成的，用于管理服务端逻辑和状态的（技术）。典型的富客户端应用——比如单页 Web 应用、或者移动应用，会使用大量的云端数据库（例如 Parse、Firebase）、认证服务（例如 Auth0、AWS Cognito）等。在前面的文章中曾经使用 “后端即服务”，简称 BaaS 来形容这些服务。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;无服务器还用来形容另一种应用，服务端逻辑还是由应用的开发者编写的，和传统架构的区别是，这种架构由事件驱动，运行于无状态的临时容器中、并且完全由第三方管理。一种理解就是“Functions As a Service”，也就是 FaaS（这个名称的&lt;a href=&#34;https://twitter.com/marak/status/736357543598002176&#34; target=&#34;_blank&#34;&gt;来源&lt;/a&gt;，是一条 &lt;a href=&#34;https://twitter.com/marka&#34; target=&#34;_blank&#34;&gt;@marka&lt;/a&gt; 的推特，目前已不可见）。&lt;a href=&#34;https://aws.amazon.com/lambda/&#34; target=&#34;_blank&#34;&gt;AWS Lambda&lt;/a&gt;就是 FaaS 平台中最著名的一个，当然，不是唯一一个。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本文的主要焦点就在 FaaS 上：这不仅是因为这一架构比较新潮更加耀眼，更深入的原因是，他和我们现有的技术架构差异颇大。&lt;/p&gt;

&lt;p&gt;BaaS 和 FaaS 的一个相似之处是他们的运维属性（淡化资源管理）类似，并经常结合使用。几大公有云都提供了无服务器产品，其中包含了 FaaS 和 BaaS 产品：例如 &lt;a href=&#34;https://aws.amazon.com/serverless/&#34; target=&#34;_blank&#34;&gt;Amazon 的无服务器产品&lt;/a&gt;。Google 的 Firebase BaaS 数据库，也具有 &lt;a href=&#34;https://firebase.google.com/docs/functions/&#34; target=&#34;_blank&#34;&gt;Google Cloud Function for Firebase&lt;/a&gt; 提供的 FaaS 支持。&lt;/p&gt;

&lt;p&gt;在规模较小的公司中，这两种产品也会有类似的关联。例如 &lt;a href=&#34;https://auth0.com/&#34; target=&#34;_blank&#34;&gt;Auth0&lt;/a&gt; 初创期间的产品是 BaaS 形态的，提供了各种方面的用户管理方案；接下来开发了相关的 FaaS 形态的服务 &lt;a href=&#34;https://webtask.io/&#34; target=&#34;_blank&#34;&gt;Webtask&lt;/a&gt;。而且他们还有更进一步的产品 &lt;a href=&#34;https://auth0.com/extend/&#34; target=&#34;_blank&#34;&gt;Extend&lt;/a&gt;，该产品让其他的 SaaS 和 BaaS 公司能够轻松的在现有产品中加入 FaaS 能力，这样就能创建一个统一的无服务器产品了。&lt;/p&gt;

&lt;h3 id=&#34;两个例子&#34;&gt;两个例子&lt;/h3&gt;

&lt;h4 id=&#34;界面驱动的应用&#34;&gt;界面驱动的应用&lt;/h4&gt;

&lt;p&gt;我们可以回忆一下一种常见的应用形态：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;面向客户&lt;/li&gt;
&lt;li&gt;三层结构&lt;/li&gt;
&lt;li&gt;服务端逻辑&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;一个很好的例子就是典型的电子商务 App ——或者怂怂的说：Pet Store？&lt;/p&gt;

&lt;p&gt;一般来说这种架构会比较像下面的这张图，例如用 Java 或者 Javascript 完成服务端开发，并使用 HTML + Javasript 组件实现客户端：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/ps.svg&#34; alt=&#34;ps.svg&#34; /&gt;&lt;/p&gt;

&lt;p&gt;这种结构下的客户端相对来说没有智能的，大多数的系统功能——认证、页面导航、搜索、事务等等都是在服务端应用实现的。&lt;/p&gt;

&lt;p&gt;在一个 Serverless 架构下，这张图可能要这么画：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/sps.svg&#34; alt=&#34;sps.svg&#34; /&gt;&lt;/p&gt;

&lt;p&gt;虽说这个视图已经做了大幅度的简化，但是和前面比起来，差异还是非常显著的：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;我们从原有应用中删除了认证逻辑，用第三方的 BaaS 服务（例如 Auth0）取而代之。&lt;/li&gt;
&lt;li&gt;另外一个 BaaS 的例子，我们让客户端能够直接访问部分数据库（用来完成商品列表），这部分数据库也是由第三方提供的（例如 Google 的 Firebase）。我们的服务端和客户端可以使用不同的安全方案来进行数据库访问。&lt;/li&gt;
&lt;li&gt;前两点的变化发生之后，可以推导出第三点差异：Pet Store 的一部分服务端逻辑现在转换到客户端了——例如用户会话的跟踪、应用的用户体验结构、读取数据库并转换为可用视图等。这一切都说明，应用本身正在向&lt;a href=&#34;https://en.wikipedia.org/wiki/Single-page_application&#34; target=&#34;_blank&#34;&gt;单页应用&lt;/a&gt;的方向转变。&lt;/li&gt;
&lt;li&gt;我们可能想要在服务端保留一些 UX 相关的功能，例如一些集中运算或者需要访问大量数据的功能。在我们的 Pet Store 中，一个符合这种描述的功能就是搜索。过去我们要实现搜索功能需要有一个持续运行的服务器，而现在，我们可以创建一个 FaaS 功能来响应来自 API 网关的 HTTP 请求。搜索功能的客户端和服务器都从同一个产品数据库中读取数据。如果我们选择使用 AWS Lambda 作为 FaaS 平台的话，因为 Lambda 支持我们旧架构中使用的 Java 和 JavaScript，所以无需完全重写，就可以把原来 Pet Store 的服务端搜索功能的代码迁移到新的 Function 实现中。&lt;/li&gt;
&lt;li&gt;最后，我们要把我们的订购功能也替换为独立的 FaaS Function，但是这里为了安全考虑，我们选择将其保留在服务端而非客户端。这个功能也会从 API 网关接受请求。在使用 FaaS 的过程中，拆解不同的逻辑需求，分发给不同的部署组件，是一个再寻常不过的任务。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;回顾一下，这个例子展示了另外一个很重要的无服务器架构的知识点。在原有版本中，所有的流程、控制和安全都是在中央服务器的服务器应用中管理的。在无服务器版本中，就没有一个集中的关注点了。我们会看到 &lt;a href=&#34;https://stackoverflow.com/questions/4127241/orchestration-vs-choreography&#34; target=&#34;_blank&#34;&gt;choreography 而不是 orchestration&lt;/a&gt; （个人理解，二者都是服务的编排方式，choreography 是智能的去中心的，同样的差距存在于 SoA 和微服务架构中的端点和管道的关系），每个组件都对架构有更多的感知——这种情况在微服务方法中也是很常见的。&lt;/p&gt;

&lt;p&gt;这种实现方法有很多好处。例如 Sam Newman 在 &lt;a href=&#34;https://samnewman.io/books/building_microservices/&#34; target=&#34;_blank&#34;&gt;Building Microservices&lt;/a&gt; 书中指出，这样构建的应用“通常更具弹性切更易变更”。每个功能都作为一个独立的可更新的组件存在、分拆不同的关注点，同时，Gojko Adzic 在&lt;a href=&#34;https://gojko.net/2017/10/05/serverless-design-gotocph.html&#34; target=&#34;_blank&#34;&gt;一次谈话&lt;/a&gt;中还提到，这一架构方法所具备的成本优势。&lt;/p&gt;

&lt;p&gt;当然这种架构设计同样是一种权衡：这种架构需要更好的分布式监控，另外还对底层系统的安全性更加依赖。更有甚者，设计微服务应用时，会有大量的关注点充斥在我们的脑海之中——在设计单体应用的时候可不是这样。多后端组件设计所体现的弹性和成本优势，相对于增加的复杂性来说是否划算，是一个非常需要具体分析的问题。&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Building Microservices 一书已经由崔立强、张骏翻译，并由人民邮电出版社出版，中文名为《微服务设计》。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;消息驱动的应用&#34;&gt;消息驱动的应用&lt;/h4&gt;

&lt;p&gt;另一个例子是后端数据处理服务。&lt;/p&gt;

&lt;p&gt;假设你正在开发一个面向用户的应用，这个应用要求有迅速的 UI 响应，另外还需要捕捉用户所有的活动。联想一下在线广告系统：用户点击广告的时候，我们希望能够迅速的把用户引导到广告的目标连接；同时还需要收集这一点击事件，以此来向广告主收取费用。（这个例子其实并非虚构——我之前的在 &lt;a href=&#34;http://www.intentmedia.com/&#34; target=&#34;_blank&#34;&gt;Intent Media&lt;/a&gt; 的团队就有这个需求，也是用无服务器方式实现的）。&lt;/p&gt;

&lt;p&gt;过去会用下图的方式来实现这种应用。“Ad Server” 会同步向用户发送响应（图中没有表示），同时向消息频道发布一个点击消息。这个消息会由 &amp;ldquo;Click Processor&amp;rdquo; 应用进行异步处理——更新数据库，扣减广告主的余额等等。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/cp.svg&#34; alt=&#34;cp.svg&#34; /&gt;&lt;/p&gt;

&lt;p&gt;在无服务器的方法中，会变成这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/scp.svg&#34; alt=&#34;scp.svg&#34; /&gt;&lt;/p&gt;

&lt;p&gt;看到区别了？架构的变更很小了——这就是异步消息处理在无服务器世界中大放异彩的原因。原本需要有一个长期运行的应用来进行消息消费，现在我们使用一个 FaaS Function 将其替换。这个 Function 在一个供应商提供的事件驱动的上下文中。注意云平台供应商提供了消息转发和 FaaS 环境——这两个系统通常是紧密相关的。&lt;/p&gt;

&lt;p&gt;FaaS 环境下，只要运行更多的 Function 代码实例，就可以并行处理更多消息。这可能是我们在实现过程中，需要特别注意的一个新概念。&lt;/p&gt;

&lt;h3 id=&#34;解密-faas&#34;&gt;解密 FaaS&lt;/h3&gt;

&lt;p&gt;行文至此，我们已经多次提到 FaaS 了，那么 FaaS 到底是什么？这里我们可以看看 Amazon FaaS 产品的&lt;a href=&#34;https://aws.amazon.com/cn/lambda/&#34; target=&#34;_blank&#34;&gt;说明&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;通过 AWS Lambda，无需配置或管理服务器即可运行代码（1）。您只需按消耗的计算时间付费 – 代码未运行时不产生费用。借助 Lambda，您几乎可以为任何类型的应用程序或后端服务运行代码（2），而且全部无需管理。只需上传您的代码，Lambda 会处理运行（3）和扩展高（4）可用性代码所需的一切工作。您可以将您的代码设置为自动从其他 AWS 服务触发（5），或者直接从任何 Web 或移动应用程序调用（6）。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;首先，FaaS 用来运行后端代码，无需管理自己的服务器以及自己的长期运行的服务应用&lt;/strong&gt;。“长期运行的服务应用”，是和容器和 PaaS 这样的现代架构的根本差别。回顾刚才提到的点击处理案例，我们把点击处理服务（不一定是物理机，但是肯定是个应用）替换为一个无需具体服务器，也无需保持运行的应用。&lt;/li&gt;
&lt;li&gt;FaaS 的服务侧不依赖具体框架和库。在语言和环境方面来说，FaaS Function 只是普通的应用。例如 AWS Lambda Function 可以使用 Javascript、Python、Go 以及任意的 JVM 语言（Java、Clojure、Scala 等），或者任意的 .NET 语言。然而你的 Lambda Function 还能运行绑定在工件之内的其他进程，所以事实上你可以使用任何能够编译成 Unix 进程的语言（例如后面会提到的 Apex）。
FaaS Function 也有明显的架构限制，尤其是在进行状态处理和长期运行的情况下。我们很快就会遇到这一情况。
再次回到我们的点击处理案例。在向 FaaS 进行迁移的时候，唯一需要修改的代码就是入口方法，也就是启动代码，这段代码用于顶层的消息处理（消息监听器接口的实现），可能只要修改代码签名即可完成任务。&lt;/li&gt;
&lt;li&gt;跟传统过程比起来，没有了服务应用，所以部署的区别是非常大的。在 FaaS 环境下，我们把我们的代码提交给 FaaS 服务商，服务商为我们提供其他所有的必要资源，完成相应的管理工作，例如虚拟机实例、进程管理等。&lt;/li&gt;
&lt;li&gt;水平伸缩是完全自动化的、弹性的，当然也是由服务商提供的。如果你的系统需要处理 100 个并发请求，服务商会自行处理，无需额外配置。运行容器是临时的，他的创建和销毁完全是受运行时驱动的。更重要的是，FaaS 语境下，服务商需要处理所有底层资源的供给问题——集群、虚拟机之类的管理工作完全和用户无关了。
再一次检视我们的点击处理应用。某个时间里，我们的客户一反寻常的发出了超出平时十倍的点击量。在传统架构中，我们的应用是否能够安然度过？例如我们的应用是否涉及为同时处理多个消息？如果答案是肯定的，那么一个实例是否就足够处理这种规模的负载？如果我们可以运行多个进程，那么这一伸缩过程是自动的还是手工的？在 FaaS 方法中，这些问题的答案就很确定了——我们的 Function 在设计之初就要有个基本假设：我们要处理水平扩展的并发需求，FaaS 服务商要给我们自动提供处理所有伸缩需求的资源。&lt;/li&gt;
&lt;li&gt;FaaS 中的 Function，通常是被需要一个触发条件的，而触发条件是由服务商定义的。在 AWS 中，触发事件包括 S3（文件或者对象）的更新、时间（定时任务），以及加入消息总线的消息（例如 &lt;a href=&#34;https://aws.amazon.com/kinesis/&#34; target=&#34;_blank&#34;&gt;Kinesis&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;多数服务商允许 Function 被外来的 HTTP 请求触发；在 AWS 中一个典型的实现方式就是 API 网关。我们 Pet Store 应用中的搜索和订购两个 Function，都需要使用 API 网关。我们的 Function 也可以直接被内部和外部平台 API 调用，但这就不是一个常用方式了。&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&#34;状态&#34;&gt;状态&lt;/h4&gt;

&lt;p&gt;FaaS Function 在本地（服务器、绑定实例）状态方面有着严格的限制，这里说的状态包括内存中的变量、本地盘中的数据等。这些存储都是可用的，但是这些状态信息在多次调用之间是没有持久化方面的保障的，也不能假设一次调用中保存的状态会在另一次调用中生效。我们经常会说，FaaS Function 是无状态的，不过更精确的描述是：FaaS Function 中需要进行持久化的状态数据都需要保存在 FaaS Function 实例之外。&lt;/p&gt;

&lt;p&gt;毫无疑问，FaaS Function 天然就是无状态的，他提供的就是纯粹的从输入到输出的转换。虽然这不是什么新鲜事情，例如 &lt;a href=&#34;http://12factor.net/&#34; target=&#34;_blank&#34;&gt;12 Factor App&lt;/a&gt; 中也有一样的&lt;a href=&#34;http://12factor.net/processes&#34; target=&#34;_blank&#34;&gt;提议&lt;/a&gt;，但是这可能对一些应用架构产生巨大的影响。这些面向状态的 Function 需要用数据库或者分布式缓存（比如 Redis）以及网络文件/对象存储（比如 S3）来保存跨请求状态数据，或者要求请求中包含更多输入项目来进行状态的传递。&lt;/p&gt;

&lt;h4 id=&#34;运行时长&#34;&gt;运行时长&lt;/h4&gt;

&lt;p&gt;典型的 FaaS Function 都要对每次调用的时长进行限制。目前 AWS Lambda Function 的限制是五分钟，超时之后就会被终止。微软和谷歌的产品也有类似限制。&lt;/p&gt;

&lt;p&gt;这就意味着，有些长期运行的应用上提供的功能，可能并不能不加重构的适应 FaaS 环境，重构过程要把原来的单个任务拆分为多个协同工作的 FaaS Function。&lt;/p&gt;

&lt;h4 id=&#34;启动延时和冷启动&#34;&gt;启动延时和冷启动&lt;/h4&gt;

&lt;p&gt;Faas 平台在处理事件之前，首先需要初始化 Function 的实例，这是一个需要耗时的操作。即使是同一个 Function，他的初始化过程也可能受到众多因素的影响，产生数毫秒或数秒的差异。这看起来可是很糟糕，但是我们可以先放下这种不安，来看看 AWS Lambda 的例子。&lt;/p&gt;

&lt;p&gt;Lambda Function 的初始化并不是热启动（复用一个之前的事件中用过的 Lambda Funtion 及其所在容器）过程，也不是冷启动（创建一个新的容器实例、启动 Function 主进程之类的工作）。而冷启动是个毋庸置疑的缓慢过程。&lt;/p&gt;

&lt;p&gt;冷启动的延迟，受制于很多因素：用的什么语言，引用什么库，代码规模如何，Lambda 配置环境怎样，是否需要连接到 &lt;a href=&#34;https://aws.amazon.com/vpc/&#34; target=&#34;_blank&#34;&gt;VPC&lt;/a&gt; 资源等等。其中很多方面都是受开发者控制的，所以降低冷启动延时通常都是可以实现的。&lt;/p&gt;

&lt;p&gt;可以和冷启动耗时相提并论的另一个问题就是冷启动频度。假设一个函数在以每秒十个事件的速度运行，其中每个事件处理都需要消耗 50 毫秒，你可能每处理 10-20 万请求之后，会看到一次 Lambda 的冷启动。如果另一个函数每个小时处理一个事件，那么可能每个时间发生的时候你都会看到冷启动，这是因为 Amazon 会在几分钟之后释放不活动的 Lambda 实例。知道这一问题之后，你就会了解到针对你的案例，冷启动会产生什么影响，是否需要执行 &amp;ldquo;Keep alive&amp;rdquo; 之类的方法来防止冷启动。&lt;/p&gt;

&lt;p&gt;冷启动看起来很让人担心吧？这取决于你的应用的类型和行为模式。我从前在 Intent Media 的团队有一个 Java 实现（常见语言中启动最慢的一个）的异步消息处理 Lambda App，每天要处理几千万的消息，完全没有操心过这个应用的启动延迟问题。这说明如果你正在写一个低延迟的交易应用，可能并不想要使用基于云的 FaaS 系统，这个决策跟实现语言的关系不大。&lt;/p&gt;

&lt;p&gt;这种情况下，不论你是否认为对你的应用产生了影响，都还是应该按照生产环境的要求进行测试。这毕竟是一个持续创新的领域：如果你的案例无法通过测试，那么不妨过几个月再来一遍。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://blog.symphonia.io/learning-lambda-part-8-addfab6b460d&#34; target=&#34;_blank&#34;&gt;我的另一篇文章&lt;/a&gt;更深入的讨论了一下冷启动问题。&lt;/p&gt;

&lt;h4 id=&#34;api-网关&#34;&gt;API 网关&lt;/h4&gt;

&lt;p&gt;&lt;img src=&#34;images/ag.svg&#34; alt=&#34;ag.svg&#34; /&gt;&lt;/p&gt;

&lt;p&gt;之前我们提到过，无服务器技术有一个要素是 API 网关。API 网关是一个 HTTP 服务器，其中定义了路由和端点，每个路由都有相关联的用于处理路由的资源。在无服务器架构中，这些处理单元通常就是  FaaS Function。&lt;/p&gt;

&lt;p&gt;当 API 网关收到请求之后，会在配置中查找对应的路由条目，在 FaaS 路由的情况下，会使用原有的请求来调用对应的 FaaS Function。通常状况下，API 网关可以把 HTTP 请求映射为一种更简单的输入提供给 FaaS Function，或把整个 HTTP 请求作为 JSON 对象传递出去。FaaS Function 会执行自有逻辑，并把结果反馈给 API 网关，API 网关就会把执行结果还原为 HTTP 响应，并将响应返回给调用者。&lt;/p&gt;

&lt;p&gt;AWS 有自己的 API 网关（就叫 &lt;a href=&#34;https://aws.amazon.com/api-gateway/&#34; target=&#34;_blank&#34;&gt;API Gateway&lt;/a&gt;），其他服务商也提供类似的能力。Amazon 的 API 网关是一个 BaaS 服务，作为一个外部服务，他需要用户的配置，但是其供给和运行就无需用户操心了。&lt;/p&gt;

&lt;p&gt;除了纯粹的路由请求，API 网关可能还要执行认证、输入验证、返回码映射等功能。&lt;/p&gt;

&lt;p&gt;FaaS 场景下的 API 网关有个功能就是，用无服务器的方式创建 HTTP 前端的微服务，并提供伸缩、管理以及其他的 FaaS Function 功能。&lt;/p&gt;

&lt;p&gt;我第一次写这篇文章的时候，Amazon 的 API 网关还很不成熟。这些工具的进展是很迅速的。AWS API Gateway 这种组件还不是很”主流“，但是可以庆幸的是他们在持续进步，会越来越好。&lt;/p&gt;

&lt;h4 id=&#34;工具&#34;&gt;工具&lt;/h4&gt;

&lt;p&gt;刚才提到的成熟度问题也适用于 FaaS 自身。在 2016 年中，这些东西还很粗糙；而 2018，我们看到了可观的进步。我们希望这一趋势能够持续下去。&lt;/p&gt;

&lt;p&gt;另外一个例子就是 FaaS 世界中的开发体验。&lt;a href=&#34;https://webtask.io/&#34; target=&#34;_blank&#34;&gt;Auth0 Webtask&lt;/a&gt; 是首先提高开发体验优先级的案例。接下来是微软在 &lt;a href=&#34;https://azure.microsoft.com/en-us/services/functions/&#34; target=&#34;_blank&#34;&gt;Azure Functions&lt;/a&gt; 产品中也这样提出。Visual Studio 及其反馈功能，一直将开发者体验作为产品的要素，Azure Function 也不例外。本地除错、本地触发之类的功能非常独到。&lt;/p&gt;

&lt;p&gt;另外一个还急需增强的功能就是监控，下文会继续讨论。&lt;/p&gt;

&lt;h4 id=&#34;开源&#34;&gt;开源&lt;/h4&gt;

&lt;p&gt;好吧，目前我们主要讨论的都是商用工具和产品。这是我们使用这类服务的主流方法，但是这里同样也有开源项目的一席之地。&lt;/p&gt;

&lt;p&gt;开源 FaaS 工具和框架，尤其是流行的&lt;a href=&#34;https://github.com/serverless/serverless&#34; target=&#34;_blank&#34;&gt;无服务器框架&lt;/a&gt;，这些框架针对 AWS API 网关和 Lambda 而来，提供优于 AWS 提供的工具的使用体验。还有一种是提供跨服务商的抽象工具，这对部分用户来说是很有价值的。类似的工具包括 &lt;a href=&#34;https://github.com/claudiajs/claudia&#34; target=&#34;_blank&#34;&gt;Claudia&lt;/a&gt; 以及 &lt;a href=&#34;https://github.com/Miserlou/Zappa&#34; target=&#34;_blank&#34;&gt;Zappa&lt;/a&gt;。还有一个例子就是 &lt;a href=&#34;https://github.com/apex/apex&#34; target=&#34;_blank&#34;&gt;Apex&lt;/a&gt;，他的吸引力在于，让用户可以使用 Amazon 直接支持范围以外的语言开发 Lambda 应用。&lt;/p&gt;

&lt;p&gt;几大服务商在开源方面也不甘落后。AWS 自己的开发工具：SAM（&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html&#34; target=&#34;_blank&#34;&gt;Serverless Application Model&lt;/a&gt;）也是&lt;a href=&#34;https://github.com/awslabs/serverless-application-model&#34; target=&#34;_blank&#34;&gt;开源&lt;/a&gt;的。&lt;/p&gt;

&lt;p&gt;FaaS 的最大益处之一就是无需担心底层的计算资源（服务器、虚拟机甚至是容器）。但是如果用户偏要关注这些怎么办？例如有一些云供应商可能无法满足的安全需要，或者用户自己具备一些服务器。开源软件能够在这些场景下，让用户拥有自己的”有服务器的“ FaaS 平台么？&lt;/p&gt;

&lt;p&gt;是的，即使是这个方面也有不少活动。开源 FaaS 方面的早期领导者之一是 IBM（&lt;a href=&#34;https://openwhisk.apache.org/&#34; target=&#34;_blank&#34;&gt;OpenWhisk&lt;/a&gt;，现在已经是一个 Apache 项目了）以及（至少对我来说很诧异的一个）微软，他开放了 &lt;a href=&#34;https://azure.microsoft.com/en-us/services/functions/&#34; target=&#34;_blank&#34;&gt;Azure Function 平台&lt;/a&gt; 方面的很多东西。很多其他的自建 FaaS实现，会用各种理由建立在 Kubernetes 这样的容器平台之上。场上还有 &lt;a href=&#34;http://www.galacticfog.com/&#34; target=&#34;_blank&#34;&gt;Galactic Fog&lt;/a&gt;、&lt;a href=&#34;https://fission.io/&#34; target=&#34;_blank&#34;&gt;Fission&lt;/a&gt; 以及 &lt;a href=&#34;https://github.com/openfaas/faas&#34; target=&#34;_blank&#34;&gt;OpenFaaS&lt;/a&gt; 等值的关注的参赛选手。这是一个既庞大又快速的领域，建议关注 CNCF 的&lt;a href=&#34;https://github.com/cncf/wg-serverless&#34; target=&#34;_blank&#34;&gt;无服务器工作组&lt;/a&gt;来保持信息同步。&lt;/p&gt;

&lt;h3 id=&#34;什么不是无服务器&#34;&gt;什么不是无服务器&lt;/h3&gt;

&lt;p&gt;行文至此，我们已经了解到，无服务器架构就是两个概念的结合体：BaaS 和 FaaS。我们也对其能力进行了一点挖掘。无服务器架构的关键属性究竟是什么？为什么 S3 这么古老的技术我也称之为无服务器？可以阅读我的另一篇文章：&lt;a href=&#34;https://blog.symphonia.io/defining-serverless-part-1-704d72bc8a32&#34; target=&#34;_blank&#34;&gt;Defining Serverless&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;在我们开始进入微服务的得与失的讨论之前，我们可以在定义方面再多花一点时间，我们来定义一下，什么不是无服务器。&lt;/p&gt;

&lt;h4 id=&#34;和-paas-的对比&#34;&gt;和 PaaS 的对比&lt;/h4&gt;

&lt;p&gt;无服务器架构中的 FaaS Function 和 &lt;a href=&#34;http://12factor.net/&#34; target=&#34;_blank&#34;&gt;Twelve-Factor applications&lt;/a&gt; 非常类似，他是不是只是一个 &lt;a href=&#34;https://en.wikipedia.org/wiki/Platform_as_a_service&#34; target=&#34;_blank&#34;&gt;PaaS&lt;/a&gt; 的变体，像 &lt;a href=&#34;http://www.heroku.com/&#34; target=&#34;_blank&#34;&gt;Heroku&lt;/a&gt; 那样的？引用 Adrian Cockcroft 的&lt;a href=&#34;https://twitter.com/adrianco/status/736553530689998848&#34; target=&#34;_blank&#34;&gt;答案&lt;/a&gt;：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If your PaaS can efficiently start instances in 20ms that run for half a second, then call it serverless.&lt;/p&gt;

&lt;p&gt;需要运行半秒的 PaaS 应用，如果能够在 20 毫秒之中完成实例的启动，那么他就是无服务器。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;换句话说，多数 PaaS 应用并不适合在事件触发的情况下完成快速的启动和停止，而 FaaS 平台必须满足这一条件。&lt;/p&gt;

&lt;p&gt;如果我是一个优秀的十二军规应用开发人员，这些因素并不会影响我的架构和开发过程，但是对运维就大有影响了。既然我们都是 DevOps 工程师，我们对运维的考虑和对开发的考虑应该一样多，对吧？&lt;/p&gt;

&lt;p&gt;运维方面，FaaS 和 PaaS 的一个关键差异就是伸缩。一般情况下，PaaS 环境中需要思考如何伸缩，例如在 Heroku 中，需要运行多少 Dynos？在 FaaS 应用中，这一过程是完全透明的。即使你的 PaaS 应用中设置了自动伸缩，也不会到请求一级（除非是有很特别的流量模型），所以 FaaS 应用在成本控制方面更加有效。&lt;/p&gt;

&lt;p&gt;有了这样的好处，还需要 PaaS 么？这里有很多原因，但是工具可能是个大问题。有人使用 &lt;a href=&#34;https://en.wikipedia.org/wiki/Cloud_Foundry&#34; target=&#34;_blank&#34;&gt;Cloud Foundry&lt;/a&gt; 这样的设施来在混合云上提供通用的开发体验；迄今为止，FaaS 还没能达到这一成熟程度。&lt;/p&gt;

&lt;h4 id=&#34;和容器对比&#34;&gt;和容器对比&lt;/h4&gt;

&lt;p&gt;使用无服务器 FaaS 的一个原因就是避免在操作系统层面来管理应用进程。Heroku 这样的 PaaS 服务也提供了这样的能力，上面我们说过 PaaS 和无服务器 FaaS 的区别。目前还有一个流程的进程抽象就是容器了，&lt;a href=&#34;https://www.docker.com/&#34; target=&#34;_blank&#34;&gt;Docker&lt;/a&gt; 就是这类技术的代表。容器管理系统将进程从操作系统级别独立出来独立部署，&lt;a href=&#34;http://mesos.apache.org/&#34; target=&#34;_blank&#34;&gt;Mesos&lt;/a&gt; 和 &lt;a href=&#34;http://kubernetes.io/&#34; target=&#34;_blank&#34;&gt;Kubernetes&lt;/a&gt; 都风光无限。除此之外，我们还看到云供应商都在提供容器平台，例如 Amazon 的 &lt;a href=&#34;https://aws.amazon.com/ecs/&#34; target=&#34;_blank&#34;&gt;ECS&lt;/a&gt; 和 &lt;a href=&#34;https://aws.amazon.com/eks/&#34; target=&#34;_blank&#34;&gt;EKS&lt;/a&gt;、&lt;a href=&#34;https://cloud.google.com/container-engine&#34; target=&#34;_blank&#34;&gt;Google Container Engine&lt;/a&gt;，都和无服务器 FaaS 一样，让团队避免管理自己的服务器。所有工作都围绕容器展开，那么还需要有 FaaS 么？&lt;/p&gt;

&lt;p&gt;原则上说，这一区别跟上面提到的 PaaS 是一样的，&lt;strong&gt;无服务器 FaaS 中的伸缩是自动管理的、透明的，并具备良好的粒度&lt;/strong&gt;，这是决定于自动的资源供给和分配的。容器平台依旧需要在集群端对尺寸和形态进行管理。&lt;/p&gt;

&lt;p&gt;另外我认为容器平台虽然在快速发展，但是目前还不够成熟和稳定。当然这并不是说 FaaS 就成熟了，选择你喜欢的就可以了。&lt;/p&gt;

&lt;p&gt;另外还有重要的一点就是容器平台也有了自己的伸缩系统。Kubernetes 有了内置的 &lt;a href=&#34;http://kubernetes.io/docs/user-guide/horizontal-pod-autoscaling/&#34; target=&#34;_blank&#34;&gt;HPA&lt;/a&gt;，Amazon 的 &lt;a href=&#34;https://aws.amazon.com/fargate/&#34; target=&#34;_blank&#34;&gt;Amazon Fargate&lt;/a&gt; 也承诺提供”无服务器容器“。&lt;/p&gt;

&lt;p&gt;正如我们所见，两种系统之间在管理和伸缩方面的差距并不大，因此这种选择更多的是要看应用的类型。例如 FaaS 是事件驱动类型的首选，容器更适合处理同步请求的多端点服务。我认为短期之内很多应用和团队会同时使用两种架构，同时二者的融合会令人相当期待。&lt;/p&gt;

&lt;h4 id=&#34;noops&#34;&gt;NoOps&lt;/h4&gt;

&lt;p&gt;无服务器可能意味着 &amp;ldquo;No sysadmin&amp;rdquo;，但是不等于 &amp;ldquo;No Ops&amp;rdquo;，这取决于你在无服务器这个兔子洞里钻了多深。&lt;/p&gt;

&lt;p&gt;&amp;ldquo;Ops&amp;rdquo; 的含义不仅仅在于服务器管理。他还（至少）意味着监控、部署、安全、网络、支持还有一部分的产品排错和系统伸缩等。在无服务器应用中，这些问题一样存在，还是需要一些策略来完成这些事情。无服务器体系如此新锐，在这方面的难度会更高。&lt;/p&gt;

&lt;p&gt;系统管理还是存在的，只是外包给了供应商而已，这事的本身没有什么对错。外包生意满街都是，做法决定结果。不管怎样都应该知道，抽象有可能会不足以完成任务，要知道在某个地方，还是有人类系统管理员正在给你的应用提供支持。&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://twitter.com/mipsytipsy&#34; target=&#34;_blank&#34;&gt;Charity Majors&lt;/a&gt; 在第一次无服务器会议上，做了&lt;a href=&#34;https://www.youtube.com/watch?v=wgT5f0eBhD8&#34; target=&#34;_blank&#34;&gt;很精彩的演讲&lt;/a&gt;。（可以阅读她的两篇文章：&lt;a href=&#34;https://charity.wtf/2016/05/31/wtf-is-operations-serverless/&#34; target=&#34;_blank&#34;&gt;WTF is operations&lt;/a&gt; 以及 &lt;a href=&#34;https://charity.wtf/2016/05/31/operational-best-practices-serverless/&#34; target=&#34;_blank&#34;&gt;Operational Best Practices&lt;/a&gt;）。&lt;/p&gt;

&lt;h4 id=&#34;存储过程即服务&#34;&gt;存储过程即服务&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;来自 &lt;a href=&#34;https://twitter.com/skamille&#34; target=&#34;_blank&#34;&gt;Camille Fournier&lt;/a&gt; 的 &lt;a href=&#34;https://twitter.com/skamille/status/719583067275403265?ref_src=twsrc%5Etfw&amp;amp;ref_url=https%3A%2F%2Fmartinfowler.com%2Farticles%2Fserverless.html&#34; target=&#34;_blank&#34;&gt;Twitter&lt;/a&gt;：
I wonder if serverless services will become a thing like stored procedures, a good idea that quickly turns into massive technical debt。&lt;/p&gt;

&lt;p&gt;我在想，无服务器应用会不会像存储过程一样——一个好主意迅速的变成了巨额的技术债。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我还见过一种 FaaS 的状况就是：“存储过程即服务”。我想这是因为，很多 FaaS Function 的例子都是和附着于数据库的代码碎片。如果我们做的事情都是这样的，那这个名字就很恰当了，但是这其实只是一个 FaaS 的子集，我觉得不应该把这种情况当做 FaaS 的全部。&lt;/p&gt;

&lt;p&gt;让 FaaS 离开存储过程的老路是个值得考虑的问题。从存储过程获取的经验，放到 FaaS 的语境中也是很有价值的。存储过程是这样的：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;通常需要用厂商指定的语言，至少是厂商指定的语言框架和扩展。&lt;/li&gt;
&lt;li&gt;必须在数据库中执行，不易测试。&lt;/li&gt;
&lt;li&gt;版本管理难于进行，很少被看做是一个常规应用。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;并不是所有的存储过程都有所有这些问题，但是存储过程中至少会出现其中一个问题。我们看看在 FaaS 中会怎样：&lt;/p&gt;

&lt;p&gt;在 FaaS 中，在我目前看来问题 1 并不存在，可以不必担心了。&lt;/p&gt;

&lt;p&gt;对问题 2 来说，我们只和代码打交道，很明显单元测试方面和其他代码并无区别。集成测试很明显会有些不同，后面我们会继续讨论。&lt;/p&gt;

&lt;p&gt;而问题 3，FaaS 只有代码，天生的支持版本控制。目前应用打包有点困扰，不过这方面也在逐渐成熟，例如 Amazon 的 &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html&#34; target=&#34;_blank&#34;&gt;无服务器应用模型（SAM）&lt;/a&gt; 以及早前提到的无服务器框架。2018 年初，Amazon 还启动了一个 &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html&#34; target=&#34;_blank&#34;&gt;无服务器应用仓库（SAR）&lt;/a&gt;，给在 AWS Serverless 服务之上构建应用的组织提供应用和应用组件的分发支持（进一步了解 SAR，可以阅读我的文章：&lt;a href=&#34;https://blog.symphonia.io/examining-the-aws-serverless-application-repository-9ef316e2fd4&#34; target=&#34;_blank&#34;&gt;Examining the AWS Serverless Application Repository&lt;/a&gt;）。&lt;/p&gt;

&lt;h2 id=&#34;收益&#34;&gt;收益&lt;/h2&gt;

&lt;p&gt;前面我们主要在定义和解释无服务器架构的概念。现在我们讨论一下，如此的应用设计，带来的好处和问题。没有严肃的评估和权衡，是不应该做出任何决策的。&lt;/p&gt;

&lt;p&gt;接下来我们就看看无服务器的好处。&lt;/p&gt;

&lt;h3 id=&#34;降低运维成本&#34;&gt;降低运维成本&lt;/h3&gt;

&lt;p&gt;最大程度的简化之后，可以说无服务器框架是一种外包方案——将服务器管理、数据库管理甚至应用逻辑外包出去。由于使用的是一个可能有很多其他用户都在使用的预定义服务，这样就产生一种&lt;a href=&#34;https://en.wikipedia.org/wiki/Economies_of_scale&#34; target=&#34;_blank&#34;&gt;规模效应&lt;/a&gt;：服务商正在运行上千类似的数据库实例，因此用户可以以相对更低的价格来获得数据库服务。&lt;/p&gt;

&lt;p&gt;主要从两个方面降低了成本：首先是从共享基础设施（例如硬件、网络）方面；第二是人力成本方面：同等规模的情况下，外包的无服务器系统，相对于自行开发和支撑的应用话费要低。&lt;/p&gt;

&lt;p&gt;这种获益，跟从前我们从 IaaS 和 PaaS 之中的获益并没有太大的差异。但是我们可以分别从 BaaS 和 FaaS 两方面来扩展这方面的讨论。&lt;/p&gt;

&lt;h3 id=&#34;baas-降低开发成本&#34;&gt;BaaS：降低开发成本&lt;/h3&gt;

&lt;p&gt;IaaS 和 PaaS 都是建立在服务器和操作系统的标准化基础上的。类似的，BaaS 的基础就是应用程序组件的标准化。&lt;/p&gt;

&lt;p&gt;认证就是一个好例子。很多应用都开发了自己的认证功能，一般包含了注册、登录、密码管理以及和其他认证系统的集成等功能。总体上看，绝大多数应用的这种功能都是类似的，&lt;a href=&#34;https://auth0.com/&#34; target=&#34;_blank&#34;&gt;Auth0&lt;/a&gt; 这样的服务应运而生，我们可以直接把这样完备的功能集成到我们的应用中，不再需要自行开发。&lt;/p&gt;

&lt;p&gt;另外一个例子就是 &lt;a href=&#34;https://firebase.google.com/docs/database/&#34; target=&#34;_blank&#34;&gt;Firebase&lt;/a&gt; 这样的 BaaS 的数据库。有些移动应用团队有让客户端直接和服务端数据库打交道的需要。BaaS数据库消除了大部分数据库管理开销，迎合无服务器应用程序的需求，为不同类型用户提供了各自合理的授权机制。&lt;/p&gt;

&lt;p&gt;因为大家各自的不同背景，可能会有人不以为然（后面也会提到一些问题），但毋庸置疑，不少成功企业都已经在几乎没有自己的服务端代码的情况下交付了成功的产品。在第一次无服务器会议上，&lt;a href=&#34;http://www.slideshare.net/ServerlessConf/joe-emison-10x-product-development&#34; target=&#34;_blank&#34;&gt;Joe Emison 给出了一些例子&lt;/a&gt;。&lt;/p&gt;

&lt;h3 id=&#34;faas-伸缩成本&#34;&gt;FaaS：伸缩成本&lt;/h3&gt;

&lt;p&gt;前面写过，无服务器 FaaS 有个有趣的事情就是：水平伸缩是完全自动化的、弹性的，当然也是由服务商提供的。这个特点有很多好处，不过最最根本的好处就是：只为必要的用量来买单，例如 AWS Lambda （的计费能够）精确到 100 毫秒。随用户的流量规模和类型不同，这可能会带来巨大的经济效益。&lt;/p&gt;

&lt;h4 id=&#34;例-偶发性请求&#34;&gt;例：偶发性请求&lt;/h4&gt;

&lt;p&gt;假设你有一个服务应用在运行，这个应用每分钟消耗 50 毫秒来处理一个请求，一个小时内的 CPU 消耗是 0.1%。如果这个应用部署在自己的独立主机上，很明显会是非常低效的。上千个类似的应用来共享这台机器才合理。&lt;/p&gt;

&lt;p&gt;无服务器 FaaS 就能更有效的处理这种情况，从而帮助用户降低成本。例如上面提到的应用，每分钟花费 100 毫秒的 CPU 时间，0.15% 的占用率，用户只要为此付款即可。&lt;/p&gt;

&lt;p&gt;如下几方面的好处：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;一些微服务类的应用，可能只有很小的负载需求，提供了根据逻辑/域拆分组件的能力，如此细粒度的应用可能在运维成本上是让人难以承受的。&lt;/li&gt;
&lt;li&gt;如果一个公司或者团队希望尝试一些新东西，如果使用 FaaS 的话，只需要非常小的运维成本。如果工作负载非常小，那么供应商的赠金可能就足够使用了。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;例-间歇型流量&#34;&gt;例：间歇型流量&lt;/h4&gt;

&lt;p&gt;另一个例子，假设一个业务的流量曲线非常陡峭，可能基线流量是每秒 20 请求，但是每 5 分钟会受到一次持续 10 秒钟的每秒 200 请求（10 倍）的冲击。我们继续假设，你的服务器的最大性能可以满足你的性能基线要求，但是在流量高峰期间，也不想要降低性能表现，怎么办呢？&lt;/p&gt;

&lt;p&gt;传统环境下，虽说高峰时段只占总量的不足 4%，还是需要增加到 10 倍的硬件数量，来应对流量高峰。自动伸缩可能在这里也不太合适，新服务器启动完成之后，可能高峰时段已经结束了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;images/inconsistent-traffic-pattern.png&#34; alt=&#34;inconsistent-traffic-pattern.png&#34; /&gt;&lt;/p&gt;

&lt;p&gt;无服务器的 FaaS 在这里就如鱼得水了。如果流量仅是数量不同，原则上你不需要做什么不同的事情，只需要支付高峰时段的额外计算费用即可。&lt;/p&gt;

&lt;p&gt;显而易见，这里我们用的例子比较极端，从而凸显了节约成本的效果，但是重点在于，从伸缩的视角来看，除非你有非常稳定的流量模式，能够持续使用服务器的稳定负载，否则 FaaS 始终是一个节约成本的手段。&lt;/p&gt;

&lt;p&gt;上文中一个值得注意的提醒：如果你的流量是一致的并且持续的以较好的利用率运行在服务器上，那可能看不到什么成本节约，FaaS 方案甚至可能要消耗更多的成本。建议进行计算，来评估和比较不同方案的差异。&lt;/p&gt;

&lt;p&gt;推荐阅读 Gojko Adzic 和 Robert Chatley 的 &lt;a href=&#34;http://www.doc.ic.ac.uk/~rbc/papers/fse-serverless-17.pdf&#34; target=&#34;_blank&#34;&gt;Serveless Computing: Economic and Architectural Impact（无服务器计算：经济和架构）&lt;/a&gt;。&lt;/p&gt;

&lt;h4 id=&#34;优化才是成本节约的根源&#34;&gt;优化才是成本节约的根源&lt;/h4&gt;

&lt;p&gt;FaaS 的成本问题，还有一个有趣的方面就是：性能优化不仅仅提高了应用的性能，还会直接的对运维成本造成影响，具体的影响范围要看服务商的收费方案粒度和形式。例如一个应用原本需要一秒钟来处理事件。通过优化之后，处理事件降低到 200 毫秒，（AWS Lambda）上，会看到节省了 80% 的计算资源。&lt;/p&gt;

&lt;h3 id=&#34;简化运维管理&#34;&gt;简化运维管理&lt;/h3&gt;

&lt;p&gt;下面的内容需要强调——无服务器方案中依旧存在一些运维工作，但是会有一些改善。&lt;/p&gt;

&lt;p&gt;在无服务器 BaaS 中，很明显运维管理要比其他架构简单：支撑更少的组件，就相当于更少的工作。&lt;/p&gt;

&lt;p&gt;而 FaaS 中，会有很多方面的影响。下面我会尝试深入阐述几个方面。&lt;/p&gt;

&lt;h4 id=&#34;faas-的伸缩优势不仅在于基础设施的成本&#34;&gt;FaaS 的伸缩优势不仅在于基础设施的成本&lt;/h4&gt;

&lt;p&gt;刚刚讲过伸缩的问题，FaaS 方案的优势不仅在于节约计算成本，还因为自动伸缩的能力降低了运维管理的成本。&lt;/p&gt;

&lt;p&gt;最好的情况下，如果你的伸缩过程是人工的——也就是说由工程师在服务器集群中增加或删除实例——在 FaaS 中就可以忘记这些，而是让供应商来完成这些任务。&lt;/p&gt;

&lt;p&gt;即使是在非 FaaS 架构中实现了自动伸缩，还是需要设置和管理的；而在 FaaS 中这就不需要了。&lt;/p&gt;

&lt;p&gt;类似的，因为伸缩是由供应商根据请求/事件来完成了，耗尽内存或者其他性能瓶颈发生之前，&lt;strong&gt;不用操心可以同时处理多少并发请求了&lt;/strong&gt;，至少 FaaS 的部分不用。而非 FaaS 或者下游数据库的部分则要谨慎的根据负载情况进行性能方面的调整。&lt;/p&gt;

&lt;h4 id=&#34;降低打包和部署的复杂度&#34;&gt;降低打包和部署的复杂度&lt;/h4&gt;

&lt;p&gt;FaaS Function 的打包和部署，相对于部署整个服务来说是比较简单的。只要把代码打个 Zip 包，然后上传就可以了。无需 Puppet/Chef，没有启动停止的 Shell 脚本，也不用考虑部署一个还是多个容器。如果是刚起步，甚至都不用打包任何东西——可以在服务商的控制台上直接编写代码（这种方式当然是不推荐的）。&lt;/p&gt;

&lt;p&gt;这个过程的重要性无需多言，对有些团队来说——这个好处非常重要：&lt;strong&gt;完全 FaaS 系统是不需要系统管理的&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;PaaS 方案有类似的部署有点，但是我们前面做过比较：相对于 PaaS 来说，FaaS 的伸缩能力具有巨大优势。&lt;/p&gt;

&lt;h4 id=&#34;上市时间和持续实验&#34;&gt;上市时间和持续实验&lt;/h4&gt;

&lt;p&gt;上面说的运维管理方面的优势，工程师们都很了解。但是对业务方面，这又意味着什么呢？&lt;/p&gt;

&lt;p&gt;很明显的一点就是成本：前面也说过：运维方面时间消耗减少，就等于降低了人员需求。但是我认为还有个更大的好处就是&lt;a href=&#34;https://en.wikipedia.org/wiki/Time_to_market&#34; target=&#34;_blank&#34;&gt;上市时间&lt;/a&gt;。我们的团队和产品日益精益和敏捷，我们希望不断尝鲜并且快速更新现有系统。持续交付让我们可以在项目稳定的基础上实现快速迭代，从而降低了从创意到部署的门槛，可以用最小代价来进行新的尝试。&lt;/p&gt;

&lt;p&gt;成本优势是无服务器技术的最容易表达的优势，时间的缩减尤其让我激动。他让&lt;a href=&#34;https://www.youtube.com/watch?v=mzjhEZLTEpM&#34; target=&#34;_blank&#34;&gt;持续实验&lt;/a&gt;成为一种可能，对于软件企业来说，这是真正的革命。&lt;/p&gt;

&lt;h4 id=&#34;更环保的计算&#34;&gt;更环保的计算？&lt;/h4&gt;

&lt;p&gt;最近十几年，数据中心的规模和数量都有了巨大的增长，数据中心所需的能源和物料消耗也都极为庞大。谷歌、苹果都在尝试将数据中心建设在可再生能源的附近，从而减少对化石能源的消耗。&lt;/p&gt;

&lt;p&gt;空转的服务器在消耗能量——这是我们需要更多更大数据中心的主要原因之一：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;一年中，商业和企业数据中心中的典型服务器所输出的计算能力一般在其最大输出能力的 5% 到 15% 之间。
&amp;ndash;福布斯&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这非常低效，造成巨大的环境问题。&lt;/p&gt;

&lt;p&gt;基于云的基础设施架构在一定程度上环节了这种问题——公司已经不再需要为低概率需求购买并长期闲置服务器，而是按需够买。然而这一问题可能因此进一步恶化：服务器易于获得却难于管理，造成大量的服务器因疏于管理而闲置。&lt;/p&gt;

&lt;p&gt;不管我们的基础设施用的是自有服务器、IaaS 还是 PaaS 方案，我们始终需要根据应用来做经年累月的决策活动。容量问题需要谨慎对待，所以过度规划其实是个必然情况。而无服务器架构中，我们不需要自行完成这一过程了——服务商需要根据我们的业务量实时的满足我们的性能需求。FaaS 供应商自身则需要通过对所有客户的数据的聚合，来完成自己的容量规划。&lt;/p&gt;

&lt;p&gt;这种区别的结果就是跨数据中心的资源的高效利用，相对于传统的容量管理方案，显著的降低了环境的不良影响。&lt;/p&gt;

&lt;h2 id=&#34;缺陷&#34;&gt;缺陷&lt;/h2&gt;

&lt;p&gt;所以亲爱的读者们，好话已经说尽，然而世事无绝对，接下来我们就要面对现实，看看无服务器体系的为难之处了。&lt;/p&gt;

&lt;p&gt;无服务器架构的可爱之处是显而易见的，但是这其中还是有一些妥协的需要。有些问题是与生俱来的，通过过程控制无法完全杜绝，需要始终保持警惕；另有一些是和当前的实现有关的，随着时间的推移，最终应该会得到解决。&lt;/p&gt;

&lt;h3 id=&#34;先天不足&#34;&gt;先天不足&lt;/h3&gt;

&lt;h4 id=&#34;服务商控制&#34;&gt;服务商控制&lt;/h4&gt;

&lt;p&gt;任何的外包活动，都会把一些系统移交给第三方供应商进行控制。这种失控有时候会爆发出来，例如系统宕机、意外的限制、成本变化、功能缺失、API 强制升级等等等等。前面提到的 Charity Majors，在 &lt;a href=&#34;https://charity.wtf/2016/05/31/operational-best-practices-serverless/&#34; target=&#34;_blank&#34;&gt;OPERATIONAL BEST PRACTICES #SERVERLESS&lt;/a&gt; 的妥协部分对这个问题做了详细阐述：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;供应商如果够聪明的话，会在用法上加入很强的限制条款，这样才能更方便的实现可靠的目标。用户所享有的弹性和选项就是混乱和脆弱的来源。当不同客户的喜好发生冲突的时候，很明显供应商会选择多数的一方来进行满足。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&#34;多租户问题&#34;&gt;多租户问题&lt;/h4&gt;

&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Multitenancy&#34; target=&#34;_blank&#34;&gt;多租户&lt;/a&gt;指的是多个不同的客户（或者租户）在同一服务器上或同样的托管应用中运行多个软件实例的情况，（多租户策略）是实现我们前面提到的规模效益的关键点。服务商会努力让客户感觉自己是系统的唯一用户，一般来说，好的供应商也都成功的完成了这一工作。但是世事无绝对，有时候有些多租户方案会出现一些安全问题（一个客户能访问到别的客户的数据）、健壮性问题（一个客户的软件故障导致了其他用户的故障）以及性能问题（高负载客户拖慢了其他用户的应用）。&lt;/p&gt;

&lt;p&gt;这些问题不是无服务器系统的专利——所有多租户系统都有这种可能。AWS Lambda 现在很成熟，我们大概不会看到她出现这种问题，但是不管使用的是 AWS 还是其他供应商，都应该注意这些 Issue，防止部分服务出现问题。&lt;/p&gt;

&lt;h4 id=&#34;供应商锁定&#34;&gt;供应商锁定&lt;/h4&gt;

&lt;p&gt;很有可能出现的情况就是，正在使用某个厂商的一个无服务器功能，在其他厂商的实现可能会是不同的。如果想要更换成其他厂商，可能需要更新运维工具（部署、监控等），还可能需要变更你的代码（满足不同的 FaaS 界面），更有甚者，架构设计可能也无法幸免于难，需要适应不同供应商的实现方式。&lt;/p&gt;

&lt;p&gt;即使系统中存在能够平滑迁移的部分，恐怕还是会被其他的架构组件所影响。如果你使用的是 AWS Lambda，用来响应一个 AWS Kinesis 消息总线的事件。&lt;a href=&#34;https://aws.amazon.com/lambda/&#34; target=&#34;_blank&#34;&gt;AWS Lambda&lt;/a&gt;、&lt;a href=&#34;https://cloud.google.com/functions/docs/&#34; target=&#34;_blank&#34;&gt;Google Cloud Function&lt;/a&gt; 以及 &lt;a href=&#34;https://azure.microsoft.com/en-us/services/functions/&#34; target=&#34;_blank&#34;&gt;Microsoft Azure Function&lt;/a&gt; 之间的差异并不大，但是很难把后面两个厂商的东西迁移到 AWS Kinesis stream 上。这说明了，从一个方案到另一个方案的迁移或者说适配，必须把整个基础设施同步迁移才可能完成。&lt;/p&gt;

&lt;p&gt;很多人对此感觉惊讶——如果想要换个供应商，可能要做很多事情。这是因为有些用户采用了一种称为“多云”的方式，这种方式把云厂商进行抽象和隔离，从而统一进行开发和运维。一般来说这比单云方式要昂贵。所以虽然供应商锁定是个显而易见的弱势，我仍然推荐选择一个喜欢的供应商，尽可能的使用它的能力。我的另一篇文章 &lt;a href=&#34;https://blog.symphonia.io/on-serverless-multi-cloud-and-vendor-lock-in-da930b3993f&#34; target=&#34;_blank&#34;&gt;On Serverless, Multi-Cloud, and Vendor Lock In&lt;/a&gt; 中对这个问题进行了更多的探讨。&lt;/p&gt;

&lt;h4 id=&#34;安全问题&#34;&gt;安全问题&lt;/h4&gt;

&lt;p&gt;在采用无服务器方法的过程中，会遇到很多安全方面的挑战。下面有一个非常简略的列表——还有很多这方面的问题需要读者自行注意。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;每个无服务器供应商都给你的系统中增加了不同的安全实现。这可能增大了恶意用户的接触机会，可能也会提高被攻击的可能性。&lt;/li&gt;
&lt;li&gt;如果直接从移动客户端使用 BaaS 数据库，那么就失去了传统应用访问数据库的那种服务端的安全隔离机制。这不是个致命问题，但是对应用的设计和开发过程需要多加注意。&lt;/li&gt;
&lt;li&gt;组织在采用无服务器方法的过程中，读者可能会受到很大冲击。每个 Function 可能有额外的问题需要解决。例如 AWS 的 Lambda 中的每个 Function 都需要配置一个很容易搞错的 &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/access-control-identity-based.html&#34; target=&#34;_blank&#34;&gt;IAM Policy&lt;/a&gt;。这是个无法忽视的难题。AWS 的生产环境中的 IAM 管理需要仔细考量。&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&#34;客户平台之间的重复逻辑&#34;&gt;客户平台之间的重复逻辑&lt;/h4&gt;

&lt;p&gt;在一个完全的 BaaS 架构中是没有服务端逻辑的——所有都在客户端。如果这是你的第一个客户端应用，那没什么问题，但是很快你需要做下一个平台了，那就需要为你的逻辑重新做一个实现——如果是传统架构的话，本来不需要如此重复。假设在这种系统中试用了一个 BaaS 数据库。所有的客户端应用（可能包括 Web、原生 iOS、以及原生的 Android）都需要和这个数据库打交道，必须学会数据库和应用之间的沟通方式。&lt;/p&gt;

&lt;p&gt;还有，如果想要迁移到一个新的数据库，可能要把所有客户端的代码都做出相应的迁移操作。&lt;/p&gt;

&lt;h4 id=&#34;服务端优化的缺失&#34;&gt;服务端优化的缺失&lt;/h4&gt;

&lt;p&gt;一个完全的 BaaS 架构中，就没有在服务端为客户端进行优化的可能性了。&lt;a href=&#34;http://samnewman.io/patterns/architectural/bff/&#34; target=&#34;_blank&#34;&gt;Backend for Frontend&lt;/a&gt; 模式中，对系统服务器底层进行了抽象和屏蔽，这样客户端就可以更快（在移动应用中更省电）的完成操作。但是全 BaaS 架构中就没有这个便利了。&lt;/p&gt;

&lt;p&gt;这一条和前面一条缺陷的起因是一样的，所有逻辑都在客户端实现，而后端则是由服务商提供的。如果使用一些轻量级的服务端框架用来支撑部分逻辑，可能会降低这种问题造成的影响。&lt;/p&gt;

&lt;h4 id=&#34;在-faas-中-不存在服务端状态&#34;&gt;在 FaaS 中，不存在服务端状态&lt;/h4&gt;

&lt;p&gt;在说完 BaaS 的缺陷之后，我们来谈谈 FaaS，之前我提到过：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FaaS Function 在本地（服务器、绑定实例）状态方面有着严格的限制，这里说的状态包括内存中的变量、本地盘中的数据等。这些存储都是可用的，但是这些状态信息在多次调用之间是没有持久化方面的保障的，也不能假设一次调用中保存的状态会在另一次调用中生效。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这一假设的原因是在 FaaS 中我们通常没有办法控制在服务端中我们的容器/服务器的启动或者停止。&lt;/p&gt;

&lt;p&gt;另外我还说过，一个解决本地状态的变通方法就是十二军规中的第六条：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;12 军规应用的进程是无状态的，不分享任何东西。所有的需要持久化的数据必须存储在一个有状态的后端服务中，这一服务通常是数据库。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Heroku 推荐这种方式，但是在 Heroku 中可以做些变通——这是因为用户能够控制 Heroku Dynos 的启动和停止。如果是 FaaS，就无法绕过了。&lt;/p&gt;

&lt;p&gt;所以在 FaaS 中，状态既然不能保存在内存里，那该如何处置？上面提到的方法是使用数据库，例如一个快速的 NoSQL 数据库或者进程外缓存（Redis 之类），或者外部的对象/文件存储（比如说 S3）都是可选的解决方法。但是这些方法都比内存或者本机持久化要慢得多。还是应该慎重考虑，你的应用是否适合如此操作。&lt;/p&gt;

&lt;p&gt;这个情况引发的另外一个忧虑就是内存内缓存。很多应用都会把外部读取的大型数据源的数据保存在内存缓存中。你可能会使用 &lt;a href=&#34;http://samnewman.io/patterns/architectural/bff/&#34; target=&#34;_blank&#34;&gt;Ehcache&lt;/a&gt; 之类的技术来访问引用数据表。又或者在使用 HTTP 服务的时候，因为 HTTP 头指定了缓存，这种情况下你的 HTTP 客户端也会使用本地缓存。&lt;/p&gt;

&lt;p&gt;FaaS 不允许使用本地缓存，这种情况适用于频繁使用的 Function。例如 AWS Lambda，我们预期一个 Function 实例持续运行几个小时，没几分钟至少使用一次。这意味着我们可以使用 Lambda 提供的（可配置的）3GB 内存或者 500 GB 的本地 &amp;ldquo;/tmp&amp;rdquo;，对一些缓存来说这可能不太够用。否则你可能不该假设进程内缓存的存在，转向使用一些低延迟的外部缓存，例如 Redis 或者 Memcached。当然这需要额外的工作，可能无法满足对应用效率的需要。&lt;/p&gt;

&lt;h3 id=&#34;实现的缺陷&#34;&gt;实现的缺陷&lt;/h3&gt;

&lt;p&gt;前面描述了一些 Serverless 的先天问题。我们看看一些缓解这些问题的变通方案。注意只是缓解，不是解决。&lt;/p&gt;

&lt;p&gt;剩下的缺点主要是现有的技术限制，随着厂商和社区的持续努力，这些问题会逐渐解决。事实上和本文的第一个版本比较来说，这个列表的已经缩短了许多。&lt;/p&gt;

&lt;h4 id=&#34;配置&#34;&gt;配置&lt;/h4&gt;

&lt;p&gt;我在编写本文的第一版的时候，AWS 在 Lambda 中提供了很少的配置能力。现在我欣喜的看到，这个问题已经解决了。如果你使用其他的平台，这方面的能力一定要检查一下。&lt;/p&gt;

&lt;h5 id=&#34;给自己一个-dos-拒绝服务攻击&#34;&gt;给自己一个 DoS（拒绝服务攻击）&lt;/h5&gt;

&lt;p&gt;“买者自负责任”，这一原则在 FaaS 交易中非常重要。AWS Lambda 对 Function 有一个指定时间内并行数量的限制。如果这个限制是 1000，那么就说明允许这个 Function 同时运行 1000 个实例。如果需要超过这个限制，那么就可能得到异常、排队或者运行缓慢等后果。&lt;/p&gt;

&lt;p&gt;这里的问题就是这个限制在整个 AWS 账号内都有效的。有些组织在生产和测试环境上使用同一个账号。这样也就意味着如果组织内有人做了一个压力测试——试着执行 1000 个并发的 Lambda Function，就成了一个针对生产环境的 &lt;a href=&#34;https://en.wikipedia.org/wiki/Denial-of-service_attack&#34; target=&#34;_blank&#34;&gt;DoS&lt;/a&gt; 了。&lt;/p&gt;

&lt;p&gt;就算开发和生产用的是不同的 AWS 账号，生产环境中一个超载的 Lambda（例如处理客户的批量上传）可能会让其他的实时 API 变得无法访问。&lt;/p&gt;

&lt;p&gt;Amazon 在这方面使用&lt;a href=&#34;https://blog.symphonia.io/aws-lambda-reserved-concurrency-f2c3a32b9f1d&#34; target=&#34;_blank&#34;&gt;保留并行&lt;/a&gt;的方式提供了一些保护。这种办法让用户可以限制一个 Lambda Function 的并行数量，这样就不会影响到账号中的其他功能。然而这个功能缺省并不会启动的，它需要谨慎的管理才能使用。&lt;/p&gt;

&lt;h5 id=&#34;执行时长&#34;&gt;执行时长&lt;/h5&gt;

&lt;p&gt;本文最初提到过，AWS Lambda 函数如果运行时间超过 5 分钟，就会退出，这一规定已经执行了几年，目前没有迹象表明 AWS 会修改这一限制。&lt;/p&gt;

&lt;h5 id=&#34;启动延迟&#34;&gt;启动延迟&lt;/h5&gt;

&lt;p&gt;前面提到过冷启动，还引用了&lt;a href=&#34;https://blog.symphonia.io/learning-lambda-part-8-addfab6b460d&#34; target=&#34;_blank&#34;&gt;我在这方面的一篇文章&lt;/a&gt;。AWS 在这方面做出了一些改进，但是问题依然存在，尤其是触发一个 JVM 实现的函数、或者是需要 VPC 资源的情况下。这方面的改进超出了本文范畴，就不再赘述了。&lt;/p&gt;

&lt;p&gt;AWS Lambda 已经提到很多了，我想其他的供应商同样会有一些不够漂亮的实现。&lt;/p&gt;

&lt;h5 id=&#34;测试&#34;&gt;测试&lt;/h5&gt;

&lt;p&gt;基于前面提到的理由，Serverless 应用的单元测试是相对简单的：所有代码都“只是代码”，多数情况下，这里没有什么必须包含的库或者必须实现的接口。&lt;/p&gt;

&lt;p&gt;Serverless 应用的集成测试就不简单了。BaaS 的世界里，要依赖外部的系统，而不再是自己的数据库。所以集成测试也要用外部系统么？如果答案是肯定的，那么这些外部系统有提供测试的能力么？能够轻松的管理状态么？外部服务商会为压力测试提供不同的计费策略么？&lt;/p&gt;

&lt;p&gt;如果要抛开外部服务独立进行集成测试，那么模拟环境的仿真程度如何？如果供应商没有提供模拟环境，用户可能自己开发么？&lt;/p&gt;

&lt;p&gt;FaaS 方面虽然会好一些，但是也有同样的问题。现在已经有办法在本地运行 Lambda 以及 Azure 的 Faas 函数。然而本地环境是不可能完全模拟云端的；依赖本地的 FaaS 环境我是很不推荐的。实际上我认为应该用正式环境运行自动的集成测试，至少是作为&lt;a href=&#34;https://martinfowler.com/bliki/DeploymentPipeline.html&#34; target=&#34;_blank&#34;&gt;部署管线&lt;/a&gt;的一部分，本地开发仅应用于开发的交互和除错。本地测试环境在持续的改进过程中，例如 &lt;a href=&#34;https://github.com/awslabs/aws-sam-cli&#34; target=&#34;_blank&#34;&gt;SAM CLI&lt;/a&gt; 就为 Lambda HTTP API 应用提供快速反馈的能力。&lt;/p&gt;

&lt;p&gt;在执行云端的集成测试之前，还应该注意前面说过的跨账号执行限制。为了更好的完成这一任务，可能需要把测试活动和生产账号进行隔离，或者更细粒度的账号管理也不算过分。&lt;/p&gt;

&lt;p&gt;在云端进行测试，而不是在自己的笔记本上，看起来很震撼。但是时代在变化，我们在云端获得的这一能力，事实上 Google 的工程师已经使用了超过 10 年了。Amazon 甚至还希望&lt;a href=&#34;https://aws.amazon.com/cloud9/&#34; target=&#34;_blank&#34;&gt;在云端运行 IDE&lt;/a&gt;，我还没有完成这一跳，不过很可能会是个方向。&lt;/p&gt;

&lt;h5 id=&#34;debug&#34;&gt;Debug&lt;/h5&gt;

&lt;p&gt;FaaS 上的除错是个有趣的领域。这里目前的进展主要是在本地运行 FaaS 函数，上面也说过这一点。微软提为本地运行的函数提供了很棒的由远程触发的除错支持。Amazon 提供了类似的能力，但是还不能用生产事件触发。&lt;/p&gt;

&lt;p&gt;对实际运行在云端生产环境的函数是个不同的事情。目前 Lambda 没有这方面的支持，我对此非常期待。&lt;/p&gt;

&lt;h5 id=&#34;部署-打包和版本&#34;&gt;部署、打包和版本&lt;/h5&gt;

&lt;p&gt;这是一个正在飞速进步的领域。AWS 在这方面做出很多创新，后续章节会进一步展开。&lt;/p&gt;

&lt;h5 id=&#34;服务发现&#34;&gt;服务发现&lt;/h5&gt;

&lt;p&gt;微服务世界中&lt;a href=&#34;https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/&#34; target=&#34;_blank&#34;&gt;服务发现&lt;/a&gt;是个常见名词：服务如何调用合适版本的其它服务。在 Serverless 世界中就很少会讨论这一问题。最开始我比较担心，目前已经释然了。很多 Serverless 的应用都是事件驱动的，服务的消费者通常要自行完成注册。基于 API 的 FaaS 通常会在 API 网关之后运行，相关的部署、流量控制也都是由网关完成的。我们甚至可以在 API 网关之前再加一层（例如使用 &lt;a href=&#34;https://aws.amazon.com/cloudfront/&#34; target=&#34;_blank&#34;&gt;AWS CloudFront&lt;/a&gt;），从而获得跨区域的服务弹性。&lt;/p&gt;

&lt;p&gt;这方面的内容我放在这里的原因是，我认为这个问题尚未解决，但是最终结果还是相当乐观的。&lt;/p&gt;

&lt;h5 id=&#34;监控&#34;&gt;监控&lt;/h5&gt;

&lt;p&gt;监控对 FaaS 来说是比较麻烦的，根本原因就在于容器的不稳定性。很多云供应商提供了一系列的监控支持，我们也看到了很多传统的第三方厂商提供了这方面的能力。然而所有的数据来源都是供应商的基础数据。有时候这是好事，但是仅就 AWS Lambda 来说，他们的数据还很基础。我们真正需要的是对第三方厂商开放 API，以此获得更多支持。&lt;/p&gt;

&lt;h5 id=&#34;api-网关以及超级-api-网关&#34;&gt;API 网关以及超级 API 网关&lt;/h5&gt;

&lt;p&gt;ThoughtWorks 在技术雷达中讨论过&lt;a href=&#34;https://www.thoughtworks.com/radar/platforms/overambitious-api-gateways&#34; target=&#34;_blank&#34;&gt;超级网关（over-ambitious API gateways）&lt;/a&gt;。参考链接中是通用的 API 网关，很明显可以用于 FaaS 函数的 HTTP 前端。API 网关在这里的问题是这类产品在自身的配置中包含了很多的应用级别的逻辑。这些逻辑一般来说是难于进行测试、版本管理的，甚至定义本身也具有很大难度。当然，通常情况下其定义过程还是优于在应用代码中实现的。&lt;/p&gt;

&lt;p&gt;这样一来就又一个权衡。如果我们将 API 网关视为 BaaS，它提供的能力是否节省了我们的投入？如果我们按照每次请求来支付 API 网关的费用，而不是按 CPU 使用率，那么最大限度地利用 API 网关的功能是否更具成本效益？&lt;/p&gt;

&lt;p&gt;我的建议是，对于 API 网关的功能应该审慎考虑，只有在网关产品的长期运行真正能够在部署、监控以及测试方面节省开销的情况下才可以大量使用。如果其配置过程无法使用版本源码或者部署脚本的话，就绝对不要使用。&lt;/p&gt;

&lt;p&gt;因为难于定义，Amazon 的 API 网关过去需要使用一些古怪的配置来为 Lambda 进行 HTTP 请求和响应的映射。由于 &lt;a href=&#34;https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html&#34; target=&#34;_blank&#34;&gt;Lambda Proxy Integration&lt;/a&gt; 的出现，这一情况大有改观，但还是需要理解一些细微之处。在开源项目 &lt;a href=&#34;https://github.com/serverless/serverless&#34; target=&#34;_blank&#34;&gt;Serverless Framework&lt;/a&gt;、&lt;a href=&#34;https://github.com/claudiajs/claudia&#34; target=&#34;_blank&#34;&gt;Claudia.js&lt;/a&gt;，以及 Amazon 的 &lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/serverless_app.html&#34; target=&#34;_blank&#34;&gt;Serverless Application Model&lt;/a&gt; 中，这些元素自身也得以简化。&lt;/p&gt;

&lt;h5 id=&#34;运维的差异&#34;&gt;运维的差异&lt;/h5&gt;

&lt;p&gt;上面说过，Serverless 并非 &amp;ldquo;No Ops&amp;rdquo;，在监控、伸缩、安全以及网络方面都有很多事情要做。然而开始之后，很容易忽略运维工作。这里一个风险就是失去对安全问题的感知。可能你的应用上线运行后，不小心就出现在黑客新闻中，然后就出现数十倍的流量，然后就陷入 DoS 之中，却无法自拔。&lt;/p&gt;

&lt;p&gt;这里就需要教育来跟进。Serverless 系统团队需要及早考虑运维方案，供应商和社区都提供了这方面的支持。包括压力测试、&lt;a href=&#34;https://www.oreilly.com/webops-perf/free/chaos-engineering.csp&#34; target=&#34;_blank&#34;&gt;混沌工程&lt;/a&gt;在内的方法都应该及早学习。&lt;/p&gt;

&lt;h2 id=&#34;serverless-的未来&#34;&gt;Serverless 的未来&lt;/h2&gt;

&lt;p&gt;行文至此，再来讨论一下我认为的 Serverless 技术一些方面在未来的可能发展。&lt;/p&gt;

&lt;h3 id=&#34;克服缺点&#34;&gt;克服缺点&lt;/h3&gt;

&lt;p&gt;Serverless 还是个新事物。前面提到的问题已经很多，而且一定不是全部。Serverless 最重要的事情就是克服这些问题，至少要大幅改善，尤其是实现方面的问题。&lt;/p&gt;

&lt;h4 id=&#34;工具-1&#34;&gt;工具&lt;/h4&gt;

&lt;p&gt;因为这一技术还很稚嫩，工具是个大问题。过去两年中，开发、应用和配置方面都有了很大进步，例如 Amazon 的 Serverless Application Model。尽管 Amazon 和 Google 可以在微软和 Auth0 身上得到更多灵感，但是起步阶段的体验依旧让人无法满意。&lt;/p&gt;

&lt;p&gt;我很高兴的看到，云供应商正在使用更高级的发布方法。传统系统中，经常需要编写自己的过程来处理流量转移场景，例如蓝绿部署以及&lt;a href=&#34;https://martinfowler.com/bliki/CanaryRelease.html&#34; target=&#34;_blank&#34;&gt;金丝雀发布&lt;/a&gt;，亚马逊为 Lambda 和 API 网关都提供了&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/automating-updates-to-serverless-apps.html&#34; target=&#34;_blank&#34;&gt;自动流量转移&lt;/a&gt;支持。这样的概念对 Serverless 系统更加重要，这种系统中的组件太多——100 个 Lambda 组成的系统进行原子发布是很不现实的。实际上 &lt;a href=&#34;https://twitter.com/natpryce&#34; target=&#34;_blank&#34;&gt;Nat Pryce&lt;/a&gt; 和我提到过一种 “Mixing desk” 方法——将一组组件逐步的从流量中引入或引出。&lt;/p&gt;

&lt;p&gt;分布式监控可能是最大的改善。我们看到 Amazon 的 &lt;a href=&#34;https://aws.amazon.com/xray/&#34; target=&#34;_blank&#34;&gt;x-Ray&lt;/a&gt; 以及大量的第三方产品，但是这个问题显然还没有得到妥善解决。&lt;/p&gt;

&lt;p&gt;远程调试同样是个普遍需要。微软 Azure Function 提供了这方面的支持，但是 Lambda 还没有。在远端运行的函数中植入断点是一个非常强大的功能。&lt;/p&gt;

&lt;p&gt;最后我期望看到在“大运维”方面的工具——如何更加有效的维护成百上千的 FaaS 函数、配置服务等。例如有的组织可能会需要看到某些服务进程不再需要（常见的安全需求），对跨服务成本需要有更好的分组和可见性（对有成本要求的自治团队来说至关重要）等等。&lt;/p&gt;

&lt;h4 id=&#34;状态管理&#34;&gt;状态管理&lt;/h4&gt;

&lt;p&gt;对大部分应用来说，缺乏对服务器内状态的持久化支持是可以接受的；但这并非全部。例如大规模缓存或者会话状态场景都有这样的需求。&lt;/p&gt;

&lt;p&gt;比如说一个高吞吐应用可能希望能让函数实例长期存活，从而更好的使用进程内缓存完成任务。在自动伸缩的传统应用中，这一场景也很常见。&lt;/p&gt;

&lt;p&gt;如果有一种非常低延迟的进程外数据库访问（例如 Redis），可能会是一个稍好的解决方案。Amazon 已经在 &lt;a href=&#34;https://aws.amazon.com/elasticache/&#34; target=&#34;_blank&#34;&gt;Elasticache 产品&lt;/a&gt; 提供了一个托管 Redis 方案，并且使用 &lt;a href=&#34;http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/placement-groups.html&#34; target=&#34;_blank&#34;&gt;Placement Groups&lt;/a&gt; 提供 EC2 服务器实例的部署协调。&lt;/p&gt;

&lt;p&gt;更多的，我认为我们会看到不同种类的混合（Serverless 和非 Serverless）应用架构来解决外部状态的问题。例如对低延迟应用，可能会使用一个传统的长期运行的服务进程来处理初始请求，在请求和本地环境中搜集所有需要进行处理的上下文，然后将包含上下文的所有请求信息发送给 FaaS 函数 Farm，这样就无需在 Serverless 中寻求数据了。&lt;/p&gt;

&lt;h4 id=&#34;平台改进&#34;&gt;平台改进&lt;/h4&gt;

&lt;p&gt;Serverless 的一些缺陷来自于平台的实现。执行时长、启动延迟以及跨函数限制都属此类。这种问题可能需要新的解决方案、或者额外的成本投入来解决。例如允许客户设置 FaaS 函数的两个实例持续可用，从而降低延迟，当然客户需要为此付出成本。微软 Azure Functions 提供的 &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/azure-functions/durable-functions-overview&#34; target=&#34;_blank&#34;&gt;Durable Functions&lt;/a&gt; 以及 &lt;a href=&#34;https://docs.microsoft.com/en-us/azure/azure-functions/functions-scale&#34; target=&#34;_blank&#34;&gt;App Service Plan-hosted functions&lt;/a&gt; 就支持这种做法。&lt;/p&gt;

&lt;p&gt;当然平台的改进，不仅仅是为了修复现存问题，因此更加值得期待。&lt;/p&gt;

&lt;h4 id=&#34;教育&#34;&gt;教育&lt;/h4&gt;

&lt;p&gt;Serverless 技术中很多服务商相关的缺陷都要通过教育来解决。这类平台的每个用户都要进行积极地思考，应用程序供应商的托管会对自身的生态会产生何种影响。我们需要思考一些问题，例如“我们是否需要考虑跨供应商的并行方案以备不时之需？”，或者“在部分失效的情况下，如何实现应用的优雅降级？”。&lt;/p&gt;

&lt;p&gt;另一个需要教育的方面就是技术运维。很多团队的系统管理员都比过去少了，Serverless 更会加速这一趋势。但是系统管理员需要做的事情却不再只是配置 Unix 服务器和编写 Chef 脚本了——他们经常要在一线处理支持、网络、安全等事务。&lt;/p&gt;

&lt;p&gt;Serverless 世界中，真正的 &lt;a href=&#34;https://martinfowler.com/bliki/DevOpsCulture.html&#34; target=&#34;_blank&#34;&gt;DevOps&lt;/a&gt; 文化显得尤为重要，这是因为有很多非系统管理的活动需要完成，通常都需要开发人员承担这一责任。这可能对于开发团队和技术领导来说并非司空见惯，因此教育和同运维的协作就至关重要了。&lt;/p&gt;

&lt;h4 id=&#34;提高服务商的透明性&#34;&gt;提高服务商的透明性&lt;/h4&gt;

&lt;p&gt;最后，在迁移方面：供应商应该让客户能够更透明和清晰的理解他们的平台，从而对平台能力进行估计以完成迁移。迁移过程颇具难度，但并非不可完成，不可靠的供应商是无法获得客户认可的。&lt;/p&gt;

&lt;h3 id=&#34;模式的诞生&#34;&gt;模式的诞生&lt;/h3&gt;

&lt;p&gt;目前看来，何时以及如何使用 Serverless 架构的问题分析方法还处于起步阶段。现在团队只能向 Serverless 平台抛出所有想法，然后看看哪里走得动，哪里行不通。感谢先驱们的工作，我们开始看到一些推荐的实践方式逐步产生，这些知识无疑会持续的积累下去。&lt;/p&gt;

&lt;p&gt;我们已经在应用架构方面看到了一些模式。例如什么规模之后 Serverless 会变得笨重？如果我们能够原子化的对 FaaS 函数进行分组部署，那么如何分组才是好的？我们在向微服务转型的过程中的方法是否适用，或者不同的架构是否会引向不同的方向？&lt;/p&gt;

&lt;p&gt;Serverless 应用架构的一个讨论热点就是这一架构和事件思维的关系。AWS Lambda 的 Ajay Nair 在 2017 年 &lt;a href=&#34;https://serverless.com/blog/ajay-nair-good-citizen-event-driven-world-emit-2017/&#34; target=&#34;_blank&#34;&gt;进行了一场演讲&lt;/a&gt;，这也是 CNCF Serverless 工作组的主要&lt;a href=&#34;https://github.com/cloudevents/spec/blob/master/spec.md&#34; target=&#34;_blank&#34;&gt;讨论方向之一&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;更进一步，创建 FaaS 和传统（持续在线的）服务组件的混合架构的合理方式是什么？怎样才能更好地把 BaaS 引入现存的生态系统？或者反过来说，什么样的信号表明 BaaS 系统需要集成更多服务端代码？&lt;/p&gt;

&lt;p&gt;我们还看到更多的使用模式方面的讨论。一个 FaaS 的标准实例就是媒体转换，不论何时，一个大型媒体文件被保存到 S3 存储中，就会自动运行一个进程来在其他位置创建一个更小的版本。然而我们现在看到很多数据处理管线、Web API 以及通用的胶水代码在使用 Serverless 方式。其中一些模式可以实现为通用组件，直接进行部署。根据这些早期想法，编写了 &lt;a href=&#34;https://blog.symphonia.io/examining-the-aws-serverless-application-repository-9ef316e2fd4&#34; target=&#34;_blank&#34;&gt;Amazon 的 Serverless 应用仓库&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;最后，我们开始看到推荐的运维模式推进工具的发展。我们如何逻辑上对 FaaS、BaaS 以及传统服务器构成的混合架构进行日志集成？如何有效的调试 FaaS 函数？云供应商会提供很多答案和模式，我期待看到这方面的发展。&lt;/p&gt;

&lt;h3 id=&#34;全球分布式架构&#34;&gt;全球分布式架构&lt;/h3&gt;

&lt;p&gt;在宠物店例子中，我们看到单独的宠物店服务器如何拆分为不同的服务端组件，以及一些逻辑被转到客户端之中。本质上这个架构的焦点还是在于客户端和一些已知位置的远程服务。&lt;/p&gt;

&lt;p&gt;Serverless 世界现在有了更加模糊的分布。就拿 Amazon 的 &lt;a href=&#34;https://aws.amazon.com/lambda/edge/&#34; target=&#34;_blank&#34;&gt;Lambda@Edge&lt;/a&gt; 来说，这是一个在 Amazon 的 CloudFront CDN 上运行 Lambda 的方式。在这种方式下，Lambda 函数会成为全球分布式架构——工程师的一个上传行为，意味着在全球&lt;a href=&#34;https://aws.amazon.com/cloudfront/details/&#34; target=&#34;_blank&#34;&gt;超过 100 数据中心&lt;/a&gt;进行部署。这并不是我们习惯的设计方式，带来了更多的能力和限制。&lt;/p&gt;

&lt;p&gt;另外 Lambda 函数能够&lt;a href=&#34;https://aws.amazon.com/greengrass/&#34; target=&#34;_blank&#34;&gt;在设备上运行&lt;/a&gt;，机器讯息模型能够在移动客户端上运行，服务端和客户端的界限日趋模糊。事实上我们看到了组件的局限性，Serverless 会再无边界。&lt;/p&gt;

&lt;h3 id=&#34;超越-faas&#34;&gt;超越 FaaS&lt;/h3&gt;

&lt;p&gt;我看到很多 FaaS 应用，主要是把现存的代码和设计思路进行 FaaS 改造：转换成为一系列的无状态函数。这很棒，但是我希望看到更多抽象或者语言，能够把 FaaS 变成一种底层实现，让开发人员在得到 FaaS 的好处的同时，不用实际的思考如何将应用设计成为不相干的函数群。&lt;/p&gt;

&lt;p&gt;例如我不知道 Google 的 &lt;a href=&#34;https://cloud.google.com/dataflow/&#34; target=&#34;_blank&#34;&gt;Dataflow&lt;/a&gt; 产品是否使用了 FaaS 方式的实现，但是我可以想象有人开发了一个产品或者开源项目做了类似的事情，使用了 FaaS 作为实现方式。这里要提到 &lt;a href=&#34;http://spark.apache.org/&#34; target=&#34;_blank&#34;&gt;Apache Spark&lt;/a&gt;，Spark 是一个用于大规模数据处理的的工具，提供了高度抽象，可以用 &lt;a href=&#34;https://aws.amazon.com/elasticmapreduce/details/spark/&#34; target=&#34;_blank&#34;&gt;Amazon EMR 和 Hadoop&lt;/a&gt; 作为其底层平台。&lt;/p&gt;

&lt;h3 id=&#34;测试-1&#34;&gt;测试&lt;/h3&gt;

&lt;p&gt;Serverless 系统中，还有更多集成和验收测试方面的工作可以做，这方面的很多事情是和云原生微服务系统是一致的。&lt;/p&gt;

&lt;p&gt;这方面的激进想法包括&lt;a href=&#34;https://vimeo.com/162635477&#34; target=&#34;_blank&#34;&gt;在生产环境中测试&lt;/a&gt;以及&lt;a href=&#34;https://nl.devoteam.com/en/blog-post/monitoring-driven-development-making-money/&#34; target=&#34;_blank&#34;&gt;用监控驱动开发&lt;/a&gt;：代码通过基本的单元测试之后，部署到一个流量子集中，和其他版本进行比较。这种方法可以和前面提到的流量迁移工具相互配合。这不是一个普适工具，但是会提高很多团队的生产效率。&lt;/p&gt;

&lt;h3 id=&#34;可移植的实现&#34;&gt;可移植的实现&lt;/h3&gt;

&lt;p&gt;有两种方式来使用 Serverless，可以降低供应商绑定的风险。&lt;/p&gt;

&lt;h4 id=&#34;厂商实现的抽象&#34;&gt;厂商实现的抽象&lt;/h4&gt;

&lt;p&gt;&lt;a href=&#34;http://serverless.com/&#34; target=&#34;_blank&#34;&gt;Serverless Framework&lt;/a&gt; 最初是用来简化 Serverless 应用的运维任务，但是还提供了大量无视应用部署位置和数量的中立操作能力。假如能够在 AWS API Gateway + Lambda 和 Auth0 webtask 之间进行方便的切换，想必是非常令人惊喜的，当然这依赖于相关平台所提供的运维能力。&lt;/p&gt;

&lt;p&gt;这方面的一个难点就是 FaaS 的接口没有一个统一的标准，但是这就是 CNCF Serverless Working Group 在 &lt;a href=&#34;https://github.com/cloudevents/spec/blob/master/spec.md&#34; target=&#34;_blank&#34;&gt;CloudEvents&lt;/a&gt; 项目的工作。&lt;/p&gt;

&lt;p&gt;为多个平台共部署抽象的必要性尚值商榷，抽象的背后可能带来更多的复杂性。例如一个平台中的安全性很可能无法满足另外一个平台的需要。&lt;/p&gt;

&lt;h4 id=&#34;可部署的实现&#34;&gt;可部署的实现&lt;/h4&gt;

&lt;p&gt;不是用第三方提供商的 Serverless 技术听起来可能有点奇怪，但是思考一下如下场景：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;一个大的技术组织想要为移动应用开发团队提供一个类似 &lt;a href=&#34;https://firebase.google.com/docs/database/&#34; target=&#34;_blank&#34;&gt;Firebase&lt;/a&gt; 的数据库服务，但是同时希望使用现存的数据库架构作为后端。&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;前面说过的有服务器的 FaaS 平台——项目中使用 FaaS 形式的架构，但是因为合规等方面的要求，必须在内部使用。&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;这两个场景下使用 Serverless 方法仍然是有收益的。可以参考一下 PaaS 的先例。最开始的 PaaS 都是基于云的（例如 Heroku），但是很快人们注意到了，在自有系统中运行 PaaS 也是有好处的——也称为 私有 PaaS（例如 &lt;a href=&#34;https://en.wikipedia.org/wiki/Cloud_Foundry&#34; target=&#34;_blank&#34;&gt;Cloud Foundry&lt;/a&gt;）。&lt;/p&gt;

&lt;p&gt;我能想象，和 PaaS 一样，最终我们会看到开源和和商业的 BaaS 以及 FaaS 实现，尤其是和 Kubernetes 这样的容器平台集成的产品。&lt;/p&gt;

&lt;h3 id=&#34;社区&#34;&gt;社区&lt;/h3&gt;

&lt;p&gt;在很多城市已经有了颇具规模的 Serverless 社区及其相关的会议、见面会以及在线小组等。我认为这一社区会持续成长，最终与 Docker、Spring 这样的社区并驾齐驱。&lt;/p&gt;

&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;

&lt;p&gt;Serverless 这个古怪的名字，是一种架构方式，我们可以用这种架构，以更小投入来运行应用中的服务端系统。Serverless 包含两种技术：BaaS 可以在前端应用中紧密的集成第三方的远端服务；FaaS 把长期运行的服务端代码转换为短期的函数实例。&lt;/p&gt;

&lt;p&gt;Serverless 不是万能药，所以要警惕那些声称 Serverless 将会替代所有现存架构的人。如果要跟进 Serverless 技术，尤其是 FaaS 之中，首先要知道，这其中有财宝——良好的伸缩和较低的成本；也有恶龙——调试和监控。&lt;/p&gt;

&lt;p&gt;不因噎废食是对新技术的一个基本态度，Serverless 有很多积极因素，其中包括降低运营开发成本、简化运维管理以及减少环境影响等。但是我认为最大的好处是，缩短了创建新组件的反馈循环过程。我是精益方法的忠实粉丝，我认为 Serverless 技术符合这一理念，能够尽快的将新方案交付给用户，并且更早获取先期反馈，这无疑是具有很大价值的。&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
