作为一名长期奋战在微服务架构一线的开发者,我深知将Spring Boot应用容器化并部署到Kubernetes集群的重要性。这次我将分享一个完整的实战案例,从零开始构建Spring Boot应用镜像,最终部署到K8s集群并实现弹性扩缩容。整个过程涉及Maven项目构建、Docker镜像制作、K8s资源编排等核心环节,特别适合刚接触云原生的Java开发者参考。
在开始之前,请确保你的开发环境满足以下条件:
提示:生产环境建议使用OpenJDK基础镜像而非Oracle JDK,避免潜在的许可问题。本文使用的是openjdk:8-jdk-alpine镜像。
在IDEA中新建Maven项目,命名为springbootdemo。关键pom.xml配置如下:
xml复制<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version> <!-- 使用稳定版本 -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
这里有几个值得注意的细节:
创建简单的REST接口用于验证部署效果:
java复制@RestController
@RequestMapping("/api")
public class HealthController {
@GetMapping("/health")
public Map<String, String> healthCheck() {
return Collections.singletonMap("status", "UP");
}
@GetMapping("/host")
public Map<String, String> hostInfo() {
try {
return Collections.singletonMap("hostname",
InetAddress.getLocalHost().getHostName());
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}
这个控制器提供了两个端点:
/api/health:基础健康检查/api/host:返回当前容器主机名(用于验证K8s负载均衡)标准的Dockerfile如下:
dockerfile复制FROM openjdk:8-jdk-alpine
# 设置时区
RUN apk add --no-cache tzdata && \
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
echo "Asia/Shanghai" > /etc/timezone
# 创建非root用户运行
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
# 优化JVM参数
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
# 复制构建产物
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
# 暴露端口
EXPOSE 8080
# 启动命令
ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]
这个Dockerfile做了多处优化:
构建命令:
bash复制# 先打包Spring Boot应用
mvn clean package -DskipTests
# 构建Docker镜像
docker build -t springbootdemo:1.0.0 .
# 测试运行
docker run -d -p 8080:8080 --name demo springbootdemo:1.0.0
验证镜像是否正常工作:
bash复制curl http://localhost:8080/api/health
# 预期输出: {"status":"UP"}
完整的deployment.yaml配置:
yaml复制apiVersion: apps/v1
kind: Deployment
metadata:
name: springboot-demo
labels:
app: springboot-demo
spec:
replicas: 2
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
selector:
matchLabels:
app: springboot-demo
template:
metadata:
labels:
app: springboot-demo
spec:
containers:
- name: app
image: springbootdemo:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1024Mi"
livenessProbe:
httpGet:
path: /api/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/health
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
关键配置说明:
service.yaml配置:
yaml复制apiVersion: v1
kind: Service
metadata:
name: springboot-service
spec:
type: NodePort
selector:
app: springboot-demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30080
这里选择NodePort类型方便测试,生产环境建议使用LoadBalancer或Ingress。
执行部署命令:
bash复制kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
# 查看部署状态
kubectl get pods -w
通过多次访问host接口验证请求被分配到不同Pod:
bash复制for i in {1..5}; do
curl http://<node-ip>:30080/api/host
done
预期会看到不同的hostname输出,证明负载均衡生效。
手动扩缩容:
bash复制# 扩容到3个实例
kubectl scale deployment springboot-demo --replicas=3
# 查看资源使用情况
kubectl top pods
yaml复制apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: springboot-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: springboot-demo
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
可能原因:
排查步骤:
优化方向:
在实际项目中,我们还需要考虑CI/CD流水线的搭建、多环境配置管理、安全加固等更多方面。这个示例提供了最基础的部署框架,你可以根据实际需求进行扩展。