Візуальний посібник з діагностики несправностей у Kubernetes

Прим. перев.: Ця стаття входить до складу опублікованих у вільному доступі матеріалів проекту learnk8s, що навчає роботу з Kubernetes компанії та індивідуальних адміністраторів. У ній Daniele Polencic, керівник проекту, ділиться наочною інструкцією про те, які кроки варто робити у разі виникнення проблем загального характеру у програм, запущених у кластері K8s.

Візуальний посібник з діагностики несправностей у Kubernetes

TL;DR: ось схема, яка допоможе вам налагодити deployment в Kubernetes:

Візуальний посібник з діагностики несправностей у Kubernetes

Блок-схема для пошуку та виправлення помилок у кластері. В оригіналі (англійською) вона доступна в PDF и як зображення.

При розгортанні програми в Kubernetes зазвичай необхідно визначити три компоненти:

  • розгортання — це певний рецепт зі створення копій програми, званих pod'ами;
  • Обслуговування - Внутрішній балансувальник навантаження, що розподіляє трафік по pod'ах;
  • Вхід — опис того, як трафік потраплятиме із зовнішнього світу до Service'у.

Ось коротке графічне резюме:

1) У Kubernetes програми отримують трафік із зовнішнього світу через два шари балансувальників навантаження: внутрішній та зовнішній.

Візуальний посібник з діагностики несправностей у Kubernetes

2) Внутрішній балансувальник називається Service, зовнішній – Ingress.

Візуальний посібник з діагностики несправностей у Kubernetes

3) Deployment створює pod'и та стежить за ними (вони не створюються вручну).

Візуальний посібник з діагностики несправностей у Kubernetes

Припустимо, ви хочете розгорнути простенький додаток а-ля Привіт світ. YAML-конфігурація для нього буде виглядати так:

apiVersion: apps/v1
kind: Deployment # <<<
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
      labels:
        any-name: my-app
    spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service # <<<
metadata:
  name: my-service
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    name: app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress # <<<
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
        serviceName: app
        servicePort: 80
      path: /

Визначення досить довге і легко заплутатися в тому, як компоненти пов'язані один з одним.

Наприклад:

  • Коли слід використовувати порт 80, а коли 8080?
  • Чи потрібно створювати новий порт для кожного сервісу, щоб вони не конфліктували?
  • Чи мають значення імена лейблів? Чи мають вони бути схожими всюди?

Перш ніж зосередитися на налагодженні, давайте згадаємо, як три компоненти пов'язані один з одним. Почнемо з Deployment та Service.

Зв'язок Deployment'а та Service'а

Ви здивуєтеся, але Deployment'и та Service'и ніяк не пов'язані. Натомість Service безпосередньо вказує на Pod'и в обхід Deployment'а.

Таким чином, нас цікавить, як пов'язані один з одним Pod'и та Service'и. Слід пам'ятати три речі:

  1. Селектор (selector) у Service'а повинен відповідати хоча б одному лейблу Pod'а.
  2. targetPort повинен збігатися з containerPort контейнера всередині Pod'а.
  3. port Service'а може бути будь-яким. Різні сервіси можуть використовувати той самий порт, оскільки вони мають різні IP-адреси.

Наступна схема представляє все вищеперелічене у графічній формі:

1) Уявимо, що сервіс направляє трафік на якийсь pod:

Візуальний посібник з діагностики несправностей у Kubernetes

2) При створенні pod'а необхідно задати containerPort для кожного контейнера в pod'ах:

Візуальний посібник з діагностики несправностей у Kubernetes

3) Під час створення сервісу необхідно вказати port и targetPort. Але через який із них йде підключення до контейнера?

Візуальний посібник з діагностики несправностей у Kubernetes

4) Через targetPort. Він повинен збігатися з containerPort.

Візуальний посібник з діагностики несправностей у Kubernetes

5) Припустимо, у контейнері відкрито порт 3000. Тоді значення targetPort має бути таким самим.

Візуальний посібник з діагностики несправностей у Kubernetes

У YAML-файлі лейбли та ports / targetPort повинні збігатися:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  labels:
    track: canary
