美文网首页
kubernetes笔录

kubernetes笔录

作者: davisgao | 来源:发表于2018-12-13 17:31 被阅读0次

1.Etcd的容量

集群的节点数的多少除了要顾及性能压力外,还要考虑etcd的存储容量。可以反向的从内存大小去推断一个集群中能够支持的节点数目,以及缓存大小(此处的缓存值反序列化缓存大小)。官方的建议是120G的主机可以支持2000个节点,由此推断,每个节点的容量大概在60M左右,但是随着版本的更新,存入Etcd的数据和结构可能发生变化,该值也是需要调整的。

  序列化缓存大小计算:
  TargetRAMMB 为主机内存大小
  clusterSize := s.GenericServerRunOptions.TargetRAMMB / 60
  DeserializationCacheSize = 25 * clusterSize < 1000 ? 1000 :25 * clusterSize 

2.controller-manager

  • controller-manager启动之初,如果发现apiserver未启动(/healthz地址访问不通),会等待10s.
  • controller-manager对控制器的检查都是动态的,所以重启apiserver无需重启controller-manager。
  • controller-manager中每个控制器内部都会有一个queue用来存贮watch到的事件,同时这个队列是通过令牌桶做了限流处理,默认情况下最小延迟5毫秒,最大延迟1000秒。
    return NewMaxOfRateLimiter(
            NewItemExponentialFailureRateLimiter(5*time.Millisecond, 1000*time.Second),
            // 10 qps, 100 bucket size.  This is only for retry speed and its only the overall factor (not per item)
            &BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(10), 100)},
      )
    
  • controller-manager对pod的gc处理主要包括三种情况
    1.gc掉超过阈值限制的pod,按时间排序gc
    func (gcc *PodGCController) gcTerminated(pods []*v1.Pod) {
      terminatedPods := []*v1.Pod{}
      for _, pod := range pods {
          if isPodTerminated(pod) {
              terminatedPods = append(terminatedPods, pod)
          }
      }
    
      terminatedPodCount := len(terminatedPods)
      sort.Sort(byCreationTimestamp(terminatedPods))
    
      deleteCount := terminatedPodCount - gcc.terminatedPodThreshold
    
      if deleteCount > terminatedPodCount {
          deleteCount = terminatedPodCount
      }
      if deleteCount > 0 {
          glog.Infof("garbage collecting %v pods", deleteCount)
      }
    
      var wait sync.WaitGroup
      for i := 0; i < deleteCount; i++ {
          wait.Add(1)
          go func(namespace string, name string) {
              defer wait.Done()
              if err := gcc.deletePod(namespace, name); err != nil {
                  // ignore not founds
                  defer utilruntime.HandleError(err)
              }
          }(terminatedPods[i].Namespace, terminatedPods[i].Name)
      }
      wait.Wait()
    }
    

