其他
Kubernetes 上 Java 应用的最佳实践
在本文中,您将了解在 Kubernetes 上运行 Java 应用程序的最佳实践。大多数这些建议也适用于其他语言。但是,我正在考虑 Java 特性范围内的所有规则,并且还展示了可用于基于 JVM 的应用程序的解决方案和工具。当使用最流行的 Java 框架(如 Spring Boot 或 Quarkus)时,这些 Kubernetes 建议中的一些是设计强制的。我将向您展示如何有效地利用它们来简化开发人员的生活。
1、不要将 Limit 设置得太低
2、首先考虑内存使用
3、适当的 liveness 和 readiness 探针
3.1 介绍
3.2 配置详情
management:
endpoint:
health:
probes:
enabled: true
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
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
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
5、考虑迁移到原生编译
<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
<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、正确配置日志记录
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>7.2</version>
</dependency>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<?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、创建集成测试
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-container-image-jib</artifactId>
</dependency>
@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());
}
}
@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、最后的想法
END
往期精彩还在只用 RedisTemplate 访问 Redis 吗?
Spring 中的 @Cacheable 缓存注解,你真的了解吗?
路透社:开源软件成为贸易战的重要环节
基于 SpringBoot + Vue 实现的可视化拖拽编辑的大屏项目
关注后端面试那些事,回复【2022面经】
获取最新大厂Java面经