【Kubernetes】入门-部署Spring应用

Kubernetes

基本概念

Pod

是什么: Kubernetes 中最小的、最简单的部署和管理单元。

类比: 一台“逻辑主机”或一个“虚拟机实例”。

关键点

  • 一个 Pod 可以包含一个或多个紧密相关的容器(例如,一个应用容器和一个日志收集 sidecar 容器)。
  • 这些容器共享网络命名空间(拥有相同的 IP 地址和端口空间)、存储卷和其他资源。
  • Pod 是短暂的,会被频繁地创建和销毁

Deployment

是什么: 用于部署和管理 Pod 副本集的声明式定义。

解决的问题: 如何确保指定数量的、完全相同的 Pod 副本始终在运行?如何无缝更新应用(滚动更新)?如何在更新出问题时快速回滚?

关键点

  • 你定义一个“期望状态”(例如,需要运行 3 个 Nginx Pod),Deployment 控制器会确保实际状态始终匹配这个期望状态。

  • 它是实现无状态应用部署的首选控制器。

Service

是什么: 一个稳定的网络端点,用于访问一组 Pod。

解决的问题: Pod 是短暂的,IP 地址会变。客户端如何稳定地找到并访问这些动态变化的 Pod?

关键点

  • Service 提供一个固定的 虚拟 IP (ClusterIP) 和 DNS 名称。

  • 通过 标签选择器 (Label Selector) 来匹配一组 Pod,并将流量负载均衡到这些 Pod 上。

  • 常见的类型:

    • ClusterIP: 默认类型,仅在集群内部可访问。

    • NodePort: 在每个节点上开放一个静态端口,从而可以从集群外部访问。

    • LoadBalancer: 使用云提供商的负载均衡器,向外部公开服务

Volume (存储卷)

是什么: 允许 Pod 中的容器持久化存储数据。

解决的问题: 容器内部的文件系统是临时的,容器重启后数据会丢失。如何持久化保存数据(如数据库文件、日志)?

关键点

  • 存储卷的生命周期独立于 Pod。即使 Pod 被销毁,存储卷中的数据依然存在。

  • 支持多种后端存储类型:本地存储、云存储(如 AWS EBS、GCE PD)、NFS、分布式存储(如 Ceph)等。

Namespace (命名空间)

是什么: 在物理集群内部创建的虚拟集群,用于实现资源隔离和分组。

解决的问题: 在有多个团队或项目的大型集群中,如何避免资源(如 Pod、Service 名称)冲突?如何实现资源配额管理?

关键点

  • 默认有 default, kube-system (系统组件), kube-public 等命名空间。

  • 可以为每个命名空间设置资源配额,限制其能使用的 CPU、内存等资源总量。

ConfigMap & Secret

  • ConfigMap: 用于将非机密的配置数据(如配置文件、环境变量、命令行参数)与容器镜像解耦。让你可以不重写镜像就能改变应用配置。

  • Secret: 与 ConfigMap 类似,但专门用于存储敏感信息,如密码、OAuth 令牌、SSH 密钥。数据会以 Base64 编码(非加密,需额外措施保障安全)存储。

使用K8S部署Spring Boot应用

前置条件,docker和kubernetes已安装配置

参考:macOS上优雅运行Docker容器

准备一个Spring Boot应用

SpringK8sDemoApplication

