11 月 19 - 20 日 Apache Pulsar 社区年度盛会来啦,立即报名! 了解详情
写点什么

手把手教你在 Serverless 平台上部署应用

  • 2020-07-23
  • 本文字数:25906 字

    阅读完需:约 85 分钟

手把手教你在Serverless平台上部署应用

如今,部署一个三层(表示层、业务逻辑和存储)架构应用程序可能会有些棘手。假设我们有一个简单的 Django 应用程序,即本教程中的民意投票应用程序。它在我们的本地计算机上运行完美,我们添加了 requirements.txt 来保存该项目的依赖项。至于数据库,由于我们仅在本地开发,因此可以使用 SQLite。


本文中,项目的目的是构建一个系统,使我们能够推送分支代码并将更改部署在单独的环境中,为我们提供唯一的 URL 进行检查。与now.shheroku.com的功能类似,我们需要一种机制,可以打包我们的代码和依赖项并进行部署,但同时还需要考虑系统的多版本、升级、负载平衡、扩展及数据库状态。


Indroduction

为了实现这一目标,我们需要两个主要组件:一个组件是获取我们的代码并准备将其发布,即 CI/CD 组件,另一个组件将我们的变更发布到互联网,称为服务组件。我们可以添加第三个组件来保持应用程序的某些状态,例如数据库和存储,但是我们将其添加到服务组件中。



Serving component

对于服务组件,我们可以使用Knative。它是基于 Kubernetes 的 Serverless 解决方案,并集成了其他一些在 Kubernetes 上构建的组件。从根本上讲,它可以通过 Docker 运行并公开到互联网,而无需繁琐的部署流程。你只需要定义一个描述镜像及其运行环境的服务,Knative就会处理其他所有事情(从路由、日志记录、监视到应用程序不同版本的管理以及服务自动扩缩容,包括 0 缩放以停止服务使用)。


可以想象,Knative 要远比上面的描述复杂得多,现阶段我们不会对其进行深入的技术讨论。


Packet

为了继续使用 Knative,我们需要一个 Kubernetes 集群。为了进行过度设计并尝试一些新的东西,让我们尝试在裸机上安装 Kubernetes。这听起来有点不知所措,但最后,它比预期的要简单得多。我一直想尝试packet.com,因为它们已经实现了部署的自动化(可以通过 API 进行控制,从而使 Terraform 等工具大放异彩),他们拥有一个市场,在该市场上竞标每小时机器使用量( 称为Spot Market,可通过 API 访问)和简洁的联网功能(如BGP-边界网关协议,我们在后面就会用到它)。


我们可以从 3 种部署类型中进行选择:按需实例、预留实例和 spot 实例。让我们尝试竞价型 spot 实例,因为这样真的很便宜。



创建 spot 实例市场请求后,它将检查符合您出价的可用机器,并开始配置它们。最高出价为 $ 0.10 / h,我们得到一个c1.small.x86实例,它有 4 个运行在 3.4Ghz(Intel E3-1240 v3)的物理核心、32GB RAM、2 x 120GB SSD 和 2 个千兆网卡。



我已经为每台服务器更新了主机名,现在我们可以安装 Kubernetes 了。



Kubernetes on bare-metal

网络上有大量教程,从手动安装所有组件到使用脚本或其他工具裸机安装 Kubernetes。最受欢迎的选择是kopskubeadmkubespray。我选择 kubespray,因为对我来说,它更容易理解,并且因为我有一些经验,所以这条路的阻力最小。在这里,你可以了解到 kops、kubeadm 和 kubespray 之间的差异比较。


Kubespray 易于安装和使用。 我们只需要克隆代码仓库并通过下面的指令安装


sudo pip3 install -r requirements.txt
复制代码


如果要在服务器上运行不同版本的 ansible,我们也可以将其安装在单独的虚拟环境中。


接下来,我们需要定义服务器清单。它配备了预定义的清单示例。我们能使用 Packet 的 API 列出所有服务器清单,但我决定使用静态方式,只需将 sample 清单资源列表复制到一个独立的清单资源中(重命名为 rabbit)。


cd kubespraycp -R inventory/sample/ intentory/rabbit
复制代码


现在向 inventory.ini 中添加我们的服务器信息。


[all]rabbit-1.vtemian.com ansible_host=147.75.84.27 ansible_user=root ip=10.80.204.129 etcd_member_name=etcd1rabbit-2.vtemian.com ansible_host=147.75.100.161 ansible_user=root ip=10.80.204.131 etcd_member_name=etcd2rabbit-3.vtemian.com ansible_host=147.75.100.215 ansible_user=root ip=10.80.204.133 etcd_member_name=etcd3
[kube-master]rabbit-1.vtemian.com
[etcd]rabbit-1.vtemian.com
[kube-node]rabbit-2.vtemian.comrabbit-3.vtemian.com
[calico-rr]
[k8s-cluster:children]kube-masterkube-nodecalico-rr
复制代码


因为当我设置集群时,kubespray 并不完全支持 Ubuntu 20.04,所以我不得不稍微更新一下任务。 我已将 python-minimal替换为 python2-minimal,并从 Ubuntu 19.10(Eoan)仓库中安装 Docker。


接下来,我们只需要运行 ansible 并静候结果。


ansible-playbook --become -i inventory/rabbit/inventory.ini cluster.yml
复制代码


如果一切正常,我们将建立并成功运行一个新集群。为了访问它,我们可以从 kube-master 节点获取管理员凭证。


scp root@rabbit-1.vtemian.com:/etc/kubernetes/admin.conf .
复制代码


接下来,把它们添加到我们的本地 kubectl 配置中(通常位于〜/.kube /config 中),我们将能够使用 kubectl 访问集群。


╰─>$ kubectl get pod --all-namespaces -o wideNAMESPACE     NAME                                           READY   STATUS    RESTARTS   AGE     IP              NODE                   NOMINATED NODE   READINESS GATESkube-system   calico-kube-controllers-5679c8548f-rffvp       1/1     Running   0          2m46s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>kube-system   calico-node-6wt2p                              1/1     Running   1          3m12s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>kube-system   calico-node-98cnq                              1/1     Running   1          3m12s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>kube-system   calico-node-kh9k8                              1/1     Running   1          3m12s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>kube-system   coredns-76798d84dd-75tz6                       1/1     Running   0          2m21s   10.233.82.1     rabbit-1.vtemian.com   <none>           <none>kube-system   coredns-76798d84dd-bqt66                       1/1     Running   0          2m17s   10.233.80.1     rabbit-3.vtemian.com   <none>           <none>kube-system   dns-autoscaler-85f898cd5c-nskgf                1/1     Running   0          2m18s   10.233.82.2     rabbit-1.vtemian.com   <none>           <none>kube-system   kube-apiserver-rabbit-1.vtemian.com            1/1     Running   0          4m58s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>kube-system   kube-controller-manager-rabbit-1.vtemian.com   1/1     Running   0          4m58s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>kube-system   kube-proxy-4ktbs                               1/1     Running   0          3m34s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>kube-system   kube-proxy-kd6n2                               1/1     Running   0          3m34s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>kube-system   kube-proxy-ts8nw                               1/1     Running   0          3m34s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>kube-system   kube-scheduler-rabbit-1.vtemian.com            1/1     Running   0          4m58s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>kube-system   kubernetes-dashboard-77475cf576-7sdr6          1/1     Running   0          2m15s   10.233.83.2     rabbit-2.vtemian.com   <none>           <none>kube-system   kubernetes-metrics-scraper-747b4fd5cd-k96pn    1/1     Running   0          2m15s   10.233.83.1     rabbit-2.vtemian.com   <none>           <none>kube-system   nginx-proxy-rabbit-2.vtemian.com               1/1     Running   0          3m35s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>kube-system   nginx-proxy-rabbit-3.vtemian.com               1/1     Running   0          3m36s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>kube-system   nodelocaldns-9l6vf                             1/1     Running   0          2m17s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>kube-system   nodelocaldns-blbcb                             1/1     Running   0          2m17s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>kube-system   nodelocaldns-vrspt                             1/1     Running   0          2m17s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none> 
复制代码


