fix: env vars через newdeploy+podspec — попадают в pod OS env для всех языков

This commit is contained in:
“Naeel”
2026-05-09 07:49:36 +04:00
parent 089994256c
commit 7887d4597d
4 changed files with 190 additions and 89 deletions
+116 -82
View File
@@ -1,94 +1,128 @@
# Переменные окружения функций (env vars)
# Function Env Vars — ПОЛНАЯ ДОКУМЕНТАЦИЯ (v1.3.77+)
## Хранение
## ПРОБЛЕМА (обнаружена 2026-05-09)
Env vars хранятся прямо в Function CRD в поле `.spec.runtime.container.env`:
Env vars хранились только в аннотации `fission-console/env-vars` на Function CRD.
Это работало для UI (хранение/отображение), но переменные **не попадали в pod функции**.
### Почему не попадали
Fission `poolmgr` создаёт warm pool подов **заранее**, до того как знает какая функция
будет запущена. Pod уже работает в момент первого вызова функции.
`spec.podspec.containers[].env` из Function CRD при poolmgr **игнорируется** — pod уже запущен.
При специализации (загрузке кода функции) fetcher передаёт в pod только:
- путь к файлу с кодом
- имя entrypoint функции
- секреты и configmaps (как файлы в `/userfunc/...`)
Переменные окружения через `poolmgr` можно установить только на уровне Environment deployment —
одинаковые для ВСЕХ функций этого окружения. Индивидуально для функции — невозможно.
### Отвергнутые варианты
| Вариант | Почему нет |
|---------|-----------|
| `spec.podspec.containers[].env` + poolmgr | Pod уже запущен до специализации |
| `spec.configmaps` → файлы `/userfunc/configs/` | Нужно читать файл в коде — не универсально |
| Патч python-env server | Только Python, другие языки сломаны |
| PostgreSQL/отдельное хранилище | Оверкилл, не официальный путь |
---
## РЕШЕНИЕ (v1.3.77+)
**Автоматическое переключение ExecutorType в зависимости от наличия env vars.**
- Функция **без env vars**`ExecutorType: poolmgr` → warm pool, быстрый cold start (~0.5 сек)
- Функция **с env vars**`ExecutorType: newdeploy` → dedicated Deployment, env vars в OS (~2-5 сек)
Пользователь ничего не настраивает — переключение происходит автоматически.
### Почему newdeploy работает
При `newdeploy` Fission создаёт отдельный Kubernetes `Deployment` для функции.
В этот Deployment попадает `spec.podspec` из Function CRD **включая** `containers[0].env`.
Kubernetes ставит переменные на уровне ОС процесса контейнера при старте пода.
### Почему это универсально для всех языков
Переменные окружения ОС — стандарт POSIX. Любой язык читает без изменений в env-сервере:
```python
os.getenv("MY_VAR") # Python
os.Getenv("MY_VAR") # Go
ENV["MY_VAR"] # Ruby
getenv("MY_VAR") # PHP
process.env.MY_VAR # Node.js
```
### Нет дублирования подов
Warm pool принадлежит **Environment**, не функции. Переключение конкретной функции на `newdeploy`
не трогает pool — другие функции без env vars продолжают использовать его.
---
## РЕАЛИЗАЦИЯ
### Backend: `handlePutFunctionEnvVars` (function_crud.go)
При `PUT /functions/:name/envvars`:
1. Сохраняет env vars в аннотацию `fission-console/env-vars` (для UI)
2. Если env vars **непустые**:
- Выставляет `spec.podspec.containers[0].env` (стандартный Kubernetes EnvVar)
- Переключает `spec.InvokeStrategy.ExecutionStrategy.ExecutorType` = `"newdeploy"`
- MinScale=0, MaxScale=1
3. Если env vars **пустые** (очищены):
- Удаляет `spec.podspec`
- Возвращает `ExecutorType` = `"poolmgr"`
### Двойное хранение
| Где | Зачем |
|-----|-------|
| Аннотация `fission-console/env-vars` | UI: отображение, редактирование |
| `spec.podspec.containers[0].env` | Fission/Kubernetes: реальное применение в pod |
### Структура Function CRD с env vars
```yaml
apiVersion: fission.io/v1
kind: Function
metadata:
name: my-fn
namespace: c479c9de005f
spec:
runtime:
container:
env:
- name: DB_HOST
value: "localhost"
- name: API_KEY
value: "secret123"
InvokeStrategy:
ExecutionStrategy:
ExecutorType: newdeploy
MinScale: 0
MaxScale: 1
StrategyType: execution
podspec:
containers:
- name: <env-name>
env:
- name: p1
value: "envv1"
metadata:
annotations:
fission-console/env-vars: '[{"name":"p1","value":"envv1"}]'
```
**Почему CRD, а не ConfigMap/PostgreSQL:**
- Env vars — часть конфигурации функции, а не статистика
- CRD хранится в etcd — единственном хранилище состояния Kubernetes
- При удалении функции env vars удаляются автоматически (нет orphan-объектов)
- Не нужен дополнительный RBAC для ConfigMap
- Fission executor сам передаёт `.spec.runtime.container.env` в pod
### Структура Function CRD без env vars
## API
### GET /console/api/functions/:name/envvars
Возвращает список переменных:
```json
{
"env_vars": [
{"name": "DB_HOST", "value": "localhost"},
{"name": "API_KEY", "value": "secret123"}
]
}
```yaml
spec:
InvokeStrategy:
ExecutionStrategy:
ExecutorType: poolmgr
StrategyType: execution
# нет podspec
```
### PUT /console/api/functions/:name/envvars
Принимает полный список (полная замена, не merge):
```json
{
"env_vars": [
{"name": "DB_HOST", "value": "new-host"},
{"name": "NEW_VAR", "value": "val"}
]
}
```
Пустой массив `[]` — удалить все переменные.
---
### GET /console/api/functions/:name
Поле `env_vars` включено в стандартный ответ (чтобы edit modal не делал отдельный запрос).
## ОГРАНИЧЕНИЯ
## Frontend (edit modal)
- Блок "Переменные окружения" появляется в нижней части edit modal при каждом открытии
- Каждая переменная — строка: input[KEY] = input[value] [×]
- Кнопка "+ Добавить переменную" добавляет пустую строку
- Сохранение происходит вместе с остальными полями (Сохранить)
- После сохранения кода/архива вызывается PUT /envvars с текущим состоянием блока
## Terraform-функции (read-only)
Функции с именем `tf-*` или с go-env (Terraform-управляемые) показывают блок env vars в режиме **только чтения**:
- Все input-поля `disabled`
- Кнопки `[×]` и `[+ Добавить]` скрыты
- Предупреждение: "⚠️ Функция управляется Terraform — переменные только для чтения"
- PUT /envvars **не вызывается** при сохранении TF-функций
Изменить переменные TF-функции можно только через Terraform (`terraform apply`).
## Backend (Go)
**Файл:** `console/internal/api/function_crud.go`
- `handleGetFunctionEnvVars` — читает `.spec.runtime.container.env` через dynamic client
- `handlePutFunctionEnvVars` — PATCH Function CRD, обновляет `.spec.runtime.container.env`
- `extractEnvVars` — вспомогательная функция, читает из unstructured в `[]map[string]string`
**Роутинг:** `console/internal/api/handlers.go`
```
GET /console/api/functions/:name/envvars → handleGetFunctionEnvVars
PUT /console/api/functions/:name/envvars → handlePutFunctionEnvVars
```
## Ограничения (MVP)
- Секретные переменные (пароли, токены) хранятся в открытом виде в etcd
- Для sensitive данных в будущем нужно добавить поддержку Secret (valueFrom.secretKeyRef)
- Нет валидации имён переменных по RFC (разрешены любые непустые строки)
- `newdeploy` медленнее cold start (~2-5 сек vs ~0.5 сек) — выбор пользователя, добавившего env vars
- MinScale=0: при отсутствии трафика pod удаляется → cold start при первом вызове
- Имена переменных: только `[A-Za-z_][A-Za-z0-9_]*` — стандарт POSIX, проверяется на UI
- TF-функции (`tf-*`) — env vars read-only, изменение запрещено через UI