@SpringBootApplication @RestController public class SpringK8sDemoApplication {  	private static Date firstTime; 	private static SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  	public static void main(String[] args) { 		SpringApplication.run(SpringK8sDemoApplication.class, args); 	}  	@GetMapping("/") 	public String hello() { 		if (firstTime == null) { 			firstTime = new Date(); 		} 		// 容器里看日志是否有负载均衡 		System.out.println("request in " + formatter.format(new Date())); 		return "Hello from Spring Boot on Kubernetes! first time: " + formatter.format(firstTime); 	}  	@GetMapping("/health") 	public String health() { 		return "OK"; 	} } 

pom.xml

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 	<modelVersion>4.0.0</modelVersion> 	<parent> 		<groupId>org.springframework.boot</groupId> 		<artifactId>spring-boot-starter-parent</artifactId> 		<version>3.5.7</version> 		<relativePath /> <!-- lookup parent from repository --> 	</parent> 	<groupId>com.example</groupId> 	<artifactId>spring-k8s-demo</artifactId> 	<version>0.0.1-SNAPSHOT</version> 	<name>spring-k8s-demo</name> 	<description>Demo project for Spring Boot</description> 	<properties> 		<java.version>21</java.version> 	</properties> 	<dependencies> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter</artifactId> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-web</artifactId> 		</dependency> 		<dependency> 			<groupId>org.springframework.boot</groupId> 			<artifactId>spring-boot-starter-test</artifactId> 			<scope>test</scope> 		</dependency> 	</dependencies> 	<build> 		<plugins> 			<plugin> 				<groupId>org.springframework.boot</groupId> 				<artifactId>spring-boot-maven-plugin</artifactId> 			</plugin> 		</plugins> 	</build> </project> 

构建 Docker 镜像

Dockerfile

FROM eclipse-temurin:21-jre-alpine AS runtime  # 创建应用目录 WORKDIR /app  # 复制jar文件 COPY target/spring-k8s-demo-0.0.1-SNAPSHOT.jar app.jar  # 暴露端口 EXPOSE 8080  # 启动应用 ENTRYPOINT ["java", "-jar", "app.jar"] 

build.sh

# 切换指定JDK版本(可选) export JAVA_HOME=`/usr/libexec/java_home -v 21`  # 可替换mvn mvnd clean package -DskipTests  # 构建镜像 docker build -t spring-k8s-demo:latest . 

编写 k8s Deployment 文件

k8s-deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata:   name: spring-app spec:   replicas: 2  # 运行 2 个 Pod 副本   selector:     matchLabels:       app: spring-app   template:     metadata:       labels:         app: spring-app     spec:       containers:       - name: spring-app         image: spring-k8s-demo:latest         ports:         - containerPort: 8080   # 容器内暴露的端口         livenessProbe:          # 存活探针           httpGet:             path: /health             port: 8080           initialDelaySeconds: 300           periodSeconds: 10         readinessProbe:         # 就绪探针           httpGet:             path: /health             port: 8080           initialDelaySeconds: 300           periodSeconds: 10         resources:              # 分配资源           requests:             memory: "256Mi"             cpu: "250m"           limits:             memory: "512Mi"             cpu: "500m" 

k8s-services.yaml

# service.yaml apiVersion: v1 kind: Service metadata:   name: sprint-service   labels:     app: sprint-service spec:   type: NodePort   selector:     app: spring-app   ports:     - protocol: TCP       port: 80       targetPort: 8080       nodePort: 30080 

配置文件解释

  • Deployment:

    • replicas: 2:确保任何时候都有 2 个 Pod 在运行。
    • selector & labels:Deployment 通过 app: spring-app 这个标签来管理它创建的 Pod。
    • image:指定从哪个镜像运行容器。
    • resources:为容器设置 CPU 和内存的资源请求与限制,这是生产环境的最佳实践。
  • Service:

    • type: NodePort: 在每个节点上开放一个静态端口,从而可以从集群外部访问
    • selector: app: spring-app:Service 通过这个标签找到要代理的 Pod。
    • ports:将 Service 的 80 端口流量转发到 Pod 的 8080 端口。

部署到集群

应用配置

kubectl apply -f k8s-deployment.yaml kubectl apply -f k8s-services.yaml 

测试

  1. 查看pod状态
kubectl get pods 

Pod 状态变为 Running

  1. 查看Deployment状态
kubectl get deployment 
  1. 查看 Service
kubectl get service 
  1. curl
curl http://192.168.64.3:30080/ 

【Kubernetes】入门-部署Spring应用

根据时间,可以看到每次轮询到不同pod,也就是不同容器

注意:因为我使用是colima所以需要使用colima list命令查出来IP是192.168.64.3

k8s 常用命令

# 查看所有节点 kubectl get nodes  # 查看节点详情 kubectl describe node <node-name>  # 查看Pod kubectl get pods  # 查看Pod详情 kubectl describe pod <pod-name>  # 查看Pod日志 kubectl logs <pod-name> -n <namespace>  # 查看单个 Pod 的日志 kubectl logs <pod-name>  # 查看部署 kubectl get deployments  # 查看部署详情 kubectl describe deployment <deployment-name>   # 查看由 Deployment 创建的所有 Pod 的日志 kubectl logs -l app=spring-app  # 转发端口(用于本地测试) kubectl port-forward svc/sprint-service 8080:80  # 水平扩缩容(将副本数从 2 增加到 3) kubectl scale deployment spring-app --replicas=3  # 查看部署历史 kubectl rollout history deployment/<deployment-name>  # 滚动更新(例如,更新到新版本的镜像) kubectl set image deployment/spring-app java-app=spring-k8s-demo:2.0.0  # 回滚到上一个版本 kubectl rollout undo deployment/<deployment-name>  # 删除部署 kubectl delete -f k8s-deployment.yaml kubectl delete -f k8s-services.yaml 

引用

https://github.com/WilsonPan/java-developer

例子: https://github.com/WilsonPan/java-developer/tree/main/samples/spring-demo

发表评论

评论已关闭。

相关文章