Часть 12 из серии статей об изучении k8s!

Указатель всей серии

  1. Основы k8s
  2. Создать кластер k8s одной командой
  3. Первое размещение контейнера в k8s
  4. Зачем нам Pod
  5. Важные поля конфигурации модуля
  6. Под проектные объемы
  7. Проверка работоспособности и восстановление pod-контейнера
  8. Модель контроллера k8s
  9. Развертывание k8s
  10. Топология StatefulSet k8s
  11. K8s StatefulSet Storage

В моей последней статье я говорил о StatefulSet состоянии хранилища. Нетрудно обнаружить, что StatefulSet на самом деле является контейнерной абстракцией существующих типичных операций и обслуживания приложений. StatefulSet, вероятно, самый сложный объект оркестровки в k8s, и для его освоения потребуется много практики, так что продолжайте практиковаться! Сегодня я представлю DaemonSet, относительно более простую тему.

DaemonSet

В k8s DaemonSet гарантирует, что все (или некоторые) узлы запускают копию Pod. По мере добавления узлов в кластер к ним добавляется Pods. Когда узлы удаляются из кластера, эти Pods собираются сборщиком мусора. Удаление DaemonSet приведет к очистке созданных им модулей. Этот Pod называется Daemon Pod.

Примеры использования DaemonSet

Daemon Pod звучит просто, но это действительно очень важно. Позвольте мне представить вам несколько примеров использования:

  1. Агент сетевых плагинов должен работать на каждом узле, чтобы обрабатывать сеть контейнеров на этом узле.
  2. Агент подключаемых модулей хранилища также должен запускаться на каждом узле для монтирования удаленных каталогов хранилища на этом узле и управления каталогом тома контейнера.
  3. Агент компонентов мониторинга и компонентов журнала также должен работать на каждом узле и отвечать за сбор информации мониторинга и журналов на этом узле.

Что еще более важно, в отличие от других объектов оркестрации, DaemonSet запускается часто, запускается весь кластер k8s.

Объект API DaemonSet

Давайте начнем понимать это с определения файла YAML:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-elasticsearch
  namespace: kube-system
  labels:
    k8s-app: fluentd-logging
spec:
  selector:
    matchLabels:
      name: fluentd-elasticsearch
  template:
    metadata:
      labels:
        name: fluentd-elasticsearch
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd-elasticsearch
        image: k8s.gcr.io/fluentd-elasticsearch:1.30
        resources:
          limits:
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers

Этот DaemonSet управляет Pod зеркальным отображением fluentd-elasticsearch. Функция этого образа очень проста: журналы в контейнере Docker перенаправляются в ElasticSearch через fluentd.

Как видите, DaemonSet на самом деле очень похож на Deployment, за исключением того, что здесь нет поля реплик; он также использует селектор для выбора и управления всеми модулями, имеющими метку name=fluentd-elasticsearch.

Шаблоны этих Pods также определяются с помощью поля шаблона. В этом поле мы определяем контейнер, который использует образ fluentd-elasticsearch:1.30, и этот контейнер монтирует два тома типа hostPath, соответствующие каталогу /var/log и каталогу /var/lib/docker/containers хоста.

После запуска fluentd он соберет информацию журнала из этих двух каталогов и отправит ее в ElasticSearch для хранения. Таким образом, мы можем легко получить эти журналы через ElasticSearch.

Контроллер DaemonSet

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

DaemonSet Controller сначала получает все списки узлов из Etcd, а затем просматривает все узлы. В это время он может легко проверить, есть ли Pod, несущий тег name=fluentd-elasticsearch, работающий в данный момент на этом узле.

Есть три возможности:

  • Нет такого типа Pod, это означает, что такой Pod должен быть создан на этом узле.
  • Если такой модуль есть, но его число больше 1, это означает, что избыточный модуль должен быть удален с этого узла.
  • Есть ровно один такой Pod, значит, этот узел в норме.

Удалить Pod легко, просто вызовите API k8s напрямую, но для создания нового Pod на данном узле DaemonSet Controller автоматически добавит определение nodeAffinity к объекту API Pod при создании Pod. Среди них имя узла, который необходимо связать, - это узел, который в настоящее время просматривается.

Конечно, DaemonSet не нужно изменять шаблон Pod в файле YAML, представленном пользователем, а напрямую изменять объект Pod, сгенерированный в соответствии с шаблоном, перед отправкой запроса к k8s. Вдобавок DaemonSet автоматически добавит в это Pod другое поле, связанное с расписанием, которое называется tolerations. Это поле означает, что этот модуль будет «терпеть» некоторую «порчу» узла.

Образец файла tolerations:

apiVersion: v1
kind: Pod
metadata:
  name: with-toleration
