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之前,不会删除下一个。