<audio id="audio" title="15 | 查询商品：资源不足有哪些性能表现？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/c9/7c/c9f8721d71357ece5f5aa8c8791ef37c.mp3"></audio>

你好，我是高楼。

这节课，我们来收拾“查询商品”这个接口。虽然这次的现象同样是TPS低、响应时间长，但是，这个接口走的路径和之前的不一样，所以在分析过程中会有些新鲜的东西，你将看到在资源真的不足的情况下，我们只有增加相应节点的资源才能提升性能。

在我的项目中，我一直都在讲，**不要轻易给出资源不足的结论。因为但凡有优化的空间，我们都要尝试做优化，而不是直接告诉客户加资源。而给出“增加资源”这个结论，也必须建立在有足够证据的基础上**。在这节课中，你也将看到这一点。

话不多说，我们直接开始今天的内容。

## 压力场景数据

对于查询商品接口，我们第一次试执行性能场景的结果如下：

<img src="https://static001.geekbang.org/resource/image/75/a2/75ef8d040cdbcef4556bbfa7fd7098a2.png" alt="">

你看，TPS只有250左右，并且响应时间也明显随着压力的增加而增加了，看起来瓶颈已经出现了，对吧？根据哥的逻辑，下一步就是看架构图啦。

## 先看架构图

我们用APM工具来看看这个接口的架构。

<img src="https://static001.geekbang.org/resource/image/6b/70/6bcbe3236d91fd7628fa5a6186243870.png" alt="">

你看，从压力机到Gateway服务、到Search服务、再到ES-Client，这个APM工具也只能帮我们到这里了。因为我们用的是ElasticSearch 7来做的搜索服务的支撑，而这个skywalking工具也没有对应的Agent，所以后面并没有配置skywalking。

在这里，我要多啰嗦几句。现在的APM工具大多是基于应用层来做的，有些运维APM采集的数据会更多一些，但也是响应时间、吞吐量等这样的信息。对于性能分析而言，现在的APM工具有减少排查时间的能力，但是在组件级的细化定位上还有待提高。虽然AI OPS也被提到了台面，但是也没见过哪个公司上了AIOPS产品后，就敢不让人看着。

总之，从细化分析的角度，我们在定位问题的根本原因时，手头有什么工具就可以用什么工具，即使什么工具都没有，撸日志也是照样能做到的，所以我建议你不要迷信工具，要“迷信”思路。

下面我们来拆分下这个接口的响应时间，看看这个案例的问题点在哪里。

## 拆分响应时间

**“在RESAR性能分析逻辑中，拆分响应时间只是一个分析的起点**。”这是我一直在强调的一句话。如果性能工程师连这个都不会做，就只能好好学习天天向上了。

根据架构图，我们拆分响应时间如下。

- Gateway服务上的响应时间：

<img src="https://static001.geekbang.org/resource/image/dc/0d/dc64c9e8ace689c86d6584b64f9f230d.png" alt="">

- Search服务上的响应时间：

<img src="https://static001.geekbang.org/resource/image/a6/c9/a6dc83d1b51445464d3db3e5421798c9.png" alt="">

- ES Client的响应时间：

<img src="https://static001.geekbang.org/resource/image/0b/81/0b168cdd73f30b5565c52a6d97b45081.png" alt="">

一层层看过之后，我们发现查询商品这个接口的响应时间消耗在了ES client上面。而在我们这个查询的路径上，在gateway/search服务上，我们并没有做什么复杂的动作。

既然知道了响应时间消耗在哪里，下面我们就来定位它，看能不能把TPS优化起来。

## 全局监控

根据高老师的经验，我们还是从全局监控开始，看全局监控可以让我们更加有的放矢。在分析的过程中，经常有人往下走了几步之后，就开始思维混乱、步伐飘逸。因为数据有很多，所以分析时很容易从一个数据走到不重要的分支上去了。而这时候，如果你心里有全局监控数据，思路就会更清晰，不会在无关的分支上消耗时间。

回到我们这个例子中，从下面的k8s worker（也就是k8s中的node，在我们的环境中我习惯叫成worker，就是为了体现：在我的地盘，我爱叫啥就叫啥）的数据上来看，似乎没有一个worker的资源使用率是特别高的。

<img src="https://static001.geekbang.org/resource/image/25/e5/25bb0386561be19b9615a4b0130fe4e5.png" alt="">