spec:
  tolerations:
  - key: node.kubernetes.io/unschedulable
    operator: Exists
    effect: NoSchedule

Значение этого допуска: «терпеть» все узлы, помеченные как не подлежащие планированию «заражение»; эффект «толерантности» состоит в том, чтобы разрешить планирование.

При нормальных обстоятельствах для узла, помеченного неуправляемым «пятном», не будет никаких Pod запланированных (эффект: NoSchedule). Однако DaemonSet автоматически добавляет это специальное значение допуска к управляемому Pods, чтобы эти Pods могли игнорировать это ограничение, а затем гарантировать, что Pod будет запланировано на каждом узле. Конечно, если узел выходит из строя, Pod может не запуститься, и DaemonSet всегда будет пытаться, пока Pod не запустится успешно.

В настоящее время вы должны догадаться, что «трюк» с DaemonSet на самом деле достигается за счет использования терпимости.

В k8s, когда сетевой плагин узла не был установлен, узел автоматически будет «испорчен» с именем node.kubernetes.io/network-unavailable.

Если текущий DaemonSet manages - это сетевой подключаемый модуль Agent Pod, тогда вы должны добавить шаблон Pod, который может «терпеть» node.kubernetes.io/network-unavailable «taint» в YAML-файле этого DaemonSet:

...
template:
    metadata:
      labels:
        name: network-plugin-agent
    spec:
      tolerations:
      - key: node.kubernetes.io/network-unavailable
        operator: Exists
        effect: NoSchedule

С таким допуском планировщик игнорирует «пятно» на текущем узле при планировании Pod и успешно планирует запуск компонента агента сетевого подключаемого модуля для этой машины.

Этот механизм является фундаментальной причиной, по которой мы можем сначала развернуть сам k8s, а затем развернуть сетевой плагин.

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

Пример DeamonSet

Прежде всего, давайте создадим объект DaemonSet:

$ kubectl create -f fluentd-es.yaml

После создания вы можете увидеть, что запущены два модуля.

$ kubectl get pod -n kube-system -l name=fluentd-elasticsearch
NAME                          READY     STATUS    RESTARTS   AGE
fluentd-elasticsearch-xxxxx   1/1       Running   0          53m
fluentd-elasticsearch-xxxxx   1/1       Running   0          53m
$ kubectl get ds -n kube-system fluentd-elasticsearch
NAME                    DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
fluentd-elasticsearch   2         2         2         2            2           <none>          1h

Вы можете видеть, что, как и Deployment, DaemonSet также имеет несколько полей статуса, таких как DESIRED и CURRENT. Это также означает, что DaemonSet может выполнять управление версиями, как Deployment. В этой версии вы можете использовать kubectl rollout history для проверки:

$ kubectl rollout history daemonset fluentd-elasticsearch -n kube-system
daemonsets "fluentd-elasticsearch"
REVISION  CHANGE-CAUSE
1         <none>

Теперь давайте обновим нашу DaemonSet версию образа:

$ kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=k8s.gcr.io/fluentd-elasticsearch:v2.2.0 --record -n=kube-system

Давайте проверим статус непрерывного обновления:

$ kubectl rollout status ds/fluentd-elasticsearch -n kube-system
Waiting for daemon set "fluentd-elasticsearch" rollout to finish: 0 out of 2 new pods have been updated...
Waiting for daemon set "fluentd-elasticsearch" rollout to finish: 0 out of 2 new pods have been updated...
Waiting for daemon set "fluentd-elasticsearch" rollout to finish: 1 of 2 updated pods are available...
daemon set "fluentd-elasticsearch" successfully rolled out

После этого давайте проверим историю:

$ kubectl rollout history daemonset fluentd-elasticsearch -n kube-system
daemonsets "fluentd-elasticsearch"
REVISION  CHANGE-CAUSE
1         <none>
2         kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=k8s.gcr.io/fluentd-elasticsearch:v2.2.0 --namespace=kube-system --record=true

Можно попробовать откатить на Revision=1

$ kubectl rollout undo daemonset fluentd-elasticsearch --to-revision=1 -n kube-system
daemonset.extensions/fluentd-elasticsearch rolled back

Конечно, вы можете удалить DaemonSet:

$ kubectl delete daemonset fluentd-elasticsearch

Заключение

В этой статье я сосредоточился на третьем важном объекте оркестровки k8s: DaemonSet. По сравнению с Deployment, DaemonSet управляет только Pod объектами, а затем с помощью небольших функций двух планировщиков, nodeAffinity и Toleration, он обеспечивает наличие только одного модуля на каждом узле.

Надеюсь, вам понравилась эта статья, и увидимся в следующей!

Больше контента на plainenglish.io