MetalB

接下来,我们应该能够安装 Knative 了。路由组件是 Knative 安装的一大步骤。它支持多个网络层(Ambassador、Contour、Glo、Istio 和 Kourier)。唯一的问题是这些层需要一个负载均衡器,该负载均衡器将暴露给 Internet(一个外部LoadBalancer)。Kubernetes 没有对此的本地支持。 基本上,当前的实现是供应商(AWS、GCP、Azure 等)特定的,并且由于使用的是裸机,因此我们暂时负担不起使用其中一种。


幸运的是,有一个名为MetalLB的裸机实现。它可以通过两种方式做到这一点:在第2层使用 ARP /NDP 或利用BGP。 因为 Packet 已经支持BGP,并且它们还提供了一个有关如何配置MetalLB的有用示例,接下来我们会尝试一下。


Packet 的 BGP-Kubernetes集成有详细说明文档。我们只需要小心处理 IPPools。在定义它们之前,我已经配置了两组弹性 IP:


全局 IP 147.75.40.130/32 和公用 IPv4 147.75.80.160/30。



出于安全原因,需要为每台服务器手动配置 IP。这相对容易做到并且操作文档可查。对于每个服务器,从网络分段为它们附加一个 IP:



然后,在每个服务器上手动(或通过 ansible)(例如,Ubuntu / Debian 的一个示例)运行:


sudo ip addr add <elastic-ip> dev lo
复制代码


要使其永久生效,我们需要编辑 /etc/network/interfaces


auto lo:0iface lo:0 inet static    address <elastic-ip>    netmask 255.255.255.255
复制代码


继续 IPPools 配置,metallb-ewr1-public 将配置 147.75.80.160/30,metallb-global-ips 将配置 147.75.40.130/32,对于 metallb-private 将配置我们的私有节点子网,在当前情况下应该是 10.80.204.128/29。你可以使用节点的专用 ip 和 CIDR-IP 转换工具


对于每一个 calico peer 配置,我们将配置节点专用 IP。接下来,我们将安装最新的 metalLB:


kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.8.1/manifests/metallb.yaml
复制代码


在 metallb-system 命名空间中紧跟 metalLB 的配置映射:


apiVersion: v1kind: ConfigMapmetadata:  namespace: metallb-system  name: configdata:  config: |    peers:    - peer-address: 127.0.0.1      peer-asn: 65000      my-asn: 65480    address-pools:    - name: ewr1-public      protocol: bgp      addresses:      - 147.75.80.160/30    - name: ewr1-private      protocol: bgp      addresses:      - 10.80.204.128/29    - name: global-ip      protocol: bgp      addresses:      - 147.75.40.130/32
复制代码


我们可以通过在主节点中运行calicoctl node status来检查所有配置是否正确:


root@rabbit-1:~# calicoctl node statusCalico process is running.IPv4 BGP status+----------------+-------------------+-------+----------+-------------+|  PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |+----------------+-------------------+-------+----------+-------------+| 147.75.100.215 | node-to-node mesh | up    | 13:46:38 | Established || 127.0.0.1      | global            | up    | 13:51:44 | Established || 147.75.100.161 | node-to-node mesh | up    | 13:47:27 | Established |+----------------+-------------------+-------+----------+-------------+
复制代码


以及其他 kubectl 命令:


╰─>$ kubectl get pod -n kube-system -o wide | grep calico-nodecalico-node-479fz                              1/1     Running   0          8m25s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>calico-node-846gr                              1/1     Running   0          7m18s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>calico-node-tpnjc                              1/1     Running   0          8m8s    10.80.204.129   rabbit-1.vtemian.com   <none>           <none>
复制代码


╰─>$ kubectl get pod -n metallb-system -o wideNAME                          READY   STATUS    RESTARTS   AGE    IP              NODE                   NOMINATED NODE   READINESS GATEScontroller-6bcfdfd677-nxnw8   1/1     Running   0          5m4s   10.233.65.193   rabbit-3.vtemian.com   <none>           <none>speaker-d6kks                 1/1     Running   0          5m4s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>speaker-kk85w                 1/1     Running   0          5m4s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>speaker-p4lc7                 1/1     Running   0          5m4s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>
复制代码


Istio

现在,我们已经启动并运行了 MetalLB,我们可以继续使用最后一个路由组件。在 Knative 支持的所有这些网络组件中,我选择了Istio,因为它是唯一与 Knative 运营商兼容的组件(将在后面进行介绍)。


我们只需要按照主安装页面上的说明进行操作,如果一切正常,我们将拥有一个带有外部 IP 的负载均衡器。


╰─>$ kubectl get service --all-namespacesNAMESPACE      NAME                        TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)                                                                                                                                      AGEdefault        kubernetes                  ClusterIP      10.233.0.1      <none>          443/TCP                                                                                                                                      101mistio-system   istio-ingressgateway        LoadBalancer   10.233.24.125   147.75.80.160   15020:30935/TCP,80:31380/TCP,443:31390/TCP,31400:31400/TCP,15029:31350/TCP,15030:31699/TCP,15031:32315/TCP,15032:31519/TCP,15443:32542/TCP   55sistio-system   istio-pilot                 ClusterIP      10.233.48.55    <none>          15010/TCP,15011/TCP,8080/TCP,15014/TCP                                                                                                       55skube-system    coredns                     ClusterIP      10.233.0.3      <none>          53/UDP,53/TCP,9153/TCP                                                                                                                       98mkube-system    dashboard-metrics-scraper   ClusterIP      10.233.61.223   <none>          8000/TCP                                                                                                                                     97mkube-system    kubernetes-dashboard        ClusterIP      10.233.16.174   <none>          443/TCP                                                                                                                                      97m
复制代码


Knative

我们已经准备好安装 Knative 了。我发现更简单的方法是安装 operator,它将进一步安装所有组件。 我尝试过手动安装每个组件,那样的话会异常麻烦。


现在,我们需要将 operator 安装在默认命名空间中,因为它将在其中查找名为 config-logging 的 ConfigMap(配置映射表)。


╰─>$ kubens default╰─>$ kubectl apply -f https://github.com/knative-sandbox/operator/releases/download/v0.14.1/operator.yaml
复制代码


一旦 CRDs 安装成功后,operator 对应的 pod 可以运行生效。


