107 lines
4.8 KiB
Go
107 lines
4.8 KiB
Go
package cloud
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
|
|
"golang.org/x/net/context"
|
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/client-go/dynamic"
|
|
)
|
|
|
|
var (
|
|
quotaGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "resourcequotas"}
|
|
limitRangeGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "limitranges"}
|
|
)
|
|
|
|
// ApplyResourceQuota создаёт ResourceQuota и LimitRange в namespace ns.
|
|
// Параметры берутся из env vars (см. ниже) — можно тюнить без пересборки.
|
|
// Идемпотентно: если объекты уже существуют — пропускается без ошибки.
|
|
//
|
|
// ResourceQuota ограничивает суммарные ресурсы пользователя:
|
|
// - CPU / memory requests и limits
|
|
// - Количество подов, функций, пакетов, triggers
|
|
//
|
|
// LimitRange задаёт дефолтные limits/requests на контейнер:
|
|
// поды без явных limits получают defaults автоматически → нет unbounded containers.
|
|
//
|
|
// Env vars (все опциональны, defaults указаны ниже):
|
|
//
|
|
// QUOTA_REQ_CPU — суммарный CPU requests (default: "1")
|
|
// QUOTA_REQ_MEM — суммарная memory requests (default: "2Gi")
|
|
// QUOTA_LIM_CPU — суммарный CPU limits (default: "12")
|
|
// QUOTA_LIM_MEM — суммарная memory limits (default: "6Gi")
|
|
// QUOTA_PODS — макс. количество подов (default: "30")
|
|
// QUOTA_FUNCTIONS — макс. количество функций (default: "20")
|
|
// QUOTA_PACKAGES — макс. количество пакетов (default: "40")
|
|
// QUOTA_HTTPTRIGGERS — макс. количество HTTP triggers (default: "20")
|
|
// LIMIT_DEFAULT_CPU — дефолтный CPU limit на контейнер (default: "500m")
|
|
// LIMIT_DEFAULT_MEM — дефолтная memory limit на контейнер (default: "256Mi")
|
|
// LIMIT_REQ_CPU — дефолтный CPU request на контейнер (default: "50m")
|
|
// LIMIT_REQ_MEM — дефолтный memory request на контейнер (default: "64Mi")
|
|
// LIMIT_MAX_CPU — максимальный CPU limit на контейнер (default: "2")
|
|
// LIMIT_MAX_MEM — максимальная memory limit на контейнер (default: "1Gi")
|
|
func ApplyResourceQuota(ctx context.Context, dyn dynamic.Interface, ns string) {
|
|
quotaObj := &unstructured.Unstructured{Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "ResourceQuota",
|
|
"metadata": map[string]any{"name": "user-quota", "namespace": ns},
|
|
"spec": map[string]any{
|
|
"hard": map[string]any{
|
|
"requests.cpu": envDefault("QUOTA_REQ_CPU", "1"),
|
|
"requests.memory": envDefault("QUOTA_REQ_MEM", "2Gi"),
|
|
"limits.cpu": envDefault("QUOTA_LIM_CPU", "12"),
|
|
"limits.memory": envDefault("QUOTA_LIM_MEM", "6Gi"),
|
|
"pods": envDefault("QUOTA_PODS", "30"),
|
|
"count/functions.fission.io": envDefault("QUOTA_FUNCTIONS", "20"),
|
|
"count/packages.fission.io": envDefault("QUOTA_PACKAGES", "40"),
|
|
"count/httptriggers.fission.io": envDefault("QUOTA_HTTPTRIGGERS", "20"),
|
|
},
|
|
},
|
|
}}
|
|
_, err := dyn.Resource(quotaGVR).Namespace(ns).Create(ctx, quotaObj, metav1.CreateOptions{})
|
|
if err != nil && !apierrors.IsAlreadyExists(err) {
|
|
log.Printf("cloud.ApplyResourceQuota: %s: %v", ns, err)
|
|
}
|
|
|
|
limitRangeObj := &unstructured.Unstructured{Object: map[string]any{
|
|
"apiVersion": "v1",
|
|
"kind": "LimitRange",
|
|
"metadata": map[string]any{"name": "user-limits", "namespace": ns},
|
|
"spec": map[string]any{
|
|
"limits": []any{
|
|
map[string]any{
|
|
"type": "Container",
|
|
"default": map[string]any{
|
|
"cpu": envDefault("LIMIT_DEFAULT_CPU", "500m"),
|
|
"memory": envDefault("LIMIT_DEFAULT_MEM", "256Mi"),
|
|
},
|
|
"defaultRequest": map[string]any{
|
|
"cpu": envDefault("LIMIT_REQ_CPU", "50m"),
|
|
"memory": envDefault("LIMIT_REQ_MEM", "64Mi"),
|
|
},
|
|
"max": map[string]any{
|
|
"cpu": envDefault("LIMIT_MAX_CPU", "2"),
|
|
"memory": envDefault("LIMIT_MAX_MEM", "1Gi"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}}
|
|
_, err = dyn.Resource(limitRangeGVR).Namespace(ns).Create(ctx, limitRangeObj, metav1.CreateOptions{})
|
|
if err != nil && !apierrors.IsAlreadyExists(err) {
|
|
log.Printf("cloud.ApplyResourceQuota LimitRange: %s: %v", ns, err)
|
|
}
|
|
}
|
|
|
|
// envDefault читает env переменную или возвращает fallback.
|
|
func envDefault(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|