learning:k8s-cka
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
learning:k8s-cka [28.03.2024 20:04] – [11.5 TROUBLESHOOTING APPLICATION ACCESS] viacheslav | learning:k8s-cka [27.03.2025 15:24] (текущий) – [Выжимка по апгрейду] viacheslav | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | https:// | ||
+ | |||
+ | ===== Установка кластера ===== | ||
+ | Мин. требования к нодам: | ||
+ | * Современная ОС типа Ubuntu 22.04 | ||
+ | * 2 ГБ памяти | ||
+ | * 2 vCPU и более для контрольной ноды | ||
+ | * Все ноды должны видеть друг друга по сети | ||
+ | |||
+ | До установки кластера через kubeadm, нужно поставить | ||
+ | * Контейнерный движок - containerd, CRI-O и т. д. (не является вопросом CKA, пример установки: | ||
+ | * Kubernetes tools - kubeadm, kubelet, kubectl (также не является вопросом CKA, https:// | ||
+ | |||
+ | Итак: | ||
+ | <code bash> | ||
+ | git clone https:// | ||
+ | cd cka # на всех нодах | ||
+ | sudo ./ | ||
+ | sudo ./ | ||
+ | |||
+ | # Установить кластер | ||
+ | sudo kubeadm init | ||
+ | |||
+ | # Настроить клиент (описано в тексте после завершения установки кластера) | ||
+ | To start using your cluster, you need to run the following as a regular user: | ||
+ | mkdir -p $HOME/.kube | ||
+ | sudo cp -i / | ||
+ | sudo chown $(id -u):$(id -g) $HOME/ | ||
+ | |||
+ | # Проверить работоспособность клиента: | ||
+ | kubectl get all | ||
+ | NAME | ||
+ | service/ | ||
+ | |||
+ | # Установить сетевой аддон (здесь: | ||
+ | kubectl apply -f https:// | ||
+ | |||
+ | # присоединить воркеров к кластеру: | ||
+ | sudo kubeadm join 10.1.0.201: | ||
+ | --discovery-token-ca-cert-hash sha256: | ||
+ | |||
+ | # Проверить наличие всех нод | ||
+ | kubectl get nodes | ||
+ | NAME | ||
+ | t-k8s1 | ||
+ | t-k8s2 | ||
+ | t-k8s3 | ||
+ | |||
+ | # В большинстве случаев достаточно просто kubeadm init, но есть ряд опций, которые могут пригодиться, | ||
+ | --apiserver-advertise-address: | ||
+ | --config: the name of a configuration file used for additional configuration | ||
+ | --dry-run: performs a dry-run before actually installing for real | ||
+ | --pod-network-cidr: | ||
+ | --service-cidr: | ||
+ | |||
+ | # Справка по опциям | ||
+ | kubeadm init -h |less | ||
+ | |||
+ | # Если kubeadm init или kubeadm join отработали криво, то откатить уже внесённые изменения на хосте можно | ||
+ | kubeadm reset | ||
+ | |||
+ | # Токен, который выдаётся после инсталляции кластера, | ||
+ | sudo kubeadm token create --print-join-command | ||
+ | |||
+ | </ | ||
+ | Настройка автокомплита и алиаса: | ||
+ | |||
+ | ===== Настройка клиента, | ||
+ | Для получения административного доступа к кластеру через клиента, | ||
+ | Под рутом для той же цели можно выполнить '' | ||
+ | Для более тонкой настройки нужно заводить спец. пользователя и настраивать ему права через управление ролями (RBAC). | ||
+ | |||
+ | Контекст - шаблон конфигурации настроек клиента. При выборе контекста активируется определённая группа параметров клиента. | ||
+ | * Кластер, | ||
+ | * Пространство имён (namespace) | ||
+ | * Пользователь | ||
+ | |||
+ | <code bash> | ||
+ | kubectl config view # просмотр контекста | ||
+ | kubectl set-context # задать контекст | ||
+ | kubectl use-context # использовать контекст | ||
+ | |||
+ | # Кластер задаётся адресом и сертификатом. Здесь добавляется второй кластер devcluster. | ||
+ | kubectl config --kubeconfig=~/ | ||
+ | # Пространство имён - то, что будет использоваться в этом контексте. Если оно не существует, | ||
+ | kubectl create ns | ||
+ | # Пользователь задаётся его сертификатом X.509 (о других опциях позже) | ||
+ | kubectl config --kubeconfig=~/ | ||
+ | # После настройки всех параметров можно создавать контекст | ||
+ | kubectl set-context devcluster --cluster=devcluster --namespace=devspace --user=vasya | ||
+ | </ | ||
+ | |||
+ | ===== Deployment ===== | ||
+ | |||
+ | Это стандартный способ развёртывания контейнеров масшабируемым способом. Масштабирование осуществляет механизм ReplicaSet. \\ | ||
+ | Деплой предоставляет возможность RollingUpdate для обновлений без простоя. | ||
+ | <code bash> | ||
+ | # Императивный способ создания деплоя | ||
+ | kubectl create deploy | ||
+ | # Справка | ||
+ | kubectl create deploy -h |less | ||
+ | |||
+ | kubectl create deploy firstnginx --image=nginx --replicas=3 | ||
+ | deployment.apps/ | ||
+ | |||
+ | kubectl get all | ||
+ | NAME | ||
+ | pod/ | ||
+ | pod/ | ||
+ | pod/ | ||
+ | |||
+ | NAME | ||
+ | service/ | ||
+ | |||
+ | NAME | ||
+ | deployment.apps/ | ||
+ | |||
+ | NAME | ||
+ | replicaset.apps/ | ||
+ | </ | ||
+ | |||
+ | ===== DaemonSet ===== | ||
+ | |||
+ | Запускает по одному экземпляру приложения на каждой ноде. Обычно используется для всяких агентов мониторинга, | ||
+ | Если нужно запустить демонсет на управляющих нодах, то надо конфигурировать toleration в нём для преодоления taint на мастер-нодах. | ||
+ | |||
+ | <code bash> | ||
+ | # В пользовательском неймспейсе нет демонсетов. Смотрим во всех неймспейсах: | ||
+ | kubectl get ds -A | ||
+ | NAMESPACE | ||
+ | kube-system | ||
+ | kube-system | ||
+ | |||
+ | # Посмотреть, | ||
+ | kubectl get ds -n kube-system calico-node -o yaml |less | ||
+ | </ | ||
+ | |||
+ | Там описаны tolerations | ||
+ | <code yaml> | ||
+ | tolerations: | ||
+ | - effect: NoSchedule | ||
+ | operator: Exists | ||
+ | - key: CriticalAddonsOnly | ||
+ | operator: Exists | ||
+ | - effect: NoExecute | ||
+ | operator: Exists | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | <code bash> | ||
+ | # Создать болванку для демонсета (create deploy, да) | ||
+ | kubectl create deploy mydaemon --image=nginx --dry-run=client -o yaml > mydeamon.yaml | ||
+ | </ | ||
+ | |||
+ | В получившемся файле mydeamon.yaml нужно | ||
+ | - '' | ||
+ | - удалить строку '' | ||
+ | - удалить строку '' | ||
+ | |||
+ | <code bash> | ||
+ | kubectl apply -f mydeamon.yaml | ||
+ | |||
+ | kubectl get ds | ||
+ | NAME | ||
+ | mydaemon | ||
+ | |||
+ | kubectl get po -o wide | ||
+ | NAME | ||
+ | firstnginx-d8679d567-4cxpj | ||
+ | firstnginx-d8679d567-9sl2r | ||
+ | firstnginx-d8679d567-vwkh7 | ||
+ | mydaemon-cw7x8 | ||
+ | mydaemon-srrb8 | ||
+ | </ | ||
+ | |||
+ | ===== StatefulSet ===== | ||
+ | |||
+ | Подвид деплоя, | ||
+ | * Стабильные и уникальные сетевые имена | ||
+ | * Стабильное постоянное хранилище данных | ||
+ | * Упорядоченное развёртывание и масштабирование | ||
+ | * Упорядоченное и автоматическое " | ||
+ | |||
+ | ^Deployment ^StatefulSet ^ | ||
+ | |Для Stateless-приложений |Для Stateful-приложений | | ||
+ | |Все поды создаются параллельно |Поды создаются последовательно один за другим (pod-0..pod-X) | | ||
+ | |Поды удаляются в случайном порядке |Поды удаляются в обратном порядке (pod-X..pod-0) | | ||
+ | |Поды имеют случайные имена |Поды имеют чётко определённые имена < | ||
+ | |Все поды используют один и тот же PV (persistent volume) |Каждая реплика использует свой PV. При перезапуске реплики она получает то же имя и подхватывает тот же PV. | | ||
+ | |Легко масштабировать |Трудно масштабировать | | ||
+ | |||
+ | Перед использованием стейтфул-сетов нужно настроить общее хранилище. Если удалить ст-сет, | ||
+ | Чтобы обеспечить доступ к подам ст-сета, | ||
+ | То, что поды будут остановлены после удаления ст-сета, | ||
+ | |||
+ | Ст-сет похож на деплой, | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Service | ||
+ | metadata: | ||
+ | name: nginx | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | ports: | ||
+ | - port: 80 | ||
+ | name: web | ||
+ | # Headless service | ||
+ | clusterIP: None | ||
+ | selector: | ||
+ | app: nginx | ||
+ | --- | ||
+ | apiVersion: apps/v1 | ||
+ | kind: StatefulSet | ||
+ | metadata: | ||
+ | name: web | ||
+ | spec: | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: nginx | ||
+ | serviceName: | ||
+ | replicas: 3 | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | terminationGracePeriodSeconds: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: k8s.gcr.io/ | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | name: web | ||
+ | volumeMounts: | ||
+ | - name: www | ||
+ | mountPath: / | ||
+ | volumeClaimTemplates: | ||
+ | - metadata: | ||
+ | name: www | ||
+ | spec: | ||
+ | accessModes: | ||
+ | resources: | ||
+ | requests: | ||
+ | storage: 1Gi | ||
+ | </ | ||
+ | |||
+ | ===== Запуск одиночных подов ===== | ||
+ | |||
+ | Это нужно только для тестирования, | ||
+ | - Нет автоперезапуска | ||
+ | - Нет балансировки | ||
+ | - Нет защиты от простоя при обновлении | ||
+ | Так что в обычной ситуации нужно использовать деплой, | ||
+ | <code bash> | ||
+ | # Справка | ||
+ | kubectl run -h |less | ||
+ | # Запустить под, который закончит работу через 1 ч. | ||
+ | kubectl run sleepy --image=busybox -- sleep 3600 | ||
+ | </ | ||
+ | |||
+ | ===== Init container ===== | ||
+ | |||
+ | Init container подготавливает почву для запуска основного контейнера в поде. Основной запускается после того, как отработал инициатор.\\ | ||
+ | https:// | ||
+ | |||
+ | Простейший вариант | ||
+ | <file yaml initme.yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: init-demo | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | # These containers are run during pod initialization | ||
+ | initContainers: | ||
+ | - name: install | ||
+ | image: busybox | ||
+ | command: | ||
+ | - sleep | ||
+ | - " | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | <code bash> | ||
+ | kubectl apply -f initme.yaml | ||
+ | |||
+ | kubectl get po | ||
+ | NAME | ||
+ | firstnginx-d8679d567-4cxpj | ||
+ | firstnginx-d8679d567-9sl2r | ||
+ | firstnginx-d8679d567-vwkh7 | ||
+ | init-demo | ||
+ | mydaemon-cw7x8 | ||
+ | mydaemon-srrb8 | ||
+ | |||
+ | kubectl get po | ||
+ | ... | ||
+ | init-demo | ||
+ | |||
+ | kubectl get po | ||
+ | ... | ||
+ | init-demo | ||
+ | </ | ||
+ | |||
+ | ===== Масштабирование ===== | ||
+ | |||
+ | Деплой, | ||
+ | Есть ещё HorizontalPodAutoscaler, | ||
+ | <code bash> | ||
+ | kubectl scale deploy firstnginx --replicas 5 | ||
+ | deployment.apps/ | ||
+ | |||
+ | kubectl get all --selector app=firstnginx | ||
+ | NAME | ||
+ | pod/ | ||
+ | pod/ | ||
+ | pod/ | ||
+ | pod/ | ||
+ | pod/ | ||
+ | |||
+ | NAME | ||
+ | deployment.apps/ | ||
+ | |||
+ | NAME | ||
+ | replicaset.apps/ | ||
+ | </ | ||
+ | |||
+ | ===== Sidecar container ===== | ||
+ | |||
+ | Обычно под содержит один контейнер, | ||
+ | * Sidecar: доп. функционал к основному контейнеру | ||
+ | * Ambassador: прокси для доступа извне | ||
+ | * Adapter: обрабатывает вывод основного контейнера | ||
+ | |||
+ | Тома, подключенные к поду, используются как общее хранилище. Основной контейнер пишет туда, а сайдкар читает это.\\ | ||
+ | Том может быть подключен как через PVC, так и непосредственно. | ||
+ | |||
+ | <file yaml sidecarlog.yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: two-containers | ||
+ | spec: | ||
+ | |||
+ | volumes: | ||
+ | - name: shared-data | ||
+ | emptyDir: {} | ||
+ | |||
+ | containers: | ||
+ | |||
+ | - name: nginx-container | ||
+ | image: nginx | ||
+ | volumeMounts: | ||
+ | - name: shared-data | ||
+ | mountPath: / | ||
+ | |||
+ | - name: busybox-container | ||
+ | image: busybox | ||
+ | volumeMounts: | ||
+ | - name: shared-data | ||
+ | mountPath: /messages | ||
+ | command: ["/ | ||
+ | args: [" | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | kubectl apply -f sidecarlog.yaml | ||
+ | pod/ | ||
+ | |||
+ | # Запустить cat в сайдкар-контейнере, | ||
+ | kubectl exec -it two-containers -c nginx-container -- cat / | ||
+ | hello from the cluster | ||
+ | </ | ||
+ | |||
+ | ===== Pod volumes ===== | ||
+ | |||
+ | Pod volumes - это часть спецификации пода, где прямо в манифесте пода жёстко заданы настройки хранилища. Это нормально, | ||
+ | |||
+ | Типы хранилищ для подключения | ||
+ | <code bash> | ||
+ | kubectl explain pod.spec.volumes |less | ||
+ | </ | ||
+ | |||
+ | Пример самого простого общего хранилища - emptyDir. Это временная общая папка, существующая только когда существует под. | ||
+ | <file yaml morevolumes.yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: morevol | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: centos1 | ||
+ | image: centos:7 | ||
+ | command: | ||
+ | - sleep | ||
+ | - " | ||
+ | volumeMounts: | ||
+ | - mountPath: /centos1 | ||
+ | name: test | ||
+ | - name: centos2 | ||
+ | image: centos:7 | ||
+ | command: | ||
+ | - sleep | ||
+ | - " | ||
+ | volumeMounts: | ||
+ | - mountPath: /centos2 | ||
+ | name: test | ||
+ | volumes: | ||
+ | - name: test | ||
+ | emptyDir: {} | ||
+ | </ | ||
+ | |||
+ | Если запустить этот манифест и создать файл в /centos1 первого контейнера, | ||
+ | <code bash> | ||
+ | kubectl exec -it morevol -c centos1 -- touch / | ||
+ | kubectl exec -it morevol -c centos2 -- ls /centos2 | ||
+ | centos1file | ||
+ | </ | ||
+ | |||
+ | ===== Persistent volume (PV) ===== | ||
+ | |||
+ | Могут быть созданы вручную или автоматически (Storage class + Storage provisioner). Поды не могут подключать PV непосредственно, | ||
+ | |||
+ | PV объёмом 2 ГБ, локальное (что в кластере бессмысленно) по каталоге /mydata, режим доступа ReadWriteOnce. | ||
+ | <file yaml pv.yaml> | ||
+ | kind: PersistentVolume | ||
+ | apiVersion: v1 | ||
+ | metadata: | ||
+ | name: pv-volume | ||
+ | labels: | ||
+ | type: local | ||
+ | spec: | ||
+ | storageClassName: | ||
+ | capacity: | ||
+ | storage: 2Gi | ||
+ | accessModes: | ||
+ | - ReadWriteOnce | ||
+ | hostPath: | ||
+ | path: "/ | ||
+ | </ | ||
+ | storageClassName используется как ярлык, без него PVC не сможет подключиться к PV. | ||
+ | |||
+ | <code bash> | ||
+ | # Проверка | ||
+ | kubectl get pv | ||
+ | NAME CAPACITY | ||
+ | pv-volume | ||
+ | </ | ||
+ | |||
+ | ===== Persistent volume claim (PVC) ===== | ||
+ | |||
+ | Позволяет поду подключиться к PV, некая заявка на предоставление места на диске. Указывается в манифесте пода. | ||
+ | |||
+ | Если не будет указан storageClassName, | ||
+ | <file yaml pvc.yaml> | ||
+ | kind: PersistentVolumeClaim | ||
+ | apiVersion: v1 | ||
+ | metadata: | ||
+ | name: pv-claim | ||
+ | spec: | ||
+ | storageClassName: | ||
+ | accessModes: | ||
+ | - ReadWriteOnce | ||
+ | resources: | ||
+ | requests: | ||
+ | storage: 1Gi | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Проверка | ||
+ | kubectl get pv,pvc | ||
+ | NAME | ||
+ | persistentvolume/ | ||
+ | |||
+ | NAME | ||
+ | persistentvolumeclaim/ | ||
+ | </ | ||
+ | Видно, что размер PVC 2 ГБ, несмотря на то, что требовался 1 ГБ. Но нельзя запросить половину объёма от PV, поэтому размер 2 ГБ. | ||
+ | |||
+ | ===== Подключение PV к поду ===== | ||
+ | |||
+ | В spec.volumes задан pv-storage, который использует pv-claim и монтируется в ''/ | ||
+ | |||
+ | <file yaml pv-pod.yaml> | ||
+ | kind: Pod | ||
+ | apiVersion: v1 | ||
+ | metadata: | ||
+ | name: pv-pod | ||
+ | spec: | ||
+ | volumes: | ||
+ | - name: pv-storage | ||
+ | persistentVolumeClaim: | ||
+ | claimName: pv-claim | ||
+ | containers: | ||
+ | - name: pv-container | ||
+ | image: nginx | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | name: " | ||
+ | volumeMounts: | ||
+ | - mountPath: "/ | ||
+ | name: pv-storage | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # После запуска пода пишем на PV | ||
+ | kubectl exec -it pv-pod -- touch / | ||
+ | </ | ||
+ | Так как PV смотрит на локальную ФС, теперь надо понять, | ||
+ | <code bash> | ||
+ | # Выяснить путь (path), здесь: /mydata | ||
+ | kubectl describe pv | ||
+ | # Где запустился под | ||
+ | kubectl get po pv-pod -o wide | ||
+ | NAME | ||
+ | pv-pod | ||
+ | </ | ||
+ | Под запустился на t-k8s3, так что там в каталоге ''/ | ||
+ | Так как PV локальный, | ||
+ | |||
+ | В целом путь такой: PV -> PVC (cсылается на имя PV) -> Pod volume (cсслыется на имя PVС) -> Pod volumeMount (cсслыется на имя volume). | ||
+ | |||
+ | ===== StorageClass ===== | ||
+ | |||
+ | Позволяет автоматически предоставлять хранилище в пользование. Даже если storageClass не используется в таком виде, он служит для связи PV и PVC как селектор.\\ | ||
+ | В кластере может существовать несколько объектов storageClass для разных типов хранилищ (быстрое/ | ||
+ | Один storageClass может быть задан по умолчанию | ||
+ | <code bash> | ||
+ | kubectl patch storageclass my-sc -p ' | ||
+ | </ | ||
+ | Чтобы предоставлять хранилище в пользование, | ||
+ | Storage Provisioner нужно настроить до storageClass. | ||
+ | |||
+ | В PV и PVC свойство storageClass может быть задано для подключения к определённому хранилищу, | ||
+ | |||
+ | ===== Storage provisioners (providers) ===== | ||
+ | |||
+ | Storage Provisioner (provider) работает с storageClass, | ||
+ | Чтобы провайдер работал, | ||
+ | Roles и RoleBindings используются для создания таких разрешений. ServiceAccount создаётся для подключения пода к нужной RoleBinding. | ||
+ | |||
+ | На мастер-ноде | ||
+ | <code bash> | ||
+ | sudo apt install nfs-server -y | ||
+ | sudo mkdir /nfsexport | ||
+ | sudo echo "/ | ||
+ | sudo systemctl restart nfs-server | ||
+ | </ | ||
+ | |||
+ | На остальных нодах | ||
+ | <code bash> | ||
+ | sudo apt install nfs-client -y | ||
+ | # Проверить, | ||
+ | showmount -e <control node IP> | ||
+ | </ | ||
+ | |||
+ | На мастер-ноде | ||
+ | <code bash> | ||
+ | # Установить helm: скачать релиз и скопировать бинарник в / | ||
+ | tar xvf helm.tar.gz | ||
+ | sudo cp linux-amd64/ | ||
+ | |||
+ | # Добавить репозиторий | ||
+ | helm repo add nfs-subdir-external-provisioner https:// | ||
+ | # Установить | ||
+ | helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/ | ||
+ | </ | ||
+ | Провайдер появляется в списке подов. | ||
+ | |||
+ | Дальше создаётся PVC. storageClassName должен соответствовать имени storageClass (список - '' | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: PersistentVolumeClaim | ||
+ | metadata: | ||
+ | name: nfs-pvc-test | ||
+ | spec: | ||
+ | storageClassName: | ||
+ | accessModes: | ||
+ | - ReadWriteMany # must be the same as PersistentVolume | ||
+ | resources: | ||
+ | requests: | ||
+ | storage: 50Mi | ||
+ | </ | ||
+ | После применения создаётся PVC с соответствующим storageClassName. | ||
+ | |||
+ | Чтобы постоянно не указывать в манифесте PVC storageClassName, | ||
+ | Если storageClass по умолчанию не указан и применён манифест PVC, где нет storageClassName, | ||
+ | No persistent volumes available for this claim and no storage class is set | ||
+ | |||
+ | Применяем патч, указывающий, | ||
+ | <code bash> | ||
+ | kubectl patch storageclass nfs-client -p ' | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | После применения патча PVC без storageClassName будут успешно привязаны. | ||
+ | <code bash> | ||
+ | kubectl get pvc | ||
+ | NAME | ||
+ | another-nfs-pvc-test | ||
+ | </ | ||
+ | |||
+ | ===== Configmaps/ | ||
+ | |||
+ | Configmap и secret по сути одно и то же, только секрет зашифрован в base64. Configmap используется для хранения переменных окружения, | ||
+ | |||
+ | <code bash> | ||
+ | echo Hello world > index.html | ||
+ | kubectl create cm webindex --from-file=index.html | ||
+ | kubectl describe cm webindex # видно, что есть ключ index.html и значение Hello world. | ||
+ | kubectl create deployment webserver --image=nginx | ||
+ | kubectl edit deploy webserver | ||
+ | </ | ||
+ | |||
+ | spec.template.spec - добавить раздел volumes: и внутри containers: сделать volumeMounts: | ||
+ | |||
+ | <code yaml> | ||
+ | spec: | ||
+ | containers: | ||
+ | - image: nginx | ||
+ | imagePullPolicy: | ||
+ | name: nginx | ||
+ | resources: {} | ||
+ | terminationMessagePath: | ||
+ | terminationMessagePolicy: | ||
+ | volumeMounts: | ||
+ | - mountPath: / | ||
+ | name: cmvol | ||
+ | volumes: | ||
+ | - name: cmvol | ||
+ | configMap: | ||
+ | name: webindex | ||
+ | </ | ||
+ | |||
+ | Именование важно: имя volumeMount ссылается на имя тома, а тот, в свою очередь, | ||
+ | <code bash> | ||
+ | kubectl exec webserver-76d44586d-l7wzz -- cat / | ||
+ | Hello world | ||
+ | </ | ||
+ | |||
+ | ===== Networking ===== | ||
+ | |||
+ | В k8s сетевое взаимодействие работает на несольких уровнях: | ||
+ | * Между контейнерами - IPC (межпроцессное взаимодействие), | ||
+ | * Между подами - через сетевой плагин. | ||
+ | * Между подами и сервисами - через ресурсы сервиса. | ||
+ | * Между внешними пользователями и сервисами - с помощью сервисов и Ingress. | ||
+ | |||
+ | Ноды подключены к внешней сети и также имеют виртуальную кластерную сеть. В кластерной сети работают сервисы, | ||
+ | Для внешнего пользователя получить доступ к кластеру можно двумя способами: | ||
+ | - Обращение через DNS на внешний балансировщик, | ||
+ | - Обращение на Ingress. Это ресурс кластера для внешних подключений, | ||
+ | |||
+ | Сетевой плагин нужен для обеспечения связи между подами. Его необходимо ставить отдельно, | ||
+ | |||
+ | ===== Services ===== | ||
+ | |||
+ | Сервисы обеспечивают доступ к подам. Если за сервисом несколько подов то сервис будет балансировать трафик между подами. Есть несколько типов сервисов: | ||
+ | * ClusterIP - сервис доступен только внутри кластерной виртуальной сети | ||
+ | * NodePort - сервис доступен на определённом порту всех кластерных нод | ||
+ | * LoadBalancer - этот вариант доступен у облачных провайдеров. Обеспечивает доступ к ClusterIP- и NodePort-сервисам. | ||
+ | * ExternalName - сервис привязывается ко внешнему DNS-псевдониму (DNS CNAME record). | ||
+ | |||
+ | Команда '' | ||
+ | Команда '' | ||
+ | |||
+ | <code bash> | ||
+ | kubectl create deploy webshop --image=nginx --replicas=3 | ||
+ | kubectl expose deploy webshop --type=NodePort --port=80 | ||
+ | kubectl describe svc webshop | ||
+ | ... | ||
+ | Selector: | ||
+ | </ | ||
+ | |||
+ | С помощью селектора сервис подключается к одноимённым ярлыкам подов (Labels: app=workshop). | ||
+ | Теперь приложение доступно по адресу любой ноды (в т. ч. управляющей) + порт (ip: | ||
+ | |||
+ | ===== Ingress controller ===== | ||
+ | |||
+ | Ингресс предоставляет внешний доступ к сервисам кластера. Работает с внешним DNS, чтобы доступ был по URL. Состоит из 2 частей: | ||
+ | Ингресс работает только с HTTP/HTTPS. Использует селектор в сервисах, | ||
+ | |||
+ | Трафик управляется согласно правилам, | ||
+ | - Сделать сервисы доступными по внешним URL | ||
+ | - Балансировка трафика | ||
+ | - Терминирование SSL/TLS | ||
+ | - Offer name based virtual hosting | ||
+ | |||
+ | <code bash> | ||
+ | helm upgrade --install ingress-nginx ingress-nginx --repo https:// | ||
+ | kubectl get pods -n ingress-nginx | ||
+ | kubectl create deploy nginxsvc --image=nginx --port=80 | ||
+ | kubectl expose deploy nginxsvc | ||
+ | |||
+ | # Создать правило ингресса: | ||
+ | kubectl create ingress nginxsvc --class=nginx --rule=nginxsvc.ru/ | ||
+ | </ | ||
+ | |||
+ | Если посмотреть внутрь созданного ингресса (kubectl describe ingress nginxsvc), то там не будет селектора - ингресс работает через правило.\\ Селектор играет роль в сервисе, | ||
+ | |||
+ | Узнать порт доступа к ингрессу извне: | ||
+ | <code bash> | ||
+ | kubectl get service -n ingress-nginx ingress-nginx-controller | ||
+ | NAME | ||
+ | ingress-nginx-controller | ||
+ | </ | ||
+ | Теперь после заведения DNS-записи можно обращаться к http:// | ||
+ | |||
+ | ===== Configuring Ingress ===== | ||
+ | <code bash> | ||
+ | # Справка с примерами | ||
+ | kubectl create ingress -h |less | ||
+ | # Можно задать несколько правил для одного хоста | ||
+ | kubectl create ingress mygress --rule="/ | ||
+ | # Точно так же можно задать несколько виртуальных хостов | ||
+ | kubectl create ingress nginxsvc --class=nginx --rule=nginxsvc.info/ | ||
+ | </ | ||
+ | |||
+ | ==== IngressClass ==== | ||
+ | В одном кластере могут находиться разные ингресс-контроллеры каждый со своей конфигурацией. | ||
+ | Контроллер задаётся в IngressClass. | ||
+ | При создании ингресс-правил опция '' | ||
+ | Если '' | ||
+ | <code bash> | ||
+ | # После создания ингресс-контроллера создаётся API-ресурс IngressClass, | ||
+ | kubectl get ingressclass -o yaml | ||
+ | |||
+ | # Отредактировать уже существующий ингресс-класс nginx, сделав его по умолчанию: | ||
+ | kubectl edit ingressclass nginx | ||
+ | </ | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | items: | ||
+ | - apiVersion: networking.k8s.io/ | ||
+ | kind: IngressClass | ||
+ | metadata: | ||
+ | annotations: | ||
+ | ingressclass.kubernetes.io/ | ||
+ | meta.helm.sh/ | ||
+ | meta.helm.sh/ | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | ===== Port forwarding ===== | ||
+ | |||
+ | Нужен для тестирования доступа к приложению без возни с сервисами и ингрессом. | ||
+ | <code bash> | ||
+ | # Редирект с 1234 на 80 | ||
+ | kubectl port-forward mypod 1234:80 | ||
+ | </ | ||
+ | По умолчанию запускается на переднем плане, поэтому чтобы освободить консоль, | ||
+ | Например | ||
+ | <code bash> | ||
+ | kubectl port-forward mypod 1234:80 & | ||
+ | curl localhost: | ||
+ | </ | ||
+ | |||
+ | ===== Lab 5 ===== | ||
+ | <code bash> | ||
+ | kubectl create deploy apples --image=nginx --replicas=3 | ||
+ | kubectl expose deploy apples --port=80 | ||
+ | kubectl create ingress apples --class=nginx --rule=my.fruit/ | ||
+ | kubectl get svc -n ingress-nginx # выяснить порт | ||
+ | echo " | ||
+ | curl my.fruit: | ||
+ | </ | ||
+ | |||
+ | ===== Cluster nodes ===== | ||
+ | <code bash> | ||
+ | # Информация по кублету | ||
+ | systemctl status kubelet | ||
+ | # Также см. /var/log или journalctl | ||
+ | # Основная информация о ноде | ||
+ | kubectl describe node $(hostname) | ||
+ | # Если установлен сервер метрик, | ||
+ | kubectl top nodes | ||
+ | </ | ||
+ | |||
+ | crictl - утилита для получения информации о контейнерах (вместо docker/ | ||
+ | Чтобы использовать её, нужно задать runtime-endpoint и image-endpoint. Самый удобный способ - на нодах, где планируется использование crictl, отредактировать файл ''/ | ||
+ | |||
+ | Всё это на конкретной ноде. Без sudo не работает, | ||
+ | <code bash> | ||
+ | # Выдать список контейнеров | ||
+ | sudo crictl ps | ||
+ | # Выдать список подов | ||
+ | sudo crictl pods | ||
+ | # В списке контейнеров имена неуникальные. Если что, то надо смотреть детальную информацию по контейнеру по его ID | ||
+ | sudo crictl inspect 82e883gf8v8re | ||
+ | # Список образов | ||
+ | sudo crictl images | ||
+ | # Скачать образ mysql с docker.io | ||
+ | sudo crictl pull docker.io/ | ||
+ | </ | ||
+ | Crictl - это только средство для контейнеров, | ||
+ | |||
+ | ===== Static pods ===== | ||
+ | |||
+ | Это поды, запущенные непосредственно процессом kubelet на ноде. Таким образом работают основные сервисы Кубера. Админ может добавить такой под, скопировав манифест в каталог ''/ | ||
+ | <code bash> | ||
+ | sudo systemctl restart kubelet | ||
+ | </ | ||
+ | Никогда не нужно делать это на управляющей/ | ||
+ | |||
+ | <code bash> | ||
+ | # На контрольной ноде генерим манифест пода | ||
+ | kubectl run staticpod --image=nginx --dry-run=client -o yaml > staticpod.yaml | ||
+ | # Вывести содержимое и затем скопировать в буфер | ||
+ | cat staticpod.yaml | ||
+ | # На РАБОЧЕЙ ноде, открыть новый файл и вставить туда содержимое | ||
+ | sudo nano / | ||
+ | # На контрольной ноде, если вывести список подов, то новый статический под будет показываться c именем ноды-хозяина в конце имени. | ||
+ | kubectl get po | ||
+ | NAME | ||
+ | staticpod-worker1 | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | ===== Managing node state ===== | ||
+ | <code bash> | ||
+ | kubectl cordon # больше не принимать нагрузку | ||
+ | kubectl drain # больше не принимать нагрузку и убрать все поды | ||
+ | --ignore-daemonsets # и демонсеты тоже | ||
+ | --delete-emptydir-data # удалить данные из томов emptyDir | ||
+ | kubectl uncordon # вернуть ноду в список доступных для распределения нагрузки | ||
+ | </ | ||
+ | cordon/ | ||
+ | |||
+ | Поды автоматически после возвращения ноды не распределяются. | ||
+ | |||
+ | ===== Managing node services ===== | ||
+ | |||
+ | На рабочей ноде это кублет и контейнерный движок, | ||
+ | |||
+ | <code bash> | ||
+ | # Состояние кублета\старт\стоп | ||
+ | sudo systemctl status\start\stop kubelet | ||
+ | # См. процесс кублета | ||
+ | ps aux |grep kubelet | ||
+ | # Процессы контейнерного движка | ||
+ | ps aux |grep containerd | ||
+ | </ | ||
+ | |||
+ | Если убить (kill) процесс kubelet, то он опять запустится, | ||
+ | <code bash> | ||
+ | systemctl cat kubelet.service | ||
+ | ... | ||
+ | [Service] | ||
+ | ExecStart=/ | ||
+ | Restart=always | ||
+ | StartLimitInterval=0 | ||
+ | RestartSec=10 | ||
+ | ... | ||
+ | </ | ||
+ | Если просто остановить сервис (sudo systemctl stop kubelet), то он сам перезапускаться не будет. | ||
+ | |||
+ | ===== Metrics server ===== | ||
+ | |||
+ | Сервер метрик нужен для мониторинга нод и состояния подов. Его нужно установить отдельно.\\ | ||
+ | https:// | ||
+ | <code bash> | ||
+ | # Установка (https:// | ||
+ | kubectl apply -f https:// | ||
+ | # Вывести поды системного пространства имён, там под сервера метрик не может запуститься | ||
+ | kubectl -n kube-system get po | ||
+ | NAME | ||
+ | metrics-server-6db4d75b97-5lslb | ||
+ | |||
+ | # Проверяем логи | ||
+ | kubectl logs -n kube-system metrics-server-6db4d75b97-5lslb | ||
+ | # Везде ругань на сертификат. | ||
+ | |||
+ | # Редактируем деплой | ||
+ | kubectl -n kube-system edit deploy metrics-server | ||
+ | </ | ||
+ | |||
+ | Там в spec.template добавить --kubelet-insecure-tls | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | containers: | ||
+ | - args: | ||
+ | - --cert-dir=/ | ||
+ | - --secure-port=10250 | ||
+ | - --kubelet-preferred-address-types=InternalIP, | ||
+ | - --kubelet-insecure-tls | ||
+ | - --kubelet-use-node-status-port | ||
+ | - --metric-resolution=15s | ||
+ | </ | ||
+ | <code bash> | ||
+ | # После этого сервер метрик начинает работать и можно проверить ресурсы | ||
+ | kubectl top pods | ||
+ | NAME | ||
+ | nfs-subdir-external-provisioner-59cb575ccc-gcxr4 | ||
+ | nginxsvc-5f8b7d4f4d-fwnnj | ||
+ | webserver-76d44586d-l7wzz | ||
+ | </ | ||
+ | |||
+ | ===== ETCD backup ===== | ||
+ | |||
+ | Etcd работает как статический под на мастер-ноде, | ||
+ | <code bash> | ||
+ | sudo apt install etcd-client | ||
+ | # Справка (старая версия API) | ||
+ | sudo etcdctl -h | ||
+ | # Если запустить c ETCDCTL_API=3 (который и нужен), | ||
+ | sudo ETCDCTL_API=3 etcdctl -h | ||
+ | # Процессы apiserver и etcd, подробности (путь к сертификатам и ключу, это нужно в дальнейшем) | ||
+ | ps aus |grep etcd | ||
+ | # Проверка работоспособности etcdctl, выводит ключи | ||
+ | sudo ETCDCTL_API=3 etcdctl --endpoints=localhost: | ||
+ | # Бэкап | ||
+ | sudo ETCDCTL_API=3 etcdctl --endpoints=localhost: | ||
+ | # Статус резервной копии (выведет табличку со сводкой по резервной копии) | ||
+ | sudo ETCDCTL_API=3 etcdctl --write-out=table snapshot status / | ||
+ | </ | ||
+ | Очень желательно бэкап держать в нескольких местах для надёжности. | ||
+ | |||
+ | ===== ETCD restore ===== | ||
+ | <code bash> | ||
+ | # Можно вытереть пару деплоев, | ||
+ | kubectl delete deploy apples | ||
+ | kubectl delete deploy webshop | ||
+ | # Переместить манифесты статических подов - это остановит их (через какое-то время). | ||
+ | cd / | ||
+ | sudo mv * .. | ||
+ | # Проверить отсутствие пода etcd | ||
+ | sudo crictl ps | ||
+ | # Восстановить базу в другой каталог (он создастся автоматически) | ||
+ | sudo ETCDCTL_API=3 etcdctl snapshot restore / | ||
+ | # Отредактировать конфиг, | ||
+ | sudo nano / | ||
+ | # Вернуть манифесты на место | ||
+ | sudo mv ../*yaml . | ||
+ | # Проверить наличие пода etcd | ||
+ | sudo crictl ps | ||
+ | # Проверить, | ||
+ | kubectl get deploy | ||
+ | </ | ||
+ | |||
+ | ===== Cluster node upgrades ===== | ||
+ | |||
+ | Пропускать минорные версии нельзя (1.23 нельзя обновить на 1.25, только на 1.24).\\ | ||
+ | Порядок обновления: | ||
+ | https:// | ||
+ | |||
+ | Если репозитории старые (apt.kubernetes.io или yum.kubernetes.io), | ||
+ | <code bash> | ||
+ | v=' | ||
+ | echo "deb [signed-by=/ | ||
+ | curl -fsSL https:// | ||
+ | sudo apt-get update | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Find the latest patch release for Kubernetes 1.29 using the OS package manager: | ||
+ | sudo apt-cache madison kubeadm | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | # Парсер вывода apt-cache madison kubeadm (выводит 1.29.2-*) | ||
+ | ver=$(apt-cache madison kubeadm |head -1 |cut -d ' | ||
+ | |||
+ | # replace x in 1.29.x-* with the latest patch version | ||
+ | sudo apt-mark unhold kubeadm && \ | ||
+ | sudo apt-get update && sudo apt-get install -y kubeadm=" | ||
+ | sudo apt-mark hold kubeadm | ||
+ | |||
+ | # Verify that the download works and has the expected version: | ||
+ | kubeadm version | ||
+ | kubeadm version: & | ||
+ | |||
+ | # Verify the upgrade plan: | ||
+ | sudo kubeadm upgrade plan | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | <code bash> | ||
+ | # trigger a graceful kube-apiserver shutdown | ||
+ | sudo killall -s SIGTERM kube-apiserver | ||
+ | # wait a little bit to permit completing in-flight requests | ||
+ | sleep 20 | ||
+ | # execute a kubeadm upgrade command | ||
+ | sudo kubeadm upgrade apply v1.29.2 | ||
+ | |||
+ | # draining | ||
+ | kubectl drain $(hostname) --ignore-daemonsets | ||
+ | # Upgrade the kubelet and kubectl: | ||
+ | sudo apt-mark unhold kubelet kubectl && \ | ||
+ | sudo apt-get update && sudo apt-get install -y kubelet=" | ||
+ | sudo apt-mark hold kubelet kubectl | ||
+ | # Restart the kubelet: | ||
+ | sudo systemctl daemon-reload | ||
+ | sudo systemctl restart kubelet | ||
+ | # Uncordon the node | ||
+ | kubectl uncordon $(hostname) | ||
+ | </ | ||
+ | |||
+ | ==== Upgrade worker nodes ==== | ||
+ | <code bash> | ||
+ | sudo apt-mark unhold kubeadm && \ | ||
+ | sudo apt-get update && sudo apt-get install -y kubeadm=" | ||
+ | sudo apt-mark hold kubeadm | ||
+ | # upgrade the local kubelet configuration | ||
+ | sudo kubeadm upgrade node | ||
+ | # На мастер-ноде: | ||
+ | kubectl drain k3 --ignore-daemonsets --delete-emptydir-data | ||
+ | # На рабочей ноде Upgrade the kubelet and kubectl: | ||
+ | sudo apt-mark unhold kubelet kubectl && \ | ||
+ | sudo apt-get update && sudo apt-get install -y kubelet=" | ||
+ | sudo apt-mark hold kubelet kubectl | ||
+ | # Restart the kubelet: | ||
+ | sudo systemctl daemon-reload | ||
+ | sudo systemctl restart kubelet | ||
+ | # На мастер-ноде: | ||
+ | kubectl uncordon k3 | ||
+ | </ | ||
+ | |||
+ | === Выжимка по апгрейду === | ||
+ | :!: Здесь не учитывается, | ||
+ | <code bash> | ||
+ | sudo -i | ||
+ | |||
+ | # желаемая версия | ||
+ | v=' | ||
+ | |||
+ | echo "deb [signed-by=/ | ||
+ | curl -fsSL https:// | ||
+ | apt update | ||
+ | |||
+ | ver=$(apt-cache madison kubeadm |head -1 |cut -d ' | ||
+ | |||
+ | apt-mark unhold kubeadm && \ | ||
+ | apt-get install -y kubeadm=" | ||
+ | apt-mark hold kubeadm | ||
+ | |||
+ | ################################# | ||
+ | kubeadm upgrade plan # для мастера, | ||
+ | kubeadm upgrade node # для рабочих нод | ||
+ | ################################# | ||
+ | |||
+ | apt-mark unhold kubelet kubectl && \ | ||
+ | apt-get install -y kubelet=" | ||
+ | apt-mark hold kubelet kubectl | ||
+ | |||
+ | systemctl daemon-reload | ||
+ | systemctl restart kubelet | ||
+ | </ | ||
+ | |||
+ | ===== Highly available cluster ===== | ||
+ | |||
+ | 2 варианта: | ||
+ | - Stacked control plane nodes - когда control plane и etcd работают на одной ноде. Оптимально иметь 3 и более узлов. | ||
+ | - External etcd cluster - etcd в отдельном кластере. Нужно больше узлов. | ||
+ | Для отказоустойчивого кластера нужен балансировщик, | ||
+ | |||
+ | Схема примерно такая: на каждой мастер-ноде стоит | ||
+ | * API-сервер: | ||
+ | * haproxy: | ||
+ | * keepalived c VIP:8443, который балансирует запросы к мастер-нодам. Клиент kubectl обращается к VIP keepalived. | ||
+ | |||
+ | <code bash> | ||
+ | # kubeadm во время развёртывания кластера нужно настраивать на VIP:8443. | ||
+ | sudo kubeadm init --control-plane-endpoint " | ||
+ | # Установить сетевой плагин | ||
+ | kubectl apply -f https:// | ||
+ | # Подключить остальные мастер-ноды (с ключом --control-plane) | ||
+ | </ | ||
+ | На всех нодах скопировать / | ||
+ | Подключить рабочие ноды. | ||
+ | |||
+ | Для клиента ''/ | ||
+ | |||
+ | ===== Scheduler ===== | ||
+ | |||
+ | Планировщик отвечает за размещение подов на рабочих узлах. | ||
+ | Узлы отбираются по критериям, | ||
+ | * Требования к ресурсам | ||
+ | * Affinity/ | ||
+ | * Taints/ | ||
+ | Планировщик перед размещением находит узлы-кандидаты и оценивает их, затем подбирает узел с наивысшей оценкой. | ||
+ | Дальше планировщик начинает привязку, | ||
+ | |||
+ | ===== 8.2 Node selector ===== | ||
+ | |||
+ | nodeSelector - поле в pod.spec, которое указывает на соответствующий ярлык на узле и запускает под там. | ||
+ | <code bash> | ||
+ | # Задать ярлык на ноде | ||
+ | kubectl label nodes worker1 disktype=ssd | ||
+ | # Удалить ярлык | ||
+ | kubectl label nodes worker1 disktype- | ||
+ | </ | ||
+ | |||
+ | Пример манифеста пода | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: nginx | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx | ||
+ | imagePullPolicy: | ||
+ | nodeSelector: | ||
+ | disktype: ssd | ||
+ | </ | ||
+ | |||
+ | В pod.spec есть ещё и nodeName, где можно задать конкретное имя узла, на котором всегда будет запускаться этот под. Но так не рекомендуется делать - если узел выйдет из строя, то под не запустится больше нигде. | ||
+ | |||
+ | ===== 8.3 Affinity ===== | ||
+ | |||
+ | Более продвинутые правила размещения подов. | ||
+ | * Node affinity - правило на узле, которое размещает там поды с совпадающими ярлыками. | ||
+ | * Inter-pod affinity - правило, | ||
+ | * Anti-affinity - применимо только к подам, не к узлам. | ||
+ | |||
+ | Т. е., | ||
+ | - Под с node-affinity-ярлыком key=value будет размещён только на узлах, имеющих совпадающий ярлык. | ||
+ | - Под с pod-affinity-ярлыком key=value будет размещён только на узлах, имеющих поды с совпадающим ярлыком. | ||
+ | |||
+ | Есть 2 режима работы affinity: | ||
+ | - requiredDuringSchedulingIgnoredDuringExecution - требование соответствия ограничению | ||
+ | - preferredDuringSchedulingIgnoredDuringExecution - предпочтение соответствия ограничению (если не может быть удовлетворено - игнорируется) | ||
+ | |||
+ | Affinity применяется только к планируемым подам, не к работающим. Те, которые уже работают, | ||
+ | |||
+ | Affinity - это не просто соответствие ярлыков, | ||
+ | Здесь выбираются ноды, где тип равен blue или green. | ||
+ | |||
+ | <code yaml> | ||
+ | spec: | ||
+ | affinity: | ||
+ | podAffinity: | ||
+ | requiredDuringSchedulingIgnoredDuringExecution: | ||
+ | nodeSelectorTerms: | ||
+ | - matchExpressions: | ||
+ | - key: type | ||
+ | operator: In | ||
+ | values: | ||
+ | - blue | ||
+ | - green | ||
+ | </ | ||
+ | |||
+ | Здесь - ноды, где задан ключ storage. | ||
+ | <code yaml> | ||
+ | nodeSelectorTerms: | ||
+ | - matchExpressions: | ||
+ | - key: storage | ||
+ | operator: Exists | ||
+ | </ | ||
+ | |||
+ | При использовании pod affinity/ | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | affinity: | ||
+ | podAffinity: | ||
+ | requiredDuringSchedulingIgnoredDuringExecution: | ||
+ | - labelSelector: | ||
+ | matchExpressions: | ||
+ | - key: security | ||
+ | operator: In | ||
+ | values: | ||
+ | - S1 | ||
+ | topologyKey: | ||
+ | </ | ||
+ | |||
+ | topologyKey ссылается на ярлык, который прописан на ноде. Обычно он имеет наклонную черту, например, | ||
+ | topologyKey разрешает запускать поды только на узлах, имеющих этот ключ. Это позволяет создавать сегментировать кластер на зоны, где запускается нагрузка. Если topologyKey не найден на ноде, то он игнорируется в настройках pod affinity. | ||
+ | |||
+ | redis-with-pod-affinity.yaml - интересный пример. В манифесте написано не запускать под, если узел уже имеет под с ярлыком app=store, вместе с тем, под сам имеет этот ярлык. Это приведёт к тому, что на одной ноде будет запущен только один под, т. к. второй уже не уживётся с таким же экземпляром на одном узле. | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: redis-cache | ||
+ | spec: | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: store | ||
+ | replicas: 3 | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: store | ||
+ | spec: | ||
+ | affinity: | ||
+ | podAntiAffinity: | ||
+ | requiredDuringSchedulingIgnoredDuringExecution: | ||
+ | - labelSelector: | ||
+ | matchExpressions: | ||
+ | - key: app | ||
+ | operator: In | ||
+ | values: | ||
+ | - store | ||
+ | topologyKey: | ||
+ | containers: | ||
+ | - name: redis-server | ||
+ | image: redis: | ||
+ | </ | ||
+ | |||
+ | web-with-pod-affinity.yaml - то же самое: один web-server не будет работать с другим на одной и той же ноде, но зато будет запускаться только там, где есть под с ярлыком app=store, т. е. с redis-cache. | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: web-server | ||
+ | spec: | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: web-store | ||
+ | replicas: 3 | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: web-store | ||
+ | spec: | ||
+ | affinity: | ||
+ | podAntiAffinity: | ||
+ | requiredDuringSchedulingIgnoredDuringExecution: | ||
+ | - labelSelector: | ||
+ | matchExpressions: | ||
+ | - key: app | ||
+ | operator: In | ||
+ | values: | ||
+ | - web-store | ||
+ | topologyKey: | ||
+ | podAffinity: | ||
+ | requiredDuringSchedulingIgnoredDuringExecution: | ||
+ | - labelSelector: | ||
+ | matchExpressions: | ||
+ | - key: app | ||
+ | operator: In | ||
+ | values: | ||
+ | - store | ||
+ | topologyKey: | ||
+ | containers: | ||
+ | - name: web-app | ||
+ | image: nginx: | ||
+ | </ | ||
+ | |||
+ | ===== 8.4 Taints / tolerations ===== | ||
+ | |||
+ | Taint применяется к узлам. Это метка, которая препятствует запуску любых подов, если у них нет соответствующего toleration.\\ | ||
+ | Toleration применяется к подам. Это метка, которая позволяет (но не предписывает!) поду запуститься на узле с соответствующим taint.\\ | ||
+ | Если affinity применяется для того, чтобы привязать поды к конкретным узлам, то taint - чтобы исключить их запуск на узлах.\\ | ||
+ | Taints и tolerations гарантируют, | ||
+ | |||
+ | Есть 3 типа taints: | ||
+ | - NoSchedule: не размещать новые поды | ||
+ | - PreferNoSchedule: | ||
+ | - NoExecute - не размещать и выгнать уже существующие | ||
+ | |||
+ | Taints задаются разными способами.\\ | ||
+ | На контрольных нодах тейнт задан по умолчанию, | ||
+ | Команды kubectl drain и kubectl cordon создают тейнт на целевом узле.\\ | ||
+ | Тейнт может быть установлен автоматически кластером в определённых условиях, | ||
+ | - нехватки места на ноде | ||
+ | - нехватки памяти | ||
+ | - нехватки доступных ID процессов | ||
+ | - невозможность для планирования | ||
+ | - недоступность по сети | ||
+ | |||
+ | И эти автоматические тейнты могут быть преодолены соответствующими толерами, | ||
+ | |||
+ | <code bash> | ||
+ | # Тейнт может быть задан админом: | ||
+ | kubectl taint nodes worker1 key1=value1: | ||
+ | # Удалить тейнт: | ||
+ | kubectl taint nodes worker1 key1=value1: | ||
+ | </ | ||
+ | |||
+ | Толер необходим для запуска подов на узле с тейнтом, | ||
+ | Ключ и значение у тейнтов и толеров позволяют задавать тонкие настройки запуска. Например, | ||
+ | <code bash> | ||
+ | kubectl taint nodes worker1 storage=ssd: | ||
+ | </ | ||
+ | позволит запускаться на узле worker1 только тем подам, у которых есть толер storage=ssd. | ||
+ | Если у пода есть толер storage=hdd, | ||
+ | |||
+ | Толер в манифесте пода задаётся ключом, | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | tolerations: | ||
+ | - key: " | ||
+ | operator: " | ||
+ | value: " | ||
+ | </ | ||
+ | Equal - оператор по умолчанию. Также часто используется '' | ||
+ | |||
+ | ===== 8.6 Limitrange / quota ===== | ||
+ | |||
+ | LimitRange - API-объект, | ||
+ | - type: к чему применяется - к подам или контейнерам | ||
+ | - defaultRequest: | ||
+ | - default: максимум ресурсов, | ||
+ | |||
+ | Quota - API-объект, | ||
+ | |||
+ | Т. е., LimitRange задаёт лимиты для каждого приложения, | ||
+ | <code bash> | ||
+ | kubectl explain limitrange.spec.limits | ||
+ | kubectl create ns limited | ||
+ | nano limitrange.yaml | ||
+ | kubectl apply -f limitrange.yaml -n limited | ||
+ | kubectl describe ns limited | ||
+ | nano limitedpod.yaml | ||
+ | kubectl run limited --image=nginx -n limited | ||
+ | </ | ||
+ | |||
+ | ===== 9.1 CNI & Network plugins ===== | ||
+ | |||
+ | Container Network Interface (CNI) - стандартный сетевой интерфейс, | ||
+ | Конфиг CNI находится в ''/ | ||
+ | Остальные плагины имеют стандартные настройки и используют доп. конфигурацию, | ||
+ | Доки по CNI: https:// | ||
+ | |||
+ | Внутренняя сеть Кубера состоит из 2 частей: | ||
+ | В случае с calico его ресурсы находятся в системном пространстве имён (работает как DaemonSet) | ||
+ | <code bash> | ||
+ | kubectl get all -n kube-system | ||
+ | </ | ||
+ | IP-диапазон для кластерной сети (см. '' | ||
+ | <code bash> | ||
+ | ps aux |grep api | ||
+ | </ | ||
+ | |||
+ | ===== 9.2 Service auto registration ===== | ||
+ | |||
+ | Кубер запускает поды coredns в системном пространстве имён как внутренние DNS-сервера.\\ | ||
+ | Эти поды опубликованы через сервис kube-dns и любой под в Кубере может обратиться к coredns.\\ | ||
+ | Поды автоматически настраиваются с IP-адресом сервиса kube-dns в качестве своего DNS-сервера.\\ | ||
+ | В результате, | ||
+ | |||
+ | Если обращение идёт к сервису в том же пространстве имён, можно обращаться к нему по короткому имени.\\ | ||
+ | Если в другом - то по FQDN ('' | ||
+ | Имя кластера по умолчанию - cluster.local, | ||
+ | Проверить можно | ||
+ | <code bash> | ||
+ | kubectl get cm -n kube-system coredns -o yaml | ||
+ | </ | ||
+ | |||
+ | Тест | ||
+ | <code bash> | ||
+ | kubectl run webserver --image=nginx | ||
+ | kubectl expose pod webserver --port=80 | ||
+ | kubectl create ns remote | ||
+ | kubectl run remotebox --image=busybox -n remote -- sleep 3600 | ||
+ | kubectl exec remotebox -n remote --nslookup webserver # can't find | ||
+ | kubectl exec remotebox -n remote --nslookup webserver.default.svc.cluster.local # works | ||
+ | </ | ||
+ | |||
+ | ===== 9.3 Network policies ===== | ||
+ | |||
+ | Изначально между подами нет сетевых ограничений и они могут свободно общаться даже если находятся в разных пространствах имён. Чтобы регулировать трафик, | ||
+ | Если политика есть и правило не совпадает, | ||
+ | Если политики нет, трафик разрешается. | ||
+ | |||
+ | В сетевой политике есть 3 разных идентификатора: | ||
+ | - Поды (podSelector): | ||
+ | - Пространства имён (namespaceSelector): | ||
+ | - Диапазон IP (ipBlock): доступ к блоку IP-адресов. | ||
+ | Когда используется сетевая политика с podSelector или namespaceSelector, | ||
+ | Если есть несколько неконфликтующих сетевых политик, | ||
+ | |||
+ | Пример https:// | ||
+ | Политика access-nginx применяется к подам, у которых есть ярлык app=nginx. Доступ к этим подам будет разрешён только тем подам, у которых есть ярлык access=true. Дальше описаны два пода - nginx и busybox. В этом манифесте у busybox доступа к nginx не будет. | ||
+ | <code yaml> | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | kind: NetworkPolicy | ||
+ | metadata: | ||
+ | name: access-nginx | ||
+ | spec: | ||
+ | podSelector: | ||
+ | matchLabels: | ||
+ | app: nginx | ||
+ | ingress: | ||
+ | - from: | ||
+ | - podSelector: | ||
+ | matchLabels: | ||
+ | access: " | ||
+ | ... | ||
+ | |||
+ | --- | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: nginx | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nwp-nginx | ||
+ | image: nginx:1.17 | ||
+ | ... | ||
+ | |||
+ | --- | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: busybox | ||
+ | labels: | ||
+ | app: sleepy | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nwp-busybox | ||
+ | image: busybox | ||
+ | command: | ||
+ | - sleep | ||
+ | - " | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Публикуем под nginx | ||
+ | kubectl expose pod nginx --port=80 | ||
+ | # Проверка доступа (будет time out, т. к. доступа нет) | ||
+ | kubectl exec busybox -- wget --spider --timeout=1 nginx | ||
+ | # Прописываем ярлык access=true для busybox, теперь предыдущая команда выполнится успешно | ||
+ | kubectl label pod busybox access=true | ||
+ | |||
+ | # См. детали политики | ||
+ | kubectl describe networkpolicy access-nginx | ||
+ | # "Not affecting egress traffic" | ||
+ | </ | ||
+ | |||
+ | ===== 9.4 Network policies (namespaces) ===== | ||
+ | |||
+ | Network policy - это РАЗРЕШЕНИЕ трафика, | ||
+ | Пример: | ||
+ | Разрешить любые поды в пространстве имён default.\\ | ||
+ | Следовательно, | ||
+ | <code yaml> | ||
+ | kind: NetworkPolicy | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | metadata: | ||
+ | namespace: default | ||
+ | name: deny-from-other-namespaces | ||
+ | spec: | ||
+ | podSelector: | ||
+ | matchLabels: | ||
+ | ingress: | ||
+ | - from: | ||
+ | - podSelector: | ||
+ | </ | ||
+ | Теперь, | ||
+ | <code bash> | ||
+ | kubectl create ns restricted | ||
+ | kubectl run lab9server --image=nginx -n restricted | ||
+ | kubectl expose po lab9server --port=80 -n restricted | ||
+ | kubectl run sleepybox1 --image=busybox -- sleep 3600 | ||
+ | kubectl run sleepybox2 --image=busybox -- sleep 3600 | ||
+ | kubectl exec sleepybox1 -- wget --spider --timeout=1 lab9server.restricted.svc.cluster.local # работает | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | Применить политику: | ||
+ | <code yaml> | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | kind: NetworkPolicy | ||
+ | metadata: | ||
+ | name: mynp | ||
+ | namespace: restricted | ||
+ | spec: | ||
+ | podSelector: | ||
+ | matchLabels: | ||
+ | ingress: | ||
+ | - from: | ||
+ | - namespaceSelector: | ||
+ | matchLabels: | ||
+ | kubernetes.io/ | ||
+ | podSelector: | ||
+ | matchLabels: | ||
+ | access: " | ||
+ | </ | ||
+ | <code bash> | ||
+ | kubectl exec sleepybox1 -- wget --spider --timeout=1 lab9server.restricted.svc.cluster.local # не работает | ||
+ | kubectl label po sleepybox1 access=yes | ||
+ | kubectl exec sleepybox1 -- wget --spider --timeout=1 lab9server.restricted.svc.cluster.local # работает (что и требуется) | ||
+ | kubectl exec sleepybox2 -- wget --spider --timeout=1 lab9server.restricted.svc.cluster.local # не работает (что и требуется) | ||
+ | </ | ||
+ | |||
+ | ===== 10.1 API access ===== | ||
+ | |||
+ | Прямого доступа к ETCD нет, доступ осуществляется через kube-apiserver. | ||
+ | Права доступа описываются в Roles и clusterRoles. | ||
+ | |||
+ | Есть 2 способа доступа к API: | ||
+ | - kubectl. Это клиентская программа, | ||
+ | - Приложения. К примеру, | ||
+ | |||
+ | Итак, kubectl/pod обращаются к API и дальше им нужен доступ к ETCD, для этого надо подключиться к Role или clusterRole для определения полномочий. | ||
+ | roleBinding и clusterRoleBinding это привязки к Role и clusterRole для kubectl/pod (типа PVC для PV). | ||
+ | |||
+ | ===== 10.2 Security context ===== | ||
+ | |||
+ | Определяет права и настройки доступа для подов и контейнеров. Включает в себя следующее: | ||
+ | * UID/ | ||
+ | * SELinux security labels | ||
+ | * Linux capabilities (доступ к спецфункциям) | ||
+ | * AppArmor (эквивалент SELinux) | ||
+ | * Seccomp | ||
+ | * AllowPrivilegeEscalation setting (может ли пользователь внутри пода запускать задачи с повышением привилегий) | ||
+ | * runAsNonRoot (запуск пода под нерутовым пользователем) | ||
+ | |||
+ | Сек. контекст может быть задан на уровне пода или на уровне контейнера. См. | ||
+ | <code bash> | ||
+ | kubectl explain pod.spec.securityContext | ||
+ | kubectl explain pod.spec.containers.securityContext | ||
+ | </ | ||
+ | Набор параметров неодинаковый.\\ | ||
+ | Если одни и те же настройки заданы одновременно и для пода, и для контейнера, | ||
+ | Пример (https:// | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: security-context-demo | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | runAsGroup: 1000 | ||
+ | fsGroup: 2000 | ||
+ | volumes: | ||
+ | - name: securevol | ||
+ | emptyDir: {} | ||
+ | containers: | ||
+ | - name: sec-demo | ||
+ | image: busybox | ||
+ | command: [" | ||
+ | volumeMounts: | ||
+ | - name: securevol | ||
+ | mountPath: /data/demo | ||
+ | securityContext: | ||
+ | allowPrivilegeEscalation: | ||
+ | </ | ||
+ | |||
+ | ===== 10.3 Using ServiceAccounts to Configure API Access ===== | ||
+ | |||
+ | В Кубере нет внутренних пользователей. Они берутся извне: | ||
+ | * Определяются сертификатами X.509 | ||
+ | * Берутся из внешних источников (Google, AD, etc.) | ||
+ | Сервисные учётки используются для авторизации подов для получения доступа к специфическим ресурсам. | ||
+ | |||
+ | <code bash> | ||
+ | # Сервисные учётки, | ||
+ | kubectl get sa | ||
+ | # Помимо стандартной, | ||
+ | |||
+ | #Все сервис-учётки: | ||
+ | kubectl get sa -A | ||
+ | # Найти запись об используемой сервис-учётке можно в свойствах пода | ||
+ | kubectl describe po < | ||
+ | </ | ||
+ | |||
+ | ===== 10.4 RBAC ===== | ||
+ | |||
+ | Role-based access control - ролевой доступ. Имеет 3 компонента: | ||
+ | |||
+ | 1) Роль: используется в пространстве имён и задаёт права доступа к ресурсам в этом пространстве имён. | ||
+ | <code bash> | ||
+ | kubectl create role -h |less | ||
+ | kubectl get roles | ||
+ | kubectl get roles -A # какие вообще существуют, | ||
+ | kubectl get role leader-locking-nfs-subdir-external-provisioner -o yaml |less | ||
+ | </ | ||
+ | |||
+ | 2) Привязка роли (roleBindings). Роль сама по себе не указывает, | ||
+ | roleBindings подключает пользователей или сервис-аккаунты к ролям. | ||
+ | <code bash> | ||
+ | kubectl create rolebinding -h |less | ||
+ | kubectl get rolebinding -A | ||
+ | kubectl get rolebinding ingress-nginx -n ingress-nginx -o yaml |less | ||
+ | </ | ||
+ | RoleRef указывает на роль, к которой идёт привязка, | ||
+ | |||
+ | 3) Сервисные учётки (ServiceAccounts).\\ | ||
+ | Сервис-учётка авторизует поды, чтобы те могли получить информацию от API.\\ | ||
+ | Все поды имеют сервис-учётку по умолчанию, | ||
+ | Если этого недостаточно, | ||
+ | У сервис-учётки нет особой конфигурации, | ||
+ | |||
+ | Демо\\ | ||
+ | Сделать под alpine (https:// | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: mypod | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: alpine | ||
+ | image: alpine:3.9 | ||
+ | command: | ||
+ | - " | ||
+ | - " | ||
+ | </ | ||
+ | <code bash> | ||
+ | kubectl apply -f mypod.yaml | ||
+ | # Если посмотреть в свойства пода, то там в качестве сервис-учётки будет default. | ||
+ | kubectl describe po mypod -o yaml | ||
+ | # Если внутри пода попробовать постучаться к API, то будет отлуп | ||
+ | kubectl exec -it mypod --sh | ||
+ | apk add curl | ||
+ | curl https:// | ||
+ | # Когда подключение идёт с токеном от сервис-учётки Default, то всё работает | ||
+ | TOKEN=$(cat / | ||
+ | echo $TOKEN # Проверить, | ||
+ | curl -H " | ||
+ | # Если полезть дальше, | ||
+ | curl -H " | ||
+ | # Для этого нужно настроить RBAC. | ||
+ | </ | ||
+ | |||
+ | Создаём сервис-учётку (https:// | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: ServiceAccount | ||
+ | metadata: | ||
+ | name: mysa | ||
+ | </ | ||
+ | Создаём роль (https:// | ||
+ | <code yaml> | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | kind: Role | ||
+ | metadata: | ||
+ | name: list-pods | ||
+ | namespace: default | ||
+ | rules: | ||
+ | - apiGroups: | ||
+ | - '' | ||
+ | resources: | ||
+ | - pods | ||
+ | verbs: | ||
+ | - list | ||
+ | </ | ||
+ | Или из командной строки: | ||
+ | <code bash> | ||
+ | kubectl create role list-pods --verb=list --resource=pods -n default | ||
+ | </ | ||
+ | |||
+ | Создаём привязку (https:// | ||
+ | <code yaml> | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | kind: RoleBinding | ||
+ | metadata: | ||
+ | name: list-pods-mysa-binding | ||
+ | namespace: default | ||
+ | roleRef: | ||
+ | kind: Role | ||
+ | name: list-pods | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | subjects: | ||
+ | - kind: ServiceAccount | ||
+ | name: mysa | ||
+ | namespace: default | ||
+ | </ | ||
+ | |||
+ | В поде надо поменять сервис-учётку (https:// | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: mysapod | ||
+ | spec: | ||
+ | serviceAccountName: | ||
+ | containers: | ||
+ | - name: alpine | ||
+ | image: alpine:3.9 | ||
+ | command: | ||
+ | - " | ||
+ | - " | ||
+ | </ | ||
+ | |||
+ | После этого список подов будет доступен через API в mysapod | ||
+ | <code bash> | ||
+ | kubectl exec -it mysapod --sh | ||
+ | apk add curl | ||
+ | TOKEN=$(cat / | ||
+ | curl -H " | ||
+ | </ | ||
+ | |||
+ | ===== 10.5 Cluster roles ===== | ||
+ | |||
+ | Обычная роль действует в рамках namespace, кластерная роль распространяется на весь кластер.\\ | ||
+ | Работает кластерная роль точно так же, как и обычная. Чтобы привязать пользователя или сервис-учётку к кластерной роли, нужен clusterRoleBinding. | ||
+ | <code bash> | ||
+ | # Список кластерных ролей | ||
+ | kubectl get clusterrole | ||
+ | # Опции (хорошая шпаргалка по синтаксису) | ||
+ | kubectl get clusterrole edit -o yaml |less | ||
+ | # Список привязок | ||
+ | kubectl get clusterrolebindings | ||
+ | </ | ||
+ | |||
+ | ===== 10.6 Creating user accounts ===== | ||
+ | |||
+ | В Кубере нет объектов-пользователей. Пользователь - это сертификат + ролевой доступ. | ||
+ | |||
+ | Чтобы создать " | ||
+ | - Сделать пару ключей. | ||
+ | - Сделать запрос на подпись сертификата (CSR) | ||
+ | - Подписать сертификат | ||
+ | - Создать конфиг, | ||
+ | - Создать роль RBAC | ||
+ | - Создать привязку | ||
+ | |||
+ | Демо | ||
+ | <code bash> | ||
+ | kubectl create ns students | ||
+ | kubectl create ns staff | ||
+ | kubectl config get-contexts # один kubernetes-admin | ||
+ | # См. текущий контекст | ||
+ | less ~/ | ||
+ | |||
+ | # Создать юзера | ||
+ | sudo useradd -m -G sudo -s /bin/bash vasya | ||
+ | sudo passwd vasya | ||
+ | su - vasya | ||
+ | openssl genrsa -out vasya.key 2048 | ||
+ | openssl req -new -key vasya.key -out vasya.csr -subj "/ | ||
+ | sudo openssl x509 -req -in vasya.csr -CA / | ||
+ | |||
+ | # Добавить в конфиг креды нового юзера | ||
+ | mkdir / | ||
+ | sudo cp -i / | ||
+ | sudo chown -R vasya: / | ||
+ | kubectl config set-credentials vasya --client-certificate=/ | ||
+ | |||
+ | # Создать контекст по умолчанию для нового юзера | ||
+ | kubectl config set-context vasya-context --cluster=kubernetes --namespace=staff --user=vasya | ||
+ | # Переключиться на контекст постоянно | ||
+ | kubectl config use-context vasya-context | ||
+ | # Уже 2 контекста - админ и Вася | ||
+ | kubectl config get-contexts | ||
+ | # Следующая команда обломится, | ||
+ | kubectl get po | ||
+ | </ | ||
+ | |||
+ | Настройка RBAC\\ | ||
+ | Переключиться с Васи обратно на админа, | ||
+ | <code bash> | ||
+ | su - user | ||
+ | </ | ||
+ | |||
+ | Роль (https:// | ||
+ | <code yaml> | ||
+ | kind: Role | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | namespace: staff | ||
+ | name: staff | ||
+ | rules: | ||
+ | - apiGroups: ["", | ||
+ | resources: [" | ||
+ | verbs: [" | ||
+ | </ | ||
+ | |||
+ | Привязка (https:// | ||
+ | <code yaml> | ||
+ | kind: RoleBinding | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | name: staff-role-binding | ||
+ | namespace: staff | ||
+ | subjects: | ||
+ | - kind: User | ||
+ | name: vasya | ||
+ | apiGroup: "" | ||
+ | roleRef: | ||
+ | kind: Role | ||
+ | name: staff | ||
+ | apiGroup: "" | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | kubectl apply -f staff-role.yaml | ||
+ | kubectl apply -f rolebind.yaml | ||
+ | |||
+ | # Переключаемся обратно на Васю | ||
+ | su - vasya | ||
+ | # Вывод конфига, | ||
+ | kubectl config view | ||
+ | # Сделать тестовый деплой и вывести поды (на этот раз всё сработает) | ||
+ | kubectl create deploy nginx --image=nginx | ||
+ | kubectl get po | ||
+ | </ | ||
+ | |||
+ | Дополнительно: | ||
+ | <code bash> | ||
+ | # Если Вася попытается посмотреть поды в ns default, то обломится | ||
+ | kubectl get po -n default | ||
+ | </ | ||
+ | |||
+ | Роль (https:// | ||
+ | <code yaml> | ||
+ | kind: Role | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | namespace: default | ||
+ | name: students | ||
+ | rules: | ||
+ | - apiGroups: ["", | ||
+ | resources: [" | ||
+ | verbs: [" | ||
+ | </ | ||
+ | |||
+ | Привязка (https:// | ||
+ | <code yaml> | ||
+ | kind: RoleBinding | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | name: students-role-binding | ||
+ | namespace: default | ||
+ | subjects: | ||
+ | - kind: User | ||
+ | name: vasya | ||
+ | apiGroup: "" | ||
+ | roleRef: | ||
+ | kind: Role | ||
+ | name: students | ||
+ | apiGroup: "" | ||
+ | </ | ||
+ | <code bash> | ||
+ | kubectl apply -f students-role.yaml | ||
+ | kubectl apply -f rolebindstudents.yaml | ||
+ | |||
+ | # Теперь у Васи всё работает | ||
+ | kubectl get po -n default | ||
+ | </ | ||
+ | |||
+ | Задача: | ||
+ | <code bash> | ||
+ | # См. справку, | ||
+ | kubectl create role -h |less | ||
+ | # Создать роль | ||
+ | kubectl create role default-pod-viewer --verb=get, | ||
+ | # Дальше в списке КЛАСТЕРНЫХ привязок есть system: | ||
+ | kubectl get clusterrolebindings | ||
+ | # Если выполнить команду от его имени, то не получится (forbidden) | ||
+ | kubectl get pods --as system: | ||
+ | # Привязать роль к system: | ||
+ | kubectl create rolebinding default-pod-viewer --role=default-pod-viewer --user=system: | ||
+ | </ | ||
+ | |||
+ | ===== 11.1 Monitoring Kubernetes resources ===== | ||
+ | |||
+ | Самое элементарное - '' | ||
+ | Если есть сервер метрик и собираются метрики, | ||
+ | <code bash> | ||
+ | kubectl top pods | ||
+ | kubectl top nodes | ||
+ | </ | ||
+ | Для полноценного мониторинга нужны инструменты типа Prometheus и Grafana. | ||
+ | |||
+ | Установка сервера метрик: | ||
+ | <code bash> | ||
+ | # Если одна мастер-нода | ||
+ | kubectl apply -f https:// | ||
+ | # Если несколько (HA cluster) | ||
+ | kubectl apply -f https:// | ||
+ | </ | ||
+ | :!: Нужно в yaml добавить '' | ||
+ | |||
+ | ===== 11.2 Troubleshooting flow ===== | ||
+ | |||
+ | Ресурсы создаются сначала в ETCD. Уже с этого момента можно использовать команды | ||
+ | <code bash> | ||
+ | kubectl describe | ||
+ | kubectl events | ||
+ | |||
+ | # Дальше под стартует на запланированных узлах, перед стартом нужно скачать образ. Вывести список доступных образов на конкретной ноде можно | ||
+ | sudo crictl images | ||
+ | # После запуска уже можно смотреть в логи | ||
+ | kubectl logs [-f] | ||
+ | # и лезть в консоль. | ||
+ | </ | ||
+ | |||
+ | kubectl get показывает базовый статус. | ||
+ | * Pending: под существует в ETCD и ждёт, когда он может быть запущен на подходящей ноде. Пока нет подходящего узла для его запуска (taint/ | ||
+ | * Running: работает нормально | ||
+ | * Succeeded: отработал нормально (перезапуск не нужен) | ||
+ | * Failed: один или несколько контейнеров в поде завершили работу с ошибкой и перезапущены не будут | ||
+ | * Unknown: состояние неизвестно. Обычно так бывает при проблемах с сетью. | ||
+ | * Completed: работа завершена. | ||
+ | * CrashLoopBackOff: | ||
+ | |||
+ | Что именно поломалось, | ||
+ | |||
+ | ===== 11.4 Troubleshooting cluster nodes ===== | ||
+ | <code bash> | ||
+ | # Основная информация о состоянии кластера | ||
+ | kubectl cluster-info | ||
+ | # ОЧЕНЬ подробная информация из всех кластерных логов (нужно парсить, | ||
+ | kubectl cluster-info dump | ||
+ | |||
+ | kubectl get nodes | ||
+ | kubectl get po -n kube-system | ||
+ | # Информация об узле, особое внимание на раздел Conditions, Taints, Allocated resources | ||
+ | kubectl describe node < | ||
+ | |||
+ | # На рабочей ноде - статус кублета (т. к. кублет - это самое главное, | ||
+ | systemctl status kubelet | ||
+ | |||
+ | # Проверить сертификат (редко нужно, но если рабочая нода давно работает и кластер не обновляется, | ||
+ | sudo openssl x509 -in / | ||
+ | </ | ||
+ | |||
+ | ===== 11.5 Troubleshooting application access ===== | ||
+ | |||
+ | Это выяснение проблем с подами, | ||
+ | Сервис использует селектор для подключения к подам с совпадающим ярлыком. Ингресс подключается к сервису и берёт его селектор для подключения к соответствующим подам. Прежде всего нужно проверить ярлыки всех этих ресурсов. | ||
+ | <code bash> | ||
+ | # Вывести сервисы и конечные точки | ||
+ | kubectl get endpoints | ||
+ | </ | ||
+ | Положим, | ||