kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
alpine-3835730047-ggn2v 1/1 Running 0 5d 10.22.19.69 ip-10-35-80-221.ec2.internal
kubectl get --all-namespaces --output json pods | jq '.items[] | select(.status.podIP=="10.22.19.69")' | jq .metadata.name
"alpine-3835730047-ggn2v"
kubectl get --all-namespaces --output json pods | jq '.items[] | select(.status.podIP=="10.22.19.69")' | jq .spec.containers[].name
"alpine"
另一种选择是使用 kubectl port-forward
将 JMX 端口从 K8S pod 转发到本地 PC。
我是这样做的:
1、在你的应用程序中添加以下JVM选项。
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.local.only=false
-Dcom.sun.management.jmxremote.port=1099
-Dcom.sun.management.jmxremote.rmi.port=1099
-Djava.rmi.server.hostname=127.0.0.1
这里的关键部分是:
jmxremote.port
和jmxremote.rmi.port
应该使用同一个端口,这是只需要转发一个端口。
127.0.0.1 应该作为 rmi 服务器主机名传递。这是 JMX 连接端口通过port-forwarding所必需的。
2、通过 kubectl 将 JMX 端口 (1099) 转发到您的本地 PC:
kubectl port-forward <your-app-pod> 1099
3、打开与本地端口 1099 的 jconsole 连接:
jconsole 127.0.0.1:1099
这种方式使得通过 JMX 调试任何 Java pod ,而无需通过 K8 服务公开暴露 JMX(从安全角度来看更好)。
快速手动删除所有被驱逐的pod,你可以使用这个命令:
kubectl get pods --all-namespaces -o json | jq '.items[] | select(.status.reason!=null) | select(.status.reason | contains("Evicted")) | "kubectl delete pods (.metadata.name) -n (.metadata.namespace)"' | xargs -n 1 bash -c
还有
kubectl -n default delete pods --field-selector=status.phase=Failed
删除全部namespace的:
kubectl get pods --all-namespaces --field-selector 'status.phase==Failed' -o json | kubectl delete -f -
我最近正在为我的kafka客户端工具解决同样的问题。挺麻烦的,但我从代码中发现的唯一方法是如下:
Properties props = ...//here you put your properties
AdminClient kafkaClient = AdminClient.create(props);
//Here you get all the consumer groups
List<String> groupIds = kafkaClient.listConsumerGroups().all().get().
stream().map(s -> s.groupId()).collect(Collectors.toList());
//Here you get all the descriptions for the groups
Map<String, ConsumerGroupDescription> groups = kafkaClient.
describeConsumerGroups(groupIds).all().get();
for (final String groupId : groupIds) {
ConsumerGroupDescription descr = groups.get(groupId);
//find if any description is connected to the topic with topicName
Optional<TopicPartition> tp = descr.members().stream().
map(s -> s.assignment().topicPartitions()).
flatMap(coll -> coll.stream()).
filter(s -> s.topic().equals(topicName)).findAny();
if (tp.isPresent()) {
//you found the consumer, so collect the group id somewhere
}
}
用的是kafkadmin客户端,参考来自:
从容器的角度出发,限制容器使用的CPU和内存,是通过cgroup来实现的,目前kubernetes的QoS只能管理CPU和内存,所以kubernetes现在也是通过对cgroup的配置来实现QoS管理的。
在kubernetes中,每个Pod都有个QoS标记,QoS的英文全称为 Quality of Service
,中文名为服务质量
,它取决于用户对服务质量的预期,也就是期望的服务质量。对于Pod来说,服务质量体现在两个指标上,一个指标是CPU,另一个指标是内存。在实际运行过程中,当NODE节点
上内存资源紧张的时候,kubernetes根据Pod具有的不同QoS标记,采取不同调度和驱逐策略。
Kubernetes 创建 Pod 时就给它指定了下列一种 QoS 类:
如果满足下面条件,将会指定 Pod 的 QoS 类为 Burstable:
1、当 NODE节点上内存资源不够的时候,QoS级别是BestEffort的Pod会最先被kill掉;当NODE节点上内存资源充足的时候,QoS级别是BestEffort的POD可以使用NODE节点上剩余的所有内存资源。
2、当NODE节点上内存资源不够的时候,如果QoS级别是BestEffort的Pod已经都被kill掉了,那么会查找QoS级别是Burstable的POD,并且这些POD使用的内存已经超出了requests设置的内存值,这些被找到的POD会被kill掉;当NODE节点上内存资源充足的时候,QoS级别是Burstable的POD会按照requests和limits的设置来使用。
3、当NODE节点上内存资源不够的时候,如果QoS级别是BestEffort和Burstable的Ppod都已经被kill掉了,那么会查找QoS级别是Guaranteed的POD,并且这些POD使用的内存已经超出了limits设置的内存值,这些被找到的POD会被kill掉;当NODE节点上内存资源充足的时候,QoS级别是Burstable的POD会按照requests和limits的设置来使用。
详细参考:配置Pod的服务质量(QoS)
我们先了解下容器在 Kubernetes 环境中的终止流程:
preStop Hook
,将会执行。terminationGracePeriodSeconds
内 (默认 30s) 还未完全停止,就发送 SIGKILL 信号强制杀死进程。SIGTERM
信号要实现优雅终止,务必在业务代码里面处理下 SIGTERM 信号,参考 处理 SIGTERM 代码示例 ) 。
别让 shell 导致收不到 SIGTERM 信号
如果容器启动入口使用了脚本 (如 CMD ["/start.sh"]),业务进程就成了 shell 的子进程,在 Pod 停止时业务进程可能收不到 SIGTERM 信号,因为 shell 不会自动传递信号给子进程。更详细解释请参考 为什么我的容器收不到 SIGTERM 信号 ?
合理使用 preStop Hook
若你的业务代码中没有处理 SIGTERM 信号,或者你无法控制使用的第三方库或系统来增加优雅终止的逻辑,也可以尝试为 Pod 配置下 preStop,在这里面实现优雅终止的逻辑,示例:
lifecycle:
preStop:
exec:
command:
- /clean.sh
在某些极端情况下,Pod 被删除的一小段时间内,仍然可能有新连接被转发过来,因为 kubelet 与 kube-proxy 同时 watch 到 pod 被删除,kubelet 有可能在 kube-proxy 同步完规则前就已经停止容器了,这时可能导致一些新的连接被转发到正在删除的 Pod,而通常情况下,当应用受到 SIGTERM 后都不再接受新连接,只保持存量连接继续处理,所以就可能导致 Pod 删除的瞬间部分请求失败。
这种情况下,我们也可以利用 preStop 先 sleep 一小下,等待 kube-proxy 完成规则同步再开始停止容器内进程:
lifecycle:
preStop:
exec:
command:
- sleep
- 5s
如果需要的优雅终止时间比较长 (preStop + 业务进程停止可能超过 30s),可根据实际情况自定义 terminationGracePeriodSeconds
,避免过早的被 SIGKILL
杀死:
使用kubectl describe pod
来查看具体的报错原因:
1、使用如下命令查看 Pod 状态:
$ kubectl get pods
2、可以使用 kubectl describe pod
命令来查询问题 Pod 的更多信息,比如:
kubectl describe pod nginx-deployment-1006230814-6winp
查看Events:
段,可以看到具体失败的原因。
你还可以看到与 Pod 相关的近期事件。系统通过指示第一次和最后一次看到事件以及看到该事件的次数来压缩多个相同的事件。From
标明记录事件的组件,SubobjectPath
告诉你引用了哪个对象(例如 Pod 中的容器), Reason
和Message
告诉你发生了什么。
如果需要列出所有事件,可使用命令:
kubectl get events
但是,需要注意的是,事件是区分名字空间的。 如果你对某些名字空间域的对象(比如 my-namespace 名字下的 Pod)的事件感兴趣, 你需要显式地在命令行中指定名字空间:
kubectl get events --namespace=my-namespace
详细kubernetes pods故障排查,可参考:Kubernetes(k8s)应用程序自测与调试
将需要维护的节点设置为不可用,并重新调度其上运行的所有pod。
步骤:
# 隔离节点
kubectl cordon ek8s-node-1
# 驱逐其上运行的pod
kubectl drain ek8s-node-1 --ignore-daemonsets --delete-local-data --force