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,81 @@
# Урок 2.1 — Создание политик валидации ресурсов
## Файлы
| Файл | Описание |
|------|----------|
| `require-resource-limits.yaml` | Обязательные CPU и memory limits |
| `disallow-latest-tag.yaml` | Запрет тега :latest |
| `allow-only-trusted-registries.yaml` | Только внутренние реестры |
| `require-labels.yaml` | Обязательные стандартные лейблы |
| `require-min-replicas-production.yaml` | Минимум 2 реплики в production |
## Применение политик
```bash
# Применить все политики из папки
kubectl apply -f .
# Проверить статус
kubectl get clusterpolicies
# Подробный статус
kubectl get clusterpolicies -o wide
```
## Локальное тестирование через Kyverno CLI
```bash
# Протестировать одну политику
kyverno apply require-resource-limits.yaml \
--resource test-resources/pod-no-limits.yaml
# Протестировать все политики против всех ресурсов
kyverno apply . --resource test-resources/ --table
# Запустить встроенные тесты
kyverno test tests/
```
## Тестирование в кластере
```bash
# Попытка создать под без limits (должна быть ошибка)
kubectl apply -f test-resources/pod-no-limits.yaml
# Создать корректный под
kubectl apply -f test-resources/pod-with-limits.yaml
# Попытка создать под с образом :latest
kubectl apply -f test-resources/pod-latest-image.yaml
# Посмотреть PolicyReport после создания
kubectl get policyreports -n default
kubectl describe policyreport -n default
# Найти все нарушения
kubectl get policyreports -A -o json | \
jq -r '.items[] | .metadata.namespace as $ns |
.results[] | select(.result == "fail") |
"\($ns)/\(.resources[0].name): \(.policy)/\(.rule): \(.message)"'
```
## Режим Audit — миграция без риска
```bash
# Переключить политику в Audit режим для оценки нарушений
kubectl patch clusterpolicy require-resource-limits \
--type merge \
-p '{"spec":{"validationFailureAction":"Audit"}}'
# Посмотреть нарушения без блокировки
kubectl get policyreports -A -o json | \
jq '[.items[].results[] | select(.result == "fail") | .policy] |
group_by(.) | map({policy: .[0], violations: length}) |
sort_by(-.violations)[]'
# После исправления — переключить обратно
kubectl patch clusterpolicy require-resource-limits \
--type merge \
-p '{"spec":{"validationFailureAction":"Enforce"}}'
```

View File

@@ -0,0 +1,51 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: allow-only-trusted-registries
annotations:
policies.kyverno.io/title: "Только доверенные реестры"
policies.kyverno.io/category: Security
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Разрешает образы только из доверенных реестров компании.
Предотвращает использование образов из публичных реестров
без проверки безопасности.
НАСТРОЙТЕ список разрешённых реестров под вашу инфраструктуру.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-trusted-registries
match:
resources:
kinds:
- Pod
exclude:
resources:
namespaces:
- kube-system
- kyverno
validate:
message: >-
Образ '{{ element.image }}' из недоверенного реестра.
Разрешены только:
- registry.company.com/
- gcr.io/company-project/
Загрузите образ в внутренний реестр и обновите манифест.
foreach:
- list: >-
request.object.spec.containers[] |
merge(request.object.spec.initContainers[] || `[]`, @)
deny:
conditions:
all:
# Образ НЕ из первого доверенного реестра
- key: "{{ element.image }}"
operator: NotStartsWith
value: "registry.company.com/"
# И НЕ из второго доверенного реестра
- key: "{{ element.image }}"
operator: NotStartsWith
value: "gcr.io/company-project/"
# Добавьте дополнительные условия по аналогии

View File

@@ -0,0 +1,44 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-latest-tag
annotations:
policies.kyverno.io/title: "Запрет тега latest"
policies.kyverno.io/category: Best Practices
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Запрещает использование тега :latest и образов без тега.
Оба варианта резолвятся в "последний доступный образ",
что делает деплойменты невоспроизводимыми.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: disallow-latest-tag
match:
resources:
kinds:
- Pod
exclude:
resources:
namespaces:
- kube-system
validate:
message: >-
Образ '{{ element.image }}' использует тег :latest или не имеет тега.
Используйте конкретный тег (например, nginx:1.25.3) или digest
(nginx@sha256:abc123...) для воспроизводимых деплойментов.
foreach:
- list: >-
request.object.spec.containers[] |
merge(request.object.spec.initContainers[] || `[]`, @)
deny:
conditions:
any:
- key: "{{ element.image }}"
operator: Contains
value: ":latest"
- key: "{{ element.image }}"
operator: NotContains
value: ":"

View File

