Kubernetes 上 Java 应用的最佳实践

简介: Kubernetes 上 Java 应用的最佳实践



在本文中,您将了解在 Kubernetes 上运行 Java 应用程序的最佳实践。大多数这些建议也适用于其他语言。但是,我正在考虑 Java 特性范围内的所有规则,并且还展示了可用于基于 JVM 的应用程序的解决方案和工具。当使用最流行的 Java 框架(如 Spring Boot 或 Quarkus)时,这些 Kubernetes 建议中的一些是设计强制的。我将向您展示如何有效地利用它们来简化开发人员的生活。

1、不要将 Limit 设置得太低

我们是否应该为 Kubernetes 上的 Java 应用设置 limit ?答案似乎显而易见。有许多工具可以验证您的 Kubernetes YAML 清单,如果您没有设置 CPU 或内存 limit ,它们肯定会打印警告。不过,社区对此也有一些“热议”。这是一篇有趣的文章,不建议设置任何 CPU limit 。这是另一篇文章,作为对上一篇文章的对比,他们考虑 CPU limit 。但我们也可以针对内存 limit 开始类似的讨论。特别是在 Java 应用程序的上下文中。

然而,对于内存管理,这个命题似乎大不相同。让我们阅读另一篇文章——这次是关于内存 limit 和 request 的。简而言之,它建议始终设置内存 limit。此外,限制应与 request 相同。在 Java 应用程序的上下文中,我们可以使用 -Xmx 、 -XX:MaxMetaspaceSize 或 -XX:ReservedCodeCacheSize 等 JVM 参数限制内存也很重要。无论如何,从 Kubernetes 的角度来看,pod 接收它 request 的资源。Limit 与它无关。

这一切让我得出了今天的第一个建议—A—不要将你的 limit 设置得太低。即使您设置了 CPU limit ,也不应该影响您的应用程序。例如,您可能知道,即使您的 Java 应用程序在正常工作中不会消耗太多 CPU,但它需要大量 CPU 才能快速启动。对于我在 Kubernetes 上连接 MongoDB 的简单 Spring Boot 应用程序,无限制和甚至 0.5 核之间的差异是显着的。通常它在 10 秒以下开始:

将 CPU limit 设置为 500 millicores ,它开始大约 30 秒:

当然,我们可以找到一些例子。但我们也会在下一节中讨论它们。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

2、首先考虑内存使用

让我们只关注内存 limit 。如果您在 Kubernetes 上运行 Java 应用程序,则有两个级别的最大使用 limit :容器和 JVM。但是,如果您没有为 JVM 指定任何设置,也有一些默认值。如果您不设置 -Xmx 参数,JVM 会将其最大堆大小设置为可用 RAM 的大约 25%。该值是根据容器内可见的内存计算的。一旦您不在容器级别设置 limit ,JVM 将看到节点的整个内存。

在 Kubernetes 上运行应用程序之前,您至少应该测量它在预期负载下消耗了多少内存。幸运的是,有一些工具可以优化在容器中运行的 Java 应用程序的内存配置。例如,Paketo Buildpacks 带有内置内存计算器,它使用公式 Heap = 总容器内存 - Non-Heap - Headroom 计算 JVM 的 -Xmx 参数。另一方面,非堆值是使用以下公式计算的:Non-Heap = Direct Memory + Metaspace + Reserved Code Cache + (Thread Stack * Thread Count) 。

Paketo Buildpacks 目前是构建 Spring Boot 应用程序的默认选项(使用 mvn spring-boot:build-image 命令)。让我们为我们的示例应用程序尝试一下。假设我们将内存限制设置为 512M,它将在 130M 的级别计算 -Xmx 。

我的应用程序可以吗?我至少应该执行一些负载测试来验证我的应用程序在高流量下的性能。但再一次 - 不要将 limit 设置得太低。例如,对于 1024M 限制, -Xmx 等于 650M。

如您所见,我们使用 JVM 参数处理内存使用情况。它可以防止我们在第一节提到的文章中描述的 OOM kills 。因此,将 request 设置为与 limit 相同的级别并没有太大意义。我建议将其设置为比正常使用高一点——比方说多 20%。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

3、适当的 liveness 和 readiness 探针

3.1 介绍

