• 开源函数计算平台 OpenFunction 保姆级入门教程


    OpenFunction 0.6.0 上周已经正式发布了,带来了许多值得注意的功能,包括函数插件、函数的分布式跟踪、控制自动缩放、HTTP 函数触发异步函数等。同时,异步运行时定义也被重构了。核心 API 也已经从 v1alpha1 升级到 v1beta1。

    官宣链接:https://openfunction.dev/blog/2022/03/25/announcing-openfunction-0.6.0-faas-observability-http-trigger-and-more/

    近年来,随着无服务器计算的兴起,出现了很多非常优秀的 Serverless 开源项目,其中比较杰出的有 Knative 和 OpenFaaS。但 Knative Serving 仅仅能运行应用,还不能运行函数,而 Serverless 的核心是函数计算,也就是 FaaS,因此比较遗憾;OpenFaaS 虽然很早就出圈了,但技术栈过于老旧,不能满足现代化函数计算平台的需求。

    OpenFunction 便是这样一个现代化的云原生 FaaS(函数即服务)框架,它引入了很多非常优秀的开源技术栈,包括 Knative、Tekton、Shipwright、Dapr、KEDA 等,这些技术栈为打造新一代开源函数计算平台提供了无限可能:

    • Shipwright 可以在函数构建的过程中让用户自由选择和切换镜像构建的工具,并对其进行抽象,提供了统一的 API;
    • Knative 提供了优秀的同步函数运行时,具有强大的自动伸缩能力;
    • KEDA 可以基于更多类型的指标来自动伸缩,更加灵活;
    • Dapr 可以将不同应用的通用能力进行抽象,减轻开发分布式应用的工作量。

    本文不打算讲一些非常高深的理论,作为刚跨进 Serverless 门槛的用户,更需要的是如何快速上手,以便对函数计算有一个感性的认知,在后续使用的过程中,咱们再慢慢理解其中的架构和设计。

    本文将会带领大家快速部署和上手 OpenFunction,并通过一个 demo 来体验同步函数是如何运作的。

    OpenFunction CLI 介绍

    OpenFunction 从 0.5 版本开始使用全新的命令行工具 ofn 来安装各个依赖组件,它的功能更加全面,支持一键部署、一键卸载以及 Demo 演示的功能。用户可以通过设置相应的参数自定义地选择安装各个组件,同时可以选择特定的版本,使安装更为灵活,安装进程也提供了实时展示,使得界面更为美观。它支持的组件和其依赖的 Kubernetes 版本如下:

    Components Kubernetes 1.17 Kubernetes 1.18 Kubernetes 1.19 Kubernetes 1.20+
    Knative Serving 0.21.1 0.23.3 0.25.2 1.0.1
    Kourier 0.21.0 0.23.0 0.25.0 1.0.1
    Serving Default Domain 0.21.0 0.23.0 0.25.0 1.0.1
    Dapr 1.5.1 1.5.1 1.5.1 1.5.1
    Keda 2.4.0 2.4.0 2.4.0 2.4.0
    Shipwright 0.6.1 0.6.1 0.6.1 0.6.1
    Tekton Pipelines 0.23.0 0.26.0 0.29.0 0.30.0
    Cert Manager 1.5.4 1.5.4 1.5.4 1.5.4
    Ingress Nginx na na 1.1.0 1.1.0
    表一 OpenFunction 使用的第三方组件依赖的 Kubernetes 版本

    ofn 的安装参数 `ofn install` 解决了 OpenFunction 和 Kubernetes 的兼容问题,会自动根据 Kubernetes 版本选择兼容组件进行安装,同时提供多种参数以供用户选择。
    参数 功能
    --all 用于安装 OpenFunction 及其所有依赖。
    --async 用于安装 OpenFunction 的异步运行时(Dapr & Keda)。
    --cert-manager * 用于安装 Cert Manager。
    --dapr * 用于安装 Dapr。
    --dry-run 用于提示当前命令所要安装的组件及其版本。
    --ingress * 用于安装 Ingress Nginx。
    --keda * 用于安装 Keda。
    --knative 用于安装 Knative Serving(以Kourier为默认网关)
    --region-cn 针对访问 gcr.io 或 github.com 受限的用户。
    --shipwright * 用于安装 ShipWright。
    --sync 用于安装 OpenFunction Sync Runtime(待支持)。
    --upgrade 在安装时将组件升级到目标版本。
    --verbose 显示粗略信息。
    --version 用于指定要安装的 OpenFunction 的版本。(默认为 "v0.6.0")
    --timeout 设置超时时间。默认为5分钟。
    表二 install 命令参数列表

    使用 OpenFunction CLI 部署 OpenFunction

    有了命令行工具 ofn 之后,OpenFunction 部署起来非常简单。首先需要安装 ofn,以 amd64 版本的 Linux 为例,仅需两步即可:

    1、下载 ofn

    $ wget -c  https://github.com/OpenFunction/cli/releases/download/v0.5.1/ofn_linux_amd64.tar.gz -O - | tar -xz
    

    2、为 ofn 赋予权限并移动到 /usr/local/bin/ 文件夹下。

    $ chmod +x ofn && mv ofn /usr/local/bin/
    

    安装好 ofn 之后,仅需一步即可完成 OpenFunction 的安装。虽然使用 --all 选项可以安装所有组件,但我知道大部分小伙伴的真实需求是不想再额外装一下 Ingress Controller 的,这个也好办,我们可以直接指定需要安装的组件,排除 ingress,命令如下:

    $ ofn install --knative --async --shipwright --cert-manager --region-cn
    Start installing OpenFunction and its dependencies.
    The following components will be installed:
    +------------------+---------+
    | COMPONENT        | VERSION |
    +------------------+---------+
    | OpenFunction     | 0.6.0   |
    | Keda             | 2.4.0   |
    | Dapr             | 1.5.1   |
    | Shipwright       | 0.6.1   |
    | CertManager      | 1.5.4   |
    | Kourier          | 1.0.1   |
    | DefaultDomain    | 1.0.1   |
    | Knative Serving  | 1.0.1   |
    | Tekton Pipelines | 0.30.0  |
    +------------------+---------+
     ✓ Dapr - Completed!
     ✓ Keda - Completed!
     ✓ Knative Serving - Completed!
     ✓ Shipwright - Completed!
     ✓ Cert Manager - Completed!
     ✓ OpenFunction - Completed!
     Completed in 2m47.901328069s.
    
     ██████╗ ██████╗ ███████╗███╗   ██╗
    ██╔═══██╗██╔══██╗██╔════╝████╗  ██║
    ██║   ██║██████╔╝█████╗  ██╔██╗ ██║
    ██║   ██║██╔═══╝ ██╔══╝  ██║╚██╗██║
    ╚██████╔╝██║     ███████╗██║ ╚████║
     ╚═════╝ ╚═╝     ╚══════╝╚═╝  ╚═══╝
    
    ███████╗██╗   ██╗███╗   ██╗ ██████╗████████╗██╗ ██████╗ ███╗   ██╗
    ██╔════╝██║   ██║████╗  ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗  ██║
    █████╗  ██║   ██║██╔██╗ ██║██║        ██║   ██║██║   ██║██╔██╗ ██║
    ██╔══╝  ██║   ██║██║╚██╗██║██║        ██║   ██║██║   ██║██║╚██╗██║
    ██║     ╚██████╔╝██║ ╚████║╚██████╗   ██║   ██║╚██████╔╝██║ ╚████║
    ╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝  ╚═══╝
    

    虽然本文演示的是同步函数,但这里把异步运行时也装上了,如果你不需要,可以把 --async 这个参数去掉,不影响本文的实验。

    安装完成后,会创建这几个 namespace:

    $ kubectl get ns
    NAME                              STATUS   AGE
    cert-manager                      Active   17m
    dapr-system                       Active   4m34s
    io                                Active   3m31s
    keda                              Active   4m49s
    knative-serving                   Active   4m41s
    kourier-system                    Active   3m57s
    openfunction                      Active   3m37s
    shipwright-build                  Active   4m26s
    tekton-pipelines                  Active   4m50s
    

    每个 namespace 对应上面安装的各个组件。目前 OpenFunction 的 Webhook 需要使用 CertManager 来验证 API 访问,后续我们会去掉这个依赖,不再需要安装 CertManager

    自定义域名后缀

    Knative Serving 目前使用 Kourier 作为入口网关,由于我们没有部署 Ingress Controller,所以我们访问函数只有 Kourier 这一个入口。

    Kourier 是一个基于 Envoy Proxy 的轻量级网关,是专门对于 Knative Serving 服务访问提供的一个网关实现。关于 Envoy 控制平面的细节本文不作赘述,感兴趣的可以去阅读 Kourier 官方文档和源码。这里我们只需要知道 Kourier 会为函数访问提供一个入口,这个访问入口是通过域名来提供的,我们要做的工作就是将相关域名解析到 Kourier 的 ClusterIP。

    Kourier 默认创建了两个 Service:

    $ kubectl -n kourier-system get svc
    NAME               TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
    kourier            LoadBalancer   10.233.7.202   <pending>     80:31655/TCP,443:30980/TCP   36m
    kourier-internal   ClusterIP      10.233.47.71   <none>        80/TCP                       36m
    

    只需要将与函数访问相关域名解析到 10.233.47.71 即可。

    虽然每个函数的域名都是不同的,但域名后缀是一样的,可以通过泛域名解析来实现解析与函数相关的所有域名。Kourier 默认的域名后缀是 example.com,通过 Knative 的 ConfigMap config-domain 来配置:

    $ kubectl -n knative-serving get cm config-domain -o yaml
    apiVersion: v1
    data:
      _example: |
        ################################
        #                              #
        #    EXAMPLE CONFIGURATION     #
        #                              #
        ################################
    
        # This block is not actually functional configuration,
        # but serves to illustrate the available configuration
        # options and document them in a way that is accessible
        # to users that `kubectl edit` this config map.
        #
        # These sample configuration options may be copied out of
        # this example block and unindented to be in the data block
        # to actually change the configuration.
    
        # Default value for domain.
        # Although it will match all routes, it is the least-specific rule so it
        # will only be used if no other domain matches.
        example.com: |
    
        # These are example settings of domain.
        # example.org will be used for routes having app=nonprofit.
        example.org: |
          selector:
            app: nonprofit
    
        # Routes having the cluster domain suffix (by default 'svc.cluster.local')
        # will not be exposed through Ingress. You can define your own label
        # selector to assign that domain suffix to your Route here, or you can set
        # the label
        #    "networking.knative.dev/visibility=cluster-local"
        # to achieve the same effect.  This shows how to make routes having
        # the label app=secret only exposed to the local cluster.
        svc.cluster.local: |
          selector:
            app: secret
    kind: ConfigMap
    metadata:
      annotations:
        knative.dev/example-checksum: 81552d0b
      labels:
        app.kubernetes.io/part-of: knative-serving
        app.kubernetes.io/version: 1.0.1
        serving.knative.dev/release: v1.0.1
      name: config-domain
      namespace: knative-serving
    

    将其中的 _example 对象删除,添加一个默认域名(例如 openfunction.dev),最终修改结果如下:

    $ kubectl -n knative-serving get cm config-domain -o yaml
    apiVersion: v1
    data:
      openfunction.dev: ""
    kind: ConfigMap
    metadata:
      annotations:
        knative.dev/example-checksum: 81552d0b
      labels:
        app.kubernetes.io/part-of: knative-serving
        app.kubernetes.io/version: 1.0.1
        serving.knative.dev/release: v1.0.1
      name: config-domain
      namespace: knative-serving
    

    配置集群域名解析

    为了便于在 Kubernetes 的 Pod 中访问函数,可以对 Kubernetes 集群的 CoreDNS 进行改造,使其能够对域名后缀 openfunction.dev 进行泛解析,需要在 CoreDNS 的配置中添加一段内容:

            template IN A openfunction.dev {
              match .*\.openfunction\.dev
              answer "{{ .Name }} 60 IN A 10.233.47.71"
              fallthrough
            }
    

    修改完成后的 CoreDNS 配置如下:

    $ kubectl -n kube-system get cm coredns -o yaml
    apiVersion: v1
    data:
      Corefile: |
        .:53 {
            errors
            health
            ready
            template IN A openfunction.dev {
              match .*\.openfunction\.dev
              answer "{{ .Name }} 60 IN A 10.233.47.71"
              fallthrough
            }
            kubernetes cluster.local in-addr.arpa ip6.arpa {
              pods insecure
              fallthrough in-addr.arpa ip6.arpa
            }
            hosts /etc/coredns/NodeHosts {
              ttl 60
              reload 15s
              fallthrough
            }
            prometheus :9153
            forward . /etc/resolv.conf
            cache 30
            loop
            reload
            loadbalance
        }
        ...
    

    同步函数 demo 示例

    配置完域名解析后,接下来可以运行一个同步函数的示例来验证一下。OpenFunction 官方仓库提供了多种语言的同步函数示例

    这里我们选择 Go 语言的函数示例,先来看一下最核心的部署清单:

    # function-sample.yaml
    apiVersion: core.openfunction.io/v1beta1
    kind: Function
    metadata:
      name: function-sample
    spec:
      version: "v2.0.0"
      image: "openfunctiondev/sample-go-func:latest"
      imageCredentials:
        name: push-secret
      port: 8080 # default to 8080
      build:
        builder: openfunction/builder-go:latest
        env:
          FUNC_NAME: "HelloWorld"
          FUNC_CLEAR_SOURCE: "true"
        srcRepo:
          url: "https://github.com/OpenFunction/samples.git"
          sourceSubPath: "functions/knative/hello-world-go"
          revision: "main"
      serving:
        template:
          containers:
            - name: function
              imagePullPolicy: Always
        runtime: "knative"
    

    Function 是由 CRD 定义的一个 CR,用来将函数转换为最终运行的应用。这个例子里面包含了两个组件:

    • build : 通过 Shipwright 选择不同的镜像构建工具,最终将应用构建为容器镜像;
    • Serving : 通过 Serving CRD 将应用部署到不同的运行时中,可以选择同步运行时或异步运行时。这里选择的是同步运行时 knative。

    国内环境由于不可抗因素,可以通过 GOPROXY 从公共代理镜像中快速拉取所需的依赖代码,只需在部署清单中的 build 阶段添加一个环境变量 FUNC_GOPROXY 即可:

    # function-sample.yaml
    apiVersion: core.openfunction.io/v1beta1
    kind: Function
    metadata:
      name: function-sample
    spec:
      version: "v2.0.0"
      image: "openfunctiondev/sample-go-func:latest"
      imageCredentials:
        name: push-secret
      port: 8080 # default to 8080
      build:
        builder: openfunction/builder-go:latest
        env:
          FUNC_NAME: "HelloWorld"
          FUNC_CLEAR_SOURCE: "true"
          FUNC_GOPROXY: "https://proxy.golang.com.cn,direct"
        srcRepo:
          url: "https://github.com/OpenFunction/samples.git"
          sourceSubPath: "functions/knative/hello-world-go"
          revision: "main"
      serving:
        template:
          containers:
            - name: function
              imagePullPolicy: Always
        runtime: "knative"
    

    在创建函数之前,需要先创建一个 secret 来存储 Docker Hub 的用户名和密码:

    $ REGISTRY_SERVER=https://index.docker.io/v1/ REGISTRY_USER=<your_registry_user> REGISTRY_PASSWORD=<your_registry_password>
    $ kubectl create secret docker-registry push-secret \
        --docker-server=$REGISTRY_SERVER \
        --docker-username=$REGISTRY_USER \
        --docker-password=$REGISTRY_PASSWORD
    

    下面通过 kubectl 创建这个 Function:

    $ kubectl apply -f function-sample.yaml
    

    查看 Function 运行状况:

    $ kubectl get function
    NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING   URL   AGE
    function-sample   Building                    builder-6ht76                   5s
    

    目前正处于 Build 阶段,builder 的名称是 builder-6ht76。查看 builder 的运行状态:

    $ kubectl get builder
    NAME            PHASE   STATE      REASON   AGE
    builder-6ht76   Build   Building            50s
    

    这个 builder 会启动一个 Pod 来构建镜像:

    $ kubectl get pod
    NAME                                     READY   STATUS     RESTARTS   AGE
    builder-6ht76-buildrun-jvtwk-vjlgt-pod   2/4     NotReady   0          2m11s
    

    这个 Pod 中包含了 4 个容器:

    • step-source-default : 拉取源代码;

    • step-prepare : 设置环境变量;

    • step-create : 构建镜像;

    • step-results : 输出镜像的 digest。

    再次查看函数状态:

    $ kubectl get function
    NAME              BUILDSTATE   SERVINGSTATE   BUILDER         SERVING         URL                                              AGE
    function-sample   Succeeded    Running        builder-6ht76   serving-6w4rn   http://openfunction.io/default/function-sample   6m
    

    已经由之前的 Building 状态变成了 Runing 状态。

    这里的 URL 我们无法直接访问,因为没有部署 Ingress Controller。不过我们可以通过其他方式来访问,Kourier 把每个访问入口抽象为一个 CR 叫 ksvc,每一个 ksvc 对应一个函数的访问入口,可以看下目前有没有创建 ksvc:

    $ kubectl get ksvc
    NAME                       URL                                                        LATESTCREATED                   LATESTREADY                     READY   REASON
    serving-6w4rn-ksvc-k4x29   http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev   serving-6w4rn-ksvc-k4x29-v200   serving-6w4rn-ksvc-k4x29-v200   True
    

    函数的访问入口就是 http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev。由于在前面的章节中已经配置好了域名解析,这里可以启动一个 Pod 来直接访问该域名:

    $ kubectl run curl --image=radial/busyboxplus:curl -i --tty
    If you don't see a command prompt, try pressing enter.
    [ root@curl:/ ]$
    [ root@curl:/ ]$ curl http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev/default/function-sample/World
    Hello, default/function-sample/World!
    [ root@curl:/ ]$ curl http://serving-6w4rn-ksvc-k4x29.default.openfunction.dev/default/function-sample/OpenFunction
    Hello, default/function-sample/OpenFunction!
    

    访问这个函数时会自动触发运行一个 Pod:

    $ kubectl get pod
    NAME                                                       READY   STATUS    RESTARTS   AGE
    serving-6w4rn-ksvc-k4x29-v200-deployment-688d58bfb-6fvcg   2/2     Running   0          7s
    

    这个 Pod 使用的镜像就是之前 build 阶段构建的镜像。事实上这个 Pod 是由 Deployment 控制的,在没有流量时,这个 Deployment 的副本数是 0。当有新的流量进入时,会先进入 Knative 的 Activator,Activator 接收到流量后会通知 Autoscaler(自动伸缩控制器),然后 Autoscaler 将 Deployment 的副本数扩展到 1,最后 Activator 会将流量转发到实际的 Pod 中,从而实现服务调用。这个过程也叫冷启动

    如果你不再访问这个入口,过一段时间之后,Deployment 的副本数就会被收缩为 0:

    $ kubectl get deploy
    NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
    serving-6w4rn-ksvc-k4x29-v200-deployment   0/0     0            0           22m
    

    总结

    通过本文的示例,相信大家应该能够体会到一些函数计算的优势,它为我们带来了我们所期望的对业务场景快速拆解重构的能力。作为用户,只需要专注于他们的开发意图,编写函数代码,并上传到代码仓库,其他的东西不需要关心,不需要了解基础设施,甚至不需要知道容器和 Kubernetes 的存在。函数计算平台会自动为您分配好计算资源,并弹性地运行任务,只有当您需要访问的时候,才会通过扩容来运行任务,其他时间并不会消耗计算资源。

    本文由博客一文多发平台 OpenWrite 发布!

  • 相关阅读:
    http数据返回值
    刷新 返回
    微信平台上遇到的bug
    iscroll修改
    iscroll
    a标签的herf和click事件
    ios9+xcode7 适配笔记
    xsd、wsdl生成C#类的命令行工具使用方法
    xcode更新,想想也是醉了
    关于#define预处理指令的一个问题
  • 原文地址:https://www.cnblogs.com/kubesphere/p/16091196.html
Copyright © 2020-2023  润新知