╰─>$ kubectl get podsNAME                                         READY   STATUS    RESTARTS   AGEknative-eventing-operator-5847fcc5d5-d4cb4   1/1     Running   0          53sknative-serving-operator-587dcd9f85-zlx7v    1/1     Running   0          53s
复制代码


我们可以创建 KnativeServing 和 KnativeEventing 资源:


╰─>$ cat <<-EOF | kubectl apply -f -apiVersion: operator.knative.dev/v1alpha1kind: KnativeServingmetadata:  name: ksEOF╰─>$ cat <<-EOF | kubectl apply -f -apiVersion: v1kind: Namespacemetadata: name: knative-eventing---apiVersion: operator.knative.dev/v1alpha1kind: KnativeEventingmetadata:  name: ke  namespace: knative-eventingEOF
复制代码


新的 pod 和资源会在默认和 knative-eventing 命名空间中安装完成


╰─>$ kubectl get pods --all-namespaces -o wideNAMESPACE          NAME                                           READY   STATUS      RESTARTS   AGE     IP              NODE                   NOMINATED NODE   READINESS GATESdefault            activator-65fc4d666-7bwst                      1/1     Running     0          39s     10.233.125.68   rabbit-2.vtemian.com   <none>           <none>default            autoscaler-74b4bb97bd-ghj59                    1/1     Running     0          38s     10.233.65.195   rabbit-3.vtemian.com   <none>           <none>default            autoscaler-hpa-594f68d5c4-8qtg4                1/1     Running     0          30s     10.233.65.198   rabbit-3.vtemian.com   <none>           <none>default            controller-6b6978c965-rqb2z                    1/1     Running     0          37s     10.233.65.196   rabbit-3.vtemian.com   <none>           <none>default            istio-webhook-856d84fbf9-wvpph                 1/1     Running     0          26s     10.233.125.71   rabbit-2.vtemian.com   <none>           <none>default            knative-eventing-operator-5847fcc5d5-d4cb4     1/1     Running     0          3m18s   10.233.125.67   rabbit-2.vtemian.com   <none>           <none>default            knative-serving-operator-587dcd9f85-zlx7v      1/1     Running     0          3m18s   10.233.125.66   rabbit-2.vtemian.com   <none>           <none>default            networking-istio-6845f7cf59-bsqc2              1/1     Running     0          26s     10.233.125.69   rabbit-2.vtemian.com   <none>           <none>default            webhook-577576647-wrw56                        1/1     Running     0          36s     10.233.65.197   rabbit-3.vtemian.com   <none>           <none>istio-system       istio-ingressgateway-75694cd848-l6zfh          1/1     Running     0          64m     10.233.125.65   rabbit-2.vtemian.com   <none>           <none>istio-system       istio-pilot-576d858689-zxv76                   1/1     Running     0          64m     10.233.65.194   rabbit-3.vtemian.com   <none>           <none>knative-eventing   broker-controller-854447b8d7-vdmdz             1/1     Running     0          18s     10.233.65.200   rabbit-3.vtemian.com   <none>           <none>knative-eventing   broker-filter-b54b58854-w9jvw                  1/1     Running     0          17s     10.233.125.72   rabbit-2.vtemian.com   <none>           <none>knative-eventing   broker-ingress-75b6b8df8d-mlppj                1/1     Running     0          16s     10.233.65.201   rabbit-3.vtemian.com   <none>           <none>knative-eventing   eventing-controller-694594fdd7-gj2br           1/1     Running     0          26s     10.233.125.70   rabbit-2.vtemian.com   <none>           <none>knative-eventing   eventing-webhook-6c6b675b6f-t4ntx              1/1     Running     0          26s     10.233.65.199   rabbit-3.vtemian.com   <none>           <none>knative-eventing   imc-controller-7bb9bd7c6d-q2tsz                1/1     Running     0          10s     10.233.125.73   rabbit-2.vtemian.com   <none>           <none>knative-eventing   imc-dispatcher-6cc5c74c7f-kdj7v                1/1     Running     0          10s     10.233.125.74   rabbit-2.vtemian.com   <none>           <none>knative-eventing   mt-broker-controller-75ddc75d57-rg6jd          1/1     Running     0          15s     10.233.65.202   rabbit-3.vtemian.com   <none>           <none>knative-eventing   v0.14.0-upgrade-4sv89                          0/1     Completed   0          9s      10.233.65.203   rabbit-3.vtemian.com   <none>           <none>
复制代码


在实际测试前,让我们配置一下 DNS 组件。每次创建新部署时,我们都希望有一个唯一的 URL。Knative 可以使用xip.io做到这一点,我们只需要创建一个作业(我们将其安装在默认名称空间中)即可:


╰─>$ kubectl apply --filename https://storage.googleapis.com/knative-nightly/serving/latest/serving-default-domain.yaml
复制代码


First Knative service

在我们的初始应用程序中,我创建了一个简单的Dockerfile


FROM python:3.7-slim
WORKDIR /app
COPY requirements.txt ./RUN pip install -r requirements.txt
COPY app ./
CMD exec gunicorn app.wsgi --bind :$PORT --workers 1 --threads 8 --timeout 0
复制代码


并在vtemian/simple-django-app下公开发布图片。


╰─>$ docker push vtemian/simple-django-appThe push refers to repository [docker.io/vtemian/simple-django-app]7aa16540cfca: Pushed2e02cc50aabc: Pushed768f0318f857: Pushed663045c38f65: Pushed715414420313: Mounted from vtemian/helloworld-pythondba4fa00b93a: Mounted from vtemian/helloworld-python9f690547ed37: Mounted from vtemian/helloworld-python6376837eded8: Mounted from vtemian/helloworld-pythonc2adabaecedb: Mounted from vtemian/helloworld-pythonlatest: digest: sha256:78799d85949e31728c70ef3dbf3a492d932fc94c140cf1047d948c89141f55ab size: 2205
复制代码


要将其发布到我们的 Knative 中,我们只需要定义一个服务:


apiVersion: serving.knative.dev/v1kind: Servicemetadata:  name: simple-django-app  namespace: defaultspec:  template:    spec:      containers:      - image: docker.io/vtemian/simple-django-app
复制代码


执行kubectl get ksvc:


╰─>$ kubectl get ksvcNAME                URL                                                     LATESTCREATED             LATESTREADY   READY     REASONsimple-django-app   http://simple-django-app.default.147.75.80.160.xip.io   simple-django-app-hc2qv                 Unknown   RevisionMissing
复制代码


转到上面生成的 URL:



这个……这真是太酷了!没有数据库,我们仍然需要构建容器,但是这看起来真的很酷!


ElasticSearch 和 Kibana

在进一步测试之前,让我们配置一些可视化工具,例如用于日志分析的 ElasticSearch + Kibana 和用于指标监控的 Prometheus + Grafana。


让我们从指标组件开始。我们将按照文档进行操作,只需要编辑 config-observability 配置。它已经为我们提供了一个配置示例,我们将继续使用它。只是暂时简化示例。 接下来,我们需要创建 knative-monitoring 命名空间,并应用该清单:


╰─>$ kubectl apply --filename https://storage.googleapis.com/knative-nightly/serving/latest/monitoring-metrics-prometheus.yaml
复制代码


容器应在 knative-monitoring 命名空间中启动并运行:


