除了 Deployment 以外,Kubernetes 里面还有很多其他的 Controller,比如 StatefulSet。
Deployment 更多是用来管理无状态的应用,StatefulSet 更多是用来管理有状态的应用。
Service 对于 Deployment 所管理的 Pod,是不会去特意标记每个 Pod,统一采取 iptables 的方式进行转发。
要注意这里的转发不是均衡分配,而是随机分配。
Service 对于 StatefulSet 所管理的 Pod,会给每个 Pod 做一个特殊的标记,并对 Pod 的启动顺序做了记录。
所以对于需要持久化标识 Pod 的场景,或者对于应用需要按主从顺序启动 Pod 的场景,StatefulSet 才是最佳的选择。
现在我们部署一个 StatefulSet 应用,yaml 配置如下。
apiVersion: v1
kind: Service
metadata:
name: jmeter
namespace: default
labels:
app: jmeter
spec:
ports:
- port: 80
name: jmeter
clusterIP: None
selector:
app: jmeter
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: jmeter
namespace: default
spec:
selector:
matchLabels:
app: jmeter
serviceName: "jmeter"
replicas: 2
template:
metadata:
labels:
app: jmeter
spec:
containers:
- name: jmeter
image: shaonianyr/jmeter:demo
ports:
- containerPort: 80
name: web
volumeMounts:
- name: log
mountPath: /jmeter/jmeter.log
volumeClaimTemplates:
- metadata:
name: log
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
可以看到,StatefulSet 绑定了一个 Headless Service 提供访问的入口。
Service 主要通过两种方式提供访问。
而 StatefulSet 不需要给 Service 分配 VIP,即 Headless Service,指定 clusterIP: None
。
然后,利用对 Pod 的标识,直接把 Pod IP 跟 Service DNS 做绑定,能指定访问到具体的某个 Pod,不会做负载均衡。
这时我们去查看 StatefulSet Pod 创建情况,会看到类似下面的结果。
$ kubectl get pods -w -l app=jmeter
NAME READY STATUS RESTARTS AGE
jmeter-0 0/1 Pending 0 0s
jmeter-0 0/1 ContainerCreating 0 5s
jmeter-0 1/1 Running 0 10s
jmeter-1 0/1 Pending 0 0s
jmeter-1 0/1 ContainerCreating 0 5s
jmeter-1 1/1 Running 0 10s
StatefulSet 对第一个 Pod 的启动标记为 jmeter-0
,第二个 Pod 的启动标记为 jmeter-1
。
所以,当我们访问 jmeter-0.default.svc.cluster.local
,流量只会代理转发到第一个 Pod。
除了提供 稳定的网络标识
,StatefulSet 也提供 稳定的存储标识
。
如果 Pod 绑定了 PVC,StatefulSet 会给每个 Pod PVC 都绑定一个特殊的编号。
当 Pod 被删除之后,PVC 和 PV 是不会被删除的,等待 Pod 再度被创建出来,会自动绑上原来的 PVC。
现在我们来看下,云上的 Jmeter 分布式,基于 Deployment 和 StatefulSet 的部署方式。
先部署 slave。
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: jmeter-slave
namespace: default
labels:
k8s-app: jmeter-slave
spec:
selector:
matchLabels:
k8s-app: jmeter-slave
replicas: 2
template:
metadata:
labels:
k8s-app: jmeter-slave
name: jmeter-slave
spec:
serviceAccountName: jmeter-slave
containers:
- name: jmeter-slave
image: shaonianyr/jmeter-slave:demo
imagePullPolicy: Always
tty: true
ports:
- name: port1
containerPort: 1099
protocol: TCP
- name: port2
containerPort: 50000
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: jmeter-slave
labels:
app: jmeter-slave
spec:
selector:
k8s-app: jmeter-slave
ports:
- name: port1
protocol: TCP
port: 1099
targetPort: 1099
- name: port2
protocol: TCP
port: 50000
targetPort: 50000
type: ClusterIP
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jmeter-slave
然后部署 master。
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: jmeter-master
namespace: default
labels:
k8s-app: jmeter-master
spec:
selector:
matchLabels:
k8s-app: jmeter-master
replicas: 1
template:
metadata:
labels:
k8s-app: jmeter-master
name: jmeter-master
spec:
serviceAccountName: jmeter-master
containers:
- name: jmeter-master
image: shaonianyr/jmeter-master:demo
imagePullPolicy: Always
tty: true
command: [ "/bin/sh","-c" ]
args: [ "jmeter -n -t baidu.jmx -Rjmeter-slave.default.svc.cluster.local" ]
ports:
- name: port
containerPort: 60000
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: jmeter-master
namespace: default
spec:
selector:
k8s-app: jmeter-master
ports:
- name: port
protocol: TCP
port: 60000
targetPort: 60000
type: ClusterIP
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: jmeter-master
这里,我们通过 -Rjmeter-slave.default.svc.cluster.local
,以 Service 提供 master 对 slave 的链接。
这样虽然也能压,但是会发现,在高并发的情况下, slave 两个 Pod 的资源使用情况,一个极高一个极低,压出来的 QPS 不如两个单副本的 Deployment。
前面讲到,Service 对 Pod 的转发,并不是均衡转发的,而是随机转发,且在高并发的时候,会存在性能损耗。
现在,我们再把上面的 -Rjmeter-slave.default.svc.cluster.local
换成 -Rslave-pod-ip1,slave-pod-ip2
。
这样配置,相当于绕开了 Service VIP 的代理,直接访问相应的 Pod IP,在高并发的情况下,压出来的 QPS 远大于上面一种配置的 QPS。
但是 Pod IP 会随着容器的重启发生变化,而 Service 却不会,所以其实我们想要后者的稳定配置,前者的稳定转发,答案便呼之欲出。
通过 StatefulSet 部署 slave,利用 Pod 的标识,便可以这样配置:-Rjmeter-slave-0.default.svc.cluster.local,jmeter-slave-1.default.svc.cluster.local
......
未完待续,有空就开更。