Модуль 17 / 19
Metrics, logs, Events и наблюдаемость
Содержание
1. Цели и результат модуля
Вы научитесь сопоставлять metrics, logs, Events и object status во времени, понимать источник каждого сигнала и не принимать kubectl top за полноценную систему мониторинга.
- metrics показывают числовое поведение
- logs объясняют события приложения
- Events фиксируют решения control plane и kubelet
- status отражает текущее наблюдение controller
- единая временная шкала превращает сигналы в evidence
2. Теория: сигналы наблюдаемости
Metrics подходят для trends, rates, saturation и alerts; logs — для контекста конкретного выполнения; traces связывают путь запроса между компонентами; Events объясняют недавние Kubernetes transitions. Один сигнал редко доказывает root cause.
Observability начинается с вопроса и гипотезы, а не с установки dashboard. Сначала определяют expected behavior, временной интервал, объект и границу системы.
3. Metrics API и metrics-server
metrics-server получает resource metrics у kubelet и публикует aggregated API metrics.k8s.io для autoscaling и kubectl top. Он хранит только свежий срез CPU/memory и не является Prometheus, TSDB или системой долгосрочной аналитики.
kubectl top показывает usage, а describe node — requests/limits. Эти числа отвечают на разные вопросы и имеют разные timestamps/windows.
4. API aggregation и TLS
APIService направляет запрос metrics.k8s.io к Service metrics-server. Доступность зависит от aggregation layer, Service endpoints, RBAC и scrape kubelet. На стенде kubelet certificate не имеет IP SAN, поэтому строгая TLS-проверка ломает scrape.
--kubelet-insecure-tls допустим здесь как явно документированный lab workaround. Production требует доверенных kubelet serving certificates; отключение проверки скрывает подмену endpoint.
5. Container logs и их границы
kubectl logs читает CRI-managed stdout/stderr выбранного container. Для multi-container Pod указывают -c либо --all-containers; --previous обращается к предыдущему container instance; --since, --tail и --timestamps ограничивают выборку.
Logs локальны и подвержены rotation/GC. После reschedule, удаления Pod или быстрой очистки runtime previous log может отсутствовать. Production требует node-level collector и внешнее хранилище с retention.
6. Events, status и временная шкала
Events — namespaced API objects с reason, reporting controller, involved object и сериями повторов. Они полезны для FailedScheduling, image pull, probe, mount и BackOff, но имеют ограниченный retention и не являются audit log.
Сортировка по creationTimestamp помогает, но repeated series и разные часы Nodes требуют осторожности. Для корреляции синхронизируйте время и фиксируйте UTC timestamps.
7. Методические указания
- Начинайте с object status и временного окна
- Сравнивайте usage с requests/limits, не смешивая их
- Указывайте container явно
- Используйте timestamps, since и tail
- Читайте Events вместе с logs
- Проверяйте APIService/Service/endpoints при сбое top
- Не используйте insecure TLS в production
- Не считайте Events или kubectl logs долговременным архивом
8. Лабораторная работа: Metrics API
Установите фиксированный metrics-server v0.9.0. Сначала сохраните x509 evidence, затем примените только лабораторный workaround и дождитесь Available APIService.
curl -fsSL https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.9.0/components.yaml \
-o /tmp/metrics-server-v0.9.0.yaml
kubectl apply --dry-run=server -f /tmp/metrics-server-v0.9.0.yaml
kubectl apply -f /tmp/metrics-server-v0.9.0.yaml
kubectl -n kube-system rollout status deployment/metrics-server --timeout=60s || true
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl -n kube-system logs deployment/metrics-server --tail=40
# Только для этой лаборатории: kubelet certificate не содержит IP SAN.
kubectl -n kube-system patch deployment metrics-server --type=json -p='[
{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}
]'
kubectl -n kube-system rollout status deployment/metrics-server --timeout=120s
kubectl wait --for=condition=Available apiservice/v1beta1.metrics.k8s.io --timeout=120s
kubectl top node9. Лабораторная работа: наблюдаемый workload
Multi-container Pod пишет различимые stdout/stderr, а crashloop намеренно завершается с 17. Requests/limits дают контекст для resource metrics.
mkdir -p /tmp/k8s-m17
cat > /tmp/k8s-m17/workload.yaml <<'EOF'
apiVersion: v1
kind: Namespace
metadata: {name: k8s-m17}
---
apiVersion: v1
kind: Pod
metadata: {name: observable, namespace: k8s-m17, labels: {app: observable}}
spec:
containers:
- name: app
image: busybox:1.37
command: ["sh", "-c", "i=0; while true; do i=$((i+1)); echo level=info component=app iteration=$i; [ $((i%5)) -eq 0 ] && echo level=warn component=app iteration=$i >&2; sleep 2; done"]
resources: {requests: {cpu: 10m, memory: 8Mi}, limits: {cpu: 100m, memory: 32Mi}}
- name: sidecar
image: busybox:1.37
command: ["sh", "-c", "while true; do echo level=info component=sidecar; sleep 5; done"]
resources: {requests: {cpu: 5m, memory: 4Mi}, limits: {cpu: 50m, memory: 16Mi}}
---
apiVersion: v1
kind: Pod
metadata: {name: crashloop, namespace: k8s-m17, labels: {app: crashloop}}
spec:
containers:
- name: app
image: busybox:1.37
command: ["sh", "-c", "echo start attempt; sleep 3; echo deliberate exit >&2; exit 17"]
EOF
kubectl apply -f /tmp/k8s-m17/workload.yaml || true
kubectl apply --dry-run=server -f /tmp/k8s-m17/workload.yaml
kubectl apply -f /tmp/k8s-m17/workload.yaml
kubectl -n k8s-m17 wait --for=condition=Ready pod/observable --timeout=120s10. Лабораторная работа: metrics и logs
Сравните Node, Pod и container usage, затем посмотрите raw Metrics API с timestamp/window. Исследуйте логи по container и общей выборкой.
sleep 20
kubectl top node
kubectl top pod -n k8s-m17
kubectl top pod -n k8s-m17 --containers
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | jq '.items[] | {name:.metadata.name, timestamp, window, usage}'
kubectl get --raw /apis/metrics.k8s.io/v1beta1/namespaces/k8s-m17/pods | jq '.items[] | {pod:.metadata.name, containers:.containers}'
kubectl describe node | sed -n '/Allocated resources:/,/Events:/p'kubectl -n k8s-m17 logs observable -c app --tail=10 --timestamps
kubectl -n k8s-m17 logs observable --all-containers=true --prefix --tail=20
kubectl -n k8s-m17 logs observable -c app --since=30s
kubectl -n k8s-m17 logs observable -c app -f
# Остановите follow через Ctrl+C.
kubectl -n k8s-m17 get pod crashloop -o jsonpath='phase={.status.phase}{" restarts="}{.status.containerStatuses[0].restartCount}{" waiting="}{.status.containerStatuses[0].state.waiting.reason}{" lastReason="}{.status.containerStatuses[0].lastState.terminated.reason}{" exit="}{.status.containerStatuses[0].lastState.terminated.exitCode}{"\n"}'
kubectl -n k8s-m17 logs crashloop -c app --timestamps
kubectl -n k8s-m17 logs crashloop -c app --previous --timestamps || true11. Events и диагностика разрывов сигнала
Соберите BackOff Events, terminated state, kubelet journal и runtime container list. Previous log может быть недоступен после CRI cleanup — это не повод выдумывать его содержимое.
kubectl -n k8s-m17 get events --sort-by=.metadata.creationTimestamp
kubectl -n k8s-m17 get events --field-selector involvedObject.name=crashloop --sort-by=.metadata.creationTimestamp
kubectl -n k8s-m17 describe pod crashloop
kubectl get events -A --sort-by=.metadata.creationTimestamp | tail -30
sudo journalctl -u kubelet --since '10 minutes ago' --no-pager | tail -80
sudo crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps -a --name crashloop
kubectl -n kube-system logs deployment/metrics-server --since=10m --tail=80Для сбоя top пройдите: APIService condition → metrics-server Pod/logs → Service/EndpointSlice → kubelet reachability/certificate → RBAC. Для CrashLoop: status/exit → Events → current/previous logs → config/resources/dependencies.
kubectl get apiservice v1beta1.metrics.k8s.io -o yaml
kubectl -n kube-system get deployment,pod,service,endpointslice -l k8s-app=metrics-server
kubectl -n kube-system get deployment metrics-server -o jsonpath='{.spec.template.spec.containers[0].args}{"\n"}'
kubectl -n kube-system logs deployment/metrics-server --tail=50
kubectl top pod -n namespace-does-not-exist 2>&1 || true
kubectl -n k8s-m17 get pod crashloop -o yaml
kubectl -n k8s-m17 get events --field-selector involvedObject.name=crashloop --sort-by=.metadata.creationTimestamp
kubectl -n k8s-m17 logs crashloop --previous 2>&1 || true12. Контроль, критерии освоения и cleanup
- metrics-server хранит history?
- Usage равно request?
- Что делает APIService?
- Почему IP SAN важен?
- Когда нужен -c?
- Что означает --previous?
- Почему previous log может исчезнуть?
- Events равны audit log?
- Зачем синхронизация времени?
- Как отличить отсутствие metrics от нулевого usage?
kubectl delete namespace k8s-m17 --wait=true
rm -rf /tmp/k8s-m17
# metrics-server остаётся инфраструктурой следующих лабораторий.
kubectl get apiservice v1beta1.metrics.k8s.io
kubectl top nodeМодуль освоен, если вы строите временную шкалу CrashLoop из status, Events и logs и локализуете отказ Metrics API по слоям.