<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Kubernetes上Java應用的最佳實踐

Android編程精選 ? 來源:piotrminkowski.com ? 2023-03-14 17:47 ? 次閱讀

在本文中,您將了解在 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 秒以下開始:

38d8c8d8-bf62-11ed-bfe3-dac502259ad0.png

將 CPU limit 設置為 500 millicores ,它開始大約 30 秒:

38e6be84-bf62-11ed-bfe3-dac502259ad0.png

當然,我們可以找到一些例子。但我們也會在下一節中討論它們。

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 。

38f4e7d4-bf62-11ed-bfe3-dac502259ad0.png

我的應用程序可以嗎?我至少應該執行一些負載測試來驗證我的應用程序在高流量下的性能。但再一次 - 不要將 limit 設置得太低。例如,對于 1024M 限制, -Xmx 等于 650M。

390fd792-bf62-11ed-bfe3-dac502259ad0.png

如您所見,我們使用 JVM 參數處理內存使用情況。它可以防止我們在第一節提到的文章中描述的 OOM kills 。因此,將 request 設置為與 limit 相同的級別并沒有太大意義。我建議將其設置為比正常使用高一點——比方說多 20%。

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//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 基礎鏡像。然而,目前,鏡像網站上的公告稱它已被正式棄用,所有用戶都應該找到合適的替代品。我想這可能會讓人很困惑,所以你會在這里找到對原因的詳細解釋。

3921e086-bf62-11ed-bfe3-dac502259ad0.png

好吧,讓我們考慮一下我們應該選擇哪個備選方案。不同的供應商提供多種替代品。如果您正在尋找它們之間的詳細比較,您應該訪問以下站點。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 性能。

5、考慮遷移到原生編譯

原生編譯是 Java 世界中真正的“游戲規則改變者”。但我敢打賭,你們中沒有多少人使用它——尤其是在生產中。當然,在將現有應用程序遷移到本機編譯的過程中存在(現在仍然存在)許多挑戰。GraalVM 在構建期間執行的靜態代碼分析可能會導致類似 ClassNotFound 或 MethodNotFound 的錯誤。為了克服這些挑戰,我們需要提供一些提示讓 GraalVM 了解代碼的動態元素。這些提示的數量通常取決于庫的數量和應用程序中使用的語言功能的一般數量。

像 Quarkus 或 Micronaut 這樣的 Java 框架試圖通過設計解決與原生編譯相關的挑戰。例如,他們盡可能避免使用反射。Spring Boot 還通過 Spring Native 項目大大改進了原生編譯支持。因此,我在這方面的建議是,如果您要創建一個新的應用程序,請按照為本機編譯做好準備的方式進行準備。例如,使用 Quarkus,您可以簡單地生成一個 Maven 配置,其中包含用于構建原生可執行文件的專用配置文件。



native


native



false
native



添加后,您可以使用以下命令進行本機構建:

$mvncleanpackage-Pnative

然后你可以分析在構建過程中是否有任何問題。即使您現在不在生產環境中運行原生應用程序(例如您的組織不批準它),您也應該將 GraalVM 編譯作為您接受管道中的一個步驟。您可以使用最流行的框架輕松地為您的應用程序構建 Java 原生鏡像。例如,使用 Spring Boot,您只需在 Maven pom.xml 中提供以下配置,如下所示:


org.springframework.boot
spring-boot-maven-plugin



build-info
build-image





paketobuildpacks/builder:tiny

true

--allow-incomplete-classpath





6、正確配置日志記錄

在編寫 Java 應用程序時,日志記錄可能不是您首先考慮的事情。然而,在全局范圍內,它變得非常重要,因為我們需要能夠收集、存儲數據,并最終快速搜索和呈現特定條目。最佳做法是將應用程序日志寫入標準輸出 (stdout) 和標準錯誤 (stderr) 流。Fluentd 是一種流行的開源日志聚合器,它允許您從 Kubernetes 集群收集日志、處理它們,然后將它們發送到您選擇的數據存儲后端。它與 Kubernetes 部署無縫集成。

Fluentd 嘗試將數據結構化為 JSON 以統一不同來源和目的地的日志記錄。假設那樣,最好的方法可能是以這種格式準備日志。使用 JSON 格式,我們還可以輕松地包含用于標記日志的附加字段,然后使用各種條件在可視化工具中輕松搜索它們。

為了將我們的日志格式化為 Fluentd 可讀的 JSON,我們可以在 Maven 依賴項中包含 Logstash Logback 編碼器庫。


net.logstash.logback
logstash-logback-encoder
7.2

然后我們只需要在文件 logback-spring.xml 中為我們的 Spring Boot 應用程序設置一個默認的控制臺日志 Appender 。












