Knative:代码精简之道

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
函数计算FC,每月15万CU 3个月
简介: 本文译自:https://content.pivotal.io/blog/knative-whittling-down-the-code 转载自:http://www.servicemesher.com/blog/knative-whittling-down-the-code/ [**编者案**] > Knative 作为 Google 发起开源的 serverless 项目,给我们

本文译自:https://content.pivotal.io/blog/knative-whittling-down-the-code
转载自:http://www.servicemesher.com/blog/knative-whittling-down-the-code/

[编者案]

Knative 作为 Google 发起开源的 serverless 项目,给我们提供了一套简单易用的 serverless 开源解决方案。本文作者直观地向我们展示了如何使用Knative来一步一步逐渐精简我们的代码,来更加简单容易的开发云原生应用。作者使用实例来逐步向我们讲述如何使用 Knative 提供的 Build、Serving 和 Eventing 三大组件来发挥威力,逐渐精简代码。如果您对 Knative 有兴趣,本文对于你通过 Knative 实践 serverless 一定会有所启发,希望您能喜欢。

对我来说,2018年最好的开源项目是Knative,这是一个建立在Kubernetes之上的serverless平台。不仅是为了serverless平台本身,也是为了它所倡导的整个开发范式。事件驱动开发并不新鲜,但Knative为围绕事件构建生态系统奠定了基础。

如果您不熟悉Knative,那么您读到的任何文档都将它分为三大组件:

  • 构建(Build) —— 我如何构建代码和打包代码?
  • 服务(Serving) —— 我的代码如何为请求提供服务?它是如何伸缩的?
  • 事件(Eventing) —— 我的代码如何被各种事件触发?

事实上这并不是一篇“Knative入门”的文章(在不久的将来会有更多关于这方面的内容),但是我最近一直在思考的是,随着开发人员越来越多地利用Knative提供的功能,他们如何减少自己需要编写的代码。这是最近Twitter上一个特别热门的话题,尤其是在KubeCon时代。我注意到的一个常见问题是,“如果您正在编写Dockerfile,它真的是一个serverless的平台吗?” 但是,其他人认为将代码打包为容器可能是最合理的解决方案,因为它是可移植的、全面的,并且具有所有依赖项。不乏强烈持有这种观点的人们,他们非常渴望争辩。

与其火上浇油,不如让我们看看Knative给开发人员提供了哪些选项来逐渐减少我们编写的代码量。我们将从最冗长的示例开始—我们自己构建的预构建容器(prebuilt container)。从这里开始,我们将逐渐减少基准代码量,减少构建自己的容器的需要,减少编写自己的Dockerfile的需要,最后减少编写自己的配置的需要。最重要的是,我们将看到Pivotal Function Service (PFS)的强大功能,以及它如何允许开发人员关注代码而不是配置。

我们将看到的所有代码都包含在两个git repos中:knative-hello-worldpfs-hello-world

预构建(Prebuilt)的Docker容器

我们将看到的第一个场景是为Knative提供一个预构建的容器镜像,该镜像已经上传到我们选择的容器镜像库。您将在Knative中看到的大多数Hello World示例都采用直接构建和管理容器的方式。这是有意义的,因为它很容易理解,而且没有引入很多新概念,这是一个很好的起点。这个概念非常简单直接:传给Knative一个暴露端口的容器,它将处理剩余的所有事情。它不关心你的代码是用GoRuby还是Java写的;它会接收传入的请求并将它们发送到你的应用程序。

让我们从一个使用Express web框架基于node.js实现的 hello world应用程序开始。

const express = require("express");
const bodyParser = require('body-parser')

const app = express();
app.use(bodyParser.text({type: "*/*"}));

app.post("/", function(req, res) {
 res.send("Hello, " + req.body + "!");
});

const port = process.env.PORT || 8080;
app.listen(port, function() {
 console.log("Server started on port", port);
});

