随着云原生化流行的大趋势,我们的基础组件也需要逐渐上Kubernetes了。Apache Zookeeper作为目前最流行的分布式协调组件,在我们的微服务架构中负责扮演注册中心的角色。
在Kubernetes中运行Zookeeper集群是很有意义的,可以利用其原生的弹性扩缩容、高可用特性。
先说下使用的k8s的版本是1.25.0,对于PodDisruptionBudget的apiVersion会有影响,某些更老的k8s版本,apiVersion是policy/v1bata,1.25.0的k8s对应的apiVersion是policy/v1。
使用StatefulSet部署Zookeeper
官方提供了使用statefulSet的方式来部署 Zookeeper 运行 Zookeeper,它会创建一个headless service,一个cluster service,一个podDisruptionBudget,一个statefulSet。
apiVersion: v1
kind: PersistentVolume
metadata:
name: zk-pv
spec:
capacity:
storage: 512Mi
accessModes:
- ReadWriteOnce
hostPath:
path: /data
---
apiVersion: v1
kind: Service
metadata:
name: zk-hs
labels:
app: zk
spec:
ports:
- port: 2888
name: server
- port: 3888
name: leader-election
clusterIP: None
selector:
app: zk
---
apiVersion: v1
kind: Service
metadata:
name: zk-cs
labels:
app: zk
spec:
ports:
- port: 2181
name: client
selector:
app: zk
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk-pdb
spec:
selector:
matchLabels:
app: zk
maxUnavailable: 1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zk
spec:
selector:
matchLabels:
app: zk
serviceName: zk-hs
replicas: 3
updateStrategy:
type: RollingUpdate
podManagementPolicy: OrderedReady
template:
metadata:
labels:
app: zk
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- zk
topologyKey: "kubernetes.io/hostname"
containers:
- name: kubernetes-zookeeper
imagePullPolicy: IfNotPresent
image: "harbor.martin.cc/google_containers/kubernetes-zookeeper:1.0-3.4.10"
resources:
requests:
memory: "0.5Gi"
cpu: "0.5"
ports:
- containerPort: 2181
name: client
- containerPort: 2888
name: server
- containerPort: 3888
name: leader-election
command:
- sh
- -c
- "start-zookeeper \
--servers=3 \
--data_dir=/var/lib/zookeeper/data \
--data_log_dir=/var/lib/zookeeper/data/log \
--conf_dir=/opt/zookeeper/conf \
--client_port=2181 \
--election_port=3888 \
--server_port=2888 \
--tick_time=2000 \
--init_limit=10 \
--sync_limit=5 \
--heap=512M \
--max_client_cnxns=60 \
--snap_retain_count=3 \
--purge_interval=12 \
--max_session_timeout=40000 \
--min_session_timeout=4000 \
--log_level=INFO"
readinessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 10
timeoutSeconds: 5
livenessProbe:
exec:
command:
- sh
- -c
- "zookeeper-ready 2181"
initialDelaySeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: datadir
mountPath: /var/lib/zookeeper
securityContext:
runAsUser: 1000
fsGroup: 1000
volumeClaimTemplates:
- metadata:
name: datadir
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 512Mi
使用 kubectl apply应用这个配置文件,等待一会之后,发现pod和service都已创建成功。
kubectl get pod 查看到 zk-0 pod一直pending.
kubectl describe pod zk-0 看到事件原因是:
0/4 nodes are available: 4 pod has unbound immediate PersistentVolumeClaims. preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling.
再使用:
kubectl logs -n kube-system kube-controller-manager-k8s-master01.example.local
查看controller-manager的pod日志:
I0513 08:13:08.710572 1 event.go:294] "Event occurred" object="default/datadir-zk-0" fieldPath="" kind="PersistentVolumeClaim" apiVersion="v1" type="Normal" reason="FailedBinding" message="no persistent volumes available for this claim and no storage class is set"
这么看,原来PV和 storage class 都没搞。这个网上的参考还是需要自己补充一下的,PV的API定义已经加到上面的资源清单了。
PV弄了个HostPath的。但是也不知道存在哪个节点上。
然后再执行kubectl apply -f zookeeper.yml创建,POD zk-0未启动成功,出来错误CrashLoopBackOff。通过kubectl logs zk-0查看POD日志:
#This file was autogenerated DO NOT EDIT clientPort=2181 dataDir=/var/lib/zookeeper/data dataLogDir=/var/lib/zookeeper/data/log tickTime=2000 initLimit=10 syncLimit=5 maxClientCnxns=60 minSessionTimeout=4000 maxSessionTimeout=40000 autopurge.snapRetainCount=3 autopurge.purgeInteval=12 server.1=zk-0.zk-hs.default.svc.cluster.local:2888:3888 server.2=zk-1.zk-hs.default.svc.cluster.local:2888:3888 server.3=zk-2.zk-hs.default.svc.cluster.local:2888:3888 Creating ZooKeeper log4j configuration mkdir: cannot create directory '/var/lib/zookeeper/data': Permission denied chown: cannot access '/var/lib/zookeeper/data': No such file or directory mkdir: cannot create directory '/var/lib/zookeeper/data': Permission denied chown: invalid group: 'zookeeper:USER' /usr/bin/start-zookeeper: line 176: /var/lib/zookeeper/data/myid: No such file or directory
有个疑问,这个日志是一直存着吗,还是POD没启动成功,我用kubectl delete -f zookeeper删除掉POD,再看下,还有没有这个日志。肯定没了:
kubectl logs zk-0
Error from server (NotFound): pods "zk-0" not found
原来是POD及容器对于卷的目录主机目录没有写与创建子目录的权限。把对应的目录POD部署节点的目录 /data/,权限修改成全部可以读写创建:
chmod 777 /data/
重启后,还是有问题:
发现pod zk-0启动成功了,但是到了zk-1启动失败,原来是每个pod要创建一个hostPath的PV。接下来创建两个zk-pv1,zk-pv2,在node2和node3上创建好对应的host文件夹/data/,并设置权限777。然后重启,三个pod都启动成功了。
恭喜自己,部署看起来是成功了,那么就真正测试一下部署好的zk集群吧。
要测试在 Kubernetes 上部署的 ZooKeeper 集群,可以执行以下步骤:
获取 ZooKeeper 的客户端工具,例如 ZooKeeper 命令行客户端 zkCli.sh
。可以在 ZooKeeper 的官方网站下载安装包,并解压获取相关工具。
进入 Kubernetes 集群中的任意一个 Pod,可以使用 kubectl exec
命令进入 Pod 的容器内部。
kubectl exec -it -- /bin/bash
在容器内部,使用 ZooKeeper 客户端工具连接到 ZooKeeper 集群。根据您的部署方式和服务发现配置,可以使用 Service 名称或 Pod IP 连接到 ZooKeeper。
./zkCli.sh -server :
或者
./zkCli.sh -server :
这里的
是您 ZooKeeper 服务的名称,
是服务的端口号。如果您使用的是 StatefulSet 部署方式,可以使用 zk-{index}.
的方式连接到每个 ZooKeeper Pod。
连接成功后,您可以执行各种 ZooKeeper 的命令和操作,例如创建节点、设置数据、监听事件等。可以通过命令 help
或 ?
获取更多可用的命令列表和用法。
[zk: :] help
[zk: :] ?
例如,可以使用以下命令创建一个节点并设置数据:
[zk: :] create /test mydata
然后使用以下命令获取节点的数据:
[zk: :] get /test
通过执行各种命令和操作,可以验证 ZooKeeper 集群的功能和可用性。
请注意,上述命令中的
和
需要替换为实际的 ZooKeeper 服务名称和端口。具体的连接信息取决于您的部署方式和配置。
都是验证通过的,说明安装成功了,Oh Yeah!
附:安装的容器镜像描述信息:
The ZooKeeper package is installed into the /opt/zookeeper directory, all configuration is sym linked into the /usr/etc/zookeeper/, and all executables are sym linked into /usr/bin. The ZooKeeper data directories are contained in /var/lib/zookeeper. This is identical to the RPM distribution that users should be familiar with.
所以 /var/lib/zookeeper这个文件夹是放数据的,所以我们就明白为什么要把这个文件夹放到PV了。