Настройка проверки готовности, работоспособности или запуска

Мне трудно понять, что лучше всего подходит для моей ситуации и как это реализовать.

В двух словах проблема такая:

  • Я развертываю свои DB (Postgres), BE (Django) и FE (React) с помощью Skaffold
  • Примерно в 50% случаев BE раскручивается до DB.
  • Одно из первых, что пытается сделать Django, - это подключиться к БД.
  • Он пытается только один раз (по замыслу и не может быть изменен), если не может, он терпит неудачу и приложение не работает

  • Таким образом, мне нужно убедиться, что каждый раз, когда я развертываю свои развертывания, развертывание БД выполняется перед запуском развертывания BE.

Я наткнулся на готовность, живучесть и запуск зонды. Я читал его пару раз, и тесты готовности звучат как то, что мне нужно: я не хочу, чтобы развертывание BE запускалось до тех пор, пока развертывание базы данных не будет готово принимать соединения.

Думаю, я не понимаю, как это настроить. Это то, что я пробовал, но до сих пор встречаю случаи, когда один загружается раньше другого.

postgres.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: postgres
  template:
    metadata:
      labels:
        component: postgres
    spec:
      containers:
        - name: postgres
          image: testappcontainers.azurecr.io/postgres
          ports:
            - containerPort: 5432
          env: 
            - name: POSTGRES_DB
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGDATABASE
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGUSER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGPASSWORD
            - name: POSTGRES_INITDB_ARGS
              value: "-A md5"
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql/data
              subPath: postgres
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-storage
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: postgres
  ports:
    - port: 1423
      targetPort: 5432

api.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: api
  template:
    metadata:
      labels:
        component: api
    spec:
      containers:
        - name: api
          image: testappcontainers.azurecr.io/testapp-api
          ports:
            - containerPort: 5000
          env:
            - name: PGUSER
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGUSER
            - name: PGHOST
              value: postgres-cluster-ip-service
            - name: PGPORT
              value: "1423"
            - name: PGDATABASE
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGDATABASE
            - name: PGPASSWORD
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: PGPASSWORD
            - name: SECRET_KEY
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: SECRET_KEY
            - name: DEBUG
              valueFrom:
                secretKeyRef:
                  name: testapp-secrets
                  key: DEBUG
          readinessProbe:
            httpGet:
              host: postgres-cluster-ip-service
              port: 1423
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: api-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: api
  ports:
    - port: 5000
      targetPort: 5000

client.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      component: client
  template:
    metadata:
      labels:
        component: client
    spec:
      containers:
        - name: client
          image: testappcontainers.azurecr.io/testapp-client
          ports:
            - containerPort: 3000
          readinessProbe:
            httpGet:
              path: api-cluster-ip-service
              port: 5000
            initialDelaySeconds: 10
            periodSeconds: 5
            timeoutSeconds: 2
---
apiVersion: v1
kind: Service
metadata:
  name: client-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: client
  ports:
    - port: 3000
      targetPort: 3000

Я не думаю, что ingress.yaml и skaffold.yaml будут полезными, но дайте мне знать, стоит ли мне их добавить.

Так что я здесь делаю не так?


Изменить:

Итак, я попробовал несколько вещей, основываясь на ответе Дэвида Мейза. Это помогло мне лучше понять, что происходит, но я все еще сталкиваюсь с проблемами, которые я не совсем понимаю, как решить.

Первая проблема заключается в том, что даже со значением по умолчанию restartPolicy: Always и даже несмотря на то, что Django дает сбой, сами модули не выходят из строя. Стручки думают, что они совершенно здоровы, даже несмотря на то, что Django потерпел неудачу.

Вторая проблема заключается в том, что, очевидно, модули должны быть осведомлены о статусе Django. Это та часть, над которой я не совсем погружаюсь, особенно должны ли зонды проверять статус других развертываний или самих себя?

Вчера я думал о первом, но сегодня я думаю о втором: Pod должен знать, что содержащаяся в нем программа потерпела неудачу. Однако все, что я пробовал, приводит только к неудачному зондированию, отказу в соединении и т. Д .:

# referring to itself
host: /health
port: 5000

host: /healthz
port: 5000

host: /api
port: 5000

host: /
port: 5000

host: /api-cluster-ip-service
port: 5000

host: /api-deployment
port: 5000

# referring to the DB deployment
host: /health
port: 1423 #or 5432

host: /healthz
port: 1423 #or 5432

host: /api
port: 1423 #or 5432

host: /
port: 1423 #or 5432

host: /postgres-cluster-ip-service
port: 1423 #or 5432

host: /postgres-deployment
port: 1423 #or 5432