请你注意，在k8s中看资源消耗，一定不要只看worker这个层面，因为这个层面还不够，一个worker上可能会运行多个pod。从上图来看，由于worker层面没有资源消耗，但是时间又明显是消耗在ES client上的，所以，接下来我们要看一下每一个pod的资源使用情况。

<img src="https://static001.geekbang.org/resource/image/13/f3/13f919d42a29f2d37f699036658006f3.png" alt="">

咦，有红色。你看，有两个与ES相关的POD，它们的CPU都飘红了，这下可有得玩了。既然是与ES相关的POD，那我们就把ES所有的POD排个序看看。

<img src="https://static001.geekbang.org/resource/image/62/bd/624d7bc2fe25f731aa42037d8d9f29bd.png" alt="">

从上图的数据来看，有一个ES Client 消耗了67%的CPU，有两个ES Data消耗了99%的CPU，ES本来就是吃CPU的大户，所以我们接下来要着重分析它。

这里我再说明一点，我们从前面的worker资源使用率一步一步走到这里，在分析方向上是合情合理的，因为这些都是属于我提到的全局监控的内容。

## 定向分析

现在我们就来扒一扒ES，看看它在哪个worker节点上。罗列Pod信息如下：

```
[root@k8s-master-1 ~]# kubectl get pods -o wide | grep elasticsearch
elasticsearch-client-0                      1/1     Running   0          6h43m   10.100.230.2     k8s-worker-1   &lt;none&gt;           &lt;none&gt;
elasticsearch-client-1                      1/1     Running   0          6h45m   10.100.140.8     k8s-worker-2   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-0                        1/1     Running   0          7h8m    10.100.18.197    k8s-worker-5   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-1                        1/1     Running   0          7h8m    10.100.5.5       k8s-worker-7   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-2                        1/1     Running   0          7h8m    10.100.251.67    k8s-worker-9   &lt;none&gt;           &lt;none&gt;
elasticsearch-master-0                      1/1     Running   0          7h8m    10.100.230.0     k8s-worker-1   &lt;none&gt;           &lt;none&gt;
elasticsearch-master-1                      1/1     Running   0          7h8m    10.100.227.131   k8s-worker-6   &lt;none&gt;           &lt;none&gt;
elasticsearch-master-2                      1/1     Running   0          7h8m    10.100.69.206    k8s-worker-3   &lt;none&gt;           &lt;none&gt;
[root@k8s-master-1 ~]# 

```

现在就比较清晰了，可以看到，在整个namespace中有两个ES client，三个ES data，三个ES master。

我们来画一个细一点的架构图，以便在脑子里记下这个逻辑：

<img src="https://static001.geekbang.org/resource/image/2e/0f/2e8a4fe8b5e214a8463d918dea8cyy0f.jpg" alt="">

再结合我们在全局分析中看到的资源使用率图，现在判断至少有两个问题：

1. ES client请求不均衡；
1. ES data CPU 高。

下面我们一个一个来分析。

### ES client请求不均衡

从上面的架构图中可以看到，search服务连两个ES client，但是只有一个ES client的CPU使用率高。所以，我们需要查一下链路，看看ES的service：

```
[root@k8s-master-1 ~]# kubectl get svc -o wide | grep search
elasticsearch-client            NodePort    10.96.140.52    &lt;none&gt;        9200:30200/TCP,9300:31614/TCP         34d     app=elasticsearch-client,chart=elasticsearch,heritage=Helm,release=elasticsearch-client
elasticsearch-client-headless   ClusterIP   None            &lt;none&gt;        9200/TCP,9300/TCP                     34d     app=elasticsearch-client
elasticsearch-data              ClusterIP   10.96.16.151    &lt;none&gt;        9200/TCP,9300/TCP                     7h41m   app=elasticsearch-data,chart=elasticsearch,heritage=Helm,release=elasticsearch-data
elasticsearch-data-headless     ClusterIP   None            &lt;none&gt;        9200/TCP,9300/TCP                     7h41m   app=elasticsearch-data
elasticsearch-master            ClusterIP   10.96.207.238   &lt;none&gt;        9200/TCP,9300/TCP                     7h41m   app=elasticsearch-master,chart=elasticsearch,heritage=Helm,release=elasticsearch-master
elasticsearch-master-headless   ClusterIP   None            &lt;none&gt;        9200/TCP,9300/TCP                     7h41m   app=elasticsearch-master
svc-mall-search                 ClusterIP   10.96.27.150    &lt;none&gt;        8081/TCP                              44d     app=svc-mall-search
[root@k8s-master-1 ~]# 

```

