Перейти к содержимому

Kubernetes / k3s

Кластер через его API-сервер: узлы, которые не Ready или испытывают давление, поды в цикле сбоев (crash loop) или застрявшие в Pending, Deployments, StatefulSets и DaemonSets с недостающими репликами, запросы томов, которые так и не привязываются, и события-предупреждения за последний час.

DumbMonit читает API с токеном выделенного сервисного аккаунта, привязанного к ClusterRole только для чтения: get и list для узлов, подов, рабочих нагрузок, запросов постоянных томов и событий, ничего больше. Он никогда не пишет, а роль не даёт доступа к Secrets или ConfigMaps. Один монитор охватывает весь кластер, независимо от дистрибутива: k3s, kubeadm, k0s, MicroK8s, Talos или управляемый кластер, до API которого DumbMonit может дотянуться.

Каждую минуту DumbMonit параллельно читает восемь коллекций: версию, узлы, поды, Deployments, StatefulSets и DaemonSets, запросы постоянных томов и события типа Warning (отфильтрованные API-сервером). Длинные списки читаются страницами по 500.

Все метрики имеют префикс dumbmonit_k8s_.

МетрикаЧтоМетки
version_infoзначение 1version (1.33.4+k3s1)
nodes, nodes_readyузлы и те, у которых условие Ready истинно
node_ready1, когда узел Readynode
node_pressure1, когда узел сообщает о таком давлении; только условия, которые узел объявляетnode, condition: memory, disk, pid, network
node_unschedulable1, когда узел закрыт для планирования (cordoned)node
node_infoзначение 1node, kubelet_version
pods, pods_running, pods_pending, pods_failed, pods_crashlooping, pods_not_readyколичества подов; завершённые поды (Succeeded) не учитываются
pod_ready, pod_crashlooping, pod_pending1 или 0 на подnamespace, pod, workload
pod_restartsперезапуски контейнеров пода, счётчикnamespace, pod, workload
pod_blocked1, пока контейнер ждёт по причине, отличной от запуска: ImagePullBackOff, CreateContainerConfigError…namespace, pod, workload, reason
workload_desired, workload_ready, workload_unavailableзапрошенные, готовые и недостающие реплики; для DaemonSet — узлыkind (Deployment, StatefulSet, DaemonSet), namespace, workload
pvc_pending1, когда запрос в состоянии Pending и он нужен какому-то поду или его касается событие-предупреждениеnamespace, pvc
pvc_bound1, когда запрос Boundnamespace, pvc
pvcs, pvcs_pendingзапросы и те, что застряли, как описано выше
warning_eventsсобытия-предупреждения, замеченные за последний час
warning_events_by_reasonто же по причинам (BackOff, FailedScheduling, ProvisioningFailed…), не более двадцати причинreason

workload — это контроллер пода: Deployment за его ReplicaSet (api для api-7c9696d669-8tpp6), StatefulSet, DaemonSet или пусто для одиночного пода. Поды описываются по одному до 500, сначала проблемные; количества всегда охватывают каждый под.

Запрос, который просто Pending, — не неисправность: класс хранилища WaitForFirstConsumer, который поставляет k3s (local-path), оставляет запрос в Pending, пока его не использует первый под. Он считается застрявшим только тогда, когда его требует какой-то под или его касается предупреждение вроде ProvisioningFailed.

Применяемые встроенные правила:

  • Kubernetes node not ready: условие Ready узла ложно или неизвестно в течение пяти минут (Warning).
  • Kubernetes node under pressure: давление по памяти, диску или PID в течение пяти минут (Advisory).
  • Kubernetes pod crash looping: контейнер в CrashLoopBackOff в течение десяти минут (Warning). Некоторые kubelet (k3s 1.37) между двумя попытками показывают контейнер как terminated с причиной Error и никогда не CrashLoopBackOff: контейнер, перезапущенный как минимум дважды и чья последняя остановка в пределах десяти минут завершилась ненулевым кодом выхода, тоже считается зацикленным. Между двумя попытками контейнер перезапускается, и состояние на мгновение исчезает; правило читает максимум за десять минут, чтобы не начинать отсчёт заново каждый раз.
  • Kubernetes pod stuck pending: под в Pending в течение пятнадцати минут (Advisory).
  • Kubernetes pod restarting: более пяти перезапусков за час для пода, который ещё не находится в crash loop (Advisory).
  • Kubernetes workload missing replicas: Deployment, StatefulSet или DaemonSet с меньшим числом доступных реплик, чем запрошено, в течение десяти минут (Advisory, эскалация через один час). Плавное обновление убирает одну реплику, пока запускает другую; десять минут это покрывают.
  • Kubernetes volume claim pending: застрявший запрос в течение пятнадцати минут (Advisory).

Плюс Monitor unreachable, когда API-сервер перестаёт отвечать.

Панель Cluster одной фразой говорит, что не так, затем перечисляет узлы (Ready или нет, давление, cordoned, версия kubelet), поды, требующие внимания (crash loop, pending, blocked, not ready, с их перезапусками), рабочие нагрузки с недостающими репликами, застрявшие запросы томов и события-предупреждения последнего часа по причинам. Здоровый кластер показывает свои количества и ничего больше.

