Istio 1.10 让 StatefulSets 更轻松

了解如何轻松地使用 Istio 1.10 部署 StatefulSets。

2021 年 5 月 19 日 | 作者:林森 - Solo.io,克里斯蒂安·波斯塔 - Solo.io,约翰·霍华德 - Google,徐仲虎 - 华为

Kubernetes StatefulSets 通常用于管理有状态的应用程序。除了管理一组 Pods 的部署和扩展之外,StatefulSets 还保证了这些 Pods 的排序和唯一性。与 StatefulSets 一起使用的常见应用程序包括 ZooKeeper、Cassandra、Elasticsearch、Redis 和 NiFi。

Istio 社区一直在朝着对 StatefulSets 的零配置支持迈进;从自动 mTLS,到消除创建 DestinationRuleServiceEntry 资源的需要,再到 Istio 1.10 中最近的 Pod 网络更改

使用 StatefulSet 与服务网格有什么不同?StatefulSet Pod 是根据相同的规范创建的,但不可互换:每个 Pod 都拥有一个持久标识,在任何重新调度过程中都会保留。在 StatefulSet 中运行的应用程序类型通常是需要在其 Pod 之间进行通信的应用程序,而且由于它们来自硬编码 IP 地址的世界,它们可能只监听 Pod IP,而不是 0.0.0.0

例如,ZooKeeper 默认情况下配置为不监听所有 IP 以进行仲裁通信

quorumListenOnAllIPs=false

在过去的几个版本中,Istio 社区已经 报告了许多 关于支持在 StatefulSets 中运行的应用程序的问题。

Istio 1.10 之前的 StatefulSets 实际应用

在一个运行 Kubernetes 1.19 的 GKE 集群中,我们安装了 Istio 1.9.5。我们在 default 命名空间中启用了自动 sidecar 注射,然后我们使用 Bitnami 提供的 Helm 图表 安装 ZooKeeper,以及用于交互式调试的 Istio sleep Pod。

$ helm repo add bitnami https://charts.bitnami.com/bitnami
$ helm install my-release bitnami/zookeeper --set replicaCount=3
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/sleep/sleep.yaml

几分钟后,所有 Pod 都正常启动,并带有 sidecar 代理

$ kubectl get pods,svc
NAME                             READY   STATUS    RESTARTS   AGE
my-release-zookeeper-0           2/2     Running   0          3h4m
my-release-zookeeper-1           2/2     Running   0          3h4m
my-release-zookeeper-2           2/2     Running   0          3h5m
pod/sleep-8f795f47d-qkgh4        2/2     Running   0          3h8m

NAME                            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                            AGE
my-release-zookeeper            ClusterIP   10.100.1.113   <none>        2181/TCP,2888/TCP,3888/TCP         3h
my-release-zookeeper-headless   ClusterIP   None           <none>        2181/TCP,2888/TCP,3888/TCP         3h
service/sleep                   ClusterIP   10.100.9.26    <none>        80/TCP                             3h

我们的 ZooKeeper 服务是否正常工作,状态是否为 运行?让我们来了解一下!ZooKeeper 监听 3 个端口

默认情况下,ZooKeeper 安装会将端口 2181 配置为监听 0.0.0.0,但端口 2888 和 3888 仅监听 Pod IP。让我们检查一下从 ZooKeeper Pod 中的任何一个 Pod 到这三个端口的网络状态

$ kubectl exec my-release-zookeeper-1 -c istio-proxy -- netstat -na | grep -E '(2181|2888|3888)'
tcp        0      0 0.0.0.0:2181            0.0.0.0:*               LISTEN
tcp        0      0 10.96.7.7:3888          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:2181          127.0.0.1:37412         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37486         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37456         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37498         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37384         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37514         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37402         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37434         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37526         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37374         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37442         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:37464         TIME_WAIT

端口 2888 或 3888 上没有 已建立 的连接。接下来,让我们获取 ZooKeeper 服务器状态

$ kubectl exec my-release-zookeeper-1 -c zookeeper -- /opt/bitnami/zookeeper/bin/zkServer.sh status
/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Error contacting service. It is probably not running.

从上面的输出中,您可以看到 ZooKeeper 服务无法正常运行。让我们检查一下 ZooKeeper Pod 中的集群配置

