Kubenetes: можно ли поразить несколько подов одним запросом в кластере Kubernetes?

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

Несмотря на то, что для балансировщика нагрузки установлено значение RR, постоянное обращение к модулям (n количество раз, где n - общее количество модулей) не помогает, поскольку могут возникать другие запросы.

Здесь обсуждалась та же проблема, но я не смог найти решения для реализации: https://github.com/kubernetes/kubernetes/issues/18755

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

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


person Vineeth Chitteti    schedule 02.04.2018    source источник
comment
Это продолжение того, о чем вы уже думали. Когда модуль получает http-вызов, он транслируется в тему (на rabbitmq или аналогичном). Все модули слушают тему, получают событие и выполняют задачи в ответ на событие.   -  person Mohit Mutha    schedule 02.04.2018
comment
Спасибо @MohitMutha, но мы добавляем еще одну внешнюю зависимость, используя внешний сервер темы / очереди.   -  person Vineeth Chitteti    schedule 02.04.2018


Ответы (5)


Если у вас есть kubectl в своем модуле и есть доступ к api-серверу, вы можете получить все адреса конечных точек и передать их в curl:

kubectl get endpoints <servicename> \
        -o jsonpath="{.subsets[*].addresses[*].ip}" | xargs curl

Альтернатива без kubectl в пакете:

рекомендуемый способ доступа к серверу api из модуля - использование прокси-сервера kubectl: https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#accessing-the-api-from-a-pod Это, конечно, добавит по крайней мере такие же накладные расходы. в качестве альтернативы вы можете напрямую вызвать REST api, вам придется предоставить токен вручную.

APISERVER=$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")
TOKEN=$(kubectl describe secret $(kubectl get secrets \
     | grep ^default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d " ")

если вы предоставите переменные APISERVER и TOKEN, вам не понадобится kubectl в вашем модуле, таким образом, вам понадобится только curl для доступа к серверу api и "jq" для анализа вывода json:

curl $APISERVER/api/v1/namespaces/default/endpoints --silent \
     --header "Authorization: Bearer $TOKEN" --insecure \
     | jq -rM ".items[].subsets[].addresses[].ip" | xargs curl

ОБНОВЛЕНИЕ (окончательная версия)

APISERVER обычно может быть установлен на kubernetes.default.svc, а токен должен быть доступен по адресу /var/run/secrets/kubernetes.io/serviceaccount/token в модуле, поэтому нет необходимости предоставлять что-либо вручную:

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token); \
curl https://kubernetes.default.svc/api/v1/namespaces/default/endpoints --silent \
     --header "Authorization: Bearer $TOKEN" --insecure \
     | jq -rM ".items[].subsets[].addresses[].ip" | xargs curl

jq доступен здесь: https://stedolan.github.io/jq/download/ ( ‹4 MiB, но оно того стоит для легкого парсинга JSON)

person Markus Dresch    schedule 11.06.2018
comment
Разве добавление kubectl в мой модуль не добавило бы дополнительных накладных расходов? - person Vineeth Chitteti; 16.11.2018
comment
он добавил бы двоичный файл ~ 50 MiB и несколько KiB для конфигурации kube. - person Markus Dresch; 16.11.2018
comment
Я добавил альтернативу, которая не требует kubectl в модуле - person Markus Dresch; 16.11.2018

У меня была похожая ситуация. Вот как я решил это (я использую пространство имен, отличное от значения по умолчанию).

Настройка доступа к кластеру Использование авторизации RBAC

Доступ к API осуществляется путем создания ServiceAccount, назначения его Pod и привязки к нему роли.

1. Создайте ServiceAccount.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-serviceaccount
  namespace: my-namespace

2. Создайте роль: в этом разделе вам нужно предоставить список ресурсов и список действий, к которым вы хотите получить доступ. Вот пример, в котором вы хотите перечислить конечные точки, а также получить подробную информацию о конкретной конечной точке.

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: my-role
  namespace: my-namespace
rules:
- apiGroups: [""]
  resources: ["endpoints"]
  verbs: ["get", "list"]

3. Привяжите роль к сервисному аккаунту

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-role-binding
  namespace: my-namespace
subjects:
- kind: ServiceAccount
  name: my-serviceaccount
roleRef:
  kind: Role
  name: my-role
  apiGroup: rbac.authorization.k8s.io

4. Назначьте учетную запись службы модулям в вашем развертывании (она должна быть в template.spec).

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
  namespace: my-namespace
spec:
  replicas: 1
  selector:
    matchLabels:
          app: my-pod
  template:
    metadata:
      labels:
        app: my-pod
    spec:
      serviceAccountName: my-serviceaccount
      containers:
      - name: my-pod
        ...

