跳转至

第六节 使用Operator快速部署Elasticsearch集群

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

现在比较好的解决方案就是针对有状态应用开发对应的 Operator 应用,比如 prometheus-operatoretcd-operator 等等,关于 Operator 的开发,可以查看前面的一篇入门文章:Kubernetes 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 集群内,其功能绝不仅限于简化 KubernetesElasticsearchKibana 的部署工作这一项任务。ECK 专注于简化所有后期运行工作,例如:

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

ECK 不仅能自动完成所有运行和集群管理任务,还专注于简化在 Kubernetes 上使用 Elasticsearch 的完整体验。ECK的愿景是为 Kubernetes 上的 Elastic 产品和解决方案提供 SaaS 般的体验。

ECK 上启动的所有 Elasticsearch 集群都默认受到保护,这意味着在最初创建的那一刻便已启用加密并受到默认强密码的保护。

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

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

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

安装 ECK

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

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

$ kubectl apply -f https://download.elastic.co/downloads/eck/0.8.1/all-in-one.yaml
customresourcedefinition.apiextensions.k8s.io/apmservers.apm.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/clusterlicenses.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/elasticsearches.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/enterpriselicenses.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/remoteclusters.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/trustrelationships.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/users.elasticsearch.k8s.elastic.co created
customresourcedefinition.apiextensions.k8s.io/kibanas.kibana.k8s.elastic.co created
clusterrole.rbac.authorization.k8s.io/elastic-operator created
clusterrolebinding.rbac.authorization.k8s.io/elastic-operator created
namespace/elastic-system created
statefulset.apps/elastic-operator created
secret/webhook-server-secret created
serviceaccount/elastic-operator created

安装成功后,会自动创建一个 elastic-systemnamespace 以及一个 operatorPod

$ kubectl get ns
NAME             STATUS   AGE
elastic-system   Active   40s
$ kubectl get pods -n elastic-system
NAME                 READY   STATUS    RESTARTS   AGE
elastic-operator-0   1/1     Running   1          2m

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

$ kubectl get crd  | grep elastic
apmservers.apm.k8s.elastic.co                     2019-07-03T06:52:56Z
clusterlicenses.elasticsearch.k8s.elastic.co      2019-07-03T06:53:00Z
elasticsearches.elasticsearch.k8s.elastic.co      2019-07-03T06:53:00Z
enterpriselicenses.elasticsearch.k8s.elastic.co   2019-07-03T06:53:00Z
kibanas.kibana.k8s.elastic.co                     2019-07-03T06:53:00Z
remoteclusters.elasticsearch.k8s.elastic.co       2019-07-03T06:53:00Z
trustrelationships.elasticsearch.k8s.elastic.co   2019-07-03T06:53:00Z
users.elasticsearch.k8s.elastic.co                2019-07-03T06:53:00Z

然后我们可以利用 CRD 对象来创建一个非常简单的单个 Elasticsearch 集群:(elastic.yaml)

apiVersion: elasticsearch.k8s.elastic.co/v1alpha1
kind: Elasticsearch
metadata:
  name: elastic
  namespace: elastic-system
spec:
  version: 7.2.0
  nodes:
  - nodeCount: 1
    config:
      node.master: true
      node.data: true
      node.ingest: true

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

$ kubectl create -f elastic.yaml

但是执行上面的命令会出现超时的情况:Error from server (Timeout): error when creating "STDIN": Timeout: request did not complete within requested timeout 30s,创建不成功。这主要是因为 ECK 添加了一个 validation webhookAdmission,我们可以临时将这个对象删除:

# Hi, get backup firstly
$ kubectl get ValidatingWebhookConfiguration -o yaml > ValidatingWebhookConfiguration.yaml
# Delete ValdiationWebhook
$ kubectl delete ValidatingWebhookConfiguration validating-webhook-configuration

然后重新执行上面的创建命令即可成功。创建成功后需要等一小会儿待镜像拉取成功,然后就可以看到一个前缀为elasticPod 运行成功:

Image still pulling problem

$ kubectl get pods -n elastic-system
NAME                    READY   STATUS     RESTARTS   AGE
elastic-es-7rkw2xzdbw   0/1     Init:0/4   0          54s
elastic-operator-0      1/1     Running    1          5m
$ docker pull elasticsearch:7.2.0

然后重新执行上面的创建命令即可成功。创建成功后需要等一小会儿待镜像拉取成功,然后就可以看到一个前缀为elasticPod 运行成功:

$ get pods -n elastic-system
NAME                             READY   STATUS    RESTARTS   AGE
elastic-es-h4xns977f5            1/1     Running   0          3h10m
elastic-operator-0               1/1     Running   1          15h

同样可以查看自定义的Elasticsearch这个 CRD 资源对象:

$ kubectl get elasticsearch -n elastic-system
NAME      HEALTH   NODES   VERSION   PHASE         AGE
elastic   green    1       7.2.0     Operational   16h

我们可以看到集群的相关信息。

同样,也可以用 CRD 对象 Kibana 来部署 kibana 应用:(kibana.yaml)

