Node-Selectors: < none>
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
Normal Scheduled 3m44s default-scheduler Successfully assigned default/liveness to node01
Normal Pulled 68s (x3 over 3m35s) kubelet, node01 Container image "alpine:3.5" already present on machine
Normal Created 68s (x3 over 3m35s) kubelet, node01 Created container healtcheck
Normal Started 68s (x3 over 3m34s) kubelet, node01 Started container healtcheck
Warning Unhealthy 23s (x9 over 3m3s) kubelet, node01 Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
Normal Killing 23s (x3 over 2m53s) kubelet, node01 Container healtcheck failed liveness probe, will be restarted
Также мы видим и на событиях кластера, что когда cat /tmp/health терпит неудачу контейнере пересоздаётся:
controlplane $ kubectl get events
controlplane $ kubectl get events | grep pod/liveness
13m Normal Scheduled pod/liveness Successfully assigned default/liveness to node01
13m Normal Pulling pod/liveness Pulling image "alpine:3.5"
13m Normal Pulled pod/liveness Successfully pulled image "alpine:3.5"
10m Normal Created pod/liveness Created container healtcheck
10m Normal Started pod/liveness Started container healtcheck
10m Warning Unhealthy pod/liveness Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
10m Normal Killing pod/liveness Container healtcheck failed liveness probe, will be restarted
10m Normal Pulled pod/liveness Container image "alpine:3.5" already present on machine
8m32s Normal Scheduled pod/liveness Successfully assigned default/liveness to node01
4m41s Normal Pulled pod/liveness Container image "alpine:3.5" already present on machine
4m41s Normal Created pod/liveness Created container healtcheck
4m41s Normal Started pod/liveness Started container healtcheck
2m51s Warning Unhealthy pod/liveness Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory
5m11s Normal Killing pod/liveness Container healtcheck failed liveness probe, will be restarted
Рассмотрим RadyNess пробу. Доступность этой пробы свидетельствует, что приложение готово к принятию запросов и можно на него сервис может переключать трафик:
controlplane $ cat << EOF > readiness.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: readiness
spec:
replicas: 2
selector:
matchLabels:
app: readiness
template:
metadata:
labels:
app: readiness
spec:
containers:
name: readiness
image: python
args:
/bin/sh
-c
sleep 15 && (hostname > health) && python -m http.server 9000
readinessProbe:
exec:
command:
cat
/tmp/healthy
initialDelaySeconds: 1
periodSeconds: 5
EOF
controlplane $ kubectl create -f readiness.yaml
deployment.apps/readiness created
controlplane $ kubectl get pods
NAME READY STATUS RESTARTS AGE
readiness-fd8d996dd-cfsdb 0/1 ContainerCreating 0 7s
readiness-fd8d996dd-sj8pl 0/1 ContainerCreating 0 7s
controlplane $ kubectl get pods
NAME READY STATUS RESTARTS AGE
readiness-fd8d996dd-cfsdb 0/1 Running 0 6m29s
readiness-fd8d996dd-sj8pl 0/1 Running 0 6m29s
controlplane $ kubectl exec -it readiness-fd8d996dd-cfsdb curl localhost:9000/health
readiness-fd8d996dd-cfsdb
Наши контейнера отлично работают. Добавим в них трафик:
controlplane $ kubectl expose deploy readiness \
-type=LoadBalancer \
-name=readiness \
-port=9000 \
-target-port=9000
service/readiness exposed
controlplane $ kubectl get svc readiness
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
readiness LoadBalancer 10.98.36.51 < pending> 9000:32355/TCP 98s
controlplane $ curl localhost:9000
controlplane $ for i in {1..5}; do curl $IP:9000/health; done
1
2
3
4
5
Каждый контейнер имеет задержку. Проверим, что будет, если один из контейнеров перезапустить будет ли на него перенаправляться трафик:
controlplane $ kubectl get pods
NAME READY STATUS RESTARTS AGE
readiness-5dd64c6c79-9vq62 0/1 CrashLoopBackOff 6 15m
readiness-5dd64c6c79-sblvl 0/1 CrashLoopBackOff 6 15m
kubectl exec -it .... -c .... bash -c "rm -f healt"
controlplane $ for i in {1..5}; do echo $i; done
1
2
3
4
5
controlplane $ kubectl delete deploy readiness
deployment.apps "readiness" deleted
Рассмотрим ситуацию, когда контейнер становится временно недоступен для работы:
(hostname > health) && (python -m http.server 9000 &) && sleep 60 && rm health && sleep 60 && (hostname > health) sleep 6000
/bin/sh -c sleep 60 && (python -m http.server 9000 &) && PID=$! && sleep 60 && kill -9 $PID
По умолчанию, в состояние Running контейнер переходит по завершения выполнения скриптов в Dockerfile и запуску скрипта, заданного в инструкции CMD, если он переопределён в конфигурации в разделе Command. Но, на практике, если у нас база данных, ей нужно ещё подняться (прочитать данные и перенести их оперативную память и другие действия), а это может занять значительно время, при этом она не будет отвечать на соединения, и другие приложения, хотя и прочитают в состоянии готовность принимать соединения не смогут этого сделать. Также, контейнер переходи в состояние Feils, когда падает главный процесс в контейнере. В случае с базой данных, она может бесконечно пытаться выполнить неправильный запрос и не сможет отвечать на приходящие запросы, при этом контейнер не буде перезапущен, так как формально демон (сервер) базы данных не упал. Для этих случаев и придуманы два идентификатора: readinessProbe и livenessProbe, проверяющих по кастомному скрипту или HTTP запросу переход контейнера в рабочее состояние или его неисправность.
esschtolts@cloudshell:~/bitrix (essch)$ cat health_check.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: healtcheck
name: healtcheck
name: healtcheck
spec:
containers:
name: healtcheck
image: alpine:3.5
args:
/bin/sh
-c
sleep 12; touch /tmp/healthy; sleep 10; rm -rf /tmp/healthy; sleep 60
readinessProbe:
exec:
command:
cat
/tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
exec:
command:
cat
/tmp/healthy
initialDelaySeconds: 15
periodSeconds: 5
Контейнер стартует через 3 секунды и через 5 секунд начинается проверка на готовность каждые 5 секунд. На второй проверке (на 15 секунде жизни) проверка на готовность cat /tmp/healthy увенчается успехом. В это время начинает осуществляться проверка на работоспособность livenessProbe и на второй проверке (на 25 секунде) заканчивается ошибкой, после чего контейнер признаётся не рабочим и пересоздается.
esschtolts@cloudshell:~/bitrix (essch)$ kubectl create -f health_check.yaml && sleep 4 && kubectl get
pods && sleep 10 && kubectl get pods && sleep 10 && kubectl get pods
pod "liveness-exec" created
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 Running 0 5s
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 Running 0 15s
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 0 26s
esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 Running 0 53s
esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-exec 0/1 Running 0 1m
esschtolts@cloudshell:~/bitrix (essch)$ kubectl get pods
NAME READY STATUS RESTARTS AGE
liveness-exec 1/1 Running 1 1m
Kubernetes предоставляет ещё и startup, переделяющий момент, когда можно включить readiness и liveness пробы в работу. Это полезно в том случае, если, к примеру, мы скачиваем приложение. Рассмотрим более подробно. Для эксперимента возьмём www.katacoda.com/courses/Kubernetes/playground и Python. Существует TCP, EXEC и HTTP, но лучше использовать HTTP, так как EXEC порождает процессы и может оставлять их в виде "зомби процессов". К тому же, если сервер обеспечивает взаимодействие по HTTP, то именно по нему и нужно проверять (https://www.katacoda.com/courses/kubernetes/playground):
controlplane $ kubectl version short
Client Version: v1.18.0
Server Version: v1.18.0
cat << EOF > job.yaml
apiVersion: v1
kind: Pod
metadata:
name: healt
spec:
containers:
name: python
image: python
command: ['sh', '-c', 'sleep 60 && (echo "work" > health) && sleep 60 && python -m http.server 9000']
readinessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 3
periodSeconds: 3
livenessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 3
periodSeconds: 3
startupProbe:
exec:
command:
cat
/health
initialDelaySeconds: 3
periodSeconds: 3
restartPolicy: OnFailure
EOF
controlplane $ kubectl create -f job.yaml
pod/healt
controlplane $ kubectl get pods # ещё не загружен
NAME READY STATUS RESTARTS AGE
healt 0/1 Running 0 11s
controlplane $ sleep 30 && kubectl get pods # ещё не загружен, но образ уже стянут
NAME READY STATUS RESTARTS AGE
healt 0/1 Running 0 51s
controlplane $ sleep 60 && kubectl get pods
NAME READY STATUS RESTARTS AGE
healt 0/1 Running 1 116s
controlplane $ kubectl delete -f job.yaml
pod "healt" deleted
Самодиагностика микро сервисного приложения
Рассмотрим работу probe на примере микро сервисного приложения bookinfo, входящего в состав Istio как пример: https://github.com/istio/istio/tree/master/samples/bookinfo. Демонстрация будет в www.katacoda.com/courses/istio/deploy-istio-on-kubernetes. После разворачивания будет доступны
Управление инфраструктурой
Хотя, и у Kubernetes есть свой графический интерфейс UI-дашборд, но кроме мониторинга и простейших действий не предоставляет. Больше возможностей даёт OpenShift, предоставляя совмещения графического и текстового создания. Полноценный продукт с сформированной экосистемой Google в Kubernetes не предоставляет, но предоставляет облачное решение Google Cloud Platform. Однако, существуют и сторонние решения, такие как Open Shift и Rancher, позволяющие пользоваться полноценно через графический интерфейс на своих мощностях. При желании, конечно, можно синхронизироваться с облаком.
Каждый продукт, зачастую, не совместим с друг другом по API, единственным известным исключением является Mail. Cloud, в котором заявляется поддержка Open Shift. Но, существует стороннее решение, реализующее подход "инфраструктура как код" и поддерживающее API большинства известных экосистем Terraform. Он, так же как Kubernetes, применяет концепция инфраструктура как код, но только не к контейнеризации, а к виртуальным машинам (серверам, сетям, дискам). Принцип Инфраструктура как код подразумевает наличии декларативной конфигурации то есть описания результата без явного указания самих действий. При активации конфигурация (в Kubernetes это kubectl apply -f name_config .yml, а в Hashicorp Terraform terraform apply) системы приводится в соответствие с конфигурационными файлами, при изменении конфигурации или инфраструктуры, инфраструктура, в конфликтующих частях с её декларацией приводится в соответствие, при этом сама система решает, как этого достичь, причём поведение может быть различным, например, при изменении метаинформации в POD она будет изменена, а при изменении образа POD будет удалён и создан уже новым. Если, до этого мы создавали серверную инфраструктуру для контейнеров в императивном виде с помощью команды gcloud публичного облака Google Cloud Platform (GCP), то теперь рассмотрим, как создать аналогичное с помощью конфигурация в декларативном описании паттерна инфраструктура как код с помощью универсального инструмента Terraform, поддерживающего облако GCP.