Kubernetes 开发随笔
常用命令
创建命令
- 创建 Pod
1 | # kubectl run <pod-name> --image=<image-name> |
- 创建 Deployment
1 | # kubectl create deployment <deployment-name> --image=<image-name> |
- 创建 Service(暴露服务)
1 | # 为指定的Deployment暴露服务 |
查看命令
- 查看所有 Service
1 | # kubectl get svc |
- 查看所有 Deployment
1 | # kubectl get deployments |
- 查看所有 Secret
1 | # kubectl get secrets |
- 查看所有 ConfigMap
1 | # kubectl get configmaps |
- 查看所有 Pode 的运行状态
1 | # 查看所有Pode的运行状态 |
- 查看指定 Pod 的日志信息
1 | # 查看指定Pod的日志信息 |
- 查看指定 Pod 的状态、事件、镜像拉取、容器日志、调度信息等详细运行情况
1 | # 查看指定Pod的详细运行情况 |
连接命令
- 连接进入指定 Pod 内部的容器
1 | # 连接进入指定 Pod 内部的容器 |
1 | # 如果Pod内部有多个容器(一个Pod可以有多个容器),可以使用 -c 参数指定具体的容器名称,容器名称可以使用 kubectl describe pod <pod-name> 命令获取得到 |
Pod 的管理
使用命令创建 Pod
- 在 Kubernetes 集群的 Master 节点中执行以下命令,通过 Deployment 创建一个 Nginx 的 Pod,并使用 Service 对外暴露服务
1 | # 创建Nginx |
- 在 Kubernetes 集群的外部,通过浏览器访问
http://<node-ip>:31603,其中 IP 可以是任意集群节点的 IP 地址(比如192.168.1.5),端口号可以通过kubectl get svc命令获取得到。若 Ngninx 容器在 Kubernetes 集群中创建并启动成功,则浏览器可以正常访问 Nginx 的首页(如下图所示)。

