Kubernetes 上 Java 应用的最佳实践 2

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: Kubernetes 上 Java 应用的最佳实践 2

5、考虑迁移到原生编译

原生编译是 Java 世界中真正的“游戏规则改变者”。但我敢打赌,你们中没有多少人使用它——尤其是在生产中。当然,在将现有应用程序迁移到本机编译的过程中存在(现在仍然存在)许多挑战。GraalVM 在构建期间执行的静态代码分析可能会导致类似 ClassNotFound 或 MethodNotFound 的错误。为了克服这些挑战,我们需要提供一些提示让 GraalVM 了解代码的动态元素。这些提示的数量通常取决于库的数量和应用程序中使用的语言功能的一般数量。

像 Quarkus 或 Micronaut 这样的 Java 框架试图通过设计解决与原生编译相关的挑战。例如,他们尽可能避免使用反射。Spring Boot 还通过 Spring Native 项目大大改进了原生编译支持。因此,我在这方面的建议是,如果您要创建一个新的应用程序,请按照为本机编译做好准备的方式进行准备。例如,使用 Quarkus,您可以简单地生成一个 Maven 配置,其中包含用于构建原生可执行文件的专用配置文件。

<profiles>
  <profile>
    <id>native</id>
    <activation>
      <property>
        <name>native</name>
      </property>
    </activation>
    <properties>
      <skipITs>false</skipITs>
      <quarkus.package.type>native</quarkus.package.type>
    </properties>
  </profile>
</profiles>

添加后,您可以使用以下命令进行本机构建:

$ mvn clean package -Pnative

然后你可以分析在构建过程中是否有任何问题。即使您现在不在生产环境中运行原生应用程序(例如您的组织不批准它),您也应该将 GraalVM 编译作为您接受管道中的一个步骤。您可以使用最流行的框架轻松地为您的应用程序构建 Java 原生镜像。例如,使用 Spring Boot,您只需在 Maven pom.xml 中提供以下配置,如下所示:

<plugin>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-maven-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>build-info</goal>
        <goal>build-image</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <image>
      <builder>paketobuildpacks/builder:tiny</builder>
      <env>
        <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE>
        <BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
          --allow-incomplete-classpath
        </BP_NATIVE_IMAGE_BUILD_ARGUMENTS>
      </env>
    </image>
  </configuration>
</plugin>

6、正确配置日志记录

在编写 Java 应用程序时,日志记录可能不是您首先考虑的事情。然而,在全局范围内,它变得非常重要,因为我们需要能够收集、存储数据,并最终快速搜索和呈现特定条目。最佳做法是将应用程序日志写入标准输出 (stdout) 和标准错误 (stderr) 流。

Fluentd 是一种流行的开源日志聚合器,它允许您从 Kubernetes 集群收集日志、处理它们,然后将它们发送到您选择的数据存储后端。它与 Kubernetes 部署无缝集成。Fluentd 尝试将数据结构化为 JSON 以统一不同来源和目的地的日志记录。假设那样,最好的方法可能是以这种格式准备日志。使用 JSON 格式,我们还可以轻松地包含用于标记日志的附加字段,然后使用各种条件在可视化工具中轻松搜索它们。

为了将我们的日志格式化为 Fluentd 可读的 JSON,我们可以在 Maven 依赖项中包含 Logstash Logback 编码器库。

<dependency>
   <groupId>net.logstash.logback</groupId>
   <artifactId>logstash-logback-encoder</artifactId>
   <version>7.2</version>
</dependency>

然后我们只需要在文件 logback-spring.xml 中为我们的 Spring Boot 应用程序设置一个默认的控制台日志 Appender 。

<configuration>
    <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>
    <logger name="jsonLogger" additivity="false" level="DEBUG">
        <appender-ref ref="consoleAppender"/>
    </logger>
    <root level="INFO">
        <appender-ref ref="consoleAppender"/>
    </root>
</configuration>