你看，整个namespace中有一个client service（解析出来的是VIP，访问此服务时不会绕过K8s的转发机制），还有一个client-headless service（解析出来的是POD IP，访问这个服务时会绕过K8s的转发机制）。

接下来，我们查一下为什么会出现访问不均衡的情况。

通过查看search服务的ES配置，我们看到如下信息：

```
  elasticsearch:
    rest:
      uris: elasticsearch-client:9200
      username: elastic
      password: admin@123

```

看到我们这里是用的elasticsearch-client:9200，我们再来看一下client service的配置：

```
---
apiVersion: v1
kind: Service
metadata:
  annotations:
    meta.helm.sh/release-name: elasticsearch-client
    meta.helm.sh/release-namespace: default
  creationTimestamp: '2020-12-10T17:34:19Z'
  labels:
    app: elasticsearch-client
    app.kubernetes.io/managed-by: Helm
    chart: elasticsearch
    heritage: Helm
    release: elasticsearch-client
  managedFields:
    - apiVersion: v1
      fieldsType: FieldsV1
      fieldsV1:
        'f:metadata': {}
        'f:spec':
          'f:ports': {}
      manager: Go-http-client
      operation: Update
      time: '2020-12-10T17:34:19Z'
  name: elasticsearch-client
  namespace: default
  resourceVersion: '4803428'
  selfLink: /api/v1/namespaces/default/services/elasticsearch-client
  uid: 457e962e-bee0-49b7-9ec4-ebfbef0fecdd
spec:
  clusterIP: 10.96.140.52
  externalTrafficPolicy: Cluster
  ports:
    - name: http
      nodePort: 30200
      port: 9200
      protocol: TCP
      targetPort: 9200
    - name: transport
      nodePort: 31614
      port: 9300
      protocol: TCP
      targetPort: 9300
  selector:
    app: elasticsearch-client
    chart: elasticsearch
    heritage: Helm
    release: elasticsearch-client
  sessionAffinity: None
  type: NodePort

```

从上面的配置来看，sessionAffinity也配置为None了，也就是说这个service不以客户端的IP来保持session。因为在这个环境配置中，Type为NodePort，而我们在k8s中配置的转发规则是iptables。所以说，service是依赖iptables的规则来做后端转发的。

接下来，我们检查一下iptables的转发规则。

我们先来看iptables中关于ES client的规则：

```
[root@k8s-master-1 ~]# iptables -S KUBE-SERVICES -t nat|grep elasticsearch-client|grep 9200
-A KUBE-SERVICES ! -s 10.100.0.0/16 -d 10.96.140.52/32 -p tcp -m comment --comment &quot;default/elasticsearch-client:http cluster IP&quot; -m tcp --dport 9200 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.140.52/32 -p tcp -m comment --comment &quot;default/elasticsearch-client:http cluster IP&quot; -m tcp --dport 9200 -j KUBE-SVC-XCX4XZ2WPAE7BUZ4
[root@k8s-master-1 ~]# 

```

可以看到，service的规则名是KUBE-SVC-XCX4XZ2WPAE7BUZ4，那我们再去查它对应的iptables规则：

```
[root@k8s-master-1 ~]# iptables -S KUBE-SVC-XCX4XZ2WPAE7BUZ4 -t nat
-N KUBE-SVC-XCX4XZ2WPAE7BUZ4
-A KUBE-SVC-XCX4XZ2WPAE7BUZ4 -m comment --comment &quot;default/elasticsearch-client:http&quot; -j KUBE-SEP-LO263M5QW4XA6E3Q
[root@k8s-master-1 ~]#
[root@k8s-master-1 ~]# iptables -S KUBE-SEP-LO263M5QW4XA6E3Q -t nat
-N KUBE-SEP-LO263M5QW4XA6E3Q
-A KUBE-SEP-LO263M5QW4XA6E3Q -s 10.100.227.130/32 -m comment --comment &quot;default/elasticsearch-client:http&quot; -j KUBE-MARK-MASQ
-A KUBE-SEP-LO263M5QW4XA6E3Q -p tcp -m comment --comment &quot;default/elasticsearch-client:http&quot; -m tcp -j DNAT --to-destination 10.100.227.130:9200

```

