Running Solr on Kubernetes

原文地址:https://lucidworks.com/post/running-solr-on-kubernetes-part-1/

包括自己在試驗過程的一些測試、解釋和注意事項

我們將為搜索工程師介紹在Kubernetes(k8s)上運行Solr的基礎知識。 具體來說,我們涵蓋以下主題:

  1. Getting started with Google Kubernetes Engine (GKE)。
    (GKR入門)
  2. Helm charts.
  3. StatefulSets, initContainers, ConfigMaps, and Persistent Volumes。
  4. Liveness and Readiness Probes
  5. Cross pod synchronization。
    (跨pod同步)
  6. Load-balancing services and pod selectors。
    (負載平衡服務和Pod選擇器)
  7. Upgrading Solr with zero-downtime canary style deployments。
    (絲雀零停機的Solr升級)
  8. Performance and Load Testing。
    (性能和負載測試)
  9. Monitoring Solr metrics with Prometheus and Grafana。
    (使用Prometheus and Grafana進行監控Solr metrics)
  10. Encrypting traffic between Solr instances using TLS。
    (使用TLS加密Solr實例之間的流量)

在下一篇文章中,我們將深入探討有關自動縮放,性能和負載測試以及其他高級操作的問題。 在深入研究細節之前,讓我們探討為什么可能要在Kubernetes上運行Solr的問題。 也就是說,k8s為Solr運算符提供了三個主要優點:

  1. 幫助實施最佳實踐和成熟的分布式系統設計模式,
  2. 降低了諸如Solr之類的復雜系統的擁有成本,并且
  3. 在用戶希望運行基于微服務的應用程序的相同操作環境中運行Solr。

就最佳實踐和設計模式而言,Kubernetes提供了一種通用語言來聲明如何在生產環境中安裝,配置和維護分布式應用程序。 運營工程師學習如何管理Solr使用Kubernetes native resources like services, StatefulSets, and volume claims,而不必擔心內部實現細節。 借助Kubernetes,運維團隊可以使用標準工具專注與集群調整,監控,性能測試,日志,報警等。

關于降低擁有成本,Kubernetes使一般運營工程師可以運行Solr,而我們的客戶無需投資培訓或雇用專家。 這對于Solr尤為重要,因為在Solr中,操作大型Solr集群通常需要非常專業的技能。 在當今的就業市場上,讓Solr專家離開以獲得更好的機會是一個真正的風險。 當然,k8s并不能消除大規模運行Solr的所有復雜性,但是要走很長一段路。

Kubernetes專為管理基于云的微服務的應用程序而構建。 Solr能夠在不到一秒的時間內搜索大量數據集,并通過流表達式提供低延遲的專業分析,因此對于數據密集型應用程序來說,Solr是一個有吸引力的后端。

但是,在幾秒鐘內將微服務部署到Kubernetes不會產生任何效果,因為隨后必須為k8之外的Solr進行復雜的部署過程。 理想情況下,運維團隊只需將Solr以及與其相關的微服務應用程序一起部署即可。 Lucidworks提供的Solr helm chart 使這成為現實。

既然您已經知道了為什么在Kubernetes上運行Solr是個好主意,那么讓我們振作起來,在云中啟動Solr集群。

Prerequisites 先決條件

在本節中,我們將介紹如何使用Kubernetes進行設置以及如何在GKE中啟動您的第一個集群。 如果您已經熟悉kubectl,helm,gcloud和GKE,則可以安全地跳到下一部分。

Kubernetes

在整個文檔中,我們展示了如何部署到基于Google Kubernetes Engine(GKE)的集群。 建議使用GKE選項,因為您可以快速部署多個節點,GKE是一個學習k8s概念的有趣環境,Google會給您$ 300的免費贈金以開始使用。 在繼續之前,請按照以下說明設置Google Cloud訪問和SDK: https : //cloud.google.com/sdk/docs/quickstarts 。 您也可以在minikube上本地運行一個單節點Solr集群,但是這里不做介紹。

Kubectl

kubectl是用于與Kubernetes集群進行交互的命令行工具。 它應該已經與minikube或gcloud SDK一起安裝了。 要驗證kubectl是否可用,請執行:kubectl version。 如果尚未安裝,只需執行以下操作:

gcloud components install kubectl

最終,您將厭倦了鍵入“ kubectl”,因此現在為將來的自己提供幫助,并在您的shell初始化腳本中添加以下別名(例如?/ .bash_profile):

alias k=kubectl

Launch GKE Cluster