2.gc掉孤儿pod:pod上的node信息不在当前可调度的节点上,即没有和有效node绑定

   ```
   func (gcc *PodGCController) gcOrphaned(pods []*v1.Pod) {
    glog.V(4).Infof("GC'ing orphaned")
  // We want to get list of Nodes from the etcd, to make sure that it's as fresh as possible.
  nodes, err := gcc.kubeClient.CoreV1().Nodes().List(metav1.ListOptions{})
  if err != nil {
    return
  }
  nodeNames := sets.NewString()
  for i := range nodes.Items {
    nodeNames.Insert(nodes.Items[i].Name)
  }

  for _, pod := range pods {
    if pod.Spec.NodeName == "" {
        continue
    }
    if nodeNames.Has(pod.Spec.NodeName) {
        continue
    }
    glog.V(2).Infof("Found orphaned Pod %v assigned to the Node %v. Deleting.", pod.Name, pod.Spec.NodeName)
    if err := gcc.deletePod(pod.Namespace, pod.Name); err != nil {
        utilruntime.HandleError(err)
    } else {
        glog.V(0).Infof("Forced deletion of orphaned Pod %s succeeded", pod.Name)
    }
  }
}

3.gc掉没有调度成功的pod:表现在pod的NodeName为空,主要由于资源等条件不满足

func (gcc *PodGCController) gcUnscheduledTerminating(pods []*v1.Pod) {
    glog.V(4).Infof("GC'ing unscheduled pods which are terminating.")

    for _, pod := range pods {
        if pod.DeletionTimestamp == nil || len(pod.Spec.NodeName) > 0 {
            continue
        }

        glog.V(2).Infof("Found unscheduled terminating Pod %v not assigned to any Node. Deleting.", pod.Name)
        if err := gcc.deletePod(pod.Namespace, pod.Name); err != nil {
            utilruntime.HandleError(err)
        } else {
            glog.V(0).Infof("Forced deletion of unscheduled terminating Pod %s succeeded", pod.Name)
        }
    }
}
  • controller-manager对node的gc处理
  //删除在删除队列中的Node
  go wait.Until(gc.runAttemptToDeleteWorker, 1*time.Second, stopCh)
   //删除孤儿Node
  go wait.Until(gc.runAttemptToOrphanWorker, 1*time.Second, stopCh)

3.kubelet中的GarbageCollect

  • containerGC
    是由runtimeManager中的containerGC完成,她通过相关的gc策略移除死亡容器。需要主要的是每个pod都包含sandBox,相关的gc策略不会作用于sandboxes,只有sandboxes内部没有容器的情况下,才会被GC清理掉。
    GC的过程遵循以下步骤:
    获取不活动并且存活时间大于策略中最小时间(MinAge)的可驱逐容器
    根据策略中每个容器能够保留的旧的实例数(MaxPerPodContainer,默认为1),移除旧的死亡容器
    根据策略中全局能够能够保留的旧的实例总数(MaxPerPodContainer,默认为1),移除旧的死亡容器。
    获取没有ready,内部不存在容器的sandboxes
    移除上述sandboxes

    注意:gc策略不应用于沙盒

  • ImageGC
    根据相关的策略,如磁盘剩余低于设定阈值进行回收,


4.实际上即便是kube-apisever自身和etcd通讯也是infomers的接口,只是本身的连接是通过回环网卡实现的。

client, err := internalclientset.NewForConfig(genericConfig.LoopbackClientConfig)
if err != nil {
    lastErr = fmt.Errorf("failed to create clientset: %v", err)
    return
}

kubeClientConfig := genericConfig.LoopbackClientConfig
sharedInformers = informers.NewSharedInformerFactory(client, 10*time.Minute)

5.Informer封装了对apiserver的事件监听处理操作包括AddFunc,UpdateFunc,DeleteFunc。


6.node的亲和与反亲和

有nodeAffinity和nodeSelector两种方式去控制的。

nodeSelector是必须要满足的情况。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

nodeAffinity有两种类型

  • requiredDuringSchedulingIgnoredDuringExecution(对应预选过程,为硬性条件必须满足)
    后缀IgnoredDuringExecutionn:表示如果labels发生改变,即便不满足规则,也继续运行。
    会有多个nodeSelectorTerms ,nodeSelectorTerms 下会有多个matchExpressions,在nodeSelectorTerms 维度需要满足一个,但是需要满足的nodeSelectorTerms下的matchExpressions必须全部满足。
  • preferredDuringSchedulingIgnoredDuringExecution(对应优选过程,涉及打分过程)
    后缀RequiredDuringExecution:表示如果labels发生改变,不满足规则,kubelet执行驱逐(未实现)。
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: label-node-resource #表示该机器上需要有的资源,通过label标注其值
                operator: Exists
                values:
                - gpu
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 10
            preference:
              matchExpressions:
              - key: label-category
               operator: In
                values:
                - loadbalance
    

6.在大规模场景下的API Server优化

  • 心跳请求
    API Server 处理心跳请求是非常大的开销。原有的 Update Node Status方式每次需要发送大于10K数据包,而开启 NodeLease 之后,Kubelet 会使用非常轻量的 NodeLease 对象 (0.1 KB) ,集群 API Server 开销的 CPU 大约降低了一半。新版本默认已开启
    [root@node3 bin]# ./kubelet -h |grep NodeLease
                  NodeLease=true|false (BETA - default=true)
    
  • 限流功能
    ①API Server只限制最大读和写并发数,而没有限制特定客户端请求并发量的功能,因此建议所有客户端使用 Informer 去 List/Watch 资源,禁止在处理逻辑里面直接调用 Client 去向 API Server List 资源。
    ②随着Kubelet 和Daemonset 会增加 List Pod 请求的请求量,可以用 Bookmark 特性来解决这个问题,在未上线 Bookmark 的集群内,可以调整 API Server 对资源的更新事件缓存量来缓解该问题 (如 --watch-cache-sizes=node#1000,pod#5000),同时调整 Daemonset Re-Watch 的时间间隔:
    informerOpts := informers.WithTweakListOptions(func(options *v1.ListOptions) {
      if options.TimeoutSeconds != nil {
        // default [5min, 10min), overwrite this timeout value
        // it's too frequently for apiserver to process "List pods" requests
        // in a large cluster.
        minWatchTimeout := 24 * time.Hour
        clientReadTimeout := int64(minWatchTimeout.Seconds() * (1 + rand.Float64()))
        options.TimeoutSeconds = &clientReadTimeout
      }
    })
    
    bookmark:可以认为是watch的资源,watch相当于给apiserver增加一个需要过滤的标签

7.什么样的容器需要放在一个Pod中

前者依赖后者关系非常紧密的:

  • 容器之间需要文件交换的,比如日志文件
  • 需要通过localhost或者本地socket通信的
  • 容器中间分厂频繁的RPC调用
  • 需要共享网络协议栈

8.sidecar的应用场景

  • 应用初始化init-contianer
  • 应用debug
  • 日志采集
  • ssh镜像
  • 网络代理:代理容器,同一个网络协议栈,物业性能锁好
  • 业务 adapter,接口升级情况等

9.configmap的限制大小为1M(时间上是ETCD的要求),manifest无法使用


10.PV和PVC

PV分为静态和动态,静态需要预先创建

  • PersistentVolume的访问模式
    ReadWriteOnce(RWO) – 只有一个节点可以读写
    ReadOnlyMany(ROX) – 多个节点可读
    ReadWriteMany(RWX) – 多个节点读写

  • PV的回收策略
    Retain – 保留,手工维护
    Recycle – 不保留直接删除 (rm -rf /thevolume/*),可以被重新挂载
    Delete – 直接删除volume

    PV可以通过节点的亲和性(NodeAffinity)限制哪些node可以挂载


11.debug的调试方式、

  • 集群中的应用需要本地应用调试时,可以将本地应用代理到集群中的一个service上
    Telepresence --swap -deployment  {{deploymenet-name}}
    
  • 本地医用需要依赖集群中的应用时,通过port-forward将远程应用代理到本地
    kubectl -n {{namespace}} port-forward  svc/aapp 
    
  • kubectl debug 无侵入性调试

12.Pod中的容器是如何共享namespace

实际上容器共享并不是共享所有内容如UTC只共享主机名称

func modifyHostOptionsForContainer(nsOpts *runtimeapi.NamespaceOption, podSandboxID string, hc *dockercontainer.HostConfig) {
    sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID)
    hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode)
    hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode)
    hc.UTSMode = ""

    if nsOpts.GetNetwork() == runtimeapi.NamespaceMode_NODE {
        hc.UTSMode = namespaceModeHost
    }
}

主要步骤如下

  • 通过pause创建PodSandbox并返回PodSandboxId(容器ID)
    podSandBoxID, err := m.runtimeService.RunPodSandbox(podSandboxConfig, runtimeHandler)
    
  • 启动其他容器通过PodSandboxId获取ns相关项,配置到hostconfig中
    func modifyHostOptionsForContainer(nsOpts *runtimeapi.NamespaceOption, podSandboxID string, hc *dockercontainer.HostConfig) {
      sandboxNSMode := fmt.Sprintf("container:%v", podSandboxID)
      hc.NetworkMode = dockercontainer.NetworkMode(sandboxNSMode)
      hc.IpcMode = dockercontainer.IpcMode(sandboxNSMode)
      hc.UTSMode = ""
    
      if nsOpts.GetNetwork() == runtimeapi.NamespaceMode_NODE {
          hc.UTSMode = namespaceModeHost
      }
     }
    

13.QOS

  • Guaranteed:Pod 里的每个容器都必须有内存/CPU 限制和请求,而且值必须相等。如果一个容器只指明limit而未设定request,则request的值等于limit值。
  • Burstable:Pod 里至少有一个容器有内存或者 CPU 请求且不满足Guarantee 等级的要求,即内存/CPU 的值设置的不同。
  • BestEffort:容器必须没有任何内存或者 CPU 的限制或请求。
    三种 QoS 优先级,从高到低(从左往右)Guaranteed --> Burstable --> BestEffort
    回收顺序:BestEffort --> Burstable --> Guaranteed

14.Informer 关键逻辑解析

https://blog.csdn.net/chaosj/article/details/83831623

15.Request和Limit对应Cgroups的实现

  • requests

    • requests用于schedule阶段,在调度pod保证所有pod的requests总和小于node能提供的计算能力
    • requests.cpu被转成docker的--cpu-shares参数,与cgroup cpu.shares功能相同
      • 设置容器的cpu的相对权重
      • 该参数在CPU资源不足时生效,根据容器requests.cpu的比例来分配cpu资源
      • CPU资源充足时,requests.cpu不会限制container占用的最大值,container可以独占CPU
    • requests.memory没有对应的docker参数,作为k8s调度依据
    • 使用requests来设置各容器需要的最小资源
  • limits
    limits限制运行时容器占用的资源
    limits.cpu会被转换成docker的–cpu-quota参数。与cgroup cpu.cfs_quota_us功能相同
    限制容器的最大CPU使用率。
    cpu.cfs_quota_us参数与cpu.cfs_period_us结合使用,后者设置时间周期
    k8s将docker的–cpu-period参数设置100毫秒。对应着cgroup的cpu.cfs_period_us
    limits.cpu的单位使用m,千分之一核
    limits.memory会被转换成docker的–memory参数。用来限制容器使用的最大内存
    当容器申请内存超过limits时会被终止

15.Node的异常和恢复

  • Node的异常
    (1)Node状态变为NotReady
    (2)Pod 5分钟之内状态无变化,5分钟之后的状态变化:
    Daemonset的Pod状态变为Nodelost,
    Deployment、Statefulset和Static Pod的状态先变为NodeLost,然后马上变为Unknown。
    Deployment的pod会recreate,但是Deployment如果是node selector停掉kubelet的node,则recreate的pod会一直处于Pending的状态。
    Static Pod和Statefulset的Pod会一直处于Unknown状态。
  • Kubelet恢复
    (1)Node状态变为Ready。
    (2)Daemonset的pod不会recreate,旧pod状态直接变为Running。 (3)Deployment的则是将kubelet进程停止的Node删除(原因可能是因为旧Pod状态在集群中有变化,但是Pod状态在变化时发现集群中Deployment的Pod实例数已经够了,所以对旧Pod做了删除处理)
    (4)Statefulset的Pod会重新recreate。
    (5)Staic Pod没有重启,但是Pod的运行时间会在kubelet起来的时候置为0。

在kubelet停止后,statefulset的pod会变成nodelost,接着就变成unknown,但是不会重启,然后等kubelet起来后,statefulset的pod才会recreate。
原文

相关文章

  • kubernetes笔录

    1.Etcd的容量 集群的节点数的多少除了要顾及性能压力外,还要考虑etcd的存储容量。可以反向的从内存大小去推断...

  • 技术资源

    1Kubernetes 1)Kubernetes指南[https://kubernetes.feisky.xyz/...

  • Kubernetes

    Kubernetes Introduction to Kubernetes with Fedora

  • Kubernetes 介绍

    什么是Kubernetes? Kubernetes 基础服务简介Kubernetes Service介绍Kuber...

  • 转载:Kubernetes核心原理系列文章

    转载:Kubernetes 学习笔记 转载:[Kubernetes] Kubernetes核心原理(一)之API ...

  • 10大Go语言开源项目

    Kubernetes (K8s)[https://github.com/kubernetes/kubernetes...

  • kubernetes Dashboard

    kubernetes Dashboard 是基于网页的 Kubernetes 用户界面。使用 kubernetes...

  • Kubernetes

    kubernetes My Study of kubernetes kubernetes结构图-来源 基本概念和术...

  • kubernetes查看token

    windows系统kubernetes linux系统kubernetes

  • Kubernetes系统架构简介

    目录 前言 什么是Kubernetes Kubernetes主要概念 Kubernetes组件 1. 前言 Kub...

网友评论

      本文标题:kubernetes笔录

      本文链接:https://www.haomeiwen.com/subject/fmfrhqtx.html