问题来了，这里好像没有负载均衡的配置（没有probability参数），并且根据iptables规则也只是转发到了一个ES client上。到这里，其实我们也就能理解，为什么在全局监控的时候，我们只看到一个ES client有那么高的CPU使用率，而另一个ES client却一点动静都没有。

但是，这里的iptables规则并不是自己来配置的，而是在部署k8s的时候自动刷进去的规则。现在只有一条规则了，所以只能转发到一个POD上去。

那我们就再刷一遍ES的POD，重装一下ES的POD，看k8s自己能不能刷出来负载均衡的iptables规则。重来一遍之后，我们再来看iptables规则：

```
[root@k8s-master-1 ~]# iptables -S KUBE-SVC-XCX4XZ2WPAE7BUZ4 -t nat
-N KUBE-SVC-XCX4XZ2WPAE7BUZ4
-A KUBE-SVC-XCX4XZ2WPAE7BUZ4 -m comment --comment &quot;default/elasticsearch-client:http&quot; -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-IFM4L7YNSTSJP4YT
-A KUBE-SVC-XCX4XZ2WPAE7BUZ4 -m comment --comment &quot;default/elasticsearch-client:http&quot; -j KUBE-SEP-5RAP6F6FATXC4DFL
[root@k8s-master-1 ~]#

```

哟，现在刷出来两条iptables规则了，看来之前在我们不断折腾的部署过程中，ES client一直是有问题的。

在上面的iptables规则里，那两条iptables的上一条中有一个关键词“——probability 0.50000000000”。我们知道，iptables的匹配规则是从上到下的，既然上一条的匹配是随机0.5，也就是说只有50%的请求会走第一条规则，那下一条自然也是随机0.5了，因为总共只有两条规则嘛。这样一来就均衡了。

我们再接着做这个接口的压力场景，看到如下信息：

<img src="https://static001.geekbang.org/resource/image/40/67/408592f11401e1yyb1c06ef48f3f8167.png" alt="">

看起来ES client均衡了，对不对？

它对应的TPS，如下：

<img src="https://static001.geekbang.org/resource/image/61/99/614e849f975cf983747e080329c0d699.png" alt="">

明显TPS提升了60左右。

ES client请求不均衡的问题解决了，现在，我们还要来看一下ES data单节点CPU高的问题。

### ES Data CPU使用率高

- **第一阶段：加一个CPU**

在TPS提升之后，我们再来看一下全局监控数据。

<img src="https://static001.geekbang.org/resource/image/2e/79/2ec7cce680e81f57717c642cf7834279.png" alt="">

看起来比一开始好多了。基于前面分析ES client的经验，我们就先来查一下ES data的iptables规则：

