kubelet 先導篇

一. 概述

本文是kubelet源碼閱讀的先導片,先了解kubelet的主要配置和功能以及一些注意事項,後面走讀源碼的時候纔會更加順暢,否則一堆 config 的初始化和chan處理,不知道支持哪些新特性,啥場景會用到,看了也沒啥意思。php

二. 配置方式

2.1 flag 模式

k8s 迭代速度很快,幾個月一個大版本,kubelet 的啓動參數也在不斷變化,一切配置以官方文檔爲準,或者拿二進制直接--help 看,否則忙活一圈才發現某個特性在當前版本不支持。下面是一份基礎可用的kubelet配置,版本 1.8html

./kubelet \
--address=192.168.5.228 \
--allow-privileged=true \
--client-ca-file=/etc/kubernetes/pki/ca.pem \
--cloud-config=/etc/kubernetes/cloud.config \
--cloud-provider=external \
--cluster-dns=172.16.0.10 \
--cluster-domain=cluster.local \
--docker-root=/data/docker \
--fail-swap-on=false \
--feature-gates=VolumeSnapshotDataSource=true,CSINodeInfo=true,CSIDriverRegistry=true \
--hostname-override=192.168.5.228 \
--kubeconfig=/etc/kubernetes/kubelet.conf \
--logtostderr=true \
--network-plugin=kubenet \
--max-pods=256 \
--non-masquerade-cidr=172.26.0.0/16 \
--pod-infra-container-image=hub.docker.com/public/pause:2.0 \
--pod-manifest-path=/etc/kubernetes/manifests \
--root-dir=/data/kubelet \
--tls-cipher-suites=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA \
--anonymous-auth=false \
--v=5 \
--enforce-node-allocatable=pods,kube-reserved,system-reserved \
--kube-reserved-cgroup=/system.slice/kubelet.service \
--system-reserved-cgroup=/system.slice \
--kube-reserved=cpu=50m \
--system-reserved=cpu=50m \
--eviction-hard=memory.available<5%,nodefs.available<10%,imagefs.available<10%% \
--eviction-soft=memory.available<10%,nodefs.available<15%,imagefs.available<15%% \
--eviction-soft-grace-period=memory.available=2m,nodefs.available=2m,imagefs.available=2m \
--eviction-max-pod-grace-period=30 \
--eviction-minimum-reclaim=memory.available=0Mi,nodefs.available=500Mi,imagefs.available=500Mi

2.2 config 模式

上面的配置文件是老版本 kubelet(1.10 之前),啓動參數都是用 flag 來聲明的,簡單粗暴,在 1.10 之後,kubelet 支持了KubeletConfiguration的方式來聲明參數,如 kubeadm 部署的集羣配置以下:java

# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
~

kubelet 的--config配置採用了本地文件KubeletConfiguration資源作參數node

本地文件/var/lib/kubelet/config.yaml內容爲:mysql

apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true
runtimeRequestTimeout: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s

爲何要這麼改呢?git

