推荐工具
https://github.com/ahmetb/kubectx
kubectx: 用来切换集群的访问
kubens: 用来切换默认的 namespace
https://github.com/ahmetb/kubectl-aliases
kubectl 命令别名
集群管理相关命令
- kubectl get cs
- # 查看节点
- kubectl get nodes
- kubectl get ing pdd --n java
- # 不调度
- kubectl taint nodes node1 key=value:NoSchedule
- kubectl cluster-info dump
- kubectl get svc --sort-by=.metadata.creationTimestamp
- kubectl get no --sort-by=.metadata.creationTimestamp
- kubectl get po --field-selector spec.nodeName=xxxx
- kubectl get events --field-selector involvedObject.kind=Service --sort-by='.metadata.creationTimestamp'
参考链接:
kubernetes 节点维护 cordon, drain, uncordon
应用管理相关
- kubectl top pod
- kubectl delete deployment,services -l App=nginx
- kubectl scale deployment/nginx-deployment --replicas=2
- kubectl get svc --all-namespaces=true
强制删除
有时 删除 pv/pvc 时会有问题, 这个使用得加 2 个命令参数 --grace-period=0 --force
删除所有失败的 pod
- kubectl get po --all-namespaces --field-selector 'status.phase==Failed'
- kubectl delete po --field-selector 'status.phase==Failed'
一些技巧
k8s 目前没有没有类似 docker-compose 的 depends_on 依赖启动机制, 建议使用重写镜像的 command.
集群管理经 (教) 验(训)
节点问题
taint 别乱用
- kubectl taint nodes xx Elasticsearch-test-ready=true:NoSchedule
- kubectl taint nodes xx Elasticsearch-test-ready:NoSchedule-
master 节点本身就自带 taint, 所以才会导致我们发布的容器不会在 master 节点上面跑. 但是如果自定义 taint 的话就要注意了! 所有 DaemonSet 和 kube-system, 都需要带上相应的 tolerations. 不然该节点会驱逐所有不带这个 tolerations 的容器, 甚至包括网络插件, kube-proxy, 后果相当严重, 请注意
taint 跟 tolerations 是结对对应存在的, 操作符也不能乱用
- NoExecute
- tolerations:
- - key: "elasticsearch-exclusive"
- operator: "Equal"
- value: "true"
- effect: "NoExecute"
- kubectl taint node cn-shenzhen.xxxx Elasticsearch-exclusive=true:NoExecute
NoExecute 是立刻驱逐不满足容忍条件的 pod, 该操作非常凶险, 请务必先行确认系统组件有对应配置 tolerations.
特别注意用 Exists 这个操作符是无效的, 必须用 Equal
- NoSchedule
- tolerations:
- - key: "elasticsearch-exclusive"
- operator: "Exists"
- effect: "NoSchedule"
- - key: "elasticsearch-exclusive"
- operator: "Equal"
- value: "true"
- effect: "NoExecute"
- kubectl taint node cn-shenzhen.xxxx Elasticsearch-exclusive=true:NoSchedule
是尽量不往这上面调度, 但实际上还是会有 pod 在那上面跑
Exists 和 Exists 随意使用, 不是很影响
值得一提的是, 同一个 key 可以同时存在多个 effect
- Taints: Elasticsearch-exclusive=true:NoExecute
- Elasticsearch-exclusive=true:NoSchedule
其他参考链接:
Kubernetes 中的 Taint 和 Toleration(污点和容忍)
kubernetes 的调度机制
隔离节点的正确步骤
- # 驱逐除了 ds 以外所有的 pod
- kubectl drain <node name> --ignore-daemonsets
- kubectl cordon <node name>
这个时候运行 get node 命令, 状态会变
node.xx Ready,SchedulingDisabled <none> 189d v1.11.5
最后
kubectl delete <node name>
维护节点的正确步骤
- kubectl drain <node name> --ignore-daemonsets
- kubectl uncordon <node name>
节点出现磁盘压力(DiskPressure)
--eviction-hard=imagefs.available<15%,memory.available<300Mi,nodefs.available<10%,nodefs.inodesFree<5%
kubelet 在启动时指定了磁盘压力, 以阿里云为例, imagefs.available<15% 意思是说容器的读写层少于 15% 的时候, 节点会被驱逐. 节点被驱逐的后果就是产生 DiskPressure 这种状况, 并且节点上再也不能运行任何镜像, 直至磁盘问题得到解决. 如果节点上容器使用了宿主目录, 这个问题将会是致命的. 因为你不能把目录删除掉, 但是真是这些宿主机的目录堆积, 导致了节点被驱逐.
所以, 平时要养好良好习惯, 容器里面别瞎写东西(容器里面写文件会占用 ephemeral-storage,ephemeral-storage 过多 pod 会被驱逐), 多使用无状态型容器, 谨慎选择存储方式, 尽量别用 hostpath 这种存储
出现状况时, 真的有种欲哭无泪的感觉.
- Events:
- Type Reason Age From Message
- ---- ------ ---- ---- -------
- Warning FreeDiskSpaceFailed 23m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 5182058496 bytes, but freed 0 bytes
- Warning FreeDiskSpaceFailed 18m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 6089891840 bytes, but freed 0 bytes
- Warning ImageGCFailed 18m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 6089891840 bytes, but freed 0 bytes
- Warning FreeDiskSpaceFailed 13m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 4953321472 bytes, but freed 0 bytes
- Warning ImageGCFailed 13m kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 4953321472 bytes, but freed 0 bytes
- Normal NodeHasNoDiskPressure 10m (x5 over 47d) kubelet, node.xxxx1 Node node.xxxx1 status is now: NodeHasNoDiskPressure
- Normal Starting 10m kube-proxy, node.xxxx1 Starting kube-proxy.
- Normal NodeHasDiskPressure 10m (x4 over 42m) kubelet, node.xxxx1 Node node.xxxx1 status is now: NodeHasDiskPressure
- Warning EvictionThresholdMet 8m29s (x19 over 42m) kubelet, node.xxxx1 Attempting to reclaim ephemeral-storage
- Warning ImageGCFailed 3m4s kubelet, node.xxxx1 failed to garbage collect required amount of images. Wanted to free 4920913920 bytes, but freed 0 bytes
参考链接:
Eviction Signals
10 张图带你深入理解 Docker 容器和镜像 http://dockone.io/article/783
节点 CPU 彪高
有可能是节点在进行 GC(container GC/image GC), 用 describe node 查查. 我有次遇到这种状况, 最后节点上的容器少了很多, 也是有点郁闷
- Events:
- Type Reason Age From Message
- ---- ------ ---- ----
- Warning ImageGCFailed 45m kubelet, cn-shenzhen.xxxx failed to get image stats: rpc error: code = DeadlineExceeded desc = context deadline exceeded
参考:
kubelet 源码分析: Garbage Collect
对象问题
pod
pod 频繁重启
原因有多种, 不可一概而论
资源达到 limit 设置值
调高 limit 或者检查应用
Readiness/Liveness connection refused
Readiness 检查失败的也会重启, 但是 Readiness 检查失败不一定是应用的问题, 如果节点本身负载过重, 也是会出现 connection refused 或者 timeout
这个问题要上节点排查
pod 被驱逐(Evicted)
节点加了污点导致 pod 被驱逐
ephemeral-storage 超过限制被驱逐
EmptyDir 的使用量超过了他的 SizeLimit, 那么这个 pod 将会被驱逐
Container 的使用量 (log, 如果没有 overlay 分区, 则包括 imagefs) 超过了他的 limit, 则这个 pod 会被驱逐
Pod 对本地临时存储总的使用量 (所有 emptydir 和 container) 超过了 pod 中所有 container 的 limit 之和, 则 pod 被驱逐
ephemeral-storage 是一个 pod 用的临时存储.
- resources:
- requests:
- ephemeral-storage: "2Gi"
- limits:
- ephemeral-storage: "3Gi"
节点被驱逐后通过 get po 还是能看到, 用 describe 命令, 可以看到被驱逐的历史原因
Message: The node was low on resource: ephemeral-storage. Container codis-proxy was using 10619440Ki, which exceeds its request of 0.
参考:
Kubernetes pod ephemeral-storage 配置
Managing Compute Resources for Containers
kubectl exec 进入容器失败
这种问题我在搭建 codis-server 的时候遇到过, 当时没有配置就绪以及健康检查. 但获取 pod 描述的时候, 显示 running. 其实这个时候容器以及不正常了.
- ~ kex codis-server-3 sh
- rpc error: code = 2 desc = containerd: container not found
- command terminated with exit code 126
解决办法: 删了这个 pod, 配置 livenessProbe
pod 的 virtual host name
Deployment 衍生的 pod,virtual host name 就是 pod name.
StatefulSet 衍生的 pod,virtual host name 是 < pod name>.<svc name>.<namespace>.svc.cluster.local. 相比 Deployment 显得更有规律一些. 而且支持其他 pod 访问
pod 接连 Crashbackoff
Crashbackoff 有多种原因.
沙箱创建 (FailedCreateSandBox) 失败, 多半是 cni 网络插件的问题
镜像拉取, 有中国特色社会主义的问题, 可能太大了, 拉取较慢
也有一种可能是容器并发过高, 流量雪崩导致.
比如, 现在有 3 个容器 abc,a 突然遇到流量洪峰导致内部奔溃, 继而 Crashbackoff, 那么 a 就会被 service 剔除出去, 剩下的 bc 也承载不了那么多流量, 接连崩溃, 最终网站不可访问. 这种情况, 多见于高并发网站 + 低效率 web 容器.
在不改变代码的情况下, 最优解是增加副本数, 并且加上 hpa, 实现动态伸缩容.
- deploy
- MinimumReplicationUnavailable
如果 deploy 配置了 SecurityContext, 但是 API-server 拒绝了, 就会出现这个情况, 在 API-server 的容器里面, 去掉 SecurityContextDeny 这个启动参数.
具体见 Using Admission Controllers
service
建了一个服务, 但是没有对应的 po, 会出现什么情况?
请求时一直不会有响应, 直到 request timeout
参考
- Configure Out Of Resource Handling
- service connection refuse
原因可能有
pod 没有设置 readinessProbe, 请求到未就绪的 pod
kube-proxy 宕机了(kube-proxy 负责转发请求)
网络过载
service 没有负载均衡
检查一下是否用了 headless service.headless service 是不会自动负载均衡的...
- kind: Service
- spec:
- # clusterIP: None 的即为 `headless service`
- type: ClusterIP
- clusterIP: None
具体表现 service 没有自己的虚拟 IP,nslookup 会出现所有 pod 的 ip. 但是 ping 的时候只会出现第一个 pod 的 ip
- / # nslookup consul
- nslookup: can't resolve'(null)': Name does not resolve
- Name: consul
- Address 1: 172.31.10.94 172-31-10-94.consul.default.svc.cluster.local
- Address 2: 172.31.10.95 172-31-10-95.consul.default.svc.cluster.local
- Address 3: 172.31.11.176 172-31-11-176.consul.default.svc.cluster.local
- / # ping consul
- PING consul (172.31.10.94): 56 data bytes
- 64 bytes from 172.31.10.94: seq=0 ttl=62 time=0.973 ms
- 64 bytes from 172.31.10.94: seq=1 ttl=62 time=0.170 ms
- ^C
- --- consul ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.170/0.571/0.973 ms
- / # ping consul
- PING consul (172.31.10.94): 56 data bytes
- 64 bytes from 172.31.10.94: seq=0 ttl=62 time=0.206 ms
- 64 bytes from 172.31.10.94: seq=1 ttl=62 time=0.178 ms
- ^C
- --- consul ping statistics ---
- 2 packets transmitted, 2 packets received, 0% packet loss
- round-trip min/avg/max = 0.178/0.192/0.206 ms
普通的 type: ClusterIP service,nslookup 会出现该服务自己的 IP
- / # nslookup consul
- nslookup: can't resolve'(null)': Name does not resolve
- Name: consul
- Address 1: 172.30.15.52 consul.default.svc.cluster.local
ReplicationController 不更新
ReplicationController 不是用 apply 去更新的, 而是 kubectl rolling-update, 但是这个指令也废除了, 取而代之的是 kubectl rollout. 所以应该使用 kubectl rollout 作为更新手段, 或者懒一点, apply file 之后, delete po.
尽量使用 deploy 吧.
StatefulSet 更新失败
StatefulSet 是逐一更新的, 观察一下是否有 Crashbackoff 的容器, 有可能是这个容器导致更新卡住了, 删掉即可.
进阶调度
使用亲和度确保节点在目标节点上运行
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: Elasticsearch-test-ready
- operator: Exists
参考链接:
使用反亲和度确保每个节点只跑同一个应用
- affinity:
- podAntiAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- - labelSelector:
- matchExpressions:
- - key: 'app'
- operator: In
- values:
- - nginx-test2
- topologyKey: "kubernetes.io/hostname"
- namespaces:
- - test
容忍运行
master 节点之所以不允许普通镜像, 是因为 master 节点带了污点, 如果需要强制在 master 上面运行镜像, 则需要容忍相应的污点.
- tolerations:
- - effect: NoSchedule
- key: node-role.kubernetes.io/master
- operator: Exists
- - effect: NoSchedule
- key: node.cloudprovider.kubernetes.io/uninitialized
- operator: Exists
阿里云 Kubernetes 问题
修改默认 ingress
新建一个指向 ingress 的负载均衡型 svc, 然后修改一下 kube-system 下 nginx-ingress-controller 启动参数.
- - args:
- - /nginx-ingress-controller
- - '--configmap=$(POD_NAMESPACE)/nginx-configuration'
- - '--tcp-services-configmap=$(POD_NAMESPACE)/tcp-services'
- - '--udp-services-configmap=$(POD_NAMESPACE)/udp-services'
- - '--annotations-prefix=nginx.ingress.kubernetes.io'
- - '--publish-service=$(POD_NAMESPACE)/<自定义 svc>'
- - '--v=2'
LoadBalancer 服务一直没有 IP
具体表现是 EXTERNAL-IP 一直显示 pending.
- ~ kg svc consul-Web
- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
- consul-Web LoadBalancer 172.30.13.122 <pending> 443:32082/TCP 5m
这问题跟 Alibaba Cloud Provider 这个组件有关, cloud-controller-manager 有 3 个组件, 他们需要内部选主, 可能哪里出错了, 当时我把其中一个出问题的 pod 删了, 就好了.
清理 Statefulset 动态 PVC
目前阿里云 Statefulset 动态 PVC 用的是 nas.
对于这种存储, 需要先把容器副本将为 0, 或者整个 Statefulset 删除.
删除 PVC
把 nas 挂载到任意一台服务器上面, 然后删除 pvc 对应 nas 的目录.
升级到 v1.12.6-aliyun.1 之后节点可分配内存变少
该版本每个节点保留了 1Gi, 相当于整个集群少了 N GB(N 为节点数)供 Pod 分配.
如果节点是 4G 的, Pod 请求 3G, 极其容易被驱逐.
建议提高节点规格.
Server Version: version.Info{Major:"1", Minor:"12+", GitVersion:"v1.12.6-aliyun.1", GitCommit:"8cb561c", GitTreeState:"", BuildDate:"2019-04-22T11:34:20Z", GoVersion:"go1.10.8", Compiler:"gc", Platform:"linux/amd64"}
新加节点出现 NetworkUnavailable
RouteController failed to create a route
看一下 kubernetes events, 是否出现了
timed out waiting for the condition -> WaitCreate: ceate route for table vtb-wz9cpnsbt11hlelpoq2zh error, Aliyun API Error: RequestId: 7006BF4E-000B-4E12-89F2-F0149D6688E4 Status Code: 400 Code: QuotaExceeded Message: Route entry quota exceeded in this route table
出现这个问题是因为达到了 VPC 的自定义路由条目限制, 默认是 48, 需要提高 vpc_quota_route_entrys_num 的配额
参考(应用调度相关):
Kubernetes 之健康检查与服务依赖处理 http://dockone.io/article/2587
kubernetes 如何解决服务依赖呢?
Kubernetes 之路 1 - Java 应用资源限制的迷思
- Control CPU Management Policies on the Node
- Reserve Compute Resources for System Daemons
- Configure Out Of Resource Handling
来源: https://yq.aliyun.com/articles/703971