Influx-Proxy部署流程以及风险点分析

1. 背景描述

我们经过针对 influx-proxy为期近一周的代码审计工作,解决了 influx-proxy在审计工作中发现的:① influxdbmeasurement获取上出现获取到保留策略名的错误;②influx-proxy发现已存在的 data race 警告性错误。同时,为满足公司对于微服务治理的使用需求,我们在原来的基础上增加了 UDP 的写入方式,并优化了 UDP 的 写入,经过测试,在不考虑时间精度的条件下,Proxy的时序数据转发率达到 100%

我们需要根据当前的修改后的 influx-proxy 去构建部署镜像,并在新Site中部署出来投入使用。

2. 风险点分析

2.1 风险点

  1. 打包镜像基础命令不支持UDP缓冲区大小修改;
  2. inlfux-proxy 挂载卷在多副本情况下的共享问题,不同的 pod 是否可以读取 不同的 pvc 目录;
  3. pod 挂了之后,当前 pod 所对应的 挂载卷 是否可以对应到新的 pod
  4. 修改了当前镜像的 缓冲区大小后,是否需要修改物理机的缓冲区大小

2.2 风险点解决

2.2.1 缓冲区修改

目前在 Dockerfile 中不支持 针队系统层面的配置的修改,经过调研,我们发现在 docker 执行的时候 的 run 参数中存在 sysctl 的修改。随后在 K8S 的官方文档中我们寻找到如下的配置信息

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "0"
- name: net.core.somaxconn
value: "1024"
- name: kernel.msgmax
value: "65536"

上述方案,经过本地实测,出现如下错误:

1
Failed to create pod sandbox: rpc error: code = Unknown desc = failed to start sandbox container for pod "influx-proxy-0": Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"write sysctl key net.core.rmem_max: open /proc/sys/net/core/rmem_max: no such file or directory\"": unknown

目前使用了ubuntu , centos 等基础镜像,均出现了该问题。目前在网上找到的资料显示需要修改宿主机的内核参数。但是这个对于我们部署在K8S集群中存在不合理性。目前还在进行相关的资料的查询。

我们做了如下的尝试:

  1. 寻找 满足 net.core.rmem_maxnet.core.rmem_default的基础镜像,未果;
  2. 用本地的 /proc/sys/net/core 替换 容器内的 core ,发现无法替换,未果
  3. 执行 RUN echo 26214400 /proc/sys/net/core/rmem_default 执行尝试直接写入,未果
  4. 执行 RUN echo net.core.rmem_default=26214400 >> /etc/sysctl.d/00-alpine.conf 尝试写入 sysctl.conf配置文件,未果

经过与周淼的讨论,目前这个问题在:sysctl variables are not available to newly created namespace 中有提及到,在 2016 年之前,会有显示所有的 网络 namespace下的内核参数,但是这一点被认为是一个BUG,因此在此次修复调整后,只对外暴露显示了部分的namespace下的内核参数,只对外提供部分的参数调整。因此,目前的方案是通过修改 当前 Pod 所在节点的内核值来满足我们的需求。

2.2.2 关于PVC挂载

influx-proxy 中目前是需要使用到 本地存储的没这就涉及到本地的挂载卷。现在有一个问题,就是 当 inlfux-proxy 存在多副本时,挂载卷 能否挂载到同一个pvc里面,在进行文件的写入的时候,是否会存在无法写入的问题?当 Pod 宕机重启后,是否会从pvc所对应的 data/ 里面进行故障恢复。

我们经过调研,在 PVC 的配置中,我们找到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0003
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp
server: 172.17.0.2

可以看到这里的 accessModes 的支持的方式有:

  • ReadWriteOnce -- the volume can be mounted as read-write by a single node
  • ReadOnlyMany -- the volume can be mounted read-only by many nodes
  • ReadWriteMany -- the volume can be mounted as read-write by many nodes

目前的这个还是需要进行先一步的测试。

目前会存在两种方案,待测试:

  1. 使用 StatefulSet的有状态的部署方式,这样可以保证到的是每一个 容器会对应一个自己的文件目录;
  2. 继续使用 Deployment 的部署方式,但是我们从代码层面做修改,做到多实例访问同一个文件目录,但是当当前的文件目录在被使用的时候,禁止其他实例访问。

经过简单的推理,上述两种方案会存在如下问题:

  1. 有状态部署在后期的维护上以及在水平扩展上没有无状态的方便快捷;
  2. 无状态的部署方式,当后端InfluxDB实例出现问题,导致写入失败,所有的 influx-proxy 均要写入 持久化目录,将导致可能存在只有一个 inlfux-proxy 实例在进行写入,其他 inlfux-proxy 将保持永远无法写入状态。

再和周淼讨论后,基本确定了使用 StatefulSet的部署方式,来进行部署。后期,会在此基础上将本地文件方式持久化修改为远程的写入的持久化的方式,可以考虑Redis / MongoDB 等。

2.2.3 POD 宕机重启之后是否会读取原来的 持久化文件