╰─>$ kubectl get pod -n knative-monitoring -o wideNAME                                 READY   STATUS    RESTARTS   AGE    IP              NODE                   NOMINATED NODE   READINESS GATESgrafana-c9c94bdff-5f77v              1/1     Running   0          2m3s   10.233.65.210   rabbit-3.vtemian.com   <none>           <none>kube-state-metrics-b6bcff8f4-tvp46   1/1     Running   0          2m7s   10.233.65.209   rabbit-3.vtemian.com   <none>           <none>node-exporter-9wkpn                  2/2     Running   0          2m4s   10.80.204.131   rabbit-2.vtemian.com   <none>           <none>node-exporter-lfjss                  2/2     Running   0          2m4s   10.80.204.129   rabbit-1.vtemian.com   <none>           <none>node-exporter-zjl7b                  2/2     Running   0          2m4s   10.80.204.133   rabbit-3.vtemian.com   <none>           <none>prometheus-system-0                  1/1     Running   0          2m1s   10.233.65.211   rabbit-3.vtemian.com   <none>           <none>prometheus-system-1                  1/1     Running   0          2m1s   10.233.125.75   rabbit-2.vtemian.com   <none>           <none>
复制代码


Grafana 默认带有一些非常漂亮的仪表板,并且可以配置 Prometheus 为数据源。唯一的问题是 Prometheus 配置的不是当前正在运行的服务。我们需要获取所有当前正在运行的服务,并检查 Prometheus 服务名称,在本例中服务名为Prometheus-system-discovery


╰─>$ kubectl -n knative-monitoring get serviceNAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGEkube-controller-manager       ClusterIP   None            <none>        10252/TCP           5m36skube-state-metrics            ClusterIP   10.233.56.244   <none>        8080/TCP,8081/TCP   5m41snode-exporter                 ClusterIP   None            <none>        9100/TCP            5m38sprometheus-system-discovery   ClusterIP   None            <none>        9090/TCP            5m36s
复制代码


我们将配置 Grafana 的配置文件,并将 Pormetheus 的 URL 替换为http://prometheus-system-discovery.knative-monitoring.svc:9090


╰─>$ kubectl -n knative-monitoring edit cm grafana-datasourcesapiVersion: v1data:  prometheus.yaml: |    datasources:     - name: prometheus       type: prometheus       access: proxy       org_id: 1       url: http://prometheus-system-discovery.knative-monitoring.svc:9090       version: 1       editable: false
复制代码


删除当前正在运行的 Grafana 容器


╰─>$ kubectl delete po -n knative-monitoring --selector=app=grafanapod "grafana-c9c94bdff-rkvrg" deleted
复制代码


等待新的 Pod 启动后,你可以通过指定端口访问


╰─>$ kubectl port-forward --namespace knative-monitoring \     (kubectl get pods --namespace knative-monitoring \     --selector=app=grafana --output=jsonpath="{.items..metadata.name}") \     3000Forwarding from 127.0.0.1:3000 -> 3000
复制代码



这些默认仪表板都很有趣,但是我发现最有用的是Knative Serving - Revision HTTP Requests,它可以描述当前正在运行的应用程序。



Kubernetes Capacity Planning 给出了集群的整体运行情况。



对于日志,我们需要配置 ElasticSearch 和 Kibana。我们需要编辑 config-observability ConfigMap,并将 logging.request-log-template 设置为


╰─>$ kubectl edit cm config-observabilitylogging.request-log-template: '{"httpRequest": {"requestMethod": "{{.Request.Method}}", "requestUrl": "{{js .Request.RequestURI}}", "requestSize": "{{.Request.ContentLength}}", "status": {{.Response.Code}}, "responseSize": "{{.Response.Size}}", "userAgent": "{{js .Request.UserAgent}}", "remoteIp": "{{js .Request.RemoteAddr}}", "serverIp": "{{.Revision.PodIP}}", "referer": "{{js .Request.Referer}}", "latency": "{{.Response.Latency}}s", "protocol": "{{.Request.Proto}}"}, "traceId": "{{index .Request.Header "X-B3-Traceid"}}"}'
复制代码


接下来执行


╰─>$ kubectl apply --filename https://storage.googleapis.com/knative-nightly/serving/latest/monitoring-logs-elasticsearch.yaml
复制代码


我们将为节点设置 beta.kubernetes.io/fluentd-ds-ready="true"标签


╰─>$ kubectl label nodes --all beta.kubernetes.io/fluentd-ds-ready="true"node/rabbit-1.vtemian.com labelednode/rabbit-2.vtemian.com labelednode/rabbit-3.vtemian.com labeled
复制代码


并检查 fluentd 守护程序集是否在我们的节点上运行


╰─>$ kubectl get daemonset fluentd-ds --namespace knative-monitoringNAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                              AGEfluentd-ds   2         2         2       2            2           beta.kubernetes.io/fluentd-ds-ready=true   5m37s
复制代码


至此,在每个节点上,Fluentd 守护程序都运行正常,收集日志并将其发送到 ElasticSearch。 此外,我们需要配置 Kibana 来访问这些日志。


下面我们将启动本地代理


╰─>$kubectl proxy
复制代码


访问Kibana UI。如果服务没有启动,您可以使用以下配置创建一个


apiVersion: v1kind: Servicemetadata:  name: kibana-logging  namespace: knative-monitoring  labels:    app: kibana-logging    kubernetes.io/cluster-service: "true"    kubernetes.io/name: "Kibana"spec:  ports:  - port: 5601    protocol: TCP    targetPort: ui  selector:    app: kibana-logging
复制代码


创建一个新索引,然后等待索引处理完成。



然后将其设置为默认索引



现在日志流应该可以展示出来了



Autoscaling

现在,我们可以真正看到集群中发生的事情了,我们来配置自动缩放和 0 缩放。 为此,我们需要编辑 config-autoscaler 配置。注释中已经描述了所有选项配置,出于测试目的,下面是我正在使用的配置:


  activator-capacity: "100.0"  container-concurrency-target-default: "100"  container-concurrency-target-percentage: "70"  enable-graceful-scaledown: "true"  enable-scale-to-zero: "true"  max-scale-down-rate: "2.0"  max-scale-up-rate: "1000.0"  panic-threshold-percentage: "20.0"  panic-window-percentage: "5.0"  pod-autoscaler-class: kpa.autoscaling.knative.dev  requests-per-second-target-default: "20"  scale-to-zero-grace-period: 30s  stable-window: 60s  target-burst-capacity: "10"  tick-interval: 2s 
复制代码


所有这些选项已在文档中进行了说明,但也许我们最感兴趣的是 0 缩放


# specifies the time an inactive revision is left running before it is scaled to zero (min: 6s).scale-to-zero-grace-period: 30s# enables scale to zeroenable-scale-to-zero: "true"
复制代码


接着配置并发请求默认数


# defines how many concurrent requests are wanted at a given time (soft limit) and is the recommended configuration for autoscaling.container-concurrency-target-default: "100"
复制代码


这些是应用于每个修订版的配置,但是你可以使用注解独立控制。我们将 Horizontal Pod Autoscaler 配置为遵循 CPU 指标并在 CPU 使用率为限制的 30%时进行缩放。