spec:
  selector:
    matchLabels:
      any-name: my-app
  template:
    metadata:
     labels:  # <<<
        any-name: my-app  # <<<
   spec:
      containers:
      - name: cont1
        image: learnk8s/app:1.0.0
        ports:
       - containerPort: 8080  # <<<
---
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - port: 80
   targetPort: 8080  # <<<
 selector:  # <<<
    any-name: my-app  # <<<

А як щодо лейблу track: canary у верхній частині розділу Deployment? Чи він повинен збігатися?

Цей лейбл відноситься до розгортання і не використовується сервісом для маршрутизації трафіку. Іншими словами, його можна видалити або присвоїти інше значення.

А що щодо селектора matchLabels?

Він завжди повинен збігатися з лейблами Pod'аоскільки використовується Deployment'ом для відстеження pod'ів.

Припустимо, що ви внесли правильні редагування. Як їх перевірити?

Перевірити лейбл pod'ів можна наступною командою:

kubectl get pods --show-labels

Або, якщо pod'и належать кільком додаткам:

kubectl get pods --selector any-name=my-app --show-labels

Де any-name=my-app - це лейбл any-name: my-app.

Залишилися складнощі?

Можна підключитись до pod'у! Для цього треба використати команду port-forward в Kubectl. Вона дозволяє підключитися до сервісу та перевірити з'єднання.

kubectl port-forward service/<service name> 3000:80

тут:

  • service/<service name> - Ім'я сервісу; у нашому випадку це my-service;
  • 3000 - порт, який потрібно відкрити на комп'ютері;
  • 80 - порт, прописаний у полі port сервісу.

Якщо вдалося встановити з'єднання, то налаштування правильні.

Якщо з'єднання встановити не вдалося, то проблема з лейблами або порти не збігаються.

Зв'язок Service'а та Ingress'а

Наступний крок у забезпеченні доступу до програми пов'язаний із налаштуванням Ingress'а. Ingress повинен знати, як відшукати сервіс, потім знайти pod'и та направити до них трафік. Ingress знаходить потрібний сервіс на ім'я та відкритий порт.

В описі Ingress та Service повинні збігатися два параметри:

  1. servicePort Ingress повинен збігатися з параметром port у Service;
  2. serviceName в Ingress має збігатися з полем name у Service.

Наступна схема підбиває підсумки щодо підключення портів:

1) Як ви вже знаєте, Service слухає якийсь port:

Візуальний посібник з діагностики несправностей у Kubernetes

2) У Ingress'а є параметр, званий servicePort:

Візуальний посібник з діагностики несправностей у Kubernetes

3) Цей параметр (servicePort) завжди повинен збігатися з port у визначенні Service:

Візуальний посібник з діагностики несправностей у Kubernetes

4) Якщо в Service заданий порт 80, то необхідно, щоб servicePort також дорівнював 80:

Візуальний посібник з діагностики несправностей у Kubernetes

На практиці необхідно звертати увагу на такі рядки:

apiVersion: v1
kind: Service
metadata:
 name: my-service  # <<<
spec:
  ports:
 - port: 80  # <<<
   targetPort: 8080
  selector:
    any-name: my-app
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: my-ingress
spec:
  rules:
  - http:
    paths:
    - backend:
       serviceName: my-service  # <<<
       servicePort: 80  # <<<
     path: /

Як перевірити, чи працює Ingress?

Можна скористатися методом з kubectl port-forward, але замість сервісу необхідно підключитися до контролера Ingress.

Спочатку потрібно дізнатися ім'я pod'а з контролером Ingress:

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Знайдіть pod Ingress'а (він може відноситися до іншого простору імен) і виконайте команду describe, щоб дізнатися номери портів:

kubectl describe pod nginx-ingress-controller-6fc5bcc 
--namespace kube-system 
 | grep Ports
Ports:         80/TCP, 443/TCP, 18080/TCP

Нарешті, підключіться до pod'у:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Тепер щоразу, коли ви надсилатимете запит на порт 3000 на комп'ютері, він буде перенаправлятися на порт 80 pod'а з контролером Ingress. Перейшовши на http://localhost:3000, ви повинні будете побачити сторінку, створену програмою.

Резюме з портів