当我们的 部署绑定了 PVC 之后,是可以直接读取 PVC的。但是这里有一个新的问题,举个例子:

现在有 3 个副本 A , B , C ,对应着的 有会有 3 个 数据持久化文件目录,在 PVC的根目录下,a , b , c 。从理论上来说,A-a , B-b , C-c 是保持一致的。但是现在 A挂了,重启之后新的 副本为 D , 那 D 是否能读取到 a , 而不是 d , 同时,如果 A,B,C重启之后,是否能自动对应的读取到a,b,c

关于PVC挂载

2.2.4 修改了当前镜像的 缓冲区大小后,是否需要修改物理机的缓冲区大小

目前找到的资料,需要通过Pod SecurigyContext配置。

缓冲区修改

3. 部署过程

3.1 部署前测试

3.1.1 测试环境

influx-proxy部署环境 K8S 环境

NAME VERSION OS KERNEL DOCKER CPU + MEM
master1 v1.17.12 Ubuntu 18.04.5 LTS 4.15.0-117-generic docker://19.3.12 4 + 8
worker1 v1.17.12 Ubuntu 18.04.5 LTS 4.15.0-117-generic docker://19.3.12 4 + 8
worker2 v1.17.12 Ubuntu 18.04.5 LTS 4.15.0-117-generic docker://19.3.12 4 + 8

InfluxDB实例环境

机器名 操作系统版本 Docker版本 Influxdb镜像
10.20.1.100 Ubuntu 18.04.5 LTS 19.03.12 harbor.oneitfarm.com/cidata/influxdb:1.8.3-alpine
10.20.4.132 Ubuntu 18.04.5 LTS 19.03.12 harbor.oneitfarm.com/cidata/influxdb:1.8.3-alpine

实例准备,默认数据库: msp

实例名 部署方式 实例镜像 所在机器
inlfux-1 容器部署 harbor.oneitfarm.com/cidata/influxdb:1.8.3-alpine [100 132]
inlfux-2 容器部署 harbor.oneitfarm.com/cidata/influxdb:1.8.3-alpine [100 132]
inlfux-3 容器部署 harbor.oneitfarm.com/cidata/influxdb:1.8.3-alpine [100 132]

3.1.2 测试内容

本次测试,我们均使用 双环单实例 进行测试。

3.1.2.1 UDP写入数据是否丢失
  1. 清空已存在的 InfluxDB 的数据库 msp 中所有的表信息
  2. 按照我们在《influx-proxy无侵入式InfluxDB高可用方案测试》中的测试方式,写入数据,写 1w 条数据。

我们在写入 1w 条数据的时候发现,在我们的数据库中出现了test_udp_proxy_1 , test_udp_proxy_3,test_udp_proxy_4 ,推测原因是因为产生的数据中没有提供test_udp_proxy_2的表,因此我们扩大测试的数据,在原来的基础上我们再一次写入 10w 的数据。最后我们查询到的表的数据为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
> show measurements;
name: measurements
name
----
test_udp_proxy_1
test_udp_proxy_3
test_udp_proxy_4
> show measurements;
name: measurements
name
----
test_udp_proxy_1
test_udp_proxy_2
test_udp_proxy_3
test_udp_proxy_4
> select sum(count) from test_udp_proxy_1;
name: test_udp_proxy_1
time sum
---- ---
0 40299
> select sum(count) from test_udp_proxy_2;
name: test_udp_proxy_2
time sum
---- ---
0 17550
> select sum(count) from test_udp_proxy_3;
name: test_udp_proxy_3
time sum
---- ---
0 21311
> select sum(count) from test_udp_proxy_4;
name: test_udp_proxy_4
time sum
---- ---
0 30838

可以看到,在我们的数据库中,存在共计:40299+17550+21311+30838 = 10998 的数据量,在时间精度允许的条件下,可以认为我们的influx-proxy具备 100% 转发能力。同时:

1
2
3
4
5
6
7
8
9
10
Concurrency Level:      20
Time taken for tests: 8.152 seconds
Complete requests: 100000
Failed requests: 0
Total transferred: 7500000 bytes
HTML transferred: 0 bytes
Requests per second: 12267.53 [#/sec] (mean)
Time per request: 1.630 [ms] (mean)
Time per request: 0.082 [ms] (mean, across all concurrent requests)
Transfer rate: 898.50 [Kbytes/sec] received
3.1.2.2 故障恢复是否正常

我们现在删除 其中的一个实例,再次执行写入操作。经过测试,我们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Using database msp
> select sum(count) from test_udp_proxy_4;
name: test_udp_proxy_4
time sum
---- ---
0 70694
> select sum(count) from test_udp_proxy_3;
name: test_udp_proxy_3
time sum
---- ---
0 40766
> select sum(count) from test_udp_proxy_2;
name: test_udp_proxy_2
time sum
---- ---
0 39856
> select sum(count) from test_udp_proxy_1;
name: test_udp_proxy_1
time sum
---- ---
0 58682

最后的数据量为:20998 。 成功恢复了数据。

现在我们调用/recovery , 从本地的实例进行恢复:

1
curl -X POST 'http://influx-proxy.ingress.sun-iot.xyz/recovery?from_circle_id=1&to_circle_id=0'

表示从 circle-1 全量数据 恢复到 circle-0

1
accepted

表示成功

3.1.3 测试部署遇到的问题

在 Openstack 的测试部署中,我们有遇到以下问题:

我们在针对 influx-proxy 实现 UDP 负载均衡时发现,每一次真实工作的 副本 只有 存在的三个副本中的某一个,即 单实例工作。针对这一问题,我们调研查找了相关资料,后与周淼,陶圣的讨论中,基本确定了问题如下:

在:client -> service -> pod 的流程中,这里的Client 是指我们的数据源,Service 层在进行负载均衡时,只能将当次连接的数据写入到当次的连接中,且在测试过程中,当次连接并不会断开,导致 Service -> Pod 的连接没有断开,即只往 当前的 Pod 写入。

3.2 正式部署

3.2.1 InfluxDB 环境的准备

3.2.1.1 机器申请

InfluxDB 实例节点

为扩展目前InfluxDB 单实例,我们使用了 influx-proxy 实现 双环双实例,满足实现 InfluxDB 的双写,满足主备,以及故障恢复等需求。因此,需申请两台 4 核16G 机器。由于 InfluxDB 为时序数据库,需使用到存储,故每个实例申请 100G 的存储空间。

influx-proxy 节点

influx-proxy部署在 K8S 集群中,但是由于 influx-proxy 涉及到 宿主机 的关于 网络空间下的内核参数的调整,为不影响当前 Site 下的其他服务,我们需要进行为 influx-proxy 调度到专属的机器上,并在当前的机器上进行相关的内核参数的调整。需申请 3 台 2C4G 的机器,搭载 3 个 influx-proxy 的副本。

3.2.1.2 部署流程整理
  1. 因我们数据组之前提供的ansible 脚本暂没有针对某一个实例的部署方式,需要修改ansible脚本,提供针对influxdbansible 部署脚本(测试版),后期关于 InfluxDB 的部署继续使用数据组提供的 ansible 的正式脚本。
  2. 脚本镜像提供出来后,给到董逸飞进行 InfluxDB 的部署安装。
  3. 关于 influx-proxyK8S 的部署,需要修改 influx-prxoy 的调度的策略,也就是需要 将influx-proxy 调度到指定的节点,目的是因为我们需要为 influx-proxy 修改宿主机的网络空间下的内核参数,需要周淼协助下。
  4. 和陶圣,周淼讨论后,influx-proxy 部署出来之后,需要将 influx-proxyheadless 的地址打入到环境变量,这个需要有人支持一下。

3.2.2 部署遇到的问题

3.2.2.1 Ansible 脚本

在执行 ansible 脚本的过程中,我们发现在登录到跳板机的过程中以及登录到实例所在的宿主机的时候,出现了秘钥的不匹配情况。该情况经过陶圣,洪清亮等人的分析,发现有效秘钥的大小与实际使用的秘钥大小相差了38 的字节,应该是因为本地Windows系统与Linux在复制秘钥的过程中出现了误差。具体原因暂不明确。

后经过了秘钥的修改,InfluxDB 成功部署。

3.2.2.2 K8S 部署

在使用当前中台的部署流程时,由于部署的时候将APP_NAME 设置到其他的名字,我们的 influx-proxy 是有三个 Pod 处于 RUNNING 状态,但是在删除了当前的部署后,设置了正确的 APP_NAME后,发现 influx-proxy 部署后,一直出现 ContainerCreating 的状态,经过查看当前的 Pod 的状态信息,发现,influx-proxy ConfigMap 无法创建。后通过使用渲染但不apply 的方式去查看当前的部署的渲染结果中是否正常渲染,即排查是否存在 ConfigMap 是否正常渲染。

经过排查,渲染正常渲染。说明从在执行 kubectl apply -f - 的过程中出现了 ConfigMap 无法被创建。查找了 MSP 的相关日志,并没找到与之相关的错误的日志信息。

我们经过了删除重试的操作,依旧无法定位到ConfigMap 无法创建的具体原因。最后的解决办法,是将正常渲染出来的 ConfigMap 进行了 kubectl apply -f - ,发现ConfigMap是可以被正常创建的,说明了ConfigMap 是正常的。出现的这个问题的具体原因还不明确。

3.2.2.3 Msp_Backend

我们在使用 MSP平台提供的 UDP 写入测试的时候发现,当前 MSP UDP的形式连接的到 influx-proxy 时,如果 influx-proxy 出现了问题导致了宕机,Pod 重启之后,MSP连接到 influx-proxy 地址依旧是上一个旧的 Pod IP 地址(由于 UDP 的特殊性,我们无法在连接时去做到流量的负载)。

建议:

  1. 开辟独立的协程,进行后端的感知
  2. 使用 HTTP 请求,目前在使用了 proxy 之后,完全有信息可以抗住 MSP 的数据量