我們是否應該避免使用額外的日志 appenders ,而只是將日志打印到標準輸出?根據我的經驗,答案是——不。您仍然可以使用其他機制來發送日志。特別是如果您使用不止一種工具來收集組織中的日志——例如 Kubernetes 上的內部堆棧和外部的全局堆棧。

就個人而言,我正在使用一種工具來幫助我解決性能問題,例如消息代理作為代理。在 Spring Boot 中,我們可以輕松地使用 RabbitMQ。只需包括以下 starter:


org.springframework.boot
spring-boot-starter-amqp

然后你需要在 logback-spring.xml 中提供一個類似的 appender 配置:









{
"time":"%date{ISO8601}",
"thread":"%thread",
"level":"%level",
"class":"%logger{36}",
"message":"%message"
}



${destination}
api-service
logs
true
ex_logstash








7、創建集成測試

好的,我知道——它與 Kubernetes 沒有直接關系。但是由于我們使用 Kubernetes 來管理和編排容器,我們還應該對容器進行集成測試。幸運的是,使用 Java 框架,我們可以大大簡化該過程。

例如,Quarkus 允許我們用 @QuarkusIntegrationTest 注釋測試。結合 Quarkus 容器構建功能,它是一個非常強大的解決方案。我們可以針對包含該應用程序的已構建鏡像運行測試。首先,讓我們包含 Quarkus Jib 模塊:


io.quarkus
quarkus-container-image-jib

然后我們必須通過在 application.properties 文件中將 quarkus.container-image.build 屬性設置為 true 來啟用容器構建。在測試類中,我們可以使用 @TestHTTPResource 和 @TestHTTPEndpoint 注解注入測試服務器 URL。

然后我們使用 RestClientBuilder 創建一個客戶端并調用在容器上啟動的服務。測試類的名字不是偶然的。為了被自動檢測為集成測試,它有 IT 后綴。

@QuarkusIntegrationTest
publicclassEmployeeControllerIT{

@TestHTTPEndpoint(EmployeeController.class)
@TestHTTPResource
URLurl;

@Test
voidadd(){
EmployeeServiceservice=RestClientBuilder.newBuilder()
.baseUrl(url)
.build(EmployeeService.class);
Employeeemployee=newEmployee(1L,1L,"JoshStevens",
23,"Developer");
employee=service.add(employee);
assertNotNull(employee.getId());
}

@Test
publicvoidfindAll(){
EmployeeServiceservice=RestClientBuilder.newBuilder()
.baseUrl(url)
.build(EmployeeService.class);
Setemployees=service.findAll();
assertTrue(employees.size()>=3);
}

@Test
publicvoidfindById(){
EmployeeServiceservice=RestClientBuilder.newBuilder()
.baseUrl(url)
.build(EmployeeService.class);
Employeeemployee=service.findById(1L);
assertNotNull(employee.getId());
}
}

您可以在我之前關于使用 Quarkus 進行高級測試的文章中找到有關該過程的更多詳細信息。最終效果如下圖所示。當我們在構建期間使用 mvn clean verify 命令運行測試時,我們的測試在構建容器鏡像后執行。

3930f4ae-bf62-11ed-bfe3-dac502259ad0.png

該 Quarkus 功能基于 Testcontainers 框架。我們還可以將 Testcontainer 與 Spring Boot 一起使用。這是 Spring REST 應用程序及其與 PostgreSQL 數據庫集成的示例測試。

@SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
publicclassPersonControllerTests{

@Autowired
TestRestTemplaterestTemplate;

@Container
staticPostgreSQLContainerpostgres=
newPostgreSQLContainer<>("postgres:15.1")
.withExposedPorts(5432);

@DynamicPropertySource
staticvoidregisterMySQLProperties(DynamicPropertyRegistryregistry){
registry.add("spring.datasource.url",postgres::getJdbcUrl);
registry.add("spring.datasource.username",postgres::getUsername);
registry.add("spring.datasource.password",postgres::getPassword);
}

@Test
@Order(1)
voidadd(){
Personperson=Instancio.of(Person.class)
.ignore(Select.field("id"))
.create();
person=restTemplate.postForObject("/persons",person,Person.class);
Assertions.assertNotNull(person);
Assertions.assertNotNull(person.getId());
}

@Test
@Order(2)
voidupdateAndGet(){
finalIntegerid=1;
Personperson=Instancio.of(Person.class)
.set(Select.field("id"),id)
.create();
restTemplate.put("/persons",person);
Personupdated=restTemplate.getForObject("/persons/{id}",Person.class,id);
Assertions.assertNotNull(updated);
Assertions.assertNotNull(updated.getId());
Assertions.assertEquals(id,updated.getId());
}

}

8、最后的想法

我希望這篇文章能幫助您在 Kubernetes 上運行 Java 應用程序時避免一些常見的陷阱。將其視為我在類似文章中找到的其他人的建議以及我在該領域的個人經驗的總結。