是不是漂亮且简洁。这段代码将启动一个web服务器,监听端口8080(除非端口已被占用),并通过返回Hello来响应HTTP POST请求。当然,还需要package.json文件,它定义了一些东西(如何启动应用程序,依赖关系等),但这有点超出了我们所看到的范围。另一部分是Dockerfile,它描述如何将所有内容打包到一个容器中。

FROM node:10.15.1-stretch-slim

WORKDIR /usr/src/app
COPY . .
RUN npm install

ENV PORT 8080
EXPOSE $PORT

ENTRYPOINT ["npm", "start"]

这里也没什么好惊讶的。我们将我们的镜像建立在官方node.js镜像的基础之上,将我们的代码复制到容器中并安装依赖项,然后告诉它如何运行我们的应用程序。剩下的就是将其上传到Docker Hub。

$ docker build . -t brianmmcclain/knative-hello-world:prebuilt

$ docker push brianmmcclain/knative-hello-world:prebuilt

如果您曾经在类似于Kubernetes的应用程序上运行过,那么所有这些看起来应该非常熟悉。将代码放入容器中,让调度器处理以确保它处于正常状态。我们可以告诉Knative关于这个容器的信息,再加上一点metadata,它会处理所有的东西。随着请求数量的增长,它将扩展实例的数量,缩减到0,路由请求,连接事件——竭尽所能。我们真正需要告诉Knative的是调用我们的应用程序,运行它的名称空间,以及容器镜像的位置。

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
 name: knative-hello-world-prebuilt
 namespace: default
spec:
 runLatest:
   configuration:
     revisionTemplate:
       spec:
         container:
           image: docker.io/brianmmcclain/knative-hello-world:prebuilt

$ kubectl apply -f 01-prebuilt.yaml

几分钟后,我们将看到一个新的pod运行起来,准备为请求服务,如果在一段时间内没有收到任何流量,Pod 数量将会缩减为0。我们可以POST一些数据,看看我们收到的响应。首先,让我们获取Kubernetes集群的Ingress IP,并将其分配给$SERVICE_IP变量:

$ export SERVICE_IP=`kubectl get svc istio-ingressgateway -n istio-system -o jsonpath="{.status.loadBalancer.ingress[*].ip}"`

然后使用IP向我们的服务发送请求,在我们的请求中设置HOST header:

$ curl -XPOST http://$SERVICE_IP -H "Host: knative-hello-world-prebuilt.default.example.com" -d "Prebuilt"

Hello, Prebuilt!

Kaniko容器构建器

上面介绍的一切可以很好的工作,但是我们甚至还没有开始接触Knative的“Build”部分。实际上,我们没有碰它,我们自己构建了这个容器。您可以在Knative文档中阅读所有关于构建以及它们如何工作的信息。总的来说,knative有一个名为“Build Templates”的概念,我喜欢这样描述他们:他们是关于如何从代码到容器的可共享逻辑。这些构建模板中的大多数模板都能够完成我们构建容器和上传镜像的需要。这些模板中最基本的可能是Kaniko Build Templates

顾名思义,它基于谷歌的Kaniko, Kaniko是在容器中构建容器镜像的工具,不依赖于正在运行的Docker守护进程。向Kaniko容器镜像提供Dockerfile和一个上传结果的位置,它就可以据此构建镜像。我们无需拉取代码、在本地构建镜像、上传到 Docker Hub,然后从 Knative 拉取镜像,我们可以让Knative为我们做这些,只需要多做一点配置。

但是,在执行此操作之前,我们需要告诉Knative如何根据容器注册中心进行身份验证。为此,我们首先需要在Kubernetes中创建一个Secret,这样我们就可以对Docker Hub进行身份验证,然后创建一个服务帐户来使用该Secret并运行构建。让我们从创造Secret开始:

apiVersion: v1
kind: Secret
metadata:
 name: dockerhub-account
 annotations:
   build.knative.dev/docker-0: https://index.docker.io/v1/
