使用Elasticsearch Operator快速部署Elasticsearch集群

转载自:https://www.qikqiak.com/post/elastic-cloud-on-k8s/

随着 kubernetes 的快速发展,很多应用都在往 kubernetes 上面迁移,现阶段对于无状态应用的迁移是非常容易做到的,但是对于有状态应用的迁移还是有一定门槛的,主要是有状态应用的运行方式各有不同,比如 MySQL、MongoDB、Redis 这些应用运行的方式方法都不太相同,特别是对于线上环境需要高可用的集群模式的时候,则差别就更大了,这就导致了有状态应用向 Kubernetes 的迁移必然进度会很慢。现在比较好的解决方案就是针对有状态应用开发对应的 Operator 应用,比如 prometheus-operator、etcd-operator 等等

同样的,对于 Elasticsearch 应用,现在官方也推出了基于 Kubernetes Operator 的应用:Elastic Cloud on Kubernetes (ECK),用户可使用该产品在 Kubernetes 上配置、管理和运行 Elasticsearch 集群。

Elastic Cloud on Kubernetes

Elastic Cloud on Kubernetes(ECK)是一个 Elasticsearch Operator,但远不止于此。 ECK 使用 Kubernetes Operator 模式构建而成,需要安装在您的 Kubernetes 集群内,其功能绝不仅限于简化 Kubernetes 上 Elasticsearch 和 Kibana 的部署工作这一项任务。ECK 专注于简化所有后期运行工作,例如:

  • 管理和监测多个集群
  • 轻松升级至新的版本
  • 扩大或缩小集群容量
  • 更改集群配置
  • 动态调整本地存储的规模(包括 Elastic Local Volume(一款本地存储驱动器))
  • 备份

ECK 不仅能自动完成所有运行和集群管理任务,还专注于简化在 Kubernetes 上使用 Elasticsearch 的完整体验。ECK 的愿景是为 Kubernetes 上的 Elastic 产品和解决方案提供 SaaS 般的体验。 在 ECK 上启动的所有 Elasticsearch 集群都默认受到保护,这意味着在最初创建的那一刻便已启用加密并受到默认强密码的保护。

从 6.8 和 7.1 版本开始,Elasticsearch 核心安全功能(TLS 加密、基于角色的访问控制,以及文件和原生身份验证)会免费提供。

通过 ECK 部署的所有集群都包括强大的基础(免费)级功能,例如可实现密集存储的冻结索引、Kibana Spaces、Canvas、Elastic Maps,等等。您甚至可以使用 Elastic Logs 和 Elastic Infrastructure 应用监测 Kubernetes 日志和基础设施。您可以获得在 Kubernetes 上使用 Elastic Stack 完整功能的体验。

ECK 内构建了 Elastic Local Volume,这是一个适用于 Kubernetes 的集成式存储驱动器。ECK 中还融入了很多最佳实践,例如在缩小规模之前对节点进行 drain 操作,在扩大规模的时候对分片进行再平衡,等等。从确保在配置变动过程中不会丢失数据,到确保在规模调整过程中实现零中断。

安装 ECK

当然前提是你要有一个已经可运行的 kubernetes 集群(1.11版本以上),最好确保你的每个节点上至少有4GB内存可以使用,因为我们知道 Elasticsearch 是比较消耗资源的。

首先在集群中安装 ECK 对应的 Operator 资源对象:

kubectl create -f https://download.elastic.co/downloads/eck/2.2.0/crds.yaml

kubectl apply -f https://download.elastic.co/downloads/eck/2.2.0/operator.yaml

这个时候会安装上若干个 CRD 对象,当然这些 CRD 资源的控制器就在上面的 elastic-operator-0 这个 Pod 中:

# kubectl get crd  | grep elastic
agents.agent.k8s.elastic.co                                 2022-06-17T08:08:05Z
apmservers.apm.k8s.elastic.co                               2022-06-17T08:08:05Z
beats.beat.k8s.elastic.co                                   2022-06-17T08:08:05Z
elasticmapsservers.maps.k8s.elastic.co                      2022-06-17T08:08:05Z
elasticsearches.elasticsearch.k8s.elastic.co                2022-06-17T08:08:05Z
enterprisesearches.enterprisesearch.k8s.elastic.co          2022-06-17T08:08:05Z
kibanas.kibana.k8s.elastic.co                               2022-06-17T08:08:05Z

