k8s-安装elasticsearch组件/解决nfs动态生成pv报错
k8s集群:
k8s的控制节点
- ip:192.168.1.180/181/182
- 主机名:master01/02/03
- 配置:6vCPU/6Gi内存
k8s的工作节点:
- ip:192.168.1.183
- 主机名:node01
- 配置:6vCPU/8Gi内存
安装elasticsearch组件
1.创建名称空间
在安装Elasticsearch集群之前,我们先创建一个名称空间,在这个名称空间下安装日志收工具elasticsearch、fluentd、kibana。我们创建一个kube-logging名称空间,将EFK组件安装到该名称空间中。
[root@master01 EFK]# cat kube-logging.yaml kind: Namespace apiVersion: v1 metadata: name: kube-logging [root@master01 EFK]# kubectl apply -f kube-logging.yaml namespace/kube-logging created
查看kube-logging名称空间是否创建成功
[root@master01 EFK]# kubectl get namespaces | grep kube-logging kube-logging Active 29s
安装elasticsearch组件
通过上面步骤已经创建了一个名称空间kube-logging,在这个名称空间下去安装日志收集组件efk,首先,我们需要部署一个有3个节点的Elasticsearch集群。我们使用3个Elasticsearch Pods可以避免高可用中的多节点群集中发生的“裂脑”的问题。 Elasticsearch脑裂可参考如下:
https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain
2.创建headless service服务
创建一个headless service的Kubernetes服务,服务名称是elasticsearch,这个服务将为3个Pod定义一个DNS域。headless service不具备负载均衡也没有IP。要了解有关headless service的更多信息,
可参考https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
[root@master01 elasticsearch]# kubectl apply -f elasticsearch_svc.yaml service/elasticsearch created
查看elasticsearch的service是否创建成功
[root@master01 elasticsearch]# kubectl get svc -n kube-logging NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 2m5s
elasticsearch_svc.yaml文件如下:
[root@master01 elasticsearch]# cat elasticsearch_svc.yaml kind: Service apiVersion: v1 metadata: name: elasticsearch namespace: kube-logging labels: app: elasticsearch spec: selector: app: elasticsearch clusterIP: None ports: - port: 9200 name: rest - port: 9300 name: inter-node
- 在kube-logging名称空间定义了一个名为 elasticsearch 的 Service服务,带有app=elasticsearch标签,当我们将 Elasticsearch StatefulSet 与此服务关联时,服务将返回带有标签app=elasticsearch的 Elasticsearch Pods的DNS A记录,然后设置clusterIP=None,将该服务设置成无头服务。最后,我们分别定义端口9200、9300,分别用于与 REST API 交互,以及用于节点间通信。
3.创建Storageclass,实现存储类动态供给
#安装nfs服务,选择k8s集群的master1节点,k8s集群的master1节点的ip是192.168.1.180
3.1安装nfs【master/node都要安装】
yum install nfs-utils -y
3.2创建一个nfs共享目录
[root@master01 nfs]# mkdir /data/v1
3.3#编辑/etc/exports文件
[root@master01 nfs]# echo "/data/v1 192.168.1.0/24(rw,no_root_squash)" >/etc/exports
加载配置
exportfs -arv
3.4#启动nfs服务
systemctl enable nfs.service systemctl start nfs
3.5#创建nfs作为存储的供应商
1、创建sa
[root@master01 nfs-provisioner]# kubectl apply -f nfs-sa.yaml serviceaccount/nfs-provisioner created
[root@master01 nfs-provisioner]# cat nfs-sa.yaml apiVersion: v1 kind: ServiceAccount metadata: name: nfs-provisioner
2、对sa做rbac授权
[root@master01 nfs-provisioner]# kubectl apply -f rbac.yaml clusterrole.rbac.authorization.k8s.io/nfs-provisioner-runner created clusterrolebinding.rbac.authorization.k8s.io/run-nfs-provisioner created role.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created rolebinding.rbac.authorization.k8s.io/leader-locking-nfs-provisioner created
rbac.yaml文件如下:
[root@master01 nfs-provisioner]# cat rbac.yaml kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-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"] - apiGroups: [""] resources: ["services", "endpoints"] verbs: ["get"] - apiGroups: ["extensions"] resources: ["podsecuritypolicies"] resourceNames: ["nfs-provisioner"] verbs: ["use"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: run-nfs-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-provisioner rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-provisioner subjects: - kind: ServiceAccount name: nfs-provisioner namespace: default roleRef: kind: Role name: leader-locking-nfs-provisioner apiGroup: rbac.authorization.k8s.io
不想写了,参考前面写的这篇
4.通过statefulset创建elasticsearch集群
4.1下载安装镜像
[root@node01 ~]# docker pull elasticsearch:7.14.0
4.2#更新资源清单文件
[root@master01 elasticsearch]# kubectl apply -f elasticsearch-statefulset.yaml statefulset.apps/es-cluster created
elasticsearch-statefulset.yaml文件解释说明:
apiVersion: apps/v1 kind: StatefulSet metadata: name: es-cluster namespace: kube-logging spec: serviceName: elasticsearch replicas: 3¬ selector: matchLabels: app: elasticsearch template: metadata: labels: app: elasticsearch
上面内容的解释:在kube-logging的名称空间中定义了一个es-cluster的StatefulSet。然后,我们使用serviceName 字段与我们之前创建的headless ElasticSearch服务相关联。这样可以确保可以使用以下DNS地址访问StatefulSet中的每个Pod:,es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local,其中[0,1,2]与Pod分配的序号数相对应。我们指定3个replicas(3个Pod副本),将selector matchLabels 设置为app: elasticseach。该.spec.selector.matchLabels和.spec.template.metadata.labels字段必须匹配。
spec: containers: - name: elasticsearch image: elasticsearch:7.14.0 imagePullPolicy: IfNotPresent resources: limits: cpu: 1000m requests: cpu: 100m ports: - containerPort: 9200 name: rest protocol: TCP - containerPort: 9300 name: inter-node protocol: TCP volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data env: - name: cluster.name value: k8s-logs - name: node.name valueFrom: fieldRef: fieldPath: metadata.name - name: discovery.seed_hosts value: "es-cluster-0.elasticsearch,es-cluster-1.elasticsearch,es-cluster-2.elasticsearch" - name: cluster.initial_master_nodes value: "es-cluster-0,es-cluster-1,es-cluster-2" - name: ES_JAVA_OPTS value: "-Xms512m -Xmx512m"
上面内容解释:在statefulset中定义了pod,容器的名字是elasticsearch,镜像是docker.elastic.co/elasticsearch/elasticsearch:7.2.0。使用resources字段来指定容器至少需要0.1个vCPU,并且容器最多可以使用1个vCPU了解有关资源请求和限制,可参考
https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
容器暴露了9200和9300两个端口,名称要和上面定义的 Service 保持一致,通过volumeMount声明了数据持久化目录,定义了一个data数据卷,通过volumeMount把它挂载到容器里的/usr/share/elasticsearch/data目录。
容器中设置了一些环境变量:
- cluster.name:Elasticsearch 集群的名称,我们这里是 k8s-logs。
- node.name:节点的名称,通过metadata.name来获取。这将解析为 es-cluster-[0,1,2],取决于节点的指定顺序
- discovery.seed_hosts:此字段用于设置在Elasticsearch集群中节点相互连接的发现方法,它为我们的集群指定了一个静态主机列表。由于我们之前配置的是无头服务,我们的 Pod 具有唯一的 DNS 地址es-cluster-[0,1,2].elasticsearch.kube-logging.svc.cluster.local,因此我们相应地设置此地址变量即可。由于都在同一个 namespace 下面,所以我们可以将其缩短为es-cluster-[0,1,2].elasticsearch。
- 要了解有关 Elasticsearch 发现的更多信息,请参阅 Elasticsearch 官方文档:
- https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-discovery.html
- ES_JAVA_OPTS:这里我们设置为-Xms512m -Xmx512m,告诉JVM使用512 MB的最小和最大堆。这个值应该根据群集的资源可用性和需求调整这些参数。
- 要了解更多信息,请参阅设置堆大小的相关文档:https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
initContainers: - name: fix-permissions image: busybox command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"] securityContext: privileged: true volumeMounts: - name: data mountPath: /usr/share/elasticsearch/data - name: increase-vm-max-map image: busybox command: ["sysctl", "-w", "vm.max_map_count=262144"] securityContext: privileged: true - name: increase-fd-ulimit image: busybox command: ["sh", "-c", "ulimit -n 65536"] securityContext: privileged: true
这里我们定义了几个在主应用程序之前运行的 Init 容器,这些初始容器按照定义的顺序依次执行,执行完成后才会启动主应用容器。
- 第一个名为 fix-permissions 的容器用来运行 chown 命令,将 Elasticsearch 数据目录的用户和组更改为1000:1000(Elasticsearch 用户的 UID)。因为默认情况下,Kubernetes 用 root 用户挂载数据目录,这会使得 Elasticsearch 无法访问该数据目录,可以参考 Elasticsearch 生产中的一些默认注意事项相关文档说明:
- 第二个名为 increase-vm-max-map 的容器用来增加操作系统对mmap计数的限制,默认情况下该值可能太低,导致内存不足的错误,要了解更多关于该设置的信息,可以查看 Elasticsearch 官方文档说明:
- 最后一个初始化容器是用来执行ulimit命令增加打开文件描述符的最大数量的。
注意事项:
此外 Elastisearch Notes for Production Use 文档还提到了由于性能原因最好禁用 swap,对于 Kubernetes 集群而言,最好也是禁用 swap 分区的。
volumeClaimTemplates: - metadata: name: data labels: app: elasticsearch spec: accessModes: [ "ReadWriteOnce" ] storageClassName: do-block-storage resources: requests: storage: 10Gi
我们这里使用 volumeClaimTemplates 来定义持久化模板,Kubernetes 会使用它为 Pod 创建 PersistentVolume,设置访问模式为ReadWriteOnce,这意味着它只能被 mount 到单个节点上进行读写,然后最重要的是使用了一个名为do-block-storage的 StorageClass 对象,所以我们需要提前创建该对象,我们这里使用的 NFS 作为存储后端,所以需要安装一个对应的nfs provisioner 驱动。
#查看es的pod是否创建成功
[root@master01 elasticsearch]# kubectl get pod -n kube-logging NAME READY STATUS RESTARTS AGE es-cluster-0 1/1 Running 0 39s es-cluster-1 1/1 Running 0 21s es-cluster-2 1/1 Running 0 13s
[root@master01 elasticsearch]# kubectl get svc -n kube-logging NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE elasticsearch ClusterIP None <none> 9200/TCP,9300/TCP 15h
pod部署完成之后,可以通过REST API检查elasticsearch集群是否部署成功,使用下面的命令将本地端口9200转发到 Elasticsearch 节点(如es-cluster-0)对应的端口:
[root@master01 elasticsearch]# kubectl port-forward es-cluster-0 9200:9200 --namespace=kube-logging
然后,在另外的终端窗口中,执行如下请求,新开一个master1终端:
[root@master01 nfs-provisioner]# curl http://localhost:9200/_cluster/state?pretty|less
输出如下:
注意:k8s1.20+包括1.21+版本通过nfs provisioner动态生成pv会报错
Unexpected error getting claim reference to claim "default/test-claim1": selfLink was empty, can't make reference,报错原因是1.20版本启用了selfLink,解决方法如下;
编辑/etc/kubernetes/manifests/kube-apiserver.yaml
在这里:
spec: containers: - command: - kube-apiserver
添加这一行:
- --feature-gates=RemoveSelfLink=false
[root@master01 nfs-provisioner]# vim /etc/kubernetes/manifests/kube-apiserver.yaml [root@master02 ~]# vi /etc/kubernetes/manifests/kube-apiserver.yaml [root@master03 ~]# vi /etc/kubernetes/manifests/kube-apiserver.yaml
然后更新资源清单yaml文件即可:
[root@master01 nfs-provisioner]# kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml pod/kube-apiserver created [root@master02 ~]# kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml pod/kube-apiserver created [root@master03 ~]# kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml pod/kube-apiserver created
[root@master01 nfs-provisioner]# kubectl get pods -n kube-system | grep apiserver kube-apiserver 0/1 CrashLoopBackOff 2 66s kube-apiserver-master01 1/1 Running 0 32s kube-apiserver-master02 1/1 Running 24 6d20h kube-apiserver-master03 1/1 Running 1 6d20h
重新更新apiserver.yaml会有生成一个新的pod:kube-apiserver,这个pod状态是CrashLoopBackOff,需要删除,高可用集群,需要一台一台的改
[root@master01 nfs-provisioner]# kubectl delete pods kube-apiserver -n kube-system pod "kube-apiserver" deleted [root@master02 ~]# kubectl delete pods kube-apiserver -n kube-system pod "kube-apiserver" deleted [root@master03 ~]# kubectl delete pods kube-apiserver -n kube-system pod "kube-apiserver" deleted

评论