啟動一個kubernetes實例。
可以使用docker desktop:
https://github.com/AliyunContainerService/k8s-for-docker-desktop

Helm

Helm是k8s生態系統中流行的用于部署應用程序的工具。 我們在下面使用Helm來部署Solr,因此請按照此處的說明進行Helm的設置: https : //github.com/helm/helm 。 安裝Tiller是使用Helm的最常見方法,但并不需要按照本文進行操作。 在Mac上簡短地嘗試:

安裝helm v2版本
brew install kubernetes-helm@2

添加環境變量:
echo 'export PATH="/usr/local/opt/helm@2/bin:$PATH"' >> ~/.bash_profile

添加tiller到 [k8s] service account
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

安裝tiller
helm init --service-account tiller

helm version

Deploy Solr to GKE

首先,將帶有Zookeeper的3節點Solr集群部署到GKE。克隆倉庫或從以下地址下載zip文件: https : //github.com/lucidworks/solr-helm-chart 。 我們已將Helm圖表提交到https://github.com/helm/charts,但仍在等待批準。

Helm的一個不錯的功能是chart可以動態鏈接到其他charts。 例如Solr chart依賴于Zookeeper chart。 讓我們通過以下操作將Zookeeper chart拖入Solr chat:

# 先移除原先的倉庫
helm repo remove stable

# 添加新的倉庫地址
helm repo add incubator https://storage.googleapis.com/kubernetes-charts-incubator

# 添加新的倉庫地址(阿里云)
helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts

# 更新charts列表
helm repo update

# 查詢倉庫列表
helm search incubator

cd solr-helm-chart/solr

# 下載requirements.yaml文件中定義的其他Chart,這些Chart會存放在charts目錄
helm dependency update

在部署之前,請花一點時間查看values.yaml中定義的配置變量。 該文件允許您為Solr部署自定義最常見的變量,例如資源分配,傳遞給Solr的JVM args和Solr版本(當前為7.6.0)。

對于生產來說,通常向在k8s中運行的Helm Tiller服務提交helm charts,但是對于本練習讓我們跳過Tiller并使用helm template命令從Solr和Zookeeper helm charts中呈現Kubernetes清單。 讓我們還將Solr版本更改為7.5.0,以便稍后在練習中可以升級到7.6.0:

# 生成solr.yaml模板文件
helm template . --name solr > solr.yaml

helm template . --set image.tag=7.5.0 --name solr > solr.yaml

現在,使用以下命令將Solr清單(solr.yaml)部署到Kubernetes:

kubectl apply -f solr.yaml

在Zookeeper和Solr初始化時要耐心等待。Kubernetes可能需要從Docker Hub提取Docker映像以及設置持久卷。 此外,在Pod初始化時,您也不必擔心在GCloud控制臺UI中看到的任何警告。 根據我們的經驗,在配置Pod時,集群工作負載UI的警告有點過于激進,可能會給人錯誤的感覺。 如果首次執行此操作后3到4分鐘內Solr和Zookeeper并沒有全部運行,則可以開始故障排除。

查看pods狀態,:

kubectl get pods

當它們準備就緒時,您將看到類似以下的輸出:

NAME               READY     STATUS    RESTARTS   AGE
solr-0             1/1       Running   0          38m
solr-1             1/1       Running   0          35m
solr-2             1/1       Running   0          34m
solr-zookeeper-0   1/1       Running   0          38m
solr-zookeeper-1   1/1       Running   0          37m
solr-zookeeper-2   1/1       Running   0          36m

如果Pod無法進入“Running”狀態或上線速度較慢,請使用describe命令查看Pod的特定活動,例如“ kubectl describe pod solr-0”。describe命令輸出包括Kubernetes啟動Pod所發生的事件。花一點時間查看為solr-0 pod報告的事件。

看起來一切正常,那么現在呢? 大多數將Solr用作后端的應用程序都不會將其公開給互聯網,而是使用無狀態微服務搜索應用程序(例如Lucidworks Fusion)作為前端。因此,我們使用以下kubectl port-forward solr-0 28983:8983將本地端口轉發到集群: kubectl port-forward solr-0 28983:8983

Now, point your browser to: http://localhost:28983/solr/#/~cloud?view=nodes

You should see something like:


avatar

創建一個集合:

curl -v "http://localhost:28983/solr/admin/collections?action=CREATE&name=perf3x1&numShards=3&replicationFactor=1&maxShardsPerNode=1&collection.configName=_default"

增加一些測試數據:

wget https://raw.githubusercontent.com/apache/lucene-solr/master/solr/example/exampledocs/books.json
curl "http://localhost:28983/solr/perf3x1/update/json/docs?commit=true" -H "Content-Type: application/json" --data-binary @books.json

此時,您將在Kubernetes中運行一個3節點的Solr集群。現在,我們將詳細介紹部署的工作方式,并介紹一些基本操作,例如在Solr實例之間啟用TLS。

Kubernetes Nuts & Bolts

在本節中,我們介紹了Solr部署的一些有趣方面。 為了節省時間,我們將不介紹Zookeeper,而是為您提供有關Zookeeper如何在Kubernetes中工作的以下指南: https://kubernetes.io/docs/tutorials/stateful-application/zookeeper/

而且,這里沒有涵蓋許多重要的Kubernetes概念。 有關k8s概念的更深入介紹,請參見:https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/

Pod

Pod是一組共享網絡和存儲的一個或多個容器(通常是Docker)。簡單的說,可以將pod視為在安裝了特定應用程序的邏輯主機上的一組相關的進程。Pod中的容器共享相同的IP地址和端口空間,因此它們可以通過localhost進行通信,但不能綁定到相同的端口。

由于k8s是一個容器編排框架,您可能想知道為什么他們發明了一個新術語而不是僅僅使用“容器”? 事實證明,盡管許多部署在Pod中只有一個容器,而我們的Solr部署就是這種情況,但部署具有多個容器的Pod并不少見。

一個很好的例子是Istio部署的sidecar Envoy代理。 具有多個相關容器的pod的經典示例是在同一pod中運行Apache httpd和memcached。 互聯網上有很多關于Pod的豐富資源,因此讓我們繼續研究更有趣的概念,并根據需要介紹Solr Pod的重要方面。

StatefulSet啟動solr

如果您是Kubernetes的新手,那么您需要了解的第一件事是Pod在集群中移動,您對此沒有太多控制權!實際上,您不必在乎Pod是否在集群中移動,因為該過程對于Kubernetes的設計至關重要。

Kubernetes執行的主要任務之一是平衡群集資源的利用率。 作為此過程的一部分,k8可能會決定將Pod移動到另一個節點。 或者,一個節點可能由于各種原因而發生故障,而k8則需要替換集群中另一個運行正常的節點上的那些發生故障的Pod。

因此,請稍等一會,如果k8將Solr pod移至另一個節點會發生什么情況。 如果Solr使用的磁盤沒有附帶,則在新節點上初始化Solr時,它將沒有任何可用的cores(Lucene索引),并且必須從磁盤中的另一個副本執行可能昂貴的快照復制。 又由于該信息也存儲在磁盤上,它將如何知道需要復制哪些cores? 對于使用一個replication因子的集合,情況將更加糟糕,因為沒有其他副本可以與之同步。

這個問題并非Solr獨有。 值得慶幸的是,Kubernetes為Solr等系統提供了一種出色的解決方案,該系統需要在磁盤上保持狀態并在Pod移動(或崩潰并重新啟動)時恢復狀態,即StatefulSets。

我們可以花整個blog來研究StatefulSet的詳細信息,但是從https://cloud.google.com/kubernetes-engine/docs/concepts/statefulset上,已經有大量資源可以做到這一點。

我們確實想消除一個誤解,即在討論在Kubernetes上運行Solr時聽到過的喃喃自語,即k8s不適合有狀態應用程序。 的確,k8s與運行有狀態應用程序的歷史混雜在一起,但這是個老新聞。 StatefulSet是k8中的一流功能,并且有許多成功的有狀態應用程序的示例。 在helm github站點上搜索帶StatefulSet的charts有110個: https://github.com/helm/charts/search?l=YAML&q=StatefulSet.

現在,讓我們來看一個StatefulSet的運行情況。如果列出了pod( kubectl get pods ),您將看到以下輸出:

NAME                             READY   STATUS    RESTARTS   AGE
solr-0                           1/1     Running   0          120m
solr-1                           1/1     Running   0          113m
solr-2                           1/1     Running   0          112m
solr-exporter-58dbb665db-46wfx   1/1     Running   0          120m
solr-zookeeper-0                 1/1     Running   0          120m
solr-zookeeper-1                 1/1     Running   0          120m
solr-zookeeper-2                 1/1     Running   0          119m

這些是StatefulSet中名為“solr”的pods。 注意,每個都獲得一個穩定的hostname,其主機索引以0開頭; 如果Pod銷毀,它將返回相同的主機名但具有不同的IP地址。 盡管對于Solr而言并不重要,但是由于它使用Zookeeper來協調集群活動,因此集合中的副本將以升序初始化,并以降序刪除。