type: kubernetes.io/basic-auth
data:
 # 'echo -n "username" | base64'
 username: dXNlcm5hbWUK
 # 'echo -n "password" | base64'
 password: cGFzc3dvcmQK

uesrname和password作为base64编码的字符串发送给Kubernetes。(对于有安全意识的读者来说,这是一种传输机制,而不是安全机制。有关Kubernetes如何存储Secret的更多信息,请在有空时查看关于on encrypting secret data at rest)。提交之后,我们将创建一个名为build-bot的服务帐户,并告诉它在推送到Docker Hub时使用这个Secret:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-bot
secrets:
- name: dockerhub-account

有关身份验证的更多信息,请确保查看knative文档中的how-authentication-work -in- knative文档。

构建模板(Build Templates)的好处是任何人都可以创建并与社区共享它们。我们可以告诉Knative通过传递一些YAML来安装这个构建模板:

$ kubectl apply -f https://raw.githubusercontent.com/knative/build-templates/master/kaniko/kaniko.yaml

然后我们需要在我们的应用YAML中添加更多:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
 name: knative-hello-world-kaniko
 namespace: default
spec:
 runLatest:
   configuration:
     build:
       serviceAccountName: build-bot
       source:
         git:
           url: https://github.com/BrianMMcClain/knative-hello-world.git
           revision: master
       template:
         name: kaniko
         arguments:
         - name: IMAGE
           value: docker.io/brianmmcclain/knative-hello-world:kaniko
     revisionTemplate:
       spec:
         container:
           image: docker.io/brianmmcclain/knative-hello-world:kaniko

虽然直接比较有点困难,但是我们实际上只向YAML中添加了一个部分—“Build”部分。我们添加的内容可能看起来很多,但如果你花时间逐条查看的话,它实际上并不坏:

  • serviceAccountName:在Knative auth文档中,它遍历了设置服务帐户的过程。所有这些都是通过设置一个Kubernetes Secret来验证我们的容器镜像库,然后将其封装到一个服务帐户中。
  • source:代码所在的位置。例如,git repository。
  • template:要使用哪个Build Template。在本例中,我们将使用Kaniko Build Template。

让我们向应用程序的新版本发送一个请求,以确保一切正常:

$ curl -XPOST http://$SERVICE_IP -H "Host: knative-hello-world-kaniko.default.example.com" -d "Kaniko"

Hello, Kaniko!

尽管这可能是一种更预先的配置,但权衡的结果是,现在我们不必每次更新代码时都构建或推送我们自己的容器镜像。相反,Knative将为我们处理这些步骤!

Buildpack Build Template

所以,这个博客的重点是我们如何编写更少的代码。虽然我们已经使用Kaniko Build Template删除了部署的一个操作组件,但是我们仍然在代码之上维护一个Dockerfile和一个配置文件。但是如果我们可以抛弃Dockerfile呢?

如果您具有使用PaaS的习惯,那么您可能已经习惯了简单地向上推代码,然后发生了一些神奇的事情,然后您就有了一个正常工作的应用程序。你不在乎这是怎么做到的。我们所知道的是,您不必编写Dockerfile来将其放入容器中,而且它可以正常工作。在Cloud Foundry,这是通过名为buildpacks的框架实现的,该框架为应用程序提供运行时和依赖项。

实际上给我们带来两大好处。不仅有一个使用buildpacks的Build Template,还有一个用于Node.js的buildpacks。就像Kaniko Build Template一样,我们将在Knative中安装buildpack Build Template:

kubectl apply -f https://raw.githubusercontent.com/knative/build-templates/master/buildpack/buildpack.yaml

现在,让我们看看使用Buildpack Build Template的YAML是什么样子的:

apiVersion: serving.knative.dev/v1alpha1
kind: Service
metadata:
 name: knative-hello-world-buildpack
 namespace: default
