Визуално ръководство за диагностика на проблеми в Kubernetes

Прим. прев.: Тази статия е част от публикуваните в свободен достъп материали на проекта learnk8s, обучаващи за работа с Kubernetes за компании и индивидуални администратори. В нея Даниеле Поленчик, ръководител на проекта, споделя нагледна инструкция за стъпките, които трябва да предприемете в случай на проблеми от общ характер с приложенията, стартирани в K8s клъстери.

Визуално ръководство за диагностика на проблеми в Kubernetes

TL;DR: ето схема, която ще ви помогне да отстраните проблеми с разгръщането в Kubernetes:

Визуално ръководство за диагностика на проблеми в Kubernetes

Блок-схема за намиране и поправка на грешки в клъстер. Оригиналът (на английски) е наличен в PDF и като изображение.

При разгръщането на приложение в Kubernetes обикновено е необходимо да определите три компонента:

  • Deployment — това е рецепта за създаване на копия на приложението, наречени pod’ове;
  • Service — вътрешен балансировчик на натоварването, който разпределя трафика между pod’овете;
  • Ingress — описание на начина, по който трафикът ще достига от външния свят до Service.

Ето кратко графично резюме:

1) В Kubernetes приложения получават трафик от външния свят през два слоя балансировчици на натоварването: вътрешен и външен.

Визуално ръководство за диагностика на проблеми в Kubernetes

2) Вътрешният балансировчик се нарича Service, а външният – Ingress.

Визуално ръководство за диагностика на проблеми в Kubernetes

3) Deployment създава pod’ове и следи за тях (те не се създават ръчно).

Визуално ръководство за диагностика на проблеми в Kubernetes

Да предположим, че искате да разгръщате простичко приложение, като Hello World. 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 трябва да съвпада с полето име в 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 конфигурацията.

Какво се случва, когато нещо върви не по план?

Възможно е pod‘ът да не се стартира или да пада.

3 стъпки за диагностика на неизправности в приложенията в Kubernetes

Преди да започнете с отстраняването на грешки в deployment‘а, е необходимо да имате добра представа как работи Kubernetes.

Тъй като всяко изтеглено приложение в K8s съдържа три компонента, отстраняването на грешки в тях трябва да се извършва в определен ред, започвайки от самото дъно.

  1. Първо трябва да се уверите, че pod‘овете работят, след това…
  2. Проверете дали услугата доставя трафик до pod‘овете, а след това…
  3. Проверете дали Ingress е конфигуриран правилно.

Визуално представяне:

1) Търсенето на проблеми трябва да започне от самото дъно. Първо проверьте дали pod‘овете имат статуси Готов и Running:

Визуално ръководство за диагностика на проблеми в Kubernetes

2) Ако pod‘овете са готови (Готов), трябва да разберете дали услугата разпределя трафик между pod‘овете:

Визуално ръководство за диагностика на проблеми в Kubernetes

3) Накрая трябва да анализирате връзката между услугата и Ingress’а:

Визуално ръководство за диагностика на проблеми в Kubernetes

1. Диагностика на pod‘овете

В повечето случаи проблемът е свързан с pod‘а. Уверете се, че pod‘овете са обозначени като Готов и 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 и Готов, но за двата други не е така.

Как да разберем какво е тръгнало не както трябва?

Има четири полезни команди за диагностика на pod‘овете:

  1. kubectl logs позволява да извлечете логовете от контейнерите в pod‘а;
  2. kubectl describe pod позволява да видите списък с събитията, свързани с pod‘а;
  3. kubectl get pod позволява да получите YAML конфигурацията на pod‘а, съхранявана в Kubernetes;
  4. kubectl exec -ti bash позволява да стартирате интерактивна командна обвивка в един от контейнерите на pod‘а.

Коя от тях да изберете?

Истината е, че няма универсална команда. Трябва да се използва комбинация от тях.

Типични проблеми с pod‘овете

Има два основни типа грешки при pod‘овете: грешки при стартиране (startup) и грешки при работа (runtime).

Грешки при стартиране:

  • ImagePullBackoff
  • ImageInspectError
  • ErrImagePull
  • ErrImageNeverPull
  • RegistryUnavailable
  • НевалидноИмеНаОбраза

Проблеми по време на изпълнение:

  • CrashLoopBackOff
  • Грешка при стартиране на контейнер
  • Грешка при спиране на контейнер
  • Грешка при проверка на не-рут потребител
  • Грешка при стартиране на инициализационен контейнер
  • Грешка при създаване на Pod Sandbox
  • Грешка при конфигуриране на Pod Sandbox
  • Грешка при спиране на Pod Sandbox
  • Грешка при настройка на мрежата
  • Грешка при премахване на мрежата

Някои грешки се срещат по-често от други. Ето някои от най-разпространените грешки и начини за разрешаването им.

ImagePullBackOff

Тази грешка се появява, когато Kubernetes не може да изтегли образ за един от контейнерите на pod-а. Ето три от най-разпространените причини за това:

  1. Неправилно посочено име на образа — например, направили сте грешка в него или образът не съществува.
  2. Посочен е несъществуващ таг за образа.
  3. Образът се съхранява в частен регистър и Kubernetes няма разрешение да му получи достъп.

