专业的编程技术博客社区

网站首页 > 博客文章 正文

精通Spring Boot 3 : 13. Spring Cloud 与 Spring Boot (5)

baijin 2025-01-15 10:41:17 博客文章 7 ℃ 0 评论

使用云平台:Kubernetes 管理系统

Kubernetes(https://kubernetes.io/),通常简称为 K8s,是一个开源平台,用于管理容器化应用程序。可以把它比作管弦乐团的指挥,但它管理的是一群机器上的容器,而不是乐器。现在,让我们来看看 Kubernetes 如何支持微服务:

使用 Kubernetes 管理微服务的好处包括:

  • 可扩展性:Kubernetes 让您可以根据需求轻松地调整单个服务的规模,省去配置整个服务器的麻烦。这使您能够快速适应变化的流量模式,避免资源浪费。
  • 高可用性:Kubernetes 能够在容器或机器发生故障时继续运行您的服务。它会自动重启崩溃的容器,并将其重新调度到健康的节点上,确保您的应用程序始终可用。
  • 解耦与自主性:Kubernetes 能够独立管理每个微服务,让开发团队自主工作,快速部署更新,而不影响其他服务。
  • 资源管理:Kubernetes 允许您为每个服务设定资源限制和配额,确保资源的公平分配,防止资源短缺。这优化了资源的使用效率,并有助于保持系统的稳定性。
  • 部署与回滚:轻松简化单个服务的部署和回滚,降低风险,使更新过程更不易干扰。
  • 可观察性与监控:您可以监测 Kubernetes 中每个服务的健康状态和性能,从而帮助您快速识别和解决问题。

以下是 Kubernetes 的一些关键特性,这些特性有助于微服务的实现:

  • Pods:这些是共享资源和存储的相关容器组。可以将它们视为您服务的迷你环境。
  • 部署:您可以定义服务的期望状态,Kubernetes 会确保它们达到这一状态,并自动处理扩展和更新。
  • 服务:您可以将微服务提供给其他服务或外部客户端,Kubernetes 会负责负载均衡和服务发现。
  • 命名空间:Kubernetes 允许您为不同的团队或环境隔离资源和配置,从而增强安全性和组织性。

Kubernetes 为部署和管理微服务提供了强大的平台,带来了以下优势:

  • 提高灵活性和加快开发周期
  • 提升可扩展性和高可用性
  • 高效利用资源以节省成本
  • 简化的操作与管理

Kubernetes 当然不是唯一的微服务平台。其他一些流行的微服务平台包括 Docker Swarm (https://docs.docker.com/engine/swarm/)、HashiCorp Nomad (https://www.nomadproject.io/)、Marathon (https://mesosphere.github.io/marathon/)、Rancher (https://www.rancher.com/)、Cattle (https://github.com/rancher/cattle) 和 Cloud Foundry (https://www.cloudfoundry.org/)。

Kubernetes 是一个强有力的工具,适合用于管理您的容器化环境。虽然 Kubernetes 有一定的学习曲线,但在大规模管理分布式系统时,它所带来的好处是非常显著的。

使用 Kubernetes 平台

正如之前所述,Kubernetes 提供服务发现、配置管理、秘密管理、可靠性、可观察性、高可用性等多种功能。因此,让我们将 Kubernetes 作为我们的云平台,看看如何部署我们的应用。

在本节中,我们将使用 Minikube 作为我们的 Kubernetes 集群,以便在您的计算机上进行本地测试。如果您已经在 Google Cloud、AWS 或 Microsoft Azure 上拥有 Kubernetes 集群,您可以按照相同的步骤进行操作。

如果你在跟着学习,你可以在 13-cloud/k8s 文件夹中找到本节的文件。

安装的前提条件

运行 Minikube 的必要条件如下:

  1. 1.
  2. 请访问 https://minikube.sigs.k8s.io/docs/start/ 来安装 Minikube。
  3. 2.
  4. 从 https://kubernetes.io/docs/tasks/tools/ 安装 kubectl CLI。如果您已经安装了 Docker Desktop,kubectl 通常会随安装一起提供。
  5. 3.
  6. 你可以选择从 https://k9scli.io/ 安装 K9s。这是一个基于文本的用户界面,可以帮助你可视化所有 Kubernetes 组件,而无需输入过多内容。

启动 Minikube 环境

要启动 Minikube,请确保您的系统至少有 4GB 的内存。您可以使用默认配置进行启动。

minikube start

如果你有更多内存,可以使用 --memory 参数。例如,如果你的电脑总内存为 8GB,你可以启动 minikube。

minikube start --memory 4096

还有其他参数,比如 --cpu,您可以用它来指定使用多少个 CPU 核心。要了解更多关于 minikube CLI 的信息,请查看这里的文档:https://minikube.sigs.k8s.io/docs/。

创建 Postgres 部署方案

我们的用户应用需要使用 PostgreSQL 作为数据库,因此我们需要按照接下来的说明来启动它。

配置映射表

我们需要创建用户名和密码。Kubernetes 提供了一种保存和使用它们的方法;在这种情况下,有一个名为 ConfigMap 的组件,声明如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres-config
  labels:
    app: postgres
data:
  POSTGRES_DB: users_db
  POSTGRES_USER: admin
  POSTGRES_PASSWORD: mysecretpassword

持久卷与持久卷声明

我们需要正确地存储数据。在 Kubernetes 中,PersistentVolume(PV)是实际的存储空间,类似于硬盘,而 PersistentVolumeClaim(PVC)是用户对特定存储量的请求。可以把它看作一个停车场(PV),用户在这里请求停车位(PVC)。PersistentVolume 的声明如下:

kind: PersistentVolume
apiVersion: v1
metadata:
  name: postgres-pv-volume
  labels:
    type: local
    app: postgres
spec:
  storageClassName: manual
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  hostPath:
    path: "/mnt/data"

持久卷声明如下所示:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: postgres-pv-claim
  labels:
    app: postgres
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

部署过程

在 Kubernetes 中,部署是您对 Pod 的自动驾驶,确保所需数量的实例正常运行,顺利更新,并在必要时进行回滚。PostgreSQL 的部署如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres  # Sets Deployment name
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 5432  # Exposes container port
          envFrom:
            - configMapRef:
                name: postgres-config
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: postgredb
          resources:
            limits:
              cpu: "1"
              memory: "1Gi"
            requests:
              cpu: "0.5"
              memory: "512Mi"
      volumes:
        - name: postgredb
          persistentVolumeClaim:
            claimName: postgres-pv-claim

在继续之前,请务必仔细阅读此声明。

服务内容

Kubernetes 中的服务是访问一组 Pod 的统一入口,隐藏它们的具体细节,并确保高可用性。PostgreSQL 服务如下:

apiVersion: v1
kind: Service
metadata:
  name: postgres
  labels:
    app: postgres
spec:
  type: NodePort
  ports:
    - port: 5432
  selector:
    app: postgres

安装声明文件

所有这些声明都在一个名为 postgresql.yaml 的文件中。要在您的 Kubernetes 中安装它,您可以执行以下命令:

kubectl apply -f postgresql.yaml

在 Kubernetes 中部署微服务:用户应用、我的复古应用和我的复古网关应用的部署

在本节中,我们将部署所有微服务。回顾一下,我们在 My Retro APP 和 My Retro Gateway 项目中分别使用了 spring-cloud-starter-kubernetes-client-all 和 spring-cloud-starter-kubernetes-fabric8-all 这两个依赖项。通过这些依赖项,我们可以将应用程序无缝集成到 Kubernetes 中,并在 Spring Boot 应用程序中充分利用 Kubernetes 的功能。接下来,我们将讨论其中的一些功能。

集群角色与集群角色绑定

ClusterRole 定义了集群级别的权限,而 ClusterRoleBinding 则将这些权限分配给所有命名空间中的用户或组。可以将其理解为先设置访问规则(ClusterRole),再将这些规则应用到用户(ClusterRoleBinding)上。

我们需要同时声明这两个,因为 Spring Cloud Kubernetes 的依赖项需要访问服务、端点、配置映射、Pod 等,以便在 Spring Boot 端实现无代码的简单集成。ClusterRole 的声明如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-read-role
rules:
- apiGroups:
  - ""
  resources:
  - endpoints
  - pods
  - services
  - configmaps
  verbs:
  - get
  - list
  - watch

ClusterRoleBinding 的声明如下:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-read-rolebinding
subjects:
- kind: ServiceAccount
  name: default
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-read-role
  apiGroup: rbac.authorization.k8s.io

通过这些声明,使用 Spring Cloud Kubernetes 依赖的 Spring Boot 应用(我的复古应用和我的复古网关)可以充分利用我们接下来要讨论的集成功能。

用户的部署与服务

让我们来开始用户应用的部署声明:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: users
  name: users
spec:
  replicas: 1
  selector:
    matchLabels:
      app: users
  template:
    metadata:
      labels:
        app: users
    spec:
      containers:
        - name: users
          image: felipeg48/users:latest
          ports:
          - containerPort: 8080
          env:
          - name: spring.cloud.consul.enabled
            value: "false"
          - name: spring.datasource.url
            value: "jdbc:postgresql://postgres.default.svc.cluster.local:5432/users_db"
          - name: spring.datasource.username
            valueFrom:
              configMapKeyRef:
                name: postgres-config
                key: POSTGRES_USER
          - name: spring.datasource.password
            valueFrom:
              configMapKeyRef:
                name: postgres-config
                key: POSTGRES_PASSWORD
          resources:
            limits:
              cpu: "0.5"
              memory: "768Mi"
          livenessProbe:
            httpGet:
              path: /actuator/health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 6

让我们一起审查这份声明:

  • 我们正在使用之前生成的这个镜像。您可以切换到自己的镜像,但这个镜像是公开可用的。
  • 我们在 env 部分声明环境变量。由于不打算使用 Consul 进行服务发现和配置,而是选择 Kubernetes 的默认设置,因此我们将 Consul 禁用,将该值设置为 false。
  • 该属性保存了 URL(是的,我们正在覆盖它的值),使用标准方式来定位服务。语法为 ..svc.cluster.local。我们可以直接使用 postgres,但我想向您展示 Kubernetes DNS 的用法。
  • 我们也在覆盖这些属性。我们通过使用这些键,引用之前定义的 postgres-config 的 ConfigMap。
  • livenessProbe: 我们使用这个声明来调用 /actuator/health 端点,以确保我们的应用程序处于健康状态。此外,我们还设置了一些资源限制,以帮助我们减少 Kubernetes 安装的整体资源使用。

接下来,我们来查看用户应用服务的声明:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: users
  name: users-service
spec:
  type: NodePort
  selector:
    app: users
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080

在这里,我们定义了一个 NodePort,并使用端口 80,该端口会重定向到容器内的 8080。

我的复古部署与服务

接下来,让我们来看看我的复古部署情况:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myretro
  name: myretro
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myretro
  template:
    metadata:
      labels:
        app: myretro
    spec:
      containers:
      - name: myretro
        image: felipeg48/myretro:latest
        ports:
        - containerPort: 8080
        env:
          - name: spring.cloud.consul.enabled
            value: "false"
        resources:
          limits:
            cpu: "0.5"
            memory: "768Mi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 6

我们正在使用我的镜像,但你可以更换成你的镜像。此外,我们通过将 spring.cloud.consul.enabled 属性的值设置为 false 来禁用 Consul 配置。

请记住,这个应用程序使用 OpenFeign 连接到用户服务。由于我们使用了 Spring Cloud Kubernetes 依赖,Kubernetes 在后台会处理服务发现和负载均衡,通过类似 users-service.default.svc.cluster.local 的主机来访问服务。

用户服务的声明如下所示:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: myretro
  name: my-retro-app
spec:
  type: NodePort
  selector:
      app: myretro
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080

我的复古网关配置、部署和服务

接下来,我们来看看 My Retro Gateway 的声明。首先,我们来查看 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: myretro-gateway
data:
  application.yaml: |-
    management:
      endpoint:
        gateway:
          enabled: true
      endpoints:
        web:
          exposure:
            include: "health,info,gateway,configprops,conditions,env,beans"
    spring:
      cloud:
        gateway:
          routes:
            - id: users
              uri: http://users-service
              predicates:
                - Path=/users/**
              filters:
                - name: CircuitBreaker
                  args:
                    name: users
                    fallbackUri: forward:/fallback/users
            - id: myretro
              uri: http://my-retro-app
              predicates:
                - Path=/retros/**
              filters:
                - name: CircuitBreaker
                  args:
                    name: retros
                    fallbackUri: forward:/fallback/retros

让我们一起回顾一下:

  • 在 ConfigMap 的数据部分,我们声明了一个新的 application.yaml 文件。是的,我们将覆盖镜像中所有的 application.yaml 文件。在这里,我们定义了一些新的属性,但最重要的属性如下。
  • 我们正在重新定义路由,唯一的变化是 uri 键,我们将 lb://(负载均衡器)替换为 http://。在这种情况下,我们使用 http:// 是因为负载均衡是由 Kubernetes 服务提供的,因此不需要添加 lb:// 前缀。此外,我们不再使用 Consul,而是采用 Kubernetes 的默认设置(服务发现和负载均衡)。

接下来是部署声明:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: myretro-gateway
  name: myretro-gateway
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myretro-gateway
  template:
    metadata:
      labels:
        app: myretro-gateway
    spec:
      containers:
      - name: myretro-gateway
        image: felipeg48/myretro-gateway:latest
        ports:
        - containerPort: 8080
        env:
        - name: spring.config.import
          value: "kubernetes:"
        - name: spring.cloud.consul.enabled
          value: "false"
        resources:
          limits:
            cpu: "0.5"
            memory: "768Mi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 6

在这里,我们禁用了 Consul(spring.cloud.consul.enabled=false)。我们使用了一个已知的属性 spring.config.import,值为 kubernetes://;只需添加这个,Spring Cloud Kubernetes 的自动配置就会查找与应用程序名称匹配的 ConfigMap,在这种情况下是我们之前声明的 myretro-gateway ConfigMap,并将 application.yaml 文件的新值应用到应用程序中。这真是太酷了!如果你有一个不同名称的 ConfigMap,仍然可以使用,但必须遵循一些命名约定。如果想了解更多,请访问:https://docs.spring.io/spring-cloud-kubernetes/reference/property-source-config/configmap-propertysource.html。

接下来,我们来回顾一下服务内容:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: myretro-gateway
  name: myretro-gateway-service
spec:
  type: NodePort
  selector:
    app: myretro-gateway
  ports:
  - name: http
    protocol: TCP
    port: 8080
    targetPort: 8080

我们正在使用 8080 端口。

在源代码 13-cloud/k8s 文件夹中,您可以找到一个名为 myretro.yaml 的文件,里面包含我们刚刚审查的所有声明。要应用这个文件,您可以使用以下命令:

kubectl apply -f myretro.yaml

就这样。现在我们可以使用 My Retro Gateway 服务来访问我们的应用程序。您可以通过以下命令检查 pod 是否正常运行以及服务的状态:

kubectl get pods
kubectl get services

访问我的复古网关:如何使用端口转发

接下来,我们将通过 Kubernetes 的端口转发和 myretro-gateway-service 来访问我们的应用。在终端中执行以下命令:

kubectl port-forward svc/myretro-gateway-service 8080:8080

通过这个,你可以选择打开浏览器,或者使用以下命令来访问应用程序:

curl -s http://localhost:8080/users | jq
curl -s http://localhost:8080/retros | jq
curl -s http://localhost:8080/retros/users | j

如果你想查看一些日志,首先需要获取 pod 的名称

kubectl get pods

然后,您可以查看日志记录

kubectl logs <pod-name>

就这样!现在你已经知道如何在 Kubernetes 中部署微服务了。

如果您希望使用 Consul 代替默认的 Kubernetes 服务发现、配置和负载均衡,可以使用 Consul for Kubernetes,这将使微服务的部署变得更加简单。请查看其文档: https://developer.hashicorp.com/consul/docs/k8s。

清理工作

关闭所有内容的最简单方法是执行以下命令,这将删除为 Kubernetes 创建的虚拟机

minikube delete --all --purge

如果你只是想停止它,可以使用

minikube stop

我鼓励你继续探索 Spring Cloud 提供的所有功能,不仅包括 Spring Cloud Consul 和 Spring Cloud Vault,还包括其他项目。

概要

在本章中,我们回顾了 Spring Cloud Consul 用于服务发现和配置,Spring Cloud Vault 用于管理秘密和配置,Spring Cloud Gateway 通过使用断路器模式实现路由、过滤和回退,以及一些 HashiCorp 的产品,如 Consul 和 Vault。

我们还研究了 Docker Compose 和 Kubernetes,了解了如何利用这个云平台实现多个实例、服务发现、高可用性等众多开箱即用的功能;得益于 Spring Cloud Kubernetes,集成变得更加简单。

我需要一本完整的第二本书来描述更多的 Spring Cloud 技术。在我的书《Spring Boot Messaging》(Apress,2017)中,我介绍了 Spring Cloud Stream,而在《Spring Cloud Data Flow》(Apress,2020)中,我则介绍了其他可以丰富您云计算之旅的技术。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表