K8S 使用StatefulSet 管理有状态应用
Published on 2019 - 12 - 26
对有状态应用的管理一般思路都是:固定机器、静态IP、持久化存储等。Kubernetes利用PetSet这个资源,弱化有状态Pet与具体物理设施之间的关联。一个PetSet能够保证在任意时刻,都有固定数量的Pet在运行,且每个Pet都有自己唯一的身份。一个“有身份”的Pet指的是该Pet中的Pod包含如下特性:
1. 静态存储;
2. 有固定的主机名,且DNS可寻址(稳定的网络身份,这是通过一种叫 Headless Service 的特殊Service来实现的。 和普通Service相比,Headless Service没有Cluster IP,用于为一个集群内部的每个成员提供一个唯一的DNS名字,用于集群内部成员之间通信 。
3. 一个有序的index(比如PetSet的名字叫mysql,那么第一个启起来的Pet就叫mysql-0,第二个叫mysql-1,如此下去。当一个Pet down掉后,新创建的Pet会被赋予跟原来Pet一样的名字,通过这个名字就能匹配到原来的存储,实现状态保存。
应用举例:
1. 数据库应用,如Mysql、PostgreSQL,需要一个固定的ID(用于数据同步)以及外挂一块NFS Volume(持久化存储)。
2. 集群软件,如zookeeper、Etcd,需要固定的成员关系。
注:使用PetSet会有些限制
1. 日常运维,对于PetSet,唯一能够更改的就是replicas;
2. 需要持久化数据卷(PV,若为nfs这种无法通过调用API来创建存储的网络存储,数据卷要在创建PetSet之前静态创建;若为aws-ebs、vSphere、openstack Cinder这种可以通过API调用来动态创建存储的虚拟存储,数据卷除了可以通过静态的方式创建以外,还可以通过StorageClass进行动态创建。需要注意的是,动态创建出来的PV,默认的回收策略是delete,及在删除数据的同时,还会把虚拟存储卷删除
3. 只能通过手动的方式升级PetSet。
StatefulSet 示例
创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv0001
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: "/nfs/sarichTest"
server: 172.18.12.189
readOnly: false
创建PetSet
apiVersion: v1
kind: Service
metadata:
name: nginx-sarich
labels:
app: nginx-sarich
spec:
ports:
- port: 80
name: web-sarich
# *.nginx.default.svc.cluster.local
clusterIP: None
selector:
app: nginx-sarich
---
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
name: web-sarich
spec:
serviceName: "nginx-sarich"
replicas: 3
template:
metadata:
labels:
app: nginx-sarich
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
terminationGracePeriodSeconds: 0
containers:
- name: nginx-sarich
image: gcr.io/google_containers/nginx-slim:0.8
ports:
- containerPort: 80
name: web-sarich
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
验证存储
# 先登录到容器中创建点文件
kubectl exec -n sarich-test -ti web-sarich-0 /bin/bash
cd /usr/share/nginx/html
echo "hello it's works!" > 0.out
exit
# 登录到nfs中,检查文件是否存在
ssh 172.18.12.189 #登录到NFS主机
ls /nfs/sarichTest
验证域名解析
# 检查StatefulSet中的POD的HostName
for i in 0 1 2 3; do kubectl exec -n sarich-test web-sarich-$i -- sh -c 'hostname'; done
检查使用上面获取的HostName来解析IP
nslookup
> web-sarich-0.nginx-sarich
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: web-sarich-0.nginx-sarich.sarich-test.svc.cluster.local
Address: 172.17.0.5
> web-sarich-1.nginx-sarich
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: web-sarich-1.nginx-sarich.sarich-test.svc.cluster.local
Address: 10.244.5.6
> web-sarich-2.nginx-sarich
Server: 10.96.0.10
Address: 10.96.0.10#53
Name: web-sarich-2.nginx-sarich.sarich-test.svc.cluster.local
Address: 10.244.52.5
验证Stateful重建
# 为web-sarich-0 添加一个index页面,内容为自己的HostName
exec web-sarich-0 -n sarich-test -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'
# 在其他pod上获取web-sarich-0的欢迎页
kubectl exec -it web-sarich-1 -n sarich-test -- curl web-sarich-0.nginx-sarich
# 得到结果
web-sarich-0
# 查询 web-sarich-0的存活时间
[root@kubernetes-master01 sarich]# kubectl get pods -n sarich-test
NAME READY STATUS RESTARTS AGE
web-sarich-0 1/1 Running 1 16h
web-sarich-1 1/1 Running 0 16h
web-sarich-2 1/1 Running 0 16h
# 可以看到 web-sarich-0被删除后几乎立即又被重建了
[root@kubernetes-master01 sarich]# kubectl delete pod web-sarich-0 -n sarich-test
pod "web-sarich-0" deleted
[root@kubernetes-master01 sarich]# kubectl get pods -n sarich-test
NAME READY STATUS RESTARTS AGE
web-sarich-0 1/1 Running 0 4s
web-sarich-1 1/1 Running 0 16h
web-sarich-2 1/1 Running 0 16h
# 查看 web-sarich-0中的欢迎页是否还在
[root@kubernetes-master01 sarich]# kubectl exec -ti web-sarich-0 -n sarich-test -- curl web-sarich-0
web-sarich-0
更新及扩容
对于StatfulSet,Kubernetes能帮我们自动做的仅有“replicas”,即副本数量。对于扩充副本数量来说,Kubernetes每次会按照顺序,一个个的创建Pod,且在前一个没有running或ready之前,不会创建下一个;对于缩减副本数量来说,Kubernetes每次会按照顺序,一个个的删除Pod。且在前一个没有真正的shutdown之前,不会删除下一个。