apiVersion: serving.knative.dev/v1kind: Servicemetadata:  name: simple-django-app  namespace: defaultspec:  template:    metadata:      annotations:        autoscaling.knative.dev/metric: cpu        autoscaling.knative.dev/target: "70"        autoscaling.knative.dev/class: hpa.autoscaling.knative.dev    spec:      containers:      - image: docker.io/vtemian/simple-django-app        resources:          requests:            cpu: 100m
复制代码


后台启动一个 curl 查看


╰─>$ watch -n 0.1 curl -SI http://simple-django-app.default.147.75.80.160.xip.io/polls/
复制代码


发现有两个正在运行的容器


╰─>$ kubectl get po -l serving.knative.dev/service=simple-django-appNAME                                                  READY   STATUS    RESTARTS   AGEsimple-django-app-g9zf5-deployment-5b76fdf7fc-mtlwt   2/2     Running   0          3m25ssimple-django-app-mg96q-deployment-7db5bb6b9c-29ffw   2/2     Running   0          4m18s
复制代码


更进一步,我们开始Locust测试。我们将按照zalando-incubator的指示进行操作,并开始提供可用于我们服务的副本:


_________________________________________________________________________________                         L O C A L - D E P L O Y M E N T_________________________________________________________________________________Target url: http://simple-django-app.default.147.75.80.160.xip.io/pollsWhere load test script is stored (e.g. https://raw.githubusercontent.com/zalando-incubator/docker-locust/master/example/simple.py): https://raw.githubusercontent.com/zalando-incubator/docker-locust/master/example/simple.pyNumber of slave(s): 4Run type [automatic/manual]: manual----------------------------------------------                   VARIABLES----------------------------------------------TARGET_URL: http://simple-django-app.default.147.75.80.160.xip.io/pollsLOCUST_FILE: https://raw.githubusercontent.com/zalando-incubator/docker-locust/master/example/simple.pySLAVES NUMBER: 4RUN_TYPE: manual || automatic=falseNUMBER OF USERS:HATCH_RATE:DURATION [in seconds]:COMPOSE: falseSEND_ANONYMOUS_USAGE_INFO: true----------------------------------------------
复制代码


运行结果很酷炫


╰─>$ kubectl get po -l serving.knative.dev/service=simple-django-appNAME                                                  READY   STATUS      RESTARTS   AGEsimple-django-app-ns6fm-deployment-85cff985d5-249rj   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-2c6m9   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-2m6kk   2/2     Running     0          86ssimple-django-app-ns6fm-deployment-85cff985d5-2mm7t   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-2q7f8   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-5xcxf   2/2     Running     0          71ssimple-django-app-ns6fm-deployment-85cff985d5-6jxfw   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-77v6w   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-8qk5s   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-9n4h6   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-b466k   2/2     Running     0          7m57ssimple-django-app-ns6fm-deployment-85cff985d5-b8qbf   2/2     Running     0          25ssimple-django-app-ns6fm-deployment-85cff985d5-bkt66   2/2     Running     0          71ssimple-django-app-ns6fm-deployment-85cff985d5-bxbzf   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-d5xt5   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-jrchv   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-mtrvh   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-mzz7g   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-p7wvx   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-pbmzb   2/2     Running     0          25ssimple-django-app-ns6fm-deployment-85cff985d5-pzb92   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-pzkrr   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-qhjxq   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-rc2xx   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-s7lzm   2/2     Running     0          25ssimple-django-app-ns6fm-deployment-85cff985d5-sdpmf   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-ss66c   2/2     Running     0          6m27ssimple-django-app-ns6fm-deployment-85cff985d5-ssrzg   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-t424m   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-tjlsz   2/2     Running     0          71ssimple-django-app-ns6fm-deployment-85cff985d5-tzcjw   2/2     Running     0          56ssimple-django-app-ns6fm-deployment-85cff985d5-w2tsp   2/2     Running     0          71ssimple-django-app-ns6fm-deployment-85cff985d5-x9626   2/2     Running     0          41ssimple-django-app-ns6fm-deployment-85cff985d5-xm5pk   2/2     Running     0          86ssimple-django-app-ns6fm-deployment-85cff985d5-xv9sw   2/2     Running     0          56s
复制代码


监控到的请求响应结果以及资源使用情况如下





目前,我们有一个运行的 Kubernetes 集群,该集群通过 Packet 构建在裸机上,具有 3 个节点,一个正在运行的 Knative,用于服务和扩展 Docker 映像。


Mysql

最后,让我们为这个安装添加一些状态。在我目前正在工作的公司Presslabs,我们为MySQL构建了一个控制器。 它负责复制、备份和其他繁琐的操作。 它的安装及其配置相当简单,但是首先,我们需要配置一些持久卷:


apiVersion: v1kind: PersistentVolumemetadata:  labels:    type: local  name: rabbit-1.vtemian.comspec:  accessModes:  - ReadWriteOnce  capacity:    storage: 11Gi  hostPath:    path: /mnt/data    type: ""  nodeAffinity:    required:      nodeSelectorTerms:      - matchExpressions:        - key: kubernetes.io/hostname          operator: In          values:          - rabbit-1.vtemian.com  persistentVolumeReclaimPolicy: Retain  storageClassName: standard  volumeMode: Filesystem
复制代码


我们为每个节点创建一个持久卷并查看:


╰─>$ kubectl get pvNAME                   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                           STORAGECLASS   REASON   AGErabbit-1.vtemian.com   11Gi       RWO            Retain           Available                                   standard                2m58srabbit-2.vtemian.com   11Gi       RWO            Retain           Bound       default/data-mysql-operator-0   standard                3m9srabbit-3.vtemian.com   11Gi       RWO            Retain           Available                                   standard                3m19s
复制代码


现在,我们可以继续使用 mysql-operator:


╰─>$ helm repo add presslabs https://presslabs.github.io/charts╰─>$ helm install presslabs/mysql-operator --name mysql-operator --set orchestrator.persistence.storageClass=standard
复制代码


此外,我们需要一个密码,它包含我们想要用于 mysql 集群的凭据。


apiVersion: v1kind: Secretmetadata:  name: my-secrettype: Opaquedata:  ROOT_PASSWORD: bXlwYXNz  DATABASE: cmFiYml0Cg==  USER: cmFiYml0Cg==  PASSWORD: bXlwYXNz
复制代码


为该集群配置两个副本


apiVersion: mysql.presslabs.org/v1alpha1kind: MysqlClustermetadata:  name: my-clusterspec:  replicas: 2  secretName: my-secret
复制代码


现在我们有两个副本:


╰─>$ kubectl get po -l app.kubernetes.io/name=mysqlNAME                 READY   STATUS    RESTARTS   AGEmy-cluster-mysql-0   4/4     Running   0          3m11smy-cluster-mysql-1   4/4     Running   0          4m37s
复制代码


查看服务如下:


╰─>$ kubectl get service -l app.kubernetes.io/name=mysqlNAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGEmy-cluster-mysql          ClusterIP   10.233.50.17    <none>        3306/TCP            10mmy-cluster-mysql-master   ClusterIP   10.233.29.255   <none>        3306/TCP            10m
复制代码


此时,服务组件已启动并正常运行,并已通过虚拟应用程序进行了测试。让我们进一步探讨构建组件。


