k8s部署eureka集群

背景

對于一般的后端微服務(wù)來說,在k8s中同時起多個相同的服務(wù)來做負載均衡,只需要簡單的修改deployment的replicas,增加pod數(shù)量,然后通過對外暴露一個service來代理這些pod。

而對于eureka來說,要實現(xiàn)eureka的高可用,那就不是修改replicas這么方便了。由于部署的多個eureka之間需要將自己注冊到彼此,因此要做一些特殊改動。

主要是用到了StatefulSet和headless service這兩個k8s對象

StatefulSet、Headless Service簡介:

StatefulSet

StatefulSet是為了解決有狀態(tài)服務(wù)的問題(對應(yīng)Deployments和ReplicaSets是為無狀態(tài)服務(wù)而設(shè)計),其應(yīng)用場景包括

穩(wěn)定的持久化存儲,即Pod重新調(diào)度后還是能訪問到相同的持久化數(shù)據(jù),基于PVC來實現(xiàn)

穩(wěn)定的網(wǎng)絡(luò)標志,即Pod重新調(diào)度后其PodName和HostName不變,基于Headless Service(即沒有Cluster IP的Service)來實現(xiàn)

有序部署,有序擴展,即Pod是有順序的,在部署或者擴展的時候要依據(jù)定義的順序依次依次進行(即從0到N-1,在下一個Pod運行之前所有之前的Pod必須都是Running和Ready狀態(tài)),基于init containers來實現(xiàn)

有序收縮,有序刪除(即從N-1到0)

StatefulSet中每個Pod的DNS格式為

statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local

serviceName為Headless Service的名字
0..N-1為Pod所在的序號,從0開始到N-1
statefulSetName為StatefulSet的名字
namespace為服務(wù)所在的namespace,Headless Service和StatefulSet必須在相同的namespace
cluster.local為Cluster Domain

Headless Service

Headless Service 和普通service的一個顯著的區(qū)別是,Headless Service的對應(yīng)的每一個Endpoints,即每一個Pod,都會有對應(yīng)的DNS域名
例如:我們可以用過這種域名來訪問某個具體的pod:

statefulSetName-0.serviceName.namespace.svc.cluster.local

在實際使用中,將service的clusterIP設(shè)置成None,就表明這個service是一個Headless Service。

StatefulSet和Headless Service的結(jié)合

通過 StatefulSet,我們得到了一些列pod,每個pod的name為statefulSetName-{0..N-1},
加入我們創(chuàng)建了一個名稱叫eureka的StatefulSet,并且設(shè)置replicas =3,那么部署到k8s后,k8s會為我們生成三個名稱依次為eureka-0,eureka-1,eureka-2的pod。
通過Headless Service,我們可以通過pod名稱來訪問某個pod,

例如,我們在namespace=test的命名空間下創(chuàng)建了一個名稱為register-server的service,并且關(guān)聯(lián)了之前StatefulSet創(chuàng)建的pod,那么我們可以在集群內(nèi)任意地方
通過eureka-0.register-server.test.svc.cluster.local這個域名訪問到eureka-0這個pod。

搭建:

有了前面的基礎(chǔ),現(xiàn)在部署eureka集群的方式就逐漸清晰了。

首先明確部署eureka的關(guān)鍵點:需要讓每個eureka注冊到另外的eureka上。
也就是eureka.client.serviceUrl.defaultZone這個配置,是一組eureka的地址。
通過StatefulSet,我們可以明確知道生成的每個eureka的名稱,
通過Headless Service,我們又可以訪問到每個eureka,所以eureka.client.serviceUrl.defaultZone的值就是

"http://eureka-0.register-server:8000/eureka/,http://eureka-1.register-server:8000/eureka/,http://eureka-2.register-server:8000/eureka/"

由于這三個pod在同一個命名空間內(nèi),可以省略.namespace.svc.cluster.local

有個這個配置,那么我們部署StatefulSet,和Headless Service
那么我們能基本能得到一個可用的eureka集群
除了會有以下問題:
紅框中的可用副本(available-replicas)會出現(xiàn)在不可用unavailable-replicas中


253A933C7535743E87D5D3F58E3A88CC.png

