learning:k8s
Различия
Показаны различия между двумя версиями страницы.
Предыдущая версия справа и слеваПредыдущая версияСледующая версия | Предыдущая версия | ||
learning:k8s [22.09.2022 13:24] – [Static pods] viacheslav | learning:k8s [25.03.2025 15:27] (текущий) – [certificate has expired or is not yet valid] viacheslav | ||
---|---|---|---|
Строка 1: | Строка 1: | ||
+ | ====== K8s ====== | ||
+ | [[https:// | ||
+ | ===== Основные понятия ===== | ||
+ | https:// | ||
+ | |||
+ | Принципы Cloud Native: | ||
+ | - Контейнеризация (докер) | ||
+ | - Динамическое управление – Кубернетис | ||
+ | - Ориентация на микросервисы – Кубернетис | ||
+ | В Кубернетисе мы задаём желаемое состояние (desired state), т. е., система сама подстраивается и предпринимает конкретные действия, | ||
+ | |||
+ | ==== Pod ==== | ||
+ | **Pods** – один или несколько контейнеров. Все контейнеры в одном поде: | ||
+ | - запускаются на одном хосте | ||
+ | - взаимодействуют друг с другом через localhost | ||
+ | - имеют одинаковый доступ к томам | ||
+ | Иными словами, | ||
+ | |||
+ | Sidecars – сопутствующие контейнеры в одном поде с основным. К примеру, | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod (с чем работаем) | ||
+ | metadata: | ||
+ | name: mypod1 (уникальный идентификатор) | ||
+ | labels: | ||
+ | app: blog | ||
+ | spec: (всё в разделе spec характерно для выбранного типа, в данном случае пода) | ||
+ | containters: | ||
+ | - name: nginx | ||
+ | Image: nginx: | ||
+ | - name: www-syncer | ||
+ | Image: syncer:1.2 | ||
+ | </ | ||
+ | |||
+ | Как запустить эту конструкцию? | ||
+ | <code bash> | ||
+ | # Предпочтительный путь (declarative): | ||
+ | kubectl apply –f [file | dir | url] | ||
+ | # imperative: | ||
+ | kubectl create # создать новый ресурс из файла | ||
+ | kubectl replace # обновить существующий ресурс из файла | ||
+ | kubectl edit # обновить существующий ресурс, | ||
+ | kubectl patch # обновить существующий ресурс слиянием code snippet | ||
+ | </ | ||
+ | |||
+ | === Binding === | ||
+ | |||
+ | Чтобы жёстко привязать под к ноде, используется '' | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Binding | ||
+ | metadata: | ||
+ | name: nginx | ||
+ | target: | ||
+ | apiVersion: v1 | ||
+ | kind: Node | ||
+ | name: node02 | ||
+ | </ | ||
+ | , а потом послать запрос POST в API в формате json, что имитирует работу планировщика. | ||
+ | <code bash> | ||
+ | # пример | ||
+ | $ curl --header " | ||
+ | http:// | ||
+ | </ | ||
+ | |||
+ | ==== Service ==== | ||
+ | Для сетевого доступа и балансировки существует тип объекта **Service.** Для определения целевых подов используются ярлыки (labels). | ||
+ | |||
+ | {{: | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Service | ||
+ | metadata: | ||
+ | name: my-blog | ||
+ | labels: | ||
+ | app: blog | ||
+ | spec: | ||
+ | selector: | ||
+ | app: blog | ||
+ | ports: | ||
+ | - protocol: TCP | ||
+ | port: 80 (порт сервиса) | ||
+ | targetPort: 80 (порт подов сервиса) | ||
+ | </ | ||
+ | Вместо имен или IP-адресов подов используется селектор, | ||
+ | |||
+ | ==== Deployment ==== | ||
+ | А что, если нужно, к примеру, | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: | ||
+ | spec: | ||
+ | replicas: 3 (копий подов) | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: blog | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: blog | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | Image: nginx:1.7.9 | ||
+ | Ports: | ||
+ | - containerPort: | ||
+ | </ | ||
+ | В данном случае раздел template является фактически описанием пода, за исключением имени, т. к. подов может быть много и имена будут присваиваться автоматически. | ||
+ | |||
+ | Ярлыки (labels) выполняют как организационную, | ||
+ | |||
+ | Документация: | ||
+ | https:// | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | <code bash> | ||
+ | kubectl explain pods # встроенная справка по разным разделам, | ||
+ | kubectl api-resources # список доступных типов ресурсов | ||
+ | kubectl apply -f service.yaml # запустить сервис или деплой | ||
+ | kubectl get service -l " | ||
+ | kubectl get deployment -l " | ||
+ | </ | ||
+ | ===== Решение проблем ===== | ||
+ | https:// | ||
+ | |||
+ | Ноды делятся на 2 типа – worker nodes и control plane. На рабочих ставится контейнерный движок (например, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | <code bash> | ||
+ | Kubectl get events [-w] # события кластера. По умолчанию хранит данные за час. [-w] – показ в реальном времени. Полезно при проблемах с cubectl apply. | ||
+ | Kubectl describe pod gowebapp-3498323ff-348hf # описание объекта вместе с его событиями | ||
+ | Kubectl get pods [–o wide] # список объектов, | ||
+ | Kubectl logs gowebapp-3498323ff-348hf # смотреть логи. Добавить –c < | ||
+ | Kubectl exec gowebapp-3498323ff-348hf – ls –l / | ||
+ | </ | ||
+ | |||
+ | ===== Управление развертыванием ===== | ||
+ | Deployment создаёт промежуточную сущность ReplicaSets, | ||
+ | |||
+ | **RollingUpdate** - стратегия по умолчанию, | ||
+ | |||
+ | **Recreate** - сначала гасятся все поды, удаляется старая реплика, | ||
+ | |||
+ | Также, имеются дополнительные стратегии, | ||
+ | |||
+ | **Canary** - создаётся ещё один файл deployment, где в labels: указан дополнительный track: canary. В основном деплое указывается track: stable. Для canary указывается, | ||
+ | |||
+ | **Blue/ | ||
+ | |||
+ | Когда тесты закончились и нужно ввести тестовый вариант (green) в прод, редактируется // | ||
+ | |||
+ | В следующий раз тестовой средой будет уже blue, т. к. green перешёл в прод. | ||
+ | |||
+ | <code bash> | ||
+ | # Развёртывание можно приостанавливать и снова продолжать | ||
+ | kubectl rollout [pause | resume] deployment < | ||
+ | # Откат изменений (declarative, | ||
+ | kubectl apply -f deployment.yaml | ||
+ | # Откат изменений (imperative) | ||
+ | kubectl rollout undo deployment < | ||
+ | </ | ||
+ | |||
+ | Сам объект Deployment имеет доп. опции, вот некоторые из них: | ||
+ | * progressDeadlineSeconds - макс. время развёртывания в секундах | ||
+ | * minReadySeconds - мин. время готовности пода в секундах без падения какого-либо из входящих в него контейнеров (станд. значение - 0) | ||
+ | * revisionHistoryLimit - кол-во хранимых реплик для возможности отката изменений (10) | ||
+ | * paused - приостановка развёртывания. Если true, то любые изменения в разделе spec не приведёт к развёртыванию. | ||
+ | |||
+ | Лабораторная (https:// | ||
+ | * Сделали образ docker новой версии, | ||
+ | * Для пробы отредактировали основной деплой на новый образ и через 1 сек. поставили развёртывание на паузу - получилось 3 пода: 1 новый и 2 старых. Describe deployment показал, | ||
+ | * '' | ||
+ | * Теперь канареечный деплой. Создаётся соответствующий файл деплоя, | ||
+ | * Канареечный деплой применяется. Теперь стало три реплики приложения, | ||
+ | * Теперь, | ||
+ | * Чтобы полностью перейти на новую версию в проде, надо обновить версию образа в прод. деплое, | ||
+ | * Удалить канареечные сущности: | ||
+ | |||
+ | ===== Конфигурация подов и контейнеров ===== | ||
+ | ==== Проверки ==== | ||
+ | Есть 2 встроенных типа проверок состояния контейнера: | ||
+ | - Liveness - запущен ли контейнер. При сбое он будет перезапущен согласно политике. | ||
+ | - Readiness - готов ли контейнер принимать запросы. При сбое под удаляется из сервиса, | ||
+ | |||
+ | Способы проверки (probe handlers): | ||
+ | * Exec - запуск команды, | ||
+ | * TCPSocket - TCP-проверка. Порт открыт и принимает соединения - успешно. | ||
+ | * HTTPGet - HTTP GET, коды 2xx-3xx - успешно. | ||
+ | |||
+ | Примеры применения проверок: | ||
+ | |||
+ | О самих проверках нужно позаботиться на стороне контейнера/ | ||
+ | |||
+ | Настройки проверок: | ||
+ | * initialDelaySeconds - задержка запуска проверки после старта контейнера | ||
+ | * periodSeconds - частота проверок (10 сек) | ||
+ | * timeoutSeconds - тайм-аут при проверке (1 сек) | ||
+ | * successThreshold - кол-во успешных проверок для признания контейнера исправным (1) | ||
+ | * failureThreshold - кол-во неуспешных проверок для признания контейнера сбойным (3) | ||
+ | |||
+ | Пример | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | labels: | ||
+ | test: readiness | ||
+ | name: exec-readiness | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | containers: | ||
+ | - name: readiness | ||
+ | image: busybox | ||
+ | args: | ||
+ | - /bin/sh | ||
+ | - -c | ||
+ | - touch / | ||
+ | readinessProbe: | ||
+ | exec: | ||
+ | command: | ||
+ | - cat | ||
+ | - / | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | </ | ||
+ | |||
+ | ==== Ресурсы ==== | ||
+ | Указываются для каждого контейнера. | ||
+ | |||
+ | Процессор - это эквивалент 1 AWS vCPU / 1 GCP Core / 1 Azure vCore / 1 Hyperthread on Intel CPU w/ | ||
+ | |||
+ | Память и хранилище - десятичные значения (K, M, G, T, P, E) и двоичные (Ki, Mi, Gi, Ti, Pi, Ei). | ||
+ | |||
+ | Требования к ресурсам (Resource requests) - нужно для более эффективного и понятного распределения по серверам кластера. Т. е., указание, | ||
+ | <code yaml> | ||
+ | containers: | ||
+ | - name: db | ||
+ | image: mysql | ||
+ | resources: | ||
+ | requests: | ||
+ | memory: " | ||
+ | cpu: " | ||
+ | </ | ||
+ | |||
+ | Предел ресурсов (Resource limits) - чтобы контейнер не сожрал слишком много ресурсов и не завалил всю систему. При попытке превысить ресурсы CPU контейнер будет ограничиваться в потреблении (throttling), | ||
+ | <code yaml> | ||
+ | containers: | ||
+ | - name: db | ||
+ | image: mysql | ||
+ | resources: | ||
+ | limits: | ||
+ | memory: " | ||
+ | cpu: " | ||
+ | </ | ||
+ | |||
+ | Когда ноды кластера не имеют достаточно ресурсов для размещения доп. подов, планировщик не может выполнить требований деплоя и поды будут помечены как Pending, пока не призойдут какие-либо изменения ситуации для достижения желаемого состояния, | ||
+ | * Будут добавлены новые хосты в кластер | ||
+ | * Будут пересмотрены требования к ресурсам в деплое | ||
+ | * Другие поды и деплои будут удалены или уменьшены в размере | ||
+ | * Кол-во желаемых подов будет уменьшено в текущем деплое | ||
+ | |||
+ | ===== Сеть ===== | ||
+ | Трафик в K8s можно поделить на 4 сегмента: | ||
+ | |||
+ | Т. к. под всегда находится на одном хосте, то его внутренний трафик не идёт "по проводу" | ||
+ | |||
+ | У каждого пода есть свой уникальный маршрутизируемый IP-адрес в рамках внутренней виртуальной сети кластера. Также, есть подобие DNS-имени на базе '' | ||
+ | |||
+ | Сервис работает как NLB 4-го уровня. Есть 3 типа сервисов: | ||
+ | - ClusterIP - публикация внутри кластера, | ||
+ | - NodePort - публикация как порт на всех рабочих нодах. Т. е., попасть к сервису можно, обратившись на любую рабочую ноду кластера на указанный порт. Это не отменяет необходимости внешнего балансировщика, | ||
+ | - LoadBalancer - создание и управление внешним балансировщиком, | ||
+ | |||
+ | Ingress Controller - это NLB 7-го уровня. Ему, в отличие от NodePort, не нужно открывать кучу портов на нодах. Есть множество реализаций контроллера Ingress, такие, как Nginx, Contour, Traefik, Amazon ALB, Google Layer-7 Load Balancer. Запускается он как поды в кластере, | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ===== Организация ресурсов ===== | ||
+ | Наивысший уровень изоляции сервисов - это **разнесение их по разным кластерам** - prod, qa, dev и т. п. Также это имеет смысл при каких-то требованиях безопасности или при географическом распределении датацентров. В этом случае имеются некоторые доп. усилия на управление. | ||
+ | |||
+ | Следующий - это **namespace** в рамках одного кластера. Каждый объект кластера входит в своё пространство, | ||
+ | |||
+ | <code bash> | ||
+ | # Вывести пространства имён | ||
+ | kubectl get namespaces | ||
+ | # Вывести поды другого пространства | ||
+ | kubectl get pods -n namespace-name | ||
+ | </ | ||
+ | |||
+ | |||
+ | **Ярлыки (labels)** - могут быть привязаны почти к любому объекту в k8s, даже к нодам (кроме встроенных). Они необязательны, | ||
+ | |||
+ | <code bash> | ||
+ | # ФИльтровать вывод по ярлыку | ||
+ | kubectl get pods -l tier=frontend | ||
+ | </ | ||
+ | |||
+ | kubectl знает, с каким кластером он работает, | ||
+ | |||
+ | <code bash> | ||
+ | # добавить namespace в контекст конфига | ||
+ | kubectl config set-context my-context --user poweruser --cluster cluster1 --namespace my-namespace | ||
+ | # Переключиться на новый контекст | ||
+ | kubectl config use-context my-context | ||
+ | # см. какой контекст сейчас используется | ||
+ | kubectl config current-context | ||
+ | # все контексты | ||
+ | kubectl config get-contexts | ||
+ | |||
+ | # переключиться на другое пространство имён в текущем контексте | ||
+ | kubectl config set-context $(kubectl config current-context) --namespace=another | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | <code bash> | ||
+ | # jsonpath - фильтр сырого вывода json. Ещё опции вывода - yaml, json, wide | ||
+ | kubectl get deployment nginx -o jsonpath=' | ||
+ | # Перенаправить порт из пода (8080) на локальную машину (8000). Потом можно делать запросы, | ||
+ | kubectl port-forward < | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | Квота ресурсов для namespace задаётся объектом ResourceQuota. | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: ResourceQuota | ||
+ | metadata: | ||
+ | name: dev-quota | ||
+ | namespace: dev | ||
+ | spec: | ||
+ | hard: | ||
+ | pods: " | ||
+ | requests.cpu: | ||
+ | requests.memory: | ||
+ | limits.cpu: " | ||
+ | limits.memory: | ||
+ | </ | ||
+ | ===== Stateful apps ===== | ||
+ | Хранилище для таких приложений может быть разных типов. Работает на уровне пода, подобно хранилищу для виртуалки. | ||
+ | |||
+ | Особый тип хранилища - **EmptyDir.** Временный каталог, | ||
+ | |||
+ | **Persistent Volume** - не зависит от времени жизни пода, кочует вместе с ним с хоста на хост. Есть опция hostPath, указывающая путь на ноде, но без крайней необходимости её лучше не применять. | ||
+ | |||
+ | **Persistent Volume Claims (PVC)** - запрос на организацию хранилища с заданными параметрами (размер, | ||
+ | |||
+ | Хранилища могут быть созданы статически (руками), | ||
+ | |||
+ | Для PVC создаётся объект '' | ||
+ | |||
+ | Чтобы у каждого пода в деплое было своё хранилище, | ||
+ | |||
+ | https:// | ||
+ | |||
+ | Пример объекта StatefulSet | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: StatefulSet | ||
+ | metadata: | ||
+ | name: gowebapp-mysql | ||
+ | labels: | ||
+ | app: gowebapp-mysql | ||
+ | tier: backend | ||
+ | spec: | ||
+ | serviceName: | ||
+ | replicas: 1 | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: gowebapp-mysql | ||
+ | tier: backend | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: gowebapp-mysql | ||
+ | tier: backend | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: gowebapp-mysql | ||
+ | env: | ||
+ | - name: MYSQL_ROOT_PASSWORD | ||
+ | value: mypassword | ||
+ | image: gowebapp-mysql: | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | livenessProbe: | ||
+ | tcpSocket: | ||
+ | port: 3306 | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | timeoutSeconds: | ||
+ | readinessProbe: | ||
+ | exec: | ||
+ | command: [" | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | timeoutSeconds: | ||
+ | volumeMounts: | ||
+ | - name: mysql-pv | ||
+ | mountPath: / | ||
+ | volumeClaimTemplates: | ||
+ | - metadata: | ||
+ | name: mysql-pv | ||
+ | spec: | ||
+ | accessModes: | ||
+ | resources: | ||
+ | requests: | ||
+ | storage: 5Gi | ||
+ | </ | ||
+ | |||
+ | ===== Динамическая конфигурация приложения ===== | ||
+ | Когда что-то в конфигурации занесено жёстко и в явном виде, это потенциальный источник проблем. Для решения используется объект '' | ||
+ | - как на переменные окружения - в разделе '' | ||
+ | - как на том - том задаётся как configMap и в контейнере он монтируется по определённому пути. Также можно монтировать configMap как полностью, | ||
+ | |||
+ | Разница в подходах в том, что в случае переменных окружения надо перезапускать контейнер для изменения настроек, | ||
+ | |||
+ | <code yaml> | ||
+ | volumeMounts: | ||
+ | - name: config-volume | ||
+ | mountPath: '/ | ||
+ | volumes: | ||
+ | - name: config-volume | ||
+ | configMap: | ||
+ | name: gowebapp | ||
+ | items: | ||
+ | - key: webapp-config-json | ||
+ | path: config.json | ||
+ | </ | ||
+ | |||
+ | Секретные данные ('' | ||
+ | |||
+ | https:// | ||
+ | |||
+ | <code bash> | ||
+ | kubectl create secret generic mysql --from-literal=password=mypassword | ||
+ | </ | ||
+ | |||
+ | Потом в файле StatefulSet для mysql заменить | ||
+ | <code yaml> | ||
+ | env: | ||
+ | - name: MYSQL_ROOT_PASSWORD | ||
+ | value: mypassword | ||
+ | # на | ||
+ | env: | ||
+ | - name: MYSQL_ROOT_PASSWORD | ||
+ | valueFrom: | ||
+ | secretKeyRef: | ||
+ | name: mysql | ||
+ | key: password | ||
+ | | ||
+ | # в разделе readinessProbe: | ||
+ | readinessProbe: | ||
+ | exec: | ||
+ | command: [" | ||
+ | </ | ||
+ | |||
+ | ===== Дополнительная нагрузка ===== | ||
+ | Помимо сервисов, | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: batch/v1 | ||
+ | kind: Job | ||
+ | metadata: | ||
+ | name: pi | ||
+ | spec: | ||
+ | template: | ||
+ | metadata: | ||
+ | name: pi | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | containers: | ||
+ | - name: pi | ||
+ | image: perl | ||
+ | command: [" | ||
+ | restartPolicy: | ||
+ | </ | ||
+ | |||
+ | Посмотреть результаты вышеописанного задания можно командой '' | ||
+ | |||
+ | Параллельные задания с очередью (parallel jobs with a work queue) - запуск нескольких подов, каждый из которых берёт на себя часть очереди на обработку - как многопоточная обработка процессорными ядрами. Для | ||
+ | * оставить '' | ||
+ | * каждый под должен иметь возможность проверить, | ||
+ | * когда под завершил работу успешно, | ||
+ | * когда хотя бы один под завершил работу успешно и остальные были уничтожены, | ||
+ | * когда любой под завершил работу успешно, | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: batch/v1 | ||
+ | kind: Job | ||
+ | metadata: | ||
+ | name: primes-parallel-wq | ||
+ | labels: | ||
+ | app: primes | ||
+ | spec: | ||
+ | parallelism: | ||
+ | template: | ||
+ | metadata: | ||
+ | name: primes | ||
+ | labels: | ||
+ | app: primes | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | containers: | ||
+ | - name: primes | ||
+ | image: debian: | ||
+ | command: [" | ||
+ | args: [" | ||
+ | restartPolicy: | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | Также существуют CronJobs - аналог cron в линуксе. Для каждого запуска создаётся новый job. Хранится 3 успешных и 1 неуспешный job. Jobs должны быть идемпотентными, | ||
+ | |||
+ | < | ||
+ | apiVersion: batch/ | ||
+ | kind: CronJob | ||
+ | metadata: | ||
+ | name: hello | ||
+ | spec: | ||
+ | schedule: "*/1 * * * *" | ||
+ | jobTemplate: | ||
+ | spec: | ||
+ | template: | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | containers: | ||
+ | - name: hello | ||
+ | image: busybox | ||
+ | args: | ||
+ | - /bin/sh | ||
+ | - -c | ||
+ | - date; echo Hello from the Kubernetes cluster | ||
+ | restartPolicy: | ||
+ | </ | ||
+ | |||
+ | DaemonSet - объект, | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ===== Безопасность ===== | ||
+ | https:// | ||
+ | |||
+ | Объект **NetworkPolicy** задаёт, | ||
+ | |||
+ | Изначально все поды внутри пространства имён могут обмениваться любым трафиком. Когда внутри пространства имён применяется сетевая политика, | ||
+ | |||
+ | Пример политики: | ||
+ | <code yaml> | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | kind: NetworkPolicy | ||
+ | metadata: | ||
+ | name: test-network-policy | ||
+ | namespace: | ||
+ | spec: | ||
+ | podSelector: | ||
+ | matchLabels: | ||
+ | tier: backend | ||
+ | policyTypes: | ||
+ | - Ingress | ||
+ | - Egress | ||
+ | </ | ||
+ | |||
+ | Правила Ingress-Egress, | ||
+ | <code yaml> | ||
+ | ingress: | ||
+ | - from: | ||
+ | - namespaceSelector: | ||
+ | matchLabels: | ||
+ | project: gowebapp | ||
+ | - from: | ||
+ | - podSelector: | ||
+ | matchLabels: | ||
+ | tier: frontend | ||
+ | ports: | ||
+ | - protocol: TCP | ||
+ | port: 3306 | ||
+ | egress: | ||
+ | - to: | ||
+ | - ipBlock: | ||
+ | cidr: 10.0.0.0/24 | ||
+ | ports: | ||
+ | - protocol: TCP | ||
+ | port: 389 | ||
+ | |||
+ | </ | ||
+ | |||
+ | **SecurityContext** - часть '' | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: myapp | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | containers: | ||
+ | - name: myapp | ||
+ | image: myapp:v1 | ||
+ | securityContext: | ||
+ | allowPrivilegeEscalation: | ||
+ | - name: sidecar | ||
+ | image: mysidecar: | ||
+ | securityContext: | ||
+ | runAsUser: 2000 | ||
+ | </ | ||
+ | В примере выше '' | ||
+ | |||
+ | **Права доступа в кластере.** Есть несколько способов аутентификации - клиентские сертификаты (используются для кластерных компонентов), | ||
+ | |||
+ | **Ролевая модель доступа (RBAC).** Строится на основе групп или ролей - '' | ||
+ | |||
+ | Пример RBAC, привязанный к пространству имён и позволяющий только get secrets. | ||
+ | <code yaml> | ||
+ | kind: Role | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | namespace: default | ||
+ | name: my-app-secret-reader | ||
+ | rules: | ||
+ | - apiGroups: ["" | ||
+ | resources: [" | ||
+ | verbs: [" | ||
+ | </ | ||
+ | |||
+ | Есть также '' | ||
+ | <code yaml> | ||
+ | kind: ClusterRole | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | name: pod-reader | ||
+ | rules: | ||
+ | - apiGroups: ["" | ||
+ | resources: [" | ||
+ | verbs: [" | ||
+ | resourceNames: | ||
+ | </ | ||
+ | |||
+ | Вывести список общекластерных ресурсов и привязанных к пространствам имён | ||
+ | <code bash> | ||
+ | kubectl api-resources --namespaced=false | ||
+ | kubectl api-resources --namespaced=true | ||
+ | </ | ||
+ | |||
+ | Роли привязываются к пользователям, | ||
+ | |||
+ | <code yaml> | ||
+ | kind: RoleBinding | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | name: my-app-read-secret | ||
+ | namespace: default | ||
+ | subjects: | ||
+ | - kind: ServiceAccount | ||
+ | name: my-app | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | roleRef: | ||
+ | kind: Role | ||
+ | name: my-app-secret-reader | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | </ | ||
+ | |||
+ | Проверить возможность выполнять команды | ||
+ | <code bash> | ||
+ | kubectl auth can-i create deployments [--as user --namespace test] | ||
+ | kubectl auth can-i delete nodes [--as user --namespace test] | ||
+ | </ | ||
+ | ===== Taints & tolerations ===== | ||
+ | Политика запуска подов на хостах. Taints применяются к хостам, | ||
+ | <code bash> | ||
+ | kubectl taint nodes node1 app=web: | ||
+ | # убрать | ||
+ | kubectl taint nodes node1 app=web: | ||
+ | </ | ||
+ | Эффект - как действовать, | ||
+ | * NoSchedule - не размещать | ||
+ | * PreferNoSchedule - стараться не размещать | ||
+ | * NoExecute - не размещать и выгнать уже существующие | ||
+ | |||
+ | '' | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | tolerations: | ||
+ | - key: " | ||
+ | operator: " | ||
+ | value: " | ||
+ | effect: " | ||
+ | </ | ||
+ | Двойные кавычки в данном случае обязательны. | ||
+ | |||
+ | Taints & tolerations применяется на мастер-нодах, | ||
+ | |||
+ | Taints & tolerations - это только средство ограничения. **Если они настроены, | ||
+ | |||
+ | ===== Node selectors & affinity ===== | ||
+ | Иногда нужно, чтобы под запускался на конкретном хосте, например, | ||
+ | <code bash> | ||
+ | kubectl label nodes < | ||
+ | </ | ||
+ | и потом в '' | ||
+ | |||
+ | Но если условия более сложные, | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | affinity: | ||
+ | nodeAffinity: | ||
+ | requiredDuringSchedulingIgnoredDuringExecution: | ||
+ | nodeSelectorTerms: | ||
+ | - matchExpressions: | ||
+ | - key: power | ||
+ | operator: In | ||
+ | values: | ||
+ | - high | ||
+ | - medium | ||
+ | </ | ||
+ | * requiredDuringSchedulingIgnoredDuringExecution - не размещать новые, не трогать уже работающие | ||
+ | * preferredDuringSchedulingIgnoredDuringExecution - стараться не размещать новые, не трогать уже работающие | ||
+ | * requiredDuringSchedulingRequiredDuringExecution - сейчас такой опции нет, есть в планах - не размещать новые, выгонять работающие | ||
+ | |||
+ | https:// | ||
+ | |||
+ | **Если настроено nodeAffinity, | ||
+ | |||
+ | ===== Static pods ===== | ||
+ | В отсутствие мастер-ноды, | ||
+ | |||
+ | Путь к каталогу манифестов задаётся параметром '' | ||
+ | |||
+ | Kubelet, будучи в кластере, | ||
+ | |||
+ | Статические поды нужны для работы компонентов мастер-хостов, | ||
+ | |||
+ | ^ Static PODs ^ DaemonSets | ||
+ | | Создаются kubelet-ом непосредственно на узле | ||
+ | | Пример применения: | ||
+ | | Игнорируются Kube-scheduler-ом | ||
+ | ===== Multiple schedulers ===== | ||
+ | Помимо основного планировщика (или вместо него) можно установить дополнительные, | ||
+ | |||
+ | Чтобы запустить доп. планировщик, | ||
+ | |||
+ | После настройки планировщика (его под должен быть в состоянии Running) его имя нужно указывать в '' | ||
+ | |||
+ | Проверить, | ||
+ | |||
+ | Логи самого планировщика (не забыть про namespace) | ||
+ | <code bash> | ||
+ | kubectl logs < | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ===== Мониторинг и логирование ===== | ||
+ | Есть ряд решений, | ||
+ | |||
+ | Metrics server (ранее Heapster) - ставится один на кластер. Собирает метрики с хостов и подов и хранит их только в памяти, | ||
+ | |||
+ | Установить Metrics server | ||
+ | <code bash> | ||
+ | # в minikube | ||
+ | minikube addons enable metrics-server | ||
+ | # в microk8s | ||
+ | microk8s enable metrics-server | ||
+ | # для прочих | ||
+ | git clone https:// | ||
+ | kubectl create -f deploy/ | ||
+ | </ | ||
+ | Через некоторое время будет доступна информация о производительности | ||
+ | <code bash> | ||
+ | kubectl top node | ||
+ | kubectl top pod | ||
+ | </ | ||
+ | |||
+ | Логи в k8s можно смотреть примерно так же, как и в докере: | ||
+ | <code bash> | ||
+ | kubectl logs -f < | ||
+ | </ | ||
+ | Но есть нюанс - если в поде несколько контейнеров, | ||
+ | <code bash> | ||
+ | kubectl logs -f < | ||
+ | </ | ||
+ | |||
+ | ===== Команды и аргументы ===== | ||
+ | <code bash> | ||
+ | # В Докере команда передаётся в момент непосредственного запуска, | ||
+ | docker run ubuntu sleep 5 | ||
+ | # Чтобы указать команду в образе, | ||
+ | CMD sleep 5 # shell command | ||
+ | # или | ||
+ | CMD [" | ||
+ | </ | ||
+ | Разница в том, что в случае с форматом json первый аргумент должен быть исполняемым (sleep). Также, в случае с json, в процессах не будет дополнительно висеть shell как среда запуска. Теперь, | ||
+ | |||
+ | Изменить время с 5 сек на другое можно, запустив построенный образ: '' | ||
+ | <code bash> | ||
+ | ENTRYPOINT [" | ||
+ | </ | ||
+ | В таком случае, | ||
+ | <code bash> | ||
+ | ENTRYPOINT [" | ||
+ | CMD [" | ||
+ | </ | ||
+ | Тогда можно запускать просто '' | ||
+ | |||
+ | В k8s это пишется в '' | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: sleeper | ||
+ | image: sleeper | ||
+ | command: [" | ||
+ | args: ["/", | ||
+ | </ | ||
+ | Т. е., command - это аналог ENTRYPOINT, а args - CMD. | ||
+ | |||
+ | ===== Переменные ===== | ||
+ | ==== В '' | ||
+ | <code yaml> | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: webapp-db | ||
+ | env: | ||
+ | - name: MYSQL_ROOT_PASSWORD | ||
+ | value: mypassword | ||
+ | </ | ||
+ | |||
+ | ==== С помощью ConfigMap ==== | ||
+ | |||
+ | ConfigMaps нужны для централизованного управления парами " | ||
+ | |||
+ | Создать ConfigMap можно, как и другие объекты в k8s, 2 способами: | ||
+ | <code bash> | ||
+ | kubectl create configmap < | ||
+ | kubectl create configmap < | ||
+ | </ | ||
+ | и декларативным, | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: ConfigMap | ||
+ | metadata: | ||
+ | name: webapp-config | ||
+ | data: | ||
+ | APP_COLOR: red | ||
+ | APP_MODE: test | ||
+ | </ | ||
+ | и применив его. Посмотреть уже существующие - '' | ||
+ | |||
+ | Как сослаться на ConfigMap в описании контейнера: | ||
+ | <code yaml> | ||
+ | # целиком | ||
+ | spec: | ||
+ | containers: | ||
+ | envFrom: | ||
+ | - configMapRef: | ||
+ | name: webapp-config | ||
+ | # или только на значение | ||
+ | env: | ||
+ | - name: APP_COLOR | ||
+ | valueFrom: | ||
+ | configMapKeyRef: | ||
+ | name: webapp-config | ||
+ | key: APP_COLOR | ||
+ | </ | ||
+ | |||
+ | Также, есть возможность использовать ConfigMap как volume: | ||
+ | <code yaml> | ||
+ | volumes: | ||
+ | - name: webapp-config-vol | ||
+ | configMap: | ||
+ | name: webapp-config | ||
+ | </ | ||
+ | |||
+ | ==== С помощью Secrets ==== | ||
+ | Secrets нужны для хранения конфиденциальных данных. Это то же самое, что и ConfigMap, только данные там зашифрованы. | ||
+ | |||
+ | <code bash> | ||
+ | kubectl create secret generic < | ||
+ | kubectl create secret generic < | ||
+ | </ | ||
+ | |||
+ | Описание объекта Secret для декларативного применения: | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Secret | ||
+ | metadata: | ||
+ | name: webapp-secret | ||
+ | data: | ||
+ | DB_HOST: mysql | ||
+ | DB_USER: root | ||
+ | DB_PASSWORD: | ||
+ | </ | ||
+ | Вопрос только в том, что для Secret нужно указывать данные не в plain text, а закодированным в base64. | ||
+ | <code bash> | ||
+ | echo -n " | ||
+ | echo -n " | ||
+ | echo -n " | ||
+ | </ | ||
+ | Так что описание должно выглядеть так: | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Secret | ||
+ | metadata: | ||
+ | name: webapp-secret | ||
+ | data: | ||
+ | DB_HOST: bXlzcWw= | ||
+ | DB_USER: cm9vdA== | ||
+ | DB_PASSWORD: | ||
+ | </ | ||
+ | Просмотр | ||
+ | <code bash> | ||
+ | kubectl get/ | ||
+ | kubectl get secret < | ||
+ | </ | ||
+ | Раскодировать значения | ||
+ | <code bash> | ||
+ | echo -n " | ||
+ | echo -n " | ||
+ | echo -n " | ||
+ | </ | ||
+ | |||
+ | Как сослаться на Secret в описании контейнера | ||
+ | <code yaml> | ||
+ | # целиком | ||
+ | spec: | ||
+ | containers: | ||
+ | envFrom: | ||
+ | - secretRef: | ||
+ | name: webapp-secret | ||
+ | # или только на значение | ||
+ | env: | ||
+ | - name: DB_PASSWORD | ||
+ | valueFrom: | ||
+ | secretKeyRef: | ||
+ | name: webapp-secret | ||
+ | key: DB_PASSWORD | ||
+ | </ | ||
+ | |||
+ | Также, есть возможность использовать Secret как volume: | ||
+ | <code yaml> | ||
+ | volumes: | ||
+ | - name: webapp-secret-vol | ||
+ | secret: | ||
+ | secretName: webapp-secret | ||
+ | </ | ||
+ | |||
+ | В этом случае в контейнере появляется каталог, | ||
+ | |||
+ | Особый секрет docker-registry для логина в приватный репозиторий Docker | ||
+ | <code bash> | ||
+ | kubectl create secret docker-registry docker-config-secret --docker-server=gitlab.example.com: | ||
+ | </ | ||
+ | ===== Обслуживание кластера ===== | ||
+ | Когда хост становится недоступным, | ||
+ | |||
+ | Если прошло меньше 5 минут, то поды заново запускаются на хосте. Нужно также помнить о том, что если под не управляется деплоем или репликой, | ||
+ | |||
+ | Чтобы выгнать нагрузку с хоста, есть команда '' | ||
+ | |||
+ | Для возвращения его в работу нужно дать команду '' | ||
+ | |||
+ | Команда '' | ||
+ | |||
+ | ==== Обновление ==== | ||
+ | Версия API-сервера не должна быть старее, | ||
+ | |||
+ | Обновление поддерживается в пределах трёх минорных версий, | ||
+ | |||
+ | Сначала нужно обновлять мастер-ноды (например, | ||
+ | |||
+ | Если после обновления мастер-ноды запустить '' | ||
+ | <code bash> | ||
+ | # Обновить kubelet | ||
+ | apt upgrade -y kubelet=1.12.0-00 | ||
+ | systemctl restart kubelet | ||
+ | </ | ||
+ | После этого '' | ||
+ | |||
+ | Рабочие ноды можно обновить все сразу, но тогда будет простой. Можно обновлять рабочие ноды последовательно, | ||
+ | <code bash> | ||
+ | # на мастере | ||
+ | kubectl drain < | ||
+ | # на ноде | ||
+ | apt upgrade -y kubeadm=1.12.0-00 | ||
+ | apt upgrade -y kubelet=1.12.0-00 | ||
+ | kubeadm upgrade node config --kubelet-version v1.12.0 | ||
+ | systemctl restart kubelet | ||
+ | # на мастере | ||
+ | kubectl uncordon < | ||
+ | </ | ||
+ | |||
+ | ===== Резервное копирование ===== | ||
+ | Бэкапить надо конфиги, | ||
+ | |||
+ | Рекомендуется использовать конфигурационные файлы и декларативный путь их применения. Даже если всё навернулось, | ||
+ | <code bash> | ||
+ | kubectl get all -A -o yaml > all-deploys.yaml | ||
+ | </ | ||
+ | Тем не менее, это не охватывает все группы ресурсов кластера. Есть сторонние решения для резервного копирования через API типа Velero. | ||
+ | |||
+ | Вместо запросов к API можно делать резервную копию ETCD-кластера. В строке его запуска есть параметр '' | ||
+ | <WRAP round info 100%> | ||
+ | При всех командах к etcd нужно указывать данные для аутентификации | ||
+ | <code bash> | ||
+ | etcdctl snapshot save snapshot.db \ | ||
+ | --endpoints=https:// | ||
+ | --cacert=/ | ||
+ | --cert=/ | ||
+ | --key=/ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # сделать снапшот | ||
+ | etcdctl snapshot save snapshot.db | ||
+ | # состояние снапшота | ||
+ | etcdctl snapshot status snapshot.db | ||
+ | # чтобы восстановить, | ||
+ | service kube-apiserver stop | ||
+ | # а потом восстановить снапшот, | ||
+ | etcdctl snapshot restore snapshot.db --data-dir=/ | ||
+ | </ | ||
+ | Будет инициализирована новая конфигурация кластера и регистрация его членов как новых членов нового кластера, | ||
+ | <code bash> | ||
+ | # перечитать конфиг сервисов | ||
+ | systemctl daemon-reload | ||
+ | # перезапустить etcd | ||
+ | service etcd restart | ||
+ | # запустить API | ||
+ | service kube-apiserver start | ||
+ | </ | ||
+ | |||
+ | Резервное копирование конфигурации ресурсов и ETCD имеют свои достоинства и недостатки, | ||
+ | |||
+ | ===== Безопасность ===== | ||
+ | Первая линия защиты - доступ к API. | ||
+ | - Кто может получать доступ (аутентификация)? | ||
+ | - Что может делать получивший доступ (авторизация)? | ||
+ | |||
+ | В k8s нельзя завести учётки пользователей, | ||
+ | |||
+ | ==== Статические файлы-списки с паролями или токенами ==== | ||
+ | Самая простая форма. Это CSV, где перечислены учётные данные пользователей (пароль, | ||
+ | < | ||
+ | password, | ||
+ | password, | ||
+ | password, | ||
+ | </ | ||
+ | Путь к этому файлу нужно прописать в аргументе запуска kube-apiserver '' | ||
+ | |||
+ | Файл с токенами подобен парольному, | ||
+ | |||
+ | Статические файлы-списки не рекомендуются к использованию, | ||
+ | |||
+ | ==== Сертификаты ==== | ||
+ | Каждый компонент кластера подключается к другому по защищённому асимметричным шифрованием соединению, | ||
+ | |||
+ | Над всем этим должен быть по меньшей мере один УЦ. Можно и второй для пары ETCD - API (как клиента ETCD). УЦ должен подписывать нижестоящие сертификаты. | ||
+ | |||
+ | Как создавать сертификаты | ||
+ | <code bash> | ||
+ | # CA key | ||
+ | openssl genrsa -out ca.key 2048 | ||
+ | # CA csr | ||
+ | openssl req -new -key ca.key -subj "/ | ||
+ | # CA crt (self-signed) | ||
+ | openssl x509 -req -in ca.csr -signkey ca.key -out ca.crt | ||
+ | # admin key | ||
+ | openssl genrsa -out admin.key 2048 | ||
+ | # admin csr (в subj нужно писать реальное имя аутентификации, | ||
+ | openssl req -new -key admin.key -subj "/ | ||
+ | # admin crt (ca signed) | ||
+ | openssl x509 -req -in admin.csr -CA ca.crt -CAkey ca.key -out admin.crt | ||
+ | |||
+ | # для системных компонентов к имени нужно добавить " | ||
+ | openssl req -new -key kube-scheduler.key -subj "/ | ||
+ | openssl req -new -key kube-controller-manager.key -subj "/ | ||
+ | # наконец, | ||
+ | openssl req -new -key kube-proxy.key -subj "/ | ||
+ | # далее по аналогии | ||
+ | |||
+ | # Использовать сертификат в запросе можно так: | ||
+ | curl https:// | ||
+ | --key admin.key --cert admin.crt --cacert ca.crt | ||
+ | </ | ||
+ | |||
+ | Другой способ - в файле конфигурации | ||
+ | <file yaml kube-config.yaml> | ||
+ | apiVersion: v1 | ||
+ | clusters: | ||
+ | - cluster: | ||
+ | certificate-authority: | ||
+ | server: https:// | ||
+ | name: kubernetes | ||
+ | kind: Config | ||
+ | users: | ||
+ | - name: kubernetes-admin | ||
+ | user: | ||
+ | client-certificate: | ||
+ | client-key: admin.key | ||
+ | </ | ||
+ | |||
+ | Всем компонентам для проверки подлинности требуется указывать сертификат УЦ - он указывается **в каждом** клиенте и сервере. | ||
+ | |||
+ | Если ETCD-серверов несколько, | ||
+ | |||
+ | Для API-сервера нужно добавлять все альтернативные имена, например, | ||
+ | < | ||
+ | kubernetes | ||
+ | kubernetes.default | ||
+ | kubernetes.default.svc | ||
+ | kubernetes.default.svc.cluster.local | ||
+ | </ | ||
+ | |||
+ | У каждого кублета на нодах тоже должен быть свой сертификат, | ||
+ | |||
+ | ==== Как проверить сертификаты ==== | ||
+ | <code bash> | ||
+ | # Если k8s установлен вручную и работает как сервисы, | ||
+ | cat / | ||
+ | # и смотреть в логи нужно соответственно | ||
+ | journalctl -u etcd.service -l | ||
+ | # Если же он установлен с помощью kubeadm, то k8s крутится на статических подах и нужно смотреть строчки запуска там. | ||
+ | cat / | ||
+ | # логи | ||
+ | kubectl logs etcd-master | ||
+ | # если нет возможности посмотреть логи через kubectl, тогда через докер | ||
+ | docker ps -a | ||
+ | docker logs < | ||
+ | </ | ||
+ | |||
+ | Создаётся табличка с заголовками типа Component, Type, Cert path, CN, ALT, Org, Issuer, Expiration. Дальше просматриваются свойства сертификатов, | ||
+ | <code bash> | ||
+ | openssl x509 -in / | ||
+ | </ | ||
+ | |||
+ | ==== Certificate API ==== | ||
+ | В Кубере есть инструмент подписи сертификатов, | ||
+ | |||
+ | Теперь, | ||
+ | - Пользователь создаёт ключ и запрос, | ||
+ | - Админ создаёт CertificateSigningRequest, | ||
+ | - Теперь запрос можно увидеть, | ||
+ | |||
+ | Вывести содержимое полученного объекта - '' | ||
+ | |||
+ | ==== KubeConfig ==== | ||
+ | Так как на любую команду, | ||
+ | |||
+ | Этот файл имеет 3 секции - Clusters (перечислены кластеры), | ||
+ | |||
+ | Контекст по умолчанию прописывается в том же файле '' | ||
+ | Вывести текущий конфиг: | ||
+ | Изменить контекст в конфиге: | ||
+ | Помимо самого кластера, | ||
+ | |||
+ | Можно вместо прописывания пути к файлу ('' | ||
+ | |||
+ | ==== Сервисные учётки ==== | ||
+ | Нужны для работы приложений с Кубером, | ||
+ | <code bash> | ||
+ | kubectl create serviceaccount < | ||
+ | kubectl get serviceaccount | ||
+ | </ | ||
+ | Сервисная учётка автоматически создаётся с токеном, | ||
+ | |||
+ | Стандартно в каждом пространстве имён существует сервисная учётка default, которая автоматически предоставляется подам через смонтированный том, давая доступ к API. Если сервисная учётка нужна другая, | ||
+ | |||
+ | Если под создан отдельно, | ||
+ | |||
+ | Если явно не указана сервисная учётка в описании пода, то автоматически монтируется default. Чтобы отключить это, в spec нужно прописать '' | ||
+ | |||
+ | ==== Защита образов ==== | ||
+ | В описании контейнера есть имя образа, | ||
+ | |||
+ | Если репозиторий приватный, | ||
+ | <code bash> | ||
+ | # логин и пароль указываются в интерактивном режиме | ||
+ | docker login private-repo.io | ||
+ | docker run private-repo.io/ | ||
+ | </ | ||
+ | |||
+ | В Кубере, | ||
+ | <code bash> | ||
+ | kubectl create secret docker-registry <secret name> \ | ||
+ | --docker-server= --docker-username= --docker-password= --docker-email= | ||
+ | </ | ||
+ | |||
+ | <code yaml> | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: app | ||
+ | image: private-repo.io/ | ||
+ | imagePullSecrets: | ||
+ | - name: <secret name> | ||
+ | </ | ||
+ | |||
+ | ==== securityContext ==== | ||
+ | В Докере при запуске контейнера можно указывать разные опции безопасности, | ||
+ | <code bash> | ||
+ | docker run --user=1000 ubuntu sleep 5000 | ||
+ | docker run --cap-add NET_ADMIN ubuntu | ||
+ | </ | ||
+ | В Кубере, | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: app | ||
+ | spec: | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | containers: | ||
+ | - name: app | ||
+ | image: ubuntu | ||
+ | command: [" | ||
+ | securityContext: | ||
+ | runAsUser: 1000 | ||
+ | capabilities: | ||
+ | add: [" | ||
+ | </ | ||
+ | В данном случае, | ||
+ | |||
+ | ==== Сетевые политики ==== | ||
+ | <WRAP round info 100%> | ||
+ | Не все сетевые решения для k8s поддерживают сетевые политики, | ||
+ | </ | ||
+ | |||
+ | |||
+ | * Ingress - входящий трафик | ||
+ | * Egress - исходящий трафик | ||
+ | |||
+ | Всем подам по умолчанию разрешён любой трафик. К примеру, | ||
+ | |||
+ | Привязка политики к поду делается так же, как и реплик с сервисами - через ярлыки и селекторы. | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | kind: NetworkPolicy | ||
+ | metadata: | ||
+ | name: db-policy | ||
+ | spec: | ||
+ | # применяется к подам с ярлыком role: db | ||
+ | podSelector: | ||
+ | matchLabels: | ||
+ | role: db | ||
+ | policyTypes: | ||
+ | - Ingress | ||
+ | - Egress | ||
+ | ingress: | ||
+ | # разрешить трафик с подов с ярлыком name: api-pod | ||
+ | - from: | ||
+ | - podSelector: | ||
+ | matchLabels: | ||
+ | name: api-pod | ||
+ | # только для подов из пространства имён с ярлыком name: prod | ||
+ | # не забыть прописать сначала этот ярлык для пространства имён | ||
+ | namespaceSelector: | ||
+ | matchLabels: | ||
+ | name: prod | ||
+ | # разрешить подключаться внешнему серверу | ||
+ | - ipBlock: | ||
+ | cidr: 192.168.1.10/ | ||
+ | ports: | ||
+ | - protocol: TCP | ||
+ | port: 3306 | ||
+ | egress: | ||
+ | # разрешить исходящий трафик на сервер по порту 80 | ||
+ | - to: | ||
+ | - ipBlock: | ||
+ | cidr: 192.168.1.20/ | ||
+ | ports: | ||
+ | - protocol: TCP | ||
+ | port: 80 | ||
+ | </ | ||
+ | |||
+ | Если убрать podSelector и оставить namespaceSelector, | ||
+ | |||
+ | Логика в '' | ||
+ | |||
+ | Далее нужно применить политику. | ||
+ | |||
+ | |||
+ | ====== MicroK8S ====== | ||
+ | https:// | ||
+ | https:// | ||
+ | <code bash> | ||
+ | # install | ||
+ | snap install microk8s --classic | ||
+ | # install add-ons | ||
+ | microk8s.enable dashboard dns ingress registry | ||
+ | |||
+ | # status, info | ||
+ | microk8s status | ||
+ | microk8s kubectl get all | ||
+ | microk8s kubectl get nodes | ||
+ | microk8s kubectl get services | ||
+ | microk8s.kubectl cluster-info | ||
+ | microk8s.kubectl get po --all-namespaces | ||
+ | |||
+ | # добавить юзера в группу | ||
+ | sudo usermod -a -G microk8s < | ||
+ | sudo chown -f -R < | ||
+ | newgrp microk8s | ||
+ | # затем перезайти в систему | ||
+ | |||
+ | # Прописать алиас, чтобы не набирать бесконечное " | ||
+ | echo "alias kubectl=' | ||
+ | echo "alias k=' | ||
+ | # Для того, чтобы автокомплит работал с алиасом | ||
+ | source < | ||
+ | source < | ||
+ | </ | ||
+ | https:// | ||
+ | Alias & auto-completion: | ||
+ | ===== Получить доступ к панели мониторинга ===== | ||
+ | Геморрой начался с самого начала. Приборная панель ставится и включается без проблем, | ||
+ | |||
+ | <code bash> | ||
+ | microk8s.kubectl -n kube-system edit service kubernetes-dashboard | ||
+ | # Заменить type: clusterIP | ||
+ | # на type: NodePort | ||
+ | |||
+ | # Узнать порт | ||
+ | microk8s.kubectl -n kube-system get services | ||
+ | NAME TYPE CLUSTER-IP | ||
+ | dashboard-metrics-scraper | ||
+ | kube-dns | ||
+ | kubernetes-dashboard | ||
+ | metrics-server | ||
+ | </ | ||
+ | Теперь можно заходить по https://, адресу ноды и, в данном случае, | ||
+ | |||
+ | Узнать [[https:// | ||
+ | < | ||
+ | token=$(microk8s kubectl -n kube-system get secret | grep default-token | cut -d " " -f1) | ||
+ | microk8s kubectl -n kube-system describe secret $token | ||
+ | </ | ||
+ | https:// | ||
+ | https:// | ||
+ | |||
+ | ===== Кластер ===== | ||
+ | <code bash> | ||
+ | # На будущей мастер-ноде выполнить | ||
+ | microk8s.add-node | ||
+ | # Полученную ссылку запустить на будущей подчинённой ноде | ||
+ | </ | ||
+ | |||
+ | ===== Развёртывание приложения ===== | ||
+ | <code bash> | ||
+ | # Название, | ||
+ | kubectl create deployment kubernetes-bootcamp --image=gcr.io/ | ||
+ | # Публикация - прокси даёт доступ с локального хоста и работает, | ||
+ | kubectl proxy | ||
+ | # проверка | ||
+ | curl http:// | ||
+ | </ | ||
+ | |||
+ | ===== Поды, узлы ===== | ||
+ | Под - из чего состоит приложение: | ||
+ | Узел - рабочая машина, | ||
+ | |||
+ | В каждом узле Kubernetes как минимум работает: | ||
+ | * Kubelet — процесс, | ||
+ | * Среда выполнения контейнера (например, | ||
+ | |||
+ | Наиболее распространенные операции: | ||
+ | * kubectl get — вывод списка ресурсов | ||
+ | * kubectl describe — вывод подробной информации о ресурсе | ||
+ | * kubectl logs — вывод логов контейнера в поде | ||
+ | * kubectl exec — выполнение команды в контейнере пода | ||
+ | |||
+ | Перечисленные выше команды можно использовать, | ||
+ | |||
+ | ===== Создание сервиса для открытия доступа к приложению ===== | ||
+ | Под - расходный материал, | ||
+ | |||
+ | Сервисы - логическое объединение подов. | ||
+ | |||
+ | Публикация сервиса | ||
+ | <code bash> | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes | ||
+ | |||
+ | root@vmls-k8s1: | ||
+ | service/ | ||
+ | |||
+ | root@vmls-k8s1: | ||
+ | NAME TYPE CLUSTER-IP | ||
+ | kubernetes | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | Labels: | ||
+ | Annotations: | ||
+ | Selector: | ||
+ | Type: | ||
+ | IP: | ||
+ | Port: < | ||
+ | TargetPort: | ||
+ | NodePort: | ||
+ | Endpoints: | ||
+ | Session Affinity: | ||
+ | External Traffic Policy: | ||
+ | Events: | ||
+ | </ | ||
+ | |||
+ | Ярлыки (labels) | ||
+ | <code bash> | ||
+ | # Узнать имена ярлыков | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | CreationTimestamp: | ||
+ | Labels: | ||
+ | Annotations: | ||
+ | Selector: | ||
+ | Replicas: | ||
+ | StrategyType: | ||
+ | MinReadySeconds: | ||
+ | RollingUpdateStrategy: | ||
+ | Pod Template: | ||
+ | Labels: | ||
+ | Containers: | ||
+ | | ||
+ | Image: | ||
+ | Port: < | ||
+ | Host Port: < | ||
+ | Environment: | ||
+ | Mounts: | ||
+ | Volumes: | ||
+ | Conditions: | ||
+ | Type | ||
+ | ---- | ||
+ | Available | ||
+ | Progressing | ||
+ | OldReplicaSets: | ||
+ | NewReplicaSet: | ||
+ | Events: | ||
+ | |||
+ | # Запрос подов по имени ярлыка | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949-btv4z | ||
+ | |||
+ | # Запрос сервисов по имени ярлыка | ||
+ | root@vmls-k8s1: | ||
+ | NAME TYPE | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | # Задать новый ярлык | ||
+ | root@vmls-k8s1: | ||
+ | pod/ | ||
+ | |||
+ | # Запрос подов, где виден новый ярлык | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | Priority: | ||
+ | Node: | ||
+ | Start Time: Mon, 17 Aug 2020 12:04:21 +0000 | ||
+ | Labels: | ||
+ | pod-template-hash=6f6656d949 | ||
+ | Annotations: | ||
+ | Status: | ||
+ | IP: | ||
+ | IPs: | ||
+ | IP: 10.1.17.2 | ||
+ | Containers: | ||
+ | kubernetes-bootcamp: | ||
+ | Container ID: | ||
+ | Image: | ||
+ | Image ID: | ||
+ | Port: < | ||
+ | Host Port: < | ||
+ | State: | ||
+ | Started: | ||
+ | Ready: | ||
+ | Restart Count: | ||
+ | Environment: | ||
+ | Mounts: | ||
+ | / | ||
+ | Conditions: | ||
+ | Type Status | ||
+ | Initialized | ||
+ | Ready True | ||
+ | ContainersReady | ||
+ | PodScheduled | ||
+ | Volumes: | ||
+ | default-token-gzgxh: | ||
+ | Type: Secret (a volume populated by a Secret) | ||
+ | SecretName: | ||
+ | Optional: | ||
+ | QoS Class: | ||
+ | Node-Selectors: | ||
+ | Tolerations: | ||
+ | | ||
+ | Events: | ||
+ | |||
+ | # Запрос подов с новым ярлыком | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949-btv4z | ||
+ | </ | ||
+ | |||
+ | Удаление сервиса | ||
+ | <code bash> | ||
+ | root@vmls-k8s1: | ||
+ | service " | ||
+ | |||
+ | # Убедиться, | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes | ||
+ | |||
+ | # и что он недоступен | ||
+ | root@vmls-k8s1: | ||
+ | curl: (7) Failed to connect to localhost port 31689: Connection refused | ||
+ | |||
+ | # в то время под живой и как изнутри него всё работает | ||
+ | root@vmls-k8s1: | ||
+ | % Total % Received % Xferd Average Speed | ||
+ | | ||
+ | 100 84 0 84 0 | ||
+ | Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6f6656d949-btv4z | v=1 | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | ===== Масштабирование ===== | ||
+ | В случае масштабирования развёртывания создаются новые поды, которые распределяются по узлам с доступными ресурсами. У сервисов есть встроенный балансировщик нагрузки, | ||
+ | <code bash> | ||
+ | |||
+ | # Под пока 1 | ||
+ | root@vmls-k8s1: | ||
+ | NAME READY | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | # Посмотреть ReplicaSet | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949 | ||
+ | |||
+ | # Размножить | ||
+ | root@vmls-k8s1: | ||
+ | deployment.apps/ | ||
+ | |||
+ | # Теперь подов 4 | ||
+ | root@vmls-k8s1: | ||
+ | NAME READY | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949-2k99k | ||
+ | kubernetes-bootcamp-6f6656d949-52l9m | ||
+ | kubernetes-bootcamp-6f6656d949-btv4z | ||
+ | kubernetes-bootcamp-6f6656d949-lhnkd | ||
+ | |||
+ | # В логах есть отражение этого факта | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | CreationTimestamp: | ||
+ | Labels: | ||
+ | Annotations: | ||
+ | Selector: | ||
+ | Replicas: | ||
+ | StrategyType: | ||
+ | MinReadySeconds: | ||
+ | RollingUpdateStrategy: | ||
+ | Pod Template: | ||
+ | Labels: | ||
+ | Containers: | ||
+ | | ||
+ | Image: | ||
+ | Port: < | ||
+ | Host Port: < | ||
+ | Environment: | ||
+ | Mounts: | ||
+ | Volumes: | ||
+ | Conditions: | ||
+ | Type | ||
+ | ---- | ||
+ | Progressing | ||
+ | Available | ||
+ | OldReplicaSets: | ||
+ | NewReplicaSet: | ||
+ | Events: | ||
+ | Type Reason | ||
+ | ---- ------ | ||
+ | Normal | ||
+ | </ | ||
+ | |||
+ | Балансировка | ||
+ | <code bash> | ||
+ | # Смотрим свойства сервиса - порт ноды | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | Labels: | ||
+ | Annotations: | ||
+ | Selector: | ||
+ | Type: | ||
+ | IP: | ||
+ | Port: < | ||
+ | TargetPort: | ||
+ | NodePort: | ||
+ | Endpoints: | ||
+ | Session Affinity: | ||
+ | External Traffic Policy: | ||
+ | Events: | ||
+ | |||
+ | # Теперь при каждом запросе они идут в разные поды | ||
+ | root@vmls-k8s1: | ||
+ | Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6f6656d949-rztf4 | v=1 | ||
+ | root@vmls-k8s1: | ||
+ | Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6f6656d949-lhnkd | v=1 | ||
+ | root@vmls-k8s1: | ||
+ | Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-6f6656d949-52l9m | v=1 | ||
+ | </ | ||
+ | |||
+ | Свёртывание масштабирования | ||
+ | <code bash> | ||
+ | root@vmls-k8s1: | ||
+ | deployment.apps/ | ||
+ | |||
+ | root@vmls-k8s1: | ||
+ | NAME READY | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949-glxgv | ||
+ | kubernetes-bootcamp-6f6656d949-lhnkd | ||
+ | kubernetes-bootcamp-6f6656d949-qmct2 | ||
+ | kubernetes-bootcamp-6f6656d949-rztf4 | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | |||
+ | ===== Плавающие обновления (rolling updates) ===== | ||
+ | Плавающие обновления позволяют обновить развёртывания без простоев, | ||
+ | В предыдущем модуле мы промасштабировали приложение до нескольких экземпляров. Это необходимо сделать, | ||
+ | Балансировщик будет работать только для доступных подов. | ||
+ | <code bash> | ||
+ | # Список развёртываний | ||
+ | root@vmls-k8s1: | ||
+ | NAME READY | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | # Список подов | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949-lhnkd | ||
+ | kubernetes-bootcamp-6f6656d949-qhlcg | ||
+ | kubernetes-bootcamp-6f6656d949-qmct2 | ||
+ | kubernetes-bootcamp-6f6656d949-rvflg | ||
+ | |||
+ | # Описание подов (о версии см. строку Image) | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | Priority: | ||
+ | Node: | ||
+ | Start Time: Wed, 19 Aug 2020 12:45:51 +0000 | ||
+ | Labels: | ||
+ | pod-template-hash=6f6656d949 | ||
+ | Annotations: | ||
+ | Status: | ||
+ | IP: | ||
+ | IPs: | ||
+ | IP: | ||
+ | Controlled By: ReplicaSet/ | ||
+ | Containers: | ||
+ | kubernetes-bootcamp: | ||
+ | Container ID: | ||
+ | Image: | ||
+ | # дальнейшая простыня обрезана | ||
+ | |||
+ | # Обновить до v2 | ||
+ | root@vmls-k8s1: | ||
+ | deployment.apps/ | ||
+ | |||
+ | # Старые поды убиваются, | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-6f6656d949-lhnkd | ||
+ | kubernetes-bootcamp-6f6656d949-qhlcg | ||
+ | kubernetes-bootcamp-6f6656d949-qmct2 | ||
+ | kubernetes-bootcamp-6f6656d949-rvflg | ||
+ | kubernetes-bootcamp-86656bc875-5hmdv | ||
+ | kubernetes-bootcamp-86656bc875-5pj79 | ||
+ | kubernetes-bootcamp-86656bc875-c8mpt | ||
+ | kubernetes-bootcamp-86656bc875-s75z4 | ||
+ | </ | ||
+ | |||
+ | Проверка | ||
+ | <code bash> | ||
+ | # Узнаём порт для подключения | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | Labels: | ||
+ | Annotations: | ||
+ | Selector: | ||
+ | Type: | ||
+ | IP: | ||
+ | Port: < | ||
+ | TargetPort: | ||
+ | NodePort: | ||
+ | Endpoints: | ||
+ | Session Affinity: | ||
+ | External Traffic Policy: | ||
+ | Events: | ||
+ | Type | ||
+ | ---- | ||
+ | Warning | ||
+ | |||
+ | # Проверка запросом, | ||
+ | root@vmls-k8s1: | ||
+ | Hello Kubernetes bootcamp! | Running on: kubernetes-bootcamp-86656bc875-s75z4 | v=2 | ||
+ | |||
+ | # Ещё один способ проверить | ||
+ | root@vmls-k8s1: | ||
+ | deployment " | ||
+ | |||
+ | # Проверка версии Image подов | ||
+ | root@vmls-k8s1: | ||
+ | Name: | ||
+ | Namespace: | ||
+ | Priority: | ||
+ | Node: | ||
+ | Start Time: Wed, 19 Aug 2020 13:24:47 +0000 | ||
+ | Labels: | ||
+ | pod-template-hash=86656bc875 | ||
+ | Annotations: | ||
+ | Status: | ||
+ | IP: | ||
+ | IPs: | ||
+ | IP: | ||
+ | Controlled By: ReplicaSet/ | ||
+ | Containers: | ||
+ | kubernetes-bootcamp: | ||
+ | Container ID: | ||
+ | Image: | ||
+ | # дальнейшая простыня обрезана | ||
+ | </ | ||
+ | |||
+ | Откат обновления | ||
+ | <code bash> | ||
+ | # Проба обновиться до версии 10 | ||
+ | root@vmls-k8s1: | ||
+ | deployment.apps/ | ||
+ | |||
+ | # Проверяем - обновление не прошло | ||
+ | root@vmls-k8s1: | ||
+ | NAME READY | ||
+ | kubernetes-bootcamp | ||
+ | |||
+ | |||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-64468f5bc5-p8zb6 | ||
+ | kubernetes-bootcamp-64468f5bc5-s2btt | ||
+ | kubernetes-bootcamp-86656bc875-5hmdv | ||
+ | kubernetes-bootcamp-86656bc875-c8mpt | ||
+ | kubernetes-bootcamp-86656bc875-s75z4 | ||
+ | |||
+ | # Если запустить | ||
+ | root@vmls-k8s1: | ||
+ | # , то будет ясно, что такой версии нет | ||
+ | |||
+ | # Откатываем назад, к предыдущей рабочей версии | ||
+ | root@vmls-k8s1: | ||
+ | deployment.apps/ | ||
+ | |||
+ | # Всё снова в порядке | ||
+ | root@vmls-k8s1: | ||
+ | NAME | ||
+ | kubernetes-bootcamp-86656bc875-5hmdv | ||
+ | kubernetes-bootcamp-86656bc875-786cb | ||
+ | kubernetes-bootcamp-86656bc875-c8mpt | ||
+ | kubernetes-bootcamp-86656bc875-s75z4 | ||
+ | |||
+ | # В строке Image будет видно, что опять развёрнута v2 | ||
+ | root@vmls-k8s1: | ||
+ | </ | ||
+ | |||
+ | https:// | ||
+ | |||
+ | |||
+ | ====== | ||
+ | {{ : | ||
+ | |||
+ | Master node - управляющий узел (или несколько узлов в HA mode)\\ | ||
+ | Worker nodes - рабочие\\ | ||
+ | etcd - распределённое хранилище ключей-значений, | ||
+ | |||
+ | ===== Компоненты мастера ===== | ||
+ | * API server - центральный компонент, | ||
+ | * Scheduler - распределяет объекты, | ||
+ | * Controller managers - запускает процессы (controllers), | ||
+ | * etcd - хранилище данных состояния кластера. Данные добавляются, | ||
+ | |||
+ | ===== Компоненты рабочего узла ===== | ||
+ | * Container Runtime - среда запуска контейнеров (Docker, CRI-O, containerd, rkt, rktlet) | ||
+ | * kubelet - агент, управляемый с мастер-ноды, | ||
+ | * kube-proxy - сетевой агент, отвечающий за динамическое обновление, | ||
+ | * addons - сторонние добавления, | ||
+ | |||
+ | ===== Сеть ===== | ||
+ | Из-за особенностей архитектуры микросервисов, | ||
+ | - Между контейнерами внутри пода (Container-to-container) | ||
+ | - Между подами на одной ноде или между нодами в кластере (Pod-to-Pod) | ||
+ | - Между подом и сервисом в одном пространстве имени (namespace) или между пространств имён в кластере (Pod-to-Service) | ||
+ | - Между внешними клиентами и сервисом (External-to-Service) | ||
+ | Все эти вещи должны быть определены до внедрения кластера K8s. | ||
+ | |||
+ | ==== Container-to-container ==== | ||
+ | При запуске каждого контейнера для него создается изолированное сетевое пространство (network namespace), которое называется сетевым пространством имён. Сетевое пространство имен совместно используется контейнерами вместе с операционной системой хоста. Когда запущен под, network namespace создаётся внутри него, и все контейнеры внутри делят это пространство и могут обращаться друг к другу через localhost. | ||
+ | |||
+ | ==== Pod-to-Pod ==== | ||
+ | В K8s поды распределяются между нодами случайным образом. Независимо от расположения подразумевается, | ||
+ | В K8s поды рассматриваются как VM в сети, где каждая имеет свой IP-адрес. Эта модель называется IP-per-Pod и гарантирует то, что поды могут общаться друг с другом. Контейнеры внутри пода делят сетевое пространство имён и должны координировать назначенные порты равно как приложения внутри ОС. Тем не менее, контейнеры, | ||
+ | |||
+ | ==== Pod-to-External World ==== | ||
+ | Для нормальной работы с контейнеризованными приложениями их надо опубликовать во внешний мир. В K8s это делается через сервисы (services), комплексные сущности, | ||
+ | |||
+ | ===== Установка ===== | ||
+ | 4 основных конфигурации: | ||
+ | - Всё в одном - мастер и рабочая нода совмещены. Подходит для тестовых, | ||
+ | - 1-нодовый etcd, 1-мастер, | ||
+ | - 1-нодовый etcd, мультимастер, | ||
+ | - Мульти-etcd, | ||
+ | |||
+ | ==== Установка на локальную машину ==== | ||
+ | * Minikube - single-node local Kubernetes cluster (рекомендуется) | ||
+ | * Docker Desktop - single-node local Kubernetes cluster for Windows and Mac | ||
+ | * CDK on LXD - multi-node local cluster with LXD containers. | ||
+ | |||
+ | ==== Локально на VM ==== | ||
+ | VMs могут быть созданы Vagrant, VMware vSphere, KVM или чем-то другим. Есть разные программы автоматизации установки, | ||
+ | |||
+ | ==== Локально на голое железо ==== | ||
+ | Поверх ОС, таких как RHEL, CoreOS, CentOS, Fedora, Ubuntu и т. д. Большинство программ автоматизации для VM подходят и здесь. | ||
+ | |||
+ | Устанавливать можно также на хостинге, | ||
+ | |||
+ | ==== Программы для установки ==== | ||
+ | * kubeadm - рекомендованный способ создания single- or multi-node Kubernetes cluster. Не поддерживает provisioning of hosts. | ||
+ | * kubespray - AWS, GCE, Azure, OpenStack, or bare metal. | ||
+ | * kops | ||
+ | * kube-aws | ||
+ | * Можно ставить from scratch, если ничего не подходит. | ||
+ | |||
+ | ==== Требования для minikube ==== | ||
+ | - kubectl - управление кластером K8s. Ставится отдельно. | ||
+ | - Гипервизор 2-го типа, типа Hyper-V. Minikube поддерживает опцию --vm-driver=none, | ||
+ | - В BIOS нужно включить VT-x/AMD-v. | ||
+ | - При первом запуске Minikube нужен выход в интернет для скачивания необходимых пакетов, | ||
+ | |||
+ | <code powershell> | ||
+ | # Поставить в Hyper-V, движок CRI-O | ||
+ | minikube.exe start --container-runtime=cri-o --driver=hyperv | ||
+ | # состояние | ||
+ | minikube status | ||
+ | # зайти внутрь виртуалки | ||
+ | minikube ssh | ||
+ | </ | ||
+ | https:// | ||
+ | |||
+ | <code bash> | ||
+ | # Т. к. докер не установлен, | ||
+ | $ sudo docker container ls | ||
+ | Cannot connect to the Docker daemon at unix:/// | ||
+ | # В CRI-O другая команда: | ||
+ | $ sudo runc list | ||
+ | </ | ||
+ | |||
+ | ===== Доступ к системе ===== | ||
+ | * kubectl - CLI tool | ||
+ | * dashboard - web-based user interface | ||
+ | * curl - access the cluster via APIs | ||
+ | |||
+ | API делится на 3 группы: | ||
+ | - Core Group (/api/v1) - содержит объекты: | ||
+ | - Named Group (/ | ||
+ | - System-wide (/healthz, /logs, /metrics, /ui и т. д.) | ||
+ | Доступ к API может быть непосредственным или через CLI/Web UI. | ||
+ | |||
+ | kubectl обычно ставится до minicube, но можно ставить и после. После установки kubectl получает свою конфигурацию от minikube автоматически (в иных случаях может понадобиться настройка). Рекомендуется версию kubectl иметь ту же, то и версию minikube. | ||
+ | |||
+ | Конфиг лежит в ~/.kube. | ||
+ | <code powershell> | ||
+ | # Вывести на экран: | ||
+ | kubectl config view | ||
+ | # Информация о кластере: | ||
+ | kubectl cluster-info | ||
+ | # Включить и открыть веб-панель | ||
+ | minikube dashboard | ||
+ | # kubectl proxy публикует веб-панель наружу, | ||
+ | </ | ||
+ | |||
+ | Если просто запустить kubectl proxy, то публикуется порт 8001, через который можно обратиться к API, например, | ||
+ | curl http:// | ||
+ | http:// | ||
+ | http:// | ||
+ | http:// | ||
+ | http:// | ||
+ | |||
+ | Чтобы обратиться к API без запуска kubectl proxy, нужна аутентификация через Bearer Token или предоставив набор ключей и сертификатов. Bearer Token генерируется на мастер-ноде и возвращается клиенту, | ||
+ | |||
+ | <code bash> | ||
+ | # Get the token: | ||
+ | $TOKEN=$(kubectl describe secret -n kube-system $(kubectl get secrets -n kube-system | grep default | cut -f1 -d ' ') | grep -E ' | ||
+ | # Get the API server endpoint: | ||
+ | $APISERVER=$(kubectl config view | grep https | cut -f 2- -d ":" | ||
+ | # Confirm that the APISERVER stored the same IP as the Kubernetes master IP by issuing the following 2 commands and comparing their outputs: | ||
+ | $ echo $APISERVER | ||
+ | https:// | ||
+ | $ kubectl cluster-info | ||
+ | Kubernetes master is running at https:// | ||
+ | # Access the API server using the curl command, as shown below: | ||
+ | $ curl $APISERVER --header " | ||
+ | { | ||
+ | " | ||
+ | "/ | ||
+ | "/ | ||
+ | "/ | ||
+ | "/ | ||
+ | | ||
+ | | ||
+ | "/ | ||
+ | "/ | ||
+ | "/ | ||
+ | "/ | ||
+ | ] | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Помимо токена, | ||
+ | <code bash> | ||
+ | curl $APISERVER --cert encoded-cert --key encoded-key --cacert encoded-ca | ||
+ | </ | ||
+ | |||
+ | ===== Объекты K8s ===== | ||
+ | Объектная модель K8s представляет различные постоянные сущности в кластере. Эти сущности описывают: | ||
+ | * Какие приложения запущены и на какой ноде | ||
+ | * Потребление ресурсов | ||
+ | * Политики приложений: | ||
+ | |||
+ | Для каждого объекта определяется секция spec, где описывается желаемое состояние (desired state). Кластер отслеживает статус объекта (status) и сравнивает текущее состояние (actual state) с желаемым. Описание идёт в формате YAML, которое kubectl конвертирует в JSON и посылает API. | ||
+ | |||
+ | Пример: | ||
+ | <code yaml> | ||
+ | # required - API endpoint, к которому идёт подключение, | ||
+ | apiVersion: apps/v1 | ||
+ | # required - тип объекта (варианты: | ||
+ | kind: Deployment | ||
+ | # required - осн. информация: | ||
+ | metadata: | ||
+ | name: nginx-deployment | ||
+ | labels: | ||
+ | app: nginx | ||
+ | # required - desired state of the Deployment object | ||
+ | spec: | ||
+ | replicas: 3 | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: nginx | ||
+ | # Pods template, удаление вышестоящих значений metadata и labels | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: nginx | ||
+ | # desired state of the Pod | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx: | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | </ | ||
+ | После создания объекта Deployment, K8s определяет его статус, | ||
+ | |||
+ | ==== Pods ==== | ||
+ | Под - это наименьшая логическая единица, | ||
+ | * Помещаются на одну ноду в рамках пода | ||
+ | * Делят общее сетевое пространство | ||
+ | * Имеют доступ к одним и тем же внешним хранилищам (volumes) | ||
+ | |||
+ | Поды не умеют сами восстанавливаться (self-heal), | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: nginx-pod | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx: | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | </ | ||
+ | |||
+ | ==== Labels ==== | ||
+ | Ярлыки нужны для организации и выбора объектов, | ||
+ | Ключей два: **app** и **env.** Например, | ||
+ | < | ||
+ | app=frontend | ||
+ | env=qa | ||
+ | |||
+ | app=backend | ||
+ | env=dev | ||
+ | </ | ||
+ | |||
+ | Селекторы ярлыков - используются контроллерами. Есть 2 типа: | ||
+ | * Equality-Based Selectors - позволяет фильтровать объекты по ключам и значениям ярлыков. = или == равно, != не равно. Например, | ||
+ | * Set-Based Selectors - позволяет фильтровать объекты по наборам значений. In, notin для значений, | ||
+ | |||
+ | ==== ReplicationControllers ==== | ||
+ | ReplicationControllers регулируют требуемое кол-во запущенных подов в кластере, | ||
+ | Стандартный контроллер - это Deployment, настраивающий ReplicaSet для управления жизненным циклом подов. | ||
+ | |||
+ | ==== ReplicaSets ==== | ||
+ | ReplicaSet - контроллер репликации нового поколения. Поддерживает оба селектора ярлыков (equality- and set-based selectors), в то время как ReplicationControllers поддерживают только equality-based. В настоящий момент, | ||
+ | |||
+ | Масштабирование подов может быть реализовано вручную или автоматически (autoscaler). | ||
+ | |||
+ | ReplicaSets сами могут быть использованы независимо как Pod controllers, | ||
+ | |||
+ | ==== Deployments ==== | ||
+ | Объекты Deployment занимаются обновлением (declarative updates) подов и ReplicaSets. DeploymentController - один из компонентов мастер-ноды, | ||
+ | |||
+ | Например, | ||
+ | |||
+ | Если в Deployment поменять шаблон подов, к примеру, | ||
+ | |||
+ | Обновление запускается, | ||
+ | |||
+ | Когда обновление завершено Deployment показывает обе ReplicaSets (A и B), где A содержит 0 подов, а B - 3. Таким образом Deployment сохраняет своё предыдущее состояние, | ||
+ | |||
+ | ==== Namespaces ==== | ||
+ | Если пользователей или команд, | ||
+ | |||
+ | Перечислить все пространства имён: | ||
+ | <code bash> | ||
+ | $ kubectl get namespaces | ||
+ | NAME | ||
+ | default | ||
+ | kube-node-lease | ||
+ | kube-public | ||
+ | kube-system | ||
+ | kubernetes-dashboard | ||
+ | </ | ||
+ | |||
+ | Обычно Kubernetes создаёт 4 пространства: | ||
+ | - kube-system - содержит объекты, | ||
+ | - kube-public - особое пространство, | ||
+ | - kube-node-lease - самое новое пространство, | ||
+ | - default - объекты и ресурсы, | ||
+ | |||
+ | Хорошей практикой считается создавать больше пространств имён, если требуется разграничение по пользователям и командам. Чтобы разделить кластер на пространства имён, используются ресурсные квоты ([[https:// | ||
+ | |||
+ | ==== Пример обновления Deployment и его отката ==== | ||
+ | <code bash> | ||
+ | # создать | ||
+ | $ kubectl create deployment mynginx --image=nginx: | ||
+ | deployment.apps/ | ||
+ | |||
+ | # вывести информацию о deployment, replicaset, pods под ярлыком app=mynginx | ||
+ | $ kubectl get deploy, | ||
+ | NAME READY | ||
+ | deployment.apps/ | ||
+ | |||
+ | NAME | ||
+ | replicaset.apps/ | ||
+ | |||
+ | NAME | ||
+ | pod/ | ||
+ | |||
+ | # Масштабировать до 3 экз. | ||
+ | $ kubectl scale deploy mynginx --replicas=3 | ||
+ | deployment.apps/ | ||
+ | |||
+ | # инфо ещё раз - теперь будет 3 пода | ||
+ | $ kubectl get deploy, | ||
+ | NAME READY | ||
+ | deployment.apps/ | ||
+ | |||
+ | NAME | ||
+ | replicaset.apps/ | ||
+ | |||
+ | NAME | ||
+ | pod/ | ||
+ | pod/ | ||
+ | pod/ | ||
+ | |||
+ | # Обратите внимание на номер replicaset (в данном случае, | ||
+ | $ kubectl describe deployment |grep " | ||
+ | Image: | ||
+ | |||
+ | # Номера ревизий деплоя nginx | ||
+ | $ kubectl rollout history deploy mynginx | ||
+ | deployment.apps/ | ||
+ | REVISION | ||
+ | 1 < | ||
+ | |||
+ | # Детали 1-й ревизии. Видно номер replicaset и образ, с которыми ревизия связана. | ||
+ | $ kubectl rollout history deploy mynginx --revision=1 | ||
+ | deployment.apps/ | ||
+ | Pod Template: | ||
+ | Labels: | ||
+ | pod-template-hash=76bcb97766 | ||
+ | Containers: | ||
+ | | ||
+ | Image: | ||
+ | Port: < | ||
+ | Host Port: < | ||
+ | Environment: | ||
+ | Mounts: | ||
+ | Volumes: | ||
+ | | ||
+ | # Обновить образ деплоя | ||
+ | $ kubectl set image deployment mynginx nginx=nginx: | ||
+ | deployment.apps/ | ||
+ | |||
+ | # Теперь ревизий две, появилась ещё одна ReplicaSet, все три пода перешли под неё. | ||
+ | |||
+ | # Откатить обновление обратно | ||
+ | $ kubectl rollout undo deployment mynginx --to-revision=1 | ||
+ | deployment.apps/ | ||
+ | |||
+ | # Ревизия 1 стала ревизией 3 | ||
+ | $ kubectl rollout history deploy mynginx | ||
+ | deployment.apps/ | ||
+ | REVISION | ||
+ | 2 < | ||
+ | 3 < | ||
+ | </ | ||
+ | Стандартная настройка позволяет последовательно обновиться 10 раз и откатиться на любую версию. | ||
+ | |||
+ | ===== Аутентификация, | ||
+ | Каждый запрос к API проходит эти три стадии. | ||
+ | * Аутентификация - вход для пользователя | ||
+ | * Авторизация - пускает API-запросы от вошедшего пользователя | ||
+ | * Контроль входа (Admission Control) - модули, | ||
+ | |||
+ | ==== Аутентификация ==== | ||
+ | В K8s нет объекта user, и не хранится никаких логинов (usernames) и прочих связанных с этим данных. Тем не менее, K8s может использовать логины для входа. Есть 2 типа пользователей: | ||
+ | * Обычные пользователи (Normal Users) - управляются вне кластера, | ||
+ | * Служебные учётки (Service Accounts) - через них внутренние процессы кластера подключаются к API для выполнения разных операций. Большинство служебных учёток создаются автоматически самим API, но они также могут быть созданы и вручную. Служебные учётки связаны с выделенным им пространством имён и получают соответствующие права подключения к API как Секреты (as Secrets). | ||
+ | |||
+ | Поддерживается и анонимный доступ, | ||
+ | |||
+ | Для аутентификации используются следующие модули: | ||
+ | * Клиентские сертификаты - указывается файл, содержащий один или несколько УЦ с опцией --client-ca-file=SOMEFILE для сервера API. УЦ, упомянутые в файле, должны удостоверять клиента, | ||
+ | * Статический токен-файл - файл с заранее заданными bearer tokens, опция --token-auth-file=SOMEFILE для сервера API. В настоящее время эти токены не имеют срока действия и не могут быть изменены без перезапуска сервера API. | ||
+ | * Bootstrap-токены - этот способ пока на тестировании. Используется по большей части для bootstrapping нового K8s-кластера. | ||
+ | * Статический парольный файл - то же, что и статический токен-файл. Это файл с базовой информацией об аутентификации, | ||
+ | * Токены служебных учёток - это автоматически включённый механизм, | ||
+ | * Токены OpenID - служат для подключения к OAuth2-провайдерам типа Azure Active Directory, Salesforce, Google и т. д. для снижения нагрузки путём выноса аутентификации на сторонние сервисы. | ||
+ | * Токены Webhook - выноспроверки bearer tokens на сторонний сервис. | ||
+ | * Аутентифицирующий прокси - если нужна какая-то доп. логика аутентификации. | ||
+ | |||
+ | Можно включить сразу несколько способов, | ||
+ | |||
+ | ==== Авторизация ==== | ||
+ | После успешной аутентификации, | ||
+ | |||
+ | Так же как и на этапе аутентификации, | ||
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | <code yaml> | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Чтобы включить ABAC authorizer, нужно запустить API-сервер с ключом %%--authorization-mode=ABAC%%. Также, необходимо определить политику авторизации ключом %%--authorization-policy-file=PolicyFile.json%%. | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | В RBAC существует два вида ролей: | ||
+ | - Role - предоставление доступа к ресурсам того или иного пространства имён | ||
+ | - ClusterRole - то же, что и Role, но в рамках всего кластера | ||
+ | |||
+ | Пример Role: | ||
+ | <code yaml> | ||
+ | kind: Role | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | namespace: lfs158 | ||
+ | name: pod-reader | ||
+ | rules: | ||
+ | - apiGroups: ["" | ||
+ | resources: [" | ||
+ | verbs: [" | ||
+ | </ | ||
+ | Здесь создаётся роль pod-reader, имеющая доступ на чтение подов в пространстве имён lfs158. После создания роли к ней можно привязать пользователей с помощью RoleBinding. | ||
+ | |||
+ | У RoleBindings также есть два типа: | ||
+ | - RoleBinding - привязывает пользователей к тому же пространству имён, как Role. Можно сослаться и на ClusterRole, | ||
+ | - ClusterRoleBinding - доступ на уровне кластера и всех пространств имён | ||
+ | |||
+ | Пример RoleBinding: | ||
+ | <code yaml> | ||
+ | kind: RoleBinding | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | metadata: | ||
+ | name: pod-read-access | ||
+ | namespace: lfs158 | ||
+ | subjects: | ||
+ | - kind: User | ||
+ | name: student | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | roleRef: | ||
+ | kind: Role | ||
+ | name: pod-reader | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | </ | ||
+ | Выдава доступа пользователю student на чтение подов в пространстве имён lfs158. | ||
+ | |||
+ | Для включения RBAC authorizer нужно запустить API-сервер с ключом %%--authorization-mode=RBAC%%. RBAC authorizer конфигурирует политики автоматически. | ||
+ | |||
+ | ==== Контроль входа ==== | ||
+ | [[https:// | ||
+ | |||
+ | Для использования контроля входа, нужно запустить API-сервер в ключом %%--enable-admission-plugins%%, | ||
+ | < | ||
+ | --enable-admission-plugins=NamespaceLifecycle, | ||
+ | </ | ||
+ | Некоторые контроллеры входа включены в K8s изначально. | ||
+ | |||
+ | ==== Пример настройки RBAC ==== | ||
+ | <code bash> | ||
+ | # создаётся namespace, к которому будет дан доступ | ||
+ | $ kubectl create namespace lfs158 | ||
+ | # создаётся каталог для сертификатов | ||
+ | $ mkdir rbac | ||
+ | $ cd rbac | ||
+ | # генерация ключа и сертификата | ||
+ | ~/rbac$ openssl genrsa -out student.key 2048 | ||
+ | ~/rbac$ openssl req -new -key student.key -out student.csr -subj "/ | ||
+ | # Закодировать сертификат в base64 и добавить поле request в файл конфигурации .yaml | ||
+ | ~/rbac$ cat student.csr | base64 | tr -d ' | ||
+ | # создать файл конфигурации | ||
+ | ~/rbac$ nano signing-request.yaml | ||
+ | </ | ||
+ | |||
+ | <code yaml> | ||
+ | apiVersion: certificates.k8s.io/ | ||
+ | kind: CertificateSigningRequest | ||
+ | metadata: | ||
+ | name: student-csr | ||
+ | spec: | ||
+ | groups: | ||
+ | - system: | ||
+ | request: <assign base64 value from the cat command> | ||
+ | usages: | ||
+ | - digital signature | ||
+ | - key encipherment | ||
+ | - client auth | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Создать объект запроса на подпись сертификата (certificate signing request object) | ||
+ | ~/rbac$ kubectl create -f signing-request.yaml | ||
+ | # Вывести список запросов, | ||
+ | ~/rbac$ kubectl get csr | ||
+ | # Подтвердить запрос | ||
+ | ~/rbac$ kubectl certificate approve student-csr | ||
+ | # Извлечь подтверждённый сертификат, | ||
+ | ~/rbac$ kubectl get csr student-csr -o jsonpath=' | ||
+ | # Настроить права для student присвоением ключа и сертификата: | ||
+ | ~/rbac$ kubectl config set-credentials student --client-certificate=student.crt --client-key=student.key | ||
+ | # Создать новую запись context в файле клиентской конфигурации kubectl для student, связанную с пространством имён lfs158 кластера minikube: | ||
+ | ~/rbac$ kubectl config set-context student-context --cluster=minikube --namespace=lfs158 --user=student | ||
+ | # теперь, | ||
+ | ~/rbac$ kubectl config view | ||
+ | |||
+ | # Создать новый деплой в пространстве имён lfs158 | ||
+ | ~/rbac$ kubectl -n lfs158 create deployment nginx --image=nginx: | ||
+ | # если сейчас попробовать вывести список подов из контекста student-context, | ||
+ | # т. к. в нём не настроены разрешения для student | ||
+ | ~/rbac$ kubectl --context=student-context get pods | ||
+ | # Создать конф. файл для ролевого объекта pod-reader, который позволяет get, watch, list в lfs158 для подов. | ||
+ | ~/rbac$ vim role.yaml | ||
+ | </ | ||
+ | <code yaml> | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | kind: Role | ||
+ | metadata: | ||
+ | name: pod-reader | ||
+ | namespace: lfs158 | ||
+ | rules: | ||
+ | - apiGroups: ["" | ||
+ | resources: [" | ||
+ | verbs: [" | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Создать ролевой объект | ||
+ | ~/rbac$ kubectl create -f role.yaml | ||
+ | # Вывести его из изначального контекста minikube, но из пространства имён lfs158: | ||
+ | ~/rbac$ kubectl -n lfs158 get roles | ||
+ | # Создать конф. файл YAML для объекта rolebinding, | ||
+ | ~/rbac$ nano rolebinding.yaml | ||
+ | </ | ||
+ | <code yaml> | ||
+ | apiVersion: rbac.authorization.k8s.io/ | ||
+ | kind: RoleBinding | ||
+ | metadata: | ||
+ | name: pod-read-access | ||
+ | namespace: lfs158 | ||
+ | subjects: | ||
+ | - kind: User | ||
+ | name: student | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | roleRef: | ||
+ | kind: Role | ||
+ | name: pod-reader | ||
+ | apiGroup: rbac.authorization.k8s.io | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Создать объект rolebinding | ||
+ | ~/rbac$ kubectl create -f rolebinding.yaml | ||
+ | # Вывести его из изначального контекста minikube, но из пространства имён lfs158: | ||
+ | ~/rbac$ kubectl -n lfs158 get rolebindings | ||
+ | # Теперь разрешения присвоены, | ||
+ | ~/rbac$ kubectl --context=student-context get pods | ||
+ | </ | ||
+ | ===== Сервисы ===== | ||
+ | {{ : | ||
+ | |||
+ | // | ||
+ | |||
+ | Чтобы получить доступ к приложению, | ||
+ | |||
+ | Группировка осуществляется через // | ||
+ | |||
+ | Сервисы могут представлять одиночные поды, ReplicaSets, | ||
+ | |||
+ | Пример сервиса: | ||
+ | <code yaml> | ||
+ | kind: Service | ||
+ | apiVersion: v1 | ||
+ | metadata: | ||
+ | name: frontend-svc | ||
+ | spec: | ||
+ | selector: | ||
+ | app: frontend | ||
+ | ports: | ||
+ | - protocol: TCP | ||
+ | port: 80 | ||
+ | targetPort: 5000 | ||
+ | </ | ||
+ | |||
+ | В данном случае создаётся сервис frontend-svc, | ||
+ | |||
+ | Теперь клиент подключается к сервису через ClusterIP, который перенаправляет трафик на один из своих подов, балансируя трафик между ними. | ||
+ | |||
+ | Можно также указать порт пода, который получает трафик. В примере, | ||
+ | |||
+ | Логический набор IP-адресов подов вместе с targetPort называется конечной точкой сервиса (Service endpoint). В примере, | ||
+ | |||
+ | ==== kube-proxy ==== | ||
+ | На всех рабочих нодах есть служба kube-proxy, которая смотрит на API-сервер мастер-ноды за добавлением или удалением сервисов и конечных точек. В примере, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | ==== Service Discovery ==== | ||
+ | Так как сервисы - это первичный режим связи в K8s, нужен способ их обнаружения. Их два: | ||
+ | |||
+ | * Переменные окружения (Environment Variables) - так как поды стартуют на любой ноде, служба kubelet добавляет набор переменных окружения в под для всех активных сервисов. Например, | ||
+ | < | ||
+ | REDIS_MASTER_SERVICE_HOST=172.17.0.6 | ||
+ | REDIS_MASTER_SERVICE_PORT=6379 | ||
+ | REDIS_MASTER_PORT=tcp:// | ||
+ | REDIS_MASTER_PORT_6379_TCP=tcp:// | ||
+ | REDIS_MASTER_PORT_6379_TCP_PROTO=tcp | ||
+ | REDIS_MASTER_PORT_6379_TCP_PORT=6379 | ||
+ | REDIS_MASTER_PORT_6379_TCP_ADDR=172.17.0.6 | ||
+ | </ | ||
+ | В этом случае, | ||
+ | |||
+ | * DNS - в K8s есть дополнение DNS, создающее запись DNS для каждого сервиса в формате my-svc.my-namespace.svc.cluster.local. Сервисы могут найти другие сервисы в рамках пространства имён просто по имени. Если добавить сервис redis-master в пространстве имён my-ns, все поды в одном пространстве имён находят сервис по имени, redis-master. Поды из других пространств имён находят этот сервис, | ||
+ | |||
+ | Это общепринятая и рекомендуемая практика. В примере выше видно, что настроен внутренний DNS, где frontend-svc привязан к 172.17.0.4, а db-svc - к 172.17.0.5. | ||
+ | |||
+ | ==== ServiceType ==== | ||
+ | Во время создания сервиса, | ||
+ | * Доступен только в рамках кластера | ||
+ | * Доступен в рамках кластера и извне | ||
+ | * Сопоставляется с объектом (entity), который находится внутри или снаружи кластера | ||
+ | |||
+ | Область доступа определяется ServiceType, | ||
+ | |||
+ | Стандартный ServiceType - это ClusterIP, виртуальный адрес сервиса, | ||
+ | |||
+ | === NodePort === | ||
+ | NodePort ServiceType - это динамически выбираемый порт (стандартно из диапазона 30000-32767), | ||
+ | |||
+ | {{: | ||
+ | |||
+ | NodePort используется для публикации сервиса во внешний мир. Клиент подключается к любой рабочей ноде на указанный порт, и дальше его запросы пробрасываются через ClusterIP сервиса к приложениям. Чтобы получить доступ извне к нескольким приложениям, | ||
+ | |||
+ | === LoadBalancer === | ||
+ | С сервис-типом балансировщика: | ||
+ | * NodePort and ClusterIP создаются автоматически, | ||
+ | * Сервис занимает статический порт на каждой рабочей ноде | ||
+ | * Опубликованный сервис использует внешний балансировщик облачного провадера | ||
+ | |||
+ | {{: | ||
+ | |||
+ | LoadBalancer ServiceType будет работать, | ||
+ | |||
+ | === ExternalIP === | ||
+ | Сервис может быть привязан ко внешнему IP, если он может маршрутизировать на одну или несколько рабочих нод. Трафик, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | Помните, | ||
+ | |||
+ | === ExternalName === | ||
+ | Внешнее имя - особый тип, у которого нет селекторов (Selectors) и не задаются никакие конечные точки(endpoints). Когда к кластеру идёт обращение на ExternalName, | ||
+ | |||
+ | Основное назначение ServiceType - сделать внешние сервисы типа my-database.example.com доступными для приложение внутри кластера. Если внешний сервис принадлежит к тому же пространству имён, для всех приложений и сервисов этого пространства имён он будет доступен просто по имени my-database. | ||
+ | |||
+ | ===== Развёртывание приложения ===== | ||
+ | Используя панель управления (dashboard). | ||
+ | |||
+ | <code bash> | ||
+ | # Запустить dashboard - стандартно оно запускается в пространстве имён default | ||
+ | minikube dashboard | ||
+ | # Если не открывается в браузере, | ||
+ | # Opening http:// | ||
+ | </ | ||
+ | В интерфейсе нажать кнопку + (create new resource), потом Create from form. | ||
+ | * application name **webserver** | ||
+ | * Container image **nginx: | ||
+ | * Number of Pods **3** | ||
+ | * Service **none** (он будет создан позже). | ||
+ | |||
+ | Show advanced options - там можно задать | ||
+ | |||
+ | После нажатия кнопки Deploy запустится развёртывание. Как и ожидалось, | ||
+ | |||
+ | Если не работает простое название образа nginx: | ||
+ | |||
+ | После того, как деплой " | ||
+ | <code bash> | ||
+ | kubectl get deployments | ||
+ | kubectl get replicasets | ||
+ | kubectl get pods | ||
+ | # Подробности про отдельный под | ||
+ | kubectl describe pod webserver-5d58b6b749-ffrhq | ||
+ | </ | ||
+ | В подробностях про под есть информация о ярлыках, | ||
+ | |||
+ | <code bash> | ||
+ | # С ключом -L в вывод добавляются соответствующие колонки, | ||
+ | kubectl get pods -L k8s-app, | ||
+ | # В данном случае в колонке k8s-app будет значение webserver, а label2 пуста. | ||
+ | |||
+ | # Ключ -l - использование селектора. Вывести все поды с ключом k8s-app и значением webserver | ||
+ | kubectl get pods -l k8s-app=webserver | ||
+ | </ | ||
+ | |||
+ | Установить приложение через CLI | ||
+ | <code bash> | ||
+ | # сначала удалим старый экземпляр, | ||
+ | kubectl delete deployments webserver | ||
+ | # после удаления деплоя удалятся и наборы реплик, | ||
+ | kubectl get replicasets | ||
+ | kubectl get pods | ||
+ | </ | ||
+ | Теперь надо сделать файл, например, | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: webserver | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | replicas: 3 | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: nginx | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: nginx | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: nginx | ||
+ | image: nginx: | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | </ | ||
+ | <code bash> | ||
+ | # создать деплой из файла (можно использовать URL) | ||
+ | kubectl create -f webserver.yaml | ||
+ | # теперь снова есть и наборы реплик, | ||
+ | </ | ||
+ | |||
+ | ==== Публикация приложения ==== | ||
+ | Ранее были рассмотрены ServiceTypes, | ||
+ | |||
+ | Конфигурация, | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Service | ||
+ | metadata: | ||
+ | name: web-service | ||
+ | labels: | ||
+ | run: web-service | ||
+ | spec: | ||
+ | type: NodePort | ||
+ | ports: | ||
+ | - port: 80 | ||
+ | protocol: TCP | ||
+ | selector: | ||
+ | app: nginx | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # применить | ||
+ | kubectl create -f webserver-svc.yaml | ||
+ | # или опубликовать уже существующий деплой: | ||
+ | kubectl expose deployment webserver --name=web-service --type=NodePort | ||
+ | |||
+ | # список сервисов | ||
+ | kubectl get services | ||
+ | NAME TYPE CLUSTER-IP | ||
+ | kubernetes | ||
+ | web-service | ||
+ | </ | ||
+ | |||
+ | web-service теперь создан, | ||
+ | |||
+ | Нет нужды сначала создавать Deployment, а затем сервис, | ||
+ | <code bash> | ||
+ | kubectl describe service web-service | ||
+ | </ | ||
+ | В данном случае, | ||
+ | |||
+ | ==== Доступ к приложению ==== | ||
+ | Наше приложение работает на виртуальной машине minikube. Чтобы узнать её IP, нужно выполнить команду | ||
+ | <code bash> | ||
+ | minikube ip | ||
+ | </ | ||
+ | Затем нужно открыть браузер и вбить туда этот адрес и тот порт, который отображался при выводе списка сервисов. Например, | ||
+ | <code bash> | ||
+ | minikube service web-service | ||
+ | </ | ||
+ | В данном случае, | ||
+ | |||
+ | ==== Проверки состояния и готовности ==== | ||
+ | Иногда приложения могут не отвечать на запросы или запускаться с задержкой. Добавление проверок на состояние и готовность (Readiness and Liveness Probes) позволяют сервису kubelet отслеживать состояние приложения, | ||
+ | |||
+ | === Liveness Probe === | ||
+ | Когда приложение в контейнере внутри пода не отвечает на запросы, | ||
+ | |||
+ | Liveness Probes устанавливаются путём настройки: | ||
+ | * Liveness command | ||
+ | * Liveness HTTP request | ||
+ | * TCP Liveness Probe | ||
+ | |||
+ | == Liveness Command == | ||
+ | Здесь проверяется наличие файла / | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | labels: | ||
+ | test: liveness | ||
+ | name: liveness-exec | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: liveness | ||
+ | image: k8s.gcr.io/ | ||
+ | args: | ||
+ | - /bin/sh | ||
+ | - -c | ||
+ | - touch / | ||
+ | livenessProbe: | ||
+ | exec: | ||
+ | command: | ||
+ | - cat | ||
+ | - / | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | </ | ||
+ | Проверка наличия этого файла происходит каждые 5 сек (periodSeconds). Параметр initialDelaySeconds говорит kubelet ждать 5 сек перед первой проверкой. Командная строчка сначала создаёт файл, а через полминуты стирает его, что вызывает непрохождение проверки, | ||
+ | |||
+ | == Liveness HTTP request == | ||
+ | kubelet шлёт запрос HTTP GET на конечную точку /healthz приложения, | ||
+ | <code yaml> | ||
+ | livenessProbe: | ||
+ | httpGet: | ||
+ | path: /healthz | ||
+ | port: 8080 | ||
+ | httpHeaders: | ||
+ | - name: X-Custom-Header | ||
+ | value: Awesome | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | </ | ||
+ | |||
+ | == TCP Liveness probe == | ||
+ | kubelet пытается открыть TCP Socket контейнера с приложением. Не открывается - перезапуск контейнера. | ||
+ | <code yaml> | ||
+ | livenessProbe: | ||
+ | tcpSocket: | ||
+ | port: 8080 | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | </ | ||
+ | |||
+ | === Readiness Probes === | ||
+ | Иногда приложения должны соответствовать определённым требованиям, | ||
+ | <code yaml> | ||
+ | readinessProbe: | ||
+ | exec: | ||
+ | command: | ||
+ | - cat | ||
+ | - / | ||
+ | initialDelaySeconds: | ||
+ | periodSeconds: | ||
+ | </ | ||
+ | |||
+ | Readiness Probes настраиваются так же, как и Liveness Probes, см. [[https:// | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Управление томами (Volumes) ===== | ||
+ | Контейнеры могут быть и производителями, | ||
+ | |||
+ | При перезапуске контейнера, | ||
+ | |||
+ | Том подключается к поду и может использоваться всеми контейнерами внутри него. Том живёт столько же, сколько под, но переживает контейнеры внутри него, что позволяет сохранить данные в случае перезапуска контейнеров. | ||
+ | |||
+ | Некоторые [[https:// | ||
+ | |||
+ | * emptyDir - Пустой том, создающийся во время запуска пода на рабочей ноде. Жёстко зависим от жизни пода - если под перестаёт существовать, | ||
+ | * hostPath - Каталог на хосте. Если под удаляется, | ||
+ | * gcePersistentDisk - монтирует диск Google Compute Engine (GCE) в под. | ||
+ | * awsElasticBlockStore - монтирует AWS EBS Volume в под. | ||
+ | * azureDisk - монтирует Microsoft Azure Data Disk. | ||
+ | * azureFile - монтирует Microsoft Azure File Volume. | ||
+ | * cephfs - монтирует существующий CephFS volume. Если под удаляется, | ||
+ | * nfs - монтирует NFS share. | ||
+ | * iscsi - монтирует iSCSI share. | ||
+ | * secret - передаёт поду секретные данные, | ||
+ | * configMap - предоставляет поду данные конфигурации, | ||
+ | * persistentVolumeClaim - подключает PersistentVolume к поду. | ||
+ | |||
+ | ==== PersistentVolumes ==== | ||
+ | В типичной ситуации, | ||
+ | |||
+ | Persistent Volume - это сетевое хранилище внутри кластера, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | PersistentVolumes могут динамически предоставляться на основе StorageClass resource. StorageClass содержит предустановленных " | ||
+ | |||
+ | Некоторые [[https:// | ||
+ | |||
+ | * GCEPersistentDisk | ||
+ | * AWSElasticBlockStore | ||
+ | * AzureFile | ||
+ | * AzureDisk | ||
+ | * CephFS | ||
+ | * NFS | ||
+ | * iSCSI | ||
+ | |||
+ | ==== PersistentVolumeClaims ==== | ||
+ | [[https:// | ||
+ | |||
+ | Есть 3 режима доступа: | ||
+ | * ReadWriteOnce (чтение-запись одной нодой) | ||
+ | * ReadOnlyMany (только чтение многими нодами) | ||
+ | * ReadWriteMany (чтение-запись многими нодами) | ||
+ | Как только подходящий PersistentVolume найден, | ||
+ | |||
+ | {{: | ||
+ | |||
+ | После завершения работы пользователем, | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ==== Container Storage Interface (CSI) ==== | ||
+ | Разные оркестраторы контейнеров - Kubernetes, Mesos, Docker или Cloud Foundry имеют собственные методы управления внешними хранилищами при использовании Volumes. Для производителей хранилищ, | ||
+ | |||
+ | К версии K8s v1.13 CSI стабилизировался, | ||
+ | |||
+ | ==== Using a Shared hostPath Volume Type ==== | ||
+ | В этом примере показано, | ||
+ | |||
+ | Сначала нужно создать | ||
+ | <code bash> | ||
+ | minikube ssh | ||
+ | mkdir pod-volume | ||
+ | cd pod-volume | ||
+ | pwd | ||
+ | # Путь / | ||
+ | # выйти из VM | ||
+ | exit | ||
+ | </ | ||
+ | |||
+ | Файл конфигурации: | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: share-pod | ||
+ | labels: | ||
+ | app: share-pod | ||
+ | spec: | ||
+ | volumes: | ||
+ | - name: host-volume | ||
+ | hostpath: | ||
+ | path: / | ||
+ | containers: | ||
+ | - image: nginx | ||
+ | name: nginx | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | volumeMounts: | ||
+ | - mountPath: / | ||
+ | name: host-volume | ||
+ | - image: debian | ||
+ | name: debian | ||
+ | volumeMounts: | ||
+ | - mountPath: / | ||
+ | name: host-volume | ||
+ | command: ["/ | ||
+ | </ | ||
+ | Два контейнера (nginx и debian) подключаются к заданному здесь же тому hostPath, и с контейнера debian создаётся содержимое index.html, которое отобразит nginx вместо своей стандартной страницы. | ||
+ | |||
+ | <code bash> | ||
+ | # запустить конфигурацию | ||
+ | kubectl create -f share-pod.yaml | ||
+ | # поглядеть, | ||
+ | kubectl get pods | ||
+ | # опубликовать на NodePort 80 | ||
+ | kubectl expose pod share-pod --type=NodePort --port=80 | ||
+ | # посмотреть инфу | ||
+ | kubectl get services, | ||
+ | # открыть в браузере, | ||
+ | minikube service share-pod | ||
+ | # грохнуть share-pod | ||
+ | kubectl delete pod share-pod | ||
+ | # Под удалился, | ||
+ | kubectl get pods | ||
+ | # но сервис живой, хоть и без IP-адреса, | ||
+ | kubectl get services, | ||
+ | </ | ||
+ | |||
+ | Рисуем новую конфигурацию - один контейнер nginx, который будет читать данные с того же тома: | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: check-pod | ||
+ | labels: | ||
+ | app: share-pod | ||
+ | spec: | ||
+ | volumes: | ||
+ | - name: check-volume | ||
+ | hostPath: | ||
+ | path: / | ||
+ | containers: | ||
+ | - image: nginx | ||
+ | name: nginx | ||
+ | ports: | ||
+ | - containerPort: | ||
+ | volumeMounts: | ||
+ | - mountPath: / | ||
+ | name: check-volume | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Запуск | ||
+ | kubectl create -f check-pod | ||
+ | # поглядеть, | ||
+ | kubectl get pods | ||
+ | # посмотреть инфу | ||
+ | kubectl get services, | ||
+ | # Сервис share-pod всё так же отображается и снова получил IP-адрес, | ||
+ | # Запустить сервис (он уже был ранее опубликован наNodePort) в браузере | ||
+ | minikube service share-pod | ||
+ | </ | ||
+ | Результат - nginx прочитал данные с тома и снова показывает изменённую на первом этапе страницу приветствия. | ||
+ | ===== ConfigMaps и Secrets ===== | ||
+ | Во время развёртывания приложения бывает нужно передать ему параметры конфигурации, | ||
+ | |||
+ | ==== ConfigMaps ==== | ||
+ | [[https:// | ||
+ | |||
+ | === Создание ConfigMap из буквальных значений (Literal Values) и отображение подробностей === | ||
+ | ConfigMap может быть создан командой kubectl create, а подробности можно посмотреть командой kubectl get. | ||
+ | |||
+ | <code bash> | ||
+ | # Создать | ||
+ | kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2 | ||
+ | # Подробности | ||
+ | kubectl get configmaps my-config -o yaml | ||
+ | |||
+ | apiVersion: v1 | ||
+ | data: | ||
+ | key1: value1 | ||
+ | key2: value2 | ||
+ | kind: ConfigMap | ||
+ | metadata: | ||
+ | creationTimestamp: | ||
+ | name: my-config | ||
+ | namespace: default | ||
+ | resourceVersion: | ||
+ | selfLink: / | ||
+ | uid: d35f0a3d-45d1-11e7-9e62-080027a46057 | ||
+ | </ | ||
+ | Опция //-o yaml// - вывод в YAML-формате. Как можно видеть, | ||
+ | |||
+ | === Создание ConfigMap из файла конфигурации === | ||
+ | Сначала нужно создать файл конфигурации, | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: ConfigMap | ||
+ | metadata: | ||
+ | name: customer1 | ||
+ | data: | ||
+ | TEXT1: Customer1_Company | ||
+ | TEXT2: Welcomes You | ||
+ | COMPANY: Customer1 Company Technology Pct. Ltd. | ||
+ | </ | ||
+ | |||
+ | Если имя файла конфигурации customer1-configmap.yaml создать ConfigMap можно следующей командой: | ||
+ | <code bash> | ||
+ | kubectl create -f customer1-configmap.yaml | ||
+ | </ | ||
+ | |||
+ | === Создание ConfigMap из файла === | ||
+ | Создаётся файл permission-reset.properties следующего содержания: | ||
+ | < | ||
+ | permission=read-only | ||
+ | allowed=" | ||
+ | resetCount=3 | ||
+ | </ | ||
+ | Затем создаётся ConfigMap: | ||
+ | <code bash> | ||
+ | kubectl create configmap permission-config --from-file=< | ||
+ | </ | ||
+ | |||
+ | === Использование ConfigMaps внутри подов === | ||
+ | == Как переменные окружения == | ||
+ | Внутри контейнера можно запрашивать данные " | ||
+ | |||
+ | В следующем примере все переменные окружения контейнера myapp-full-container получают значения ключей full-config-map: | ||
+ | <code yaml> | ||
+ | ... | ||
+ | containers: | ||
+ | - name: myapp-full-container | ||
+ | image: myapp | ||
+ | envFrom: | ||
+ | - configMapRef: | ||
+ | name: full-config-map | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | |||
+ | А здесь все переменные окружения контейнера myapp-specific-container получают значения от отдельных пар " | ||
+ | <code yaml> | ||
+ | ... | ||
+ | containers: | ||
+ | - name: myapp-specific-container | ||
+ | image: myapp | ||
+ | env: | ||
+ | - name: SPECIFIC_ENV_VAR1 | ||
+ | valueFrom: | ||
+ | configMapKeyRef: | ||
+ | name: config-map-1 | ||
+ | key: SPECIFIC_DATA | ||
+ | - name: SPECIFIC_ENV_VAR2 | ||
+ | valueFrom: | ||
+ | configMapKeyRef: | ||
+ | name: config-map-2 | ||
+ | key: SPECIFIC_INFO | ||
+ | ... | ||
+ | </ | ||
+ | Таким образом, | ||
+ | |||
+ | == Как тома == | ||
+ | Мы можем смонтировать ConfigMap (например, | ||
+ | <code yaml> | ||
+ | ... | ||
+ | containers: | ||
+ | - name: myapp-vol-container | ||
+ | image: myapp | ||
+ | volumeMounts: | ||
+ | - name: config-volume | ||
+ | mountPath: /etc/config | ||
+ | volumes: | ||
+ | - name: config-volume | ||
+ | configMap: | ||
+ | name: vol-config-map | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | ==== Secrets ==== | ||
+ | Положим, | ||
+ | |||
+ | Объект Secret позволяет закодировать секретную информацию перед тем, как использовать. Кодировать можно пароли, | ||
+ | |||
+ | Важно помнить, | ||
+ | |||
+ | === Создание секрета из непосредственного ввода (from Literal) и показ подробностей === | ||
+ | <code bash> | ||
+ | # создание | ||
+ | kubectl create secret generic my-password --from-literal=password=mysqlpassword | ||
+ | |||
+ | # детали | ||
+ | kubectl get secret my-password | ||
+ | NAME TYPE | ||
+ | my-password | ||
+ | |||
+ | # ещё детали | ||
+ | kubectl describe secret my-password | ||
+ | Name: my-password | ||
+ | Namespace: | ||
+ | Labels: | ||
+ | Annotations: | ||
+ | |||
+ | Type Opaque | ||
+ | |||
+ | Data | ||
+ | ==== | ||
+ | password: | ||
+ | </ | ||
+ | При выводе деталей нигде не показывается содержимое секрета. Тип показан как Opaque (" | ||
+ | |||
+ | === Создание секрета вручную === | ||
+ | Секрет можно создать вручную из конфига YAML. Следующий пример называется mypass.yaml. Там есть 2 типа карт (maps) для приватной информации внутри секрета: | ||
+ | |||
+ | С инфокартами (data maps), каждое значение приватной информации должно быть закодировано base64. Если нужно использовать конфиг-файл для секрета, | ||
+ | <code bash> | ||
+ | echo mysqlpassword | base64 | ||
+ | bXlzcWxwYXNzd29yZAo= | ||
+ | </ | ||
+ | И затем уже использовать полученные значение в конфиге: | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Secret | ||
+ | metadata: | ||
+ | name: my-password | ||
+ | type: Opaque | ||
+ | data: | ||
+ | password: bXlzcWxwYXNzd29yZAo= | ||
+ | </ | ||
+ | Помните, | ||
+ | <code bash> | ||
+ | echo " | ||
+ | mysqlpassword | ||
+ | </ | ||
+ | |||
+ | Поэтому убедитесь, | ||
+ | |||
+ | Со картами данных строк (stringData maps), нет надобности кодировать значение каждого поля приватной информации. Это значение будет закодировано, | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Secret | ||
+ | metadata: | ||
+ | name: my-password | ||
+ | type: Opaque | ||
+ | stringData: | ||
+ | password: mysqlpassword | ||
+ | </ | ||
+ | |||
+ | Создать секрет, | ||
+ | <code bash> | ||
+ | kubectl create -f mypass.yaml | ||
+ | </ | ||
+ | |||
+ | === Создание секрета из файла и отображение подробностей === | ||
+ | Для этого используется команда kubectl create secret. | ||
+ | <code bash> | ||
+ | # Сначала закодировать и записать в файл | ||
+ | echo mysqlpassword | base64 | ||
+ | echo -n ' | ||
+ | # Теперь создать секрет из файла | ||
+ | kubectl create secret generic my-file-password --from-file=password.txt | ||
+ | |||
+ | # После создания можно посмотреть подробности с помощью команд get и describe. | ||
+ | # Серкртная информация не отображается, | ||
+ | kubectl get secret my-file-password | ||
+ | kubectl describe secret my-file-password | ||
+ | </ | ||
+ | |||
+ | === Ипользование секретов внутри подов === | ||
+ | Секреты используются контейнерами как [[https:// | ||
+ | |||
+ | == Использование сектретов как переменных окружения == | ||
+ | Ссылка только на ключ password секрета my-password и привязка значения к переменной WORDPRESS_DB_PASSWORD: | ||
+ | <code yaml> | ||
+ | .... | ||
+ | spec: | ||
+ | containers: | ||
+ | - image: wordpress: | ||
+ | name: wordpress | ||
+ | env: | ||
+ | - name: WORDPRESS_DB_PASSWORD | ||
+ | valueFrom: | ||
+ | secretKeyRef: | ||
+ | name: my-password | ||
+ | key: password | ||
+ | .... | ||
+ | </ | ||
+ | |||
+ | == Использование секретов как файлов == | ||
+ | Можно смонтировать секрет как том. В примере создаётся файл для каждого ключа секрета my-password, | ||
+ | |||
+ | <code yaml> | ||
+ | .... | ||
+ | spec: | ||
+ | containers: | ||
+ | - image: wordpress: | ||
+ | name: wordpress | ||
+ | volumeMounts: | ||
+ | - name: secret-volume | ||
+ | mountPath: "/ | ||
+ | readOnly: true | ||
+ | volumes: | ||
+ | - name: secret-volume | ||
+ | secret: | ||
+ | secretName: my-password | ||
+ | .... | ||
+ | </ | ||
+ | |||
+ | ==== Пример использования ConfigMaps ==== | ||
+ | web-config.yaml - пространство имён default, две строки с данными - STRING и PATH. | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: ConfigMap | ||
+ | metadata: | ||
+ | name: web-config | ||
+ | namespace: default | ||
+ | data: | ||
+ | STRING: Welcome to example of ConfigMaps! | ||
+ | PATH: / | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Применить конфиг | ||
+ | kubectl create -f web-config.yaml | ||
+ | # Проверить, | ||
+ | kubectl get configmaps | ||
+ | # или kubectl get cm | ||
+ | # можно так: | ||
+ | kubectl describe cm web-config | ||
+ | </ | ||
+ | |||
+ | app-config.yaml | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Pod | ||
+ | metadata: | ||
+ | name: app-config | ||
+ | spec: | ||
+ | containers: | ||
+ | - image: nginx | ||
+ | name: nginx | ||
+ | command: ["/ | ||
+ | env: | ||
+ | - name: DATA_STRING | ||
+ | valueFrom: | ||
+ | configMapKeyRef: | ||
+ | name: web-config | ||
+ | key: STRING | ||
+ | optional: true | ||
+ | - name: DATA_PATH | ||
+ | valueFrom: | ||
+ | configMapKeyRef: | ||
+ | name: web-config | ||
+ | key: PATH | ||
+ | optional: true | ||
+ | restartPolicy: | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Создать под на основе конфига | ||
+ | kubectl create -f app-config.yaml | ||
+ | # т.к. сервис не создавался и не публиковался, | ||
+ | kubectl exec app-config -- /bin/sh -c 'cat / | ||
+ | # Welcome to example of ConfigMaps! | ||
+ | </ | ||
+ | ===== Ingress ===== | ||
+ | {{ : | ||
+ | |||
+ | Сервисы определяют правила маршрутизации для конкретных сервисов по отдельности и существуют, | ||
+ | |||
+ | Ingress - балансировщик HTTP/HTTPS 7-го уровня со следующими возможностями: | ||
+ | * TLS (Transport Layer Security) | ||
+ | * Name-based virtual hosting | ||
+ | * Fanout routing | ||
+ | * Loadbalancing | ||
+ | * Custom rules | ||
+ | |||
+ | При использовании Ingress, пользователь не подключается непосредственно к сервису, | ||
+ | <code yaml> | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | kind: Ingress | ||
+ | metadata: | ||
+ | name: virtual-host-ingress | ||
+ | namespace: default | ||
+ | spec: | ||
+ | rules: | ||
+ | - host: blue.example.com | ||
+ | http: | ||
+ | paths: | ||
+ | - backend: | ||
+ | serviceName: | ||
+ | servicePort: | ||
+ | - host: green.example.com | ||
+ | http: | ||
+ | paths: | ||
+ | - backend: | ||
+ | serviceName: | ||
+ | servicePort: | ||
+ | </ | ||
+ | В этом примере, | ||
+ | |||
+ | //Fanout Ingress rules// - когда запросы к example.com/ | ||
+ | <code yaml> | ||
+ | apiVersion: networking.k8s.io/ | ||
+ | kind: Ingress | ||
+ | metadata: | ||
+ | name: fan-out-ingress | ||
+ | namespace: default | ||
+ | spec: | ||
+ | rules: | ||
+ | - host: example.com | ||
+ | http: | ||
+ | paths: | ||
+ | - path: /blue | ||
+ | backend: | ||
+ | serviceName: | ||
+ | servicePort: | ||
+ | - path: /green | ||
+ | backend: | ||
+ | serviceName: | ||
+ | servicePort: | ||
+ | </ | ||
+ | |||
+ | ==== Ingress Controller ==== | ||
+ | Ingress Controller - приложение, | ||
+ | <code bash> | ||
+ | # Запустить Ingress Controller в Minikube | ||
+ | minikube addons enable ingress | ||
+ | </ | ||
+ | |||
+ | ==== Развёртывание Ingress resource ==== | ||
+ | После Ingress Controller нужно создать Ingress resource. Например, | ||
+ | <code bash> | ||
+ | kubectl create -f virtual-host-ingress.yaml | ||
+ | </ | ||
+ | |||
+ | Как только Ingress resource создан, | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | ===== Углублённые темы ===== | ||
+ | |||
+ | ==== Annotations ==== | ||
+ | Аннотации можно прицепить к любому объекту, | ||
+ | <code yaml> | ||
+ | apiVersion: extensions/ | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: webserver | ||
+ | annotations: | ||
+ | description: | ||
+ | developer: Vasya | ||
+ | .... | ||
+ | </ | ||
+ | |||
+ | В отличие от ярлыков, | ||
+ | * Store build/ | ||
+ | * Phone/pager numbers of people responsible, | ||
+ | * Pointers to logging, monitoring, analytics, audit repositories, | ||
+ | и т. п. | ||
+ | |||
+ | Будут видны в деталях деплоя | ||
+ | <code bash> | ||
+ | kubectl describe deployment webserver | ||
+ | </ | ||
+ | |||
+ | ==== Jobs и CronJobs ==== | ||
+ | Job (задача) создаёт поды для конкретной задачи и несёт ответственность за сбои пода, обеспечивает выполнение задачи. Когда задача выполнена, | ||
+ | * parallelism - кол-во подов, запускаемых параллельно | ||
+ | * completions - число ожидаемых выполнений | ||
+ | * activeDeadlineSeconds - время, отведённое на задачу | ||
+ | * backoffLimit - число попыток выполнения, | ||
+ | * ttlSecondsAfterFinished - отсрочка чистки после завершения задач | ||
+ | |||
+ | Начиная с версии K8s 1.4, появилась возможность | ||
+ | * startingDeadlineSeconds - интервал для запуска пропущенных задач | ||
+ | * concurrencyPolicy - разрешение или запрет на одновременное выполнение задач или замещения старых задач новыми | ||
+ | |||
+ | ==== Quota Management ==== | ||
+ | Когда кластером пользуется множество людей, возникает вопрос распределения ресурсов. ResourceQuota API resource ограничивает потребляемые ресурсы в рамках пространства имён. | ||
+ | |||
+ | Типы квот: | ||
+ | * Compute Resource Quota - ограничение общего кол-ва ресурсов | ||
+ | * Storage Resource Quota - ограничение ресурсов хранения (PersistentVolumeClaims, | ||
+ | * Object Count Quota - ограничение кол-ва объектов определённого типа (pods, ConfigMaps, PersistentVolumeClaims, | ||
+ | |||
+ | ==== Autoscaling ==== | ||
+ | Несмотря на то, что масштабировать объекты вручную достаточно просто, | ||
+ | |||
+ | Автомасштабирование может быть задействовано в кластере через контроллеры, | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | ==== DaemonSets ==== | ||
+ | Если нужно собирать данные мониторинга со всех нод или запускать на них сервис хранилища (storage daemon), на всех нодах должен постоянно работать особый под, и это делается объектом [[https:// | ||
+ | |||
+ | Когда нода добавляется в кластер, | ||
+ | |||
+ | Новая функция ресурса DaemonSet - запуск подов только на указанных нодах путём настройки nodeSelectors и node affinity rules. Подобно Deployment resources, DaemonSets поддерживают rolling updates и rollbacks. | ||
+ | |||
+ | Конфиг DaemonSet идентичен ReplicaSet, за исключением kind. | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: DaemonSet | ||
+ | metadata: | ||
+ | name: monitoring | ||
+ | spec: | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: monitoring-agent | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: monitoring-agent | ||
+ | spec: | ||
+ | containers: | ||
+ | - name: monitoring-agent | ||
+ | image: monitoring-agent | ||
+ | </ | ||
+ | |||
+ | ==== StatefulSets ==== | ||
+ | Контроллер [[https:// | ||
+ | |||
+ | StatefulSet controller предоставляет уникальные данные (identity) и гарантирует очерёдность деплоя и масштабирования подов. Подобно Deployments, | ||
+ | |||
+ | ==== Kubernetes Federation ==== | ||
+ | Даёт возможность управления несколькими кластерами из одной панели управления. Общая синхронизация ресурсов и обнаружение, | ||
+ | |||
+ | Хотя это пока альфа-версия, | ||
+ | |||
+ | ==== Custom Resources ==== | ||
+ | В Kubernetes, ресурс - это конечная точка API, хранящая набор объектов API. Например, | ||
+ | |||
+ | Хотя в большинстве случаев существующие ресурсы Kubernetes достаточны для выполнения наших требований, | ||
+ | |||
+ | Для создания своего ресурса необходимо создать и установить custom controller, который может интерпретировать структуру ресурса и выполнять необходимые действия. Custom controllers могут быть установлены и управляться в уже работающем кластере. Есть 2 способа добавления настраиваемого ресурса: | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | ==== Helm ==== | ||
+ | Чтобы развернуть приложение, | ||
+ | |||
+ | Клиент подключается к серверу для управления [[https:// | ||
+ | |||
+ | Установить Helm: https:// | ||
+ | |||
+ | * Чарт — формат пакета в Helm, это каталог определённой стуктуры с текстовыми файлами (набор шаблонов, | ||
+ | * Пакетом в Helm называется чарт упакованный в .tgz архив, у которого есть версия. | ||
+ | * Релиз - экземпляр чарта, который работает в кластере Kubernetes | ||
+ | * Приложение для Kubernetes — это набор манифестов. | ||
+ | * Шаблон (шаблонизированный манифест) — это манифест, | ||
+ | |||
+ | Структура чарта | ||
+ | <code bash> | ||
+ | < | ||
+ | .helmignore # | ||
+ | Chart.yaml # | ||
+ | values.yaml # | ||
+ | charts/ # каталог для чартов, | ||
+ | crds/ # собственные определения ресурсов | ||
+ | templates/ | ||
+ | _helpers.tpl # | ||
+ | NOTES.txt # Текст, выводимый после применения чарта (инстукция по подключению и т. п.) | ||
+ | </ | ||
+ | |||
+ | Для шаблонизации в Helm используются go templates, в котором, | ||
+ | В '' | ||
+ | Часто можно встретить такие конструкции: | ||
+ | |||
+ | '' | ||
+ | '' | ||
+ | '' | ||
+ | | ||
+ | https:// | ||
+ | |||
+ | Пример Chart.yaml | ||
+ | <code yaml> | ||
+ | apiVersion: v2 # версия api чарта | ||
+ | name: nginx # имя чарта | ||
+ | type: application | ||
+ | version: 0.1.0 # версия чарта, должна быть по SemVer2 | ||
+ | appVersion: " | ||
+ | description: | ||
+ | |||
+ | dependencies: | ||
+ | - name: backend | ||
+ | version: 0.1.0 | ||
+ | - name: frontend | ||
+ | version: 0.1.0 | ||
+ | </ | ||
+ | |||
+ | Пример values.yaml | ||
+ | <code yaml> | ||
+ | image: | ||
+ | repository: nginx | ||
+ | tag: 1.21.6 | ||
+ | imagePullPolicy: | ||
+ | replicas: 2 | ||
+ | service: | ||
+ | port: 80 | ||
+ | resources: | ||
+ | requests: | ||
+ | memory: " | ||
+ | cpu: " | ||
+ | limits: | ||
+ | memory: " | ||
+ | cpu: " | ||
+ | </ | ||
+ | Пример templates/ | ||
+ | <code yaml> | ||
+ | --- | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: {{ .Release.Name }} | ||
+ | labels: | ||
+ | app: {{ .Chart.Name }} | ||
+ | spec: | ||
+ | replicas: {{ .Values.replicas }} | ||
+ | selector: | ||
+ | matchLabels: | ||
+ | app: {{ .Chart.Name }} | ||
+ | template: | ||
+ | metadata: | ||
+ | labels: | ||
+ | app: {{ .Chart.Name }} | ||
+ | spec: | ||
+ | containers: | ||
+ | — name: nginx | ||
+ | image: "{{ .Values.image.repository }}: | ||
+ | imagePullPolicy: | ||
+ | ports: | ||
+ | — containerPort: | ||
+ | resources: | ||
+ | {{ toYaml .Values.resources | indent 10 }} | ||
+ | </ | ||
+ | |||
+ | <code bash> | ||
+ | # Проверить чарт | ||
+ | helm lint ./< | ||
+ | |||
+ | # Создать пакет (Chart.Name-Chart.Version.tgz) | ||
+ | helm package ./< | ||
+ | |||
+ | # Запустить в Кубере | ||
+ | helm install < | ||
+ | |||
+ | # Переопределить версию образа при запуске | ||
+ | helm install --set image.tag=1.21.5 < | ||
+ | |||
+ | # посмотреть информацию о шаблонах и значениях переменных, | ||
+ | helm get all < | ||
+ | |||
+ | # Список версий чартов. | ||
+ | helm search repo bitnami/ | ||
+ | # Chart version - версия чарта, это не версия приложения, | ||
+ | # App version - версия приложения. | ||
+ | |||
+ | # Установить определённую версию чарта | ||
+ | helm install myrabbitmq bitnami/ | ||
+ | # Показать readme чарта | ||
+ | helm show readme bitnami/ | ||
+ | # readme уже установленного чарта (релиза) | ||
+ | helm status myrabbitmq | ||
+ | |||
+ | # Обновление. Реплики также задаются через переменную Helm, а не через кластер напрямую. | ||
+ | helm гupgrade myrabbitmq bitnami/ | ||
+ | # Обновление на новую версию | ||
+ | helm гupgrade myrabbitmq bitnami/ | ||
+ | |||
+ | # История релизов | ||
+ | helm history myrabbitmq | ||
+ | # Откатиться на пред. версию | ||
+ | helm rollback myrabbitmq | ||
+ | # Деинсталляция | ||
+ | helm uninstall myrabbitmq | ||
+ | |||
+ | # Показать манифест с подставленными переменными | ||
+ | helm template ./< | ||
+ | # Конкретный шаблон | ||
+ | helm template ./< | ||
+ | </ | ||
+ | |||
+ | У имени деплоя должно быть уникальное имя в рамках пространства имён, поэтому в metadata.name нужно указывать изменяющееся имя, например | ||
+ | <code yaml> | ||
+ | apiVersion: apps/v1 | ||
+ | kind: Deployment | ||
+ | metadata: | ||
+ | name: {{ .Release.Name }}-{{ .Chart.Name }} | ||
+ | </ | ||
+ | Можно заменить эту конструкцию на функцию | ||
+ | <code yaml> | ||
+ | metadata: | ||
+ | name: {{ printf " | ||
+ | # Так же можно поступить и с образом (только разделитель будет ":" | ||
+ | image: {{ printf " | ||
+ | </ | ||
+ | |||
+ | Значение по умолчанию | ||
+ | <code yaml> | ||
+ | env: | ||
+ | - name: USERNAME | ||
+ | value: {{ default " | ||
+ | </ | ||
+ | Quote нужен для того, чтобы нормально обрабатывались значения с пробелами | ||
+ | В это время в values.yaml: | ||
+ | |||
+ | Условие if/else: если пароль предоставлен '' | ||
+ | Дефис перед открывающими фигурными скобками удаляет всю пустоту в начале строки (типа trimStart). Можно делать это и в конце '' | ||
+ | <code yaml> | ||
+ | env: | ||
+ | - name: PASSWORD | ||
+ | {{- if .Values.password }} | ||
+ | value: {{ .Values.password }} | ||
+ | {{- else }} | ||
+ | value: testPassword | ||
+ | {{- end }} | ||
+ | </ | ||
+ | В values.yaml: | ||
+ | |||
+ | Использовать дочерние значения | ||
+ | <code yaml> | ||
+ | ports: | ||
+ | {{- range .Values.containerPorts }} | ||
+ | - name: {{ .name }} | ||
+ | containerPort: | ||
+ | {{- end }} | ||
+ | </ | ||
+ | В values.yaml: | ||
+ | <code yaml> | ||
+ | containerPorts: | ||
+ | - name: http | ||
+ | port: 8080 | ||
+ | </ | ||
+ | |||
+ | Макрос шаблона позволяет избавиться от повторяющихся инструкций в разных шаблонах. Макросы содержатся в _helpers.tpl. | ||
+ | Вместо повторяющихся в каждом шаблоне | ||
+ | <code yaml> | ||
+ | metadata: | ||
+ | name: {{ printf " | ||
+ | </ | ||
+ | пишем | ||
+ | <code yaml> | ||
+ | metadata: | ||
+ | name: {{ template " | ||
+ | </ | ||
+ | |||
+ | Вариант с кодированием base64: если значения даны, то кодировать их, если нет - сгенерировать и кодировать. | ||
+ | <code yaml> | ||
+ | apiVersion: v1 | ||
+ | kind: Secret | ||
+ | metadata: | ||
+ | name: {{ include " | ||
+ | labels: | ||
+ | {{- include "" | ||
+ | data: | ||
+ | {{- if empty .Values.accessKey }} | ||
+ | | ||
+ | {{- else }} | ||
+ | | ||
+ | {{- end }} | ||
+ | {{- if empty .Values.secretKey }} | ||
+ | | ||
+ | {{- else }} | ||
+ | | ||
+ | {{- end }} | ||
+ | </ | ||
+ | |||
+ | ==== Security Contexts and Pod Security Policies ==== | ||
+ | Время от времени нужно задавать специфические разрешения и настраивать доступ к подам и контейнерам. Security Contexts позволяют задать Discretionary Access Control for object access permissions, | ||
+ | |||
+ | Если нужно задать настройки безопасности на многих подах или контейнерах в рамках всего кластера, | ||
+ | |||
+ | ==== Network Policies ==== | ||
+ | Изначально в Kubernetes все поды свободно общаются без ограничений, | ||
+ | |||
+ | Network Policy API resource specifies podSelectors, | ||
+ | |||
+ | Let's keep in mind that not all the networking solutions available for Kubernetes support Network Policies. Review the Pod-to-Pod Communication section from the Kubernetes Architecture chapter if needed. By default, Network Policies are namespaced API resources, but certain network plugins provide additional features so that Network Policies can be applied cluster-wide. | ||
+ | |||
+ | ==== Monitoring and Logging ==== | ||
+ | Сбор данных по использованию подов, нод, сервисов и т. д. нужен, чтобы понимать потребление ресурсов и на этой основе принимать решения по масштабированию приложения. Вот 2 популярных решения по мониторингу: | ||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | Другой важный компонент - логи. Можно собирать логи разных кластерных компонентов, | ||
+ | |||
+ | |||
+ | |||
+ | ===== What's Next on Your Kubernetes Journey? ===== | ||
+ | Now that you have a better understanding of Kubernetes, you can continue your journey by: | ||
+ | * Participating in activities and discussions organized by the Kubernetes community | ||
+ | * Attending events organized by the Cloud Native Computing Foundation and The Linux Foundation | ||
+ | * Expanding your Kubernetes knowledge and skills by enrolling in the self-paced LFS258 - Kubernetes Fundamentals, | ||
+ | * Preparing for the Certified Kubernetes Administrator or the Certified Kubernetes Application Developer exams, offered by the Cloud Native Computing Foundation | ||
+ | And many other options. | ||
+ | |||
+ | ===== Ошибки ===== | ||
+ | ==== certificate has expired or is not yet valid ==== | ||
+ | Кластер долго не обновлялся, | ||
+ | <color # | ||
+ | <code bash> | ||
+ | kubeadm certs renew all | ||
+ | reboot | ||
+ | sudo cp ~/ | ||
+ | sudo cp / | ||
+ | </ | ||
+ | https:// | ||