当前位置: 首页 > news >正文

从 0 到 1 掌控云原生部署:Java 项目的 Docker 容器化与 K8s 集群实战指南

引言:容器化部署为何成为 Java 开发的必选项

在 Java 开发领域,部署方式正经历着从传统虚拟机到容器化的革命性转变。根据 2024 年 JetBrains 开发者调查,超过 78% 的企业级 Java 项目已采用容器化部署,其中 Kubernetes 成为容器编排的绝对主导者,市场占有率高达 83%。

这种转变并非偶然。传统 Java 应用部署面临着环境一致性差、资源利用率低、扩展能力弱等痛点:开发环境运行正常的代码,到了测试环境却频频报错;为应对流量峰值不得不长期维持大量闲置服务器;系统出现故障时,手动恢复耗时费力。

Docker 容器化技术与 Kubernetes 集群管理的结合,为这些问题提供了完美解决方案:

  • 环境一致性:容器封装了应用及其所有依赖,确保 "一次构建,到处运行"
  • 资源高效利用:容器比虚拟机更轻量,能在相同硬件上运行更多应用实例
  • 弹性伸缩:根据负载自动调整实例数量,平衡性能与成本
  • 故障自愈:自动检测并替换故障实例,提高系统可用性
  • 简化部署流程:实现从代码提交到生产部署的自动化流水线

本文将带领你完成 Java 项目的容器化之旅,从基础的 Docker 镜像构建,到复杂的 Kubernetes 集群部署,再到高级的自动伸缩和故障自愈配置,全方位掌握云原生部署技术栈。

一、Docker 容器化基础:从理论到实践

1.1 Docker 核心概念与工作原理

Docker 采用了操作系统级虚拟化技术,通过隔离用户空间实现应用的独立运行。其核心组件包括:

  • 镜像 (Image):包含运行应用所需的代码、运行时、库、环境变量和配置文件的不可变模板
  • 容器 (Container):镜像的运行实例,是一个独立的可执行软件包
  • Docker 引擎:创建和管理容器的核心软件,包括 Docker 守护进程和命令行客户端
  • 仓库 (Registry):存储和分发 Docker 镜像的仓库,如 Docker Hub、阿里云容器仓库等
  • 网络 (Network):实现容器间通信的网络模型
  • 卷 (Volume):用于持久化存储容器数据的机制

Docker 的工作流程可概括为:

1.2 准备一个可容器化的 Java 项目

我们将以一个 Spring Boot 应用为例,演示容器化过程。首先创建项目结构并添加核心依赖:

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 http://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.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>docker-k8s-demo</artifactId><version>1.0.0</version><name>docker-k8s-demo</name><description>A demo project for Docker and Kubernetes deployment</description><properties><java.version>17</java.version><lombok.version>1.18.30</lombok.version><springdoc.version>2.1.0</springdoc.version><fastjson2.version>2.0.45</fastjson2.version><mybatis-plus.version>3.5.5</mybatis-plus.version></properties><dependencies><!-- Spring Boot Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot Actuator (用于健康检查和监控) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version><optional>true</optional></dependency><!-- Swagger3 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>${springdoc.version}</version></dependency><!-- MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version></dependency><!-- MySQL Driver --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- FastJSON2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>${fastjson2.version}</version></dependency><!-- Test --><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><configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes><!-- 构建分层jar,优化Docker镜像构建 --><layers><enabled>true</enabled></layers></configuration></plugin><!-- 编译插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.11.0</version><configuration><source>${java.version}</source><target>${java.version}</target><encoding>UTF-8</encoding></configuration></plugin></plugins></build>
</project>

应用主类

package com.example.dockerk8sdemo;import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Info;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;/*** 应用主类** @author ken*/
@SpringBootApplication
@MapperScan("com.example.dockerk8sdemo.mapper")
@OpenAPIDefinition(info = @Info(title = "Docker-K8s Demo API", version = "1.0", description = "演示API"))
@Slf4j
public class DockerK8sDemoApplication {public static void main(String[] args) {SpringApplication.run(DockerK8sDemoApplication.class, args);}/*** 应用启动完成事件*/@EventListener(ApplicationReadyEvent.class)public void onApplicationReady() {log.info("Application started successfully");// 打印环境信息,用于验证容器环境String javaVersion = System.getProperty("java.version");String userName = System.getenv("USER_NAME");log.info("Running with Java version: {}", javaVersion);log.info("USER_NAME environment variable: {}", userName);}
}

配置文件

# application.yml
spring:profiles:active: ${SPRING_PROFILES_ACTIVE:default}datasource:url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:demo}?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=trueusername: ${DB_USERNAME:root}password: ${DB_PASSWORD:root}driver-class-name: com.mysql.cj.jdbc.Driver# 应用配置
server:port: ${PORT:8080}servlet:context-path: /api# Actuator配置(暴露健康检查和监控端点)
management:endpoints:web:exposure:include: health,info,metrics,prometheusendpoint:health:show-details: alwaysprobes:enabled: truemetrics:export:prometheus:enabled: true# MyBatis-Plus配置
mybatis-plus:mapper-locations: classpath*:mapper/**/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.slf4j.Slf4jImplglobal-config:db-config:id-type: auto

健康检查控制器

