探索如何将 Kubernetes 和 Istio 的完整功能嵌入到单一二进制文件中,实现无 Pod 的极简部署方案。
本文为译文,阅读原文请转到:https://jimmysong.io/trans/podless-kubernetes-istio/
Kubernetes 经常被批评(有些不公平)操作起来过于复杂,促使大多数人依赖托管服务。然而,k3s
[1] 某种程度上颠覆了这一点,将完整的 Kubernetes 发行版打包成一个二进制文件。这非常方便,特别是在物联网等小型环境中运行时;虽然隔离组件对非常大规模、先进的部署有好处,但对较小的环境来说,操作微服务可能只是一种负担——这正是 Istio 多年前选择重构为更单体架构的原因[2]。
然而,它还是没有那么“精简”。在一个空集群中运行 k3d cluster create test
后,我们会在集群中看到各种 pod:
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system local-path-provisioner-6c86858495-gc9jq 1/1Running02m18s
kube-system coredns-6799fbcd5-pdf4b 1/1Running02m18s
kube-system helm-install-traefik-crd-cp9s2 0/1Completed02m18s
kube-system helm-install-traefik-pch7c 0/1Completed12m18s
kube-system traefik-f4564c4f4-q4lkj 1/1Running02m8s
kube-system metrics-server-54fd9b65b-d69w6 1/1Running02m18s
kube-system svclb-traefik-58c5bb65-sq54b 2/2Running0 2m8s
k3d
[3] 是一个方便的工具,可以在 Docker 内部部署k3s
,便于测试。
这是怎么回事?我们的“单二进制 Kubernetes”怎么变成了 6 个不同的容器?
虽然 k3s 将许多组件(kube-proxy
、flannel
、containerd
、kubelet
等)嵌入到一个二进制文件中,但其他组件则作为标准 pod 在集群中运行。
此外,一旦我们部署了我们最喜欢的 服务网格[4],我们将会有更多的 pod,使我们离没有 pod 的目标更远。
没有 pod 的 Kubernetes?
那么问题是——我们能否通过进一步推进 k3s
的理念,将完整的集群功能嵌入到一个二进制文件中,来获得一个功能齐全的 Kubernetes 和 Istio 部署?
警告:这些是实验性概念;绝不要在生产环境中尝试!
首先,我们可以直接去除一些不必要的组件,如 servicelb
(负载均衡服务需要)、traefik
(Ingress 需要)、local-storage
(PVC 需要)和 metrics-server
(kubectl top
需要)。
这就剩下 coredns
和 Istio。
如果我们追求极简,我们肯定会希望使用 Istio 的 ambient mode[5],它完全不需要 sidecar。幸运的是,它开箱即用并且有完整的 DNS 支持[6]。这让我们可以去掉 coredns
。
这样一来,如果我们能运行 Istio ambient,就可以去掉 kube-system
中的所有内容。这相对简单;难点在于不为 Istio 添加更多的 pod。
嵌入 Istio
通过 k3s
的一个分支,我修改了它,使 Istio 本身嵌入到 k3s
中。k3s
可以作为服务器和/或代理运行。通常你会有 1 个服务器,每个其他节点作为代理运行。
在 server
上,我们希望运行 Istiod
(Istio 的控制平面)。在代理上,我们希望运行 istio-cni
(每个节点的控制平面)和 ztunnel
(每个节点的数据平面)。
这三个组件都可以直接嵌入到 k3s
中,只需一些工作!
使用这个自定义构建,我们可以通过一些自定义配置启动一个新的 k3d
集群,禁用我们不再需要的组件:
apiVersion: k3d.io/v1alpha5
kind:Simple
metadata:
name:podless
servers:1
agents:1
options:
k3d:
wait:true
timeout:"60s"
disableLoadbalancer:true
disableRollback:true
k3s:
extraArgs:
-arg:--disable-cloud-controller
nodeFilters:
-server:*
-arg:--disable-kube-proxy
nodeFilters:
-server:*
-arg:--disable-network-policy
nodeFilters:
-server:*
-arg:--disable-helm-controller
nodeFilters:
-server:*
-arg:--disable=coredns,servicelb,traefik,local-storage,metrics-server
nodeFilters:
- server:*
这里我们禁用了上面看到的所有 pod,包括一些额外的。
一个显著的例子是 kube-proxy
。像其他一些项目一样(如 Cilium[7]),Istio 的 ztunnel
可以有效地替代大多数用例中的 kube-proxy
。
无 pod 的服务网格
所有配置就绪后,我们的集群是什么样子?
$ kubectl get pods --all-namespaces
No resources found
到目前为止一切顺利....当然,什么都不运行很容易;真正的挑战是保持集群的功能。
让我们部署一些应用程序 pod。再次强调,这些是集群中的唯一 pod:
$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
default shell-5fff89ccf5-98kgg 1/1 Running 0 19s
default echo-66d88ff694-9qprp 1/1 Running 0 14s
然后我们可以发送流量:
$ kubectl exec deploy/shell -- curl -s echo
RequestHeader=Accept:*/*
RequestHeader=User-Agent:curl/8.5.0
Hostname=echo-66d88ff694-9qprp
流量完全正常,包括服务流量(以前由 kube-proxy
处理)和 DNS(以前由 coredns
处理)。现在这些全部由 ztunnel
处理,并且所有内容都通过安全的 mTLS 传输。
除了 mTLS 加密,我们还可以基于 mTLS 身份应用策略。同样,这些都由 ztunnel
执行。
apiVersion: security.istio.io/v1
kind:AuthorizationPolicy
metadata:
name:allow-default
spec:
action:ALLOW
selector:
matchLabels:
app:echo
rules:
-from:
-source:
namespace:["cluster.local/ns/default/sa/shell"]
现在 default
命名空间的流量被允许,但其他流量不被允许。我们可以通过从 shell
发送流量以及我在 other
命名空间中部署的新测试工作负载来验证这一点:
$ kubectl exec deploy/shell -- curl -s echo
RequestHeader=Accept:*/*
RequestHeader=User-Agent:curl/8.5.0
Hostname=echo-66d88ff694-9qprp
$ kubectl exec deploy/shell -n other -- curl -s echo
command terminated with exit code 56
正如预期的那样,我们的其他应用程序被拒绝了!
此外,如果我们愿意,我们可以将流量升级通过完整的 HTTP 代理("waypoint"[8]):
$ istioctl x waypoint apply --enroll-namespace
waypoint default/waypoint applied
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
echo-66d88ff694-czd65 1/1Running
093m
shell-56bd5dbdbf-f4gh9 1/1Running093m
waypoint-7cd4dc789f-2s7z21/1Running041s
$ kubectl exec deploy/shell -- curl -s echo
RequestHeader=Accept:*/*
RequestHeader=User-Agent:curl/8.5.0
RequestHeader=X-Request-Id:18d72190-9caa-4162-8bc5-4c11518d7568
Hostname=echo-66d88ff694-czd65
现在我们的 waypoint 已经部署,所有到命名空间的流量会自动转发到它,在那里可以执行完整的 HTTP 策略。这里,我们可以看到 X-Request-Id
被添加到我们的请求中,但我们还可以获得 自动配置的其他功能[9],以及更多 我们可以配置的内容[10]。
总结
最终,我们能够部署一个完整的 Kubernetes 集群和服务网格,所有基础设施组件嵌入到一个隐藏的节点二进制文件中——集群功能不需要 pod。
这实际操作起来是否实用?不太实用。然而,这确实表明 Kubernetes/Istio 被认为过于臃肿和复杂的看法并不完全准确。
它真的比典型的集群更简单吗?某种程度上是的……我们确实替换了两个组件(kube-proxy
和 coredns
),但其余的我们基本上只是隐藏和打包。这显然不如完全替换有意义,但也不错。话虽如此,隐藏东西对 社交媒体参与度[11] 有好处,而 k3s
通过有效地隐藏和打包取得了巨大成功,因此显然提供了一些实实在在的好处。
引用链接
[1]
k3s
: https://k3s.io/[2]
Istio 多年前选择重构为更单体架构的原因: https://blog.christianposta.com/microservices/istio-as-an-example-of-when-not-to-do-microservices/[3]
k3d
: https://github.com/k3d-io/k3d/[4]
服务网格: https://istio.io/[5]
ambient mode: https://istio.io/latest/docs/ops/ambient/getting-started/[6]
DNS 支持: https://istio.io/latest/docs/ops/configuration/traffic-management/dns-proxy/[7]
Cilium: https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/[8]
"waypoint": https://istio.io/latest/docs/ops/ambient/architecture/[9]
自动配置的其他功能: https://istio.io/latest/blog/2021/zero-config-istio/[10]
我们可以配置的内容: https://istio.io/latest/docs/tasks/[11]
社交媒体参与度: https://twitter.com/wm/status/1577081662848241664
获取更多云原生社区资讯,加入微信群,请加入云原生社区,点击阅读原文了解更多。