CI/CD

Tekton

Knative 以前有一个构建组件,现在已不推荐使用,而是首推Tekton。关于如何配置 Tekton 并将其与 Knative 集成,有一些不错的使用文档,但首先得安装它:


╰─>$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
复制代码


最后,我们只需要编辑 config-artifact-pvc 配置,以允许 Tekton 将 artifacts 保存在 PVC 中。


data:  size: 5Gi  storageClassName: default
复制代码


看一下 Tekton 容器的运行情况,我们可以看到它运行正常:


╰─>$ kubectl get po -n tekton-pipelinesNAME                                           READY   STATUS    RESTARTS   AGEtekton-pipelines-controller-5c44bcfc44-gfhdx   1/1     Running   0          85mtekton-pipelines-webhook-7bd568f6c6-vll6v      1/1     Running   0          85m
复制代码


How does Tekton work?

在建立 pipeline 之前,让我们先探讨一下 Tekton。Tekton 利用 CRDs 并允许我们通过定义 Kubernetes 资源来描述 pipeline。 我将从该指南及其官方文档进行说明


任务是用于定义实际工作单元的模板。就像定义一个函数及其参数和行为一样。它定义一个或多个步骤,并在每个步骤中执行一个容器。 可以参考来自https://github.com/knative-sample/tekton-knative 的示例。


apiVersion: tekton.dev/v1alpha1kind: Taskmetadata:  name: deploy-using-kubectlspec:  inputs:    resources:      - name: git-source        type: git    params:      - name: pathToYamlFile        description: The path to the yaml file to deploy within the git source      - name: imageUrl        description: Url of image repository      - name: imageTag        description: Tag of the images to be used.        default: "latest"  steps:    - name: update-yaml      image: alpine      command: ["sed"]      args:        - "-i"        - "-e"        - "s;__IMAGE__;${inputs.params.imageUrl}:${inputs.params.imageTag};g"        - "/workspace/git-source/${inputs.params.pathToYamlFile}"    - name: run-kubectl      image: registry.cn-hangzhou.aliyuncs.com/knative-sample/kubectl:v0.5.0      command: ["kubectl"]      args:        - "apply"        - "-f"        - "/workspace/git-source/${inputs.params.pathToYamlFile}"
复制代码


TaskRun是 Task 的运行实例。 它按顺序执行任务的所有步骤,直到所有步骤完成为止。 参考来自https://github.com/knative-sample/tekton-knative 的示例。


apiVersion: tekton.dev/v1alpha1kind: TaskRunmetadata:  name: source-to-imagespec:  taskRef:    name: source-to-image  params:    - name: pathToContext      value: "${params.pathToContext}"    - name: imageUrl      value: "${params.imageUrl}"    - name: imageTag      value: "${params.imageTag}"  resources:    inputs:      - name: git-source        resource: git-source
复制代码


管道允许我们定义多个任务。 使用 TaskRun,我们只能运行一个任务。 管道中的每个任务都作为容器执行。 参考来自https://github.com/knative-sample/tekton-knative 示例


apiVersion: tekton.dev/v1alpha1kind: Pipelinemetadata:  name: build-and-deploy-pipelinespec:  resources:    - name: git-source      type: git  params:    - name: pathToContext      description: The path to the build context, used by Kaniko - within the workspace      default: src    - name: pathToYamlFile      description: The path to the yaml file to deploy within the git source    - name: imageUrl      description: Url of image repository    - name: imageTag      description: Tag to apply to the built image  tasks:  - name: source-to-image    taskRef:      name: source-to-image    params:      - name: pathToContext        value: "${params.pathToContext}"      - name: imageUrl        value: "${params.imageUrl}"      - name: imageTag        value: "${params.imageTag}"    resources:      inputs:        - name: git-source          resource: git-source  - name: deploy-to-cluster    taskRef:      name: deploy-using-kubectl    runAfter:      - source-to-image    params:      - name: pathToYamlFile        value:  "${params.pathToYamlFile}"      - name: imageUrl        value: "${params.imageUrl}"      - name: imageTag        value: "${params.imageTag}"    resources:      inputs:        - name: git-source          resource: git-source
复制代码


与 TaskRun 类似,PipelineRun执行管道中定义的所有任务。 参考来自https://github.com/knative-sample/tekton-knative 示例。


apiVersion: tekton.dev/v1alpha1kind: PipelineRunmetadata:  generateName: tekton-kn-sample-spec:  pipelineRef:    name: build-and-deploy-pipeline  resources:    - name: git-source      resourceRef:        name: tekton-knative-git  params:    - name: pathToContext      value: "src"    - name: pathToYamlFile      value: "knative/helloworld-go.yaml"    - name: imageUrl      value: "registry.cn-hangzhou.aliyuncs.com/knative-sample/tekton-knative-helloworld"    - name: imageTag      value: "1.0"  trigger:    type: manual  serviceAccount: pipeline-account  
复制代码


PipelineResources允许我们定义任务的输入和输出所使用的对象。 参考来自https://github.com/knative-sample/tekton-knative 示例


apiVersion: tekton.dev/v1alpha1kind: PipelineResourcemetadata:  name: tekton-knative-gitspec:  type: git  params:    - name: revision      value: master    - name: url      value: https://github.com/knative-sample/tekton-knative
复制代码


Pipeline setup

以上这些都是我们要使用的所有主要组件。


让我们创建一个名为ci的新名称空间,并安装上面的清单以满足我们的需求。 我已在示例应用中提交了更改。


╰─>$ kubectl get poNAME                                                           READY   STATUS      RESTARTS   AGEtekton-simple-django-app-1-deploy-to-cluster-982xv-pod-kkmpw   0/3     Completed   0          3m18stekton-simple-django-app-1-source-to-image-8c47t-pod-ccc44     0/3     Completed   0          3m44s
复制代码


╰─>$ kubectl get pipelinerunNAME                         SUCCEEDED   REASON      STARTTIME   COMPLETIONTIMEtekton-simple-django-app-1   True        Succeeded   2m14s       95s
复制代码


Github webhook trigger

现在,我们必须通过删除并重新创建Pipelinerun来手动触发构建。让我们尝试通过配置一个 Github Webhook 使其自动化,该 Webhook 在每次进行新提交时都会触发一次构建过程。


设置过程并不复杂,但也不简单。当 github hook 产生触发时,该触发会落入一个 事件监听器 容器(通过 Istio 公开到 Internet)。我们需要从 payload 中提取相关参数,例如提交信息。为此,我们将使用TriggerBindings。然后,TriggerTemplate使用这些参数来生成我们的 pipeline 来运行。 以下配置参考了@ nikhilthomas1



我们为此过程创建一个角色,服务帐户和角色绑定。


---apiVersion: rbac.authorization.k8s.io/v1kind: Rolerules:- apiGroups:  - triggers.tekton.dev  resources:  - eventlisteners  - triggerbindings  - triggertemplates  - pipelineresources  verbs:  - get- apiGroups:  - triggers.tekton.dev  resources:  - pipelineruns  - pipelineresources  verbs:  - create- apiGroups:  - ""  resources:  - configmaps  verbs:  - get  - list  - create  - update  - delete---apiVersion: v1kind: ServiceAccountmetadata:  name: tekton-triggers-sa---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:  name: tekton-triggers-rolebindingsubjects:- kind: ServiceAccount  name: tekton-triggers-saroleRef:  apiGroup: rbac.authorization.k8s.io  kind: Role  name: tekton-triggers-role⏎
复制代码