package com.example.dockerk8sdemo.controller;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
import java.util.Random;/*** 健康检查和演示控制器** @author ken*/
@RestController
@RequestMapping("/health")
@RequiredArgsConstructor
@Tag(name = "健康检查", description = "应用健康状态检查接口")
@Slf4j
public class HealthController implements HealthIndicator {private final Random random = new Random();/*** 自定义健康检查端点*/@Overridepublic Health health() {// 实际应用中可以检查数据库连接、缓存等资源int errorCode = check();if (errorCode != 0) {return Health.down().withDetail("Error Code", errorCode).withDetail("Message", "Some resources are not available").build();}return Health.up().withDetail("Message", "Application is running normally").withDetail("Version", "1.0.0").build();}/*** 简单的健康检查逻辑*/private int check() {// 模拟健康检查,99%概率返回健康return random.nextInt(100) < 99 ? 0 : 1;}/*** 测试接口*/@GetMapping("/test")@Operation(summary = "测试接口", description = "用于验证服务是否正常运行")public Map<String, Object> test() {Map<String, Object> result = new HashMap<>(4);result.put("status", "success");result.put("message", "Service is running");result.put("instance", System.getenv("HOSTNAME")); // 在容器中会显示容器IDresult.put("timestamp", System.currentTimeMillis());log.info("Test endpoint called");return result;}/*** 模拟负载的接口*/@GetMapping("/load/{seconds}")@Operation(summary = "模拟负载", description = "用于测试自动伸缩功能")public Map<String, Object> simulateLoad(@Parameter(description = "负载持续时间(秒)") @PathVariable int seconds) {long endTime = System.currentTimeMillis() + seconds * 1000L;log.info("Simulating load for {} seconds", seconds);// 消耗CPUwhile (System.currentTimeMillis() < endTime) {Math.sqrt(random.nextDouble());}Map<String, Object> result = new HashMap<>(3);result.put("status", "success");result.put("message", "Load simulation completed");result.put("durationSeconds", seconds);return result;}
}

1.3 编写高效的 Dockerfile

Dockerfile 是构建 Docker 镜像的蓝图,一个优化的 Dockerfile 能显著减小镜像体积并提高构建速度。以下是针对 Java 应用的优化 Dockerfile:

# 多阶段构建:第一阶段编译打包
FROM maven:3.9.6-eclipse-temurin-17 AS builder# 设置工作目录
WORKDIR /app# 复制pom.xml和依赖文件,利用缓存
COPY pom.xml .
COPY src ./src# 编译打包,跳过测试以加快构建
RUN mvn clean package -DskipTests# 提取Spring Boot分层
RUN java -Djarmode=layertools -jar target/*.jar extract# 第二阶段:运行环境
FROM eclipse-temurin:17-jre-alpine# 添加非root用户,增强安全性
RUN addgroup -S appgroup && adduser -S appuser -G appgroup# 设置工作目录
WORKDIR /app# 复制分层文件
COPY --from=builder /app/dependencies/ ./
COPY --from=builder /app/spring-boot-loader/ ./
COPY --from=builder /app/snapshot-dependencies/ ./
COPY --from=builder /app/application/ ./# 切换到非root用户
USER appuser# 暴露端口
EXPOSE 8080# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \CMD wget -q --spider http://localhost:8080/api/health || exit 1# 启动命令
ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]# 元数据标签
LABEL maintainer="ken"
LABEL version="1.0.0"
LABEL description="Docker-K8s Demo Application"

这个 Dockerfile 采用了多项优化技术:

  1. 多阶段构建:将编译环境和运行环境分离,最终镜像只包含运行所需文件
  2. 分层构建:利用 Spring Boot 的分层功能,将不常变化的依赖与频繁变化的应用代码分离,提高缓存利用率
  3. 使用轻量基础镜像:采用 Alpine 版本的 JRE,比完整版小很多
  4. 非 root 用户运行:增强容器安全性,避免潜在的权限问题
  5. 健康检查:内置健康检查命令,便于容器平台监控应用状态
  6. 适当的元数据:添加标签信息,提高镜像可维护性

1.4 构建并测试 Docker 镜像

构建镜像命令:

# 构建镜像,指定标签
docker build -t docker-k8s-demo:1.0.0 .# 查看构建的镜像
docker images | grep docker-k8s-demo

运行容器测试:

# 简单运行
docker run -d -p 8080:8080 --name demo-app docker-k8s-demo:1.0.0# 带环境变量运行
docker run -d \-p 8080:8080 \--name demo-app \-e SPRING_PROFILES_ACTIVE=prod \-e DB_HOST=mysql \-e DB_USERNAME=root \-e DB_PASSWORD=secret \docker-k8s-demo:1.0.0# 查看容器日志
docker logs -f demo-app# 测试接口
curl http://localhost:8080/api/health/test# 停止并删除容器
docker stop demo-app
docker rm demo-app

1.5 推送镜像到仓库

为了在 Kubernetes 集群中使用镜像,需要将其推送到容器仓库:

# 登录Docker Hub(如果使用其他仓库,如阿里云容器仓库,替换为相应地址)
docker login# 标记镜像(格式:仓库地址/用户名/镜像名:标签)
docker tag docker-k8s-demo:1.0.0 yourusername/docker-k8s-demo:1.0.0# 推送镜像
docker push yourusername/docker-k8s-demo:1.0.0# 如果使用私有仓库
docker tag docker-k8s-demo:1.0.0 private-registry.example.com/demo/docker-k8s-demo:1.0.0
docker push private-registry.example.com/demo/docker-k8s-demo:1.0.0

二、Kubernetes 核心概念与集群搭建

2.1 Kubernetes 核心组件与架构

Kubernetes(简称 K8s)是一个开源的容器编排平台,能够自动化容器的部署、扩展和管理。其核心架构如下:

核心组件说明:

  • 控制平面 (Control Plane)

    • API Server:所有操作的统一入口,提供 RESTful API
    • etcd:分布式键值存储,保存集群的所有状态
    • Scheduler:负责 Pod 的调度,决定将 Pod 部署到哪个节点
    • Controller Manager:运行各种控制器进程,如节点控制器、副本控制器等
    • Cloud Controller Manager:与云服务提供商集成的控制器
  • 节点 (Node)

    • Kubelet:在每个节点上运行,确保容器按照 Pod 规范运行
    • Kube-proxy:网络代理,维护节点网络规则
    • 容器运行时:负责运行容器的软件,如 containerd

Kubernetes 的核心对象包括:

  • Pod:最小部署单元,包含一个或多个容器
  • Service:定义 Pod 的访问方式,提供固定访问点
  • Deployment:管理 Pod 和 ReplicaSet,支持滚动更新
  • StatefulSet:用于管理有状态应用
  • ConfigMap/Secret:配置管理
  • Namespace:提供资源隔离
  • Ingress:管理外部访问

2.2 搭建本地 Kubernetes 集群

对于开发和测试,我们可以使用 Minikube 搭建本地单节点集群:

# 安装Minikube(Windows使用Chocolatey,macOS使用Homebrew)
# macOS:
brew install minikube# 启动集群(指定容器运行时为containerd,内存2GB)
minikube start --driver=docker --container-runtime=containerd --memory=2048# 检查集群状态
minikube status# 安装kubectl(Kubernetes命令行工具)
# macOS:
brew install kubectl# 验证kubectl配置
kubectl config view# 查看节点信息
kubectl get nodes# 查看集群组件
kubectl get pods -n kube-system

对于生产环境,建议使用 kubeadm 搭建多节点集群,或使用云服务商提供的托管 Kubernetes 服务(如 EKS、GKE、AKS、阿里云 ACK 等)。

2.3 kubectl 命令行工具基础

kubectl 是与 Kubernetes 集群交互的主要工具,常用命令:

# 查看资源
kubectl get pods          # 查看所有Pod
kubectl get services      # 查看所有Service
kubectl get deployments   # 查看所有Deployment
kubectl get namespaces    # 查看所有Namespace# 查看详细信息
kubectl describe pod <pod-name>
kubectl describe deployment <deployment-name># 查看日志
kubectl logs <pod-name>
kubectl logs -f <pod-name>  # 实时查看日志# 执行命令
kubectl exec -it <pod-name> -- /bin/sh# 创建资源
kubectl create -f <yaml-file># 应用配置(创建或更新资源)
kubectl apply -f <yaml-file># 删除资源
kubectl delete pod <pod-name>
kubectl delete -f <yaml-file># 查看集群信息
kubectl cluster-info# 切换命名空间
kubectl config set-context --current --namespace=<namespace-name>

三、在 Kubernetes 部署 Java 应用

3.1 编写 Kubernetes 配置文件

Kubernetes 使用 YAML 或 JSON 格式的配置文件定义资源。我们需要创建以下配置文件来部署 Java 应用:

1. 命名空间配置(namespace.yaml)

apiVersion: v1
kind: Namespace
metadata:name: demo-applabels:name: demo-app

2. 配置文件(configmap.yaml)

apiVersion: v1
kind: ConfigMap
metadata:name: demo-app-confignamespace: demo-app
data:# 应用配置SPRING_PROFILES_ACTIVE: "prod"SERVER_PORT: "8080"# 日志配置LOG_LEVEL: "INFO"LOG_PATTERN: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"

3. 密钥配置(secret.yaml)

apiVersion: v1
kind: Secret
metadata:name: demo-app-secretnamespace: demo-app
type: Opaque
data:# 注意:这里的值需要Base64编码DB_USERNAME: cm9vdA==  # root的Base64编码DB_PASSWORD: c2VjcmV0  # secret的Base64编码

生成 Base64 编码的命令:echo -n "value" | base64

4. 部署配置(deployment.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:name: demo-appnamespace: demo-applabels:app: demo-app
spec:# 初始副本数replicas: 2# 选择器,用于匹配Pod模板selector:matchLabels:app: demo-app# 策略配置strategy:# 滚动更新策略rollingUpdate:maxSurge: 1        # 可以超出期望副本数的最大数量maxUnavailable: 0  # 更新过程中允许不可用的最大副本数type: RollingUpdate# Pod模板template:metadata:labels:app: demo-appspec:# 容器配置containers:- name: demo-app# 镜像地址(替换为你的镜像仓库地址)image: yourusername/docker-k8s-demo:1.0.0imagePullPolicy: Always# 端口配置ports:- containerPort: 8080name: httpprotocol: TCP# 环境变量配置env:# 从ConfigMap获取环境变量- name: SPRING_PROFILES_ACTIVEvalueFrom:configMapKeyRef:name: demo-app-configkey: SPRING_PROFILES_ACTIVE- name: SERVER_PORTvalueFrom:configMapKeyRef:name: demo-app-configkey: SERVER_PORT# 从Secret获取环境变量- name: DB_USERNAMEvalueFrom:secretKeyRef:name: demo-app-secretkey: DB_USERNAME- name: DB_PASSWORDvalueFrom:secretKeyRef:name: demo-app-secretkey: DB_PASSWORD# 直接指定环境变量- name: DB_HOSTvalue: mysql-service- name: DB_PORTvalue: "3306"- name: DB_NAMEvalue: demo# 资源限制resources:requests:memory: "256Mi"cpu: "200m"limits:memory: "512Mi"cpu: "500m"# 健康检查livenessProbe:httpGet:path: /api/healthport: 8080initialDelaySeconds: 60  # 启动后多久开始检查periodSeconds: 30        # 检查间隔timeoutSeconds: 3        # 超时时间failureThreshold: 3      # 失败多少次视为不健康readinessProbe:httpGet:path: /api/health/testport: 8080initialDelaySeconds: 30periodSeconds: 10timeoutSeconds: 3failureThreshold: 3# 启动探针(确保应用完全启动)startupProbe:httpGet:path: /api/healthport: 8080failureThreshold: 30periodSeconds: 10

5. 服务配置(service.yaml)

apiVersion: v1
kind: Service
metadata:name: demo-app-servicenamespace: demo-app
spec:selector:app: demo-appports:- port: 80targetPort: 8080protocol: TCPname: httptype: ClusterIP  # 集群内部访问

6. ingress 配置(ingress.yaml)- 可选

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: demo-app-ingressnamespace: demo-appannotations:nginx.ingress.kubernetes.io/rewrite-target: /nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:rules:- host: demo-app.localhttp:paths:- path: /pathType: Prefixbackend:service:name: demo-app-serviceport:number: 80

3.2 部署应用到 Kubernetes 集群

执行以下命令部署应用:

# 创建命名空间
kubectl apply -f namespace.yaml# 创建配置和密钥
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml# 部署应用
kubectl apply -f deployment.yaml# 创建服务
kubectl apply -f service.yaml# (可选)创建Ingress
# 首先需要安装Ingress控制器:minikube addons enable ingress
kubectl apply -f ingress.yaml# 查看部署状态
kubectl get deployments -n demo-app
kubectl get pods -n demo-app
kubectl get services -n demo-app# 查看部署详情
kubectl describe deployment demo-app -n demo-app# 查看Pod日志
kubectl logs -f <pod-name> -n demo-app

3.3 访问 Kubernetes 中的应用

有多种方式可以访问 Kubernetes 集群中的应用:

  1. 端口转发
# 将本地端口8080转发到集群中的服务
kubectl port-forward -n demo-app service/demo-app-service 8080:80# 现在可以通过http://localhost:8080访问应用
curl http://localhost:8080/api/health/test
  1. 使用 Minikube 隧道(针对 NodePort 或 LoadBalancer 类型服务)
# 在另一个终端启动隧道
minikube tunnel# 获取服务地址
kubectl get service demo-app-service -n demo-app
  1. 通过 Ingress 访问
# 添加主机映射(Windows在C:\Windows\System32\drivers\etc\hosts,Linux/macOS在/etc/hosts)
echo "$(minikube ip) demo-app.local" | sudo tee -a /etc/hosts# 访问应用
curl http://demo-app.local/api/health/test

3.4 部署 MySQL 数据库

为了让我们的 Java 应用能够正常工作,需要部署一个 MySQL 数据库:

mysql-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:name: mysql-confignamespace: demo-app
data:MYSQL_DATABASE: "demo"MYSQL_ROOT_HOST: "%"

mysql-secret.yaml

apiVersion: v1
kind: Secret
metadata:name: mysql-secretnamespace: demo-app
type: Opaque
data:MYSQL_ROOT_PASSWORD: c2VjcmV0  # secret的Base64编码

mysql-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:name: mysqlnamespace: demo-app
spec:replicas: 1selector:matchLabels:app: mysqltemplate:metadata:labels:app: mysqlspec:containers:- name: mysqlimage: mysql:8.3.0ports:- containerPort: 3306env:- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: MYSQL_ROOT_PASSWORD- name: MYSQL_DATABASEvalueFrom:configMapKeyRef:name: mysql-configkey: MYSQL_DATABASE- name: MYSQL_ROOT_HOSTvalueFrom:configMapKeyRef:name: mysql-configkey: MYSQL_ROOT_HOSTvolumeMounts:- name: mysql-datamountPath: /var/lib/mysqlresources:requests:memory: "256Mi"cpu: "200m"limits:memory: "512Mi"cpu: "500m"livenessProbe:exec:command: ["mysqladmin", "ping", "-u", "root", "-p$(MYSQL_ROOT_PASSWORD)"]initialDelaySeconds: 30periodSeconds: 10readinessProbe:exec:command: ["mysqladmin", "ping", "-u", "root", "-p$(MYSQL_ROOT_PASSWORD)"]initialDelaySeconds: 5periodSeconds: 5volumes:- name: mysql-datapersistentVolumeClaim:claimName: mysql-pvc

mysql-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: mysql-pvcnamespace: demo-app
spec:accessModes:- ReadWriteOnceresources:requests:storage: 1Gi

mysql-service.yaml

apiVersion: v1
kind: Service
metadata:name: mysql-servicenamespace: demo-app
spec:selector:app: mysqlports:- port: 3306targetPort: 3306clusterIP: None  # Headless Service

部署 MySQL:

kubectl apply -f mysql-configmap.yaml
kubectl apply -f mysql-secret.yaml
kubectl apply -f mysql-pvc.yaml
kubectl apply -f mysql-deployment.yaml
kubectl apply -f mysql-service.yaml# 查看MySQL部署状态
kubectl get pods -n demo-app | grep mysql

四、实现服务的自动部署与滚动更新

4.1 CI/CD 流水线集成

实现自动部署的关键是建立 CI/CD 流水线。我们以 GitHub Actions 为例,演示如何配置自动构建 Docker 镜像并部署到 Kubernetes:

创建.github/workflows/deploy.yml 文件

name: Build and Deploy to Kubernetes# 触发条件:main分支有push或pull request合并
on:push:branches: [ main ]pull_request:branches: [ main ]jobs:build:runs-on: ubuntu-lateststeps:# 检出代码- uses: actions/checkout@v4# 设置JDK 17- name: Set up JDK 17uses: actions/setup-java@v4with:java-version: '17'distribution: 'temurin'cache: maven# 构建项目- name: Build with Mavenrun: mvn clean package -DskipTests# 设置Docker Buildx- name: Set up Docker Buildxuses: docker/setup-buildx-action@v3# 登录到Docker Hub- name: Login to DockerHubuses: docker/login-action@v3with:username: ${{ secrets.DOCKERHUB_USERNAME }}password: ${{ secrets.DOCKERHUB_TOKEN }}# 构建并推送Docker镜像- name: Build and pushuses: docker/build-push-action@v5with:context: .push: truetags: ${{ secrets.DOCKERHUB_USERNAME }}/docker-k8s-demo:latest,${{ secrets.DOCKERHUB_USERNAME }}/docker-k8s-demo:${{ github.sha }}# 只有在main分支push时才部署到K8s- name: Deploy to Kubernetesif: github.ref == 'refs/heads/main' && github.event_name == 'push'uses: steebchen/kubectl@v4with:config: ${{ secrets.KUBE_CONFIG_DATA }}  # 存储在GitHub Secrets中的kubeconfigcommand: apply -f k8s/

配置说明:

  1. 首先需要在 GitHub 仓库的 Secrets 中添加:

    • DOCKERHUB_USERNAME:Docker Hub 用户名
    • DOCKERHUB_TOKEN:Docker Hub 访问令牌
    • KUBE_CONFIG_DATA:base64 编码的 kubeconfig 文件内容
  2. 生成 KUBE_CONFIG_DATA 的命令:

cat ~/.kube/config | base64
  1. 这个流水线会:
    • 当代码推送到 main 分支或有 PR 合并到 main 分支时触发
    • 构建 Java 项目
    • 构建 Docker 镜像并推送到 Docker Hub
    • 只有在 main 分支的 push 事件才会部署到 Kubernetes

4.2 Kubernetes 滚动更新策略

Kubernetes 的 Deployment 资源提供了滚动更新功能,确保应用更新过程中不中断服务。我们在前面的 deployment.yaml 中已经配置了滚动更新策略:

strategy:rollingUpdate:maxSurge: 1        # 可以超出期望副本数的最大数量(绝对值或百分比)maxUnavailable: 0  # 更新过程中允许不可用的最大副本数type: RollingUpdate

手动触发滚动更新的方法:

# 方法1:修改镜像版本
kubectl set image deployment/demo-app demo-app=yourusername/docker-k8s-demo:1.0.1 -n demo-app# 方法2:使用kubectl apply更新配置
kubectl apply -f deployment.yaml# 查看更新状态
kubectl rollout status deployment/demo-app -n demo-app# 查看更新历史
kubectl rollout history deployment/demo-app -n demo-app# 回滚到上一版本
kubectl rollout undo deployment/demo-app -n demo-app# 回滚到指定版本
kubectl rollout undo deployment/demo-app --to-revision=2 -n demo-app

滚动更新的工作流程:

4.3 蓝绿部署与金丝雀发布

除了滚动更新,Kubernetes 还支持蓝绿部署和金丝雀发布等高级部署策略。

蓝绿部署:同时维护两个相同的环境(蓝环境和绿环境),新版本部署到非生产环境,测试通过后切换流量:

# 创建新版本的Deployment(绿环境)
kubectl apply -f deployment-v2.yaml# 测试新版本
kubectl port-forward -n demo-app deployment/demo-app-v2 8081:8080# 切换Service指向新版本
kubectl patch service demo-app-service -n demo-app -p '{"spec":{"selector":{"app":"demo-app-v2"}}}'# 如果出现问题,切换回旧版本
kubectl patch service demo-app-service -n demo-app -p '{"spec":{"selector":{"app":"demo-app"}}}'

金丝雀发布:先将少量流量导向新版本,验证无误后逐步增加流量比例:

# 部署新版本,副本数较少
kubectl apply -f deployment-canary.yaml# 通过Ingress控制流量分配(需要支持权重的Ingress控制器,如NGINX Ingress)
kubectl apply -f ingress-canary.yaml

ingress-canary.yaml 示例:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: demo-app-primarynamespace: demo-appannotations:nginx.ingress.kubernetes.io/rewrite-target: /nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:rules:- host: demo-app.localhttp:paths:- path: /pathType: Prefixbackend:service:name: demo-app-serviceport:number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: demo-app-canarynamespace: demo-appannotations:nginx.ingress.kubernetes.io/rewrite-target: /nginx.ingress.kubernetes.io/ssl-redirect: "false"nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-weight: "10"  # 10%流量到金丝雀版本
spec:rules:- host: demo-app.localhttp:paths:- path: /pathType: Prefixbackend:service:name: demo-app-canary-serviceport:number: 80

五、实现服务的弹性伸缩

5.1 Kubernetes HPA(Horizontal Pod Autoscaler)

Horizontal Pod Autoscaler(HPA)可以根据 CPU 利用率、内存使用率或自定义指标自动调整 Pod 的数量。

1. 首先确保已部署 metrics-server

# 安装metrics-server
minikube addons enable metrics-server# 验证metrics-server是否运行
kubectl get pods -n kube-system | grep metrics-server

2. 创建 HPA 配置(hpa.yaml)

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:name: demo-app-hpanamespace: demo-app
spec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: demo-appminReplicas: 2        # 最小副本数maxReplicas: 10       # 最大副本数metrics:- type: Resourceresource:name: cputarget:type: UtilizationaverageUtilization: 70  # CPU利用率目标值- type: Resourceresource:name: memorytarget:type: UtilizationaverageUtilization: 80  # 内存利用率目标值behavior:scaleUp:stabilizationWindowSeconds: 60  # 扩容稳定窗口policies:- type: Percentvalue: 50                     # 每次扩容50%periodSeconds: 60             # 扩容间隔scaleDown:stabilizationWindowSeconds: 300 # 缩容稳定窗口(更长,避免频繁波动)policies:- type: Percentvalue: 30                     # 每次缩容30%periodSeconds: 120            # 缩容间隔

3. 部署 HPA

kubectl apply -f hpa.yaml# 查看HPA状态
kubectl get hpa -n demo-app
kubectl describe hpa demo-app-hpa -n demo-app

5.2 基于自定义指标的弹性伸缩

除了 CPU 和内存,Kubernetes 还支持基于自定义指标的弹性伸缩,如请求数、队列长度等。

1. 部署 Prometheus 和 Prometheus Adapter

# 添加Helm仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update# 安装Prometheus
helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring --create-namespace

2. 配置 Prometheus Adapter:创建 custom-metrics-config.yaml:

rules:default: falseexternal:- seriesQuery: 'http_server_requests_seconds_count{job!=""}'resources:overrides:kubernetes_namespace:resource: namespacekubernetes_pod_name:resource: podname:matches: "^(.*)_count"as: "${1}_per_second"metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[5m])) by (<<.GroupBy>>)'

使用 Helm 安装 Prometheus Adapter:

helm install prometheus-adapter prometheus-community/prometheus-adapter \-n monitoring \--set configMap.create=true \--set configMap.content=$(cat custom-metrics-config.yaml)

3. 创建基于自定义指标的 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:name: demo-app-hpa-customnamespace: demo-app
spec:scaleTargetRef:apiVersion: apps/v1kind: Deploymentname: demo-appminReplicas: 2maxReplicas: 10metrics:- type: Podspods:metric:name: http_server_requests_seconds_per_secondtarget:type: AverageValueaverageValue: "100"  # 每个Pod平均每秒100个请求behavior:scaleUp:stabilizationWindowSeconds: 30policies:- type: Percentvalue: 50periodSeconds: 60scaleDown:stabilizationWindowSeconds: 300policies:- type: Percentvalue: 30periodSeconds: 120

5.3 测试弹性伸缩功能

使用以下方法测试弹性伸缩功能:

  1. 增加负载测试 CPU 触发扩容
# 在一个终端中持续发送请求
while true; do curl http://localhost:8080/api/health/load/1; sleep 0.1; done# 在另一个终端监控HPA和Pod
watch -n 5 "kubectl get hpa -n demo-app; echo; kubectl get pods -n demo-app"
  1. 观察扩容过程:当 CPU 利用率超过目标值(70%)时,HPA 会自动增加 Pod 数量。

  2. 停止负载测试观察缩容

# 停止发送请求后,等待缩容稳定窗口(300秒)
watch -n 5 "kubectl get hpa -n demo-app; echo; kubectl get pods -n demo-app"

当负载降低后,HPA 会在稳定窗口后逐渐减少 Pod 数量到最小副本数。

六、实现服务的故障自愈

6.1 Kubernetes 的自愈机制

Kubernetes 提供了多种机制确保服务的高可用性和故障自愈:

  1. Pod 健康检查:通过 livenessProbe、readinessProbe 和 startupProbe 检测 Pod 状态
  2. 自动重启:当容器崩溃时,Kubelet 会根据 restartPolicy 自动重启容器
  3. 节点故障处理:当节点故障时,控制器会在健康节点上重新创建 Pod
  4. 副本集维护:ReplicaSet 控制器确保实际副本数与期望副本数一致

6.2 配置 Pod 的重启策略和健康检查

在 deployment.yaml 中配置重启策略和健康检查:

spec:template:spec:restartPolicy: Always  # 总是重启失败的容器containers:- name: demo-app# ...其他配置...# 存活探针:检测容器是否运行正常,失败则重启livenessProbe:httpGet:path: /api/healthport: 8080initialDelaySeconds: 60  # 应用启动时间较长,延迟开始检查periodSeconds: 30        # 检查间隔timeoutSeconds: 3        # 超时时间failureThreshold: 3      # 连续3次失败视为不健康# 就绪探针:检测容器是否可以接收请求,失败则从Service移除readinessProbe:httpGet:path: /api/health/testport: 8080initialDelaySeconds: 30periodSeconds: 10timeoutSeconds: 3failureThreshold: 3# 启动探针:确保应用完全启动startupProbe:httpGet:path: /api/healthport: 8080failureThreshold: 30     # 最多检查30次periodSeconds: 10        # 每10秒检查一次

重启策略(restartPolicy)可选值:

  • Always:总是重启失败的容器(默认值)
  • OnFailure:只有当容器以非零退出码终止时才重启
  • Never:从不重启容器

6.3 配置 Pod 的反亲和性与节点亲和性

为了提高服务的可用性,可以配置 Pod 的反亲和性,确保 Pod 分散在不同节点上:

spec:template:spec:affinity:# 反亲和性:避免同一应用的Pod调度到同一节点podAntiAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: appoperator: Invalues:- demo-apptopologyKey: "kubernetes.io/hostname"# 节点亲和性:优先调度到具有特定标签的节点nodeAffinity:preferredDuringSchedulingIgnoredDuringExecution:- weight: 100preference:matchExpressions:- key: environmentoperator: Invalues:- production- weight: 50preference:matchExpressions:- key: hardwareoperator: Invalues:- high-performance

6.4 测试故障自愈功能

测试 Kubernetes 的故障自愈能力:

  1. 手动删除 Pod
# 查看当前Pod
kubectl get pods -n demo-app# 删除一个Pod
kubectl delete pod <pod-name> -n demo-app# 观察是否会自动创建新的Pod
kubectl get pods -n demo-app
  1. 模拟容器崩溃
# 进入容器
kubectl exec -it <pod-name> -n demo-app -- /bin/sh# 在容器内杀死应用进程
ps aux | grep java
kill -9 <java-pid># 退出容器,观察Pod状态
exit
kubectl get pods -n demo-app
  1. 模拟节点故障(仅在多节点集群中可行):
# 标记节点不可调度
kubectl cordon <node-name># 排空节点
kubectl drain <node-name> --ignore-daemonsets# 观察Pod是否迁移到其他节点
kubectl get pods -n demo-app -o wide# 恢复节点
kubectl uncordon <node-name>

在以上测试中,Kubernetes 都会自动恢复 Pod 到期望状态,体现了其强大的故障自愈能力。

七、监控与日志管理

7.1 部署 Prometheus 和 Grafana 监控

使用 Helm 部署 Prometheus 和 Grafana:

# 添加Helm仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update# 创建命名空间
kubectl create namespace monitoring# 安装kube-prometheus-stack
helm install prometheus prometheus-community/kube-prometheus-stack \--namespace monitoring \--set grafana.service.type=NodePort \--set prometheus.service.type=NodePort# 查看部署状态
kubectl get pods -n monitoring# 获取Grafana访问地址
minikube service prometheus-grafana -n monitoring --url

配置 Java 应用暴露 Prometheus 指标:

我们的 Spring Boot 应用已经添加了 actuator 和 prometheus 依赖,只需在 application.yml 中配置:

management:endpoints:web:exposure:include: health,info,metrics,prometheusmetrics:export:prometheus:enabled: true

在 Grafana 中导入 Spring Boot 应用监控面板(ID:12856),可以查看 JVM、内存、CPU、请求数等指标。

7.2 集中式日志管理

部署 ELK(Elasticsearch, Logstash, Kibana)栈或 EFK(Elasticsearch, Fluentd, Kibana)栈进行日志集中管理:

# 安装EFK
helm repo add elastic https://helm.elastic.co
helm repo update# 安装Elasticsearch
helm install elasticsearch elastic/elasticsearch \--namespace logging \--create-namespace \--set replicas=1 \--set minimumMasterNodes=1 \--set resources.requests.cpu=1 \--set resources.requests.memory=2Gi \--set resources.limits.cpu=1 \--set resources.limits.memory=2Gi# 安装Kibana
helm install kibana elastic/kibana --namespace logging# 安装Fluentd
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install fluentd bitnami/fluentd --namespace logging \--set elasticsearch.host=elasticsearch-master \--set elasticsearch.port=9200

配置 Java 应用输出 JSON 格式日志,在 logback-spring.xml 中:

<?xml version="1.0" encoding="UTF-8"?>
<configuration><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder class="net.logstash.logback.encoder.LogstashEncoder"><includeMdcKeyName>traceId</includeMdcKeyName><includeMdcKeyName>spanId</includeMdcKeyName><fieldNames><timestamp>timestamp</timestamp><message>message</message><logger>logger</logger><thread>thread</thread><level>level</level></fieldNames><timestampPattern>yyyy-MM-dd'T'HH:mm:ss.SSSZ</timestampPattern></encoder></appender><root level="INFO"><appender-ref ref="CONSOLE" /></root>
</configuration>

添加 logstash-logback-encoder 依赖:

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

八、生产环境最佳实践与优化

8.1 资源限制与请求配置

为所有容器设置合理的资源限制和请求是生产环境的基本要求:

resources:requests:memory: "256Mi"  # 容器需要的最小内存cpu: "200m"      # 容器需要的最小CPU(1000m = 1核)limits:memory: "512Mi"  # 容器允许使用的最大内存cpu: "500m"      # 容器允许使用的最大CPU

设置资源限制的好处:

  • 防止单个容器耗尽节点资源
  • 帮助调度器做出更好的调度决策
  • 提高集群资源利用率
  • 避免节点级别的资源竞争

8.2 安全最佳实践

  1. 使用非 root 用户运行容器:在 Dockerfile 中创建并使用普通用户
  2. 限制容器权限
securityContext:runAsNonRoot: truerunAsUser: 1000runAsGroup: 3000fsGroup: 2000allowPrivilegeEscalation: falsecapabilities:drop: ["ALL"]
  1. 使用 PodSecurityContext
podSecurityContext:seccompProfile:type: RuntimeDefault
  1. 配置网络策略:限制 Pod 间通信
  2. 定期更新基础镜像:修复已知漏洞
  3. 使用镜像拉取密钥:限制对私有仓库的访问
  4. 加密敏感数据:使用 Secret 存储敏感信息,并考虑使用 SealedSecrets 或 Vault

8.3 备份与灾难恢复

  1. 备份 etcd 数据
# 创建备份
kubectl -n kube-system exec -it <etcd-pod-name> -- etcdctl \--endpoints=https://127.0.0.1:2379 \--cacert=/etc/kubernetes/pki/etcd/ca.crt \--cert=/etc/kubernetes/pki/apiserver-etcd-client.crt \--key=/etc/kubernetes/pki/apiserver-etcd-client.key \snapshot save /backup/etcd-snapshot.db# 复制备份到本地
kubectl cp -n kube-system <etcd-pod-name>:/backup/etcd-snapshot.db ./etcd-snapshot.db
  1. 使用 Velero 进行集群备份
# 安装Velero
velero install \--provider aws \--plugins velero/velero-plugin-for-aws:v1.6.0 \--bucket my-backup-bucket \--secret-file ./credentials-velero \--use-volume-snapshots=false \--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://minio:9000# 创建备份
velero backup create demo-app-backup --include-namespaces demo-app# 查看备份
velero backup get# 恢复备份
velero restore create --from-backup demo-app-backup
  1. 多区域部署:对于关键业务,考虑跨区域部署以提高可用性

8.4 性能优化建议

  1. 使用适当的容器运行时:containerd 比 Docker 更轻量,性能更好

  2. 优化节点配置

    • 关闭 Swap
    • 配置适当的内核参数
    • 使用高性能网络插件(如 Calico)
  3. 优化 Pod 调度

    • 使用节点亲和性将 Pod 调度到合适的节点
    • 使用 Pod 反亲和性避免单点故障
    • 考虑使用 PriorityClass 确保关键 Pod 优先调度
  4. 使用本地存储:对于有状态应用,考虑使用本地 SSD 提高性能

  5. 配置资源自动伸缩:根据实际负载调整资源分配

  6. 使用 Service Mesh:如 Istio,提供更精细的流量控制和监控

九、总结与展望

通过容器化和 Kubernetes,我们不仅解决了传统部署的诸多痛点,更构建了一个弹性、可靠、可扩展的应用平台,为业务的快速迭代和持续创新提供了坚实基础。

http://www.dtcms.com/a/457104.html

相关文章:

  • 哪里可以做足球网站虚拟主机 2个网站
  • 建设银行的英语网站首页dede导入wordpress
  • 支付宝小程序 MAU 增长新路径:生态工具与流量闭环的协同实战
  • C++ 成员初始化列表
  • 三门县住房和城乡建设规划局网站商业网站是怎么做的
  • Spring Security 最简配置完全指南-从入门到精通前后端分离安全配置
  • Go泛型实战指南:从入门到工程最佳实践|Go语言进阶(12)
  • easyexcel实现excel读取
  • 用jsp实现网站开发实例高校网站站群建设公司
  • 个人网站导航html源码团购网站模板
  • wpf之RelativeSource用法总结
  • 【C语言基础详细版】06. 动态内存管理:从原理到实战应用
  • 磁悬浮轴承转子不平衡质量控制深度解析
  • 关于力扣2025.10.8每日的收货
  • 烟台做网站的价格网络工程是冷门专业吗
  • 亲测可用,R语言 ggplot2 箱线图线条控制参数详解,箱线图离散数值控制
  • 沙漠风网站建设公司太原不错的互联网公司
  • 记录thinkphp模型查询时select与count执行顺序的疑问
  • AI编写的一个服务器监控源码
  • C# TCP 客户端开发笔记(TcpClient)
  • 网站建设数据库怎么弄个人养老金交15年领多少
  • Linux的Socket编程之TCP
  • ST-Raptor:无需微调,准确率超越 GPT-4o 的半结构化表格问答新范式
  • 深入洞察:华为BLM战略模型和BEM执行模型(图解)
  • wordpress跳转手机站wordpress 短代码 对齐
  • TNNLS-2022《Fast Incomplete Multi-view Clustering with View-independent Anchors》
  • 聊城网站开发培训公司网站可以免费建吗
  • 河南网站设计公司价格青岛企业网站seo技巧
  • 动态DP细谈
  • PHP Exception:深入理解与最佳实践