了解 Kubernetes 中的 liveness 和 readiness 探针之间的区别至关重要。如果这两个探针都没有仔细实施,它们可能会降低服务的整体运行,例如导致不必要的重启。第三种类型的探针,启动探针,是 Kubernetes 中一个相对较新的特性。它允许我们避免在 liveness 或 readiness 探针上设置 initialDelaySeconds ,因此如果您的应用程序启动需要很长时间,它特别有用。有关 Kubernetes 探针的一般和最佳实践的更多详细信息,我可以推荐那篇非常有趣的文章。

Liveness 探针用于决定是否重启容器。如果应用程序因任何原因不可用,有时重启容器是有意义的。另一方面,readiness 探针用于确定容器是否可以处理传入流量。如果一个 pod 被识别为未就绪,它将被从负载平衡中移除。readiness 探针失败不会导致 pod 重启。Web 应用程序最典型的 liveness 或 readiness 探针是通过 HTTP 端点实现的。

由于 liveness 探针的后续失败会导致 pod 重新启动,因此它不应检查您的应用程序集成的可用性。这些事情应该由 readiness 验证。

3.2 配置详情

好消息是,最流行的 Java 框架(如 Spring Boot 或 Quarkus)提供了两种 Kubernetes 探针的自动配置实现。他们遵循最佳实践,因此我们通常不必了解基础知识。但是,在 Spring Boot 中,除了包含 Actuator 模块之外,您还需要使用以下属性启用它们:

management:
  endpoint: 
    health:
      probes:
        enabled: true

由于 Spring Boot Actuator 提供了多个端点(例如 metric、 trace),因此最好将其公开在与默认端口不同的端口(通常为 8080 )。当然,同样的规则也适用于其他流行的 Java 框架。另一方面,一个好的做法是检查您的主要应用程序端口——尤其是在 readiness 探针中。因为它定义了我们的应用程序是否准备好处理传入的请求,所以它也应该在主端口上监听。它与 liveness probe 看起来正好相反。如果整个工作线程池都很忙,我不想重新启动我的应用程序。我只是不想在一段时间内收到传入流量。

我们还可以自定义 Kubernetes 探针的其他方面。假设我们的应用程序连接到外部系统,但我们没有在我们的 readiness 探针中验证该集成。它并不重要,不会对我们的运营状态产生直接影响。这是一个配置,它允许我们在探针中仅包含选定的集成集 (1),并在主服务器端口上公开 readiness 情况 (2) 。

spring:
  application:
    name: sample-spring-boot-on-kubernetes
  data:
    mongodb:
      host: ${MONGO_URL}
      port: 27017
      username: ${MONGO_USERNAME}
      password: ${MONGO_PASSWORD}
      database: ${MONGO_DATABASE}
      authentication-database: admin
management:
  endpoint.health:
    show-details: always
    group:
      readiness:
        include: mongo # (1)
        additional-path: server:/readiness # (2)
    probes:
      enabled: true
  server:
    port: 8081

几乎没有任何应用可以不依赖外部解决方案(如数据库、消息代理或其他应用程序)。在配置 readiness 探针时,我们应该仔细考虑到该系统的连接设置。首先你应该考虑外部服务不可用的情况。你将如何处理?我建议将这些超时减少到较低的值,如下所示。

spring:
  application:
    name: sample-spring-kotlin-microservice
  datasource:
    url: jdbc:postgresql://postgres:5432/postgres
    username: postgres
    password: postgres123
    hikari:
      connection-timeout: 2000
      initialization-fail-timeout: 0
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect
  rabbitmq:
    host: rabbitmq
    port: 5672
    connection-timeout: 2000

4、选择合适的 JDK

如果您已经使用 Dockerfile 构建了镜像,那么您可能使用的是来自 Docker Hub 的官方 OpenJDK 基础镜像。然而,目前,镜像网站上的公告称它已被正式弃用,所有用户都应该找到合适的替代品。我想这可能会让人很困惑,所以你会在这里找到对原因的详细解释。

好吧,让我们考虑一下我们应该选择哪个备选方案。不同的供应商提供多种替代品。如果您正在寻找它们之间的详细比较,您应该访问以下站点。17版本推荐使用 Eclipse Temurin。

另一方面,Jib 或 Cloud Native Buildpacks 等最流行的镜像构建工具会自动为您选择供应商。默认情况下,Jib 使用 Eclipse Temurin,而 Paketo Buildpacks 使用 Bellsoft Liberica 实现。当然,您可以轻松地覆盖这些设置。我认为,例如,如果您在与 JDK 提供程序(如 AWS 和 Amazon Corretto)匹配的环境中运行您的应用程序,这可能是有意义的。