所以如果需要修改pods數量,則修改values.yaml定義的變量,然后進行一次發布即可。

# 修改values.yaml文件:
replicaCount: 5

# 重新發布:
> helm template . --name solr > solr.yaml
> kubectl apply -f solr.yaml

poddisruptionbudget.policy/solr-zookeeper configured
service/solr-zookeeper-headless configured
service/solr-zookeeper configured
statefulset.apps/solr-zookeeper configured
statefulset.apps/solr configured
configmap/solr-config-map configured
poddisruptionbudget.policy/solr configured
service/solr-exporter configured
deployment.apps/solr-exporter configured
service/solr-headless configured
service/solr-svc configured

# 查看節點:
> kubectl get pods
NAME                             READY   STATUS     RESTARTS   AGE
solr-0                           1/1     Running    0          21m
solr-1                           1/1     Running    0          19m
solr-2                           1/1     Running    0          18m
solr-3                           1/1     Running    0          34s
solr-4                           0/1     Init:1/2   0          9s

添加副本:


avatar

Statefulset介紹

StatefulSet 是Kubernetes中的一種控制器,他解決的什么問題呢?我們知道Deployment是對應用做了一個簡化設置,Deployment認為一個應用的所有的pod都是一樣的,他們之間沒有順序,也無所謂在那臺宿主機上。需要擴容的時候就可以通過pod模板加入一個,需要縮容的時候就可以任意殺掉一個。但是實際的場景中,并不是所有的應用都能做到沒有順序等這種狀態,尤其是分布式應用,他們各個實例之間往往會有對應的關系,例如:主從、主備。還有數據存儲類應用,它的多個實例,往往會在本地磁盤存一份數據,而這些實例一旦被殺掉,即使從建起來,實例與數據之間關系也會丟失,而這些實例有不對等的關系,實例與外部存儲有依賴的關系的應用,被稱作“有狀態應用”。StatefulSet與Deployment相比,相同于他們管理相同容器規范的Pod,不同的時候,StatefulSet為pod創建一個持久的標識符,他可以在任何編排的時候得到相同的標識符。

StatefulSet由以下幾個部分組成:

  • Headless Service(無頭服務)用于為Pod資源標識符生成可解析的DNS記錄。
  • volumeClaimTemplates (存儲卷申請模板)基于靜態或動態PV供給方式為Pod資源提供專有的固定存儲。
  • StatefulSet,用于管控Pod資源。

kubectl explain sts.spec 主要字段解釋:

  1. replicas 副本數
  2. selector 那個pod是由自己管理的
  3. serviceName 必須關聯到一個無頭服務商
  4. template 定義pod模板(其中定義關聯那個存儲卷)
  5. volumeClaimTemplates 生成PVC

Statefulset優點

  1. 穩定的持久化存儲,即Pod重新調度后還是能訪問到相同的持久化數據,基于PVC來實現
  2. 穩定的網絡標志,即Pod重新調度后其PodName和HostName不變,基于Headless Service(即沒有Cluster IP的Service)來實現
  3. 有序部署,有序擴展,即Pod是有順序的,在部署或者擴展的時候要依據定義的順序依次依次進行(即從0到N-1,在下一個Pod運行之前所有之前的Pod必須都是Running和Ready狀態),基于init containers來實現
  4. 有序、平滑的收縮、刪除 既Pod是有順序的,在收縮或者刪除的時候要依據定義的順序依次進行(既從N-1到0,既倒序)。
  5. 有序的滾動更新,或金絲雀發布。

Persistent Volumes

為了證明StatefulSet中的副本返回了相同的hostname和附加的存儲,我們需要殺死Pod。 在開始殺死集群中的Pod之前,讓我們介紹一下Solr StatefulSets的重要方面,即PersistentVolumes。 如果查看Solr helm chart,您會注意到StatefulSet具有以下volumeMount:

volumeMounts:
  - name: solr-pvc
    mountPath: /opt/solr/server/home

讓我們登錄solr-0,看看它是什么:

kubectl exec -it solr-0 --container solr -- /bin/bash
solr@solr-1:/opt/solr-8.4.0$ lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0  59.6G  0 disk
└─sda1   8:1    0  59.6G  0 part /etc/hosts
sr0     11:0    1 470.7M  0 rom
sr1     11:1    1   148K  0 rom
sr2     11:2    1 824.6M  0 rom