```
-- 查看下有哪些ES data的POD
[root@k8s-master-1 ~]# kubectl get pods -o wide | grep data
elasticsearch-data-0                        1/1     Running   0          10h     10.100.18.197    k8s-worker-5   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-1                        1/1     Running   0          10h     10.100.5.5       k8s-worker-7   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-2                        1/1     Running   0          10h     10.100.251.67    k8s-worker-9   &lt;none&gt;           &lt;none&gt;


-- 查看ES data对应的iptables规则
[root@k8s-master-1 ~]# iptables -S KUBE-SERVICES -t nat|grep elasticsearch-data
-A KUBE-SERVICES ! -s 10.100.0.0/16 -d 10.96.16.151/32 -p tcp -m comment --comment &quot;default/elasticsearch-data:http cluster IP&quot; -m tcp --dport 9200 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.16.151/32 -p tcp -m comment --comment &quot;default/elasticsearch-data:http cluster IP&quot; -m tcp --dport 9200 -j KUBE-SVC-4LU6GV7CN63XJXEQ
-A KUBE-SERVICES ! -s 10.100.0.0/16 -d 10.96.16.151/32 -p tcp -m comment --comment &quot;default/elasticsearch-data:transport cluster IP&quot; -m tcp --dport 9300 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.16.151/32 -p tcp -m comment --comment &quot;default/elasticsearch-data:transport cluster IP&quot; -m tcp --dport 9300 -j KUBE-SVC-W4QKPGOO4JGYQZDQ


-- 查看9200（外部通信）对应的规则
[root@k8s-master-1 ~]# iptables -S KUBE-SVC-4LU6GV7CN63XJXEQ -t nat
-N KUBE-SVC-4LU6GV7CN63XJXEQ
-A KUBE-SVC-4LU6GV7CN63XJXEQ -m comment --comment &quot;default/elasticsearch-data:http&quot; -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-ZHLKOYKJY5GV3ZVN
-A KUBE-SVC-4LU6GV7CN63XJXEQ -m comment --comment &quot;default/elasticsearch-data:http&quot; -m statistic --mode random --probability 1 -j KUBE-SEP-6ILKZEZS3TMCB4VJ
-A KUBE-SVC-4LU6GV7CN63XJXEQ -m comment --comment &quot;default/elasticsearch-data:http&quot; -j KUBE-SEP-JOYLBDPA3LNXKWUK


-- 查看以上三条规则的转发目标
[root@k8s-master-1 ~]# iptables -S KUBE-SEP-ZHLKOYKJY5GV3ZVN -t nat
-N KUBE-SEP-ZHLKOYKJY5GV3ZVN
-A KUBE-SEP-ZHLKOYKJY5GV3ZVN -s 10.100.18.197/32 -m comment --comment &quot;default/elasticsearch-data:http&quot; -j KUBE-MARK-MASQ
-A KUBE-SEP-ZHLKOYKJY5GV3ZVN -p tcp -m comment --comment &quot;default/elasticsearch-data:http&quot; -m tcp -j DNAT --to-destination 10.100.18.197:9200
[root@k8s-master-1 ~]# iptables -S KUBE-SEP-6ILKZEZS3TMCB4VJ -t nat
-N KUBE-SEP-6ILKZEZS3TMCB4VJ
-A KUBE-SEP-6ILKZEZS3TMCB4VJ -s 10.100.251.67/32 -m comment --comment &quot;default/elasticsearch-data:http&quot; -j KUBE-MARK-MASQ
-A KUBE-SEP-6ILKZEZS3TMCB4VJ -p tcp -m comment --comment &quot;default/elasticsearch-data:http&quot; -m tcp -j DNAT --to-destination 10.100.251.67:9200
[root@k8s-master-1 ~]# iptables -S KUBE-SEP-JOYLBDPA3LNXKWUK -t nat
-N KUBE-SEP-JOYLBDPA3LNXKWUK
-A KUBE-SEP-JOYLBDPA3LNXKWUK -s 10.100.5.5/32 -m comment --comment &quot;default/elasticsearch-data:http&quot; -j KUBE-MARK-MASQ
-A KUBE-SEP-JOYLBDPA3LNXKWUK -p tcp -m comment --comment &quot;default/elasticsearch-data:http&quot; -m tcp -j DNAT --to-destination 10.100.5.5:9200
[root@k8s-master-1 ~]

```

Everythins is perfect!！规则很合理。ES Data总共有三个pod，从逻辑上来看，它们各占了三分之一。

在前面的ES client分析中，我们讲到，第一个POD是0.5，下一条自然也只剩下0.5，这很容易理解。现在，ES data的部分有三条iptables规则，我们来简单说明一下。

> 
通常我们理解的iptables就是一个防火墙。不过，要是从根本上来讲，它不算是一个防火墙，只是一堆规则列表，而通过iptables设计的规则列表，请求会对应到netfilter框架中去，而这个netfilter框架，才是真正的防火墙。其中，netfilter是处于内核中的，iptables就只是一个用户空间上的配置工具而已。


> 
我们知道iptables有四表五链。四表是：fileter表（负责过滤）、nat表（负责地址转换）、mangle表（负责解析）、raw表（关闭nat表上启用的连接追踪）；五链是：prerouting链（路由前）、input链（输入规则）、forward链（转发规则）、output链（输出规则）、postrouting链（路由后）。


> 
而在这一部分，我们主要是看nat表以及其上的链。对于其他的部分，如果你想学习，可自行查阅iptables相关知识。毕竟，我还时刻记得自己写的是一个性能专栏，而不是计算机基础知识专栏，哈哈。


从上面的信息可以看到，我们的这个集群中有三个ES data服务，对应着三条转发规则，其中，第一条规则的匹配比例是：0.33333333349；第二条比例：0.50000000000；第三条是1。而这三条转发规则对应的POD IP和端口分别是：10.100.18.197:9200、10.100.251.67:9200、10.100.5.5:9200，这也就意味着，通过这三条iptables规则可以实现负载均衡，画图理解如下：

