kubernetes笔录

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。
原文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容