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

TL;DR: ето схема, която ще ви помогне да отстраните проблемите в Kubernetes:
Блок-схема за откриване и коригиране на грешки в клъстера. Оригиналната версия (на английски) е налична в и .
При разгръщане на приложение в Kubernetes обикновено е необходимо да се определят три компонента:
- Deployment — това е определен рецепта за създаване на копия на приложението, наричани pod’и;
- Услуга — вътрешен балансер на натоварването, който разпределя трафика между pod’ите;
- Ingress — описание на начина, по който трафикът ще преминава от външния свят към Service.
Ето кратко графично резюме:
1) В Kubernetes приложенията получават трафик от външния свят през два слоя балансери на натоварването: вътрешен и външен.

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

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

Да предположим, че искате да разширите просто приложение а-ла 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-ите. Трябва да помним три неща:
- Селектор (
selector) на Service-а трябва да съвпада с поне един етикет на Pod-а. -
targetPortтрябва да съвпада сcontainerPortв контейнера вътре в Pod-а. -
portна Service-а може да бъде произвольен. Различни услуги могат да използват един и същ порт, тъй като имат различни IP адреси.
Следната схема представя всичко изброено по-горе в графична форма:
1) Да предположим, че услугата насочва трафика към определен pod:

2) При създаване на pod-а е необходимо да зададете containerPort за всеки контейнер в pod-овете:

3) При създаване на услугата е необходимо да посочите port и targetPort. Но през кой от тях става свързването с контейнера?

4) Чрез targetPort. Той трябва да съвпада с containerPort.

5) Да предположим, че в контейнера е отворен порт 3000. Тогава стойността targetPort трябва да бъде същата.

В 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 трябва да съвпадат два параметъра:
-
servicePortв Ingress трябва да съвпада с параметъраportв Service; -
serviceNameв Ingress трябва да съвпада с полетоnameв Service.
Следната схема резюмира свързването на портове:
1) Както вече знаете, Service слуша на определен port:

2) Ingress разполага с параметър, наречен servicePort:

3) Този параметър (servicePort) винаги трябва да съвпада с port в дефиницията на Service:

4) Ако в Service е зададен порт 80, необходимо е servicePort също да е равен на 80:

На практика е необходимо да се обърне внимание на следните редове:
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. При посещение на , трябва да видите страницата, създадена от приложението.
Резюме по портовете
Нека отново припомним какви портове и етикети трябва да съвпадат:
- Селекторът в дефиницията на Service трябва да съвпада с етикета на pod-а;
-
targetPortв определението на Service трябва да съвпада сcontainerPortконтейнера в pod-а; -
portв определението на Service може да бъде всякакво. Различни услуги могат да използват един и същ порт, тъй като имат различни IP адреси; -
servicePortIngress-ът трябва да съвпада сportв определението на Service; - Името на услугата трябва да съвпада с полето
serviceNameв Ingress-а.
Невъзможно е просто да знаем как да структуриране YAML конфигурация.
Какво се случва, когато нещо не е наред?
Може би pod-ът не стартира или пада.
3 стъпки за диагностика на проблеми с приложения в Kubernetes
Преди да започнете отстраняването на проблеми с deployment-а, е необходимо да имате добро разбиране за това как работи Kubernetes.
Тъй като всяко изтеглено в K8s приложение съдържа три компонента, отстраняването на проблеми трябва да се извършва в определен ред, започвайки от най-долния.
- Първо трябва да се уверите, че pod-овете работят, след това…
- Да проверите дали услугата доставя трафик до pod-овете, а след това…
- Да проверите дали Ingress е правилно конфигуриран.
Визуално представяне:
1) Търсенето на проблеми трябва да започне от най-долния слой. Първо проверете, че pod-овете имат статуси Готово и В процес на изпълнение:

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

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

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-овете:
-
kubectl logsпозволява извличане на логове от контейнерите в pod-а; -
kubectl describe podпозволява преглед на списъка с события, свързани с pod-а; -
kubectl get podпозволява получаване на YAML конфигурацията на pod-а, съхранена в Kubernetes; -
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. Ето три от най-честите причини за това:
- Неправилно указаното име на изображението — например, имате грешка в него или изображението не съществува;
- Указан е несъществуващ етикет за изображението;
- Изображението се съхранява в частен регистър и Kubernetes няма разрешения за достъп до него.
Първите две причини са лесни за отстраняване — просто коригирайте името на изображението и етикета. В последния случай е необходимо да добавите данните за удостоверяване на частния регистър в Secret и да добавите линкове към него в pod-овете. В документацията на Kubernetes как може да стане това.
CrashLoopBackOff
Kubernetes изписва грешка CrashLoopBackOff, ако контейнерът не може да стартира. Обикновено това се случва, когато:
- В приложението има грешка, която не му позволява да стартира;
- Контейнерът ;
- Тестът за жизненост е завършил неуспешно твърде много пъти.
Необходимо е да опитате да достъпите логовете от контейнера, за да установите причината за провала му. Ако е трудно да се получи достъп до логовете, тъй като контейнерът се рестартира прекалено бързо, можете да използвате следната команда:
kubectl logs --previousТя извежда съобщения за грешки от предходната инкарнация на контейнера.
RunContainerError
Тази грешка възниква, когато контейнерът не може да стартира. Тя отговаря на момента преди стартирането на приложението. Обикновено причината е неправилна настройка, например:
- опит за монтиране на несъществуващ том, като ConfigMap или Secrets;
- опит за монтиране на том от тип само за четене като четим и записваем.
За анализ на подобни грешки добре подхожда командата kubectl describe pod.
Pod-овете са в състояние Pending
След създаването pod остава в състояние Pending.
Защо това се случва?
Ето възможните причини (при условие, че планировчикът работи нормално):
- В кластера има недостатъчно ресурси като изчислителна мощност и памет за стартиране на pod-a.
- В съответното пространство име е установен обект
ResourceQuotaи създаването на pod ще доведе до превишаване на квотата в пространството име. - Pod е в статус Pending
PersistentVolumeClaim.
В този случай се препоръчва да се използва командата kubectl describe и да се провери раздела Събития:
kubectl describe pod При грешки, свързани с ResourceQuotas, се препоръчва да се прегледат логовете на кластера с помощта на командата
kubectl get events --sort-by=.metadata.creationTimestampPod’овете не са в статус Ready
Ако pod е маркиран като В процес на изпълнение, но не е в статус Готово, значи проверката за готовност (readiness probe) завършва неуспешно.
Когато това се случи, pod не се свързва със сервица, и трафикът към него не достига. Провалът на теста за готовност е причинен от проблеми в приложението. В този случай за търсене на грешката е необходимо да се анализира раздела Събития в изхода на командата kubectl describe.
2. Диагностика на услуги
Ако pod’овете са маркирани като В процес на изпълнение и Готово, но от приложението все още няма отговор, е необходимо да се провери конфигурацията на услугата.
Услугите маршрутизират трафика към pod’овете в зависимост от техните етикети. Затова първото нещо, което трябва да се направи, е да се провери колко pod’а работят с услугата. За това можете да проверите endpoint’ите в услугата:
kubectl describe service | grep Endpoints Endpoint е двойка стойности от вида <IP-адрес:порт>, и в изхода трябва да присъства поне една такава двойка (т.е. с услугата работи поне един pod).
Ако разделът Endpoints е празен, възможни са два варианта:
- няма нито един pod с правилен етикет (подсказка: проверете дали е избрано правилното пространство име);
- има грешка в етикетите на услугата в селектора.
Ако виждате списък с 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, сме включили в статията няколко съвета за решаване на свързаните с него проблеми.
Отстраняване на неизправности на контролера Ingress Nginx
Проектът Ingress-nginx има официален . Командата 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-ове.
Изразявам благодарност , и за ценни коментари и допълнения.
P.S. от преводача
Прочетете също в нашия блог:
- «»;
- «»;
- «»;
- «».
Източник: habr.com