作者:Piotr

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • cpu
    cpu
    +關注

    關注

    68

    文章

    10512

    瀏覽量

    207253
  • 內存
    +關注

    關注

    8

    文章

    2790

    瀏覽量

    72954
  • JAVA
    +關注

    關注

    19

    文章

    2909

    瀏覽量

    103199
  • 容器
    +關注

    關注

    0

    文章

    482

    瀏覽量

    21923
  • kubernetes
    +關注

    關注

    0

    文章

    219

    瀏覽量

    8596

原文標題:Kubernetes 上 Java 應用的最佳實踐

文章出處:【微信號:AndroidPush,微信公眾號:Android編程精選】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    [轉帖]java項目實踐視頻

    java項目實踐視頻  java私塾推出的項目視頻,感覺很不錯,其中需求分析,系統架構設計很合理。對正在做畢業設計的同學有很大幫助。 課程大綱: &
    發表于 05-17 14:47

    全新java初學者實踐教程

    java 這套技術。要想掌握這套技術實踐是非常重要的。那么很多初學者,在第一步實踐的時候就遇到了困難,就是配置環境變量。以至于,因無法繼續實踐而苦惱。下面為了幫廣大愛好者解決這個問題,
    發表于 08-07 22:24

    C編程最佳實踐.doc

    C編程最佳實踐.doc
    發表于 08-17 14:37

    Kubernetes之路 1 - Java應用資源限制的迷思

    摘要: 隨著容器技術的成熟,越來越多的企業客戶在企業中選擇Docker和Kubernetes作為應用平臺的基礎。然而在實踐過程中,還會遇到很多具體問題。本文分析并解決了Java應用在容器
    發表于 03-29 13:06

    Kubernetes Ingress 高可靠部署最佳實踐

    摘要: 在Kubernetes集群中,Ingress作為集群流量接入層,Ingress的高可靠性顯得尤為重要,今天我們主要探討如何部署一套高性能高可靠的Ingress接入層。簡介
    發表于 04-17 14:35

    Dockerfile的最佳實踐

    ”微服務一條龍“最佳指南-“最佳實踐”篇:Dockerfile
    發表于 07-11 16:22

    變量聲明最佳實踐?

    所以我們開始編寫32位和16位代碼,并過渡到MPLAB X和XC編譯器。我想到的一個主題是聲明變量的最佳實踐。常規IpType。h或類型。h pr STDIN?;騃t8或字節char等任何想法,走哪條路?
    發表于 09-30 12:01

    Kubernetes Dashboard實踐學習

    關于Kubernetes Dashboard的實踐學習
    發表于 04-10 14:09

    虛幻引擎的紋理最佳實踐

    紋理是游戲不可或缺的一部分。 這是一個藝術家可以直接控制的領域,以提高游戲的性能。 本最佳實踐指南介紹了幾種紋理優化,這些優化可以幫助您的游戲運行得更流暢、看起來更好。 最佳實踐系列指
    發表于 08-28 06:39

    全新java基礎實踐教程.chm

    全新java基礎實踐教程:最近我發現不少初學者,學習java的時候,看了好多java的歷史、優點和應用范圍。對于這些知識,并不難理解。我也當然同意j
    發表于 12-08 10:33 ?0次下載

    10個Java編程中異常處理最佳實踐

    這里是我收集的10個Java編程中進行異常處理的10最佳實踐。在Java編程中對于檢查異常有褒有貶,強制處理異常是一門語言的功能。在本文中,我們將盡量減少使用檢查型異常,同時學會在
    的頭像 發表于 05-03 17:49 ?1785次閱讀

    JAVA并發編程實踐

    JAVA并發編程實踐資料免費下載。
    發表于 06-01 15:31 ?14次下載

    教你們Kubernetes五層的安全的最佳實踐

    Kubernetes)也是黑客們的熱門目標,如果它們沒有得到有效的保護,它們可能會使你的整個環境面臨風險。在本文中,我們將討論容器堆棧每一層安全的最佳實踐。 了解容器安全的含義很重要。作為依賴共享內核的應用程序層構造,容器可以比
    的頭像 發表于 07-09 10:13 ?1164次閱讀

    最常用的11款Kubernetes工具

    我看來,Kubernetes 最重要的是將最佳實踐整合到了一個系統中,這個系統可以從樹莓派(Raspberry Pi)擴展到財富 500 強中最大的基礎設施
    的頭像 發表于 08-23 10:43 ?1930次閱讀

    Kubernetes Operator最佳實踐介紹

    kubernetes operator是通過連接主API并watch時間的一組進程,一般會watch有限的資源類型。
    的頭像 發表于 04-19 09:16 ?704次閱讀
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>