This commit is contained in:
2026-04-08 20:22:14 +07:00
commit 34fbdd1412
96 changed files with 5321 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
# Урок 4.1 — Автоматическое создание ConfigMaps и Secrets
## Файлы
| Файл | Триггер | Что создаёт |
|------|---------|-------------|
| `generate-namespace-config.yaml` | Namespace | ConfigMap с мета-данными namespace |
| `generate-default-networkpolicy.yaml` | Namespace | NetworkPolicy deny-all |
| `generate-resource-quota.yaml` | Namespace | ResourceQuota по tier |
| `quota-defaults-configmap.yaml` | — | ConfigMap с квотами (нужен заранее) |
| `clone-registry-secret.yaml` | Namespace | Копия registry Secret |
| `generate-developer-rolebinding.yaml` | Namespace+label | RoleBinding для команды |
| `developer-clusterrole.yaml` | — | ClusterRole developer (нужна заранее) |
## Подготовка (применить до политик)
```bash
# 1. Создать ConfigMap с квотами
kubectl apply -f quota-defaults-configmap.yaml
# 2. Создать ClusterRole developer
kubectl apply -f developer-clusterrole.yaml
# 3. Создать мастер-секрет для registry (замените данные на реальные)
kubectl create secret docker-registry registry-credentials-master \
--docker-server=registry.company.com \
--docker-username=robot-account \
--docker-password=your-token \
--namespace=kyverno
# 4. Применить все политики
kubectl apply -f generate-namespace-config.yaml
kubectl apply -f generate-default-networkpolicy.yaml
kubectl apply -f generate-resource-quota.yaml
kubectl apply -f clone-registry-secret.yaml
kubectl apply -f generate-developer-rolebinding.yaml
```
## Тест: создать namespace и проверить генерацию
```bash
# Создать тестовый namespace
kubectl create namespace test-team \
--dry-run=client -o yaml | \
kubectl apply -f -
kubectl label namespace test-team \
team=platform \
tier=standard \
environment=staging
# Подождать ~5 секунд для генерации, затем проверить
kubectl get configmap,networkpolicy,resourcequota,rolebinding \
-n test-team
# Детали сгенерированных ресурсов
kubectl get configmap namespace-config -n test-team -o yaml
kubectl get networkpolicy default-deny-all -n test-team -o yaml
kubectl get resourcequota default-quota -n test-team -o yaml
```
## Тест synchronize — защита от случайного удаления
```bash
# Удалить NetworkPolicy вручную
kubectl delete networkpolicy default-deny-all -n test-team
# Kyverno восстановит её через несколько секунд
sleep 10
kubectl get networkpolicy -n test-team
# default-deny-all снова присутствует
```
## Тест cascade delete
```bash
# Удалить namespace — все сгенерированные ресурсы удалятся вместе с ним
kubectl delete namespace test-team
# Проверить что ресурсы не зависли
kubectl get generaterequests -n kyverno
```
## Мониторинг GenerateRequests
```bash
# Просмотр очереди генерации
kubectl get generaterequests -n kyverno -o wide
# Детали при ошибке
kubectl describe generaterequests -n kyverno | grep -A 20 "Status:"
# Метрики генерации
kubectl port-forward -n kyverno svc/kyverno-svc-metrics 8000:8000 &
curl -s http://localhost:8000/metrics | grep kyverno_generate
```

View File

@@ -0,0 +1,39 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: clone-registry-secret
annotations:
policies.kyverno.io/title: "Копирование Registry Secret во все Namespace"
policies.kyverno.io/category: Security
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Namespace
policies.kyverno.io/description: >-
При создании Namespace копирует Secret с кредентиалами реестра
из namespace kyverno (мастер-копия) в новый namespace.
synchronize: true — при обновлении мастер-секрета все копии обновятся.
Создайте мастер-секрет: kubectl create secret docker-registry
registry-credentials-master -n kyverno ...
spec:
rules:
- name: clone-registry-credentials
match:
resources:
kinds:
- Namespace
exclude:
resources:
names:
- kube-system
- kube-public
- kube-node-lease
- kyverno
generate:
apiVersion: v1
kind: Secret
name: registry-credentials
namespace: "{{ request.object.metadata.name }}"
synchronize: true
# clone копирует существующий ресурс вместо создания из шаблона
clone:
namespace: kyverno
name: registry-credentials-master

View File

@@ -0,0 +1,36 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: developer
labels:
app: kyverno-config
rules:
- apiGroups: [""]
resources:
- pods
- pods/log
- pods/exec
- services
- configmaps
- persistentvolumeclaims
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources:
- deployments
- statefulsets
- daemonsets
- replicasets
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["batch"]
resources:
- jobs
- cronjobs
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["networking.k8s.io"]
resources:
- ingresses
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources:
- secrets
verbs: ["get", "list", "watch"] # только чтение секретов

View File