Доступ Кластеры с использованием Kubernetes API

После установки всех аспектов безопасности у вас будет достаточно прав для доступа к API в вашем Pod. Вся необходимая информация для связи с сервером API монтируется в /var/run/secrets/kubernetes.io/serviceaccount вашего модуля. Вы можете использовать следующий сценарий оболочки (возможно, добавьте его в свою КОМАНДУ или ENTRYPOINT образа Docker).

#!/bin/bash
# Point to the internal API server hostname
API_SERVER=https://kubernetes.default.svc

# Path to ServiceAccount token
SERVICE_ACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# Read this Pod's namespace
NAMESPACE=$(cat ${SERVICE_ACCOUNT}/namespace)

# Read the ServiceAccount bearer token
TOKEN=$(cat ${SERVICE_ACCOUNT}/token)

# Reference the internal certificate authority (CA)
CA_CERT=${SERVICE_ACCOUNT}/ca.crt

С этого момента это просто вызов REST API. Вы можете прочитать эти переменные среды на любом языке по вашему выбору и получить доступ к API.

Вот пример перечисления конечной точки для вашего варианта использования

# List all the endpoints in the namespace that Pod is running
curl --cacert ${CA_CERT} --header "Authorization: Bearer ${TOKEN}" -X GET \
  "${API_SERVER}/api/v1/namespaces/${NAMESPACE}/endpoints"

# List all the endpoints in the namespace that Pod is running for a deployment
curl --cacert ${CA_CERT} --header "Authorization: Bearer ${TOKEN}" -X GET \
  "${API_SERVER}/api/v1/namespaces/${NAMESPACE}/endpoints/my-deployment"

Для получения дополнительной информации о доступных конечных точках API и способах их вызова см. Справочник по API.

person Iraj Hedayati    schedule 11.12.2020

Для тех из вас, кто пытается найти альтернативу, я использовал hazelcast в качестве распределенного слушателя событий. Добавлен аналогичный POC на github: https://github.com/vinrar/HazelcastAsEventListener

person Vineeth Chitteti    schedule 23.09.2018

Я решил эту проблему, используя этот скрипт. Вам просто нужно написать эквивалентную команду для вызова API. Я использовал curl для этого.

Ниже приводится использование сценария:

function usage {
    echo "usage: $PROGNAME [-n NAMESPACE] [-m MAX-PODS] -s SERVICE -- COMMAND"
    echo "  -s SERVICE   K8s service, i.e. a pod selector (required)"
    echo "     COMMAND   Command to execute on the pods"
    echo "  -n NAMESPACE K8s namespace (optional)"
    echo "  -m MAX-PODS  Max number of pods to run on (optional; default=all)"
    echo "  -q           Quiet mode"
    echo "  -d           Dry run (don't actually exec)"
}

Например, чтобы запустить команду curl http://google.com на всех модулях службы с именем s1 и пространством имен n1, вам необходимо выполнить ./kcdo -s s1 -n n1 -- curl http://google.com.

person Lokesh    schedule 30.10.2018
comment
Некоторое объяснение сценария, а также фрагменты кода желательны. Внешние ссылки исчезают, и сброс сценария на людей без каких-либо объяснений бесполезен. - person Abhijit Sarkar; 21.12.2018
comment
Я очень спешил, поэтому не стал вдаваться в подробности. Во всяком случае, я обновил ответ. Дай мне знать, если все в порядке. - person Lokesh; 21.12.2018
comment
Хороший сценарий, спасибо! Как с помощью этого скрипта выполнить завиток IP-адресов всех модулей? Например, я хочу выполнить / status / live на всех модулях, используя этот скрипт, а не curl google.com - person Ramandeep Singh; 09.09.2020
comment
Получил решение: ./kcdo-s s1 -n n1 - curl localhost: 8080 / status / live. Итак, через скрипт можно использовать localhost, и он работает. - person Ramandeep Singh; 09.09.2020

Мне нужен был доступ ко всем модулям, чтобы я мог изменить уровень журнала в классе, поэтому я сделал это изнутри одного из модулей:

// Change level to DEBUG
host <service-name>| awk '{print $4}' | while read line; do
curl --location --request POST "http://$line:9111/actuator/loggers/com.foo.MyClassName" \
--header 'Content-Type: application/json' \
--data-raw '{"configuredLevel": "DEBUG"}' 
done
// Query level on all pods
host <service-name>| awk '{print $4}' | while read line; do    
curl --location --request GET "http://$line:9111/actuator/loggers/com.foo.MyClassName"
echo
done

Вам нужны host и curl для его выполнения.

Не уверен, что это хорошая практика.

person Chobicus    schedule 15.06.2021