Очевидно, я неправильно настраиваю зонд, несмотря на то, что это «сверхлегкая» реализация (как ее описали в нескольких блогах). Например, маршруты /health и /healthz: они встроены в Kubernetes или их нужно настраивать? Перечитываем документы, чтобы, надеюсь, прояснить это.


person cjones    schedule 22.01.2020    source источник
comment
Когда служба определяет, что не может связаться с базой данных, что она делает? Есть ли в вашем изображении что-то вроде tail -f /dev/null «чтобы контейнер оставался живым»? Пути HTTP, такие как /healthz, представляют собой маршруты, которые ваша служба должна предоставить сама, просто выполнения GET / может быть достаточно для начала.   -  person David Maze    schedule 23.01.2020


Ответы (2)


Вы просто не ждете достаточно долго.

Артефакты развертывания, которые вы здесь показываете, выглядят вполне нормально. Это даже совершенно нормально для вашего приложения, если оно не может достичь базы данных, скажем, потому что оно еще не запущено. Однако у каждого модуля есть политика перезапуска, который по умолчанию Always. Итак, когда модуль выходит из строя, Kubernetes перезапускает его; и когда он снова выйдет из строя, он будет снова запущен; и когда он продолжает выходить из строя, Kubernetes будет делать паузу на десятки секунд между перезапусками (ужасное состояние CrashLoopBackOff).

В конце концов, если вы находитесь в этом цикле ожидания и перезапуска, база данных действительно появится, а затем Kubernetes перезапустит ваши модули приложений, после чего приложение запустится в обычном режиме.

Единственное, что я бы здесь изменил, это то, что ваши зонды готовности для двух модулей должны проверять сами службы, а не какую-либо другую службу. Вероятно, вы хотите, чтобы path был чем-то вроде / или /healthz или чем-то еще, что является фактическим путем HTTP-запроса в службе. Это может вернуть 503 Service Unavailable, если обнаружит, что его зависимость недоступна, или вы можете просто аварийно завершить работу. Просто сбой - это нормально.

Это совершенно нормальная установка в Kubernetes; Невозможно более прямо сказать, что модуль A не может запуститься, пока служба B не будет готова. Обратной стороной этого является то, что шаблон на самом деле довольно общий: если ваше приложение вылетает и перезапускается всякий раз, когда оно не может достичь своей базы данных, не имеет значения, размещена ли база данных вне кластера, или если она выйдет из строя через некоторое время после этого. время запуска; та же логика попытается перезапустить ваше приложение, пока оно снова не заработает.

person David Maze    schedule 22.01.2020
comment
Полностью согласен, и я до сих пор не совсем понимаю, почему кто-то всегда заботится о развертывании базы данных, но не убирает развертывание базы данных из всего процесса, чтобы упростить дизайн. Но это другое обсуждение. - person BMW; 22.01.2020
comment
@BMW Итак, вместо автоматизации всего развертывания, поэтому разработчикам просто нужно запустить skaffold dev, чтобы начать работу, я должен попросить их настроить локальную базу данных разработчиков, убедиться, что она работает постоянно, а затем развернуть сервер и клиент сервисы с skaffold dev, чтобы они могли начать разработку? Идет вразрез с почти всеми людьми DevOps, с которыми я разговаривал, которые говорят, что вы хотите, чтобы ваши разработчики разрабатывали, а не настраивали и поддерживали базы данных разработчиков. - person cjones; 22.01.2020
comment
Привет, Дэвид, спасибо за ответ. Это помогло мне понять несколько вещей, но я все еще добавил несколько проблем в конец своего исходного вопроса. - person cjones; 22.01.2020

На самом деле, думаю, я мог бы разобраться в этом.

Отчасти проблема заключается в том, что, хотя по умолчанию используется restartPolicy: Always, модули не знают, что Django потерпел неудачу, поэтому они думают, что они исправны.

Мое мнение было неправильным, поскольку я изначально предполагал, что мне нужно обратиться к развертыванию БД, чтобы увидеть, началось ли оно перед запуском развертывания API. Вместо этого мне нужно было проверить, не сработал ли Django, и повторно развернуть его, если это так.

Для меня это было сделано следующим образом:

livenessProbe:
  tcpSocket:
    port: 5000
  initialDelaySeconds: 2
  periodSeconds: 2
readinessProbe:
  tcpSocket:
    port: 5000
  initialDelaySeconds: 2
  periodSeconds: 2

Я изучаю Kubernetes, поэтому поправьте меня, если есть лучший способ сделать это или это просто неправильно. Я просто знаю, что он выполняет то, что я хочу.

person cjones    schedule 22.01.2020