假设我们使用 Paketo Buildpacks 和 Skaffold 在 Kubernetes 上部署 Java 应用程序。为了将默认的 Bellsoft Liberica buildpack 替换为另一个,我们只需要在 buildpacks 部分中逐字设置它。下面是一个利用 Amazon Corretto buildpack 的示例。

apiVersion: skaffold/v2beta22
kind: Config
metadata:
  name: sample-spring-boot-on-kubernetes
build:
  artifacts:
    - image: piomin/sample-spring-boot-on-kubernetes
      buildpacks:
        builder: paketobuildpacks/builder:base
        buildpacks:
          - paketo-buildpacks/amazon-corretto
          - paketo-buildpacks/java
        env:
          - BP_JVM_VERSION=17

我们还可以使用不同的 JDK 供应商轻松测试我们的应用程序的性能。如果您正在寻找此类比较的示例,您可以阅读我描述此类测试和结果的文章。我使用几个可用的 Paketo Java 构建包测量了与 Mongo 数据库交互的 Spring Boot 3 应用程序的不同 JDK 性能。

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1天前
|
Kubernetes 持续交付 开发工具
阿里云协同万兴科技落地ACK One GitOps方案,全球多机房应用自动化发布,效率提升50%
阿里云协同万兴科技落地ACK One GitOps方案,全球多机房应用自动化发布,效率提升50%
|
29天前
|
存储 监控 对象存储
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
针对本地存储和 PVC 这两种容器存储使用方式,我们对 ACK 的容器存储监控功能进行了全新升级。此次更新完善了对集群中不同存储类型的监控能力,不仅对之前已有的监控大盘进行了优化,还针对不同的云存储类型,上线了全新的监控大盘,确保用户能够更好地理解和管理容器业务应用的存储资源。
117 21
|
30天前
|
Java 编译器 开发者
Java中的this关键字详解:深入理解与应用
本文深入解析了Java中`this`关键字的多种用法
127 9
|
30天前
|
Java 应用服务中间件 API
【潜意识Java】javaee中的SpringBoot在Java 开发中的应用与详细分析
本文介绍了 Spring Boot 的核心概念和使用场景,并通过一个实战项目演示了如何构建一个简单的 RESTful API。
41 5
|
30天前
|
人工智能 自然语言处理 搜索推荐
【潜意识Java】了解并详细分析Java与AIGC的结合应用和使用方式
本文介绍了如何将Java与AIGC(人工智能生成内容)技术结合,实现智能文本生成。
61 5
|
30天前
|
SQL Java 数据库连接
【潜意识Java】深入理解MyBatis,从基础到高级的深度细节应用
本文详细介绍了MyBatis,一个轻量级的Java持久化框架。内容涵盖MyBatis的基本概念、配置与环境搭建、基础操作(如创建实体类、Mapper接口及映射文件)以及CRUD操作的实现。此外,还深入探讨了高级特性,包括动态SQL和缓存机制。通过代码示例,帮助开发者更好地掌握MyBatis的使用技巧,提升数据库操作效率。总结部分强调了MyBatis的优势及其在实际开发中的应用价值。
32 1
|
1月前
|
存储 监控 对象存储
ACK容器监控存储全面更新:让您的应用运行更稳定、更透明
介绍升级之后的ACK容器监控体系,包括各大盘界面展示和概要介绍。
|
1月前
|
缓存 Kubernetes Docker
GitLab Runner 全面解析:Kubernetes 环境下的应用
GitLab Runner 是 GitLab CI/CD 的核心组件,负责执行由 `.gitlab-ci.yml` 定义的任务。它支持多种执行方式(如 Shell、Docker、Kubernetes),可在不同环境中运行作业。本文详细介绍了 GitLab Runner 的基本概念、功能特点及使用方法,重点探讨了流水线缓存(以 Python 项目为例)和构建镜像的应用,特别是在 Kubernetes 环境中的配置与优化。通过合理配置缓存和镜像构建,能够显著提升 CI/CD 流水线的效率和可靠性,助力开发团队实现持续集成与交付的目标。
|
JSON Kubernetes 安全
使用 Java 操作 Kubernetes API
使用 Java 操作 Kubernetes API
4392 0
使用 Java 操作 Kubernetes API
|
4天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
49 14

热门文章

最新文章