我们是否应该避免使用额外的日志 appenders ,而只是将日志打印到标准输出?根据我的经验,答案是——不。您仍然可以使用其他机制来发送日志。特别是如果您使用不止一种工具来收集组织中的日志——例如 Kubernetes 上的内部堆栈和外部的全局堆栈。就个人而言,我正在使用一种工具来帮助我解决性能问题,例如消息代理作为代理。在 Spring Boot 中,我们可以轻松地使用 RabbitMQ。只需包括以下 starter:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

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

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <springProperty name="destination" source="app.amqp.url" />
  <appender name="AMQP"
    class="org.springframework.amqp.rabbit.logback.AmqpAppender">
    <layout>
      <pattern>
{
  "time": "%date{ISO8601}",
  "thread": "%thread",
  "level": "%level",
  "class": "%logger{36}",
  "message": "%message"
}
      </pattern>
    </layout>
    <addresses>${destination}</addresses>  
    <applicationId>api-service</applicationId>
    <routingKeyPattern>logs</routingKeyPattern>
    <declareExchange>true</declareExchange>
    <exchangeName>ex_logstash</exchangeName>
  </appender>
  <root level="INFO">
    <appender-ref ref="AMQP" />
  </root>
</configuration>

7、创建集成测试

好的,我知道——它与 Kubernetes 没有直接关系。但是由于我们使用 Kubernetes 来管理和编排容器,我们还应该对容器进行集成测试。幸运的是,使用 Java 框架,我们可以大大简化该过程。例如,Quarkus 允许我们用 @QuarkusIntegrationTest 注释测试。结合 Quarkus 容器构建功能,它是一个非常强大的解决方案。我们可以针对包含该应用程序的已构建镜像运行测试。首先,让我们包含 Quarkus Jib 模块:

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-container-image-jib</artifactId>
</dependency>

然后我们必须通过在 application.properties 文件中将 quarkus.container-image.build 属性设置为 true 来启用容器构建。在测试类中,我们可以使用 @TestHTTPResource 和 @TestHTTPEndpoint 注解注入测试服务器 URL。然后我们使用 RestClientBuilder 创建一个客户端并调用在容器上启动的服务。测试类的名字不是偶然的。为了被自动检测为集成测试,它有 IT 后缀。

@QuarkusIntegrationTest
public class EmployeeControllerIT {
    @TestHTTPEndpoint(EmployeeController.class)
    @TestHTTPResource
    URL url;
    @Test
    void add() {
        EmployeeService service = RestClientBuilder.newBuilder()
                .baseUrl(url)
                .build(EmployeeService.class);
        Employee employee = new Employee(1L, 1L, "Josh Stevens", 
                                         23, "Developer");
        employee = service.add(employee);
        assertNotNull(employee.getId());
    }
    @Test
    public void findAll() {
        EmployeeService service = RestClientBuilder.newBuilder()
                .baseUrl(url)
                .build(EmployeeService.class);
        Set<Employee> employees = service.findAll();
        assertTrue(employees.size() >= 3);
    }
    @Test
    public void findById() {
        EmployeeService service = RestClientBuilder.newBuilder()
                .baseUrl(url)
                .build(EmployeeService.class);
        Employee employee = service.findById(1L);
        assertNotNull(employee.getId());
    }
}

您可以在我之前关于使用 Quarkus 进行高级测试的文章中找到有关该过程的更多详细信息。最终效果如下图所示。当我们在构建期间使用 mvn clean verify 命令运行测试时,我们的测试在构建容器镜像后执行。

