Statefulset详细解析
StatefulSet实现Pod的拓扑状态
StatefulSet的核心功能就是通过某种方式记录这些状态,然后在Pod被重新创建时能够为新Pod恢复这些状态
只要知道一个Pod的名字以及它对应的Service的名字,就可以通过这条DNS记录访问到Pod的IP地址(pod的名称.service名称) -> Pod的IP
定义Stateful的对象中有一个serviceName字段来告诉Stateful控制器使用具体的service来解析它所管理的Pod的IP地址
Pod的名称由StatfulSet对象的名称+Pod创时所在的索引组成 StatefulSet使用这个DNS记录解析规则来维持Pod的拓扑状态
StatefulSet给它所管理的所有Pod的名字进行了编号,编号规则是: - .而且这些编号都是从0开始累加与StatefulSet的每个Pod实例一一对应 绝不重复
这些Pod的创建也是严格按照编号顺序进行的.比如,在web-0进入到Running 状态、并且细分状态(Conditions)成为Ready之前,web-1会一直处于Pending状态
把这两个Pod删除之后Kubernetes会按照原先编号的顺序,创建出了两个新的 Pod.并且Kubernetes依然为它们分配了与原来相同的“网络身份”:web-0.nginx和 web-1.nginx
通过永远不改变Pod名称的方式StatefulSet保证了Pod网络标识的稳定性.不管Pod是被删除重启还是被调度到其他节点上都不会改变Pod的名称
StatefulSet管理的pod使用的镜像是一样的,但是每个pod的命令和初始化流程可以完全不一样来实现主从Pod的拓扑部署
尽管web-0.nginx这条DNS记录本身不会变但它解析到的Pod的IP地址并不是固定的.这就意味着,对于“有状态应用”实例的访问必须使用DNS记录或者hostname的方式而绝不应该直接访问这些Pod的IP地址
StatefulSet实现Pod的存储状态
通过PVC机制来实现存储状态管理
在StatefulSet对象中除了定义PodTemplate还会定义一个volumeClaimTemplates凡是被这个StatefulSet管理的Pod都会声明一个对应的PVC,这个PVC的定义就来自于 volumeClaimTemplates这个模板字段
这个PVC的名字,会被分配一个与这个Pod完全一致的编号
把一个Pod比如web-0删除之后,这个Pod对应的PVC和PV并不会被删除,而这个Volume 里已经写入的数据,也依然会保存在远程存储服务里
StatefulSet在重新创建web-0这个pod的时候.它声明使用的PVC的名字还是叫作:www-web-0 这个PVC的定义,还是来自于PVC模板(volumeClaimTemplates)这是StatefulSet创建 Pod的标准流程
Kubernetes为它查找名叫www-web-0的PVC时,就会直接找到旧Pod遗留下来的同名的 PVC进而找到跟这个PVC绑定在一起的PV.这样新的Pod就可以挂载到旧Pod对应的那个Volume并且获取到保存在Volume里的数据.通过这种方式Kubernetes的StatefulSet就实现了对应用存储状态的管理
StatefulSet其实就是一种特殊的Deployment,而其独特之处在于,它的每个Pod都被编号了.而且,这个编号会体现在Pod的名字和hostname等标识信息上,这不仅代表了Pod的创建顺序,也是Pod的重要网络标识(即:在整个集群里唯一的、可被的访问身份).有了这个编号后StatefulSet就使用Kubernetes里的两个标准功能:Headless Service 和 PV/PVC,实现了对 Pod 的拓扑状态和存储状态的维护
StatefulSet搭建Mysql有状态集群
1.Master节点和Slave节点需要有不同的配置文件(即:不同的 my.cnf)
只需要给主从节点分别准备两份不同的 MySQL 配置文件 然后根据Pod的序号(Index)挂载进去即可
2.Master节点和Slave节点需要能够传输备份信息文件
3.在Slave节点第一次启动之前需要执行一些初始化SQL操作
apiVersion: v1 kind: ConfigMap metadata: name: mysql labels: app: mysql data: master.cnf: | [mysqld] log-bin slave.cnf: | [mysqld] super-read-only
configmap.yaml
apiVersion: v1 kind: Service metadata: name: mysql labels: app: mysql spec: ports: - name: mysql port: 3306 clusterIP: None selector: app: mysql --- apiVersion: v1 kind: Service metadata: name: mysql-read labels: app: mysql spec: ports: - name: mysql port: 3306 selector: app: mysql
service.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: pv001 labels: name: pv001 spec: nfs: path: /data/volumes/v1 server: 192.168.164.141 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 20Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv002 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: 192.168.164.141 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 20Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv003 labels: name: pv003 spec: nfs: path: /data/volumes/v3 server: 192.168.164.141 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 20Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv004 labels: name: pv004 spec: nfs: path: /data/volumes/v4 server: 192.168.164.141 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 20Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv005 labels: name: pv005 spec: nfs: path: /data/volumes/v5 server: 192.168.164.141 accessModes: ["ReadWriteMany","ReadWriteOnce"] capacity: storage: 20Gi
pv.yaml
apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql spec: selector: matchLabels: app: mysql serviceName: mysql replicas: 3 template: metadata: labels: app: mysql spec: initContainers: - name: init-mysql image: mysql:5.7 command: - bash - "-c" - | set -ex [[ `hostname` =~ -([0-9]+)$ ]] || exit 1 ordinal=${BASH_REMATCH[1]} echo [mysqld] > /mnt/conf.d/server-id.cnf echo server-id=$((100 + $ordinal )) >> /mnt/conf.d/server-id.cnf if [[ $ordinal -eq 0 ]]; then cp /mnt/config-map/master.cnf /mnt/conf.d/ else cp /mnt/config-map/slave.cnf /mnt/conf.d/ fi volumeMounts: - name: conf mountPath: /mnt/conf.d - name: config-map mountPath: /mnt/config-map - name: clone-mysql image: gcr.io/google-samples/xtrabackup:1.0 command: - bash - "-c" - | set -ex [[ -d /var/lib/mysql/mysql ]] && exit 0 [[ `hostname` =~ -([0-9]+)$ ]] || exit 1 ordinal=${BASH_REMATCH[1]} [[ $ordinal -eq 0 ]] && exit 0 ncat --recv-only mysql-$((ordinal-1)).mysql 3307 | xbstream -x -C /var/lib/mysql xtrabackup --prepare --target-dir=/var/lib/mysql volumeMounts: - name: data mountPath: /var/lib/mysql subPath: mysql - name: conf mountPath: /etc/mysql/conf.d containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ALLOW_EMPTY_PASSWORD value: "1" ports: - name: mysql containerPort: 3306 volumeMounts: - name: data mountPath: /var/lib/mysql subPath: mysql - name: conf mountPath: /etc/mysql/conf.d resources: requests: cpu: 500m memory: 500Mi livenessProbe: exec: command: ["mysqladmin","ping"] initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 readinessProbe: exec: command: ["mysql","-h","127.0.0.1","-e","SELECT 1"] initialDelaySeconds: 5 periodSeconds: 2 timeoutSeconds: 1 - name: xtrabackup image: gcr.io/google-samples/xtrabackup:1.0 ports: - name: xtrabackup containerPort: 3307 command: - bash - "-c" - | set -ex cd /var/lib/mysql if [[ -f xtrabackup_slave_info ]]; then mv xtrabackup_slave_info change_master_to.sql.in rm -f xtrabackup_binlog_info elif [[ -f xtrabackup_binlog_info ]]; then [[ `cat xtrabackup_binlog_info` =~ ^(.*)[[:space:]]+(.*?)$ ]] || exit 1 rm xtrabackup_binlog_info echo "change master to master_log_file=‘${BASH_REMATCH[1]}‘,\ MASTER_LOG_POS=${BASH_REMATCH[2]}" > change_master_to.sql.in fi if [[ -f change_master_to.sql.in ]]; then echo "waiting for mysqld to be ready" until mysql -h 127.0.0.1 -e "SELECT 1";do sleep 1;done echo "Initializing replication from clone position" mv change_master_to.sql.in change_master_to.sql.orig mysql -h 127.0.0.1 <<EOF $(<change_master_to.sql.orig), MASTER_HOST=‘mysql-0.mysql‘, MASTER_USER=‘root‘, MASTER_PASSWORD=‘‘, MASTER_CONNECT_RETRY=10; START SLAVE; EOF fi exec ncat --listen --keep-open --send-only --max-conns=1 3307 -c "xtrabackup --backup --slave-info --stream=xbstream --host=127.0.0.1 --user=root" volumeMounts: - name: data mountPath: /var/lib/mysql subPath: mysql - name: conf mountPath: /etc/mysql/conf.d volumes: - name: conf emptyDir: {} - name: config-map configMap: name: mysql volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi
StatefulSet.yaml
问题调试
volumes字段属于Pod对象的
volumesMount字段属于容器对象的
volumeClaimTemplates字段属于StatefulSet对象的
- name: MYSQL_ALLOW_EMPTY_PASSWORD
value: "1" 不能写成value: 1 必须加上双引号 否则会出现异常
CrashLoopBackOff状态 查看Pod中指定容器的日志信息
kubectl logs mysql-0 init-mysql
kubectl logs mysql-0 clone-mysql
kubectl logs mysql-0 mysql
kubectl logs mysql-0 xtrabackup
Pending状态
kubectl describe pod mysql-2 一般是pod调度失败