--max-pods int32                                                                                            Number of Pods that can run on this Kubelet. (default 110) (DEPRECATED: This parameter should be set via the config file specified by the Kubelet's --config flag. See https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/ for more information.)

以上面的max-pods配置爲例,你運行kubelet --help你會發現,kubelet的絕大多數命令行flag參數都被DEPRECATED了,後面一句就是官方推薦咱們使用--config文件來指定這些配置,具體內容能夠查看這裏Set Kubelet parameters via a config file,一來是區分出什麼是機器特有配置,什麼是機器能夠共享的配置,二來是爲了支持以後更高級的動態Kubelet配置Dynamic Kubelet Configuration,即把能夠共享的配置作成一種資源,節點共享一份配置github

2.3 動態 config 模式

kubelet 的支持動態 kubelet 配置,即dynamic-config,在1.11版本開始支持,原理以下:golang

參考:https://kubernetes.io/docs/ta...web

config 文件的概念出來以後,kubelet 的配置就劃分爲了兩部分:算法

  • KubeletFlag: 指那些不容許在kubelet運行時進行修改的配置集,或者不能在集羣中各個Nodes之間共享的配置集。直接以 flag 的形式添加,如nodeip
  • KubeletConfiguration: 指能夠在集羣中各個Nodes之間共享的配置集。


動態配置在 1.17仍然處於 beta 狀態,其中有幾個關鍵點還沒解決:

  • 沒有提供原生的集羣灰度能力,須要用戶本身實現自動化灰度節點配置。若是全部Node引用同一個Kubelet ConfigMap,當該ConfigMap發生錯誤變動後,可能會致使集羣短期不可用。

    • 分批灰度全部Nodes的能力
    • 或者是滾動灰度全部Nodes的能力
  • 哪些集羣配置能夠經過Kubelet Dynamic Config安全可靠的動態變動,尚未一個徹底明確的集合。一般狀況下,咱們能夠參考staging/src/k8s.io/kubelet/config/v1beta1/types.go:62中對KubeletConfiguration的定義註解瞭解那些Dynamic Config,可是仍是建議在測試集羣中測試事後,再經過Dynamic Config灰度到生產環境。

三.參數含義

不管是flag 參數仍是 config 參數,都是kubelet 運行的必備條件,這裏對一些關鍵指標進行解釋,如下內容大量引用官方文檔

所有 config:https://kubernetes.io/zh/docs...

config 文件中的參數列表爲:https://github.com/kubernetes...

剩下的就是 flag參數,參數的示例值見上文。

3.1 flag 參數

  • cloud-config: 雲服務商的配置文件路徑,cloud-provider開啓時使用
  • cloud-provider: 雲服務商,爲空表示沒有云服務商,用於肯定節點名稱。
  • hostname-override: 若是爲非空,將使用此字符串而不是實際的主機名做爲節點標識
  • config: 用於聲明 config 文件的路徑
  • container-runtime: 容器運行時,默認爲 docker,還支持remote、rkt(已棄用)
  • docker-root: docker 根目錄的路徑,默認值:/var/lib/docker
  • kubeconfig: kubeconfig 配置文件的路徑,指定如何鏈接到 API 服務器。提供 --kubeconfig 將啓用 API 服務器模式,而省略 --kubeconfig 將啓用獨立模式。
  • logtostderr: 日誌輸出到 stderr 而不是文件(默認值爲 true)
  • network-plugin: 僅當容器運行環境設置爲 docker 時生效,如 kubenet
  • non-masquerade-cidr: kubelet 向該 IP 段以外的 IP 地址發送的流量將使用 IP 假裝技術,該參數將在將來版本中刪除 ?
  • pod-infra-container-image: 僅當容器運行環境設置爲 docker 時生效,指定pause鏡像
  • root-dir: 設置用於管理 kubelet 文件的根目錄(例如掛載卷的相關文件),默認/var/lib/kubelet
  • tls-cipher-suites: 服務器端加密算法列表,以逗號分隔,若是不設置,則使用 Go 語言加密包的默認算法列表
  • v : 設置 kubelet 日誌級別詳細程度的數值

3.2 config 參數

  • address: kubelet綁定的主機IP地址,默認爲0.0.0.0表示綁定所有網絡接口
  • allow-privileged: 是否容許以特權模式啓動容器。當前默認值爲false,已廢棄
  • client-ca-file: 基礎 ca證書,即 ca.pem
  • cluster-dns: 集羣內DNS服務的IP地址,僅當 Pod 設置了 「dnsPolicy=ClusterFirst」 屬性時可用
  • cluster-domain: 集羣的域名
  • fail-swap-on: 設置爲 true 表示若是主機啓用了交換分區,kubelet 將沒法使用。(默認值爲 true)
  • max-pods: kubelet 能運行的 Pod 最大數量。(默認值爲 110)
  • feature-gates: 用於 alpha 實驗性質的特性開關組,每一個開關以 key=value 形式表示
  • pod-manifest-path: 設置包含要運行的static Pod 的文件的路徑,或單個靜態 Pod 文件的路徑
  • anonymous-auth: 設置爲 true 表示 kubelet 服務器能夠接受匿名請求。
  • enforce-node-allocatable: 包含由 kubelet 強制執行的節點可分配資源級別。可選配置爲:‘none’、‘pods’、‘system-reserved’ 和 ‘kube-reserved’
  • kube-reserved-cgroup: 頂層kube cgroup 的名稱
  • system-reserved-cgroup: 頂層system cgroup 的名稱
  • kube-reserved: kube 組件資源預留
  • system-reserved: 系統組件組件預留
  • eviction-hard: 硬驅逐策略
  • eviction-soft: 軟驅逐策略
  • eviction-soft-grace-period: 軟驅逐寬限期
  • eviction-max-pod-grace-period: 響應知足軟驅逐閾值(soft eviction threshold)而終止 Pod 時使用的最長寬限期(以秒爲單位)
  • eviction-minimum-reclaim: 當本節點壓力過大時,kubelet 執行軟性驅逐操做。此參數設置軟性驅逐操做須要回收的資源的最小數量(例如:imagefs.available=2Gi)。

四. 最佳實踐

4.1 合理配置驅逐與預留值

詳細內容參考:k8s節點資源預留與 pod 驅逐

驅逐:經過--eviction-hard標誌預留一些內存後,當節點上的可用內存降至保留值如下時,kubelet 將會對pod進行驅逐。驅逐有軟硬兩種,並且軟驅逐能夠細化到持續多久才觸發。

預留:能夠給系統核心進程、k8s 核心進程配置資源預留,總數-預留數剩下的纔是給 pod 分配的量,資源預留建議使用階梯式,機器配置越高,預留越多

如,對於內存資源:

  • 內存少於1GB,則設置255 MiB
  • 內存大於4G,設置前4GB內存的25%
  • 接下來4GB內存的20%(最多8GB)
  • 接下來8GB內存的10%(最多16GB)
  • 接下來112GB內存的6%(最高128GB)
  • 超過128GB的任何內存的2%
  • 在1.12.0以前的版本中,內存小於1GB的節點不須要保留內存

4.2 減小心跳上報頻率

設計文檔:node-heartbeat

目的:

  • 在 Kubernetes 集羣中,影響其擴展到更大規模的一個核心問題是如何有效的處理節點的心跳。在一個典型的生產環境中 (non-trival),kubelet 每 10s 彙報一次心跳,每次心跳請求的內容達到 15kb(包含節點上數十計的鏡像,和若干的卷信息),這會帶來兩大問題:
  • 心跳請求觸發 etcd 中 node 對象的更新,在 10k nodes 的集羣中,這些更新將產生近 1GB/min 的 transaction logs(etcd 會記錄變動歷史);

API Server 很高的 CPU 消耗,node 節點很是龐大,序列化/反序列化開銷很大,處理心跳請求的 CPU 開銷超過 API Server CPU 時間佔用的 80%。

方法:

  • 爲了解決這個問題,Kubernetes 引入了一個新的 build-in Lease API ,將與心跳密切相關的信息從 node 對象中剝離出來,也就是上圖中的 Lease 。本來 kubelet 每 10s 更新一次 node 對象升級爲:每 10s 更新一次 Lease 對象,代表該節點的存活狀態,Node Controller 根據該 Lease 對象的狀態來判斷節點是否存活;
  • 處於兼容性的考慮,下降爲每 60s 更新一次 node 對象,使得 Eviction_ _Manager 等能夠繼續按照原有的邏輯工做。
  • 由於 Lease 對象很是小,所以其更新的代價遠小於更新 node 對象。kubernetes 經過這個機制,顯著的下降了 API Server 的 CPU 開銷,同時也大幅減少了 etcd 中大量的 transaction logs,成功將其規模從 1000 擴展到了幾千個節點的規模,該功能在社區 Kubernetes-1.14 中已經默認啓用。

具體參考:https://kubernetes.io/docs/co...

4.3 bookmark

設計文檔:watch-bookmark

目的:

  • watch client 重啓後會對全部的資源進行從新 watch,apiserver負載會劇增,能夠減小沒必要要的 watch 事件
  • 若是重啓前爲 resourceVersion v1的資源,重啓後發現資源變成了v2,客戶端並不會知道,仍然使用 v1,apiserver會將 v2 轉到 v1,這裏的處理實際上是沒有必要的

方法:

kubernetes 從v1.15開始 支持 bookmark 機制,bookmark 主要做用是隻將特定的事件發送給客戶端,從而避免增長 apiserver 的負載。bookmark 的核心思想歸納起來就是在 client 與 server 之間保持一個「心跳」,即便隊列中無 client 須要感知的更新,reflector 內部的版本號也須要及時的更新。

好比:每一個節點上的 kubelet 僅關注 和本身節點相關的 pods,pod storage 隊列是有限的(FIFO),當 pods 的隊列更新時,舊的變動就會從隊列中淘汰,當隊列中的更新與某個 kubelet client 無關時,kubelet client watch 的 resourceVersion 仍然保持不變,若此時 kubelet client 重連 apiserver 後,這時候 apiserver 沒法判斷當前隊列的最小值與 kubelet client 之間是否存在須要感知的變動,所以返回 client too old version err 觸發 kubelet client 從新 list 全部的數據。

EventType多了一種枚舉值:Bookmark

Added    EventType = "ADDED"
  Modified EventType = "MODIFIED"
  Deleted  EventType = "DELETED"
  Error    EventType = "ERROR"
  Bookmark EventType = "BOOKMARK"

4.4 hugepages

設計文檔: hugepages

目的:

HugePages是Linux內核的一個特性,使用hugepage能夠用更大的內存頁來取代傳統的4K頁面。能夠提升內存的性能,下降CPU負載,做用詳情參考hugepage的優點與使用

對於大內存工做集或者對內存訪問延遲很敏感的應用來講,開啓hugepages的效果比較顯著,如 mysql、java 程序、dpdk等,默認狀況下 kubelet啓動的 pod 是沒有開啓hugepages的。

方法:

從 k8s1.8 開始就開始支持hugepage,kubelet中經過--feature-gates=HugePages=true來開啓,hugepage 建議謹慎使用,由管理員來分配,由於預分配的大頁面會減小節點上可分配的內存量。該節點將像對待其餘系統保留同樣對待預分配的大頁面,可分配 mem 的公式就變成了:

[Allocatable] = [Node Capacity] - 
 [Kube-Reserved] - 
 [System-Reserved] - 
 [Pre-Allocated-HugePages * HugePageSize] -
 [Hard-Eviction-Threshold]

如開了 hugepage 的 node 狀態爲:

apiVersion: v1
kind: Node
metadata:
  name: node1
...
status:
  capacity:
    memory: 10Gi
    hugepages-2Mi: 1Gi
  allocatable:
    memory: 9Gi
    hugepages-2Mi: 1Gi

在建立 pod 時能夠聲明使用這些頁內存,

apiVersion: v1
kind: Pod
metadata:
  name: example
spec:
  containers:
...
    volumeMounts:
    - mountPath: /hugepages-2Mi
      name: hugepage-2Mi
    - mountPath: /hugepages-1Gi
      name: hugepage-1Gi
    resources:
      requests:
        hugepages-2Mi: 1Gi
        hugepages-1Gi: 2Gi
      limits:
        hugepages-2Mi: 1Gi
        hugepages-1Gi: 2Gi
  volumes:
  - name: hugepage-2Mi
    emptyDir:
      medium: HugePages-2Mi
  - name: hugepage-1Gi
    emptyDir:
      medium: HugePages-1Gi

4.5 新的健康檢查機制

設計文檔:[https://github.com/kubernetes...]

目的:

健康檢查的基礎內容參考:[K8S 中的健康檢查機制
](https://www.jianshu.com/p/5de...

kubelet 中有 health check 相關的邏輯來判斷 pod 啓動後狀態是否正常,若是檢查不經過會殺死 pod 重啓,直到檢查經過,但對於慢啓動容器 來講現有的健康檢查機制不太好用

慢啓動容器:指須要大量時間(一到幾分鐘)啓動的容器。啓動緩慢的緣由可能有多種:

  • 長時間的數據初始化:只有第一次啓動會花費不少時間
  • 負載很高:每次啓動都花費不少時間
  • 節點資源不足/過載:即容器啓動時間取決於外部因素

這種容器的主要問題在於,在livenessProbe失敗以前,應該給它們足夠的時間來啓動它們。對於這種問題,現有的機制的處理方式爲:

  • 方法一:livenessProbe中把延遲初始時間initialDelaySeconds設置的很長,以容許容器啓動(即initialDelaySeconds大於平均啓動時間)。雖然這樣能夠確保livenessProbe不會檢測失敗,可是不知道initialDelaySeconds應該配置爲多少,啓動時間不是一個固定值。另外,由於livenessProbe在啓動過程還沒運行,所以pod 得不到反饋,events 看不到內容,若是你initialDelaySeconds是 10 分鐘,那這 10 分鐘內你不知道在發生什麼。
  • 方法二:增長livenessProbe的失敗次數。即failureThreshold*periodSeconds的乘積足夠大,簡單粗暴,同時容器在初次成功啓動後,就算死鎖或以其餘方式掛起,livenessProbe也會不斷探測

方法二能夠解決這個問題,但不夠優雅。

由於livenessProbe的設計是爲了在 pod 啓動成功後進行健康探測,最好前提是 pod 已經啓動成功,不然啓動階段的屢次失敗是沒有意義的,所以官方提出了一種新的探針:即startupProbe,startupProbe並非一種新的數據結構,他徹底複用了livenessProbe,只是名字改了下,多了一種概念,關於這個 probe 的提議討論能夠參考issue

使用方式:startup-probes

ports:
- name: liveness-port
  containerPort: 8080
  hostPort: 8080

livenessProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 1
  periodSeconds: 10

startupProbe:
  httpGet:
    path: /healthz
    port: liveness-port
  failureThreshold: 30
  periodSeconds: 10

這個配置的含義是:

startupProbe首先檢測,該應用程序最多有5分鐘(30 * 10 = 300s)完成啓動。一旦startupProbe成功一次,livenessProbe將接管,以對後續運行過程當中容器死鎖提供快速響應。若是startupProbe從未成功,則容器將在300秒後被殺死。

k8s 1.16 纔開始支持startupProbe這個特性

Reference

相關文章
相關標籤/搜索