Kubernetes管理经验

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: 生产端kubernetes宝(血)贵(泪)经(教)验(训) 原文:https://www.zeusro.tech/2019/01/25/kubernetes-experience/

推荐工具

kubectx

kubectx:用来切换集群的访问

kubens:用来切换默认的namespace

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'

参考链接:

  1. 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依赖启动机制,建议使用wait-for-it重写镜像的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,后果相当严重,请注意

tainttolerations是结对对应存在的,操作符也不能乱用

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在那上面跑

ExistsExists随意使用,不是很影响

值得一提的是,同一个key可以同时存在多个effect

Taints:             elasticsearch-exclusive=true:NoExecute
                    elasticsearch-exclusive=true:NoSchedule

其他参考链接:

  1. Kubernetes中的Taint和Toleration(污点和容忍)
  2. 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

参考链接:

  1. Eviction Signals

节点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)
  1. 节点加了污点导致pod被驱逐
  2. ephemeral-storage超过限制被驱逐

    1. EmptyDir 的使用量超过了他的 SizeLimit,那么这个 pod 将会被驱逐
    2. Container 的使用量(log,如果没有 overlay 分区,则包括 imagefs)超过了他的 limit,则这个 pod 会被驱逐
    3. 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.

参考:

  1. Kubernetes pod ephemeral-storage配置
  2. 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

参考

  1. Configure Out Of Resource Handling
service connection refuse

原因可能有

  1. pod没有设置readinessProbe,请求到未就绪的pod
  2. kube-proxy宕机了(kube-proxy负责转发请求)
  3. 网络过载
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

参考链接:

  1. advanced-scheduling-in-kubernetes
  2. kubernetes-scheulder-affinity

使用反亲和度确保每个节点只跑同一个应用

      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-systemnginx-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。

  1. 对于这种存储,需要先把容器副本将为0,或者整个Statefulset删除。
  2. 删除PVC
  3. 把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的配额

参考(应用调度相关):

  1. kubernetes如何解决服务依赖呢?
  2. Kubernetes之路 1 - Java应用资源限制的迷思
  3. Control CPU Management Policies on the Node
  4. Reserve Compute Resources for System Daemons
  5. Configure Out Of Resource Handling
相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
运维 Kubernetes 大数据
利用 Kubernetes 降本增效?EasyMR 基于 Kubernetes 部署的探索实践
Kubernetes 是市面上最受欢迎的集群管理解决方案之一,本文将介绍 EasyMR 作为一款提供一站式可视化组件安装部署与可观测运维管理能力的大数据计算引擎产品,是如何基于 Kubernetes 部署进行实践探索的。
78 0
|
6月前
|
存储 Kubernetes API
Kubernetes:现代应用部署与管理的新篇章
Kubernetes:现代应用部署与管理的新篇章
151 0
|
存储 Kubernetes 监控
Kubernetes 架构知识
Kubernetes 架构知识
214 1
|
运维 Kubernetes 监控
Kubernetes 微服务 Pod 影响力
Kubernetes 微服务 Pod 影响力
244 1
|
运维 Kubernetes 监控
Kubernetes微服务Pod 影响力
Kubernetes微服务Pod 影响力
222 1
Kubernetes微服务Pod 影响力
|
Kubernetes 算法 Cloud Native
【云原生 | 从零开始学Kubernetes】六、Kubernetes核心技术Pod
我们以具体实例来说,拉取策略就是 imagePullPolicy
198 0
【云原生 | 从零开始学Kubernetes】六、Kubernetes核心技术Pod
|
存储 Kubernetes Cloud Native
【云原生 | 从零开始学Kubernetes】五、Kubernetes核心技术Pod
Pod是K8S系统中可以创建和管理的最小单元,是资源对象模型中由用户创建或部署的最小资源对象模型,也是在K8S上运行容器化应用的资源对象,其它的资源对象都是用来支撑或者扩展Pod对象功能的,比如控制器对象是用来管控Pod对象的,Service或者Ingress资源对象是用来暴露Pod引用对象的,PersistentVolume资源对象是用来为Pod提供存储等等,K8S不会直接处理容器,而是Pod,Pod是由一个或多个container组成。
198 0
【云原生 | 从零开始学Kubernetes】五、Kubernetes核心技术Pod
|
机器学习/深度学习 Kubernetes 监控
Kubernetes 企业如何落地
Kubernetes 企业如何落地
192 0
|
监控 Kubernetes 安全
如何为你的Kubernetes保驾护航
如何为你的Kubernetes保驾护航
如何为你的Kubernetes保驾护航
|
运维 Kubernetes 容器
管理Kubernetes的 5 件小事, 你都了解吗
管理Kubernetes的 5 件小事, 你都了解吗
156 0
管理Kubernetes的 5 件小事, 你都了解吗