@@ -0,0 +1,51 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-default-networkpolicy
annotations:
policies.kyverno.io/title: "Генерация NetworkPolicy по умолчанию"
policies.kyverno.io/category: Security
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Namespace
policies.kyverno.io/description: >-
При создании Namespace автоматически создаёт NetworkPolicy
"запрети весь трафик по умолчанию". Zero Trust сетевая модель:
команды явно разрешают нужный трафик поверх этой базовой политики.
spec:
rules:
- name: generate-deny-all-networkpolicy
match:
resources:
kinds:
- Namespace
exclude:
resources:
names:
- kube-system
- kube-public
- kube-node-lease
- kyverno
- monitoring
- logging
generate:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny-all
namespace: "{{ request.object.metadata.name }}"
synchronize: true
data:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: default-deny-all
labels:
generated-by: kyverno
annotations:
description: >-
Запрещает весь входящий и исходящий трафик по умолчанию.
Добавьте явные NetworkPolicy для разрешения нужного трафика.
spec:
podSelector: {} # применяется ко всем подам
policyTypes:
- Ingress
- Egress

View File

@@ -0,0 +1,52 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-developer-rolebinding
annotations:
policies.kyverno.io/title: "Генерация RoleBinding для команды"
policies.kyverno.io/category: RBAC
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Namespace
policies.kyverno.io/description: >-
При создании Namespace с лейблом team: <name> автоматически создаёт
RoleBinding, дающий группе <name>-developers права ClusterRole developer.
Namespace с лейблом team=payments → группа payments-developers получает доступ.
spec:
rules:
- name: generate-team-rolebinding
match:
resources:
kinds:
- Namespace
selector:
matchExpressions:
- key: team
operator: Exists
exclude:
resources:
names:
- kube-system
- kube-public
- kube-node-lease
- kyverno
generate:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
name: developer-access
namespace: "{{ request.object.metadata.name }}"
synchronize: true
data:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: developer-access
labels:
generated-by: kyverno
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: developer # ClusterRole должна существовать
subjects:
- kind: Group
name: "{{ request.object.metadata.labels.team }}-developers"
apiGroup: rbac.authorization.k8s.io

View File

@@ -0,0 +1,45 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-namespace-config
annotations:
policies.kyverno.io/title: "Генерация ConfigMap при создании Namespace"
policies.kyverno.io/category: Governance
policies.kyverno.io/severity: low
policies.kyverno.io/subject: Namespace
policies.kyverno.io/description: >-
При создании нового Namespace автоматически создаёт ConfigMap
с базовой конфигурацией. synchronize: true — ConfigMap обновляется
при изменении namespace и удаляется вместе с ним.
spec:
rules:
- name: generate-config
match:
resources:
kinds:
- Namespace
exclude:
resources:
names:
- kube-system
- kube-public
- kube-node-lease
- kyverno
generate:
apiVersion: v1
kind: ConfigMap
name: namespace-config
namespace: "{{ request.object.metadata.name }}"
synchronize: true
data:
kind: ConfigMap
metadata:
labels:
generated-by: kyverno
policy: generate-namespace-config
data:
namespace: "{{ request.object.metadata.name }}"
environment: "{{ request.object.metadata.labels.environment || 'unknown' }}"
team: "{{ request.object.metadata.labels.team || 'unknown' }}"
created-at: "{{ time_now_utc() }}"
documentation: "https://wiki.company.com/kubernetes/namespaces"

View File

@@ -0,0 +1,59 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-resource-quota
annotations:
policies.kyverno.io/title: "Генерация ResourceQuota для Namespace"
policies.kyverno.io/category: Resources
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Namespace
policies.kyverno.io/description: >-
При создании Namespace генерирует ResourceQuota.
Квота зависит от лейбла tier: standard | premium.
Значения квот берутся из ConfigMap quota-defaults.
spec:
rules:
- name: generate-quota
match:
resources:
kinds:
- Namespace
exclude:
resources:
names:
- kube-system
- kube-public
- kube-node-lease
- kyverno
context:
- name: quotaConfig
configMap:
name: quota-defaults
namespace: kyverno
generate:
apiVersion: v1
kind: ResourceQuota
name: default-quota
namespace: "{{ request.object.metadata.name }}"
synchronize: true
data:
kind: ResourceQuota
apiVersion: v1
metadata:
name: default-quota
labels:
generated-by: kyverno
spec:
hard:
# Квота CPU зависит от tier namespace
requests.cpu: >-
{{ quotaConfig.data.\"{{ request.object.metadata.labels.tier || 'standard' }}_cpu_request\" || '4' }}
requests.memory: >-
{{ quotaConfig.data.\"{{ request.object.metadata.labels.tier || 'standard' }}_memory_request\" || '8Gi' }}
limits.cpu: >-
{{ quotaConfig.data.\"{{ request.object.metadata.labels.tier || 'standard' }}_cpu_limit\" || '8' }}
limits.memory: >-
{{ quotaConfig.data.\"{{ request.object.metadata.labels.tier || 'standard' }}_memory_limit\" || '16Gi' }}
pods: "50"
services: "20"
persistentvolumeclaims: "10"

View File

@@ -0,0 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: quota-defaults
namespace: kyverno
labels:
app: kyverno-config
data:
# Standard tier (по умолчанию)
standard_cpu_request: "4"
standard_memory_request: "8Gi"
standard_cpu_limit: "8"
standard_memory_limit: "16Gi"
# Premium tier (для нагруженных сервисов)
premium_cpu_request: "16"
premium_memory_request: "32Gi"
premium_cpu_limit: "32"
premium_memory_limit: "64Gi"
# Dev tier (для разработки)
dev_cpu_request: "2"
dev_memory_request: "4Gi"
dev_cpu_limit: "4"
dev_memory_limit: "8Gi"

View File

@@ -0,0 +1,106 @@
# Урок 4.2 — Управление жизненным циклом генерируемых ресурсов
## Файлы
| Файл | Описание |
|------|----------|
| `cleanup-debug-pods.yaml` | CleanupPolicy — автоудаление debug подов старше 4ч |
## Параметр synchronize — детально
```yaml
generate:
synchronize: true # живая ссылка: обновляется, восстанавливается, удаляется вместе
synchronize: false # создать один раз, дальше независимо
```
| Событие | synchronize: true | synchronize: false |
|---------|-------------------|-------------------|
| Источник изменился | Генерируемый обновляется | Без изменений |
| Генерируемый удалён | Восстанавливается | Не восстанавливается |
| Источник удалён | Генерируемый удаляется | Остаётся |
## Применение CleanupPolicy
```bash
# CleanupPolicy требует Kyverno >= 1.9
kubectl apply -f cleanup-debug-pods.yaml
# Проверить статус
kubectl get clustercleanuppolicies
```
## Тест CleanupPolicy
```bash
# Создать debug-под
kubectl run debug-test \
--image=busybox:1.36 \
--labels="purpose=debug" \
--restart=Never \
-- sleep 3600
# Принудительно запустить cleanup (изменив schedule на ближайшую минуту)
# Или подождать час и проверить:
kubectl get pod debug-test
# Проверить логи cleanup controller
kubectl logs -n kyverno \
-l app.kubernetes.io/component=cleanup-controller \
--tail=30
```
## Диагностика GenerateRequests
```bash
# Все GenerateRequests
kubectl get generaterequests -n kyverno
# Зависшие в Processing статусе
kubectl get generaterequests -n kyverno \
-o jsonpath='{range .items[?(@.status.state=="Failed")]}{.metadata.name}{"\n"}{end}'
# Детали конкретного
kubectl describe generaterequest <name> -n kyverno
# Типичные причины ошибок:
# 1. Нет прав у Kyverno ServiceAccount
kubectl get clusterrolebinding | grep kyverno
# 2. Ресурс уже существует (не управляется Kyverno)
# 3. Синтаксическая ошибка в шаблоне generate
```
## Взять под управление существующий ресурс
```bash
# Если ресурс уже существует и нужно передать его Kyverno:
kubectl label configmap namespace-config \
-n my-namespace \
kyverno.io/managed=true
# Или удалить ресурс, Kyverno пересоздаст его
kubectl delete configmap namespace-config -n my-namespace
# Kyverno создаст новый через background controller
```
## Bootstrap Namespace — полный сценарий
```bash
# Одна команда создаёт полностью настроенный namespace:
kubectl create namespace payments-service
kubectl label namespace payments-service \
team=payments \
tier=premium \
environment=production
# Через ~10 секунд:
kubectl get all,configmap,secret,networkpolicy,resourcequota,rolebinding \
-n payments-service
# Ожидаемый результат:
# configmap/namespace-config
# networkpolicy/default-deny-all
# resourcequota/default-quota (с premium квотами)
# secret/registry-credentials
# rolebinding/developer-access (для группы payments-developers)
```

View File

@@ -0,0 +1,29 @@
apiVersion: kyverno.io/v2alpha1
kind: ClusterCleanupPolicy
metadata:
name: cleanup-debug-pods
annotations:
policies.kyverno.io/title: "Автоудаление debug подов"
policies.kyverno.io/category: Governance
policies.kyverno.io/severity: low
policies.kyverno.io/description: >-
Удаляет поды с лейблом purpose=debug старше 4 часов.
Запускается каждый час по расписанию.
Предотвращает накопление забытых отладочных подов в кластере.
spec:
schedule: "0 * * * *" # каждый час в :00
match:
resources:
kinds:
- Pod
namespaces:
- default
- staging
selector:
matchLabels:
purpose: debug
conditions:
any:
- key: "{{ time_since('', request.object.metadata.creationTimestamp, '') }}"
operator: GreaterThan
value: "4h"