Kubernetes 项目基于 Nacos 实现服务注册发现

在前面的Spring Cloud 项目基于 Nacos 实现服务注册发现文章中,我们已经分别学习了 Spring Boot 项目 和 Spring Cloud 项目基于 Nacos 实现服务注册发现。本文中,我们将在此基础上讲述,基于 Kubernetes 部署 Spring Boot 项目 和 Spring Cloud 项目时,如何基于 Nacos 实现服务注册发现。

前提要求

  • 服务需要是 Spring Boot 或 Spring Cloud 项目
  • 服务已经集成了 Nacos 客户端

环境准备

1、创建 Kubernetes 项目命名空间

假设我们的项目为 demo, 首先我们同 kubectl 创建命名空间。

namespace-prod.yaml

apiVersion: v1
kind: Namespace
metadata:
   name: demo-prod
   labels:
     name: demo-prod
   annotations:
     kubesphere.io/alias-name: Demo-生产环境
     kubesphere.io/description: Demo-生产环境

使用 kubectl 执行如下命令,初始化项目命名空间:

kubectl apply -f namespace-prod.yaml

2、创建 Nacos 命名空间

在 Nacos 命名空间功能界面,点击 “新建命名空间” 创建新的命名空间

属性
名称 demo
别名 demo-生产环境
描述 demo-生产环境

3、创建 Nacos 的 Config Map 配置

nacos-config.yaml 文件

kind: ConfigMap
apiVersion: v1
metadata:
  name: nacos-config
  namespace: demo-prod
  annotations:
    field.cattle.io/description: Nacos 配置
data:
  nacos.config.namespace: fa05d9d6-d47d-4d49-b797-b918ffba2e22
  nacos.config.server-addr: 'http://192.168.3.1:8848'
  nacos.config.password: SKf643GmN7Rwxjrf
  nacos.config.username: nacos
  nacos.discovery.namespace: fa05d9d6-d47d-4d49-b797-b918ffba2e22
  nacos.discovery.password: SKf643GmN7Rwxjrf
  nacos.discovery.username: nacos
  spring.cloud.nacos.namespace: fa05d9d6-d47d-4d49-b797-b918ffba2e22
  spring.cloud.nacos.server-addr: 'http://192.168.3.1:8848'
  spring.cloud.nacos.username: nacos
  spring.cloud.nacos.password: SKf643GmN7Rwxjrf
  spring.cloud.nacos.access-key: nacos
  spring.cloud.nacos.secret-key: SKf643GmN7Rwxjrf

使用 kubectl 执行如下命令,初始化配置

kubectl apply -f nacos-config.yaml --namespace=demo-prod;

服务部署

假设现有一服务 wans-demo , 其部署脚本 wans-demo.yaml 如下:

---
apiVersion: v1
kind: Service
metadata:
  name: wans-demo-svc
  labels:
    app: wans-demo
    micrometer-prometheus-discovery: 'true'
  annotations:
    kubesphere.io/alias-name: Demo 服务
    kubesphere.io/description: Demo 服务
    prometheus.io/path: /actuator/prometheus
    prometheus.io/port: '50000'
    prometheus.io/scrape: 'true'
spec:
  ports:
    - name: tcp-6008
      port: 6008
      protocol: TCP
      targetPort: 6008
    - name: tcp-50000
      protocol: TCP
      port: 50000
      targetPort: 50000
  selector:
    app: wans-demo
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wans-demo-deploy
  annotations:
    kubesphere.io/alias-name: Demo 服务
    kubesphere.io/description: Demo 服务
  labels:
    app: wans-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wans-demo
  template:
    metadata:
      labels:
        app: wans-demo
    spec:
      containers:
        - name: wans-demo
          image: >-
            wans-docker.pkg.coding.net/demo/docker/wans-demo:latest
          ports:
            - name: http-6008
              containerPort: 6008
              protocol: TCP
            - name: management-port
              containerPort: 50000
              protocol: TCP
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
            - name: SPRING_CLOUD_NACOS_SERVER-ADDR
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.server-addr
            - name: SPRING_CLOUD_NACOS_USERNAME
              valueFrom:
                configMapKeyRef:
                  key: spring.cloud.nacos.username
                  name: nacos-config
            - name: SPRING_CLOUD_NACOS_PASSWORD
              valueFrom:
                configMapKeyRef:
                  key: spring.cloud.nacos.password
                  name: nacos-config
            - name: SPRING_CLOUD_NACOS_CONFIG_NAMESPACE
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.namespace
            - name: SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.namespace
            - name: JAVA_TOOL_OPTIONS
              value: >-
                -Xmx2048M -Xms2048M -Xmn768M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M
                -XX:MetaspaceSize=256M -XX:+PrintGCDetails
                -XX:+PrintGCTimeStamps -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
                -XX:NewRatio=1 -XX:SurvivorRatio=4 -XX:GCLogFileSize=20m
                -XX:+HeapDumpOnOutOfMemoryError
                -XX:HeapDumpPath=/logs/heaperror.log
                -Xloggc:/logs/gcerror.log
          resources: {}
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 90
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 60
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          startupProbe:
            httpGet:
              path: /actuator/health
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 60
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          lifecycle:
            preStop:
              exec:
                command:
                  - curl
                  - '-XPOST'
                  - '127.0.0.1:50000/actuator/shutdown'
          imagePullPolicy: Always
      imagePullSecrets:
        - name: coding-registry-cred
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app: wans-demo
                topologyKey: kubernetes.io/hostname

使用 kubectl 执行如下命令,部署服务

kubectl apply -f wans-demo.yaml --namespace=demo-prod;

说明

  • 此服务配置了 Prometheus 的监控端点 ,且配置了服务暴露端点地址为 /actuator/prometheus, 暴露端口为 50000
  • 此服务配置了 启动、就绪、存活3种探针
  • 此服务启动了 生命周期检查中的 preStop 配合,Spring Boot 的配置可实现优雅停机
  • 此服务配置了 从 config-map 中获取配置,用于 覆盖Spring Boot 程序启动时的配置
    • SPRING_PROFILES_ACTIVE 变量指定的值,会替换掉 Spring Boot 程序中的 spring.profiles.active 值,进而激活指定的环境,这里使用了 prod ,标识要激活 prod 环境
    • 响应的服务启动后,Nacos 会自动的去拉取 wans-demo.yaml 和 wans-demo-prod.yaml 两个配置文件,便可以正常的启动成功

服务配置

一、基于不同文件名进行不同环境配置

Spring Boot 允许你为不同的环境创建具有特定名称的配置文件。例如,你可以有 application.yml 用于默认配置,application-dev.yml 用于开发环境,application-test.yml 用于测试环境,以及 application-prod.yml 用于生产环境。

所以我们可以通过在 Nacos 中配置不同的环境配置文件来区分不同的环境配置。

# wans-demo.yml (默认环境)
spring:
  datasource:
    url: ${DB_URL:localhost}

# wans-demo-dev.yml (Demo 应用 - 开发环境)
spring:
  application:
    name: wans-demo
  profiles:
    active: app1
  datasource:
    url: localhost:3306/db_dev

# wans-demo-test.yml (Demo 应用 - 测试环境)
spring:
  application:
    name: wans-demo
  profiles:
    active: app2
  datasource:
    url: localhost:3306/db_test

# wans-demo-prod.yml (Demo 应用 - 生产环境)
spring:
  application:
    name: wans-demo
  profiles:
    active: app3
  datasource:
    url: localhost:3306/db_prod

可以通过设置 SPRING_PROFILES_ACTIVE 属性来激活特定的环境配置。例如,如果你想使用生产环境配置,可以在指定 SPRING_PROFILES_ACTIVE 参数值为 prod

二、使用 YAML 文件中的多文档块进行环境配置

在单个 YAML 文件中,你可以使用“多文档”来区分不同环境的配置。这种方法的好处是所有配置都集中在一个文件中,易于管理。

spring:
  # 公共配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver

# 特定环境配置
---
spring:
  profiles: dev
  datasource:
    url: localhost:3306/db_dev
    username: dev_user
    password: dev_pass

---
spring:
  profiles: test
  datasource:
    url: localhost:3306/db_test
    username: test_user
    password: test_pass

---
spring:
  profiles: prod
  datasource:
    url: localhost:3306/db_prod
    username: prod_user
    password: prod_pass

在这个例子中,每个 — 分隔的块代表一个特定的环境配置。通过设置 SPRING_PROFILES_ACTIVE 属性来激活特定的环境配置。例如,如果你想使用生产环境配置,可以在指定 SPRING_PROFILES_ACTIVE 参数值为 prod

三、多环境多实例配置(于同一套代码,使用不同环境,且暴露服务名不同)

不管是 基于不同文件名进行不同环境配置 还是 使用 YAML 文件中的多文档块进行环境配置 ,其始终还是是同一个服务实例的不同环境下的配置引用。假设我们的需求是 同一的服务镜像,使用不同的环境配置,且部署名称和注册服务也不相同,要如何配置呢?下面,我们一起去探究下这个问题。

这里还是以 wans-demo 服务实例进行说明,并将 wans-demo 分部部署成 wans-demo-app1、wans-demo-app2

wans-demo-app1.yaml