spec:
 runLatest:
   configuration:
     build:
       serviceAccountName: build-bot
       source:
         git:
           url: https://github.com/BrianMMcClain/knative-hello-world.git
           revision: master
       template:
         name: buildpack
         arguments:
         - name: IMAGE
           value: docker.io/brianmmcclain/knative-hello-world:buildpack
     revisionTemplate:
       spec:
         container:
           image: docker.io/brianmmcclain/knative-hello-world:buildpack

这与我们使用Kaniko Build Template时非常相似。实际上,我们来做个比较:

<   name: knative-hello-world-kaniko
>   name: knative-hello-world-buildpack
---
<           name: kaniko
>           name: buildpack
---
<             value: docker.io/brianmmcclain/knative-hello-world:kaniko
>             value: docker.io/brianmmcclain/knative-hello-world:buildpack
---
<             image: docker.io/brianmmcclain/knative-hello-world:kaniko
>             image: docker.io/brianmmcclain/knative-hello-world:buildpack

那么区别是什么呢?首先,我们可以完全抛弃Dockerfile。Buildpack Build Template将分析我们的代码,确定它是一个Node.js应用程序,并通过下载Node.js运行时和依赖项为我们构建一个容器。虽然Kaniko Build Template将我们从Docker容器生命周期的管理工作中解放出来,但Buildpack Build Template更进一步,完全不需要管理Dockerfile了。

$ kubectl apply -f 03-buildpack.yaml
service.serving.knative.dev "knative-hello-world-buildpack" configured

$ curl -XPOST http://$SERVICE_IP -H "Host: knative-hello-world-buildpack.default.example.com" -d "Buildpacks"
Hello, Buildpacks!

Pivotal Function Service

让我们检查一下代码库的剩余部分。我们有响应POST请求的Node.js代码,使用Express框架设置web服务器。package.json文件定义了我们的依赖项。虽然这不是真正的代码,但我们也在维护定义Knative服务的YAML。不过,我们可以继续削减。

进入Pivotal Function Service (PFS),这是构建在Knative之上的Pivotal的商业serverless产品。PFS旨在消除管理代码以外的任何东西的需要。这包括我们在代码库中管理自己的web服务器。使用PFS,我们的代码如下:

module.exports = x => "Hello, " + x + "!";

就是这样,没有Dockerfile,没有YAML。只要一行代码。当然,像所有优秀的节点开发人员一样,我们仍然需要有自己的package.json文件,尽管它不依赖于Express。一旦部署完毕,riff将使用这一行代码并将其封装在自己的托管容器镜像中。它将把它与调用代码所需的逻辑打包在一起,并像运行在Knative上的任何其他函数一样提供服务。

PFS CLI使得部署我们的函数变得非常容易。我们将给函数命名为pfs-hello-world,为它提供到代码所在的GitHub存储库的链接,并告诉它将生成的容器映像上传到我们的私有容器镜像库中。

pfs function create pfs-hello-world --git-repo https://github.com/BrianMMcClain/pfs-hello-world.git --image $REGISTRY/$REGISTRY_USER/pfs-hello-world --verbose

几分钟后,我们将看到我们的函数进入运行状态,我们可以像任何其他Knative函数一样,向其发送请求:

$ curl -XPOST http://$SERVICE_IP -H "Host: pfs-hello-world.default.example.com" -H "Content-Type: text/plain" -d "PFS"

Hello, PFS!

或者,更简单的是,使用riff CLI来调用我们的函数:

$ pfs service invoke pfs-hello-world --text -- -d "PFS CLI"

Hello, PFS CLI!

我们终于实现了目标! 由23行YAML、14行代码和一个10行Dockerfile组成的简化代码行。

是不是一下子对PFS感兴趣了呢?要申请提前访问,只需填写这张快速表格!

接下来工作?