Давайте ще раз згадаємо про те, які порти та лейбли повинні збігатися:

  1. Селектор у визначенні Service повинен збігатися з лейблом pod'а;
  2. targetPort у визначенні Service повинен збігатися з containerPort контейнера всередині pod'а;
  3. port у визначенні Service може бути будь-яким. Різні послуги можуть використовувати той самий порт, оскільки вони різні IP-адреси;
  4. servicePort Ingress'а повинен збігатися з port у визначенні Service;
  5. Назва сервісу має співпадати з полем serviceName у Ingress'і.

На жаль, недостатньо знати, як правильно структурувати конфігурацію YAML.

Що трапляється, коли щось іде не так?

Можливо, під не запускається або він падає.

3 кроки для діагностики несправностей у додатках у Kubernetes

Перш ніж приступати до налагодження deployment'а, необхідно мати гарне уявлення про те, як працює Kubernetes.

Оскільки в кожному викаченому додатку K8s є три компоненти, проводити їх налагодження слід у певному порядку, починаючи з самого низу.

  1. Спочатку треба переконатися, що pod'и працюють, потім...
  2. Перевірити, чи постачає сервіс трафік pod'ам, а потім…
  3. Перевірте, чи правильно настроєно Ingress.

Візуальне подання:

1) Починати пошук проблем слід із самого низу. Спочатку перевірте, що pod'и мають статуси Ready и Running:

Візуальний посібник з діагностики несправностей у Kubernetes

2) Якщо pod'и готові (Ready), слід з'ясувати, чи розподіляє сервіс трафік між pod'ами:

Візуальний посібник з діагностики несправностей у Kubernetes

3) Нарешті, потрібно проаналізувати зв'язок сервісу та Ingress'а:

Візуальний посібник з діагностики несправностей у Kubernetes

1. Діагностика pod'ів

У більшості випадків проблема пов'язана з pod'ом. Переконайтеся, що pod'и значаться як Ready и Running. Перевірити це можна за допомогою команди:

kubectl get pods
NAME                    READY STATUS            RESTARTS  AGE
app1                    0/1   ImagePullBackOff  0         47h
app2                    0/1   Error             0         47h
app3-76f9fcd46b-xbv4k   1/1   Running           1         47h

У висновку команди, наведеному вище, останній pod значиться як Running и ReadyПроте для двох інших це не так.

Як зрозуміти, що пішло негаразд?

Є чотири корисні команди для діагностики pod'ів:

  1. kubectl logs <имя pod'а> дозволяє витягти логи з контейнерів у pod'і;
  2. kubectl describe pod <имя pod'а> дозволяє переглянути список подій, пов'язаних з pod'ом;
  3. kubectl get pod <имя pod'а> дозволяє отримати YAML-конфігурацію pod'а, що зберігається в Kubernetes;
  4. kubectl exec -ti <имя pod'а> bash дозволяє запустити інтерактивну командну оболонку в одному з контейнерів pod'а

Яку з них вибрати?

Справа в тому, що нема універсальної команди. Слід використовувати їхню комбінацію.

Типові проблеми pod'ів

Існує два основних типи помилок pod'ів: помилки під час запуску (startup) та помилки під час роботи (runtime).

Помилки запуску:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • InvalidImageName

Runtime-помилки:

  • CrashLoopBackOff
  • RunContainerError
  • KillContainerError
  • VerifyNonRootError
  • RunInitContainerError
  • CreatePodSandboxError
  • ConfigPodSandboxError
  • KillPodSandboxError
  • SetupNetworkError
  • TeardownNetworkError

Деякі помилки трапляються частіше, ніж інші. Ось кілька найпоширеніших помилок та способи їх усунення.

ImagePullBackOff

Ця помилка з'являється коли Kubernetes не може отримати образ для одного з контейнерів pod'а. Ось три найпоширеніші причини цього:

  1. Неправильно вказано ім'я образу - наприклад, ви зробили в ньому помилку, або образ не існує;
  2. Вказано неіснуючий тег для образу;
  3. Образ зберігається в закритому реєстрі, і Kubernetes не має повноважень для доступу до нього.