---
apiVersion: v1
kind: Service
metadata:
  name: wans-demo-app1-svc
  labels:
    app: wans-demo-app1
    micrometer-prometheus-discovery: 'true'
  annotations:
    kubesphere.io/alias-name: Demo1 服务
    kubesphere.io/description: Demo1 服务
    prometheus.io/path: /actuator/prometheus
    prometheus.io/port: '50000'
    prometheus.io/scrape: 'true'
spec:
  ports:
    - name: tcp-6008
      port: 6008
      protocol: TCP
      targetPort: 6008
    - name: tcp-50000
      protocol: TCP
      port: 50000
      targetPort: 50000
  selector:
    app: wans-demo-app1
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wans-demo-app1-deploy
  annotations:
    kubesphere.io/alias-name: Demo1 服务
    kubesphere.io/description: Demo1 服务
  labels:
    app: wans-demo-app1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wans-demo-app1
  template:
    metadata:
      labels:
        app: wans-demo-app1
    spec:
      containers:
        - name: wans-demo-app1
          image: >-
            wans-docker.pkg.coding.net/demo/docker/wans-demo:latest
          ports:
            - name: http-6008
              containerPort: 6008
              protocol: TCP
            - name: management-port
              containerPort: 50000
              protocol: TCP
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
            - name: SPRING_CLOUD_NACOS_SERVER-ADDR
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.server-addr
            - name: SPRING_CLOUD_NACOS_USERNAME
              valueFrom:
                configMapKeyRef:
                  key: spring.cloud.nacos.username
                  name: nacos-config
            - name: SPRING_CLOUD_NACOS_PASSWORD
              valueFrom:
                configMapKeyRef:
                  key: spring.cloud.nacos.password
                  name: nacos-config
            - name: SPRING_CLOUD_NACOS_CONFIG_NAMESPACE
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.namespace
            - name: SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.namespace
            - name: JAVA_TOOL_OPTIONS
              value: >-
                -Xmx2048M -Xms2048M -Xmn768M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M
                -XX:MetaspaceSize=256M -XX:+PrintGCDetails
                -XX:+PrintGCTimeStamps -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
                -XX:NewRatio=1 -XX:SurvivorRatio=4 -XX:GCLogFileSize=20m
                -XX:+HeapDumpOnOutOfMemoryError
                -XX:HeapDumpPath=/logs/heaperror.log
                -Xloggc:/logs/gcerror.log
          resources: {}
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 90
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 60
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          startupProbe:
            httpGet:
              path: /actuator/health
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 60
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          lifecycle:
            preStop:
              exec:
                command:
                  - curl
                  - '-XPOST'
                  - '127.0.0.1:50000/actuator/shutdown'
          imagePullPolicy: Always
      imagePullSecrets:
        - name: coding-registry-cred
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app: wans-demo-app1
                topologyKey: kubernetes.io/hostname

wans-demo-app2.yaml

---
apiVersion: v1
kind: Service
metadata:
  name: wans-demo-app2-svc
  labels:
    app: wans-demo-app2
    micrometer-prometheus-discovery: 'true'
  annotations:
    kubesphere.io/alias-name: Demo2 服务
    kubesphere.io/description: Demo2 服务
    prometheus.io/path: /actuator/prometheus
    prometheus.io/port: '50000'
    prometheus.io/scrape: 'true'
spec:
  ports:
    - name: tcp-6008
      port: 6008
      protocol: TCP
      targetPort: 6008
    - name: tcp-50000
      protocol: TCP
      port: 50000
      targetPort: 50000
  selector:
    app: wans-demo-app2
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wans-demo-app2-deploy
  annotations:
    kubesphere.io/alias-name: Demo 服务
    kubesphere.io/description: Demo 服务
  labels:
    app: wans-demo-app2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wans-demo-app2
  template:
    metadata:
      labels:
        app: wans-demo-app2
    spec:
      containers:
        - name: wans-demo-app2
          image: >-
            wans-docker.pkg.coding.net/demo/docker/wans-demo:latest
          ports:
            - name: http-6008
              containerPort: 6008
              protocol: TCP
            - name: management-port
              containerPort: 50000
              protocol: TCP
          env:
            - name: SPRING_PROFILES_ACTIVE
              value: prod
            - name: SPRING_CLOUD_NACOS_SERVER-ADDR
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.server-addr
            - name: SPRING_CLOUD_NACOS_USERNAME
              valueFrom:
                configMapKeyRef:
                  key: spring.cloud.nacos.username
                  name: nacos-config
            - name: SPRING_CLOUD_NACOS_PASSWORD
              valueFrom:
                configMapKeyRef:
                  key: spring.cloud.nacos.password
                  name: nacos-config
            - name: SPRING_CLOUD_NACOS_CONFIG_NAMESPACE
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.namespace
            - name: SPRING_CLOUD_NACOS_DISCOVERY_NAMESPACE
              valueFrom:
                configMapKeyRef:
                  name: nacos-config
                  key: spring.cloud.nacos.namespace
            - name: JAVA_TOOL_OPTIONS
              value: >-
                -Xmx2048M -Xms2048M -Xmn768M -XX:MaxMetaspaceSize=256M -XX:MetaspaceSize=256M
                -XX:MetaspaceSize=256M -XX:+PrintGCDetails
                -XX:+PrintGCTimeStamps -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
                -XX:NewRatio=1 -XX:SurvivorRatio=4 -XX:GCLogFileSize=20m
                -XX:+HeapDumpOnOutOfMemoryError
                -XX:HeapDumpPath=/logs/heaperror.log
                -Xloggc:/logs/gcerror.log
          resources: {}
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 90
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 60
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          startupProbe:
            httpGet:
              path: /actuator/health
              port: 50000
              scheme: HTTP
            initialDelaySeconds: 60
            timeoutSeconds: 5
            periodSeconds: 30
            successThreshold: 1
            failureThreshold: 9
          lifecycle:
            preStop:
              exec:
                command:
                  - curl
                  - '-XPOST'
                  - '127.0.0.1:50000/actuator/shutdown'
          imagePullPolicy: Always
      imagePullSecrets:
        - name: coding-registry-cred
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchLabels:
                    app: wans-demo-app2
                topologyKey: kubernetes.io/hostname