Создайте сервисный аккаунт только для чтения для DumbMonit

Заголовок раздела «Создайте сервисный аккаунт только для чтения для DumbMonit»
  1. Создайте сервисный аккаунт, ClusterRole, которая может только выполнять get и list для узлов, подов, рабочих нагрузок, запросов томов и событий, и свяжите их. Роль не читает ни Secret, ни ConfigMap. На сервере k3s выполняйте каждую команду как sudo k3s kubectl.

    kubectl create serviceaccount dumbmonit -n kube-system
    kubectl create clusterrole dumbmonit-read --verb=get,list --resource=nodes,pods,persistentvolumeclaims,events,deployments.apps,statefulsets.apps,daemonsets.apps
    kubectl create clusterrolebinding dumbmonit-read --clusterrole=dumbmonit-read --serviceaccount=kube-system:dumbmonit
  2. Выдайте ему токен, который не истекает: Secret типа service-account-token, который заполняет Kubernetes. Затем выведите токен и CA кластера в base64.

    echo '{"apiVersion":"v1","kind":"Secret","metadata":{"name":"dumbmonit-token","namespace":"kube-system","annotations":{"kubernetes.io/service-account.name":"dumbmonit"}},"type":"kubernetes.io/service-account-token"}' | kubectl apply -f -
    kubectl -n kube-system get secret dumbmonit-token -o jsonpath='{.data.token}' | base64 -d
    kubectl -n kube-system get secret dumbmonit-token -o jsonpath='{.data.ca\.crt}'
  3. В DumbMonit введите адрес API-сервера, например “https://k3s.lan:6443”. Вставьте токен как Service account token, а CA в base64 — как Cluster CA certificate: тогда доверяется только этот CA. Один API-сервер сообщает о всём кластере.

Для GitOps или чтобы точно просмотреть, что выдаётся, примените этот манифест вместо трёх команд kubectl create и Secret из шага 2 (kubectl apply -f dumbmonit.yaml), затем выведите токен и CA, как в шаге 2:

apiVersion: v1
kind: ServiceAccount
metadata:
name: dumbmonit
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: dumbmonit-read
rules:
- apiGroups: [""]
resources: ["nodes", "pods", "persistentvolumeclaims", "events"]
verbs: ["get", "list"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dumbmonit-read
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: dumbmonit-read
subjects:
- kind: ServiceAccount
name: dumbmonit
namespace: kube-system
---
apiVersion: v1
kind: Secret
metadata:
name: dumbmonit-token
namespace: kube-system
annotations:
kubernetes.io/service-account.name: dumbmonit
type: kubernetes.io/service-account-token

Чтобы проверить роль до добавления монитора, спросите у API-сервера, что может делать аккаунт; первая команда должна ответить yes, вторая — no:

kubectl auth can-i list pods --as=system:serviceaccount:kube-system:dumbmonit
kubectl auth can-i list secrets --as=system:serviceaccount:kube-system:dumbmonit

Токен, созданный командой kubectl create token dumbmonit -n kube-system, тоже работает, но истекает (по умолчанию через один час): тогда монитор даёт сбой с сообщением “The API server refused the token (401)”. Приведённый выше Secret — это долгоживущая форма, которую Kubernetes документирует для такого случая. Чтобы отозвать доступ DumbMonit, удалите Secret или сервисный аккаунт.

Учётные данныеПоля
Токен сервисного аккаунтаТокен, выведенный первой командой kubectl get secret, отправляется как Authorization: Bearer.

Адрес: API-сервер в виде URL (https://k3s.lan:6443), имя хоста или IP (192.0.2.10, тогда порт 6443) или host:port. За балансировщиком нагрузки или в высокодоступном k3s укажите адрес, который обслуживает API, тот, что в вашем kubeconfig.

Опции:

  • Port (6443), когда адрес его не задаёт.
  • Cluster CA certificate: значение ca.crt в base64, как его выводит последняя команда шага 2; PEM-сертификат тоже подходит, даже вставленный в одну строку. С ним DumbMonit доверяет этому CA и никакому другому.
  • Accept an unverifiable certificate: пропускает все проверки, для сети, которой вы доверяете.
  • Timeout per request (10 с).
  • Ignored namespaces: поды, рабочие нагрузки, запросы и события этих пространств имён не учитываются, например пространства имён CI, полного недолговечных подов. События узлов сохраняются всегда.

“The certificate of the API server could not be verified”: либо отсутствует CA (вставьте его, как в шаге 2), либо адрес не входит в число имён в сертификате сервера. k3s перечисляет kubernetes, localhost, имя хоста узла и его IP-адреса; добавьте другое имя через --tls-san (в /etc/rancher/k3s/config.yaml: tls-san: [k3s.lan]) и перезапустите k3s.

“The service account may not read … (403)”: ClusterRole или её привязка отсутствует или неполна. Сообщение называет ресурс, который API-сервер отклонил; примените манифест выше ещё раз.