apiVersion: kibana.k8s.elastic.co/v1alpha1
kind: Kibana
metadata:
  name: kibana
  namespace: elastic-system
spec:
  version: 7.2.0
  nodeCount: 1
  elasticsearchRef:
    name: elastic

注意属性spec.elasticsearchRef.name的值为上面我们创建的 Elasticsearch 对象的 name:elastic。直接添加这个资源对象即可:

docker pull docker.elastic.co/kibana/kibana:7.2.0

$ kubectl create -f kibana.yaml
$ kubectl get pods -n elastic-system
NAME                             READY   STATUS    RESTARTS   AGE
elastic-es-h4xns977f5            1/1     Running   0          3h15m
elastic-operator-0               1/1     Running   1          15h
kibana-kibana-79479c64bc-zpdr7   1/1     Running   0          162m
$ kubectl get svc -n elastic-system
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
elastic-es                ClusterIP   10.103.72.176    <none>        9200/TCP   22h
elastic-es-discovery      ClusterIP   None             <none>        9300/TCP   22h
elastic-webhook-service   ClusterIP   10.110.255.150   <none>        443/TCP    22h
kibana-kibana             ClusterIP   10.101.233.229   <none>        5601/TCP   6h21m
$ kubectl get svc -n elastic-system
NAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
elastic-es                ClusterIP   10.103.72.176    <none>        9200/TCP   22h
elastic-es-discovery      ClusterIP   None             <none>        9300/TCP   22h
elastic-webhook-service   ClusterIP   10.110.255.150   <none>        443/TCP    22h
kibana-kibana             ClusterIP   10.101.233.229   <none>        5601/TCP   6h25m

最后我们可以去访问 kibana 来验证我们的集群,比如我们可以再添加一个 Ingress 对象:(ingress.yaml)

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kibana
  namespace: elastic-system
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: kibana.jacob-test.com
    http:
      paths:
      - backend:
          serviceName: kibana-kibana
          servicePort: 5601
        path: /

创建上面的 Ingress 对象:

$ kubectl create -f ingress.yaml
ingress.extensions/kibana created

$ kubectl get ingress -n elastic-system
NAME     HOSTS                   ADDRESS   PORTS   AGE
kibana   kibana.jacob-test.com             80      7s

将上面的域名做好 DNS 解析或者 hosts 映射。

然后我们需要获取访问 kibana 的用户名和密码,用户名默认是elastic,可以通过如下方式获取访问密码:

$ PASSWORD=$(kubectl get secret elastic-elastic-user -n elastic-system -o=jsonpath='{.data.elastic}' | base64 --decode)
$ echo $PASSWORD
...这是访问密码..
vz8flb8z8m92dfk5z4j472pp

接下来在浏览器中打开上面的kibana.jacob-test.com地址,正常就会跳转到登录页面了:

Alt Image Text

使用上面的用户名和密码登录即可进入 kibana dashboard 页面:

Alt Image Text

更新集群

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

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

apiVersion: elasticsearch.k8s.elastic.co/v1alpha1
kind: Elasticsearch
metadata:
  name: elastic
  namespace: elastic-system
spec:
  version: 7.2.0
  nodes:
  - nodeCount: 2
    config:
      node.master: true
      node.data: true
      node.ingest: true

直接更新集群信息即可:

$ kubectl apply -f elastic.yaml
$ kubectl get pods -n elastic-system
NAME                             READY   STATUS    RESTARTS   AGE
elastic-es-7pz8pv5ztj            1/1     Running   0          7m56s
elastic-es-h4xns977f5            1/1     Running   0          3h36m
elastic-operator-0               1/1     Running   1          15h
kibana-kibana-79479c64bc-zpdr7   1/1     Running   0          3h3m
$ kubectl get elasticsearch -n elastic-system
NAME      HEALTH   NODES   VERSION   PHASE         AGE
elastic   green    2       7.2.0     Operational   3h37m

持久化

上面我们部署的集群默认是使用的emptyDir volume,我们知道emptyDirPod 的生命周期是一致的,Pod 重建后数据肯定就没有了,在生产环境中肯定是不行的。

我们可以在集群中使用 PV/PVC 来进行持久化,同样,在上面的 Elasticsearch 资源对象中添加存储相关数据:

apiVersion: elasticsearch.k8s.elastic.co/v1alpha1
kind: Elasticsearch
metadata:
  name: elastic
  namespace: elastic-system
spec:
  version: 7.2.0
  nodes:
  - nodeCount: 2
    config:
      node.master: true
      node.data: true
      node.ingest: true
    volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 10Gi
        #storageClassName: standard # 可以指定可用的storage class

为了能够获得磁盘的最佳性能,ECK 支持每个节点使用 local volume,关于在 ECK 中使用 local volume 的方法可以查看下面几篇资料:

关于定制 Elasticsearch 资源对象的一些方法,我们可以直接去查看 ECK 的 git 仓库中的介绍即可,当然我们也可以直接在集群中通过describe命令去获取Elastisearch CRD的资源声明:

$ kubectl describe crd elasticsearch