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

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

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

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

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

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

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

  • Deployment — това е определен рецепта за създаване на копия на приложението, наричани pod’и;
  • Услуга — вътрешен балансер на натоварването, който разпределя трафика между 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-a за проследяване на 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 конфигурация.

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

Може би pod-ът не стартира или пада.

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

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

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

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

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

1) Търсенето на проблеми трябва да започне от най-долния слой. Първо проверете, че pod-овете имат статуси Готово и В процес на изпълнение:

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

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

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

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

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

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

В повечето случаи проблемът е свързан с pod-а. Уверете се, че pod-овете са обозначени като Готово и В процес на изпълнение. Можете да проверите това с командата:

kubectl get pods
ИМЕ                    ГОТОВ STATUS           РЕСТАРТИ  ВЪЗРАСТ
app1                    0/1   ImagePullBackOff  0         47h
app2                    0/1   Грешка           0         47h
app3-76f9fcd46b-xbv4k   1/1   Работи           1         47h

В изхода от горната команда, последният pod е обозначен като В процес на изпълнение и Готово, но за другите двама не е така.

Как да разберем какво е сгрешено?

Съществуват четири полезни команди за диагностика на 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-овете: грешки при стартиране и грешки по време на работа.

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

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

Грешки по време на изпълнение:

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

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

ImagePullBackOff

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

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

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

CrashLoopBackOff

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

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

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

kubectl logs  --previous

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

RunContainerError

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

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

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

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

След създаването pod остава в състояние Pending.

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

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

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

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

kubectl describe pod

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

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

Pod’овете не са в статус Ready

Ако pod е маркиран като В процес на изпълнение, но не е в статус Готово, значи проверката за готовност (readiness probe) завършва неуспешно.

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

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

Ако pod’овете са маркирани като В процес на изпълнение и Готово, но от приложението все още няма отговор, е необходимо да се провери конфигурацията на услугата.

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

kubectl describe service  | grep Endpoints

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

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

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

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

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

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

kubectl port-forward service/ 3000:80

Тук:

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

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

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

  • pod’овете са маркирани като В процес на изпълнение и Готово;
  • услугата успешно разпределя трафика между 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