這表明我們在/opt/solr/server/home安裝了20G磁盤。那是怎么發生的? 為了使永久卷附加到集中的每個副本,您需要一個卷聲明模板,該模板設置組標識(對于Solr,gid = 8983和所需的大小(20 GB):

statefulset.yaml生成到solr.yaml文件

volumeClaimTemplates:
  - metadata:
      name: solr-pvc
      annotations:
        pv.beta.kubernetes.io/gid: "8983"
    spec:
      accessModes:
        - ReadWriteOnce
        
      resources:
        requests:
          storage: 20Gi

顯然,真正的Solr部署需要更多磁盤空間,可以通過更改values.yaml文件中的volumeClaimTemplates.storageSize參數來增加磁盤空間。 在后臺,GKE從Google計算引擎分配磁盤。 您可以使用UI從UI獲取有關持久卷附加的存儲的詳細信息,如下所示:

avatar

或者通過命令:

kubectl describe PersistentVolumeClaim solr-pvc-solr-0

得到的結果:

Name:          solr-pvc-solr-0
Namespace:     default
StorageClass:  hostpath
Status:        Bound
Volume:        pvc-a0b488d5-90e6-4ad2-918c-d36f0aa2ee9a
Labels:        app=solr
               component=server
               release=solr
Annotations:   control-plane.alpha.kubernetes.io/leader:
                 {"holderIdentity":"32e0bba2-4724-11ea-bfa0-3c15c2dd97b4","leaseDurationSeconds":15,"acquireTime":"2020-02-04T13:28:09Z","renewTime":"2020-...
               pv.beta.kubernetes.io/gid: 8983
               pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
               volume.beta.kubernetes.io/storage-provisioner: docker.io/hostpath
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      20Gi
Access Modes:  RWO
VolumeMode:    Filesystem
Mounted By:    solr-0
Events:        <none>

Using an initContainer to Bootstrap Solr Home

如果查看/opt/solr/server/home目錄,則會看到solr.xml文件。這里發生了一些有趣的事情。 首先,StatefulSet的pod規范,使用環境變量將以下內容傳遞給Solr:

- name: "SOLR_HOME"
  value: "/opt/solr/server/home"

Solr 7.x要求SOLR_HOME目錄包含solr.xml文件。當k8s掛載solr-pvc卷時,它最初是一個空目錄。 因此,我們利用另一個有用的Kubernetes工具initContainer將solr.xml引導到我們的持久卷目錄中。

statefulset.yaml生成到solr.yaml文件

initContainers:
  - name: check-zk
    image: busybox:latest
    command:
      - 'sh'
      - '-c'
      - |
        COUNTER=0;
        while [  $COUNTER -lt 120 ]; do
          for i in "solr-zookeeper-0.solr-zookeeper-headless" "solr-zookeeper-1.solr-zookeeper-headless" "solr-zookeeper-2.solr-zookeeper-headless" ;
            do mode=$(echo srvr | nc $i 2181 | grep "Mode");
              if [ "$mode" == "Mode: leader" ] || [ "$mode" == "Mode: standalone" ]; then
                exit 0;
              fi;
            done;
          let COUNTER=COUNTER+1;
          sleep 2;
        done;
        echo "Did NOT see a ZK leader after 240 secs!";
        exit 1;
  - name: "cp-solr-xml"
    image: busybox:latest
    command: ['sh', '-c', 'cp /tmp/solr.xml /tmp-config/solr.xml']
    volumeMounts:
    - name: "solr-xml"
      mountPath: "/tmp"
    - name: "solr-pvc"
      mountPath: "/tmp-config"

cp-solr-xml initContainer只是將solr.xml文件從/tmp 復制到/tmp-config,該文件恰好與Solr容器在/opt/solr/server/home看到的永久卷(solr-pvc)相同。 但是,等等,solr.xml是如何進入initContainer的/tmp的呢?使用Kubernetes ConfigMap和StatefulSet的volume進行定義:

solr-xml-configmap.yaml生成到solr.yaml

apiVersion: "v1"
kind: "ConfigMap"
metadata:
  name: "solr-config-map"
  labels:
    app: solr
    chart: solr-1.0.0
    release: solr
    heritage: Tiller
data:
  solr.xml: |
    <?xml version="1.0" encoding="UTF-8" ?>
    <solr>
      <solrcloud>
        <str name="host">${host:}</str>
        <int name="hostPort">${jetty.port:8983}</int>
        <str name="hostContext">${hostContext:solr}</str>
        <bool name="genericCoreNodeNames">${genericCoreNodeNames:true}</bool>
        <int name="zkClientTimeout">${zkClientTimeout:30000}</int>
        <int name="distribUpdateSoTimeout">${distribUpdateSoTimeout:600000}</int>
        <int name="distribUpdateConnTimeout">${distribUpdateConnTimeout:60000}</int>
        <str name="zkCredentialsProvider">${zkCredentialsProvider:org.apache.solr.common.cloud.DefaultZkCredentialsProvider}</str>
        <str name="zkACLProvider">${zkACLProvider:org.apache.solr.common.cloud.DefaultZkACLProvider}</str>
      </solrcloud>
      <shardHandlerFactory name="shardHandlerFactory"
        class="HttpShardHandlerFactory">
        <int name="socketTimeout">${socketTimeout:600000}</int>
        <int name="connTimeout">${connTimeout:60000}</int>
      </shardHandlerFactory>
    </solr>

現在,在ConfigMap的solr.xmlkey中包含一個solr.xml文件內容。 為了使其可用于StatefulSet中的pod,我們使用以下命令將ConfigMap掛載為volume:

statefulset.yaml生成到solr.yaml

volumes:
  - name: solr-xml
    configMap:
      name: solr-config-map
      items:
      - key: solr.xml
        path: solr.xml

使用initContainers和ConfigMaps將solr.xml引導到Solr的主目錄中非常麻煩。 它確實是使用initContainers在啟動主容器之前使pod處于良好狀態的一個很好的例子。 將來,Solr應該對此內置的問題有更好的解決方案,請參閱: https : //issues.apache.org/jira/browse/SOLR-13035 。

概括地說,Solr StatefulSet已根據集合名稱和副本序號為集群中的每個節點分配了主機名,例如solr-0,solr-1等,并為每個pod分配了20G永久volume在/opt/solr/server/home目錄。

Replacing Lost Stateful Replicas

首先查看solr pod運行在哪個node上。

> kubectl get pod -o=custom-columns=NODE:.spec.nodeName,NAME:.metadata.name
NODE             NAME
docker-desktop   solr-0
docker-desktop   solr-1
docker-desktop   solr-2
docker-desktop   solr-exporter-58dbb665db-46wfx
docker-desktop   solr-zookeeper-0
docker-desktop   solr-zookeeper-1
docker-desktop   solr-zookeeper-2

現在kill調一個solr pod看會發生什么:

kubectl delete po solr-2 --force --grace-period 0

查看現在solr pod狀態:

kubectl get pods
NAME                             READY   STATUS     RESTARTS   AGE
apple-app                        1/1     Running    0          17h
banana-app                       1/1     Running    0          17h
solr-0                           1/1     Running    0          4h19m
solr-1                           1/1     Running    0          4h12m
solr-2                           0/1     Init:0/2   0          1s

等待片刻后,請注意丟失的solr-2 pod已重新添加到集群中。 如果您重新運行get nodes,您將看到solr-2 pod已經在之前相同的nodes上重新創建。 這是因為k8s在努力維持平衡集群。

Liveness and Readiness Probes

Kubernetes使用liveness和readiness探針時刻監控你的pods的狀態。 目前,Solr helm chart使用以下命令/solr/admin/info/system:

# solr.yaml文件 由statefulset生成

livenessProbe:
  initialDelaySeconds: 20
  periodSeconds: 10
  httpGet:
    scheme: "HTTP"
    path: /solr/admin/info/system
    port: 8983
readinessProbe:
  initialDelaySeconds: 15
  periodSeconds: 5
  httpGet:
    scheme: "HTTP"
    path: /solr/admin/info/system
    port: 8983

Coordinating Pod Initialization

在繼續下一節之前,讓我們看一下k8s如何協調Solr和Zookeeper pods之間的時序性。 具體來說,Solr要求Zookeeper在完全初始化并處理請求之前可用。 但是,對于k8s,我們希望能夠在無需協調順序的情況下部署pods。 實際上,在Kubernetes中沒有在StatefulSets之間命令pod初始化的概念。 為此,我們依靠initContainer在k8s調用主Solr容器之前測試ZK運行狀況。 如果ZK不健康,則initContainer睡眠幾秒鐘,然后一分鐘重試。

statefulset.yaml生成到solr.yaml文件

initContainers:
  - name: check-zk
    image: busybox:latest
    command:
      - 'sh'
      - '-c'
      - |
        COUNTER=0;
        while [  $COUNTER -lt 120 ]; do
          for i in "solr-zookeeper-0.solr-zookeeper-headless" "solr-zookeeper-1.solr-zookeeper-headless" "solr-zookeeper-2.solr-zookeeper-headless" ;
            do mode=$(echo srvr | nc $i 2181 | grep "Mode");
              if [ "$mode" == "Mode: leader" ] || [ "$mode" == "Mode: standalone" ]; then
                exit 0;
              fi;
            done;
          let COUNTER=COUNTER+1;
          sleep 2;
        done;
        echo "Did NOT see a ZK leader after 240 secs!";
        exit 1;

如果Solr不在線,請使用以下命令檢查initContainers的狀態:

kubectl describe pod <pod name>

Upgrading Solr

還記得我們說過Kubernetes幫助實施最佳實踐和經過驗證的設計模式嗎? 在不停機的情況下執行滾動升級是StatefulSet中內置的最佳實踐之一。 要查看實際效果,只需重新運行helm template命令,而無需使用–set image.tag參數:

helm template . --name solr > solr.yaml

kubectl apply -f solr.yaml

請注意,它如何檢測到Solr StatefulSet發生了變化,但其他所有資源均保持不變。 從solr-2開始,k8s進行從Solr 7.5.0容器到7.6.0容器的滾動升級。 solr-2初始化之后,查看一下日志,您會看到它現在正在運行Solr 7.6.0:

一切都很好,只是它沒有考慮到要升級的節點上所有leader的重新選舉。 在這種情況下,Kube也支持我們,因為它向solr進程發送了SIGTERM,這觸發了solr開始卸載內核并正常關閉。 k8s將等待30秒以使Solr正常關閉,這對于大多數用例來說已經足夠了。 如果需要,您可以使用Pod規范上的terminationGracePeriodSeconds增加超時時間。

Kubernetes升級策略

在Kubernetes 1.7及更高版本中,通過.spec.updateStrategy字段允許配置或禁用Pod、labels、source request/limits、annotations自動滾動更新功能。

  • OnDelete:通過.spec.updateStrategy.type 字段設置為OnDelete,StatefulSet控制器不會自動更新StatefulSet中的Pod。用戶必須手動刪除Pod,以使控制器創建新的Pod。

  • RollingUpdate 滾動更新:通過.spec.updateStrategy.type 字段設置為RollingUpdate,實現了Pod的自動滾動更新,如果.spec.updateStrategy未指定,則此為默認策略。StatefulSet控制器將刪除并重新創建StatefulSet中的每個Pod。它將以Pod終止(從最大序數到最小序數)的順序進行更新每個Pod。在更新下一個Pod之前,必須等待這個Pod Running and Ready。

  • Partitions 滾動更新的分區更新:通過指定 .spec.updateStrategy.rollingUpdate.partition 來對 RollingUpdate 更新策略進行分區,如果指定了分區,則當 StatefulSet 的 .spec.template 更新時,具有大于或等于分區序數的所有 Pod 將被更新。
    具有小于分區的序數的所有 Pod 將不會被更新,即使刪除它們也將被重新創建。如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于其 .spec.replicas,則其 .spec.template 的更新將不會更新Pod。默認partition的值是0,簡單來說就是當partition等N,N+的都會更新。

示例:修改更新策略,以partition方式進行更新,更新值為2,只有myapp編號大于等于2的才會進行更新。類似于金絲雀部署方式。

# 修改分區更新必須大于等于2的pods
> kubectl patch sts solr -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'

# 進行升級,將solr版本修改為8.3
> kubectl set image sts/solr solr=solr:8.3.0

# 查看狀態
> kubectl get pods solr-2 -o yaml |grep image

    image: solr:8.3.0
    imagePullPolicy: Always
    image: busybox:latest

則只會將solr-2升級為8.3.0版本。

多StatefulSet的金絲雀發布

在StatefulSet上滾動更新升級所有Pod,但是如果要在整個集群上滾動發布Solr更新之前進行試驗,即要執行所謂的“canary release”,那該怎么辦。

例如,假設我們想嘗試Solr 8.0.0,但是僅升級一部分,以防萬一我們的實驗出錯了。 或者,可以嘗試一些不同的Solr配置參數組合。 關鍵是您的canary pod有了一些更改,需要在跨群集推出之前進行驗證。

對于本實驗,我們只想將發布單個canary pod。 在實施該解決方案之前,讓我們介紹一下Kubernetes服務如何與一組Pod一起工作。 首先,使用以下命令查看為Solr集群定義的服務:

> kubectl get svc -o wide -l app=solr

NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE   SELECTOR
solr-exporter   ClusterIP   10.105.249.59    <none>        9983/TCP   10m   app=solr,component=exporter,release=solr
solr-headless   ClusterIP   None             <none>        8983/TCP   10m   app=solr,component=server,release=solr
solr-svc        ClusterIP   10.103.229.114   <none>        8983/TCP   10m   app=solr,component=server,release=solr

Kubernetes使用Pod標簽選擇器為一組Pod提供上層請求的負載均衡服務。 例如,solr-svc服務選擇帶有以下標簽的容器:app=solr,release=solr和component=server:

# solr.yaml文件內容:

---
# Source: solr/templates/service-headless.yaml
---

apiVersion: "v1"
kind: "Service"
metadata:
  name: "solr-headless"
  labels:
    app: solr
    chart: solr-1.0.0
    release: solr
    heritage: Tiller
spec:
  clusterIP: "None"
  ports:
  - port: 8983
    name: "solr-headless"
  selector:
    app: "solr"
    release: "solr"
    component: "server"

因此,只要Pod的標簽與服務的選擇器匹配,Pod來自哪個StatefulSet(或Deployment)都無關緊要。 這意味著我們可以在集群中部署多個StatefulSet,每個StatefulSet指向不同版本的Solr,并且該服務將流量路由到這些SstatefulSet。

我們將其作為練習,供讀者使用不同的Solr版本使用單個副本部署另一個StatefulSet。canary pod上線后,您需要使用Solr集合API將集合中的副本添加到canary Solr實例上。

擴展伸縮pods

使用kubectl的scale命名,設置pod數量,則可以擴展或減少pods數量。
有序擴展,有序收縮。

# 將solr應用擴容到6個pod
> kubectl scale sts solr --replicas=6

# 查看pods狀態
> kubectl get pods
NAME                             READY   STATUS            RESTARTS   AGE
solr-0                           1/1     Running           2          4h41m
solr-1                           1/1     Running           2          4h40m
solr-2                           1/1     Running           2          4h39m
solr-3                           1/1     Running           2          4h21m
solr-4                           1/1     Running           2          4h20m
solr-5                           0/1     PodInitializing   0          8s

Performance Smoke Test

我們現在不會花很多時間在性能和負載測試上,因為我們將在下一篇文章中更詳細地介紹它。 目前,我們要回答的問題之一是Solr在Kubernetes中是否較慢。

首先,我們需要大數據的索引,因此我們選擇使用在Dataproc中運行的Spark和Lucidworks提供的spark-solr庫。以下Scala腳本從存儲在Google Cloud Storage(GCS)中的Spark索引導出750萬個文檔:

該腳本允許我們根據需要使用Spark將其擴展到盡可能多的并發索引核心,因此我們可以測試存儲在GCS中的海量Solr集群和任意大小的數據集。 索引到以“ n1-standard-4”實例類型運行的3節點群集導致了16,800個文檔/秒(3個分片/每個分片1個副本)。 我們在Spark端使用了12個并發執行程序核心。

相比之下,我們對在GCE(虛擬機而非容器)上運行的Solr進行了相同的測試,并獲得了約15,000個文檔/秒。 因此,在這種情況下,在Kube上運行速度更快,但這是一個相當小的數據集,并且云VM的性能可能會略有不同。 重要的是,Kube在使用相同的n1-standard-4實例類型的GCE中具有與基于VM的性能相當的性能。 在下一篇文章中,我們將在啟用Solr復制的情況下在更大的集合上運行更長的性能和負載測試。

Solr Metrics in Prometheus

Encrypting Traffic Between Solr Instances Using TLS

Wrap Up

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 引言 Kubernetes已經成為市場上事實上領先的編配工具,不僅對技術公司如此,對所有公司都是如此,因為它允許您...
    JFrog閱讀 1,061評論 0 4
  • 介紹 Helm 是 Deis 開發的一個用于 Kubernetes 應用的包管理工具,主要用來管理 Charts。...
    小波同學閱讀 1,455評論 0 4
  • 我喜歡 在冬日里洗大澡 氤氳白汽 再添一抹雪菊香 壁爐光暖 屋子竟也通亮 白貓黑狗偎在身旁 像是臉紅 也像在把時光...
    一角天空閱讀 243評論 4 2
  • 余生。。。 愿幸福與愛都在 愿我們都被這個世界溫柔以待 愿2019一切都好!!!
    緣分www閱讀 158評論 0 0
  • 文cherry 今天是海南高考的最后一天,三年了,我每一次只要一想到高考。就像打開了回憶的閘門,那些人那些事總能在...
    小櫻桃的小屋閱讀 239評論 2 9