<img src="https://static001.geekbang.org/resource/image/5e/88/5e9c08e967c62d9e9a84084d8c2de888.jpg" alt="">

我们假设有30个请求进来，那ES Data 0上就会有30x0.33333333349=10个请求；对于剩下的20个请求，在ES Data 1上就会有20x0.50000000000=10个请求；而最后剩下的10个请求，自然就到了ES Data 2上。这是一个非常均衡的逻辑，只是在iptables规则中，我看着这几个数据比例，实在是觉得别扭。

既然明白了这个逻辑，下面我们还是把查询商品接口的场景压起来看一下：

<img src="https://static001.geekbang.org/resource/image/06/49/06eb3b24a57226cd9a6717cd1e3f6049.png" alt="">

从数据上来看，经常会出现ES data 某个节点消耗CPU高的情况。可是，对应到我们前面看到的全局worker监控界面中，并没有哪个worker的CPU很高。所以，在这里，我们要查一下ES Data中的cgroup配置，看它的限制是多少。

<img src="https://static001.geekbang.org/resource/image/8y/3c/8yyd469d83c9a346aff8c659f6c4ae3c.png" alt="">

也就是说，ES data的每个POD都是配置了一颗CPU，难怪CPU使用率动不动就红了。

还有一点你要记住，前面我们在查看data列表的时候发现，ES data 0 在worker-5上，ES data 1 在worker-7上，ES data 2 在worker-9上。而我们现在看到的却是，它们都各自分到了一个CPU。既然如此，那我们就再添加一个CPU，然后再回去看一下worker-5/7/9的反应。为什么只加一个CPU呢？因为从worker-7上来看，现在的CPU使用率已经在50%左右了，要是加多了，我怕它吃不消。

<img src="https://static001.geekbang.org/resource/image/f5/01/f57acfca05yy43bd882a57404ea50d01.png" alt="">

看一下压力场景执行的效果：

<img src="https://static001.geekbang.org/resource/image/29/9c/299bbd39759ca90bdc4953334bc0c39c.png" alt="">

似乎……不怎么样？TPS并没有增加。

- **第二阶段：加副本**

我们再看加了CPU之后的全局POD监控：

<img src="https://static001.geekbang.org/resource/image/92/94/925d14923a3bfac75ef84b9b21622294.png" alt="">

还是只有一个ES data的CPU使用率高，所以我想查一下ES中的数据分布。因为负载均衡的问题解决了，并且知道有三个ES data节点。现在我们就要知道是不是每个节点都被访问到了。

```
pms                               0 p 10.100.18.199 _w   32   17690 18363   6.7mb  7820 true  true  8.5.1 false
pms                               0 p 10.100.18.199 _15  41    2110     0 465.7kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.18.199 _16  42   21083 30255   9.5mb  5900 true  true  8.5.1 false
pms                               0 p 10.100.18.199 _17  43    2572     0   568kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.18.199 _18  44    1403     0 322.9kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.18.199 _19  45    1856     0 414.1kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.18.199 _1a  46    1904     0   423kb  5500 true  true  8.5.1 true

```

为啥数据都在一个节点上（都是10.100.18.199）？看起来只有一个数据副本的原因了。

```
green open pms                                            A--6O32bQaSBrJPJltOLHQ 1 0   48618 48618  55.1mb  18.3mb

```

所以，我们先把副本数加上去，因为我们有三个data节点，所以这里加三个副本：

```
PUT /pms/_settings
{
    &quot;number_of_replicas&quot;: 3
}

我们再次查看ES中的数据分布，如下所示：

pms                               0 r 10.100.18.200 _w   32   17690 18363   6.7mb  7820 true  true  8.5.1 false
pms                               0 r 10.100.18.200 _15  41    2110     0 465.7kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.18.200 _16  42   21083 30255   9.5mb  5900 true  true  8.5.1 false
pms                               0 r 10.100.18.200 _17  43    2572     0   568kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.18.200 _18  44    1403     0 322.9kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.18.200 _19  45    1856     0 414.1kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.18.200 _1a  46    1904     0   423kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.251.69 _w   32   17690 18363   6.7mb  7820 true  true  8.5.1 false
pms                               0 p 10.100.251.69 _15  41    2110     0 465.7kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.251.69 _16  42   21083 30255   9.5mb  5900 true  true  8.5.1 false
pms                               0 p 10.100.251.69 _17  43    2572     0   568kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.251.69 _18  44    1403     0 322.9kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.251.69 _19  45    1856     0 414.1kb  5500 true  true  8.5.1 true
pms                               0 p 10.100.251.69 _1a  46    1904     0   423kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.140.10 _w   32   17690 18363   6.7mb  7820 true  true  8.5.1 false
pms                               0 r 10.100.140.10 _15  41    2110     0 465.7kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.140.10 _16  42   21083 30255   9.5mb  5900 true  true  8.5.1 false
pms                               0 r 10.100.140.10 _17  43    2572     0   568kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.140.10 _18  44    1403     0 322.9kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.140.10 _19  45    1856     0 414.1kb  5500 true  true  8.5.1 true
pms                               0 r 10.100.140.10 _1a  46    1904     0   423kb  5500 true  true  8.5.1 true

```