越来越多的构建模板。这是Knative最令人兴奋的特性之一,因为它有很大的潜力为各种场景打开一个自定义构建模板的社区。现在,您可以为JibBuildKit等工具使用模板。已经有一个pull request来更新Buildpack构建模板,以支持Cloud Native Buildpacks

2018年是一个激动人心的开始,但是更让我兴奋的是看到Knative社区在2019年的增长。我们当然可以期望从社区获得更多的构建模板和更多的事件源。不仅如此,我们还可以期望与现有技术更好地集成,例如Spring,它已经对此功能提供了强大的支持

如果您希望开始使用Knative进行开发,那么Bryan Friedman和我将在2月21日主持一个很棒的网络研讨会,讨论developing serverless applications on Kubernetes with Knative。我们将深入研究Knative的三个组件,它们是如何工作的,以及作为开发人员如何利用它们来编写更好的代码。

如果你4月2-4日在费城,请加入我们的CF Summit! Bryan和我将讨论the way to build serverless on Knative,或者如果您看到我们,就说声hi!

相关实践学习
Docker镜像管理快速入门
本教程将介绍如何使用Docker构建镜像,并通过阿里云镜像服务分发到ECS服务器,运行该镜像。
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
Kubernetes Cloud Native 容器
完全免费的K8S学习平台:在线集群环境助力你的云原生之路!
完全免费的K8S学习平台:在线集群环境助力你的云原生之路!
3141 1
|
4月前
|
SQL Java 应用服务中间件
数据库连接池详解及性能优化趋势
Sharding-JDBC所构建的Database Mesh与Service Mesh相互独立,协同工作。服务间的交互由Service Mesh Sidecar负责管理,而基于SQL的数据库访问则交由Sharding-JDBC-Sidecar处理。业务应用无需关心物理部署细节,实现真正的零侵入。Sharding-JDBC-Sidecar与宿主机生命周期绑定,非静态IP,确保了动态和弹性。尽管如此,数据运维操作仍可通过启动Sharding-JDBC-Server进程作为静态IP入口,借助命令行或UI客户端轻松完成。
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
435 1
服务架构的演进:从单体到微服务的探索之旅
|
11月前
|
人工智能 自然语言处理 监控
PMP认证有哪些要求:干货分享
本文详细介绍PMP的报考要求、考试内容与形式、认证有效期及价值,以及备考策略和推荐的学习工具,帮助考生高效准备。
500 2
|
关系型数据库 MySQL 数据库
MySQL的语法涵盖了数据定义、数据操作、数据查询和数据控制等多个方面
MySQL的语法涵盖了数据定义、数据操作、数据查询和数据控制等多个方面
267 6
|
存储 物联网 Serverless
什么是Serverless
Serverless是一种云计算架构模式,让开发者通过第三方服务直接构建和部署应用。在此模式下,应用被分解为无状态函数,由云平台统一管理。开发者仅需关注业务逻辑编写,无需管理底层服务器及基础设施。这种方式具有诸多优点:无需管理服务器,可专注业务逻辑;能够根据负载自动调整资源,实现按需计费;具备高可用性和灵活性,并支持多种编程语言。此外,Serverless应用按需启动和停止,无请求时不消耗资源,进一步提高了效率和成本效益。已被广泛应用于Web应用、移动应用、数据分析和物联网等领域。
921 3
|
微服务
微服务架构与单体架构:比较和对比
【8月更文挑战第22天】
919 0
|
Kubernetes 调度 Perl
serverless-OpenWhisk安装
serverless-OpenWhisk安装
427 0
serverless-OpenWhisk安装
|
JSON 负载均衡 Java
SpringCloud Feign 远程调用(史上最详细讲解)
SpringCloud Feign 远程调用(史上最详细讲解)
14707 0
SpringCloud Feign 远程调用(史上最详细讲解)
|
数据可视化 Shell 开发工具
在 IDEA 中使用 Git 图文教程
在 IDEA 中使用 Git 图文教程
1129 0
在 IDEA 中使用 Git 图文教程