Първите две причини са лесни за отстраняване — просто поправете името на образа и тага. В случай на последната трябва да въведете данните за достъп до частния регистър в Secret и да добавите връзки към него в pod-овете. В документацията на Kubernetes има пример как това може да се направи.

CrashLoopBackOff

Kubernetes изводи грешка CrashLoopBackOff, ако контейнерът не може да стартира. Обикновено се случва, когато:

  1. В приложението има грешка, която пречи на стартирането му;
  2. Контейнер настроен неправилно;
  3. Тестът за Liveness е провален твърде много пъти.

Необходимо е да се опитате да достъпите логовете от контейнера, за да установите причината за провала му. Ако достъпът до логовете е затруднен, тъй като контейнерът се реконструира твърде бързо, можете да използвате следната команда:

kubectl logs  --previous

Тя извежда съобщения за грешки от предишната реинкарнация на контейнера.

Грешка при стартиране на контейнер

Тази грешка възниква, когато контейнерът не може да стартира. Тя съответства на момента преди стартиране на приложението. Обикновено причината за нея е неправилна настройка, например:

  • опит за монтиране на несъществуващ том, като ConfigMap или Secrets;
  • опит за монтиране на том от тип read-only като read-write.

За анализ на подобни грешки е полезна командата kubectl describe pod.

Pod-овете са в състояние Pending

След като pod-ът бъде създаден, той остава в състояние Pending.

Защо се случва това?

Ето възможни причини (изходя от предположението, че планировчикът работи нормално):

  1. В кластера липсват ресурси, като изчислителна мощност и памет, за да стартира pod-ът.
  2. В съответното пространство от имена е инсталиран обект ResourceQuota Създаването на pod ще доведе до преминаване на пространството в името извън квотата.
  3. Pod е свързан с Pending PersistentVolumeClaim.

В този случай се препоръчва да се използва командата kubectl describe и да се провери секцията Събития:

kubectl describe pod

В случай на грешки, свързани с ResourceQuotas, се препоръчва да се прегледат логовете на кластера с помощта на командата

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

Pod-овете не са в състояние Ready

Ако pod-ът е отбелязан като Running, но не е в състояние Готов, това означава, че проверката за готовност (readiness probe) не е успешна.

Когато това се случи, pod-ът не се свързва със службата и трафикът не достига до него. Неуспехът в теста за готовност е причинен от проблеми в приложението. В този случай е необходимо да се анализира секцията Събития в изхода на командата kubectl describe.

2. Диагностика на услугите

Ако pod-овете са отбелязани като Running и Готов, но все още няма отговор от приложението, трябва да проверите настройките на услугата.

Услугите се занимават с маршрутизация на трафика към pod-овете в зависимост от техните етикети. Затова първото нещо, което трябва да направите, е да проверите колко pod-а работят с услугата. За целта можете да проверите endpoint-ите в услугата:

kubectl describe service  | grep Endpoints

Endpoint е двойка стойности от вида <IP-адрес:порт>, и в изхода трябва да присъства поне една такава двойка (т.е. поне един pod работи с услугата).

Ако секцията Endpoints е празна, са възможни два варианта:

  1. няма нито един pod с правилния етикет (подсказка: проверете дали namespace е избран правилно);
  2. има грешка в етикетите на услугата в селектора.

Ако виждате списък с endpoint-и, но все пак не можете да получите достъп до приложението, то вероятната причина е грешка в targetPort описанието на услугата.

Как да проверите работоспособността на услугата?

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

kubectl port-forward service/ 3000:80

Тук:

  • <service-name> — името на услугата;
  • 3000 — портът, който отваряте на компютъра;
  • 80 — портът на страната на услугата.

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

Ако сте прочели до тук, то:

  • pod-овете са отбелязани като Running и Готов;
  • услугата успешно разпределя трафика между pod-овете.

Обаче все още не можете да "достигнете" приложението.

Това означава, че вероятно контролерът Ingress е неправилно конфигуриран. Тъй като контролерът Ingress е външен компонент в кластера, съществуват различни методи за отстраняване на неизправности в зависимост от неговия тип.

Но преди да прибегнете до помощта на специализирани инструменти за настройка на Ingress, можете да направите нещо доста просто. Ingress използва serviceName и servicePort за свързване с услугата. Необходимо е да проверите дали те са конфигурирани правилно. Можете да направите това с командата:

kubectl describe ingress

Ако колоната 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);
  • kubectl ingress-nginx logs — проверява логовете.

Обърнете внимание: в някои случаи може да се наложи да посочите правилното пространство с имена за контролера на Ingress с помощта на флага --namespace.

Резюме

Диагностицирането в Kubernetes може да се окаже предизвикателство, ако не знаете откъде да започнете. Винаги следва да подходите към проблема от принципа „отдолу нагоре“: започнете с pod-овете, след това преминете към услугите и Ingress. Методите за отстраняване на грешки, описани в статията, могат да се прилагат и към други обекти, такива като:

  • неработещи Job-ове и CronJob-ове;
  • StatefulSet-ове и DaemonSet-ове.

Искам да благодаря Gergely Risko, Daniel Weibel и Charles Christyraj за ценните коментари и допълнения.

P.S. от преводача

Прочетете също в нашия блог:

Източник: habr.com

Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри 🔥 Купете надежден хостинг за сайтове с защита от DDoS, VPS VDS сървъри | ProHoster