- 若希望删除刚才在 Kubernetes 集群中创建的 Pod,可以在 Master 节点上执行以下命令:
1 | # 删除Service |
特别注意
- 在 Kubernetes 集群中,除了可以使用上述的方式通过 Deployment 来间接创建 Pod 外,还可以使用命令直接创建 Pod,比如
kubectl run mypod --image=nginx --restart=Never,删除 Pod 可以使用命令kubectl delete pod mypod。 - 在较新的 Kubernetes 版本(
v1.19及以上)中,kubectl run命令无论是否指定--restart=Never,都会直接创建一个 Pod,而不会再创建 Deployment、ReplicaSet 或 Job 等控制器。 - 在 Kubernetes 早期版本(
v1.18及之前)中,kubectl run默认会创建 Deployment(即--restart=Always时),此行为在后续新版本中已被废弃。 - 因此,在现代 Kubernetes 中,
kubectl run命令仅用于快速创建测试用的 Pod。
最佳实践
平滑重启 Java 应用
术语说明
- Kubernetes 命令
kubectl rollout restart会触发一次 Deployment 滚动更新(Rolling Update),从而达到滚动重启的效果。 - 在社区里,也常将该命令称为 "滚动重启",这属于口语化叫法;因为它在效果上达到了不停服、一个一个平滑替换 Pod,看起来像 "重启",所以滚动重启是现象,滚动更新是机制。
Java 应用平滑重启的概念
- 在 Kubernetes 中实现 Java 应用程序的平滑重启,本质上是通过 Deployment 滚动更新(Rolling Update)的方式实现,也就是逐个重建 Pod,而不是将全部 Pod 停掉再重建,这样可以保证服务在重启过程中尽量不丢失请求。
- 所谓 Java 应用平滑重启(Graceful Restart)有两个关键点:
- (1) 不中断现有请求:Java 应用在收到
SIGTERM信号时,应该先停止接收新请求,但允许正在处理的请求完成,再退出。 - (2) 保证服务可用性:Kubernetes 通过 Deployment 的滚动更新或 Pod 生命周期钩子来保证至少有一部分实例在运行,避免服务全部不可用。
- (1) 不中断现有请求:Java 应用在收到
Kubernetes 滚动更新的机制
- Kubernetes 可以使用命令
kubectl rollout restart deployment <deployment-name>实现 Deployment 滚动更新(Rolling Update) - Kubernetes 执行 Deployment 滚动更新(Rolling Update)后,实际上会:
- 更新 Deployment 的一个字段(通常是
spec.template.metadata.annotations,加上一个时间戳); - 由于模板变更,Deployment Controller 认为需要 “滚动更新”;
- 逐个创建新 Pod,删除旧 Pod,且遵守
spec.strategy.rollingUpdate策略,比如:1
2
3
4
5
6
7apiVersion: apps/v1
kind: Deployment
strategy:
type: RollingUpdate # 使用滚动更新方式(默认),逐个替换旧 Pod,保证服务不中断
rollingUpdate:
maxUnavailable: 0 # 滚动更新期间不可用的 Pod 数量最多允许 0 个,保证全部 Pod 始终保持可用
maxSurge: 1 # 滚动更新期间最多只允许额外创建 1 个新 Pod,保证有新 Pod 启动后才关闭旧 PodmaxSurge = 1表示每次更新期间最多只允许额外创建一个新 Pod;maxUnavailable = 0表示保证全部 Pod 始终保持可用;maxUnavailable与maxSurge的默认值如下,如果希望 Deployment 滚动更新期间 Pod 保持 100% 可用,可以设置maxUnavailable = 0和maxSurge = 1。字段 默认值 含义 maxUnavailable25%更新时最多允许不可用的 Pod 数量(百分比或整数)。默认是 25%,即如果有 4 个副本,最多可以有 1 个 Pod 不可用。maxSurge25%更新时允许额外创建的 Pod 数量(百分比或整数)。默认是 25%,即如果有 4 个副本,最多可以额外启动 1 个新 Pod。
- 更新 Deployment 的一个字段(通常是
Java 应用平滑重启的实现
- 在以下条件下(缺一不可),Kubernetes 可以做到几乎无感重启 Java 应用(不中断现有请求):
- (1) Deployment 的滚动更新策略配置合理,比如:
1
2
3
4
5
6
7apiVersion: apps/v1
kind: Deployment
strategy:
type: RollingUpdate # 使用滚动更新方式(默认),逐个替换旧 Pod,保证服务不中断
rollingUpdate:
maxUnavailable: 0 # 更新期间不可用的 Pod 数量最多允许 0 个,确保 Pod 始终保持满负载可用
maxSurge: 1 # 更新时额外创建 1 个新 Pod,保证有新 Pod 启动后才关闭旧 Pod - (2) Pod 的
readinessProbe就绪探针配置正确,比如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: my-java-app
image: myrepo/my-java-app:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /actuator/health # 发送 HTTP 请求进行检测
port: 8080
initialDelaySeconds: 10 # 容器启动后等待 10 秒开始检查
periodSeconds: 3 # 每 3 秒检查一次
successThreshold: 1 # 成功 1 次即标记为就绪- 就绪探针未通过的 Pod 不会被分配到服务流量,保证请求不会被直接丢弃;
- 只有当 Java 应用真正启动并能响应健康检查时,Kubernetes 才会把流量分配过去;
- 这可以避免 Java 应用刚启动就到接收请求的问题。
- (3) 通过
terminationGracePeriodSeconds指定 Pod 在接收到SIGTERM信号后允许的优雅终止等待时间,比如:1
2
3
4
5
6
7
8
9
10
11apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
terminationGracePeriodSeconds: 60 # Pod 优雅终止的最大等待时间(秒),K8s 会先发送 SIGTERM 信号,给容器最多 60 秒时间处理完当前所有请求并退出,超时后发送 SIGKILL 信号强制终止 Pod
containers:
- name: my-java-app
image: myrepo/my-java-app:latest
ports:
- containerPort: 8080- 针对 Java 应用,Pod 的
terminationGracePeriodSeconds通常设置为 30 ~ 60 秒,给 Java 应用足够时间处理完当前所有请求,再将其对应的容器杀掉; - 在此期间,容器可以自行处理收尾工作。如果超时仍未退出,Kubernetes 会发送
SIGKILL信号强制终止 Pod。
- 针对 Java 应用,Pod 的
- (4) Java 应用支持优雅关机(Graceful Shutdown),比如:
- 如果是 Java 普通应用,开发者可以自行监听
SIGTERM信号,处理收尾工作1
2
3
4
5Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("Graceful shutdown initiated...");
// 停止接收新请求
// 等待正在处理请求完成
})); - 如果是 SpringBoot(
2.3及以上版本)Web 应用,可以开启优雅停机,开发者通常无需自己监听SIGTERM信号1
2
3
4
5server:
shutdown: graceful # 开启优雅停机(Graceful Shutdown),让应用在关闭前有时间处理完当前所有请求
spring:
lifecycle:
timeout-per-shutdown-phase: 60s # 优雅停机的最长等待时间,超时后强制停止(默认 30 秒,可根据实际需要调整)
- 如果是 Java 普通应用,开发者可以自行监听
- (1) Deployment 的滚动更新策略配置合理,比如:
Java 应用平滑重启的注意事项
纯 SpringBoot Web 应用:
- 只需要配置
server.shutdown=graceful和spring.lifecycle.timeout-per-shutdown-phase,开启优雅关机; - SpringBoot 会自动在接收到
SIGTERM时:- 停止接收新的 HTTP 请求(Tomcat / Netty 等 Web 服务器)。
- 等待正在处理的请求完成,最长等待时间由
spring.lifecycle.timeout-per-shutdown-phase决定。 - 关闭 Spring 上下文及 Bean。
- 整个流程已经自动实现了 “优雅停机”,开发者不需要额外写 Shutdown Hook。
- 只需要配置
如果在 SpringBoot Web 应用内有额外的线程 / 资源:
- 需要开发者自己实现资源关闭逻辑,可用
@PreDestroy注解或 Shutdown Hook 实现。
- 需要开发者自己实现资源关闭逻辑,可用
开发者什么时候需要自己监听 SIGTERM
- 如果 Java 应用中有非 Spring 管理的异步线程或任务,比如:
- 自己创建的
Thread(线程) 或ExecutorService(线程池); - 消息队列消费者(Kafka / RabbitMQ);
- 这类资源需要手动在 Shutdown Hook 或者 Spring Bean 的
@PreDestroy中关闭,避免被强制 Kill 掉,比如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void cleanup() {
if (threadPool != null && !threadPool.isShutdown()) {
// 优雅关闭线程池
threadPool.shutdown();
try {
// 阻塞当前线程 50 秒,让线程池中等待执行的任务和正在执行的任务执行完成
if (!threadPool.awaitTermination(50, TimeUnit.SECONDS)) {
// 等待超时后,立刻关闭线程池
threadPool.shutdownNow();
// 再次阻塞当前线程 5 秒,然后检查线程池是否已经关闭
if (!threadPool.awaitTermination(5, TimeUnit.SECONDS)) {
System.out.println("Thread pool did not terminate");
}
}
} catch (Exception e) {
// 捕获到异常后,立刻关闭线程池
threadPool.shutdownNow();
// 捕获到异常后,中断当前线程
Thread.currentThread().interrupt();
}
}
}
- 自己创建的
- 如果 Java 应用中有非 Spring 管理的异步线程或任务,比如:
