什么是 StatefulSet
StatefulSet 是用来管理有状态的应用,例如数据库。前面我们部署的应用,都是不需要存储数据,不需要记住状态的,可以随意扩充副本,每个副本都是一样的,可替代的。而像数据库、Redis 这类有状态的,则不能随意扩充副本。StatefulSet 会固定每个 Pod 的名字
部署 StatefulSet 类型的 redis
一主多从redis,动态扩缩容redis从库
redis-configmap.yaml
apiVersion: v1kind: ConfigMapmetadata:name: redisnamespace: demodata:redis.conf: |-daemonize noprotected-mode nobind 0.0.0.0port 6379timeout 300databases 16save 900 1save 300 10save 60 10000loglevel noticelogfile /data/redis.log################### RDB ####################dir /datastop-writes-on-bgsave-error yesrdbcompression yesdbfilename dump.rdb################## REPL ###################slave-read-only yesrepl-diskless-sync norepl-timeout 120################## SECURITY ###################requirepass 123456masterauth 123456################## AOF ####################appendonly noappendfilename "appendonly.aof"appendfsync no
redis-statefulset.yaml
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: demo
spec:
selector:
app: redis
ports:
- name: redis
port: 6379
targetPort: 6379
protocol: TCP
clusterIP: None
type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: demo
spec:
replicas: 2 # 默认是一主一从
selector:
matchLabels:
app: redis
serviceName: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:5
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- |
set -e
hostname=$(echo `hostname` | grep -oE '\-([0-9]+)$')
if [ "$hostname" != "-0" ]; then #如果不是第一个节点,则向第一个节点同步
redis-server /etc/redis.conf slaveof redis-0.redis.demo.svc.cluster.local 6379 # namespace为demo,所以主机名.demo.svc.cluster.local
else
redis-server /etc/redis.conf
fi
volumeMounts:
# - name: redis-data
# mountPath: /data
- name: redis-conf
mountPath: /etc/redis.conf
subPath: redis.conf
volumes:
- name: redis-conf
configMap:
name: redis
volumeClaimTemplates:
- metadata:
name: redis-data
namespace: demo
spec:
accessModes: ["ReadWriteMany"]
storageClassName: nfs-client
resources:
requests:
storage: 500Mi
部署
# 部署configmap
$ kubectl apply -f redis-configmap.yaml
# 部署statefulset
$ kubectl apply -f redis-statefulset.yaml
# 查看pod
$ kubectl get pods -n demo
NAME READY STATUS RESTARTS AGE
redis-0 1/1 Running 0 41s
redis-1 1/1 Running 0 16s
# 查看redis-1复制情况
$ kubectl exec redis-1 -n demo -- redis-cli -a 123456 info replication
# Replication
role:slave
master_host:redis-0.redis.demo.svc.cluster.local
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:98
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:4762f4b0a19cb5c31433078041b5e93bd12217d0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:98
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:98
扩容
扩容从redis-0->redis-N
# 扩容
$ kubectl scale statefulset redis --replicas=4 -n demo
# 查看pod
$ kubectl get pods -n demo
NAME READY STATUS RESTARTS AGE
redis-0 1/1 Running 0 2m13s
redis-1 1/1 Running 0 2m11s
redis-2 1/1 Running 0 27s
redis-3 1/1 Running 0 25s
# 查看复制信息,可见同步正常
$ kubectl exec redis-3 -n demo -- redis-cli -a 123456 info replication
# Replication
role:slave
master_host:redis-0.redis.demo.svc.cluster.local
master_port:6379
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:210
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:4762f4b0a19cb5c31433078041b5e93bd12217d0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:141
repl_backlog_histlen:70
缩容
缩容从redis-N->redis-0
# 缩容
$ kubectl scale statefulset redis --replicas=1 -n demo
# 查看pod
$ kubectl get pods -n demo
NAME READY STATUS RESTARTS AGE
redis-0 1/1 Running 0 11m
StatefulSet 特性
- Service 的 CLUSTER-IP 是空的,Pod 名字也是固定的。
- Pod 创建和销毁是有序的,创建是顺序的,销毁是逆序的。
- Pod 重建不会改变名字,除了IP,所以不要用IP直连
```shell
$ kubectl get svc -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis ClusterIP None
6379/TCP 4m37s
$ kubectl get pods -n demo NAME READY STATUS RESTARTS AGE redis-0 1/1 Running 0 41s redis-1 1/1 Running 0 16s
Endpoints 会多一个 hostname
```yaml
apiVersion: v1
kind: Endpoints
metadata:
annotations:
endpoints.kubernetes.io/last-change-trigger-time: "2022-07-05T08:23:31Z"
creationTimestamp: "2022-07-05T08:17:28Z"
labels:
service.kubernetes.io/headless: ""
name: redis
namespace: demo
resourceVersion: "593328"
uid: 880e7bd6-cdb2-4442-a837-20c55a2b1f89
subsets:
- addresses:
- hostname: redis-1
ip: 172.16.126.17
nodeName: k8s.lechuang.com
targetRef:
kind: Pod
name: redis-1
namespace: demo
resourceVersion: "593307"
uid: a7888f6d-ce37-42b8-80db-646a0eaf3c4b
- hostname: redis-2
ip: 172.16.126.22
nodeName: k8s.lechuang.com
targetRef:
kind: Pod
name: redis-2
namespace: demo
resourceVersion: "593326"
uid: 277aa568-6e59-4ed4-8bfa-3b011003d6ab
- hostname: redis-0
ip: 172.16.126.9
nodeName: k8s.lechuang.com
targetRef:
kind: Pod
name: redis-0
namespace: demo
resourceVersion: "592620"
uid: 8c566a38-8029-418d-abce-2180b44e954b
ports:
- name: redis
port: 6379
protocol: TCP
访问时,如果直接使用 Service 名字连接,会随机转发请求要连接指定 Pod,可以通过pod-name.service-name连接到指定pod。
# 临时启动一个redis-client容器
$ kubectl run -n demo redis-client --restart='Never' --image redis:5 --command -- bash
# pod-name.service-name连接到指定pod
$ kubectl exec --tty -i redis-client -n demo -- redis-cli -h redis-1.redis -a 123456 info replication
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Replication
role:slave
master_host:redis-0.redis.demo.svc.cluster.local
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:1092
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:4762f4b0a19cb5c31433078041b5e93bd12217d0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1092
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:365
repl_backlog_histlen:728
问题
pod 重建后,数据库持久化的内容丢失了,接下来解决这个问题。