原因是我們默認是通過ip的方式來注冊eureka(eureka.instance.prefer-ip-address配置默認為true),但是eureka的注冊地址又是域名的形式,兩者不一致。
要解決這個問題,還需做一些額外的配置。

1.在application.yaml中,將eureka.instance.prefer-ip-address設(shè)置成false。

 eureka:
     instance:
        prefer-ip-address: false

2.StatefulSet.yaml中,增加環(huán)境變量配置,將pod的名稱綁定到環(huán)境變量

         env:
          - name: MY_POD_NAME      
            valueFrom:
              fieldRef:
                fieldPath: metadata.name

3.在application.yaml中指定eureka的 hostname,其中MY_POD_NAME取到了第二部中綁定的當(dāng)前pod名稱
eureka:
instance:
hostname: ${MY_POD_NAME}.register-server

如上配置后,便可以得到一個eureka集群。

后面是一些配置文件:
整體文件配置:


096DBDAA639ACBB9F55B40545C56928A.png

service.yaml

apiVersion: v1
kind: Service
metadata:
  name: register-server
  labels:
    service: register-server
spec:
  clusterIP: None
  type: ClusterIP
  ports:
    - port: 8000
      targetPort: http
      protocol: TCP
      name: http
  selector:
    service: register-server

StatefulSet.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: register-server
  labels:
    service: register-server
spec:
  replicas: 3
  serviceName: register-server
  selector:
    matchLabels:
      service: register-server
  template:
    metadata:
      labels:
        service: register-server
      annotations:
        service: register-server
    spec:
      containers:
        - name: register-server
          image: codewjy/eureka:0.1.0
          imagePullPolicy: Always
          env:
          - name: MY_POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          ports:
            - name: http
              containerPort: 8000
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: 8001
              scheme: HTTP
            failureThreshold: 3
            initialDelaySeconds: 60
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          resources:
            limits:
              # cpu: 100m
              memory: 1.7Gi
            requests:
                # cpu: 100m
              memory: 1.2Gi
          volumeMounts:
          - mountPath: /Charts
            name: data
      volumes:
      - name: data
  podManagementPolicy: "Parallel"

application.yml

eureka:
  instance:
    leaseRenewalIntervalInSeconds: 10
    leaseExpirationDurationInSeconds: 30
    metadata-map:
      VERSION: 1.0.0
    hostname: ${MY_POD_NAME}.register-server #設(shè)置eureka hostname
    prefer-ip-address: false #不使用ip注冊,因為eureka相互注冊的工程中,使用的服務(wù)名,例如register-server-0.register-server,如果使用ip注冊,會導(dǎo)致eureka認為其他副本不可用,即eureka服務(wù)都會出現(xiàn)在unavailable-replicas中,而不是available-replicas中
  client:
    # 檢索服務(wù)選項,注冊中心不需要檢索服務(wù)
    fetch-registry: ${EUREKA_CLIENT_FETCH_REGISTRY:true}
    # 注冊中心將自己作為客戶端來嘗試注冊自己,注冊中心集群環(huán)境下需開啟此配置
    register-with-eureka: ${EUREKA_CLIENT_REGISTER_WITH_EUREKA:true}
    serviceUrl:
      defaultZone: ${EUREKA_DEFAULT_ZONE:http://register-server-0.register-server:8000/eureka/,http://register-server-1.register-server:8000/eureka/,http://register-server-2.register-server:8000/eureka/} #這里在部署的時候會使用環(huán)境變量替換 EUREKA_DEFAULT_ZONE值
    registryFetchIntervalSeconds: 10
    disable-delta: true
  server:
    evictionIntervalTimerInMs: 4000
    enable-self-preservation: ${EUREKA_SERVER_ENABLE_SELF_PRESERVATION:false}

bootstrap.yml

spring:
  application:
    name: register-server
server:
  port: 8000
management:
  server:
    port: 8001

服務(wù)注冊:

將一般的微服務(wù)注冊到eureka集群中,可以通過eureka的service來訪問eureka,即:將eureka.client.serviceUrl.defaultZone設(shè)置成register-server.test.svc.cluster.local,使用了k8s的service負載均衡,將服務(wù)注冊到任意一個活著的eureka上,然后eureka集群內(nèi)部會做同步,最終注冊到eureka集群內(nèi)部所有eureka上

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