TriggerTemplate 非常基础。它描述了绑定中可以使用的一些参数,并将它们与 PipelineRun 和其他资源打包在一起:


apiVersion: triggers.tekton.dev/v1alpha1kind: TriggerTemplatemetadata:  name: tekton-triggertemplatespec:  params:  - name: gitrevision    description: The git revision    default: master  - name: gitrepositoryurl    description: The git repository url  - name: namespace    description: The namespace to create the resources  - name: gitrepositoryname    description: The name of the deployment to be created / patched  resourcetemplates:  - apiVersion: tekton.dev/v1alpha1    kind: PipelineResource    metadata:      name: source-repo-$(params.gitrepositoryname)-$(uid)      namespace: $(params.namespace)    spec:      type: git      params:      - name: revision        value: $(params.gitrevision)      - name: url        value: $(params.gitrepositoryurl)  - apiVersion: tekton.dev/v1alpha1    kind: PipelineRun    metadata:      name: teokton-build-$(params.gitrepositoryname)-$(uid)      namespace: $(params.namespace)    spec:      pipelineRef:        name: build-and-deploy-pipeline      serviceAccountName: pipeline-account      resources:      - name: git-source        resourceRef:          name: source-repo-$(params.gitrepositoryname)-$(uid)      params:      - name: pathToContext        value: ""      - name: pathToDockerFile        value: Dockerfile      - name: pathToYamlFile        value: knative.yaml      - name: imageUrl        value: docker.io/vtemian/$(params.gitrepositoryname)      - name: imageTag        value: latest
复制代码


我们的 TriggerBinding 也将非常简单。只是将 Github 的 payload 映射到 TriggerTemplate。


apiVersion: triggers.tekton.dev/v1alpha1kind: TriggerBindingmetadata:  name: tekton-pipelinebindingspec:  params:  - name: gitrevision    value: $(body.head_commit.id)  - name: namespace    value: default  - name: gitrepositoryurl    value: $(body.repository.url)  - name: gitrepositoryname    value: $(body.repository.name)
复制代码


最后,我们需要 EventListener,与 TemplateTrigger 进行绑定


apiVersion: triggers.tekton.dev/v1alpha1kind: EventListenermetadata:  name: el-tekton-listenerspec:  serviceAccountName: tekton-triggers-sa  triggers:  - bindings:      - name: tekton-pipelinebinding    template:      name: tekton-triggertemplate
复制代码


╰─>$ kubectl get service | grep tekel-tekton-listener                ClusterIP      10.233.47.3     <none>                                                 8080/TCP                             114m
复制代码


现在服务启动了,我们只需要使用 Istio 公开它。让我们使用一个单独的服务帐户来使用 Tekton 工具:


kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: tekton-triggers-createwebhookrules:- apiGroups:  - ""  resources:  - secrets  verbs:  - get  - list  - create  - update  - delete- apiGroups:  - tekton.dev  resources:  - eventlisteners  verbs:  - get  - list  - create  - update  - delete- apiGroups:  - extensions  resources:  - ingresses  verbs:  - create  - get  - list  - delete  - update---apiVersion: v1kind: ServiceAccountmetadata:  name: tekton-triggers-createwebhook---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:  name: tekton-triggers-createwebhooksubjects:- kind: ServiceAccount  name: tekton-triggers-createwebhookroleRef:  apiGroup: rbac.authorization.k8s.io  kind: Role  name: tekton-triggers-createwebhook⏎
复制代码


跟随任务本身:


 apiVersion: tekton.dev/v1beta1kind: Taskspec:  params:  - description: The external domain for the EventListener    name: ExternalDomain    type: string  - description: The name of the Service used in the VirtualService    name: Service    type: string  - description: The service port that the VirtualService is being created on    name: ServicePort    type: string  steps:  - args:    - -ce    - |      set -ex      cat << EOF | kubectl create -f -      apiVersion: networking.istio.io/v1alpha3      kind: Gateway      metadata:        name: $(inputs.params.Service)-gateway      spec:        selector:          istio: ingressgateway        servers:        - port:            number: 80            name: http-$(inputs.params.Service)            protocol: HTTP          hosts:          - $(inputs.params.ExternalDomain)      ---      apiVersion: networking.istio.io/v1alpha3      kind: VirtualService      metadata:        name: $(inputs.params.Service)-virtual-service      spec:        hosts:        - $(inputs.params.ExternalDomain)        gateways:        - $(inputs.params.Service)-gateway        http:        - route:          - destination:              host: $(inputs.params.Service)              port:                number: $(inputs.params.ServicePort)      EOF    command:    - sh    image: lachlanevenson/k8s-kubectl:latest    name: create-istio-gateway-virtualservice    resources: {}  volumes:  - emptyDir: {}    name: work
复制代码


最后是初始化:


apiVersion: tekton.dev/v1beta1kind: TaskRunmetadata:spec:  params:  - name: ExternalDomain    value: simple-django-app-event-listner.default.147.75.80.160.xip.io  - name: Service    value: el-tekton-listener  - name: ServicePort    value: "8080"  serviceAccountName: tekton-triggers-createwebhook  taskRef:    kind: Task    name: create-istio-gateway-virtualservice  timeout: 1h0m0s
复制代码


然后检查结果:


╰─>$ kubectl get VirtualServiceNAME                                 GATEWAYS                                                          HOSTS                                                                                                                                                  AGEel-tekton-listener-virtual-service   [el-tekton-listener-gateway]                                      [simple-django-app-event-listner.default.147.75.80.160.xip.io]
复制代码


现在我们已经在集群中启动并运行了工具,我们可以创建 webhook。 为此,我们需要一 Github token:


apiVersion: v1kind: Secretmetadata:  name: webhook-secretstringData:  #https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line#creating-a-token  token: <token>  secret: random-string-data
复制代码


下面的 task 将会创建 webhook