Перші дві причини легко усунути — достатньо поправити ім'я образу і тег. У разі останньої необхідно внести облікові дані до закритого реєстру до Secret та додати посилання на нього до pod'ів. У документації Kubernetes є приклад як це можна зробити.

CrashLoopBackOff

Kubenetes виводить помилку CrashLoopBackOffякщо контейнер не може запуститися. Зазвичай таке трапляється, коли:

  1. У додатку є помилка, яка дозволяє його запустити;
  2. контейнер налаштований неправильно;
  3. Тест Liveness завершився невдало надто багато разів.

Необхідно спробувати дістатися до ліг з контейнера, щоб з'ясувати причину його збою. Якщо отримати доступ до логів важко, оскільки контейнер надто швидко перезапускається, можна скористатися наступною командою:

kubectl logs <pod-name> --previous

Вона виводить повідомлення про помилки з попередньої реінкарнації контейнера.

RunContainerError

Ця помилка виникає, коли контейнер не в змозі запуститись. Вона відповідає моменту до запуску програми. Зазвичай її причиною є неправильне налаштування, наприклад:

  • спроба примонтувати неіснуючий том, такий як ConfigMap або Secrets;
  • спроба примонтувати том типу read-only як read-write.

Для аналізу подібних помилок добре підходить команда kubectl describe pod <pod-name>.

Pod'и у стані Pending

Після створення pod залишається в стані Pending.

Чому таке відбувається?

Ось можливі причини (я виходжу з припущення, що планувальник працює нормально):

  1. У кластері недостатньо ресурсів, таких як обчислювальна потужність та пам'ять, для запуску pod'а.
  2. У відповідному просторі імен встановлено об'єкт ResourceQuota і створення pod'а призведе до того, що простір імен вийде за межі квоти.
  3. Pod прив'язаний до Pending PersistentVolumeClaim.

У цьому випадку рекомендується скористатися командою kubectl describe та перевірити розділ Events:

kubectl describe pod <pod name>

У разі помилок, пов'язаних з ResourceQuotasрекомендується переглянути логи кластера за допомогою команди

kubectl get events --sort-by=.metadata.creationTimestamp

Pod'и не в змозі Ready

Якщо під значиться як Running, але не може Ready, означає перевірка його готовності (readiness probe) завершується невдало.

Коли таке відбувається, під не підключається до сервісу, і трафік на нього не надходить. Збій тесту readiness викликаний проблемами у додатку. У цьому випадку для пошуку помилки необхідно проаналізувати розділ Events у висновку команди kubectl describe.

2. Діагностика сервісів

Якщо pod'и значаться як Running и Ready, але відповіді від програми, як і раніше, немає, слід перевірити налаштування сервісу.

Сервіси займаються маршрутизацією трафіку до pod'ів залежно від їхніх лейблів. Тому перше, що потрібно зробити, — перевірити, скільки pod'ів працюють із сервісом. Для цього можна перевірити endpoint'и у сервісі:

kubectl describe service <service-name> | grep Endpoints

Endpoint - це пара значень виду <IP-адрес:порт>, і у висновку має бути присутня хоча б одна така пара (тобто з сервісом працює хоча б один pod).

Якщо розділ Endpoins порожній, можливі два варіанти:

  1. немає жодного pod'а з правильним лейблом (підказка: перевірте, чи правильно обрано namespace);
  2. є помилка у лейблах сервісу у селекторі.

Якщо ви бачите список endpoint'ів, але, як і раніше, не можете отримати доступ до програми, то ймовірним винуватцем є помилка в targetPort в описі сервісу.

Як перевірити працездатність сервісу?

Незалежно від типу сервісу можна використовувати команду kubectl port-forward для підключення до нього:

kubectl port-forward service/<service-name> 3000:80

тут:

  • <service-name> - Ім'я сервісу;
  • 3000 - порт, який ви відкриваєте на комп'ютері;
  • 80 - порт на стороні сервісу.

3. Діагностика Ingress

Якщо ви дочитали до цього місця, то:

  • pod'и значаться як Running и Ready;
  • сервіс успішно розподіляє трафік по pod'ах.

Однак ви, як і раніше, не можете «достукатися» до програми.

