文章引导
前言
环境准备
部署NFS
部署StorageClass存储类(动态制备 PersistentVolume)
部署ZooKeeper
ZooKeeper健康检查
前言
ZooKeeper是一个用于维护配置信息、命名、提供分布式同步和提供组服务的集中式服务。所有这些类型的服务都以某种形式被分布式应用程序使用。每次实现它们时,都要做大量的工作来修复不可避免的错误和竞争条件。由于实现这些类型的服务很困难,应用程序最初通常会吝啬它们,这使得它们在发生更改时很脆弱,很难管理。即使操作正确,在部署应用程序时,这些服务的不同实现也会导致管理的复杂性。
在ZooKeeper Wiki上了解更多关于ZooKeeper。
NFS:网络文件系统,英文Network File System(NFS),是由SUN公司研制的UNIX表示层协议(presentation layer protocol),能使使用者访问网络上别处的文件就像在使用自己的计算机一样。
StorageClass存储类:(简称SC)
介绍:StorageClass 为管理员提供了描述存储 “类” 的方法。 不同的类型可能会映射到不同的服务质量等级或备份策略,或是由集群管理员制定的任意策略。 Kubernetes 本身并不清楚各种类代表的什么。这个类的概念在其他存储系统中有时被称为 “配置文件”。
创建PV有两种方法,第一种手动去写/创建PV的yaml文件,就叫做静态制备,如果遇到成百上千个需要创建PV的环境,那么对于集群管理员是一个耗时耗力的事情;现在使用sc存储类来进行动态制备。只需要管理员规定一个模板,PVC来遵循这个模板,那么就PV就会自动自动创建
本次文章采用动态制备
环境准备
CPU架构 | 系统版本 | Docker版本 | K8S版本 |
---|---|---|---|
x86_64 | CentOS 7.8 | 19.03.12 | v1.19.9 |
部署NFS
- 通过yum下载nfs相关组件,rpcbind:是一个RPC服务,主要是在nfs共享时候负责通知客户端,服务器的nfs端口号的。简单理解rpc就是一个中介服务
yum install -y nfs-utils rpcbind
- 创建共享的目录并授权(一定要授权不然,不会会权限不够)
mkdir /root/nfs
chmod 777 /root/nfs
- 编辑nfs共享配置文件
vim /etc/exports
#写入以下配置
/root/nfs *(rw,no_root_squash,no_all_squash,sync)
/root/nfs #代表共享的目录
* #代表不限制IP连接
rw #可进行读写
no_root_squash #NFS客户端使用root连接服务器端时,那么对于服务端共享目录也具有共享权限
no_all_squash #(默认)访问用户先与本机用户匹配,匹配失败后再映射为匿名用户或用户组
sync #文件写入的时候同时写入硬盘
- 启动rpcbind和nfs(需要先启动rpcbind,后启动nfs)
systemctl start rpcbind
systemctl enable rpcbind #加入开机自启
systemctl start nfs
systemctl enable nfs
- 查看共享目录
showmount -e localhost #localhost(指本机 127.0.0.1 的意思)
6. 通过其他服务器测试挂载nfs磁盘
yum install -y nfs-utils #下载nfs-utils
mkdir /test #创建要被挂载的目录
mount 10.64.95.18:/root/nfs/mysql /test #IP地址是nfs服务器地址和共享目录,后者是本机目录
注意:挂载端需要下载第一步中的:nfs-utils,不会挂载的时候会提示以下错误
部署StorageClass(动态制备 PersistentVolume)
由于Kubernetes没有NFS驱动,也默认不支持NFS类型的StorageClass,从而需要外部驱动并修改配置文件
vim /etc/kubernetes/manifests/kube-apiserver.yaml #打开apiserver的yaml文件
添加以下配置
- --feature-gates=RemoveSelfLink=false
然后通过cat命令能查到配置
因为是自动应用,可以不用重启apiserver,如果不生效就通过以下命令重启下apiserver
systemctl restart kubelet
下载外部的制备器(内部含镜像文件,可提前下载)
访问github,下载deployment.yaml和rbac.yaml的两个文件,访问不到的话可以使用以下配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest #镜像
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: nfs #可以按照自己的命名更改
- name: NFS_SERVER
value: 10.10.10.60 #NFS服务器地址
- name: NFS_PATH
value: /ifs/kubernetes #共享目录
volumes:
- name: nfs-client-root
nfs:
server: 10.10.10.60 #NFS服务器地址
path: /ifs/kubernetes #共享目录
deployment.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
rbac.yaml
开始安装
kubectl apply -f rbac.yaml #yaml文件里包含命名空间,可根据自身情况修改
kubectl apply -f deployment.yaml #yaml文件里面的下载策略,建议修改为imagePullPolicy: IfNotPresent
查看制备器的状态
创建StorageClass
[root@ks8-master ~]# vim storageclass.yaml
[root@ks8-master ~]# cat storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: nfs # 这个就是deployemnt的yaml文件中的环境变量:PROVISIONER_NAME
parameters:
archiveOnDelete: "false"
allowVolumeExpansion: true
[root@ks8-master ~]# kubectl apply -f storageclass.yaml
查看创建的StorageClass
测试
[root@ks8-master pvc]# vim auto-pvc.yaml
#storageClassName要和刚才创建的storageClass一样
[root@ks8-master pvc]# cat pvc1.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: aotu-pvc-test
namespace: ggzj
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 20Gi
storageClassName: managed-nfs-storage
[root@ks8-master pvc]# kubectl apply -f auto-pvc.yaml
persistentvolumeclaim/aotu-pvc-test created
[root@ks8-master pvc]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
aotu-pvc-test Bound pvc-af12e765-3459-4b7a-acdb-744fb81b2bb5 20Gi RWX managed-nfs-storage 13s
删除测试PVC
[root@ks8-master pvc]# kubectl delete -f auto-pvc.yaml
persistentvolumeclaim "aotu-pvc-test" deleted
[root@ks8-master pvc]# kubectl get pv
注意:deployment的yaml文件的PROVISIONER_NAME的环境变量的值必须与StorageClass的yaml文件的provisioner的值对应
StorageClass的yaml文件的name值必须与PVC的yaml文件的storageClassName值对应
部署ZooKeeper
这里引用kubernetes的官方文档
- 创建一个zookeeper的yaml文件并保存,包含一个无头服务、 一个 Service、 一个 PodDisruptionBudget 和一个 StatefulSet。(本文章的K8s的版本较低,故不能使用PodDisruptionBudget 已经注释)。
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: Always
image: "registry.k8s.io/kubernetes-zookeeper:1.0-3.4.10" #镜像拉取不下来可以按照下面注意里面的镜像
imagePullPolicy: IfNotPresent
resources:
requests:
memory: "1Gi"
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
annotations:
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" #managed-nfs-storage为我们创建的storage-class名称
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
注意:image中的镜像可能拉去不下来,请使用这个景象mirrorgooglecontainers/kubernetes-zookeeper:1.0-3.4.10
volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" #这里面的storage-class的值必须为上述的storage-class名字
- 创建zk集群
[root@ks8-master ~] kubectl apply -f zookeeper.yaml
service/zk-hs created
service/zk-cs created
statefulset.apps/zk created
- 查看创建状态,等待所有POD的状态都显示
Running
[root@ks8-master ~] kubectl get pods -w -l app=zk
NAME READY STATUS RESTARTS AGE
zk-0 1/1 Running 0 40s
zk-1 1/1 Running 0 40s
zk-2 1/1 Running 0 40s
- 开始验证ZK集群
促成 Leader 选举
由于在匿名网络中没有用于选举 leader 的终止算法,Zab 要求显式的进行成员关系配置, 以执行 leader 选举。Ensemble 中的每个服务器都需要具有一个独一无二的标识符, 所有的服务器均需要知道标识符的全集,并且每个标识符都需要和一个网络地址相关联。
(1) 使用命令 kubectl exec 获取 zk StatefulSet 中 Pod 的主机名。
StatefulSet 控制器基于每个 Pod 的序号索引为它们各自提供一个唯一的主机名。 主机名采用 <statefulset 名称>-<序数索引> 的形式。 由于 zk StatefulSet 的 replicas 字段设置为 3,这个集合的控制器将创建 3 个 Pod,主机名为:zk-0、zk-1 和 zk-2。
[root@ks8-master ~] for i in 0 1 2; do kubectl exec zk-$i -- hostname; done
zk-0
zk-1
zk-2
ZooKeeper ensemble 中的服务器使用自然数作为唯一标识符, 每个服务器的标识符都保存在服务器的数据目录中一个名为 myid 的文件里。
(2) 检查每个服务器的 myid 文件的内容。由于标识符为自然数并且序号索引是非负整数,你可以在序号上加 1 来生成一个标识符。
[root@ks8-master ~] for i in 0 1 2; do echo "myid zk-$i";kubectl exec zk-$i -- cat /var/lib/zookeeper/data/myid; done
myid zk-0
1
myid zk-1
2
myid zk-2
3
(3) 获取 zk StatefulSet 中每个 Pod 的全限定域名(Fully Qualified Domain Name,FQDN)
zk-hs Service 为所有 Pod 创建了一个域:zk-hs.default.svc.cluster.local。
[root@ks8-master ~] for i in 0 1 2; do kubectl exec zk-$i -- hostname -f; done
zk-0.zk-hs.default.svc.cluster.local
zk-1.zk-hs.default.svc.cluster.local
zk-2.zk-hs.default.svc.cluster.local
Kubernetes DNS 中的 A 记录将 FQDN 解析成为 Pod 的 IP 地址。 如果 Kubernetes 重新调度这些 Pod,这个 A 记录将会使用这些 Pod 的新 IP 地址完成更新, 但 A 记录的名称不会改变。
(4) ZooKeeper 在一个名为 zoo.cfg 的文件中保存它的应用配置。 使用 kubectl exec 在 zk-0 Pod 中查看 zoo.cfg 文件的内容。
文件底部为 server.1、server.2 和 server.3,其中的 1、2 和 3 分别对应 ZooKeeper 服务器的 myid 文件中的标识符。 它们被设置为 zk StatefulSet 中的 Pods 的 FQDNs。
[root@ks8-master ~] kubectl exec zk-0 -- cat /opt/zookeeper/conf/zoo.cfg
clientPort=2181
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
tickTime=2000
initLimit=10
syncLimit=2000
maxClientCnxns=60
minSessionTimeout= 4000
maxSessionTimeout= 40000
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
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
ZooKeeper健康检查
最基本的健康检查是向一个 ZooKeeper 服务器写入一些数据,然后从另一个服务器读取这些数据。[root@ks8-master ~] kubectl exec zk-0 zkCli.sh create /hello world
最后会输出以下内容:
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
Created /hello
使用下面的命令从 zk-1 Pod 获取数据。
[root@ks8-master ~] kubectl exec zk-1 zkCli.sh get /hello
最后会消费以下内容:
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
world #获取内容
cZxid = 0x100000002
ctime = Thu Dec 08 15:13:30 UTC 2016
mZxid = 0x100000002
mtime = Thu Dec 08 15:13:30 UTC 2016
pZxid = 0x100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0