在 Knative 上部署 12 要素应用程序
原文:Deploying 12 Factor Apps to Knative
我尝试按照本文内容在 Azure 上使用 ACS-Engine 部署的 Kubernetes 上进行了测试,版本部分的案例得到了相反结果,不过还是按照原文发了出来,作为一个获取感性认识的入门还是不错的。
Google Next18 活动中,Google 宣称将会把 GKE 上的无服务器插件以 Knative 的名称进行开源。当时它被描述为无服务器平台的构建块,由此推断,Knative 可能需要 Google、Pivotal 或者 RedHat 的协助才能使用。这可能是开源的古怪时机。从我最初的摸索来看,Knative 能工作;当我把 Heroku/Cloud Foundry buildpacks 加入进来之后,整个系统变得越来越像 Heroku/Cloud Foundry,相对于原始 Kubernetes,我更加了解和喜爱这一系统。
本文中我们会将 Knative 安装到你自己的 Kubernetes 集群中(knctl install
),然后用 Knative 做些有趣的事情(knctl deploy
)。
Knative 能够为在 Kubernetes 集群上运行无状态应用的运维人员带来很多惊喜。对我来说,最引人入胜的一点就是伸缩性:在高负载时候进行扩容,没人喜欢你的应用了,就会一直缩容到 0。
下载和安装 Knative 客户端工具 knctl
,然后就可以在你的 Kubernetes 上部署 Knative,然后发布你的应用了。
在 MacOS 中,可以利用我们的 Homebrew tap 进行安装:
brew install starkandwayne/kubernetes/knctl
这里我假设你再使用 Minikube。在 Minikube 中,可以使用 Node Port 代替 Load balancer:
minikube start --memory=8192 --cpus=3 \
--kubernetes-version=v1.11.3 \
--vm-driver=hyperkit \
--bootstrapper=kubeadm
knctl install --node-ports --exclude-monitoring
可以参考 Knative 文档来获取在其它类型 Kubernetes 集群上进行部署的要点。
knctl install
命令可能要花上几分钟,甚至是十分钟或者更多。这个过程中需要下载大概一打镜像。不管是互联网带宽还是镜像尺寸都可能有变化,所以坐下放松一会,或者出去走走也好。
如果 knctl install
失败了,可能是你的 Internet 比较慢,Docker 镜像在命令超时之前还没能完成下载。运行 kubectl get pods --all-namespaces
直到所有 Pod 都在运行,然后再次运行 knctl install
命令继续完成安装过程。
现在你的 Kubernetes 就是无服务器架构了,不错吧。
可以运行 kubectl get pods --all-namespaces
看看原始的 Knative 的 Pod 们。
$ 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
...
部署预构建的应用
接下来我们试试用一个现有的 Docker 镜像来作为自动伸缩的无状态应用运行到 Knative 上,在当前 Kubernetes 命令空间中:
kubectl create ns helloworld
knctl deploy \
--namespace helloworld \
--service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev1
命令执行后会输出一些信息,表明 hello
服务已经创建,它的第一个版本 hello-00001
已经创建,并且被标记为 latest
和 previous
(第一个版本)。
Name hello
Waiting for new revision to be created...
Tagging new revision 'hello-00001' as 'latest'
Tagging new revision 'hello-00001' as 'previous'
Succeeded
我们可以用一个 curl 请求,发送到 Knative 的路由层,来调用我们的 hello
服务:
$ knctl curl --namespace helloworld --service hello
Running: curl '-H' 'Host: hello.helloworld.example.com' 'http://192.168.64.8:32380'
Hello World: Rev1!
Succeeded
如果没有马上显示 Hello World: Rev1!
,可能是因为你的系统还在下载应用镜像、可以稍后重试。
我使用的是 Minikube 中的 NodePort Ingress,这意味着我不能设置漂亮的 DNS 路由。以后我会讨论一下公共负载均衡、DNS、Knative 路由以及 https://github.com/cppforlife/kwt
。
knctl deploy
之后,我们的 Kubernetes 用单 Pod 的形式运行这一服务:
$ kubectl get pods --namespace helloworld
NAME READY STATUS RESTARTS AGE
hello-00001-deployment-5864685cbc-v8r7n 3/3 Running 0 15s
Knative 中,服务的版本是一个重要特性。我们可以看到我们的唯一版本的服务正在处理 100% 的流量:
$ knctl revisions list --namespace helloworld --service hello
Revisions for service 'hello'
Name Tags Allocated Traffic % Serving State Annotations Age
hello-00001 latest 100% Active - 3m
previous
1 revisions
下一步我们使用 knctl deploy
创建一个新的版本,然后把所有流量分配到新版本:
$ knctl deploy \
--namespace helloworld \
--service hello \
--image gcr.io/knative-samples/helloworld-go \
--env TARGET=Rev2
Name hello
Waiting for new revision (after revision 'hello-00001') to be created...
Tagging new revision 'hello-00002' as 'latest'
Tagging older revision 'hello-00001' as 'previous'
Succeeded
现在请求被发送到了我们的新版本:
$ knctl revisions list --namespace helloworld --service hello
Revisions for service 'hello'
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 '-H' 'Host: hello.helloworld.example.com' 'http://192.168.64.8:32380'
Hello World: Rev2!
部署第二个版本之后,起初两个版本的 Pod 都会持续运行:
$ 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
我们会看到 Knative 自动将没有路由指向的版本缩容到 0。
路由
下图展示了路由到服务某版本的过程中所涉及到的 Knative Serving 模块:
版本是代码、依赖和配置的的只读快照。没有被路由引用的版本会被放弃,其中的 Kubernetes 资源会被删除。
我们可以看到当前的路由:
$ knctl routes list --namespace helloworld
Routes in namespace 'helloworld'
Name Traffic All Traffic Assigned Ready Domain Age
hello 100% -> hello: true true hello.helloworld.example.com 2h
缩容至 0
如果离开五分钟再回来,你会发现 hello-00002
Pod 正在被终结或者已经不见了:
$ 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
下一次 knctl curl
,Knative 会动态的启动一个 Pod 来满足这一请求。
$ 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
我还不太清楚为什么 hello-00001-deployment-...
Pod 没有被缩容和终结。
未完待续
后续文章中将会尝试:
- Knative Build 组件:使用
Dockerfile
或者 Cloud Foundry buildpack 自动从定制代码构建容器镜像(代码可以保存本地或者 Git 仓库之中)。 - 为 Kubernetes 的负载均衡设置 DNS,从而为 Knative 路由和服务提供公共 URL。
- 在不同版本之间进行流量分割(例如 10% 到最新版本,90% 到前一版本;然后将 100% 分配给新版本,老版本流量降低到 0%)。
- Knative Eventing:在应用中进行 CloudEvents的绑定和分发。让你的服务更加“无服务器”。
社区
Knative 核心团队有自己的 Knative Slack,可以在 https://slack.knative.dev 申请加入。
knative-dev Group 中包含了总结和提议。
鸣谢
感谢 Google 的 Mark Chmarny,在 2018 Spring One 上首先回应了我的问题。
感谢 Pivotal 的 Dmitriy Kalinin 花时间帮助我将 Knative 运行起来,并给我展示了他的 knctl Knative CLI,以及 kwt Kubernetes Workstation Tools。相对于 YAML + kubectl
组合来说,一个好用的客户端工具更能够帮助 Knative 走向实用。