安装成功后,会自动创建一个 elastic-system 的 namespace 以及一个 operator 的 Pod:

# kubectl get pods -n elastic-system
NAME                 READY   STATUS    RESTARTS   AGE
elastic-operator-0   1/1     Running   0          26s

创建es 集群

# elastic.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
  namespace: elastic-system
spec:
  version: 8.2.3
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false

声明了要创建一个 8.2.3 版本的单节点的 Elasticsearch 资源对象:

# kubectl apply -f elastic.yaml 
elasticsearch.elasticsearch.k8s.elastic.co/quickstart created

创建成功后需要等一小会儿待镜像拉取成功,然后就可以看到一个前缀为quickstart的 Pod 运行成功:

# kubectl get pods -n elastic-system
NAME                      READY   STATUS     RESTARTS   AGE
elastic-operator-0        1/1     Running    0          6m53s
quickstart-es-default-0   0/1     Init:0/2   0          60s

监视群集运行状况和创建进度:

创建集群时,没有HEALTH状态,PHASE为空。过了一会儿,PHASE变成Ready,HEALTH变成green。HEALTH状态来自Elasticsearch 的集群健康API 。

# kubectl get elasticsearch -n elastic-system
NAME         HEALTH    NODES   VERSION   PHASE             AGE
quickstart   unknown           8.2.3     ApplyingChanges   7m4s

# kubectl get elasticsearch -n elastic-system
NAME         HEALTH   NODES   VERSION   PHASE   AGE
quickstart   green    1       8.2.3     Ready   16m

请求Elasticsearch访问

# kubectl get service quickstart-es-http -n elastic-system
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
quickstart-es-http   ClusterIP   172.16.216.137   <none>        9200/TCP   8m23s

默认用户名是elastic

查看密码:


PASSWORD=$(kubectl get secret quickstart-es-elastic-user -o go-template='{{.data.elastic | base64decode}}' -n elastic-system)

# kubectl get secret quickstart-es-elastic-user -o go-template='{{.data.elastic | base64decode}}' -n elastic-system
73gzg8XY91rLf34Us8O0UcN9

# kubectl get secret quickstart-es-elastic-user -o=jsonpath='{.data.elastic}' -n elastic-system | base64 --decode; echo 
73gzg8XY91rLf34Us8O0UcN9

请求

# pod内部请求
curl -u "elastic:$PASSWORD" -k "https://quickstart-es-http:9200"

# 本机
kubectl port-forward service/quickstart-es-http 9200 -n elastic-system

# 新开一个窗口
PASSWORD=$(kubectl get secret quickstart-es-elastic-user -o go-template='{{.data.elastic | base64decode}}' -n elastic-system)