两个服务 wans-demo-app1 和 wans-demo-app2 现在使用了相同的镜像,并都使用了 prod 环境,它们都回去拉取 wans-demo.yaml 和 wans-demo-prod.yaml 两个配置文件,虽然,两个服务独立,但是运行的镜像相同,配置相同,并不能满足我们期望的目标,所以还需进一步进行改进。

改进方式1:区分应用,配置独立,区分注册名称

此需要做一下几件事:

  • Kubernetes 中,使用 SPRING_APPLICATION_NAME 变量,将2个服务的 spring.application.name 各自进行修改,分部指定为 wans-demo-app1wans-demo-app2,Nacos 上注册的服务名称默认与spring.application.name保持一致。
  • 在 Nacos 分别创建 wans-demo-app1-prod.yamlwans-demo-app2-prod.yaml 配置文件
  • SPRING_PROFILES_ACTIVE 参数值为 prod
改进方式2:不区分应用,共享配置,区分注册名称

有时候,我们可能需要将 wans-demo 当做 很多个独立的实例去运行,可能有十几二十个,继续采用方式1,会有很多个配置文件,如果想共享配置,则可以使用 YAML 文件中的多文档块进行环境配置。

  • 修改程序中的 bootstrap.yaml 文件增加 公共配置 dataId: wans-demo-app.yaml
# 指定启用环境
spring:
  # 设置应用名称,根据应用实际情况替换
  application:
    name: wans-demo-app
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      # 设置nacos地址
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      # 配置中心设置
      config:
        file-extension: yaml
        # 指定命名空间,默认为public
        namespace: d4a77f87-ee73-4f71-a281-d8cf617d8051
        # 指定默认配置
        extension-configs:
          - dataId: common-config.yaml
            group: DEFAULT_GROUP
            refresh: true
          - dataId: dynamic-tp.yaml
            group: DEFAULT_GROUP
            refresh: true
          - dataId: wans-demo-app.yaml
            group: DEFAULT_GROUP
            refresh: true
      # 注册中心设置
      discovery:
        namespace: d4a77f87-ee73-4f71-a281-d8cf617d8051
        metadata:
          '[name]' : ${swagger.title}
          '[detail]' : ${swagger.description}
          '[version]' : ${swagger.version}
  • 在 Nacos 配置文件中创建 wans-demo-app.yaml 配置, 采用文档块的方式为每个服务实例单独指定环境配置
spring:
  # 公共配置
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver

# 特定应用配置
---
spring:
  profiles: app1
  datasource:
    url: localhost:3306/db_dev
    username: dev_user
    password: dev_pass

---
spring:
  profiles: app2
  datasource:
    url: localhost:3306/db_test
    username: test_user
    password: test_pass

---
spring:
  profiles: app3
  datasource:
    url: localhost:3306/db_prod
    username: prod_user
    password: prod_pass
  • Kubernetes 中,使用 SPRING_APPLICATION_NAME 变量,将2个服务的 spring.application.name 各自进行修改,分部指定为 app1app2

  • Kubernetes 中,使用 SPRING_CLOUD_NACOS_DISCOVERY_SERVICE 变量,将n个服务的 spring.cloud.nacos.discovery.service 各自进行修改,分部指定为 wans-demo-app1wans-demo-app2 等。

作者:Jeebiz  创建时间:2024-10-02 20:24
最后编辑:Jeebiz  更新时间:2024-10-03 00:38