apiVersion: tekton.dev/vlalpha1kind: Taskmetadata:  name: create-webhookspec:  volumes:  - name: github-secret    secret:      secretName: $(inputs.params.GitHubSecretName)  inputs:    params:    - name: ExternalDomain      description: "The external domain for the EventListener e.g. `$(inputs.params.EventListenerName).<PROXYIP>.nip.io`"    - name: GitHubUser      description: "The GitHub user"    - name: GitHubRepo      description: "The GitHub repo where the webhook will be created"    - name: GitHubOrg      description: "The GitHub organization where the webhook will be created"    - name: GitHubSecretName      description: "The Secret name for GitHub access token. This is always mounted and must exist"    - name: GitHubAccessTokenKey      description: "The GitHub access token key name"    - name: GitHubSecretStringKey      description: "The GitHub secret string key name"    - name: GitHubDomain      description: "The GitHub domain. Override for GitHub Enterprise"      default: "github.com"    - name: WebhookEvents      description: "List of events the webhook will send notifications for"      default: '[\"push\",\"pull_request\"]'  steps:  - name: create-webhook    image: pstauffer/curl:latest    volumeMounts:    - name: github-secret      mountPath: /var/secret    command:    - sh    args:    - -ce    - |      set -e      echo "Create Webhook"      if [ $(inputs.params.GitHubDomain) = "github.com" ];then        curl -v -d "{\"name\": \"web\",\"active\": true,\"events\": $(inputs.params.WebhookEvents),\"config\": {\"url\": \"$(inputs.params.ExternalDomain)\",\"content_type\": \"json\",\"insecure_ssl\": \"1\" ,\"secret\": \"$(cat /var/secret/$(inputs.params.GitHubSecretStringKey))\"}}" -X POST -u $(inputs.params.GitHubUser):$(cat /var/secret/$(inputs.params.GitHubAccessTokenKey)) -L https://api.github.com/repos/$(inputs.params.GitHubOrg)/$(inputs.params.GitHubRepo)/hooks      else        curl -d "{\"name\": \"web\",\"active\": true,\"events\": $(inputs.params.WebhookEvents),\"config\": {\"url\": \"$(inputs.params.ExternalDomain)/\",\"content_type\": \"json\",\"insecure_ssl\": \"1\" ,\"secret\": \"$(cat /var/secret/$(inputs.params.GitHubSecretStringKey))\"}}" -X POST -u $(inputs.params.GitHubUser):$(cat /var/secret/$(inputs.params.GitHubAccessTokenKey)) -L https://$(inputs.params.GitHubDomain)/api/v3/repos/$(inputs.params.GitHubOrg)/$(inputs.params.GitHubRepo)/hooks      fi
复制代码


并进行初始化


apiVersion: tekton.dev/v1alpha1kind: TaskRunmetadata:  name: create-api-repo-webhook-runspec:  taskRef:    name: create-webhook  inputs:    params:    - name: GitHubOrg      value: "vtemian"    - name: GitHubUser      value: "vtemian"    - name: GitHubRepo      value: "simple-django-app"    - name: GitHubSecretName      value: webhook-secret    - name: GitHubAccessTokenKey      value: token    - name: GitHubSecretStringKey      value: secret    - name: ExternalDomain      value: http://simple-django-app-event-listner.default.147.75.80.160.xip.io  timeout: 1000s  serviceAccountName: tekton-triggers-createwebhook
复制代码



现在,每次我们进行新更改时,都会触发一个新构建:


╰─>$ kubectl get po | grep teoteokton-build-simple-django-app-2fcdr-source-to-image-v86-mwxhw   0/3     Error       0          71mteokton-build-simple-django-app-qlw5w-source-to-image-sz2-gpqdm   0/3     Error       0          73mteokton-build-simple-django-app-sl9zf-source-to-image-knl-tzxpk   1/3     Running     0          18steokton-build-simple-django-app-xh54x-deploy-to-cluster-b-5p7r4   0/3     Completed   0          66mteokton-build-simple-django-app-xh54x-source-to-image-wv5-9bsdt   0/3     Completed   0          66m
复制代码


并且部署应用程序


╰─>$ kubectl get po | grep simplesimple-django-app-cjx8b-deployment-7cd5c5999d-vwjhv               2/2     Running     0          4h3msimple-django-app-d2n6n-deployment-77c664bf4f-pz6hg               2/2     Running     0          4h29msimple-django-app-hcmpl-deployment-7687b96b5f-pv2wz               2/2     Running     0          67m
复制代码


Conclusions

总结一下,我们设法配置了裸机基础架构,安装了 Knative,并拥有了一个通过 git push 代码来构建和部署我们应用程序新版本的 CI/CD。这些看起来有些麻烦,所以我们留下了一些有关修订、路由蓝绿部署的细节。


从工程角度来看,VercelHerokuGoogle Cloud RunAWS ECS之类的平台确实非常出色,因为它们减轻了部署应用程序和管理基础架构的负担。感谢 Knative 和 Tekton,它降低了我们使用此类平台的门槛。


英文原文:


Building a serverless hosting platform


2020-07-23 14:362142
用户头像
王坤祥 日拱一卒,功不唐捐。

发布了 70 篇内容, 共 13.4 次阅读, 收获喜欢 123 次。

关注

评论

发布
暂无评论
发现更多内容

谈谈容器和K8s

Gabriel

当国产iVX遇上新晋产品PowerPlatform,能否披荆斩棘、稳住阵脚?

代码制造者

程序员 编辑器 低代码 快速开发 开发工具

【自学成才系列一】multipass安装篇

小朱

multipass

信创舆情一线--英特尔暂停向浪潮供货

统小信uos

服务器 舆情 芯片

架构0期Week4作业2

Nan Jiang

最详细的 Spring Cloud OAuth2 单点登录使用教程送给大家

小闫

面试 后端 JVM SpringCloud

Linux 操作系统!开篇!!!

cxuan

Linux

Java 面试必考的 6 个技能,都在这了

架构大数据双料架构师

javascript 部分数据类型的用法

Isuodut

游戏夜读 | 关卡设计新手必看

game1night

面试官:十亿级数据ES搜索怎么优化?我直接傻了

犬来八荒

Java 面试 大厂

被“假”老干妈耍惨了?憨憨腾讯花1624万卖萌,引全网吃瓜!

程序员生活志

腾讯 互联网 大厂

架构0期Week4作业1

Nan Jiang

去面试Spring Cloud 被问的35个问题

小谈

面试 springboot SpringCloud buffer JVM原理

Linux 性能优化实战 笔记-IO篇

程序员老王

让你秒懂Spring中Mybatis的花样配置

小谈

Java spring 面试 Spring Cloud mybatis

极客大学架构师训练营 系统架构 第8课 听课总结

John(易筋)

极客时间 系统架构 极客大学 极客大学架构师训练营 系统架构演化

使用 Flutter 快速实现请假与写周报应用

LeanCloud

flutter 后端 数据 教程

拥抱开源开放,易观技术开发者的星海征途

易观大数据

海豚调度 调度引擎

这20道微服务面试题要是不会,offer就与你无缘

犬来八荒

Java 架构 面试 微服务

Google官方MVP+Dagger2架构详解

小吴选手

架构 架构师 架构是训练营

「NIO系列」——之Reactor模型

小谈

Spring Boot reactor 后端 nio SpringCloud

终于有大佬把TCP/IP协议讲清楚了!面试再也不怂面试官提问了

小闫

jdk JVM Netty buffer TCP/IP

到底什么是HashMap?

小闫

Java spring 后端 JVM hashmap

攻克SpringBoot底层源码后,才发现开发原来这么香

无予且行

Java spring 面试 Spring Boot 开发

七月份最新“美团+字节+腾讯”面试题,测试一下你能走到哪一面?

犬来八荒

Java 面试 线程’

如果是你,年薪80万和阿里P7月薪36K,会怎么选?

犬来八荒

Java 腾讯 面试 阿里

基于 Flagger 和 Nginx-Ingress 实现金丝雀发布

郭旭东

Kubernetes CI/CD

再有人问你分布式事务,把这篇扔给他

码哥小胖

分布式 Java 分布式

如何快速将 Linux 系统制作成 ISO 镜像文件?

JackTian

Linux 运维 操作系统 镜像文件 ISO

手把手教你在Serverless平台上部署应用_架构_Vlad Temian_InfoQ精选文章