该 Quarkus 功能基于 Testcontainers 框架。我们还可以将 Testcontainer 与 Spring Boot 一起使用。这是 Spring REST 应用程序及其与 PostgreSQL 数据库集成的示例测试。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PersonControllerTests {
    @Autowired
    TestRestTemplate restTemplate;
    @Container
    static PostgreSQLContainer<?> postgres = 
       new PostgreSQLContainer<>("postgres:15.1")
            .withExposedPorts(5432);
    @DynamicPropertySource
    static void registerMySQLProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }
    @Test
    @Order(1)
    void add() {
        Person person = 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)
    void updateAndGet() {
        final Integer id = 1;
        Person person = Instancio.of(Person.class)
                .set(Select.field("id"), id)
                .create();
        restTemplate.put("/persons", person);
        Person updated = restTemplate.getForObject("/persons/{id}", Person.class, id);
        Assertions.assertNotNull(updated);
        Assertions.assertNotNull(updated.getId());
        Assertions.assertEquals(id, updated.getId());
    }
}

8、最后的想法

我希望这篇文章能帮助您在 Kubernetes 上运行 Java 应用程序时避免一些常见的陷阱。将其视为我在类似文章中找到的其他人的建议以及我在该领域的个人经验的总结。



相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
1月前
|
人工智能 算法 Java
Java与AI驱动区块链:构建智能合约与去中心化AI应用
区块链技术和人工智能的融合正在开创去中心化智能应用的新纪元。本文深入探讨如何使用Java构建AI驱动的区块链应用,涵盖智能合约开发、去中心化AI模型训练与推理、数据隐私保护以及通证经济激励等核心主题。我们将完整展示从区块链基础集成、智能合约编写、AI模型上链到去中心化应用(DApp)开发的全流程,为构建下一代可信、透明的智能去中心化系统提供完整技术方案。
209 3
|
1月前
|
消息中间件 缓存 Java
Spring框架优化:提高Java应用的性能与适应性
以上方法均旨在综合考虑Java Spring 应该程序设计原则, 数据库交互, 编码实践和系统架构布局等多角度因素, 旨在达到高效稳定运转目标同时也易于未来扩展.
113 8
|
2月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
401 12
|
2月前
|
监控 Kubernetes Java
使用 New Relic APM 和 Kubernetes Metrics 监控 EKS 上的 Java 微服务
在阿里云AKS上运行Java微服务常遇性能瓶颈与OOMKilled等问题。本文教你通过New Relic实现集群与JVM双层监控,集成Helm部署、JVM代理注入、GC调优及告警仪表盘,打通从节点资源到应用内存的全链路观测,提升排障效率,保障服务稳定。
180 1
|
2月前
|
安全 Java API
Java SE 与 Java EE 区别解析及应用场景对比
在Java编程世界中,Java SE(Java Standard Edition)和Java EE(Java Enterprise Edition)是两个重要的平台版本,它们各自有着独特的定位和应用场景。理解它们之间的差异,对于开发者选择合适的技术栈进行项目开发至关重要。
331 1
存储 jenkins 持续交付
446 2
|
存储 设计模式 运维
YAML 管理 Kubernetes 应用
YAML 管理 Kubernetes 应用
484 1
|
存储 设计模式 运维
如何不编写 YAML 管理 Kubernetes 应用?
Kubernetes 将自身边界内的事物都抽象为资源。其中的主要部分,是以 Deployment、StatefulSet 为代表的 workload 工作负载控制器,其他各类资源都围绕这些主要的资源工作。这些资源合并起来,可以为 IT 技术工作者展现出一个以 workload 为中心的模型。Kubernetes 中所有的资源,都通过声明式配置文件来编辑描述,一条条的 Yaml 字段定义,给了 IT 技术人员最大的自由度的同时,也对技术人员的能力提出了极高的要求。
|
Kubernetes Shell 开发工具
使用 Kustomize 帮你管理 kubernetes 应用(二): Kustomize 的使用方法
本篇为系列文章第二篇,手把手教你使用 Kustomize 的两种方式。
4830 0
|
Kubernetes 容器
使用 Kustomize 帮你管理 kubernetes 应用(一):什么是 Kustomize ?
本篇为系列文章第一篇,介绍我对 Kustomize 的了解过程以及 Kustomize 是什么,为什么它能解决我的燃眉之急。
4333 0

热门文章

最新文章

推荐镜像

更多