@@ -0,0 +1,46 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-standard-labels
annotations:
policies.kyverno.io/title: "Обязательные стандартные лейблы"
policies.kyverno.io/category: Governance
policies.kyverno.io/severity: medium
policies.kyverno.io/subject: Deployment,StatefulSet,DaemonSet
policies.kyverno.io/description: >-
Требует наличия стандартных лейблов у workload ресурсов.
Лейблы используются для мониторинга, алертинга и распределения затрат.
Допустимые значения environment: dev | staging | production
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-required-labels
match:
resources:
kinds:
- Deployment
- StatefulSet
- DaemonSet
exclude:
resources:
namespaces:
- kube-system
- kyverno
validate:
message: >-
Ресурс '{{ request.object.metadata.name }}' должен иметь лейблы:
app, version, team, environment (dev|staging|production)
Пример:
labels:
app: my-service
version: "1.0.0"
team: payments
environment: production
pattern:
metadata:
labels:
app: "?*"
version: "?*"
team: "?*"
environment: "^(dev|staging|production)$"

View File

@@ -0,0 +1,31 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-min-replicas-production
annotations:
policies.kyverno.io/title: "Минимальное количество реплик в production"
policies.kyverno.io/category: Availability
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Deployment
policies.kyverno.io/description: >-
В namespace production требуется минимум 2 реплики для Deployment.
Одна реплика = single point of failure при обновлении ноды или пода.
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-min-replicas
match:
resources:
kinds:
- Deployment
namespaces:
- production
validate:
message: >-
Deployment '{{ request.object.metadata.name }}' в production
имеет {{ request.object.spec.replicas }} реплику(и).
Минимально требуется 2 реплики для обеспечения доступности.
pattern:
spec:
replicas: ">=2"

View File

@@ -0,0 +1,49 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
annotations:
policies.kyverno.io/title: "Обязательные resource limits"
policies.kyverno.io/category: Resources
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Pod
policies.kyverno.io/description: >-
Каждый контейнер (включая init и ephemeral) обязан иметь
limits.memory и limits.cpu. Без лимитов контейнер может
потребить все ресурсы ноды и убить соседей (noisy neighbor).
spec:
validationFailureAction: Enforce
background: true
rules:
- name: check-container-limits
match:
resources:
kinds:
- Pod
exclude:
resources:
namespaces:
- kube-system
- kyverno
validate:
message: >-
Контейнер '{{ element.name }}' в поде '{{ request.object.metadata.name }}'
(namespace: {{ request.object.metadata.namespace }}) не имеет resource limits.
Добавьте в манифест:
resources:
limits:
memory: "256Mi"
cpu: "500m"
Документация: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
foreach:
- list: >-
request.object.spec.containers[] |
merge(request.object.spec.initContainers[] || `[]`, @) |
merge(request.object.spec.ephemeralContainers[] || `[]`, @)
pattern:
resources:
limits:
memory: "?*"
cpu: "?*"

View File

@@ -0,0 +1,19 @@
apiVersion: v1
kind: Pod
metadata:
name: pod-latest-image
namespace: default
spec:
containers:
- name: nginx
image: nginx:latest # тег :latest — политика отклонит
resources:
limits:
memory: "128Mi"
cpu: "100m"
- name: redis
image: redis # нет тега вообще — тоже отклонит
resources:
limits:
memory: "128Mi"
cpu: "100m"

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: Pod
metadata:
name: pod-no-limits
namespace: default
spec:
containers:
- name: nginx
image: nginx:1.25.3
# нет блока resources — политика require-resource-limits отклонит

View File

@@ -0,0 +1,21 @@
apiVersion: v1
kind: Pod
metadata:
name: pod-with-limits
namespace: default
labels:
app: my-app
version: "1.0.0"
team: platform
environment: staging
spec:
containers:
- name: nginx
image: nginx:1.25.3 # конкретный тег — хорошо
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "100m"

View File

@@ -0,0 +1,30 @@
name: resource-validation-tests
policies:
- ../require-resource-limits.yaml
- ../disallow-latest-tag.yaml
- ../require-labels.yaml
resources:
- pod-with-limits.yaml
- pod-no-limits.yaml
- pod-latest-image.yaml
results:
- policy: require-resource-limits
rule: check-container-limits
resource: pod-with-limits
namespace: default
result: pass
- policy: require-resource-limits
rule: check-container-limits
resource: pod-no-limits
namespace: default
result: fail
- policy: disallow-latest-tag
rule: disallow-latest-tag
resource: pod-with-limits
namespace: default
result: pass
- policy: disallow-latest-tag
rule: disallow-latest-tag
resource: pod-latest-image
namespace: default
result: fail