Це означає, що, швидше за все, неправильно налаштований контролер Ingress. Оскільки контролер Ingress є стороннім компонентом кластера, існують різні методи налагодження в залежності від його типу.

Але перш ніж вдатися до допомоги спеціальних інструментів для налаштування Ingress, можна зробити щось зовсім просте. Ingress використовує serviceName и servicePort для підключення до сервісу Необхідно перевірити, чи правильно вони налаштовані. Зробити це можна за допомогою команди:

kubectl describe ingress <ingress-name>

Якщо стовпець Backend порожній, висока ймовірність помилки у конфігурації. Якщо бекенди на місці, але доступу до додатку, як і раніше, немає, то проблема може бути пов'язана з:

  • налаштування доступності Ingress'а з публічного інтернету;
  • налаштування доступності кластера з публічного інтернету.

Виявити проблеми з інфраструктурою можна, підключившись безпосередньо до pod'у Ingress'а. Для цього спочатку знайдіть pod Ingress-контролера (він може бути в іншому просторі імен):

kubectl get pods --all-namespaces
NAMESPACE   NAME                              READY STATUS
kube-system coredns-5644d7b6d9-jn7cq          1/1   Running
kube-system etcd-minikube                     1/1   Running
kube-system kube-apiserver-minikube           1/1   Running
kube-system kube-controller-manager-minikube  1/1   Running
kube-system kube-proxy-zvf2h                  1/1   Running
kube-system kube-scheduler-minikube           1/1   Running
kube-system nginx-ingress-controller-6fc5bcc  1/1   Running

Скористайтеся командою describe, щоб встановити порт:

kubectl describe pod nginx-ingress-controller-6fc5bcc
--namespace kube-system 
 | grep Ports

Нарешті, підключіться до pod'у:

kubectl port-forward nginx-ingress-controller-6fc5bcc 3000:80 --namespace kube-system

Тепер усі запити на порт 3000 на комп'ютері будуть надсилатися на порт 80 pod'а.

Чи працює він тепер?

  • Якщо так, то проблема з інфраструктурою. Необхідно з'ясувати, як саме здійснюється маршрутизація трафіку кластером.
  • Якщо ні, то проблема із контролером Ingress.

Якщо не вдається змусити працювати контролер Ingress, доведеться провести його налагодження.

Існує багато різновидів контролерів Ingress. Найпопулярнішими є Nginx, HAProxy, Traefik та ін. (Докладніше про існуючі рішення див. нашому огляді - прим. перев.) Слід скористатися посібником з усунення несправностей у документації відповідного контролера. Оскільки Ingress Nginx є найпопулярнішим контролером Ingress, ми включили до статті кілька порад щодо вирішення пов'язаних із ним проблем.

Налагодження контролера Ingress Nginx

Проект Ingress-nginx має офіційний плагін для kubectl. Команду kubectl ingress-nginx можна використовувати для:

  • аналізу логів, бекендів, сертифікатів тощо;
  • підключення до Ingress'у;
  • вивчення поточної конфігурації.

Допоможуть вам у цьому наступні три команди:

  • kubectl ingress-nginx lint - Перевіряє nginx.conf;
  • kubectl ingress-nginx backend - Досліджує бекенд (за аналогією з kubectl describe ingress <ingress-name>);
  • kubectl ingress-nginx logs - Перевіряє логи.

Зверніть увагу: в деяких випадках може знадобитися вказати правильний простір імен для контролера Ingress за допомогою прапора --namespace <name>.

Резюме

Діагностика в Kubernetes може виявитися непростим завданням, якщо не знати, з чого почати. До проблеми завжди слід підходити за принципом «знизу-вгору»: починайте з pod'ів, а потім переходьте до сервісу та Ingress'у. Методи налагодження, описані у статті, можуть застосовуватись і до інших об'єктів, таких як:

  • непрацюючі Job'и та CronJob'и;
  • StatefulSet'и і DaemonSet'и.

Висловлюю подяку Gergely Risko, Daniel Weibel и Charles Christyraj за цінні зауваження та доповнення.

PS від перекладача

Читайте також у нашому блозі:

Джерело: habr.com

Додати коментар або відгук