curl -u "elastic:$PASSWORD" -k "https://127.0.0.1:9200"                                                                       
{
  "name" : "quickstart-es-default-0",
  "cluster_name" : "quickstart",
  "cluster_uuid" : "XbHPlyg7Q8-G-zGMI4Wf5w",
  "version" : {
    "number" : "8.2.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "9905bfb62a3f0b044948376b4f607f70a8a151b4",
    "build_date" : "2022-06-08T22:21:36.455508792Z",
    "build_snapshot" : false,
    "lucene_version" : "9.1.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

安装kibana

# kibana.yaml

apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
  namespace: elastic-system
spec:
  version: 8.2.3
  count: 1
  elasticsearchRef:
    name: quickstart # 跟文件elastic.yaml中的保持一致
# kubectl create -f kibana.yaml
kibana.kibana.k8s.elastic.co/quickstart created

注意属性spec.elasticsearchRef.name的值为上面我们创建的 Elasticsearch 对象的 name:quickstart


# kubectl get pods -n elastic-system
NAME                             READY   STATUS    RESTARTS   AGE
elastic-operator-0               1/1     Running   0          28m
quickstart-es-default-0          1/1     Running   0          22m
quickstart-kb-7cdb446796-wvbck   1/1     Running   0          2m42s

# kubectl get pod --selector='kibana.k8s.elastic.co/name=quickstart' -n elastic-system
NAME                             READY   STATUS    RESTARTS   AGE
quickstart-kb-7cdb446796-wvbck   1/1     Running   0          3m6s
# kubectl get svc -n elastic-system
NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
elastic-webhook-server        ClusterIP   172.16.118.8     <none>        443/TCP    29m
quickstart-es-default         ClusterIP   None             <none>        9200/TCP   23m
quickstart-es-http            ClusterIP   172.16.216.137   <none>        9200/TCP   23m
quickstart-es-internal-http   ClusterIP   172.16.99.247    <none>        9200/TCP   23m
quickstart-es-transport       ClusterIP   None             <none>        9300/TCP   23m
quickstart-kb-http            ClusterIP   172.16.4.134     <none>        5601/TCP   3m19s

# kubectl get service quickstart-kb-http -n elastic-system
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
quickstart-kb-http   ClusterIP   172.16.4.134   <none>        5601/TCP   3m59s
kubectl port-forward service/quickstart-kb-http 5601

问题:给kibana添加ingress规则,配置域名进行访问,报错:502 Bad Gateway,如下是有关配置

添加一个 Ingress 对象:

# kibana_ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kibana
  namespace: elastic-system
spec:
  ingressClassName: nginx
  rules:
    - host: www.fff.com # 自定义域名,本机hosts配置解析
      http:
        paths:
          - backend:
              service:
                name: quickstart-kb-http
                port:
                  number: 5601
            path: /
            pathType: Prefix
# kubectl apply -f kibana_ingress.yaml
ingress.networking.k8s.io/kibana created

# kubectl get ingress -n elastic-system
NAME     CLASS   HOSTS         ADDRESS   PORTS   AGE
kibana   nginx   www.fff.com             80      33s

不论是使用http://www.kkk.com/还是https://www.kkk.com/访问,最终结果都是报错:502 Bad Gateway

解决办法:
修改 kibana.yaml文件,增加配置,关闭https,使用http对外提供服务,否则需要配置证书

也就是说,访问kibana的web页面需要配置https,但是k8s中的ingress配置的是http,因此访问才会报错的,关闭https,使用http,k8s中的ingress配置的也是http,这样一来就可以实现效果了

官方地址:https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-tls-certificates.html#k8s-setting-up-your-own-certificate

# 修改后的kibana.yaml文件内容

apiVersion: kibana.k8s.elastic.co/v1
kind: Kibana
metadata:
  name: quickstart
  namespace: elastic-system
spec:
  version: 8.2.3
  count: 1
  elasticsearchRef:
    name: quickstart
  http:
    tls:
      selfSignedCertificate:
        # 关闭https,使用http对外提供服务,否则需要配置证书【请参考官方文档】
        disabled: true

更新集群

上面我们部署的 Elasticsearch 集群是一个单节点的,我们可以直接修改 Elasticsearch 的资源清单文件来修改集群配置。ECK 会确保我们在现有集群上进行修改不会中断应用。

比如,我们将集群升级到2个节点,只需要设置spec.nodes[0].nodeCount=2即可:

# elastic.yaml
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
  namespace: elastic-system
spec:
  version: 8.2.3
  nodeSets:
  - name: default
    count: 2
    config:
      node.store.allow_mmap: false
# 直接更新集群信息
kubectl apply -f elastic.yaml

持久化

发现一个问题,文件 elastic.yaml 中没有关于存储的配置,但是在执行后,若是k8s集群中存储类设置的有默认的,比如是:rook-ceph-block,则会自动创建一个容量大小为1G的PVC使用

如默认存储类是rook-cephfs,则会在存储类 rook-cephfs 中创建



假如把PVC给扩容到10Gi,通过修改pvc使用的yaml文件来进行扩容,可以顺利实现


在这种情况下,把集群单节点扩展到集群三节点,通过修改elastic.yaml文件实现后,观察如下:

原先单节点日志会看到有俩节点加入到集群中来

会发现,原先节点仍是master节点,新增加的俩节点是node节点,也是默认使用的存储类创建的1G的PVC使用的,分别扩容到11G,12G:

官方有关文档地址:https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-persistent-storage.html

因此可以通过修改存储卷声明模板来对这些进行有关修改,而不是部署后再进行修改

注意:卷声明的名称必须始终为elasticsearch-data. 如果您选择了不同的名称,则必须自己设置与data.path匹配的相应卷挂载(/usr/share/elasticsearch/data默认情况下)。

存储类回收策略,默认是:DeleteOnScaledownAndClusterDeletion,这意味着所有的 PersistentVolumeClaims 都会和 Elasticsearch 集群一起被删除。
但是,DeleteOnScaledownOnly在删除 Elasticsearch 集群时保留 PersistentVolumeClaims。如果您使用与以前相同的名称和节点集重新创建已删除的集群,则新集群将采用现有的 PersistentVolumeClaims。

修改elastic.yaml文件内容如下:

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
  namespace: elastic-system
spec:
  version: 8.2.3
  nodeSets:
  - name: default
    count: 3
    config:
      node.store.allow_mmap: false
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data # Do not change this name unless you set up a volume mount for the data path.
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 20Gi
        storageClassName: rook-ceph-block

更新报错:

Error from server (Elasticsearch.elasticsearch.k8s.elastic.co "quickstart" is invalid: spec.nodeSet[0].volumeClaimTemplates: Invalid value: []v1.PersistentVolumeClaim{v1.PersistentVolumeClaim{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ObjectMeta:v1.ObjectMeta{Name:"elasticsearch-data", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ClusterName:"", ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:v1.PersistentVolumeClaimSpec{AccessModes:[]v1.PersistentVolumeAccessMode{"ReadWriteOnce"}, Selector:(*v1.LabelSelector)(nil), Resources:v1.ResourceRequirements{Limits:v1.ResourceList(nil), Requests:v1.ResourceList{"storage":resource.Quantity{i:resource.int64Amount{value:21474836480, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"20Gi", Format:"BinarySI"}}}, VolumeName:"", StorageClassName:(*string)(0xc000992910), VolumeMode:(*v1.PersistentVolumeMode)(nil), DataSource:(*v1.TypedLocalObjectReference)(nil), DataSourceRef:(*v1.TypedLocalObjectReference)(nil)}, Status:v1.PersistentVolumeClaimStatus{Phase:"", AccessModes:[]v1.PersistentVolumeAccessMode(nil), Capacity:v1.ResourceList(nil), Conditions:[]v1.PersistentVolumeClaimCondition(nil), AllocatedResources:v1.ResourceList(nil), ResizeStatus:(*v1.PersistentVolumeClaimResizeStatus)(nil)}}}: volume claim templates can only have their storage requests increased, if the storage class allows volume expansion. Any other change is forbidden): error when applying patch:
{"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"elasticsearch.k8s.elastic.co/v1\",\"kind\":\"Elasticsearch\",\"metadata\":{\"annotations\":{},\"name\":\"quickstart\",\"namespace\":\"elastic-system\"},\"spec\":{\"nodeSets\":[{\"config\":{\"node.store.allow_mmap\":false},\"count\":3,\"name\":\"default\",\"volumeClaimTemplates\":[{\"metadata\":{\"name\":\"elasticsearch-data\"},\"spec\":{\"accessModes\":[\"ReadWriteOnce\"],\"resources\":{\"requests\":{\"storage\":\"20Gi\"}},\"storageClassName\":\"rook-ceph-block\"}}]}],\"version\":\"8.2.3\",\"volumeClaimDeletePolicy\":\"DeleteOnScaledownAndClusterDeletion\"}}\n"}},"spec":{"nodeSets":[{"config":{"node.store.allow_mmap":false},"count":3,"name":"default","volumeClaimTemplates":[{"metadata":{"name":"elasticsearch-data"},"spec":{"accessModes":["ReadWriteOnce"],"resources":{"requests":{"storage":"20Gi"}},"storageClassName":"rook-ceph-block"}}]}],"volumeClaimDeletePolicy":"DeleteOnScaledownAndClusterDeletion"}}
to:
Resource: "elasticsearch.k8s.elastic.co/v1, Resource=elasticsearches", GroupVersionKind: "elasticsearch.k8s.elastic.co/v1, Kind=Elasticsearch"
Name: "quickstart", Namespace: "elastic-system"
for: "elastic.yaml": admission webhook "elastic-es-validation-v1.k8s.elastic.co" denied the request: Elasticsearch.elasticsearch.k8s.elastic.co "quickstart" is invalid: spec.nodeSet[0].volumeClaimTemplates: Invalid value: []v1.PersistentVolumeClaim{v1.PersistentVolumeClaim{TypeMeta:v1.TypeMeta{Kind:"", APIVersion:""}, ObjectMeta:v1.ObjectMeta{Name:"elasticsearch-data", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string(nil), Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ClusterName:"", ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:v1.PersistentVolumeClaimSpec{AccessModes:[]v1.PersistentVolumeAccessMode{"ReadWriteOnce"}, Selector:(*v1.LabelSelector)(nil), Resources:v1.ResourceRequirements{Limits:v1.ResourceList(nil), Requests:v1.ResourceList{"storage":resource.Quantity{i:resource.int64Amount{value:21474836480, scale:0}, d:resource.infDecAmount{Dec:(*inf.Dec)(nil)}, s:"20Gi", Format:"BinarySI"}}}, VolumeName:"", StorageClassName:(*string)(0xc000992910), VolumeMode:(*v1.PersistentVolumeMode)(nil), DataSource:(*v1.TypedLocalObjectReference)(nil), DataSourceRef:(*v1.TypedLocalObjectReference)(nil)}, Status:v1.PersistentVolumeClaimStatus{Phase:"", AccessModes:[]v1.PersistentVolumeAccessMode(nil), Capacity:v1.ResourceList(nil), Conditions:[]v1.PersistentVolumeClaimCondition(nil), AllocatedResources:v1.ResourceList(nil), ResizeStatus:(*v1.PersistentVolumeClaimResizeStatus)(nil)}}}: volume claim templates can only have their storage requests increased, if the storage class allows volume expansion. Any other change is forbidden
volume claim templates can only have their storage requests increased, if the storage class allows volume expansion. Any other change is forbidden
只有在存储类允许卷扩展的情况下,卷声明模板才能增加其存储请求。禁止任何其他更改
如果存储类允许卷扩展,则可以在volumeClaimTemplates中增加存储请求大小。ECK将相应地更新现有的PersistentVolumeClaims,并自动重新创建状态集。
如果卷驱动程序支持ExpandInUsePersistentVolumes,则可以在线调整文件系统的大小,而无需重新启动Elasticsearch进程或重新创建POD。 # 这一步就是刚才的操作,修改pvc使用的yaml文件,把容量从1G扩容到10G,并且无需重新启动Elasticsearch进程或重新创建POD
如果卷驱动程序不支持ExpandInUsePersistentVolumes,则必须在调整大小后手动删除POD,以便使用扩展的文件系统自动重新创建。

volumeClaimTemplates中禁止进行任何其他更改,例如更改存储类或减小卷大小。要进行这些更改,可以创建具有不同设置的新节点集,并删除现有节点集。
实际上,这相当于重命名现有节点集,同时在一次更新中修改其声明设置。在删除已删除节点集的POD之前,ECK确保数据已迁移到其他节点。

简单来说,对已经存在的集群,只能在线调整PVC的大小,不支持其他操作,若想通过存储卷声明模板来进行有关配置,最好是创建新的集群,使用原有的PVC,从而实现目的

最最保险的办法,就是在创建es集群之前就修改elastic.yaml文件内容,增加上存储卷声明的有关配置,进而创建集群,而不是集群创建好了再修改操作

apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: quickstart
  namespace: elastic-system
spec:
  version: 8.2.3
  volumeClaimDeletePolicy: DeleteOnScaledownAndClusterDeletion # 一起删除
  nodeSets:
  - name: default
    count: 1
    config:
      node.store.allow_mmap: false
    volumeClaimTemplates:
    - metadata:
        name: elasticsearch-data # Do not change this name unless you set up a volume mount for the data path.
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 20Gi
        storageClassName: rook-ceph-block

关于这些默认使用文件的其他配置可以查看官网介绍

https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-node-configuration.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html
https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-kibana-es.html

posted @ 2022-06-17 17:52  哈喽哈喽111111  阅读(1743)  评论(1编辑  收藏  举报