我们接着压起来，看看POD的资源：

<img src="https://static001.geekbang.org/resource/image/11/cc/11ayy743164f36a8d0b04bc76af52ccc.png" alt="">

现在看着是不是开心多了？data节点的CPU都用起来了。

我们再看一下worker的资源：

```
[root@k8s-master-1 ~]# kubectl get pods -o wide | grep data
elasticsearch-data-0                        1/1     Running   0          16m     10.100.18.199    k8s-worker-5   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-1                        1/1     Running   0          17m     10.100.251.68    k8s-worker-9   &lt;none&gt;           &lt;none&gt;
elasticsearch-data-2                        1/1     Running   0          18m     10.100.140.9     k8s-worker-2   &lt;none&gt;           &lt;none&gt;

```

现在ES Data的POD分布到2、5、9三这个worker上去了，我们查看下全局监控：

<img src="https://static001.geekbang.org/resource/image/53/b1/53a522a2754d7fcea550fde05cd791b1.png" alt="">

嗯，不错，ES data的POD把资源用起来了。其实这里要想继续调，还可以把CPU加大，ES本来就是吃CPU、内存的大户。不过，我们前面在配置的时候，给ES data的CPU也确实太小了。这个问题，并不是我故意设计出来的，而是当时在部署的时候，没考虑到这些。

最后，我们来看优化后的效果：

<img src="https://static001.geekbang.org/resource/image/1f/24/1fb93ff5yycfc2bb6049244820e73b24.png" alt="">

呀呀呀，你看TPS压都压不住呀，很快涨到900左右了！这个优化结果很好。

现在回过头来看第一个阶段，我们加CPU没有效果，主要还是因为副本数量太少。其实，在ES的优化中，还有很多细节可以玩。只不过，在我们这个课程中，我希望给你的是一个整体的分析思路和逻辑，而不是纠结于每个细节上的参数。所以，在这里，我们就不再说具体参数的调整了。

如果你想在ES上做更多的优化，可以在分析完业务之后，确定一下ES的架构、数据索引、分片等信息，然后再来设计一个合理的ES部署。

## 总结

在这节课中，我们看到APM工具也有无能为力的地方。所以说，当我们分析到一个具体组件之后，要想再往下分析，就得靠自己的技术功底了。

在出现请求不均衡的时候，我们一定要先去看负载均衡的逻辑有没有问题。当看到ES client不均衡时，我们去看了iptables的原理，在发现iptables只有一个转发规则的时候，接下来要做的当然就是重刷转发规则了。

在ES client转发均衡了之后，我们在ES data单节点上又看到CPU使用率过高。由于ES data在POD当中，我们自然就要想到去看cgroup的限制。

而在添加了CPU之后，我们发现TPS并没有提高，这时候就要去看ES的逻辑了。ES的强大之处就在于多副本多分片的查询能力，所以，我们增加了副本之后，CPU就用起来了，这是一个合理的优化结果，TPS也自然提高了。

经过一系列的动作，我们终于把资源给用起来了。这也是我一直在强调的，**性能优化第一个阶段的目标，就是把资源给用起来，然后再考虑更细节的优化**。

## 课后作业

最后，我给你留两道题，请你思考一下：

1. 当负载出现不均衡时，主要的分析方向是什么？
1. 什么时候才需要去看组件内部的实现逻辑？

记得在留言区和我讨论、交流你的想法，每一次思考都会让你更进一步。

如果你读完这篇文章有所收获，也欢迎你分享给你的朋友，共同学习进步。我们下一讲再见！
