doc: add detailed layer1 multi-tenant fix report

This commit is contained in:
Naeel
2026-04-26 09:06:44 +03:00
parent 7faaa9dc1f
commit e2dff8db09
@@ -0,0 +1,248 @@
# 2026-04-26 - Layer1 multi-tenant NSWatcher: полный разбор до 5/5 PASS
## Цель
Довести `test_layer1.sh` до `PASS=5 FAIL=0` для сценария:
1. создаётся новый namespace
2. namespace получает label `fission.io/managed=true`
3. Fission без рестарта подхватывает namespace
4. в namespace создаются `Environment`, `Function`, `HTTPTrigger`
5. функция успешно вызывается через router
Ключевое требование: всё должно происходить без rolling restart Fission-компонентов.
## Исходный симптом
Первый устойчивый симптом был таким:
- `test_layer1.sh` стабильно доходил до `4/5`
- шаг вызова функции падал
- в user namespace наблюдалось:
- `FailedCreate`
- `serviceaccount "fission-fetcher" not found`
Это означало, что poolmgr deployment для environment уже создаётся, но pod не может стартовать без `fission-fetcher` ServiceAccount.
## Что уже было исправлено до RBAC-этапа
Кодовая часть hot-registration была уже внедрена ранее:
- `pkg/utils/serviceaccount.go`
- добавлена `EnsureNamespaceSA(...)`
- `pkg/executor/multitenant/ns_watcher.go`
- при регистрации нового namespace вызывается `EnsureNamespaceSA(...)`
- образ `naeel/fission-bundle:v1.22.0-multi-ns-8` уже был собран и задеплоен
То есть логика в коде уже существовала; сбой был не в отсутствии вызова, а в невозможности выполнить его успешно в кластере.
## Диагностика 1: executor не может создать ServiceAccount/Role/RoleBinding
Была проведена проверка прав service account `fission-executor`.
Подтверждено:
- код `EnsureNamespaceSA` вызывается
- `ns_watcher` регистрирует namespace
- executor не имеет достаточных RBAC-прав для provisioning ресурсов в новом namespace
Первый явный пробел:
- отсутствовали права на:
- `serviceaccounts`
- `roles`
- `rolebindings`
После начального RBAC fix было видно, что `ServiceAccount/fission-fetcher` уже создаётся, но этого оказалось недостаточно.
## Диагностика 2: initial RBAC fix оказался неполным
После расширения прав на `serviceaccounts/roles/rolebindings` тест перестал падать на отсутствии SA, но при детальной диагностике выяснилось, что `EnsureNamespaceSA` всё ещё не может полностью создать `Role` для fetcher.
Ключевой лог executor:
```text
error while creating role for sa fission-fetcher in namespace diag-ns-82702
... is attempting to grant RBAC permissions not currently held:
{APIGroups:[""], Resources:["events"], Verbs:["create"]}
```
И дополнительный лог перед этим:
```text
localsubjectaccessreviews.authorization.k8s.io is forbidden
```
### Что это означает
Функция `setupSAAndRoleBindings()` делает две важные вещи:
1. пытается проверить уже существующие права через `LocalSubjectAccessReview`
2. если прав нет, создаёт `Role` с нужными permission-ами
Следовательно executor должен иметь не только право создавать `Role/RoleBinding`, но и:
- `authorization.k8s.io/localsubjectaccessreviews:create`
- все permission-ы, которые он пытается делегировать через создаваемую `Role`
В нашем случае fetcher получает право:
- `events:create`
По правилам Kubernetes нельзя создать `Role`, выдающую право, которого нет у самого вызывающего субъекта. Поэтому executor должен был сам иметь `events:create`.
### Реальный root cause на этом этапе
`fission-executor` не имел:
- `events.create`
- `localsubjectaccessreviews.create`
Из-за этого:
- `ServiceAccount` создавался
- но `Role` и `RoleBinding` создавались не полностью или не создавались вовсе
- downstream specialization ломалась
## Исправление 1: полный executor RBAC для dynamic SA provisioning
В `deploy/multitenant/rbac.yaml` был добавлен и затем расширен `ClusterRole`:
- `fission-executor-sa-provisioner`
Итоговый набор прав для него:
- core:
- `serviceaccounts`: `get`, `list`, `watch`, `create`, `update`, `patch`
- `events`: `create`
- `authorization.k8s.io`:
- `localsubjectaccessreviews`: `create`
- `rbac.authorization.k8s.io`:
- `roles`: `get`, `list`, `watch`, `create`, `update`, `patch`
- `rolebindings`: `get`, `list`, `watch`, `create`, `update`, `patch`
После применения этого манифеста было подтверждено:
- `kubectl auth can-i create events --as=system:serviceaccount:fission:fission-executor` -> `yes`
- `kubectl auth can-i create localsubjectaccessreviews.authorization.k8s.io --as=system:serviceaccount:fission:fission-executor` -> `yes`
И в новом test namespace автоматически появлялись:
- `ServiceAccount/fission-fetcher`
- `Role/fission-fetcher-role-*`
- `RoleBinding/fission-fetcher-rolebinding-*`
## Изменение симптома после executor-fix
После полного executor RBAC fix шаг 5 перестал падать с `500` timeout от executor.
Новый симптом:
- постоянный `HTTP 404`
- router не видел route/function в новом namespace
Это был важный индикатор того, что executor-path уже работает лучше, а оставшаяся проблема находится в router-path.
## Диагностика 3: router NSWatcher не мог watch/list namespaces
Лог router показал прямую ошибку:
```text
failed to list *v1.Namespace: namespaces is forbidden:
User "system:serviceaccount:fission:fission-router" cannot list resource
"namespaces" at the cluster scope
```
При этом код router уже содержал dynamic namespace watcher:
- `pkg/router/ns_watcher.go`
То есть логика была, но RBAC для `fission-router` отсутствовал.
### Реальный root cause на этом этапе
`fission-router` не имел cluster-scope прав:
- `namespaces:list`
- `namespaces:watch`
Из-за этого:
- router не подхватывал новые labeled namespaces
- `HTTPTriggerSet.AddNamespace(...)` не вызывался
- HTTP trigger не попадал в router runtime map
- вызов функции возвращал `404`
## Исправление 2: router RBAC для NSWatcher
В тот же `deploy/multitenant/rbac.yaml` добавлены:
- `ClusterRole/fission-router-ns-watcher`
- `ClusterRoleBinding/fission-router-ns-watcher`
С правами:
- core `namespaces`: `list`, `watch`
После применения подтверждено:
- `kubectl auth can-i list namespaces --as=system:serviceaccount:fission:fission-router` -> `yes`
- `kubectl auth can-i watch namespaces --as=system:serviceaccount:fission:fission-router` -> `yes`
## Финальная проверка
После обоих RBAC fixes повторный запуск `test_layer1.sh` дал:
```text
ИТОГ: PASS=5 FAIL=0
```
На шаге 5 функция успешно ответила:
```text
HTTP 200 - hello from layer1
```
## Что именно оказалось правдой по итогу
Итоговая проблема состояла из двух последовательных RBAC-дырок:
1. executor не мог полностью provision-ить `fission-fetcher` в динамическом namespace
2. router не мог подхватить новый namespace из-за отсутствия namespace watch/list
То есть код hot-registration в целом был правильный, но runtime contract в Kubernetes RBAC был реализован не полностью.
## Итоговые изменения
### Код и манифесты
- `deploy/multitenant/rbac.yaml`
- executor namespace watch
- executor SA provisioning RBAC
- router namespace watch RBAC
### Документация
- `doc/progress.md`
- `doc/thinking/2026-04-26-rbac-fix.md`
- `doc/thinking/2026-04-26-layer1-pass-detailed.md`
### Коммиты по ходу исправления
- `161de70` - `multi-tenant: EnsureNamespaceSA + ns_watcher SA provisioning (v8)`
- `8ccc9fb` - первый RBAC commit
- `f617913` - полный executor RBAC fix для fetcher role provisioning
- `7faaa9d` - router namespace watch RBAC
## Практический вывод
Для hot namespace onboarding в Fission недостаточно просто добавить informer-ы в коде.
Нужно обеспечить весь runtime contract:
- executor видит namespace
- executor может provision-ить service accounts и RBAC в tenant namespace
- executor может делегировать все требуемые permission-ы
- router видит namespace и подписывается на triggers/functions в нём
Если хотя бы одно из этих звеньев отсутствует, поведение выглядит как "код вроде есть, но dynamic namespace не работает".