Skip to content

Latest commit

 

History

History
653 lines (611 loc) · 61.5 KB

lesson 5.md

File metadata and controls

653 lines (611 loc) · 61.5 KB
date created
2021-10-15 16:02:06 (+03:00), Friday

Урок 5: Хранение данных. Вечерняя школа «Kubernetes для разработчиков» youtube

Техническая пауза, опрос 00:00:20

Вступление 00:04:15

  • Сергей приводит в пример новичков, которые развернув "Hello World!" приложение в kubernetes радуются его неубиваемости и пробуют добавить в деплоймент образ какого нибудь MySQL, получают 3 отдельных базы а не какой-то кластер и спрашивают, почему так происходит.
  • Происходит это потому, что kubernetes не знает ничего о базах данных и не умеет их "готовить"
  • Зато kubernetes умеет предоставлять приложению место, где оно сможет хранить свои данные

Хранение данных 00:06:48

  • В kubernetes работает принцип good state - dead state (stateless)
  • Kubernetes изначально разрабатывался и предназначался для запуска микросервисов
    • Микросервисы, как правило, маленькие, быстрые, потребляющие мало ресурсов, их можно запускать в нужное количество реплик и вся нагрузка будет более или менее равномерно распределяться по всем репликам, отказ небольшой части реплик практически не скажется на работе сервиса, упавшие реплики будут восстановлены
  • С приложениями, которые хранят состояние (state), вышеописанный подход не работает в полной мере, т.к. мы будем получать уникальные реплики
    • Например, приложение обслуживает бэкэнд личного кабинета и хранит состояние залогинившихся пользователей, если какая-то реплика умрёт, то пользователи, которых она обслуживала, вылетят из ЛК и им будет необходимо снова залогиниться
  • Таким образом, хранить состояние внутри реплики - это плохая идея, есть более подходящие варианты:
    • Данные стоит хранить в базе данных
    • Данные, к которым нужен оперативный доступ стоит хранить в соответствующих решениях, например memcached, redis, tarantool и т.п.
    • Для файлов стоит использовать, например, S3-совместимое хранилище (1, 2)
      • Хороший S3 может работать как CDN, например, вы загрузили в S3 файлик, отдали пользователю ссылку и пользователь сможет получить файл по этой ссылке прямо из S3 мимо основного бэкэнда
  • Если всё таки очень хочется хранить состояние в репликах, то есть несколько способов, о них ниже

HostPath 00:09:29

screenshot1

  • Начнём разбирать с тома (volume), под названием HostPath
    • Когда на прошлых занятиях мы работали с ConfigMap, Secrets, мы их монтировали как файлы внутрь контейнера, мы столкнулись с двумя понятиями:
      • volumeMounts - точки монтирования внутри контейнера
      • volumes - тома, которые мы монтируем в эти точки
    • С томами, на которых можно хранить данные, всё работает так же:
      • у нас будет точка монтирования, volumeMount, где мы укажем, куда монтировать наш том
      • в разделе volumes мы будем указывать тип тома, в нашем случае это hostPath
  • HostPath - аналог механизма из docker compose - мы берем каталог, который находится на ноде и монтируем этот каталог внутрь контейнера, таким образом приложение внутри контейнера получает доступ к каталогу, который находится на ноде
    • Вопрос в зрительский зал - насколько это хорошо с точки зрения безопасности?
      • Общее настроение - вариант плохой
    • Почему это плохой вариант?
      • Потому что такой механизм позволяет получить доступ к каталогам на уровне ноды, в некоторых сценариях этим могут воспользоваться злоумышленники, получившие доступ к контейнеру
        • В связи с этим в продакшн кластерах часто запрещают к использованию данный тип томов с помощью политик безопасности (pod security policy) или с помощью внешних валидаторов манифестов типа gatekeeper, kubeval

Посмотрим, как это выглядит в yaml манифестах 00:12:25

  • работаем в каталоге ~/school-dev-k8s/practice/5.saving-data/1.hostpath
  • смотрим содержимое манифеста деплоймента
# deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: quay.io/testing-farm/nginx:1.12
        name: nginx
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 10m
            memory: 100Mi
          limits:
            cpu: 100m
            memory: 100Mi
        volumeMounts:         # раздел для указание точек монтирования
        - name: data          # имя тома
          mountPath: /files   # путь к точке монтирования (внутри пода)
      volumes:              # перечисление томов, которые будут смонитированы
      - name: data          # имя тома
        hostPath:           # тип тома
          path: /data_pod   # путь к каталогу (внутри узла)
...
  • применяем деплоймент
$ kubectl apply -f deployment.yaml

deployment.apps/my-deployment created

$ kubectl get pod

No resources found in s024713 namespace.

$ kubectl get all