$ istioctl proxy-config cluster my-release-zookeeper-1 --port 3888 --direction inbound -o json
[
    {
        "name": "inbound|3888||",
        "type": "STATIC",
        "connectTimeout": "10s",
        "loadAssignment": {
            "clusterName": "inbound|3888||",
            "endpoints": [
                {
                    "lbEndpoints": [
                        {
                            "endpoint": {
                                "address": {
                                    "socketAddress": {
                                        "address": "127.0.0.1",
                                        "portValue": 3888
                                    }
                                }
                            }
                        }
                    ]
                }
            ]
        },
...

有趣的是,端口 3888 上的入站流量的端点是 127.0.0.1。这是因为在 Istio 1.10 之前的版本中,Envoy 代理会将入站流量重定向到 回环 接口,如 我们关于更改的博文 中所述。

使用 Istio 1.10 的 StatefulSets 实际应用

现在,我们将集群升级到 Istio 1.10,并将 default 命名空间配置为启用 1.10 sidecar 注射。让我们滚动重启 ZooKeeper StatefulSet,以便更新 Pod 以使用新版本的 sidecar 代理

$ kubectl rollout restart statefulset my-release-zookeeper

一旦 ZooKeeper Pod 达到运行状态,让我们检查一下从任何一个 ZooKeeper Pod 到这三个端口的网络连接

$ kubectl exec my-release-zookeeper-1 -c istio-proxy -- netstat -na | grep -E '(2181|2888|3888)'
tcp        0      0 0.0.0.0:2181            0.0.0.0:*               LISTEN
tcp        0      0 10.96.8.10:2888         0.0.0.0:*               LISTEN
tcp        0      0 10.96.8.10:3888         0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.6:42571         10.96.8.10:2888         ESTABLISHED
tcp        0      0 10.96.8.10:2888         127.0.0.6:42571         ESTABLISHED
tcp        0      0 127.0.0.6:42655         10.96.8.10:2888         ESTABLISHED
tcp        0      0 10.96.8.10:2888         127.0.0.6:42655         ESTABLISHED
tcp        0      0 10.96.8.10:37876        10.96.6.11:3888         ESTABLISHED
tcp        0      0 10.96.8.10:44872        10.96.7.10:3888         ESTABLISHED
tcp        0      0 10.96.8.10:37878        10.96.6.11:3888         ESTABLISHED
tcp        0      0 10.96.8.10:44870        10.96.7.10:3888         ESTABLISHED
tcp        0      0 127.0.0.1:2181          127.0.0.1:54508         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54616         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54664         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54526         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54532         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54578         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54634         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54588         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54610         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54550         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54560         TIME_WAIT
tcp        0      0 127.0.0.1:2181          127.0.0.1:54644         TIME_WAIT

端口 2888 和 3888 上都有 已建立 的连接!接下来,让我们检查一下 ZooKeeper 服务器状态

$ kubectl exec my-release-zookeeper-1 -c zookeeper -- /opt/bitnami/zookeeper/bin/zkServer.sh status
/opt/bitnami/java/bin/java
ZooKeeper JMX enabled by default
Using config: /opt/bitnami/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower

ZooKeeper 服务现在正在运行!

我们可以从 sleep Pod 连接到每个 ZooKeeper Pod,并运行以下命令来发现 StatefulSet 中每个 Pod 的服务器状态。请注意,无需为任何 ZooKeeper Pod 创建 ServiceEntry 资源,并且我们可以从 sleep Pod 直接使用它们的 DNS 名称(例如 my-release-zookeeper-0.my-release-zookeeper-headless)来调用这些 Pod。

$ kubectl exec -it deploy/sleep -c sleep -- sh  -c 'for x in my-release-zookeeper-0.my-release-zookeeper-headless my-release-zookeeper-1.my-release-zookeeper-headless my-release-zookeeper-2.my-release-zookeeper-headless; do echo $x; echo srvr|nc $x 2181; echo; done'
my-release-zookeeper-0.my-release-zookeeper-headless
Zookeeper version: 3.7.0-e3704b390a6697bfdf4b0bef79e3da7a4f6bac4b, built on 2021-03-17 09:46 UTC
Latency min/avg/max: 1/7.5/20
Received: 3845
Sent: 3844
Connections: 1
Outstanding: 0
Zxid: 0x200000002
Mode: follower
Node count: 6

my-release-zookeeper-1.my-release-zookeeper-headless
Zookeeper version: 3.7.0-e3704b390a6697bfdf4b0bef79e3da7a4f6bac4b, built on 2021-03-17 09:46 UTC
Latency min/avg/max: 0/0.0/0
Received: 3856
Sent: 3855
Connections: 1
Outstanding: 0
Zxid: 0x200000002
Mode: follower
Node count: 6

my-release-zookeeper-2.my-release-zookeeper-headless
Zookeeper version: 3.7.0-e3704b390a6697bfdf4b0bef79e3da7a4f6bac4b, built on 2021-03-17 09:46 UTC
Latency min/avg/max: 0/0.0/0
Received: 3855
Sent: 3854
Connections: 1
Outstanding: 0
Zxid: 0x200000002
Mode: leader
Node count: 6
Proposal sizes last/min/max: 48/48/48

现在我们的 ZooKeeper 服务正在运行,让我们使用 Istio 来保护对我们常规服务和无头服务的全部通信。将双向 TLS 应用于 default 命名空间

$ kubectl apply -n default -f - <<EOF
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "default"
spec:
  mtls:
    mode: STRICT
EOF

继续从 sleep Pod 发送一些流量,并打开 Kiali 仪表板以可视化 default 命名空间中的服务

Visualize the ZooKeeper Services in Kiali
在 Kiali 中可视化 ZooKeeper 服务

流量流上的挂锁图标表示连接是安全的。

总结

随着 Istio 1.10 中新的网络更改,带有 sidecar 的 Kubernetes Pod 与没有 sidecar 的 Pod 具有相同的网络行为。如本文所示,此更改使有状态应用程序能够在 Istio 中正常运行。我们相信这是 Istio 迈向提供透明服务网格和零配置 Istio 的目标的一大步。

分享这篇文章