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

TL;DR: ето схема, която ще ви помогне да отстраните проблеми с разгръщането в Kubernetes:
Блок-схема за намиране и поправка на грешки в клъстер. Оригиналът (на английски) е наличен в и .
При разгръщането на приложение в Kubernetes обикновено е необходимо да определите три компонента:
- Deployment — това е рецепта за създаване на копия на приложението, наречени pod’ове;
- Service — вътрешен балансировчик на натоварването, който разпределя трафика между 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-а. -
portService-ът може да бъде произволен. Различни услуги могат да използват един и същ порт, тъй като имат различни 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-а за проследяване на 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 трябва да съвпада с полетоимев 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‘овете имат статуси Готов и Running:

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

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

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‘овете:
-
kubectl logsпозволява да извлечете логовете от контейнерите в pod‘а; -
kubectl describe podпозволява да видите списък с събитията, свързани с pod‘а; -
kubectl get podпозволява да получите YAML конфигурацията на pod‘а, съхранявана в Kubernetes; -
kubectl exec -ti bashпозволява да стартирате интерактивна командна обвивка в един от контейнерите на pod‘а.
Коя от тях да изберете?
Истината е, че няма универсална команда. Трябва да се използва комбинация от тях.
Типични проблеми с pod‘овете
Има два основни типа грешки при pod‘овете: грешки при стартиране (startup) и грешки при работа (runtime).
Грешки при стартиране:
-
ImagePullBackoff -
ImageInspectError -
ErrImagePull -
ErrImageNeverPull -
RegistryUnavailable -
НевалидноИмеНаОбраза
Проблеми по време на изпълнение:
-
CrashLoopBackOff -
Грешка при стартиране на контейнер -
Грешка при спиране на контейнер -
Грешка при проверка на не-рут потребител -
Грешка при стартиране на инициализационен контейнер -
Грешка при създаване на Pod Sandbox -
Грешка при конфигуриране на Pod Sandbox -
Грешка при спиране на Pod Sandbox -
Грешка при настройка на мрежата -
Грешка при премахване на мрежата
Някои грешки се срещат по-често от други. Ето някои от най-разпространените грешки и начини за разрешаването им.
ImagePullBackOff
Тази грешка се появява, когато Kubernetes не може да изтегли образ за един от контейнерите на pod-а. Ето три от най-разпространените причини за това:
- Неправилно посочено име на образа — например, направили сте грешка в него или образът не съществува.
- Посочен е несъществуващ таг за образа.
- Образът се съхранява в частен регистър и Kubernetes няма разрешение да му получи достъп.
Първите две причини са лесни за отстраняване — просто поправете името на образа и тага. В случай на последната трябва да въведете данните за достъп до частния регистър в Secret и да добавите връзки към него в pod-овете. В документацията на Kubernetes как това може да се направи.
CrashLoopBackOff
Kubernetes изводи грешка CrashLoopBackOff, ако контейнерът не може да стартира. Обикновено се случва, когато:
- В приложението има грешка, която пречи на стартирането му;
- Контейнер ;
- Тестът за Liveness е провален твърде много пъти.
Необходимо е да се опитате да достъпите логовете от контейнера, за да установите причината за провала му. Ако достъпът до логовете е затруднен, тъй като контейнерът се реконструира твърде бързо, можете да използвате следната команда:
kubectl logs --previousТя извежда съобщения за грешки от предишната реинкарнация на контейнера.
Грешка при стартиране на контейнер
Тази грешка възниква, когато контейнерът не може да стартира. Тя съответства на момента преди стартиране на приложението. Обикновено причината за нея е неправилна настройка, например:
- опит за монтиране на несъществуващ том, като ConfigMap или Secrets;
- опит за монтиране на том от тип read-only като read-write.
За анализ на подобни грешки е полезна командата kubectl describe pod.
Pod-овете са в състояние Pending
След като pod-ът бъде създаден, той остава в състояние Pending.
Защо се случва това?
Ето възможни причини (изходя от предположението, че планировчикът работи нормално):
- В кластера липсват ресурси, като изчислителна мощност и памет, за да стартира pod-ът.
- В съответното пространство от имена е инсталиран обект
ResourceQuotaСъздаването на pod ще доведе до преминаване на пространството в името извън квотата. - Pod е свързан с Pending
PersistentVolumeClaim.
В този случай се препоръчва да се използва командата kubectl describe и да се провери секцията Събития:
kubectl describe pod В случай на грешки, свързани с ResourceQuotas, се препоръчва да се прегледат логовете на кластера с помощта на командата
kubectl get events --sort-by=.metadata.creationTimestampPod-овете не са в състояние Ready
Ако pod-ът е отбелязан като Running, но не е в състояние Готов, това означава, че проверката за готовност (readiness probe) не е успешна.
Когато това се случи, pod-ът не се свързва със службата и трафикът не достига до него. Неуспехът в теста за готовност е причинен от проблеми в приложението. В този случай е необходимо да се анализира секцията Събития в изхода на командата kubectl describe.
2. Диагностика на услугите
Ако pod-овете са отбелязани като Running и Готов, но все още няма отговор от приложението, трябва да проверите настройките на услугата.
Услугите се занимават с маршрутизация на трафика към pod-овете в зависимост от техните етикети. Затова първото нещо, което трябва да направите, е да проверите колко pod-а работят с услугата. За целта можете да проверите endpoint-ите в услугата:
kubectl describe service | grep Endpoints Endpoint е двойка стойности от вида <IP-адрес:порт>, и в изхода трябва да присъства поне една такава двойка (т.е. поне един pod работи с услугата).
Ако секцията Endpoints е празна, са възможни два варианта:
- няма нито един pod с правилния етикет (подсказка: проверете дали namespace е избран правилно);
- има грешка в етикетите на услугата в селектора.
Ако виждате списък с 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, включихме в статията няколко съвета за решение на свързани с него проблеми.
Отладка на контролера 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
