部署elasticsearch集群
已经完成的工作:创建存储。首先配置了nfs存储提供商(nfs-deployment.yaml),然后通过创建存储类(storageclass.yaml)来将nfs服务器与存储类绑定:
[root@master 31efk]# cat storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: do-block-storage
provisioner: example.com/nfs
创建了elasticsearch的服务(elasticsearch_svc.yaml),定义了两个接口,一个是对外的,一个是elasticsearch集群内部通信的。
[root@master 31efk]# cat elasticsearch_svc.yaml
kind: Service
apiVersion: v1
metadata:
name: elasticsearch31
namespace: kube-logging31
labels:
app: elasticsearch
spec:
selector:
app: elasticsearch
clusterIP: None
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node
现在创建elasticsearch的具体pod。
这里的集群创建方式为statefulset,因为elasticsearch需要稳定的pod管理和持久化的存储。
-
有状态服务:Elasticsearch节点存储数据,因此需要持久化存储(即磁盘卷),并且需要保证每个节点能够持有自己特定的数据,即使在Pod重启或迁移时。
-
稳定的网络标识:StatefulSet为每个Pod分配一个稳定且唯一的网络标识(DNS名),这对于Elasticsearch这样的分布式系统至关重要,因为节点之间需要通过固定的名称相互通信。
-
顺序部署和终止:StatefulSet保证Pod按照顺序创建和删除,这有助于Elasticsearch集群的节点顺利加入和退出,确保集群的健康状态。
elasticsearch的yaml文件
第一部分
第一部分,可以看到部署在kube-logging31命名空间,匹配了elasticsearch31的service。容器内部也开放对应的端口。
对于statefulset来讲,需要持久化存储,这里定义了PVC模板,用来从storageClass进行请求PV。(当 PVC 指定了某个 StorageClass
时,Kubernetes 会使用该 StorageClass
动态创建(或供应)一个 PV,并将其绑定到 PVC 上。)这里的PVC的name是data,这里容器绑定的存储名称也是data,路径为/usr/share/elasticsearch/data。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: elastic-cluster31
namespace: kube-logging31
spec:
serviceName: elasticsearch31
replicas: 3
selector:
matchLabels:
app: elasticsearch
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: do-block-storage
resources:
requests:
storage: 10Gi
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.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
第二部分
container的环境变量:这里设置的环境变量主要是为了进行一个集群的搭建,这里的集群和statefulset里面的name是不一样的,具体关系如下:
-
selector
主要用于 Kubernetes 内部 ,它负责确定哪些 Pods 属于哪个 StatefulSet,或者哪些 Pods 应该由某个服务来管理。但是,selector
并不能直接影响 Elasticsearch 集群的内部逻辑,也不能用来标识 Elasticsearch 中的节点。 -
可以说selector主要方便容器的生命周期管理,不能控制elasticsearch集群内部的逻辑。而elasticsearch需要进行节点选举等更加精确的控制,因此还需要额外的配置。
-
node.name
:作用于 Elasticsearch 层,是 Elasticsearch 集群中唯一标识每个节点的配置。每个 Elasticsearch 节点必须有一个唯一的node.name
,以便 Elasticsearch 能够正确地管理和协同多节点的操作,如主节点选举、数据分片分配等。
所以下面的container的环境变量就是规定了elasticsearch的集群名称,以及node.name。
由于在statefulset中,创建的pod的名字是固定的,名称格式是<statefulset-name>-<ordinal> 。由于statefulset的名称是es-cluster,所以pod的名称就是es-cluster31-0,es-cluster31-1,es-cluster31-2。而这里的discovery.seed_hosts是每一个pod的DNS地址,每一个pod的DNS地址格式为:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
所以看到 discovery.seed_hosts:"es-cluster31-0.elasticsearch31.kube-logging31.svc.cluster.local,es-cluster31-1.elasticsearch31.kube-logging31.svc.cluster.local,es-cluster31-2.elasticsearch31.kube-logging31.svc.cluster.local"
env:
- name: cluster.name
value: k8s-logs
- name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: discovery.seed_hosts
value: "elastic-cluster31-0.elasticsearch31.kube-logging31.svc.cluster.local,elastic-cluster31-1.elasticsearch31.kube-logging31.svc.cluster.local,elastic-cluster31-2.elasticsearch31.kube-logging31.svc.cluster.local"
- name: cluster.initial_master_nodes
value: "elastic-cluster31-0,elastic-cluster31-1,elastic-cluster31-2"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
第三部分
这里的三个初始化容器是为了更好的准备elasticsearch的容器环境,确保在elasticsearch启动之前,所有的权限都已经配置正确。
fix-permission
第一个fix-permission是为了修复数据目录的权限。这里需要介绍pod的创建顺序。initcontainers和主容器都是 Pod 的一部分。当 Kubernetes 创建 Pod 时,会按照以下顺序执行操作:
- 创建 Pod :Kubernetes 会首先创建整个 Pod 实体,包括其描述的所有容器(
initContainers
和主容器)。 - 挂载卷(PVC) :当 Pod 被创建时,Kubernetes 会根据 Pod 的定义(包括
volumeMounts
和PersistentVolumeClaim
)将所需的持久卷挂载到 Pod 中的指定路径。这一步是 在所有容器启动之前 完成的。 - 启动
initContainers
:卷挂载完成后,Kubernetes 会启动initContainers
,这些容器可以访问 Pod 中已经挂载的卷。 - 启动主容器 :当 所有
initContainers
成功运行完成 后,Kubernetes 才会启动主容器(即 Elasticsearch 容器)。
所以可以看到,在initContainer启动之前,持久卷的挂载已经到了pod的指定路径。但是Kubernetes 动态分配的存储卷(通过 PVC 申请的持久卷)可能默认具有不适合 Elasticsearch 的文件权限。
Elasticsearch 容器通常以特定用户身份(如 UID 1000
)运行,而持久卷可能以 root 用户(UID 0
)创建。为了让 Elasticsearch 拥有正确的访问权限,initContainer
需要修改挂载卷的数据目录权限。因此第一个initContainer:fix-permissions
容器的运行确保数据目录的权限是正确的(chown -R 1000:1000
),这样 Elasticsearch 主容器启动时能够正常访问和写入数据。
initContainers:
- name: fix-permissions
image: busybox
imagePullPolicy: IfNotPresent
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
imagePullPolicy: IfNotPresent
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
- name: increase-fd-ulimit
image: busybox
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "ulimit -n 65536"]
securityContext:
privileged: true
increase-vm-max-map
容器本质上是一个轻量级的隔离环境,在容器中运行的应用程序实际上是一个linux进程。一般来讲,linux会限制每一个进程的内存映射数量,从而限制这个进程对资源的消耗。
Luence是elasticsearch的核心技术,是一个全文检索技术。Luence通过使用操作系统的 内存映射(memory-mapped I/O) 技术来高效访问索引文件,尤其是在处理大规模的索引时。内存映射允许 Lucene 将磁盘上的索引文件映射为进程的虚拟内存空间,因此可以像访问普通内存一样访问这些文件。
但是对于elasticsearch这种日志收集的容器,linux给的 65530个内存映射区域是不够的**,** 当 Elasticsearch/Lucene 达到系统允许的最大内存映射区域数量时,操作系统会拒绝为新的索引文件或数据文件创建内存映射。这会导致Lucene 无法加载新的索引文件,从而影响搜索和查询的执行。
所以这个increase-vm-max-map的容器,把vm-max-count的数量增加到了262144。
increase-fd-ulimit
这个容器用来提升进程可以打开的最大文件描述符的限制。
elasticsearch部署
随后进行elasticsearch的部署,发现三个容器正常启动:
[root@master 31efk]# kubectl get pods -n kube-logging31 -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
elastic-cluster31-0 1/1 Running 0 114s 10.244.104.62 node2 <none> <none>
elastic-cluster31-1 1/1 Running 0 110s 10.244.166.184 node1 <none> <none>
elastic-cluster31-2 1/1 Running 0 103s 10.244.104.63 node2 <none> <none>
由于elasticsearch没有对外暴露端口,可以进行端口映射,然后在集群外面访问
kubectl port-forward elastic-cluster31-0 9200:9200 -n kube-logging31
然后打开另一个端口,进行访问,可以看到很多内容。
[root@master 31efk]# curl http://localhost:9200/_cluster/state?pretty
{
"cluster_name" : "k8s-logs",
"cluster_uuid" : "4fuxJ67HSxSBsVDfKIA8Qg",
"version" : 17,
"state_uuid" : "PpwhJDXlQvarKzCMhUy2Og",
"master_node" : "UWRrJRlCQ-uDcqZ3M6Y4YQ",
"blocks" : { },
"nodes" : {
"UWRrJRlCQ-uDcqZ3M6Y4YQ" : {
"name" : "elastic-cluster31-1",
"ephemeral_id" : "S3NLgNGrRBOjWuoNC1CcHw",
"transport_address" : "10.244.166.185:9300",
"attributes" : {
"ml.machine_memory" : "3953741824",
"ml.max_open_jobs" : "20",
"xpack.installed" : "true"
}
},
"PlT4KVDFSI2qztJqF99eYw" : {
"name" : "elastic-cluster31-2",
"ephemeral_id" : "z1akgw8xTjurYdq1_DZ7-Q",
"transport_address" : "10.244.104.1:9300",
"attributes" : {
"ml.machine_memory" : "3953741824",
"ml.max_open_jobs" : "20",
"xpack.installed" : "true"
}
},
"XXd95XfcRgW69C1ls0ZFTw" : {
"name" : "elastic-cluster31-0",
"ephemeral_id" : "aKjaY6m0TbCTnynDPdD7JA",
"transport_address" : "10.244.104.2:9300",
"attributes" : {
"ml.machine_memory" : "3953741824",
"ml.max_open_jobs" : "20",
"xpack.installed" : "true"
}
}
},
"metadata" : {
"cluster_uuid" : "4fuxJ67HSxSBsVDfKIA8Qg",
"cluster_coordination" : {
"term" : 1,
"last_committed_config" : [
"UWRrJRlCQ-uDcqZ3M6Y4YQ",
"PlT4KVDFSI2qztJqF99eYw",
"XXd95XfcRgW69C1ls0ZFTw"
],
"last_accepted_config" : [
"UWRrJRlCQ-uDcqZ3M6Y4YQ",
"PlT4KVDFSI2qztJqF99eYw",
"XXd95XfcRgW69C1ls0ZFTw"
],
...
其实如果不进行端口映射,在elasticsearch的内部也可以进行访问。可以看到elasticsearch的三个pod,他们之间可以互相发现。
[root@master 31efk]# kubectl exec -it elastic-cluster31-0 -n kube-logging31 -- /bin/bash
Defaulted container "elasticsearch" out of: elasticsearch, fix-permissions (init), increase-vm-max-map (init), increase-fd-ulimit (init)
[root@elastic-cluster31-0 elasticsearch]# curl http://localhost:9200/_cat/nodes?v
ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
10.244.166.185 18 95 1 0.13 0.20 0.18 mdi * elastic-cluster31-1
10.244.104.1 17 88 0 0.02 0.05 0.08 mdi - elastic-cluster31-2
10.244.104.2 23 88 0 0.02 0.05 0.08 mdi - elastic-cluster31-0
pv和pvc
[root@master 31efk]# kubectl get pv --all-namespaces -owide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE VOLUMEMODE
pvc-124274e8-1977-4e46-895d-d6b9449682f7 10Gi RWO Delete Bound kube-logging31/data-elastic-cluster31-1 do-block-storage 145m Filesystem
pvc-677221e5-ed6a-417b-ac69-6b90304afa4e 10Gi RWO Delete Bound kube-logging31/data-elastic-cluster31-0 do-block-storage 148m Filesystem
pvc-944a372c-1c0d-442d-998a-b62c5be7ca21 10Gi RWO Delete Bound kube-logging31/data-elastic-cluster31-2 do-block-storage 144m Filesystem
[root@master 31efk]# kubectl get pvc --all-namespaces -owide
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
kube-logging31 data-elastic-cluster31-0 Bound pvc-677221e5-ed6a-417b-ac69-6b90304afa4e 10Gi RWO do-block-storage 149m Filesystem
kube-logging31 data-elastic-cluster31-1 Bound pvc-124274e8-1977-4e46-895d-d6b9449682f7 10Gi RWO do-block-storage 145m Filesystem
kube-logging31 data-elastic-cluster31-2 Bound pvc-944a372c-1c0d-442d-998a-b62c5be7ca21 10Gi RWO do-block-storage 145m Filesystem