<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Kubernetes 故障檢測和自愈工具 NPD

          共 50807字,需瀏覽 102分鐘

           ·

          2021-08-30 06:50

          節(jié)點問題檢測器(Node Problem Detector)是一個守護程序,用于監(jiān)視和報告節(jié)點的健康狀況(包括內(nèi)核死鎖、OOM、系統(tǒng)線程數(shù)壓力、系統(tǒng)文件描述符壓力等指標)。你可以將節(jié)點問題探測器以 DaemonSet 或獨立守護程序運行。節(jié)點問題檢測器從各種守護進程收集節(jié)點問題,并以 NodeCondition 和 Event 的形式報告給 API Server。您可以通過檢測相應(yīng)的指標,提前預(yù)知節(jié)點的資源壓力,可以在節(jié)點開始驅(qū)逐 Pod 之前手動釋放或擴容節(jié)點資源壓力,防止 Kubenetes 進行資源回收或節(jié)點不可用可能帶來的損失。

          ?

          Git 倉庫地址:https://github.com/kubernetes/node-problem-detector

          Kubernetes 目前問題

          • 基礎(chǔ)架構(gòu)守護程序問題:ntp服務(wù)關(guān)閉;
          • 硬件問題:CPU,內(nèi)存或磁盤損壞;
          • 內(nèi)核問題:內(nèi)核死鎖,文件系統(tǒng)損壞;
          • 容器運行時問題:運行時守護程序無響應(yīng)
          • ...

          當kubernetes中節(jié)點發(fā)生上述問題,在整個集群中,k8s服務(wù)組件并不會感知以上問題,就會導致pod仍會調(diào)度至問題節(jié)點。

          為了解決這個問題,我們引入了這個新的守護進程node-problem-detector,從各個守護進程收集節(jié)點問題,并使它們對上游層可見。一旦上游層面發(fā)現(xiàn)了這些問題,我們就可以討論補救措施。

          NPD 使用

          構(gòu)建

          NPD使用Go modules管理依賴,因此構(gòu)建它需要Go SDK 1.11+:

          cd $GOPATH/src/k8s.io
          go get k8s.io/node-problem-detector
          cd node-problem-detector

          export GO111MODULE=on 
          go mod vendor

          # 設(shè)置構(gòu)建標記
          export BUILD_TAGS="disable_custom_plugin_monitor disable_system_stats_monitor"

          # 在Ubuntu 14.04上需要安裝
          sudo apt install libsystemd-journal-dev
          make all

          安裝

          # add repo
          helm repo add feisky https://feisky.xyz/kubernetes-charts
          helm update

          # install packages
          helm install feisky/node-problem-detector --namespace kube-system --name npd

          啟動參數(shù)

          • --version: 在控制臺打印 NPD 的版本號.
          • --hostname-override: 供 NPD 使用的自定義的節(jié)點名稱,NPD 會優(yōu)先獲取該參數(shù)設(shè)置的節(jié)點名稱,其次是從 NODE_NAME 環(huán)境變量中獲取,最后從 os.Hostname() 方法獲取。
          system-log-monitor 相關(guān)參數(shù)
          • --config.system-log-monitor: system log monitor 配置文件路徑,多個文件用逗號分隔, 如 config/kernel-monitor.json. NPD 會為每一個配置文件生成單獨的 log monitor。你可以使用不同的 log monitors 來監(jiān)控不同的系統(tǒng)日志。
          system-stats-monitor 相關(guān)參數(shù)
          • --config.system-stats-monitor: system status monitor 配置文件路徑,多個文件用逗號分隔, 如 config/system-stats-monitor.json. NPD 會為每一個配置文件生成單獨的 status monitor。你可以使用不同的 status monitors 來監(jiān)控系統(tǒng)的不同狀態(tài)。
          custom-plugin-monitor 相關(guān)參數(shù)
          • --config.custom-plugin-monitor: 用戶自定義插件配置文件路徑,多個文件用逗號分隔, 如 config/custom-plugin-monitor.json. NPD 會為每一個配置文件生成單獨的自定義插件監(jiān)視器。你可以使用不同的自定義插件監(jiān)視器來監(jiān)控不同的系統(tǒng)問題。
          K8s exporter 相關(guān)參數(shù)
          • --enable-k8s-exporter: 是否開啟上報信息到 API Server,默認為 true.

          • --apiserver-override: 一個URI參數(shù),用于自定義node-problem-detector連接apiserver的地址。如果--enable-k8s-exporter為false,則忽略此內(nèi)容。格式與Heapster的源標志相同。例如,要在沒有身份驗證的情況下運行,請使用以下配置:http://APISERVER_IP:APISERVER_PORT?inClusterConfig=false

            請參閱 heapster 文檔以獲取可用選項的完整列表。

          • --address: 綁定 NPD 服務(wù)器的地址。

          • --port: NPD 服務(wù)端口,如果為0,表示禁用 NPD 服務(wù)。

          Prometheus exporter 相關(guān)參數(shù)
          • --prometheus-address: 綁定Prometheus抓取端點的地址,默認為127.0.0.1。
          • --prometheus-port: 綁定Prometheus抓取端點的端口,默認為20257。使用0禁用。
          Stackdriver exporter 相關(guān)參數(shù)
          • --exporter.stackdriver: Stackdriver exporter程序配置文件的路徑,例如 config/exporter/stackdriver-exporter.json,默認為空字符串。設(shè)置為空字符串以禁用。
          過期參數(shù)
          • --system-log-monitors: system log monitor 配置文件路徑,多個文件用逗號分隔。該選項已過期, 被 --config.system-log-monitor 取代, 即將被移除. 如果在啟動NPD時同時設(shè)置了 --system-log-monitors 和 --config.system-log-monitor,會引發(fā)panic。
          • --custom-plugin-monitors: 用戶自定義插件配置文件路徑,多個文件用逗號分隔。該選項已過期, 被 --config.custom-plugin-monitor 取代, 即將被移除. 如果在啟動NPD時同時設(shè)置了 --custom-plugin-monitors 和 --config.custom-plugin-monitor,會引發(fā)panic。

          覆蓋配置文件

          構(gòu)建節(jié)點問題檢測器的 docker 鏡像時,會嵌入 默認配置。

          不過,你可以像下面這樣使用 ConfigMap 將其覆蓋:

          1、更改 config/ 中的配置文件

          2、創(chuàng)建 ConfigMap node-strick-detector-config:

          kubectl create configmap node-problem-detector-config --from-file=config/

          3、更改 node-problem-detector.yaml 以使用 ConfigMap:

          apiVersion: apps/v1
          kind: DaemonSet
          metadata:
            name: node-problem-detector-v0.1
            namespace: kube-system
            labels:
              k8s-app: node-problem-detector
              version: v0.1
              kubernetes.io/cluster-service: "true"
          spec:
            selector:
              matchLabels:
                k8s-app: node-problem-detector  
                version: v0.1
                kubernetes.io/cluster-service: "true"
            template:
              metadata:
                labels:
                  k8s-app: node-problem-detector
                  version: v0.1
                  kubernetes.io/cluster-service: "true"
              spec:
                hostNetwork: true
                containers:
                - name: node-problem-detector
                  image: k8s.gcr.io/node-problem-detector:v0.1
                  securityContext:
                    privileged: true
                  resources:
                    limits:
                      cpu: "200m"
                      memory: "100Mi"
                    requests:
                      cpu: "20m"
                      memory: "20Mi"
                  volumeMounts:
                  - name: log
                    mountPath: /log
                    readOnly: true
                  - name: config # Overwrite the config/ directory with ConfigMap volume
                    mountPath: /config
                    readOnly: true
                volumes:
                - name: log
                  hostPath:
                    path: /var/log/
                - name: config # Define ConfigMap volume
                  configMap:
                    name: node-problem-detector-config

          4、使用新的配置文件重新創(chuàng)建節(jié)點問題檢測器:

          說明: 此方法僅適用于通過 kubectl 啟動的節(jié)點問題檢測器。

          如果節(jié)點問題檢測器作為集群插件運行,則不支持覆蓋配置。插件管理器不支持 ConfigMap。

          如何驗證NPD捕獲信息

          通常這些錯誤是比較難真實測試,只能通過發(fā)送消息到j(luò)ournal來模擬。

          • 發(fā)送一個kernel deadlock類型的condition:在對應(yīng)的node節(jié)點上執(zhí)行以下操作
          echo "task docker:7 blocked for more than 300 seconds." |systemd-cat -t kernel

          然后通過k8s控制臺,你可以看到對應(yīng)的信息:

          • 發(fā)送一個event
          echo "Error trying v2 registry: failed to register layer: rename /var/lib/docker/image/test /var/lib/docker/image/ddd: directory not empty.*" |systemd-cat -t docker

          然后通過以下命令來對應(yīng)的event

          kubectl describe node/xxxx

          實現(xiàn)原理

          核心組件

          Problem Daemon(Monitor)

          Problem Daemon 是監(jiān)控任務(wù)子守護進程,NPD 會為每一個 Problem Daemon 配置文件創(chuàng)建一個守護進程,這些配置文件通過 --config.custom-plugin-monitor、--config.system-log-monitor、--config.system-stats-monitor 參數(shù)指定。每個 Problem Daemon監(jiān)控一個特定類型的節(jié)點故障,并報告給NPD。目前 Problem Daemon 以 Goroutine 的形式運行在NPD中,未來會支持在獨立進程(容器)中運行并編排為一個Pod。在編譯期間,可以通過相應(yīng)的標記禁用每一類 Problem Daemon。

          • custom-plugin-monitor:用戶自定義的 Problem Daemon
          • system-log-monitor:系統(tǒng)日志監(jiān)控
          • system-stats-monitor:系統(tǒng)狀態(tài)監(jiān)控

          ProblemDaemonHandler

          ProblemDaemonHandler 定義了 Problem Daemon 的初始化方法

          type ProblemDaemonHandler struct {
              // 初始化 Problem Daemon實例,如果初始化過程中出錯,則拋出 panic
              CreateProblemDaemonOrDie func(string) Monitor
            // 說明了從命令行參數(shù)配置 Problem Daemon 的方式
              CmdOptionDescription string
          }

          在NPD啟動時,init()方法中完成了 ProblemDaemonHandler 的注冊:

          var (
              // 在 NPD 啟動過程中,通過 init() 方法注冊
              handlers = make(map[types.ProblemDaemonType]types.ProblemDaemonHandler)
          )

          // 注冊 problem daemon 工廠方法,將會用于創(chuàng)建 problem daemon
          func Register(problemDaemonType types.ProblemDaemonType, handler types.ProblemDaemonHandler) {
              handlers[problemDaemonType] = handler
          }

          Exporter

          Exporter 用于上報節(jié)點健康信息到某種控制面。在 NPD 啟動時,會根據(jù)需求初始化并啟動各種 Exporter。Exporter 分為三類:

          • K8s Exporter:會將節(jié)點健康信息上報到 API Server。
          • Prometheus Exporter:負責上報節(jié)點指標信息到 Prometheus。
          • Plugable Exporters:可插拔的 Exporter(如 Stackdriver Exporter),我們也可以自定義 Exporter,并在 init() 方法中注冊,這樣在 NPD 啟動時就會自動初始化并啟動。

          ExporterHandler

          ExporterHandler 和 ProblemDaemonHandler 功能類似,其定義了 Exporter 的初始化方法。也是在NPD啟動時,init()方法中完成了 ExporterHandler 的注冊

          type ExporterHandler struct {
              // CreateExporterOrDie initializes an exporter, panic if error occurs.
              CreateExporterOrDie func(CommandLineOptions) Exporter
              // CmdOptionDescription explains how to configure the exporter from command line arguments.
              Options CommandLineOptions
          }

          Condition Manager

          K8s Exporter 獲取到的異常 Condition 信息會上報給 Condition Manager, Condition Manager 每秒檢查 Condition 的變化,并同步到 API Server 的 Node 對象中。

          Problem Client

          Problem Client 負責與 API Server 交互,并將巡檢過程中生成的 Events 和 Conditions 上報給 API Server。

          type Client interface {
          // 從 API Server 獲取當前節(jié)點所有指定類型的 Conditions
          GetConditions(conditionTypes []v1.NodeConditionType) ([]*v1.NodeCondition, error)
          // 調(diào)用 API Server 接口更新當前節(jié)點的 Condition 列表
          SetConditions(conditions []v1.NodeCondition) error
          // 上報 Event 信息到 API Server
          Eventf(eventType string, source, reason, messageFmt string, args ...interface{})
          // 從 API Server 獲取當前 node-problem-detector 實例所在的節(jié)點信息
          GetNode() (*v1.Node, error)
          }

          Problem Detector

          Problem Detector 是 NPD 的核心對象,它負責啟動所有的 Problem Daemon(也可以叫做 Monitor),并利用 channel 收集 Problem Daemon 中發(fā)現(xiàn)的異常信息,然后將異常信息提交給 Exporter,Exporter 負責將這些異常信息上報到指定的控制面(如 API Server、Prometheus、Stackdriver等)。

          Status

          Status 是 Problem Daemon 向 Exporter 上報的異常信息對象。

          type Status struct {
              // problem daemon 的名稱
              Source string `json:"source"`
              // 臨時的節(jié)點問題 —— 事件對象,如果此Status用于Condition更新則此字段可以為空
            // 從老到新排列在數(shù)組中
              Events []Event `json:"events"`
              // 永久的節(jié)點問題 —— NodeCondition。PD必須總是在此字段報告最新的Condition
              Conditions []Condition `json:"conditions"`
          }

          Tomb

          用于從外部控制協(xié)程的生命周期, 它的邏輯很簡單,準備結(jié)束生命周期時:

          1. 外部協(xié)作者發(fā)起一個通知
          2. 協(xié)作線程接收到通知,進行清理
          3. 清理完成后,協(xié)程反向通知外部協(xié)作者
          4. 外部協(xié)作者退出阻塞

          啟動過程

          NPD啟動過程

          NPD 啟動過程完成的工作有:

          1. 打印 NPD 版本號
          2. 設(shè)置節(jié)點名稱,優(yōu)先使用命令行中設(shè)置的節(jié)點名稱,其次是環(huán)境變量 NODE_NAME 中的節(jié)點名稱,最次是 os.Hostname()
          3. 校驗命令行參數(shù)的合法性
          4. 初始化 problem daemons
          5. 初始化默認 Exporters(包含 K8s Exporter、Prometheus Exporter)和可插拔 Exporters(如 Stackdriver Exporter)
          6. 使用 problem daemons 和 Exporters 構(gòu)建 Problem Detector,并啟動

          檢測流程

          NPD檢測流程

          節(jié)點自愈

          采集節(jié)點的健康狀態(tài)是為了能夠在業(yè)務(wù)Pod不可用之前提前發(fā)現(xiàn)節(jié)點異常,從而運維或開發(fā)人員可以對Docker、Kubelet或節(jié)點進行修復(fù)。在NPDPlus中,為了減輕運維人員的負擔,提供了根據(jù)采集到的節(jié)點狀態(tài)從而進行不同自愈動作的能力。集群管理員可以根據(jù)節(jié)點不同的狀態(tài)配置相應(yīng)的自愈能力,如重啟Docker、重啟Kubelet或重啟CVM節(jié)點等。同時為了防止集群中的節(jié)點雪崩,在執(zhí)行自愈動作之前做了嚴格的限流,防止節(jié)點大規(guī)模重啟。同時為了防止集群中的節(jié)點雪崩,在執(zhí)行自愈動作之前做了嚴格的限流。具體策略為:

          在同一時刻只允許集群中的一個節(jié)點進行自愈行為,并且兩個自愈行為之間至少間隔1分鐘

          當有新節(jié)點添加到集群中時,會給節(jié)點2分鐘的容忍時間,防止由于節(jié)點剛剛添加到集群的不穩(wěn)定性導致錯誤自愈

          custom-plugin-monitor

          此Problem Daemon為NPD提供了一種插件化機制,允許基于任何語言來編寫監(jiān)控腳本,只需要這些腳本遵循NPD關(guān)于退出碼和標準輸出的規(guī)范。通過調(diào)用用戶配置的腳本來檢測各種節(jié)點問題

          腳本退出碼:

          1. 0:對于Evnet來說表示Normal,對于NodeCondition表示False
          2. 1:對于Evnet來說表示W(wǎng)arning,對于NodeCondition表示True

          腳本輸出應(yīng)該小于80字節(jié),避免給Etcd的存儲造成壓力

          使用標記禁用:disable_custom_plugin_monitor

          示例

          {
            "plugin""custom",  // 插件類型
            "pluginConfig": { // 插件配置
              "invoke_interval""10s"// 執(zhí)行時間間隔
              "timeout""3m"// 健康檢查超時時間
              "max_output_length"80,
              "concurrency"1 // 并行度
            },
            "source""health-checker"// 事件源
            "metricsReporting"true,  // 是否上報指標信息
            "conditions": [ // 發(fā)現(xiàn)異常后在 Node 中設(shè)置的 Condition 信息
              {
                "type""KubeletUnhealthy",
                "reason""KubeletIsHealthy",
                "message""kubelet on the node is functioning properly"
              }
            ],
            "rules": [ // 巡檢規(guī)則
              {
                "type""permanent",
                "condition""KubeletUnhealthy",
                "reason""KubeletUnhealthy",
                "path""/home/kubernetes/bin/health-checker"// 二進制文件路徑
                "args": [ // 二進制文件啟動參數(shù)
                  "--component=kubelet",
                  "--enable-repair=true",// 是否啟用自愈,自愈會嘗試重啟組件
                  "--cooldown-time=1m"// 冷卻時間,組件啟動后的一段時間為冷卻時間,冷卻時間能如果發(fā)現(xiàn)異常,不會嘗試自愈
                  "--loopback-time=0",// 要回溯的 journal 日志的時間,如果為0,則從組件啟動時間開始回溯
                  "--health-check-timeout=10s" // 健康檢查超時時間
                ],
                "timeout""3m" // 巡檢超時時間
              }
            ]
          }

          plugin

          plugin 是NPD或用戶自定義的一些異常檢查程序,可以用任意語言編寫。custom-plugin-monitor 在執(zhí)行過程中會執(zhí)行這些異常檢測程序,并根據(jù)返回結(jié)果來判斷是否存在異常。NPD提供了三個 plugin,分別是:

          • health-check:檢查kubelet、docker、kube-proxy、cri等進程是否健康。
          • log-counter:依賴的插件是 journald,其作用是統(tǒng)計指定的 journal 日志中近一段時間滿足正則匹配的歷史日志條數(shù)。
          • network_problem.sh:檢查 conntrack table 的使用率是否超過 90%。

          health-checker

          命令行參數(shù)
          參數(shù)名稱參數(shù)說明默認值
          systemd-service與 --service 相同,已被 --service 取代
          serviceThe underlying service responsible for the component. Set to the corresponding component for docker and kubelet, containerd for cri.
          loopback-timeThe duration to loop back, if it is 0, health-check will check from start time.0min
          log-patternThe log pattern to look for in service journald logs. The format for flag value:
          health-check-timeoutThe time to wait before marking the component as unhealthy.10s
          enable-repairFlag to enable/disable repair attempt for the component.true
          crictl-pathThe path to the crictl binary. This is used to check health of cri component.Linux:/usr/bin/crictl  Windows:C:/etc/kubernetes/node/bin/crictl.exe
          cri-socket-pathThe path to the cri socket. Used with crictl to specify the socket path.Linux:unix:///var/run/containerd/containerd.sock  Windows:npipe:////./pipe/containerd-containerd
          cooldown-timeThe duration to wait for the service to be up before attempting repair.2min
          componentThe component to check health for. Supports kubelet, docker, kube-proxy, and cri.
          結(jié)構(gòu)定義
          type healthChecker struct {
              component       string // 要進行健康檢查的組件名稱,支持 kubelet、docker、kube-proxy 和 cri
              service         string // 組件的服務(wù)名稱,需要通過 service 讀取 journal 日志,并檢查日志是否存在異常
              enableRepair    bool // 是否啟動自動修復(fù),如果啟動自動修復(fù),當發(fā)現(xiàn)異常時會調(diào)用 repairFunc 嘗試自動修復(fù)
              healthCheckFunc func() (bool, error) // 組件健康檢查方法
              repairFunc         func() // 組件自愈方法,這是一種”best-effort“形式的自愈,會嘗試 kill 掉組件的進程,但可能失敗
              uptimeFunc         func() (time.Duration, error)  // 獲取組件的啟動時間(啟動后經(jīng)過的時間)
              crictlPath         string // crictl 二進制文件路徑,用于對 CRI(Container Runtime Interface) 組件執(zhí)行健康檢查
              healthCheckTimeout time.Duration // 健康檢查超時時間
              coolDownTime       time.Duration // 服務(wù)啟動后,在冷卻時間內(nèi)如果發(fā)現(xiàn)異常,不會嘗試自動修復(fù)。超出冷卻時間后才會嘗試自動修復(fù)
              loopBackTime       time.Duration // 待檢 journal 查日志的起始時間間隔,如果該值為0,則從組件啟動的日志開始檢查
              logPatternsToCheck map[string]int // 要檢查的 journal 日志的正則表達式
          }
          執(zhí)行流程

          health-checker 的執(zhí)行流程可以分為三個步驟:

          1. 調(diào)用 healthCheckFunc() 方法判斷組件進程是否健康
          2. 獲取組件近一段時間的 journal 日志,判斷異常日志數(shù)量是否達到上限
          3. 如果前兩步檢查都未發(fā)現(xiàn)異常,則返回 true。否則,如果啟動了自動修復(fù)機制,則調(diào)用 repairFunc() 嘗試自愈
          健康檢查
          func getHealthCheckFunc(hco *options.HealthCheckerOptions) func() (bool, error) {
              switch hco.Component {
              case types.KubeletComponent:
                // 訪問 http://127.0.0.1:10248/healthz,判斷 kubelet 是否健康
                  return healthCheckEndpointOKFunc(types.KubeletHealthCheckEndpoint, hco.HealthCheckTimeout)
              case types.KubeProxyComponent:
                // 訪問 http://127.0.0.1:10256/healthz,判斷 kube-proxy 是否健康
                  return healthCheckEndpointOKFunc(types.KubeProxyHealthCheckEndpoint, hco.HealthCheckTimeout)
              case types.DockerComponent:
                  return func() (bool, error) { // 執(zhí)行 docker ps 命令判斷 Docker 是否健康
                      if _, err := execCommand(hco.HealthCheckTimeout, getDockerPath(), "ps"); err != nil {
                          return falsenil
                      }
                      return truenil
                  }
              case types.CRIComponent:
                  return func() (bool, error) {// 執(zhí)行 circtl --runtime-endpoint=unix:///var/run/containerd/containerd.sock --image-endpoint=unix:///var/run/containerd/containerd.sock
                      if _, err := execCommand(hco.HealthCheckTimeout, hco.CriCtlPath, "--runtime-endpoint="+hco.CriSocketPath, "--image-endpoint="+hco.CriSocketPath, "pods"); err != nil {
                          return falsenil
                      }
                      return truenil
                  }
              default:
                  glog.Warningf("Unsupported component: %v", hco.Component)
              }

              return nil
          }
          組件自愈
          func getRepairFunc(hco *options.HealthCheckerOptions) func() {
              switch hco.Component {
              case types.DockerComponent:
                  // Use "docker ps" for docker health check. Not using crictl for docker to remove
                  // dependency on the kubelet.
                  return func() {
                      execCommand(types.CmdTimeout"pkill""-SIGUSR1""dockerd")
                      execCommand(types.CmdTimeout"systemctl""kill""--kill-who=main", hco.Service)
                  }
              default:
                  // Just kill the service for all other components
                  return func() {
                      execCommand(types.CmdTimeout"systemctl""kill""--kill-who=main", hco.Service)
                  }
              }
          }

          log-counter

          依賴的插件是 journald,其作用是統(tǒng)計指定的 journal 日志中近一段時間滿足正則匹配的歷史日志條數(shù)。

          命令行參數(shù)
          參數(shù)名稱參數(shù)說明默認值
          journald-sourceThe source configuration of journald, e.g., kernel, kubelet, dockerd, etc
          log-pathThe log path that log watcher looks up
          lookbackThe log path that log watcher looks up
          delayThe time duration log watcher delays after node boot time. This is useful when log watcher needs to wait for some time until the node is stable.
          patternThe regular expression to match the problem in log. The pattern must match to the end of the line.
          countThe number of times the pattern must be found to trigger the condition1
          執(zhí)行流程
          log-counter執(zhí)行流程
          Count()
          func (e *logCounter) Count() (count int, err error) {
              start := e.clock.Now()
              for {
                  select {
                  case log, ok := <-e.logCh:
                      if !ok {
                          err = fmt.Errorf("log channel closed unexpectedly")
                          return
                      }
                      // 只統(tǒng)計 logCounter 啟動之前的日志
                      if start.Before(log.Timestamp) {
                          return
                      }
                      e.buffer.Push(log)
                      if len(e.buffer.Match(e.pattern)) != 0 {
                          count++
                      }
                  case <-e.clock.After(timeout):
                      // 如果超過一定時間沒有新日志生成,則退出
                      return
                  }
              }
          }
          journal日志檢查
          func checkForPattern(service, logStartTime, logPattern string, logCountThreshold int) (bool, error) {
            // 從 journal 日志中匹配符合規(guī)則的錯誤日志
              out, err := execCommand(types.CmdTimeout, "/bin/sh""-c",
                  // Query service logs since the logStartTime
                  `journalctl --unit "`+service+`" --since "`+logStartTime+
                      // 正則匹配
                      `" | grep -i "`+logPattern+
                      // 計算錯誤發(fā)生次數(shù)
                      `" | wc -l`)
              if err != nil {
                  return true, err
              }
              occurrences, err := strconv.Atoi(out)
              if err != nil {
                  return true, err
              }
            // 如果錯誤日志數(shù)量超過閾值,則返回 false
              if occurrences >= logCountThreshold {
                  glog.Infof("%s failed log pattern check, %s occurrences: %v", service, logPattern, occurrences)
                  return falsenil
              }
              return truenil
          }

          network_problem.sh

          檢查 conntrack table 的使用率是否超過 90%

          #!/bin/bash

          # This plugin checks for common network issues.
          # Currently only checks if conntrack table is more than 90% used.

          readonly OK=0
          readonly NONOK=1
          readonly UNKNOWN=2

          # "nf_conntrack" replaces "ip_conntrack" - support both
          readonly NF_CT_COUNT_PATH='/proc/sys/net/netfilter/nf_conntrack_count'
          readonly NF_CT_MAX_PATH='/proc/sys/net/netfilter/nf_conntrack_max'
          readonly IP_CT_COUNT_PATH='/proc/sys/net/ipv4/netfilter/ip_conntrack_count'
          readonly IP_CT_MAX_PATH='/proc/sys/net/ipv4/netfilter/ip_conntrack_max'

          if [[ -f $NF_CT_COUNT_PATH ]] && [[ -f $NF_CT_MAX_PATH ]]; then
            readonly CT_COUNT_PATH=$NF_CT_COUNT_PATH
            readonly CT_MAX_PATH=$NF_CT_MAX_PATH
          elif [[ -f $IP_CT_COUNT_PATH ]] && [[ -f $IP_CT_MAX_PATH ]]; then
            readonly CT_COUNT_PATH=$IP_CT_COUNT_PATH
            readonly CT_MAX_PATH=$IP_CT_MAX_PATH
          else
            exit $UNKNOWN
          fi

          readonly conntrack_count=$(< $CT_COUNT_PATH) || exit $UNKNOWN
          readonly conntrack_max=$(< $CT_MAX_PATH) || exit $UNKNOWN
          readonly conntrack_usage_msg="${conntrack_count} out of ${conntrack_max}"

          if (( conntrack_count > conntrack_max * 9 /10 )); then
            echo "Conntrack table usage over 90%: ${conntrack_usage_msg}"
            exit $NONOK
          else
            echo "Conntrack table usage: ${conntrack_usage_msg}"
            exit $OK
          fi

          system-log-monitor

          system-log-monitor 用于監(jiān)控系統(tǒng)和內(nèi)核日志,根據(jù)預(yù)定義規(guī)則來報告問題、指標。它支持基于文件的日志、Journald、kmsg。要監(jiān)控其它日志,需要實現(xiàn)LogWatcher接口

          LogMonitor

          type logMonitor struct {
              // 配置文件路徑
              configPath string
              // 讀取日志的邏輯委托給LogWatcher,這里解耦的目的是支持多種類型的日志
              watcher    watchertypes.LogWatcher
              // 日志緩沖,讀取的日志在此等待處理
              buffer     LogBuffer
              // 對應(yīng)配置文件中的字段
              config     MonitorConfig
              // 對應(yīng)配置文件中的conditions字段
              conditions []types.Condition
              // 輸入日志條目的通道
              logCh      <-chan *logtypes.Log
              // 輸出狀態(tài)的通道
              output     chan *types.Status
              // 用于控制此Monitor的生命周期
              tomb       *tomb.Tomb
          }

          LogWatcher

          LogWatcher 的主要作用的監(jiān)聽文件更新,并將追加的文件內(nèi)容寫入 LogBuffer 中供 LogMonitor 處理。NPD 中提供了三種 LogWatcher 的實現(xiàn):

          • filelog:監(jiān)聽任意文本類型日志。
          • journald:監(jiān)聽 journald 日志。
          • kmsg:監(jiān)聽內(nèi)核日志設(shè)備,如 /dev/kmsg。
          LogWatcher 也需要在 init() 方法中完成注冊。
          type LogWatcher interface {
              // 開始監(jiān)控日志,并通過通道輸出日志
              Watch() (<-chan *types.Log, error)
              // 停止,注意釋放打開的資源
              Stop()
          }

          filelog

          filelog 通過監(jiān)控指定的文件更新,并對日志內(nèi)容進行正則匹配,以發(fā)現(xiàn)異常日志,從而判斷組件是否正常。

          {
              "plugin""filelog",
              "pluginConfig": {
                  "timestamp""^time=\"(\\S*)\"",// 時間戳解析表達式
                  "message""msg=\"([^\n]*)\"",  // 日志解析表達式
                  "timestampFormat""2006-01-02T15:04:05.999999999-07:00" // 時間戳格式
              },
              "logPath""/var/log/docker.log"// 日志路徑
              "lookback""5m"// 日志回溯時長
              "bufferSize"10// 緩沖大?。ㄈ罩緱l數(shù))
              "source""docker-monitor",
              "conditions": [],
              "rules": [ // 健康檢查規(guī)則
                  {
                      "type""temporary",
                      "reason""CorruptDockerImage",
                      "pattern""Error trying v2 registry: failed to register layer: rename /var/lib/docker/image/(.+) /var/lib/docker/image/(.+): directory not empty.*"
                  }
              ]
          }

          journald

          journald 底層依賴 sdjournal 包,監(jiān)控系統(tǒng)日志的更新,并且可以從指定的歷史時間點開始讀取。如果未指定 journal 日志路徑,則從系統(tǒng)默認路徑讀取。讀取到的日志會轉(zhuǎn)換成 logtypes.Log 對象,并寫入 logCh 通道中。journal 通過監(jiān)控 journal 文件更新,并對日志內(nèi)容進行正則匹配,以發(fā)現(xiàn)異常日志,從而判斷組件是否正常。

          {
              "plugin""journald",
              "pluginConfig": {
                  "source""abrt-notification"
              },
              "logPath""/var/log/journal"// journal 日志路徑
              "lookback""5m"// 日志回溯時長
              "bufferSize"10// log 緩存大?。ㄈ罩緱l數(shù))
              "source""abrt-adaptor",
              "conditions": [],
              "rules": [ // 健康檢查規(guī)則
                  {
                      "type""temporary",
                      "reason""CCPPCrash",
                      "pattern""Process \\d+ \\(\\S+\\) crashed in .*"
                  },
                  {
                      "type""temporary",
                      "reason""UncaughtException",
                      "pattern""Process \\d+ \\(\\S+\\) of user \\d+ encountered an uncaught \\S+ exception"
                  },
                  {
                      "type""temporary",
                      "reason""XorgCrash",
                      "pattern""Display server \\S+ crash in \\S+"
                  },
                  {
                      "type""temporary",
                      "reason""VMcore",
                      "pattern""System encountered a fatal error in \\S+"
                  },
                  {
                      "type""temporary",
                      "reason""Kerneloops",
                      "pattern""System encountered a non-fatal error in \\S+"
                  }
              ]
          }

          kmsg

          kmsg 和 journald 的實現(xiàn)原理類似,它底層依賴 kmsgparser 包,實現(xiàn)內(nèi)核日志的監(jiān)控更新和回溯。默認的文件路徑是 /dev/kmsg。kmsg 通過監(jiān)控系統(tǒng)日志文件更新,并對日志內(nèi)容進行正則匹配,以發(fā)現(xiàn)異常日志,從而判斷組件是否正常。

          {
              "plugin""kmsg",
              "logPath""/dev/kmsg"// 內(nèi)核日志路徑
              "lookback""5m",  // 日志回溯時長
              "bufferSize"10,  // 緩存大?。ㄈ罩緱l數(shù))
              "source""kernel-monitor",
              "metricsReporting"true,
              "conditions": [
                  {
                      "type""KernelDeadlock",
                      "reason""KernelHasNoDeadlock",
                      "message""kernel has no deadlock"
                  },
                  {
                      "type""ReadonlyFilesystem",
                      "reason""FilesystemIsNotReadOnly",
                      "message""Filesystem is not read-only"
                  }
              ],
              "rules": [
                  {
                      "type""temporary",
                      "reason""OOMKilling",
                      "pattern""Killed process \\d+ (.+) total-vm:\\d+kB, anon-rss:\\d+kB, file-rss:\\d+kB.*"
                  },
                  {
                      "type""temporary",
                      "reason""TaskHung",
                      "pattern""task [\\S ]+:\\w+ blocked for more than \\w+ seconds\\."
                  },
                  {
                      "type""temporary",
                      "reason""UnregisterNetDevice",
                      "pattern""unregister_netdevice: waiting for \\w+ to become free. Usage count = \\d+"
                  },
                  {
                      "type""temporary",
                      "reason""KernelOops",
                      "pattern""BUG: unable to handle kernel NULL pointer dereference at .*"
                  },
                  {
                      "type""temporary",
                      "reason""KernelOops",
                      "pattern""divide error: 0000 \\[#\\d+\\] SMP"
                  },
                  {
                      "type""temporary",
                      "reason""Ext4Error",
                      "pattern""EXT4-fs error .*"
                  },
                  {
                      "type""temporary",
                      "reason""Ext4Warning",
                      "pattern""EXT4-fs warning .*"
                  },
                  {
                      "type""temporary",
                      "reason""IOError",
                      "pattern""Buffer I/O error .*"
                  },
                  {
                      "type""temporary",
                      "reason""MemoryReadError",
                      "pattern""CE memory read error .*"
                  },
                  {
                      "type""permanent",
                      "condition""KernelDeadlock",
                      "reason""AUFSUmountHung",
                      "pattern""task umount\\.aufs:\\w+ blocked for more than \\w+ seconds\\."
                  },
                  {
                      "type""permanent",
                      "condition""KernelDeadlock",
                      "reason""DockerHung",
                      "pattern""task docker:\\w+ blocked for more than \\w+ seconds\\."
                  },
                  {
                      "type""permanent",
                      "condition""ReadonlyFilesystem",
                      "reason""FilesystemIsReadOnly",
                      "pattern""Remounting filesystem read-only"
                  }
              ]
          }

          LogBuffer

          LogBuffer 是一個可循環(huán)寫入的日志隊列,max 字段控制可記錄日志的最大條數(shù),當日志條數(shù)超過 max 時,就會從頭覆蓋寫入。LogBuffer 也支持正則匹配 buffer 中的日志內(nèi)容。

          type LogBuffer interface {
          // 把日志寫入 log buffer 中
          Push(*types.Log)
          // 對 buffer 中的日志進行正則匹配
          Match(string) []*types.Log
          // 把 log buffer 中的日志按時間由遠到近連接成一個字符串
          String() string
          }

          實現(xiàn)原理

          啟動過程

          執(zhí)行過程

          system-stats-monitor

          將各種健康相關(guān)的統(tǒng)計信息報告為Metrics

          目前支持的組件僅僅有主機信息、磁盤:

          • disk/io_time 設(shè)備隊列非空時間,毫秒
          • disk/weighted_io 設(shè)備隊列非空時間加權(quán),毫秒
          • disk/avg_queue_len 上次調(diào)用插件以來,平均排隊請求數(shù)

          使用標記禁用:disable_system_stats_monitor

          • cpuCollector:采集 CPU 相關(guān)指標信息。
          • diskCollector:采集磁盤相關(guān)指標信息。
          • hostCollector:采集宿主機相關(guān)指標信息。
          • memoryCollector:采集內(nèi)存相關(guān)指標信息。
          • osFeatureCollector:采集系統(tǒng)屬性相關(guān)指標。
          • netCollector:采集網(wǎng)絡(luò)相關(guān)指標信息。

          檢查規(guī)則

          自定義插件規(guī)則

          CustomRule

          // 自定義規(guī)則(插件),描述CPM如何調(diào)用插件,分析調(diào)用結(jié)果
          type CustomRule struct {
              // 報告永久還是臨時問題
              Type types.Type `json:"type"`
              // 此問題觸發(fā)哪種NodeCondition,僅當永久問題才設(shè)置此字段
              Condition string `json:"condition"`
              // 問題的簡短原因,對于永久問題,通常描述NodeCondition的一個子類型
              Reason string `json:"reason"`
              // 自定義插件(腳本)的文件路徑
              Path string `json:"path"`
              // 傳遞給自定義插件的參數(shù)
              Args []string `json:"args"`
              // 自定義插件執(zhí)行超時
              TimeoutString *string `json:"timeout"`
              Timeout *time.Duration `json:"-"`
          }

          示例

          health-checker-kubelet.json(https://github.com/kubernetes/node-problem-detector/blob/master/config/health-checker-kubelet.json)

          系統(tǒng)日志監(jiān)控規(guī)則

          systemlogtypes.Rule

          type Rule struct {
              // 報告永久還是臨時問題
              Type types.Type `json:"type"`
              // 此問題觸發(fā)哪種NodeCondition,僅當永久問題才設(shè)置此字段
              Condition string `json:"condition"`
              // 問題的簡短原因,對于永久問題,通常描述NodeCondition的一個子類型
              Reason string `json:"reason"`
              // Pattern is the regular expression to match the problem in log.
              // Notice that the pattern must match to the end of the line.
              Pattern string `json:"pattern"`
          }

          示例

          kernel-monitor.json(https://github.com/kubernetes/node-problem-detector/blob/master/config/kernel-monitor.json)

          異常上報

          node-problem-detector使用 Event 和 NodeCondition 將問題報告給apiserver。

          • NodeCondition:導致節(jié)點無法處理于Pod生命周期的的永久性問題應(yīng)報告為NodeCondition。
          • Event:對pod影響有限的臨時問題應(yīng)作為event報告。

          異常類型

          • temporary:致節(jié)點無法處理于Pod生命周期的的永久性問題
          • permanent:對pod影響有限的臨時問題

          指標上報

          通過配置 metricsReporting  可以選擇是否開啟 System Log Monitor 的指標上報功能。該字段默認為 true。

          臨時異常只會上報 counter 指標,如下:

          # HELP problem_counter Number of times a specific type of problem have occurred.
          # TYPE problem_counter counter
          problem_counter{reason="TaskHung"} 2

          永久異常會上報 gauge 指標和 counter 指標,如下:

          # HELP problem_counter Number of times a specific type of problem have occurred.
          # TYPE problem_counter counter
          problem_counter{reason="DockerHung"} 1
          # HELP problem_gauge Whether a specific type of problem is affecting the node or not.
          # TYPE problem_gauge gauge
          problem_gauge{condition="KernelDeadlock",reason="DockerHung"} 1
          Counter是一個累計類型的數(shù)據(jù)指標,它代表單調(diào)遞增的計數(shù)器。`
           `Gauge是可以任意上下波動數(shù)值的指標類型。

          指標

          NPD對指標這一概念也進行了封裝,它依賴OpenCensus而不是Prometheus這樣具體的實現(xiàn)的API。

          所有指標如下:

          const (
              CPURunnableTaskCountID  MetricID = "cpu/runnable_task_count"
              CPUUsageTimeID          MetricID = "cpu/usage_time"
              CPULoad1m               MetricID = "cpu/load_1m"
              CPULoad5m               MetricID = "cpu/load_5m"
              CPULoad15m              MetricID = "cpu/load_15m"
              ProblemCounterID        MetricID = "problem_counter"
              ProblemGaugeID          MetricID = "problem_gauge"
              DiskIOTimeID            MetricID = "disk/io_time"
              DiskWeightedIOID        MetricID = "disk/weighted_io"
              DiskAvgQueueLenID       MetricID = "disk/avg_queue_len"
              DiskOpsCountID          MetricID = "disk/operation_count"
              DiskMergedOpsCountID    MetricID = "disk/merged_operation_count"
              DiskOpsBytesID          MetricID = "disk/operation_bytes_count"
              DiskOpsTimeID           MetricID = "disk/operation_time"
              DiskBytesUsedID         MetricID = "disk/bytes_used"
              HostUptimeID            MetricID = "host/uptime"
              MemoryBytesUsedID       MetricID = "memory/bytes_used"
              MemoryAnonymousUsedID   MetricID = "memory/anonymous_used"
              MemoryPageCacheUsedID   MetricID = "memory/page_cache_used"
              MemoryUnevictableUsedID MetricID = "memory/unevictable_used"
              MemoryDirtyUsedID       MetricID = "memory/dirty_used"
              OSFeatureID             MetricID = "system/os_feature"
              SystemProcessesTotal    MetricID = "system/processes_total"
              SystemProcsRunning      MetricID = "system/procs_running"
              SystemProcsBlocked      MetricID = "system/procs_blocked"
              SystemInterruptsTotal   MetricID = "system/interrupts_total"
              SystemCPUStat           MetricID = "system/cpu_stat"
              NetDevRxBytes           MetricID = "net/rx_bytes"
              NetDevRxPackets         MetricID = "net/rx_packets"
              NetDevRxErrors          MetricID = "net/rx_errors"
              NetDevRxDropped         MetricID = "net/rx_dropped"
              NetDevRxFifo            MetricID = "net/rx_fifo"
              NetDevRxFrame           MetricID = "net/rx_frame"
              NetDevRxCompressed      MetricID = "net/rx_compressed"
              NetDevRxMulticast       MetricID = "net/rx_multicast"
              NetDevTxBytes           MetricID = "net/tx_bytes"
              NetDevTxPackets         MetricID = "net/tx_packets"
              NetDevTxErrors          MetricID = "net/tx_errors"
              NetDevTxDropped         MetricID = "net/tx_dropped"
              NetDevTxFifo            MetricID = "net/tx_fifo"
              NetDevTxCollisions      MetricID = "net/tx_collisions"
              NetDevTxCarrier         MetricID = "net/tx_carrier"
              NetDevTxCompressed      MetricID = "net/tx_compressed"
          )

          其中ProblemCounterID 和 ProblemGaugeID 是針對所有Problem的Counter/Gauge,其他都是SystemStatsMonitor暴露的指標。

          治愈系統(tǒng)

          在NPD的術(shù)語中,治愈系統(tǒng)(Remedy System)是一個或一組進程,負責分析NPD檢測出的問題,并且采取補救措施,讓K8S集群恢復(fù)健康狀態(tài)。

          目前官方提及的治愈系統(tǒng)有只有Draino。NPD項目并沒有提供對Draino的集成,你需要手工部署和配置Draino。

          Draino

          Draino(https://github.com/planetlabs/draino)是Planet開源的小項目,最初在Planet用于解決GCE上運行的K8S集群的持久卷相關(guān)進程(mkfs.ext4、mount等)永久卡死在不可中斷睡眠狀態(tài)的問題。Draino的工作方式簡單粗暴,只是檢測到NodeCondition并Cordon、Drain節(jié)點。

          基于Label和NodeCondition自動的Drain掉故障K8S節(jié)點:

          1. 具有匹配標簽的的K8S節(jié)點,只要進入指定的NodeCondition之一,立即禁止調(diào)度(Cordoned)
          2. 在禁止調(diào)度之后一段時間,節(jié)點被Drain掉

          Draino可以聯(lián)用Cluster Autoscaler,自動的終結(jié)掉Drained的節(jié)點。

          在Descheduler項目成熟以后,可以代替Draino。

          ?

          原文鏈接:https://www.jianshu.com/p/eeba98425307

          瀏覽 500
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉网站在线 | 世界二级毛片国语 | 天天一啪极品御姐 | 国产无遮挡又黄又爽又色视频软件 | 大香蕉精品在线观看 |