NAME                            READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-deployment   0/1     0            0           64s

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/my-deployment-f9c7845d9   1         0         0       64s
  • видим, что что-то пошло не так - деплоймент создан, поды не появились, репликасет не перешёл в статус READY
  • разбираемся
$ kubectl describe deployments.apps my-deployment

# часть вывода скрыта
# видим, что есть проблемы, но что именно не так, не понятно
Conditions:
  Type             Status  Reason
  ----             ------  ------
  Progressing      True    NewReplicaSetCreated
  Available        True    MinimumReplicasAvailable
  ReplicaFailure   True    FailedCreate
OldReplicaSets:    <none>
NewReplicaSet:     my-deployment-f9c7845d9 (0/1 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  4m10s  deployment-controller  Scaled up replica set my-deployment-f9c7845d9 to 1

$kubectl describe replicasets.apps my-deployment-f9c7845d9

# часть вывода скрыта
# видим сообщение от replicaset-controller о том что hostPath запрещён к использованию посредством PodSecurityPolicy
Conditions:
  Type             Status  Reason
  ----             ------  ------
  ReplicaFailure   True    FailedCreate
Events:
  Type     Reason        Age                     From                   Message
  ----     ------        ----                    ----                   -------
  Warning  FailedCreate  3m43s (x17 over 9m14s)  replicaset-controller  Error creating: pods "my-deployment-f9c7845d9-" is forbidden: PodSecurityPolicy: unable to admit pod: [spec.volumes[0]: Invalid value: "hostPath": hostPath volumes are not allowed to be used]

Q&A

  • 01:16:23
    • Q: На какой ноде будет лежать каталог HostPath
    • A: Будет происходить попытка смонтировать каталог с той ноды, где запущен под
  • 01:16:42
    • Q: Где управлять PodSecurityPolicy?
    • A: Рассмотрим позже, это отдельная трехчасовая лекция, эта тема есть в курсе "Мега", но здесь тоже будет обзорная лекция
  • 01:17:03
    • Q: По умолчанию все политики открыты?
    • A: Да, по умолчанию никаких psp не включено в kubernetes, их нужно специально включать для того чтобы можно было применять какие либо ограничения
  • 00:17:12
    • Q: Кажется, это ответ на эту ссылку (PodSecurityPolicy Deprecation: Past, Present, and Future), но она была воспринята как информация о прекращении поддержки hostPath
    • A: Такой тип в deprecated перейти не может, потому что он нужен для системных компонентов kubernetes, чтобы они запускались. Там решается проблема курицы и яйца, Мюнгхаузена, который вытаскивает себя из болота и т.д., грубо говоря, ему (кому?) нужно запуститься, пока еще не все компоненты запущены, поэтому приходится использовать hostPath для таких решений

screenshot1

  • Еще один вариант тома, т.н. Ephemeral
  • В перводе с английского - пустой каталог
  • Создаёт временный диск и монтирует его внутрь контейнера
  • Т.е., это не заранее обозначенный каталог на ноде, а специальный каталог, создаваемый посредством container runtime interface, который будет использоваться в нашем контейнере всё время, пока живёт под
  • После того, как под закончит свою работу (его выключат, обновят и т.п.), emptyDir будет удалён вместе с подом
  • Таким образом, данные хранимые в emptyDir живут столько же, сколько и под, удалён под - удалены данные
  • Если контейнер внутри пода упадёт, сохранность данных это не затронет
  • Можно провести аналогию emptyDir со стандартным docker volume, при условии, что в манифесте docker compose мы не указываем, какой каталог монтировать, но указываем имя, будет создан volume, с тем отличием, что emptyDir будет удален при завершении работы пода

Зачем нужен EmptyDir? 00:19:23

  • emptyDir применяется для работы с данными, которые не имеет смысл хранить постоянно, например:
    • для временных баз данных, например для автотестов
    • при тестировании приложений и сервисов, требующих работы с диском
Пробуем применить EmptyDir в учебном кластере 00:20:50
  • Переходим в каталог ~/school-dev-k8s/practice/5.saving-data/2.emptydir, видим манифест:
# deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - image: quay.io/testing-farm/nginx:1.12
        name: nginx
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 10m
            memory: 100Mi
          limits:
            cpu: 100m
            memory: 100Mi
        volumeMounts:       
        - name: data
          mountPath: /files
      volumes:          # перечисление томов, которые будут смонитированы
      - name: data      # имя тома
        emptyDir: {}    # тип тома и пустой словарь в качестве значения, чтобы манифест прошел валидацию
...
  • Видим знакомую картину, в разделе volumes подключаем том типа emptyDir
  • Фигурные скобки в качестве значения переданы для того чтобы манифест прошел валидацию (непонятно только чью, попробовал убрать их, ошибок не встретил, подставил вместо них ~ - аналогично)
  • Далее Сергей применяет манифест и демонстрирует, что мы можем писать в каталог /files, а также что после перезахода в контейнер, он остаётся на месте

Q&A

  • 00:24:43
    • Q: Ограничения на размер emptyDir?
    • A: Такие же как на том, где фактически расположен emptyDir, обычно это каталог /var/lib/kubelet/pods/_POD_ID_/volumes/kubernetes.io~empty-dir или оперативная память
  • 00:25:18
    • Q: Можно ли создать emptyDir для нескольких подов?
    • A: Можно, но для каждого пода это будет отдельный emptyDir
  • 00:25:30
    • Q: А если apply - тоже пропадёт emptyDir?
    • A: Если произошли изменения, которые привели к созданию новых подов, то, как следствие, emptyDir пропадёт
  • 00:25:57
    • Q: Какой смысл использовать emptyDir, если можно положить данные в любую папку в контейнере и они так же не сохранятся при удалении пода
    • A:
      • В связи со "слоёной" системой устройства хранилища в докер контейнере мы имеем большой оверхед по производительности, поэтому для работы используют механизм монтирования томов
      • Коллега подсказывают, что для обмена данными между разными контейнерами внутри одного пода тоже могут применяться emptyDir

PV/PVC/Storage class 00:27:32

PVC, persistentVolumeClaim 00:29:20

  • Это наша заявка на то, какой диск нам нужен 00:33:15
# пример описания подключения такого тома к поду
volumes:                    # раздел объявления томов
  - name: mypd              # задаём имя
    persistentVolumeClaim:  # указываем тип тома
      claimName: myclaim    # название клэйма
  • В kubernetes для хранения данных используются внешние системы хранения данных, такие как:
    • Ceph
    • Gluster
    • LINSTOR
    • Различные аппаратные решения
    • Облачные решения - gcp, aws
  • Storage class - это третий параметр, который учитывается в PVC
  • В storage class мы можем описать подключение к таким системам и указать данные для подключения, такие как:
    • адреса
    • логины/пароли/токены
    • различные другие настройки для взаимодействия с СХД

Persistent Volume 00:33:24

  • Абстракция, которая создаётся и в которой записывается информация о том диске, который был выдан нашему приложению, в PVC заявка, в PV хранится информация о том, какой диск в СХД был выдан нашему приложению, как правило, там ID диска хранится, или что-то подобное, например адрес. Storage class - это информация для kubernetes для получения диска, адрес API, креды и т.п.
  • Откуда берутся PV? 00:34:47
    • Самый простой вариант - системный администратор СХД руками создаёт диски и с данными этих дисков создаёт манифесты PV в kubernetes. Таким образом формируется список свободных PV в kubernetes, который описывает какие-то диски в СХД
    • Второй вариант - это использование PV Provisioner (см. ниже)

Пример монтирования PV в под 00:35:29

  • Имеем набор PV:

    • NFS PV 1 50GB
    • NFS PV 2 100GB
    • RBD PV 3 100GB (3)
  • PVС на диск в 50GB, storageClass'а NFS

  • Pod, с описанием тома типа PVC с именем, указанным в предыдущем пункте

  • Что произойдёт при применении манифестов:

    • PVC отработает и подключит к ноде диск в 50GB, переведя это PV в состояние BOUND, связанный с нашим PVC
    • К поду будет подключен наш PV
      • Грубо говоря, СХД подключается к диску ноды, где запущен наш под, данные прокидываются в контейнер, приложение начинает работать, kubernetes у себя записывает, что первый том (NFS PV 1 50GB) был занят (BOUND) таким-то подом, после этого его никто не сможет использовать, приложение его заняло, с ним работают, PV имеет статус BOUND
  • К исходным данным добавляется еще один PVС на диск в 50GB, storageClass'а NFS и запрашивающий его под

  • После применения манифеста будет выдан PV 2, т.к. он удовлетворяет нашему запросу, не важно что он больше чем нужно

  • Происходит аналогичная история, PV 2 становится BOUND, диск подключается по той же схеме к другому поду

  • Из 100GB места на PV 50GB места будут незадействованы

  • Такая ситуация типична, бывают случаи, когда на запросы небольшие, а PV нарезаны неудачным образом и на PVC небольшого объема выдаётся огромный PV. Во избежание таких ситуаций используют механизм PV Provisioner screenshot1

PV Provisioner 00:39:09

  • Если у нас нет свободных PV, но есть PVC, они будут висеть в состоянии PENDING, т.е. ждать, пока появятся PV, которые удовлетворяют запросу (PVC) - пока проснётся админ, нарежет СХД на PV, напишет и применит манифесты для подключения PV в kubernetes и только после этого приложение сможет запуститься
  • Чтобы не заниматься ручной нарезкой хранилищ на PV, придумали PV Provisioner
  • Это такая программа, которая ходит в облако или СХД, нарезает PV и создаёт нужные манифесты, причем диски создаются в соответствии запросам PVC
  • Таким образом мы получаем полную автоматизацию и целевое расходование дискового пространства

Манифесты, с которыми будем работать

  • Всё в каталоге ~/school-dev-k8s/practice/5.saving-data/3.pvc
# pvc.yaml
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: fileshare # Имя PVC
spec:
  storageClassName: csi-ceph-hdd-ms1 # Имя класса, оно формируется отдельно администратором
  accessModes:
  - ReadWriteMany # Режим работы с диском, ниже будет об этом
  resources:
    requests:
      storage: 10Mi # Запрашиваем диск размером в 10 мегабайт (если точнее - мебибайт, см. ниже в вопросах)
  • storageClassName можно не указывать, если в кластере создан и задан storageClass по умолчанию
ConfigMap 00:43:42
# configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fileshare
data:
  default.conf: |
    server {
      listen       80 default_server;
      server_name  _;
      default_type text/plain;
      location / {
        return 200 '$hostname\n';
      }
      location /files {
        alias /data;
        autoindex on;
        client_body_temp_path /tmp;
        dav_methods PUT DELETE MKCOL COPY MOVE;
        create_full_put_path on;
        dav_access user:rw group:rw all:r;
      }
    }
Deployment 00:44:16
# deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fileshare
spec:
  replicas: 2
  selector:
    matchLabels:
      app: fileshare
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: fileshare
    spec:
      initContainers:
      - image: busybox
        name: mount-permissions-fix
        command: ["sh", "-c", "chmod 777 /data"]
        volumeMounts:
        - name: data
          mountPath: /data
      containers:
      - image: centosadmin/reloadable-nginx:1.12
        name: nginx
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 10m
            memory: 100Mi
          limits:
            cpu: 100m
            memory: 100Mi
        volumeMounts:
        - name: config
          mountPath: /etc/nginx/conf.d
        - name: data
          mountPath: /data
      volumes:
      - name: config
        configMap:
          name: fileshare
      - name: data
        persistentVolumeClaim:
          claimName: fileshare
  • Немного грустного - в учебном кластере воспроизвести эксперименты с данными манифестами не получится 00:45:52
Работаем в консоли
  • Применяем манифест PVC 00:49:17 screenshot1
  • Видим что создался PVC с именем fileshare, с состоянием Bound
  • Иногда состояние может быть Pending, т.к. монтирования диска происходит не мгновенно, иногда это может занимать несколько секунд, много зависит от скорости СХД
  • В столбце VOLUME мы видим имя объекта PVC, созданного провижинером под нас
  • CAPACITY (размер) - 1Gi, хотя мы просили 10Mi, это зависит от настроек провижинера, видимо в нашем случае минимальный размер составляет 1Gi
  • Смотрим вывод k get pv 00:50:50
NAME                                      CAPACITY  ACCESS MODES  RECLAIM POLICY  STATUS    CLAIM                             STORAGECLASS             REASON  AGE 
pvc-3fc6a2fa-46df-4134-8b6e-3e93ea8413b7  10Gi      RWX           Delete          Bound     s000001/fileshare                 csi-ceph-hdd-ms1                 4d2h
pvc-59a16cc0-d96c-4760-afa3-1c12e255743c  1Gi       RWO           Delete          Bound     prometheus-monitoring/alertmana~  csi-ceph-hdd-ms1                 15d 
pvc-d1a0fad4-a977-4160-a4c3-1de9d264dd18  1Gi       RWO           Delete          Bound     prometheus-monitoring/kube-prom~  csi-ceph-hdd-ms1                 15d 
pvc-daf05a58-57cd-432b-874d-58dd2c86173e  2Gi       RWX           Retain          Released  s022208/csi-ceph-hdd-dp1-claim    csi-ceph-hdd-dp1-retain          16d 
pvc-e87abf3f-53bd-415a-a8c9-c6ea6f6f7901  2Gi       RWX           Retain          Released  s022208/csi-ceph-hdd-dp1-claim    csi-ceph-hdd-dp1-retain          16d 
pvc-ece71893-ead4-4ec1-a10d-a65fdf52b87f  50Gi      RWO           Delete          Bound     prometheus-monitoring/prometheu~  csi-ceph-ssd-ms1                 15d 
  • Видим все PV в учебном кластере
  • Claim s000001/fileshare соответстует нашему манифесту PVC
  • STATUS Released означает, что PV больше не задействован PVC
  • Что будет с данными, если мы удалим под? Сейчас узнаем, но для этого нужно его создать
  • 00:52:37 Сергей применяет все манифесты в каталоге и демонстирует, что PVC остался без изменений
  • Далее Сергей в ожидании окончания деплоя выводит различную информацию и комментирует это, в основном, ссылаясь на то, что мы рассмотрим позже
  • Дождались, видим что у нас развернулся деплоймент fileshare и один из подов успешно стартовал, а второй нет screenshot2
  • Сергей говорит о том, что согласно манифесту мы можем удалять под, но данные хранятся на PV и не потеряются пока существует соответствующий объект PVC, любой под может подключить этот PV, на который ссылается PVC и работать с этими данными
  • 00:55:06 Что будет c PV если мы удалим PVC - это зависит от политики удаления (RECLAIMPOLICY), это указывается в storage class:
    • Retain - оставить как есть, если сделать запрос k get pv мы увидим этот диск в состоянии Released
    • Delete - удалить вместе с pvc
  • 00:58:03 Если удалить PVС, то данные остаются непосредственно на СХД, их можно вытащить уже оттуда
  • Если мы уже удалили PVC, но RECLAIMPOLICY у нас Retain, мы можем отредактировать PV через kubectl edit и сменить Released на Available, тогда он снова будет доступен для подключения, после этого нужно создать PVC с запросом подключения этого конкретного PV
  • Смотрим на наши storage classes
$ k get sc
NAME                       PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
csi-ceph-hdd-dp1           cinder.csi.openstack.org   Delete          Immediate           true                   19d
csi-ceph-hdd-dp1-retain    cinder.csi.openstack.org   Retain          Immediate           true                   19d
csi-ceph-hdd-ms1           cinder.csi.openstack.org   Delete          Immediate           true                   19d
csi-ceph-hdd-ms1-retain    cinder.csi.openstack.org   Retain          Immediate           true                   19d
csi-ceph-ssd-dp1           cinder.csi.openstack.org   Delete          Immediate           true                   19d
csi-ceph-ssd-dp1-retain    cinder.csi.openstack.org   Retain          Immediate           true                   19d
csi-ceph-ssd-ms1           cinder.csi.openstack.org   Delete          Immediate           true                   19d
csi-ceph-ssd-ms1-retain    cinder.csi.openstack.org   Retain          Immediate           true                   19d
csi-high-iops-dp1          cinder.csi.openstack.org   Delete          Immediate           true                   19d
csi-high-iops-dp1-retain   cinder.csi.openstack.org   Retain          Immediate           true                   19d
csi-high-iops-ms1          cinder.csi.openstack.org   Delete          Immediate           true                   19d
csi-high-iops-ms1-retain   cinder.csi.openstack.org   Retain          Immediate           true                   19d
  • По поводу таблицы вывода storage classes 00:59:04
  • Речь идёт об учебном кластере, но общие принципы везде одни и те же
    • Суффиксы dp1 и ms1 отвечают за зону доступности, территориальное расположение, в каждом отдельном ДЦ есть своя СХД, обслуживающая этот ДЦ, т.е. узлы из определённого ДЦ будут иметь приоритет на работу с СХД в этом же ДЦ, т.к. это будет существенно быстрее
      • Подключение к СХД в другом ДЦ будет существенно медленнее, за счёт административных или технических ограничений может быть невозможно в принципе (в AWS реализовано такое ограничение)
    • hdd, ssd, high-iops - суффиксы, отвечающие за тип и/или производительность выделенных блоков дисков в СХД
      • стоит учитывать не только о производительность, но и о стоимость работы с такими хранилищами
  • PROVISIONER в даном случае используется от openstack
  • Провижинеры бывают встроенные, а бывают внешние, в выводе мы видим один из внешних
  • 01:03:00 Последнее время активно внедряется технология CSI - container storage interface, в будущем планируется переход CSI и отказ от встроенных в kubernetes провижинеров
  • С помощью CSI можно делать с томами разные вещи, например, менять размеры дисков
    • 01:02:31 Идёт рассказ о концепции CSI
    • 01:03:31 Сергей показывает как увеличить размер подключённого PV
      • Редактируем k edit pvc <pvc-name> PVC и указываем желаемый размер, в нашем случае 10Mi=>10Gi
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
          ...
      spec:
          accesModes:
          - ReadWriteMany
          resources:
              requests:
                  storage: 10Gi    # производим замену тут
      ...
      • После этого k get pvc показывает, что размер PVC остался старый, а k get pv, что размер PV увеличился
      • Если посмотреть в describe PVC k describe pvc <pvc-name>, то в событиях сообщение от провижинера о том, что требуется resize файловой системы на ноде, наш CSI не умеет на лету менять размер файловой системы
      • В нашем случае достаточно пересоздать под, использующий данный PVC
      • Сергей убивает под в состоянии Running и ждёт пока поднимется новый
      • Новый долго не поднимается, Сергей идет в describe пода и видит в событиях, что resize успешно выполнен
      • Вывод k get pvc теперь показывает новый размер диска
      • Далее мы заходим внутрь пода через k exec <pod-name> --bash, наблюдаем вывод df -h и видим наш диск нужного размера
      • 01:08:27 Некоторые системы позволяют делать подобное на лету, зависит от СХД и CSI для неё
    • 01:08:38 Уменьшить размер диска, к сожалению, нельзя

initContainers 01:09:07

  • Обычный контейнер, который может запускаться перед запуском основного приложения, обычно его применяют для каких-то доп. настроек
  • В примере выше
    • запускался контейнер busybox и в нём выполнялся скрипт по установке прав 777 на каталог /data
    • также в контейнер монтировался том с именем data в точку монтирования /data
    • data - это том, полученный из pvc fileshare
    • в нашем случае это нужно потому что процесс nginx в основном контейнере работает под ограниченным пользователем, а когда мы создаём файловую систему, владельцем являет root, поэтому, нам нужно от имени root поменять права таким образом, чтобы процесс nginx мог писать в данный каталог
  • таких контейнером может быть несколько, они выполняются по порядку описания в манифесте
  • можно монтировать те же тома, что в основном контейнере
    • пример - загрузка дампа БД из бэкапа
  • можно запускать процессы от root, как в нашем примере, чтобы приложения в основных контейнерах работали от ограниченного пользователя, т.н. Rootless mode, это применяется для уменьшения потенциальной площади атаки в случае эксплуатации различных уязвимостей в контейнере
  • После выполнения действия такие контейнеры останавливаются

Q&A

  • 01:15:16
    • Q: Почему в манифестах kubernetes принято писать МБ как Mi а ГБ как Gi?
    • A: Можно сходить в википедию, там об этом прекрасно написано, если грубо, приставка мега означает 10^2, а Mi - Меби - 2^10, т.е. те самые мегабайты, привычные нам из информатики
  • 01:16:01
    • Q: initContainer только для модификации данных на дисках?
    • A: initContainer можно использовать для чего угодно, например для получения настроек из сервера конфигураций, волта того же и подсовывания их вашему приложению
  • 01:16:21
    • Q: exec -it [pod_name] -- bash //это мы зашли в контейнер в поде, а если у меня в поде больше 1го контейнера то как мне попасть в нужный?
    • A: Для этого есть ключ -c и нужно указать имя контейнера
  • 01:16:41
    • Q: Можно ли использовать PV для сохранения данных базы данных сервиса в pod? Таким образом если pod со своей базой данных удалился/упал, то сохранятся данные базы данных в PV?
    • A: Да, конечно, для подобных сценариев всё и предназначено
  • 01:17:08
    • Q: initContainers выполняет действие и останавливается (и висит потушенным) или удаляется?
    • A: Да, удаляется (судя во всему, с помощью механизма Garbage Collection)
  • 01:17:19
    • Q: Стоит ли рассматривать init container как 1 команду?
    • A: Не стоит, лучше рассматривать его как контейнер, в котором что-то выполняется
  • 01:17:47
    • Q: initContainer может модифицировать основной контейнер?
    • A:
      • В теории можно этого добиться, например, обратиться к системе управления контейнерами из initContainer, но никто так не делает
      • Это не модификация контейнера, но возможно подразумавалось что-то подобное - можно, например, подключить emptyDir к initContainer, наполнить его данными и затем подключить к основному контейнеру, ну и вариации на эту тему, как пример выше с настройкой прав доступа для дальнейшей работы nginx
  • 01:18:50
    • Q: Как система делает так, чтобы при пересоздании Pod'a был подключен именно тот PV, который использовался раньше?
    • A: В поде указывается claimName для pvc, а pvc соответствует определённому тому, диску на СХД

Разбираемся, почему не стартовал один из подов 01:19:57

  • 01:20:08 Если в k get pod мы видим в STATUS что-то типа Init:X/Y - это означает, что из Y initContainers успешно отработали X.
    • Таким образом можно понимать, на каком этапе находится запуск приложения
  • 01:20:36 Смотрим describe, видим различные ошибки, ключевые звучат как "Invalid input received: Invalid volume: Volume status must be available to reserve, but status is in-use. (HTTP 400)" и "timed out waiting for condition"
    • Это говорит нам о том, что статус диска не тот который нужн и мы не дождались нужного состояния
    • Смотрим вывод k get pvc fileshare -o yaml, вспоминаем, что у нас указан accessModes: ReadWriteMany, а также, что мы берём диск определённого storageClass
      • Проблема в том, что СХД, которая описана в этом storageClass, не позволяет создавать диски, которые могут работать одновременно с несколькими узлами кластера, доступно только одно подключение
      • Таким образом мы понимаем, что первый под запустился и захватил диск, а второй под не может этого сделать

ReadWriteMany 01:23:35

  • А теперь про те СХД, которые в таком режиме работать умеют
  • Для примера берём:
    • Кластер kubernetes из 3х нод
    • СХД на основе cephFS, это кластерная файловая система, которая как раз умеет обеспечивать множественный доступ к своим ресурсам, т.е. мы можем подключить её одновременно на 3 наших узла
    • Приложение, которое будет запускаться в 3 реплики, по одной на каждую ноду
  • Как всё в нашем примере должно работать:
    • Наше приложение будет использовать диск, который хранится на нашем СХД
    • СХД подключается ко всем нодам
    • kubernetes прокидывает точки монтирования с узлов в контейнеры наших реплик приложения
    • Таким образом, каждый контейнер подключается не напрямую к СХД, а работает с точкой монтирования на своём узле, а подключением дисков занимается та часть kubernetes, которая работает на каждом узле нашего кластера, которая собственно и отвечает за запуск контейнеров, их работоспособность и т.д.

ReadWriteOnce 01:25:34

  • Когда нам нужен диск данного типа либо система хранение не поддерживает ReadWriteMany
  • Для примера берём
    • Кластер kubernetes из 3х нод, где на 2й ноде запущено 2 реплики приложения
    • СХД на основе ceph, но отдающая блочные устройства RBD, по факту диски
    • Такой диск можно смонтировать только на один узел, в у нас это будет узел №2
    • Если попытаться смонтировать этот диск на другие узлы, мы получим ошибки, подобные тем, что видели раньше
    • Но те 2 реплики, которые запущены на 2м узле, смогут без проблем работать с нашим диском, т.к. они просто используют точку монтирования на узле
      • Это всё работает без проблем, потому что с точки зрения ОС это 2 процесса, которые работают под управлением одного ядра и соответственно, они просто используют диск, подключенный к нашему серверу, никаких кластерных ухищрений в данном случае не происходит
      • Недавно в kubernetes добавили ReadWriteOncePod, который запрещает совместный доступ из разных подов к точке монтирования

Заключение 01:29:00

  • На этом всё, что касалось хранения данных в kubernetes
  • По факту, kubernetes никакие данные не хранит, но он может подключать внешние СХД к своим узлам и точки монтирования этих устройств прокидывать в контейнеры
  • Еще одна распространенная ошибка, когда говорят "я себе поставил кластер kubernetes с помощью kubeadm, kubespray и у меня не запускается манифест из гитхаба, в котором PVC создаётся" - естественно, он не запустится, т.к. в свежем кластере не настроены СХД, не настроены storageClasses, которые описывают доступ к этим СХД, люди почему-то думают, что при установке kubernetes автоматически будет организована какая-то СХД волшебным образом из воздуха

Минутка рекламы вводного курса по Apache Kafka 01:30:09

  • 01:31:28
    • Q: Политики безопасности (для hostPath) прописаны вами или уже присутствуют в kubernetes
    • A: Нами, по умолчанию ничего не прописано, можно сказать, что присутствует дыра в безопасности
  • 01:31:49
    • Q: Что делать, если приложение течёт по памяти за лимиты, а под не убивается, где почитать подробнее?
    • A:
      • Ни разу не было такого
      • Если мы указываем для контейнера определённый лимит, то как только приложение попытается запросить больше, то придёт OOM killer и убъет этот процесс
      • Есть 2 варианта прихода OOM killer
        • когда кончается общая память на узле, killer приходит к самому невезучему процессу, у которого самый низкий score, который был запущен совсем недавно и убивает его, а затем ищет следующих претендентов
        • если лимит в контейнере, то есть, по факту установлены cgroup лимиты для процесса и процесс исчерпал свой лимит, то убивается такой процесс в контейнере, а kubernetes видит, что процесс в контейнере был убит и перезапускает контейнер
      • Возможно, ситуация такая, что вы не замечаете, что контейнер перезапускался
  • 01:33:46
    • Q: Какой на ваш взгляд лучший GUI для kubernetes? Lens?
    • A:
      • Лучший GUI - его отсутствие, т.е. kubectl
      • Я пробовал Lens под виндой, он жутко тормозил и запускался через раз
  • 01:34:48
    • Q: Что за скрипт, который следит за конфигом nginx? Ведь это уже 2 процесса, кто обеспечивает их работу?
    • A:
      • Это плохие практики, лучше так не делать, 3 года назад, когда это писалось, это было еще нормально
      • В скрипте бесконечный цикл и процесс nginx, который отправили в фон, таким образом 2 процесса работают внутри контейнера, цикл следит за входящими в контейнер сигналами и попутно проверяет контрольные суммы файлов конфигурации nginx, если они изменились, выполняется nginx reload (вероятно, nginx -s reload)
  • 01:36:07
    • Q: Есть ли политика у Provisioner по удалению (освобождению дисков) или администратор должен сам следить за дисками, которые долгое время не использовались?
    • A: Нет, администратор должен сам следить, Provisioner только создаёт диски, правда, можно его еще заставить их расширять
  • 01:36:45
    • Q: Какие есть кейсы использования hostPath, если он не рекомендуется к использованию
    • A:
      • hostPath используется в агентах мониторинга, node exporter монтирует файловую систему узла, чтобы узнавать, сколько места на диске осталось
      • также, штука, ответственная за сеть (будет позже) использует hostPath, чтобы создавать сеть в подах
      • иногда hostPath используется в целях разработки, для простого обмена данными с подом, например, подбросить исправленный код на веб-сервер
      • в общем и целом - для системных задач
  • 01:37:57
    • Q: Нормальным ли решением будет запускать СУБД в kubernetes с хранением данных на PV?
    • A: Для тестов - да, для продакшн - нет, будет слишком много проблем
  • 01:39:46
    • Q: Что ещё можно сделать через initContainer? Можно, например, ssh туннель открыть?
    • A: Можно сделать всё что угодно, и shh туннель открыть, но непонятно, зачем это нужно, т.к. такой контейнер имеет ограниченный срок жизни
  • 01:40:09
    • Q: PV Provisioner - это уже не часть kubernetes, правильно?
    • A: В текущем виде - это часть kubernetes, но в будущем планируется переход на container storage interface, а это уже будет внешняя система расширения
  • 01:41:09
    • Q: Вначале презентации было сказано, что для файлов лучше юзать S3 и т.д., тогда зачем было то, о чём рассказывалось?
    • A: Чтобы мы знали о такой опции, в некоторых случаях это приемлемо
  • 01:43:14
    • Q: Где всё таки лучше держать БД?
    • A: На виртуалках или bare metal, в зависимости от потребностей
  • 01:43:23
    • Q: Если надо сохранить дамп JVM, при OOM например, как это сделать?
    • A: Нужно думать
  • 01:44:26
    • Q: Где создаётся emptyDir? На файловой системе ноды или где-то еще?
    • A: По умолчанию да, но, при желании, можно и в оперативной памяти создать
  • 01:44:59
    • Q: Ресайз без потери данных произойдёт?
    • A: Да
  • 01:45:07
    • Q: Можете что-то рассказать про https://github.com/rancher/local-path-provisioner, если использовали?
    • A:
      • Конретно этот провижинер не использовал, но обычно провижинер создаёт PV из подключенных устройств, если мы добавляем какой-то диск, то провижинер создаёт под этот диск манифест PV, это некая альтернатива hostPath, так называемые local volumes, мы создаём манифест PV и в нём мы указываем, что это диск типа "локальный" и указываем, что он ссылается на конкретный каталог на узле
      • Отличия от hostPath в том, что в случае с hostPath, человек, который создал деплоймент, может указать любой путь, даже подключить корень, а в случае с дисками типа local, этот путь задаёт администратор и обычно это безопасный путь, где нет ничего, что может подвергнуть систему проблемам
  • 01:46:35
    • Q: initContainer можно использовать для ожидания старта других подов?
    • A:
      • Можно, но не нужно, зависимости запуска - плохая идея
      • Нужно строить систему таким образом, чтобы она была готова к тому, что соседи могут падать и подниматься
  • 01:48:37
    • Q: Какой смысл делать несколько initContainers, если можно в одном выполнить несколько команд?
    • A:
      • Может быть 5 разных задач под разные образы, например:
        • busybox - выполнить простой скрипт
        • MySQL - получить дамп базы и развернуть его
      • Такой вот docker-way
  • 01:49:08
    • Q: Что будет, если поменять лимиты PVC, что он перестанет влазить в конкретные хранилища?
    • A: Зависит от того, как СХД обрабатывает такие ситуации, тот же ceph упадёт и будет лежать, пока в него не добавят дисков
  • 01:50:31
    • Q: Отличия ванильного kubernetes от openshift?
    • A:
      • В openshift упор на безопасность и куча дополнительных типов ресурсов и куча вещей, которые делаются иначе
      • В openshift есть сценарии, одобренные Redhat, по которым стоит действовать
      • В kubernetes больше свободы, но не для всего есть готовые решения
      • В целом, зная одно, проще будет работать с другим
  • 01:51:35
    • Q: Что такое /dev/vda1 с точкой монтирования /etc/hosts?
    • A: Это всякие служебные точки монтирования, которые монтируют hosts с узла, resolv.conf с узла и т.п.