diff --git a/deployments/function-pods/README.md b/deployments/function-pods/README.md deleted file mode 100644 index 7c78ec539..000000000 --- a/deployments/function-pods/README.md +++ /dev/null @@ -1,35 +0,0 @@ -### Function Pod Template - -In order to leverage custom manifests for Pod and frontend Service of Function Pods created by Function Runner, the following additional Kubernetes Resource Manifests (KRM) need to be provisioned in the Porch environment - -* A ConfigMap containing 2 data elements: a) a KRM of type Pod under the `template` key and b) a KRM of type Service under `serviceTemplate` key -* A Kubernetes Role providing read access to resource type ConfigMap in the porch-system namespace -* A Kubernetes RoleBinding, binding to the above listed Role to the ServiceAccount (porch-fn-runner) used by Function Runner Pod - -All of the above KRMs are predefined in `deployment.yaml` file present in this folder. - -### How to enable Function Pod Template use by Function Runner - -* Apply the [deployment.yaml manifest](deployment.yaml) from this directory - -``` -kubectl apply -f deployment.yaml -``` - -* Add an additional argument `--function-pod-template` in command section of function-runner deployment instructing it to use the Function Pod Template ConfigMap, as shown below - -``` -kubectl edit deployment -n porch-system function-runner -``` - -``` - command: - - /server - - --config=/config.yaml - - --functions=/functions - - --pod-namespace=porch-fn-system - - --function-pod-template=kpt-function-eval-pod-template - - --max-request-body-size=6291456 # Keep this in sync with porch-server's corresponding argument -``` - -After the function-runner Pods restart, they will start using the Pod and Service templates from ConfigMap. diff --git a/deployments/function-pods/deployment.yaml b/deployments/function-pods/deployment.yaml index 9dcee5ed3..f482d2642 100644 --- a/deployments/function-pods/deployment.yaml +++ b/deployments/function-pods/deployment.yaml @@ -11,73 +11,80 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +# Sample replacement for the function-runner base templates. Applying this +# overwrites base-pod-template and base-service-template in porch-fn-system. +# The function-runner looks these up by name; no extra CLI flags are required. --- apiVersion: v1 -kind: ConfigMap +kind: PodTemplate metadata: - name: kpt-function-eval-pod-template - namespace: porch-system -data: - template: | - apiVersion: v1 - kind: Pod + name: base-pod-template + namespace: porch-fn-system +template: + metadata: annotations: - cluster-autoscaler.kubernetes.io/safe-to-evict: true - spec: - initContainers: - - name: copy-wrapper-server - image: ghcr.io/kptdev/porch-wrapper-server:latest - command: - - cp - - -a - - /home/nonroot/wrapper-server/. - - /wrapper-server-tools - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: image-replaced-by-kpt-func-image - command: - - /wrapper-server-tools/wrapper-server - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} - serviceTemplate: | - apiVersion: v1 - kind: Service - spec: - ports: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + spec: + initContainers: + - name: copy-wrapper-server + image: ghcr.io/kptdev/porch-wrapper-server:latest + command: + - cp + - -a + - /home/nonroot/wrapper-server/. + - /wrapper-server-tools + volumeMounts: + - name: wrapper-server-tools + mountPath: /wrapper-server-tools + imagePullPolicy: IfNotPresent + containers: + - name: function + image: image-replaced-by-kpt-func-image + command: + - /wrapper-server-tools/wrapper-server + env: + - name: OTEL_METRICS_EXPORTER + value: prometheus + - name: OTEL_TRACES_EXPORTER + value: none + - name: OTEL_EXPORTER_PROMETHEUS_HOST + value: 0.0.0.0 + - name: OTEL_EXPORTER_PROMETHEUS_PORT + value: "9464" + ports: + - containerPort: 9464 + name: metrics + readinessProbe: + exec: + command: [ "/wrapper-server-tools/grpc-health-probe", "-addr", "localhost:9446" ] + livenessProbe: + exec: + command: [ "/wrapper-server-tools/grpc-health-probe", "-addr", "localhost:9446" ] + volumeMounts: + - name: wrapper-server-tools + mountPath: /wrapper-server-tools + imagePullPolicy: IfNotPresent + volumes: + - name: wrapper-server-tools + emptyDir: {} +--- +apiVersion: config.porch.kpt.dev/v1alpha1 +kind: ServiceTemplate +metadata: + name: base-service-template + namespace: porch-fn-system +template: + spec: + ports: - port: 9446 protocol: TCP targetPort: 9446 - selector: - fn.kpt.dev/image: to-be-replaced - type: ClusterIP ---- -# Need to lookup and access Configmap containing Function Pod Template -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: porch-fn-runner - namespace: porch-system -rules: - - apiGroups: [""] - resources: ["configmaps"] - verbs: ["get", "list"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: porch-fn-runner - namespace: porch-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: porch-fn-runner -subjects: - - kind: ServiceAccount - name: porch-fn-runner + name: server + - port: 9464 + protocol: TCP + targetPort: 9464 + name: metrics + selector: + fn.kpt.dev/image: to-be-replaced + type: ClusterIP diff --git a/docs/content/en/docs/2_concepts/functions.md b/docs/content/en/docs/2_concepts/functions.md index b14dd89bd..ab5e02016 100644 --- a/docs/content/en/docs/2_concepts/functions.md +++ b/docs/content/en/docs/2_concepts/functions.md @@ -9,7 +9,7 @@ description: | ## What are Functions in Porch? **Functions** in Porch are [KRM (Kubernetes Resource Model) functions](https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md) - -containerized programs that transform or validate Kubernetes resource manifests within a package's files. Functions are +programs (usually containerized) that transform or validate Kubernetes resource manifests within a package's files. Functions are declared in a package's Kptfile and executed by Porch when rendering the package. Functions enable: @@ -20,9 +20,51 @@ Functions enable: For details on how to declare and configure functions in the Kptfile pipeline, see the [kpt functions documentation](https://kpt.dev/book/04-using-functions/). +## Function Configuration + +Porch uses **FunctionConfig** custom resources to choose an executor for each function image and to supply executor-specific settings. +A FunctionConfig names the function image and optional registry prefixes, then attaches a pod executor, a binary executor, a Go executor, or any combination of the three. +Tags on each executor select which image versions use that path. + +The default Porch install deploys FunctionConfig objects for common catalog functions into `porch-fn-system`. +porch-server, function-runner, and porch-controllers each run an embedded reconciler that copies those objects into an in-memory store used at evaluation time. + +```yaml +apiVersion: config.porch.kpt.dev/v1alpha1 +kind: FunctionConfig +metadata: + name: set-namespace + namespace: porch-fn-system +spec: + image: set-namespace + prefixes: + - "" + - ghcr.io/kptdev/krm-functions-catalog + podExecutor: + tags: + - v0.4.1 + timeToLive: 30m + binaryExecutor: + tags: + - v0.4.2 + path: set-namespace + goExecutor: + id: set-namespace + tags: + - v0.4 + - v0.4.5 +``` + +The spec, status, and matching rules are documented in [Function Configuration]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md" %}}). + ## Function Execution in Porch -Porch executes functions through a **function runner** component that calls kpt to orchestrate containerized function execution. The functions run in isolated containers (Kubernetes pods managed by the `function-runner` microservice). Porch passes the package's resources to kpt, which passes the resources on as a [ResourceList](https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md#resourcelist) to each function in the pipeline in turn. kpt executes the functions sequentially in the order declared in the Kptfile pipeline and passes the function results back to Porch, which stores them in the PackageRevisionResources's `status.renderStatus` field. Execution is triggered automatically following creation or clone of a package revision, update of a package revision, and when a package revision is proposed. +Porch executes functions through the Engine's function runtime, which picks one of three executors for each function image based on the matching FunctionConfig: an in-process **Go executor**, a **binary executor** running inside the function-runner, or a **pod executor** running the function image in a dedicated Kubernetes pod (the default for arbitrary images). +See [Function Configuration]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md" %}}) for how executors are selected and configured per image. + +Regardless of executor, Porch passes the package's resources to [kpt](https://kpt.dev), which passes them on as a [ResourceList](https://github.com/kubernetes-sigs/kustomize/blob/master/cmd/config/docs/api-conventions/functions-spec.md#resourcelist) to each function in the pipeline in order. +kpt runs the functions sequentially and returns the results to Porch, which stores them in the PackageRevisionResources `status.renderStatus` field. +Rendering is triggered automatically after creating or cloning a package revision, after updating a package revision, and when a package revision is proposed. ## When Functions Execute @@ -77,7 +119,8 @@ enabling iterative development on incomplete packages. ## Key Points - Functions are standard KRM functions declared in the Kptfile pipeline (see [kpt functions docs](https://kpt.dev/book/04-using-functions/)) -- Porch invokes kpt to execute functions via a function-runner component using containerized execution +- Function execution is configured with FunctionConfig custom resources that select a pod, binary, or Go executor per image tag +- porch-server, function-runner, and porch-controllers each reconcile FunctionConfig objects into an in-memory store used at evaluation time - Functions automatically execute during package rendering on Draft package revisions - Function results are stored in `status.renderStatus` of the PackageRevisionResources view of a package revision - Published packages are immutable - functions don't re-execute after publication diff --git a/docs/content/en/docs/3_getting_started/installing-porch.md b/docs/content/en/docs/3_getting_started/installing-porch.md index ee99c7970..a0bb84017 100644 --- a/docs/content/en/docs/3_getting_started/installing-porch.md +++ b/docs/content/en/docs/3_getting_started/installing-porch.md @@ -72,15 +72,30 @@ kubectl api-resources | grep porch You should see Porch API resources: ```bash +functionconfigs config.porch.kpt.dev/v1alpha1 true FunctionConfig packagerevs config.porch.kpt.dev/v1alpha1 true PackageRev packagevariants config.porch.kpt.dev/v1alpha1 true PackageVariant packagevariantsets config.porch.kpt.dev/v1alpha2 true PackageVariantSet repositories config.porch.kpt.dev/v1alpha1 true Repository +servicetemplates config.porch.kpt.dev/v1alpha1 true ServiceTemplate packagerevisionresources porch.kpt.dev/v1alpha1 true PackageRevisionResources packagerevisions porch.kpt.dev/v1alpha1 true PackageRevision packages porch.kpt.dev/v1alpha1 true PorchPackage ``` +Verify that FunctionConfig resources were deployed into the function-pod namespace (default `porch-fn-system`, configured on the function-runner with `--pod-namespace`). + +```bash +kubectl get functionconfigs -n porch-fn-system +``` + +A healthy install shows one FunctionConfig per bundled catalog function (apply-replacements, set-namespace, starlark, kubeconform, and others). +The `Server Applied`, `FnRunner Applied`, and `Controller Applied` columns are the generations each component has loaded. They should match the resource generation when the spec is in sync. + +These FunctionConfig objects tell porch-server, function-runner, and porch-controllers which executor (pod, binary, or Go) to use for each function image. +See [Function Configuration]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md" %}}) for the spec +and [Pod Templates]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md" %}}) for the `PodTemplate` and `ServiceTemplate` used by the pod executor. + ## Troubleshooting ### Pods not starting diff --git a/docs/content/en/docs/5_architecture_and_components/engine/interactions.md b/docs/content/en/docs/5_architecture_and_components/engine/interactions.md index d0879d88c..325a2e5ba 100644 --- a/docs/content/en/docs/5_architecture_and_components/engine/interactions.md +++ b/docs/content/en/docs/5_architecture_and_components/engine/interactions.md @@ -151,11 +151,14 @@ the draft. The Task handler has no direct repository access. ### Function Runtime Integration -The task handler uses function runtimes configured in the engine: +The task handler uses function runtimes configured in the engine. +FunctionConfig decides which runtime handles a given image (see [Function Configuration]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md" %}})): -- **Builtin Runtime**: For built-in functions (set-namespace, etc.) -- **gRPC Runtime**: For external function runner service -- **Multi-Runtime**: Chains multiple runtimes together +The **builtin runtime** runs compiled-in Go processors (`apply-replacements`, `set-namespace`, `starlark`) for tags listed on `goExecutor`. + +The **gRPC runtime** calls the function-runner for everything else (binary fast path, then pod). + +The **multi-runtime** tries builtin first and falls back to gRPC. The engine configures these runtimes during initialization and passes them to the task handler. diff --git a/docs/content/en/docs/5_architecture_and_components/function-runner/design.md b/docs/content/en/docs/5_architecture_and_components/function-runner/design.md index 27b888867..63232322b 100644 --- a/docs/content/en/docs/5_architecture_and_components/function-runner/design.md +++ b/docs/content/en/docs/5_architecture_and_components/function-runner/design.md @@ -165,7 +165,7 @@ Choosing between evaluators depends on deployment requirements and function char **Considerations:** - Requires pre-cached function binaries -- Limited to functions in configuration file +- Limited to functions listed on a FunctionConfig `binaryExecutor` - No container isolation (functions run in runner process) - Manual configuration and binary management needed @@ -194,14 +194,14 @@ Function Runner deploys with **pod evaluator by default** when no evaluators are **Configuration method:** - Use `--disable-runtimes` flag to disable specific evaluators - Pod evaluator: No additional configuration needed -- Executable evaluator: Requires configuration file mapping images to binaries +- Executable evaluator: Requires FunctionConfig resources with `binaryExecutor` (and binaries under `--functions`) - Multi-evaluator: Automatically used when multiple evaluators enabled ### Migration Considerations Switching between evaluator configurations requires: - Function Runner restart with new flags -- Executable evaluator requires configuration file with image-to-binary mappings +- Executable evaluator requires FunctionConfig `binaryExecutor` entries and binaries under `--functions` - Pod evaluator requires Kubernetes cluster access and RBAC permissions - No data migration needed (evaluators are stateless) @@ -213,7 +213,7 @@ The evaluator implementations differ fundamentally in execution mechanism and pe |--------|---------------|----------------------| | **Execution Environment** | Kubernetes pods | Local processes | | **Startup Latency** | Variable (pod creation, image pull, cluster speed) | Milliseconds (local process spawn) | -| **Function Discovery** | Dynamic (any image) | Static (configuration file) | +| **Function Discovery** | Dynamic (any image) | Static (FunctionConfig binaryExecutor) | | **Isolation** | Container isolation | Process isolation | | **Resource Management** | Kubernetes limits/quotas | OS process limits | | **Kubernetes API Load** | High (pod/service CRUD) | None | @@ -357,20 +357,20 @@ For detailed explanations of how these differences affect operations, see the in ### Executable Evaluator Configuration -**Decision**: Use static configuration file mapping images to binary paths. +**Decision**: Use FunctionConfig custom resources to map images to binary paths, watched by an embedded reconciler. **Rationale:** -- Simple and explicit configuration -- No dynamic discovery complexity -- Clear mapping between function images and binaries -- Easy to audit and validate +- Same CRD configures pod, binary, and Go executors +- Spec changes apply without restarting the function-runner +- Prefix and tag matching is shared with the builtin runtime +- Easy to audit with `kubectl get functionconfigs` **Alternatives considered:** +- **Static YAML config file**: Required a process restart and drifted from pod-executor settings - **Directory scanning**: Implicit mapping, harder to debug -- **Database storage**: Unnecessary complexity - **Dynamic download**: Security and caching concerns **Trade-offs:** -- Requires manual configuration updates -- No automatic discovery of new binaries -- Configuration must be kept in sync with available binaries +- Binaries still have to be present under `--functions` (or an absolute path) +- Duplicate `spec.image` values on different FunctionConfig objects are ignored +- Only images listed on `binaryExecutor` take the fast path, everything else falls back to pods diff --git a/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/_index.md b/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/_index.md index 7e95b8982..68f9f95f4 100644 --- a/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/_index.md +++ b/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/_index.md @@ -14,8 +14,8 @@ The Function Runner provides three core functional areas that work together to e Executes KRM functions through pluggable evaluator strategies: - **Evaluator Interface**: Common contract for all function execution strategies -- **Pod Evaluator**: Executes functions in Kubernetes pods with wrapper server integration -- **Executable Evaluator**: Runs pre-cached function binaries locally for fast execution +- **Pod Evaluator**: Executes functions in Kubernetes pods with wrapper server integration; TTL, parallelism, and template overrides come from FunctionConfig +- **Executable Evaluator**: Runs local binaries listed on FunctionConfig `binaryExecutor` for fast execution - **Multi-Evaluator**: Chains evaluators with fallback logic (exec → pod) - **Request Channel Pattern**: Channel-based communication for pod cache coordination - **Wrapper Server Integration**: gRPC wrapper injected into function pods for structured execution @@ -27,11 +27,11 @@ For detailed architecture and process flows, see [Function Evaluation]({{% relre Manages function execution pods with caching and garbage collection: - **Pod Cache Manager**: Orchestrates pod lifecycle via channel-based communication - **Pod Manager**: Handles pod and service CRUD operations -- **Pod Creation**: Template-based pod creation with init container for wrapper server injection +- **Pod Creation**: Template-based pod creation from base PodTemplate / ServiceTemplate plus FunctionConfig templateOverrides - **Service Management**: ClusterIP service frontends for service mesh compatibility - **TTL-Based Caching**: Reuses pods with configurable expiration and extension on use - **Garbage Collection**: Periodic cleanup of expired pods and failed pod handling -- **Pod Warming**: Pre-creates pods for frequently-used functions +- **Pod Warming**: Pre-creates pods for FunctionConfig images that declare a podExecutor For detailed architecture and process flows, see [Pod Lifecycle Management]({{% relref "/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md" %}}). diff --git a/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/function-evaluation.md b/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/function-evaluation.md index 2a66865d5..ecb840711 100644 --- a/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/function-evaluation.md +++ b/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/function-evaluation.md @@ -49,6 +49,7 @@ All evaluators implement a common interface that defines the contract for functi **Request structure:** - **Image**: Function container image identifier +- **Tag**: Optional version constraint. When set, evaluators resolve it against cached tags. - **ResourceList**: Serialized KRM resources as YAML bytes **Response structure:** @@ -69,13 +70,14 @@ Three evaluator implementations provide different execution strategies: - Executes functions in Kubernetes pods - Uses wrapper server for gRPC interface - Manages pod cache with TTL-based expiration +- Reads per-image TTL, waitlist, parallelism, and templateOverrides from FunctionConfig - Handles service mesh compatibility via ClusterIP services **Executable Evaluator:** -- Executes pre-cached function binaries locally -- Configuration file maps images to binary paths +- Executes local function binaries inside the function-runner process +- Image-to-binary mapping comes from FunctionConfig `binaryExecutor` (path + tags) - Fast execution without pod overhead -- Returns NotFoundError for uncached functions +- Returns `NotFoundError` for images not in the binary cache **Multi-Evaluator:** - Chains multiple evaluators together @@ -160,30 +162,18 @@ Once gRPC client acquired, function execution proceeds: ## Executable Evaluator -Executes pre-cached function binaries locally for fast execution. +Executes local function binaries inside the function-runner process for a fast path that skips pod startup. -### Configuration-Based Caching +### FunctionConfig-backed cache -The executable evaluator uses a configuration file to map images to binaries: +The executable evaluator does not read a YAML config file. +An embedded FunctionConfig reconciler watches FunctionConfig objects in the function-pod namespace and fills an in-memory store. +For each `spec.binaryExecutor`, the store records the binary path (absolute, or relative to `--functions`) against the listed tags and `spec.prefixes`. -**Configuration structure:** -- YAML file with functions array -- Each function has name and images list -- Images map to binary in cache directory +When the evaluation request includes a version constraint (`Tag`), the store selects the highest cached tag that satisfies the constraint. +When `Tag` is empty, lookup uses the exact tag on the image reference. A miss returns `NotFoundError` so the multi-evaluator can fall through to the pod evaluator. -**Configuration benefits:** -- Explicit control over cached functions -- No automatic caching (predictable behavior) -- Simple file-based configuration -- Easy to update without restart - -### Function Cache Lookup - -**Lookup characteristics:** -- Simple map lookup by image name -- Fast O(1) operation -- NotFoundError triggers fallback in multi-evaluator -- No network or Kubernetes API calls +Spec changes are applied on reconcile. The function-runner does not need to restart. ### Local Execution @@ -420,8 +410,8 @@ The evaluation system employs several performance strategies. ### Cache Warming **Warming strategy:** -- Pre-create pods for frequently-used functions -- Configuration file specifies functions and TTLs +- Pre-create pods for FunctionConfig objects that declare a `podExecutor` with at least one tag +- First prefix and first tag are used to build the image name - Concurrent pod creation at startup - Reduces first-request latency @@ -456,7 +446,7 @@ The evaluation system employs several performance strategies. **Resource considerations:** - Function pods have resource limits - Limits prevent resource exhaustion -- Configurable via pod template +- Configurable via the base PodTemplate and FunctionConfig `templateOverrides` - Affects concurrent execution capacity **Performance tuning:** diff --git a/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md b/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md index 2313aa64b..e91a1ad09 100644 --- a/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md +++ b/docs/content/en/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md @@ -21,7 +21,7 @@ Key design characteristics: - **Single-threaded cache management** eliminates race conditions - **Channel-based communication** provides clean separation between components - **Service mesh compatibility** through ClusterIP services fronting each pod -- **Template-based pod creation** supports ConfigMap-based and inline specifications +- **Template-based pod creation** from a base PodTemplate / ServiceTemplate plus FunctionConfig templateOverrides - **TTL-based lifecycle** with automatic garbage collection - **Failed pod detection** with immediate deletion and cache eviction - **Pod warming** capability for pre-creating pods at startup @@ -106,25 +106,25 @@ The Pod Manager handles low-level Kubernetes operations for function pods and th **Core responsibilities:** -**Pod Lifecycle Operations** - Retrieve existing pods by label lookup, create new pods from templates, wait for pods to reach Running state with Ready condition, validate pod template versions, patch pod metadata with TTL annotations and image labels. +**Pod Lifecycle Operations** - Retrieve existing pods by label lookup, create new pods from templates, wait for pods to reach Running state with Ready condition, validate pod template versions, patch pod metadata with the image label and template-version annotation. **Service Management** - Create ClusterIP services fronting each function pod, retrieve existing services, wait for service endpoints to become active, verify pod IP matches service endpoint IP. **Image Metadata Operations** - Cache image digests and entrypoints, inspect container images to extract configuration, handle private registry authentication and TLS configuration, manage image pull secrets for function pods. -**Template System** - Load pod templates from ConfigMaps or use inline defaults, load service templates from ConfigMaps or use inline defaults, track template versions to detect changes requiring pod replacement, patch templates with function-specific configuration. +**Template System** - Load the `base-pod-template` PodTemplate and `base-service-template` ServiceTemplate (creating them from inline defaults if missing), merge FunctionConfig `templateOverrides`, and track the PodTemplate resourceVersion so pods are replaced when the base template changes. ### Pod Template System -The pod manager supports two template sources: ConfigMap-based templates for customization and inline templates as fallback defaults. +The pod manager always starts from two cluster objects in the function-pod namespace: `base-pod-template` (`corev1.PodTemplate`) and `base-service-template` (`ServiceTemplate`). +If a get returns a Kubernetes `NotFound` error, the manager creates the object from the inline default compiled into the function-runner (the same spec as `deployments/porch/22-function-templates.yaml`). -**ConfigMap-Based Templates:** +After the function image, wrapper-server command, entrypoint args, and metadata annotations are patched onto a copy of the PodTemplate, `spec.podExecutor.templateOverrides` from the matching FunctionConfig is merged. +Overrides can set `serviceAccountName`, a pod `securityContext`, and resource / env / envFrom on the init container and the function container. -When functionPodTemplateName is configured, the pod manager retrieves a ConfigMap containing template and serviceTemplate keys. The ConfigMap's ResourceVersion is used as the template version for tracking changes. When the ConfigMap is updated, existing pods with old template versions are replaced on next use. - -**Inline Templates:** - -When no ConfigMap is configured, the pod manager uses hardcoded inline templates with sensible defaults including init container for wrapper-server binary, main container with wrapper-server as entrypoint, EmptyDir volume for tools, readiness probe using grpc-health-probe, and cluster autoscaler safe-to-evict annotation. +The PodTemplate `resourceVersion` is stored on the pod as `fn.kpt.dev/template-version`. +When the live template is newer, the next reuse deletes the old pod and creates a replacement. +See [Pod Templates]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md" %}}) for editing guidance. ### Container Configuration @@ -144,17 +144,10 @@ The original entrypoint is extracted from the image metadata (either from image ### Pod Metadata Patching -Before creating a pod, the pod manager patches metadata fields for cache management and tracking: - -**Annotations:** -- fn.kpt.dev/reclaim-after - Unix timestamp when pod should be garbage collected (current time + TTL) -- fn.kpt.dev/template-version - Template version for detecting template changes -- cluster-autoscaler.kubernetes.io/safe-to-evict - "true" to allow cluster autoscaler to evict - -**Labels:** -- fn.kpt.dev/image - Pod ID for label-based lookup and service selector matching - -The reclaim-after annotation is updated each time a pod is reused, extending its lifetime. The garbage collector uses this annotation to determine when pods should be deleted. +Before creating a pod, the pod manager patches metadata fields for cache management and tracking. +It sets the `fn.kpt.dev/template-version` annotation to the PodTemplate `resourceVersion` (so a later template edit can force replacement) and the `fn.kpt.dev/image` label for lookup and service selectors. +The base template may already include `cluster-autoscaler.kubernetes.io/safe-to-evict: "true"`. +Idle-pod TTL is tracked in the function-runner process, not as a pod annotation. ## Service Management @@ -197,7 +190,8 @@ When no cached pod exists for a function image, the cache manager checks the wai ### Pod Reuse -Pod reuse provides significant performance benefits by eliminating pod startup time for subsequent evaluations. The cache manager updates the pod's TTL annotation each time it is reused, extending its lifetime. +Pod reuse provides significant performance benefits by eliminating pod startup time for subsequent evaluations. +Each reuse updates the in-memory `lastActivity` timestamp so the garbage collector keeps the pod alive. **Reuse benefits:** - Faster evaluation (no pod startup delay, typically 5-15 seconds saved) @@ -207,7 +201,8 @@ Pod reuse provides significant performance benefits by eliminating pod startup t **TTL update on reuse:** -When a cached pod is reused, the cache manager spawns a goroutine to patch the pod's reclaim-after annotation with a new timestamp (current time + TTL). This patch operation is asynchronous and doesn't block the evaluation request. +When a cached pod is reused, the cache manager records the current time as `lastActivity`. +That update is in memory and does not patch the pod. ## Pod Lifecycle Stages @@ -223,11 +218,14 @@ Pods include a readiness probe that executes grpc-health-probe to verify the wra ### Pod Warming -Pod warming pre-creates function pods at startup to eliminate cold start latency for frequently used functions. Warming is configured through a YAML file mapping function images to TTLs. +Pod warming pre-creates function pods at startup so the first evaluation of a bundled function does not pay cold-start latency. +When `--warm-up-pod-cache` is true (the default), the cache manager walks every FunctionConfig in its store. +For each object that has a `podExecutor` with at least one tag, it starts one pod using the first prefix (or the default image prefix) and the first tag. **Concurrent Creation:** -Warming creates all configured pods concurrently to minimize startup time. Each function is processed in a separate goroutine with a 1-minute timeout per pod. Using fixed names ensures only one pod is created per function even if multiple function runner instances start simultaneously. +Warming creates those pods concurrently. Each function is processed in a separate goroutine with a 1-minute timeout per pod. +Using fixed names ensures only one pod is created per function even if multiple function-runner instances start simultaneously. **Startup Optimization:** @@ -239,35 +237,34 @@ Warming is particularly valuable for high-traffic functions, latency-sensitive w ### TTL-Based Expiration -Each pod has a reclaim-after annotation containing a Unix timestamp indicating when the pod should be garbage collected. The GC compares this timestamp to the current time to determine expiration. +Each cached pod has an in-memory `lastActivity` timestamp. +The garbage collector deletes a pod when it has no waiters and `time.Since(lastActivity)` exceeds the TTL for that image. **TTL lifecycle:** -- **Pod Creation** - reclaim-after set to (current time + TTL) -- **Pod Reuse** - reclaim-after updated to (current time + TTL) -- **GC Scan** - If current time > reclaim-after, pod is deleted +- **Pod Creation / first use** - `lastActivity` set to now +- **Pod Reuse** - `lastActivity` updated to now +- **GC Scan** - idle pods whose last activity is older than TTL are deleted -This approach provides automatic cleanup of unused pods while keeping frequently used pods alive indefinitely through TTL updates on each use. +This keeps frequently used pods alive without writing TTL onto the pod object. **TTL configuration:** -- Default TTL configured at function runner startup (e.g., 10 minutes) -- Per-function TTL can be specified in cache warming config -- Dynamic updates through TTL extension on each pod reuse +- Default TTL is the function-runner `--pod-ttl` flag (default 30 minutes) +- Per-function TTL comes from FunctionConfig `spec.podExecutor.timeToLive` +- Activity is refreshed in memory on each reuse ### GC Scan Process -The garbage collector runs periodically on a configurable interval (default 1 minute). Each scan lists all function pods in the namespace and checks their TTL annotations to determine if they should be deleted. +The garbage collector runs periodically on a configurable interval (default 1 minute). +Each scan walks the in-memory pod cache and removes unhealthy pods plus idle pods that have exceeded their TTL. The GC runs synchronously in the cache manager's select loop, ensuring no concurrent modifications to the cache during garbage collection. **Scan operations:** -- List all pods with label fn.kpt.dev/image -- Check if Failed - delete immediately -- Check if being deleted - skip -- Check reclaim-after annotation -- If expired - delete pod and service -- If missing annotation - patch with new TTL -- If invalid annotation - patch with new TTL -- Evict deleted pods from cache +- For each cached function image, inspect its pods +- Delete immediately if the pod is Failed, missing, or its service is gone +- Evict if the gRPC target no longer matches the service DNS name +- If the pod is idle (empty waitlist) and `lastActivity` is older than TTL, delete the pod and service +- Drop empty cache entries ### Failed Pod Handling @@ -329,7 +326,8 @@ Pod reuse is the primary performance optimization, eliminating pod startup laten ### Cache Warming -Cache warming pre-creates pods at startup for frequently used functions, eliminating cold start latency. Warming is configured through a YAML file and creates all pods concurrently to minimize startup time. +Cache warming pre-creates pods at startup for FunctionConfig images that declare a `podExecutor`. +All such pods are created concurrently to minimize startup time. **Warming benefits:** - First evaluation served from ready pod (<100ms instead of 5-15 seconds) @@ -360,7 +358,8 @@ When multiple pods serve the same function, the cache manager selects the pod wi ### Resource Management -Function pods have resource limits configured via pod template to prevent resource exhaustion. Limits affect concurrent execution capacity and should be tuned based on function requirements and cluster resources. +Function pods have resource limits configured via the base PodTemplate and FunctionConfig `templateOverrides` to prevent resource exhaustion. +Limits affect concurrent execution capacity and should be tuned based on function requirements and cluster resources. ## Concurrency Model diff --git a/docs/content/en/docs/5_architecture_and_components/function-runner/interactions.md b/docs/content/en/docs/5_architecture_and_components/function-runner/interactions.md index eb2910e38..6020d6461 100644 --- a/docs/content/en/docs/5_architecture_and_components/function-runner/interactions.md +++ b/docs/content/en/docs/5_architecture_and_components/function-runner/interactions.md @@ -8,7 +8,15 @@ description: | ## Overview -The Function Runner is a **separate gRPC service** that interacts with multiple systems: the Task Handler (via gRPC), Kubernetes API (for pod management), container registries (for image metadata), and wrapper servers (for function execution). It operates independently from the Porch server, enabling isolated function execution. +The Function Runner is a **separate gRPC service** that interacts with multiple systems: +the Task Handler (via gRPC), Kubernetes API (for pod management, FunctionConfig objects, and pod/service templates), +container registries (for image metadata), and wrapper servers (for function execution). +It operates independently from the Porch server, enabling isolated function execution. + +An embedded FunctionConfig reconciler watches FunctionConfig objects in the function-pod namespace and fills an in-memory store. +That store is how the executable evaluator resolves binaries and how the pod evaluator applies per-image TTL, parallelism, and template overrides. +Go execution is not handled here, it runs in porch-server / porch-controllers. +See [Function Configuration]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md" %}}). ### High-Level Architecture @@ -35,6 +43,19 @@ The Function Runner is a **separate gRPC service** that interacts with multiple └─────────────────────────────────────────────────────────┘ ``` +## FunctionConfig reconciler + +The function-runner starts a controller-runtime manager whose cache is limited to `--pod-namespace` (default `porch-fn-system`). +The FunctionConfig reconciler (`ReconcilerForFunctionRunner`) upserts each object into `FunctionConfigStore`, refreshes the binary cache when `binaryExecutor` is set, and writes `status.functionRunnerObservedGeneration`. +On delete it drops the store entry and removes its finalizer. + +At evaluation time the executable evaluator looks up a binary by image name, prefix, and tag (or the best tag matching a version constraint). +A miss is `NotFoundError`, which the multi-evaluator treats as a signal to try the pod evaluator. +The pod evaluator reads `podExecutor` from the same store for TTL, waitlist length, max parallel pods, and `templateOverrides`. + +The Engine's builtin Go runtime is a different reconciler instance, running inside porch-server (and porch-controllers). +Function-runner never executes Go processors. + ## Task Handler Integration The Function Runner integrates with the Task Handler through the gRPC Runtime: @@ -111,7 +132,8 @@ Both Porch and Function Runner must agree on message size limits: ## Evaluator Execution Patterns -The Function Runner uses different execution patterns based on evaluator type: +The Function Runner uses different execution patterns based on evaluator type. +Which evaluator succeeds for a given image is determined by the FunctionConfig store (binary tags vs everything else falling through to pods). ### Pod-Based Execution @@ -140,6 +162,7 @@ Pod Pod Pod ``` **Execution pattern:** +- Pod executor settings (TTL, parallelism, templateOverrides) come from the matching FunctionConfig - Pod cache checked for existing pod (reuse if available) - Pod selection uses round-robin among pods with minimum waitlist length - Cache miss triggers pod creation with wrapper server @@ -157,7 +180,7 @@ gRPC Request ↓ Executable Evaluator ↓ - Lookup in Config + Lookup in FunctionConfig store ↓ Found? ──No──> Return NotFoundError │ @@ -169,8 +192,8 @@ gRPC Request ``` **Execution pattern:** -- Configuration file maps images to binary paths -- Fast O(1) lookup by image name +- FunctionConfig `binaryExecutor` maps images to binary paths in the in-memory store +- Lookup by image name and tag (or highest tag matching a version constraint) - Direct process execution with ResourceList input - NotFoundError triggers fallback in multi-evaluator @@ -187,24 +210,26 @@ Pod Evaluator ↓ Kubernetes Client ↓ - ┌──────┴──────┬──────────┬─────────┐ - ↓ ↓ ↓ ↓ -Pod Ops Service Ops ConfigMap Secrets - ↓ ↓ ↓ ↓ -Create/Get Create/Get Template Auth -Delete Delete Retrieval Config + ┌──────┴──────┬────────────┬───────────────┐ + ↓ ↓ ↓ ↓ +Pod Ops Service Ops Templates Secrets + ↓ ↓ ↓ ↓ +Create/Get Create/Get PodTemplate Auth +Delete Delete ServiceTemplate Config + FunctionConfig ``` **Resource operations:** - **Pods**: Create from template, get status, list by label, delete - **Services**: Create ClusterIP frontend, get endpoints, delete -- **ConfigMaps**: Retrieve pod/service templates for customization +- **PodTemplate / ServiceTemplate**: Base spec for function pods (`base-pod-template`, `base-service-template`) +- **FunctionConfig**: Per-image executor settings, TTL, and templateOverrides - **Secrets**: Access registry authentication and TLS certificates **Template system:** -- ConfigMap-based templates for organization-specific customization -- Inline templates as fallback defaults -- Template version tracking for pod replacement on changes +- Cluster `PodTemplate` and `ServiceTemplate` objects, created from inline defaults if missing +- FunctionConfig `templateOverrides` merged per image +- Template version tracking (PodTemplate resourceVersion) for pod replacement on changes **For detailed pod management, see [Pod Lifecycle Management]({{% relref "/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md" %}}).** @@ -333,6 +358,7 @@ The Function Runner follows standard integration patterns: **Function Runner responsibilities:** - gRPC service for function execution - Evaluator selection and orchestration +- FunctionConfig cache for binary and pod executor settings - Pod and service lifecycle management - Image metadata caching - Registry authentication diff --git a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/_index.md b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/_index.md index 5f2e07af1..b5bd243e2 100644 --- a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/_index.md +++ b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/_index.md @@ -19,4 +19,6 @@ Manage the lifecycle of Repositories, PackageRevisions, PackageVariants, and Pac ### [Function Runner]({{% relref "function-runner-config" %}}) Executes KRM functions in isolated containers: +- [Function Configuration]({{% relref "function-runner-config/function-configuration" %}}) - FunctionConfig CRD, executors, and reconciler +- [Pod Templates]({{% relref "function-runner-config/pod-templates" %}}) - Base PodTemplate / ServiceTemplate - [Private Registry Access]({{% relref "function-runner-config/private-registries-config" %}}) - Container registry authentication \ No newline at end of file diff --git a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/_index.md b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/_index.md index 89a7c7c0d..b14259fe0 100644 --- a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/_index.md +++ b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/_index.md @@ -10,6 +10,9 @@ KPT functions and KRM functions are synonymous terms referring to the same conta {{% /alert %}} The Function Runner executes KRM functions in a secure, isolated environment. +Binary vs pod selection and most per-function settings come from [FunctionConfig]({{% relref "function-configuration" %}}) resources, not from a static config file. +The flags below are process-wide defaults. A matching FunctionConfig overrides TTL, waitlist length, and parallelism for that image. +Go execution is declared on the same CRD but runs in porch-server and porch-controllers, not in this process. ## Configuration Options @@ -18,40 +21,44 @@ The Function Runner executes KRM functions in a secure, isolated environment. #### Generic Arguments ```bash args: -- --port=9445 # Server port (default: 9445) -- --disable-runtimes=exec,pod # Disable specific runtimes (exec, pod) -- --log-level=2 # Log verbosity level 0-5 (default: 2) +- --port=9445 # Server port (default: 9445) +- --disable-runtimes=exec,pod # Disable specific runtimes (exec, pod) +- --log-level=2 # Log verbosity level 0-5 (default: 2) +- --default-image-prefix=ghcr.io/kptdev/krm-functions-catalog # Prefix for unqualified function names ``` #### Exec Runtime Arguments ```bash args: -- --functions=./functions # Path to cached functions (default: ./functions) -- --config=./config.yaml # Path to exec runtime config file (default: ./config.yaml) +- --functions=./functions # Directory of cached function binaries (default: ./functions) ``` +Binary-to-image mappings come from FunctionConfig `binaryExecutor` entries. +`--functions` is the directory used when `spec.binaryExecutor.path` is relative. + #### Pod Runtime Arguments ```bash args: -- --pod-cache-config=/pod-cache-config/pod-cache-config.yaml # Pod cache config file path -- --warm-up-pod-cache=true # Warm up pod cache on startup (default: true) -- --pod-namespace=porch-fn-system # Namespace for KRM function pods (default: porch-fn-system) -- --pod-ttl=30m # Pod TTL before GC (default: 30m) -- --scan-interval=1m # GC scan interval (default: 1m) -- --function-pod-template= # ConfigMap with pod specification -- --max-request-body-size=6291456 # Max gRPC message size in bytes (default: 6MB) -- --max-waitlist-length # Maximum waitlist length per pod -- --max-parallel-pods-per-function # Maximum parallel pods per function +- --warm-up-pod-cache=true # Pre-create pods for FunctionConfig podExecutor images (default: true) +- --pod-namespace=porch-fn-system # Namespace for KRM function pods (default: porch-fn-system) +- --pod-ttl=30m # Default pod TTL before GC (default: 30m) +- --scan-interval=1m # GC scan interval (default: 1m) +- --max-request-body-size=6291456 # Max gRPC message size in bytes (default: 6MB) +- --max-waitlist-length=2 # Default waitlist length per pod (default: 2) +- --max-parallel-pods-per-function=1 # Default max pods per function (default: 1) +- --max-grpc-retries=2 # Retries on gRPC Unavailable (default: 2) ``` +`--pod-ttl`, `--max-waitlist-length`, and `--max-parallel-pods-per-function` are fallbacks used when the matching FunctionConfig does not set `timeToLive`, `preferredMaxQueueLength`, or `maxParallelExecutions`. + #### Private Registry Arguments ```bash args: -- --enable-private-registries=false # Enable private registry support +- --enable-private-registries=false # Enable private registry support - --registry-auth-secret-path=/var/tmp/config-secret/.dockerconfigjson # Registry auth secret path -- --registry-auth-secret-name=auth-secret # Registry auth secret name -- --enable-private-registries-tls=false # Enable TLS for private registries -- --tls-secret-path=/var/tmp/tls-secret/ # TLS secret path +- --registry-auth-secret-name=auth-secret # Registry auth secret name +- --enable-private-registries-tls=false # Enable TLS for private registries +- --tls-secret-path=/var/tmp/tls-secret/ # TLS secret path ``` ### Environment Variables @@ -62,52 +69,48 @@ env: value: "" # Required for pod runtime ``` -## Advanced Configuration +## FunctionConfig and templates -### Pod Templates +Per-function executor choice, tags, binary paths, Go ids, pod TTL, and template overrides are declared on FunctionConfig objects in `porch-fn-system`. +The function-runner runs an embedded reconciler that watches those objects and updates its in-memory store without a process restart. -Customize function evaluator pod specifications using ConfigMap templates: - -```bash -args: -- --function-pod-template=kpt-function-eval-pod-template # ConfigMap name -``` +The pod evaluator builds function pods from the `base-pod-template` `PodTemplate` and `base-service-template` `ServiceTemplate` in the pod namespace, then applies `spec.podExecutor.templateOverrides`. +There is no `--function-pod-template` flag and no ConfigMap template. -For detailed pod template configuration, see [Pod Templates]({{% relref "pod-templates" %}}) documentation. +See [Function Configuration]({{% relref "function-configuration" %}}) and [Pod Templates]({{% relref "pod-templates" %}}). ## Runtime Configuration ### Exec Runtime -The exec runtime runs functions as local executables: +The exec runtime runs functions as local binaries listed on a FunctionConfig `binaryExecutor`. +`--functions` is only the directory that relative `path` values are resolved against. ```bash args: -- --functions=./functions # Directory containing cached function executables -- --config=./config.yaml # Configuration file for exec runtime +- --functions=./functions ``` ### Pod Runtime -The pod runtime runs functions as Kubernetes pods: +The pod runtime runs functions as Kubernetes pods. +Cache warming walks FunctionConfig objects that have a `podExecutor` and pre-creates one pod for the first prefix/tag of each. ```bash args: -- --pod-namespace=porch-fn-system # Namespace for function pods -- --pod-ttl=30m # How long pods live before cleanup -- --scan-interval=1m # How often to scan for expired pods -- --warm-up-pod-cache=true # Pre-deploy common function pods +- --pod-namespace=porch-fn-system # Namespace for function pods +- --pod-ttl=30m # How long pods live before cleanup +- --scan-interval=1m # How often to scan for expired pods +- --warm-up-pod-cache=true # Pre-deploy common function pods ``` ### Disabling Runtimes -To disable specific runtimes: - ```bash args: -- --disable-runtimes=exec # Disable exec runtime only -- --disable-runtimes=pod # Disable pod runtime only -- --disable-runtimes=exec,pod # Disable both runtimes +- --disable-runtimes=exec # Disable exec runtime only +- --disable-runtimes=pod # Disable pod runtime only +- --disable-runtimes=exec,pod # Disable both runtimes ``` ## Resource Limits @@ -196,6 +199,7 @@ spec: {{% alert title="Note" color="primary" %}} For advanced configuration options: -- [Pod Templates]({{% relref "pod-templates" %}}) - Customize function pod specifications +- [Function Configuration]({{% relref "function-configuration" %}}) - Executor selection and per-function settings +- [Pod Templates]({{% relref "pod-templates" %}}) - Base PodTemplate / ServiceTemplate and templateOverrides - [Private Registries]({{% relref "private-registries-config" %}}) - Configure private registry access -{{% /alert %}} \ No newline at end of file +{{% /alert %}} diff --git a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md new file mode 100644 index 000000000..12064f16d --- /dev/null +++ b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md @@ -0,0 +1,164 @@ +--- +title: "Function Configuration" +type: docs +weight: 1 +description: "Configure KRM function execution with FunctionConfig resources" +--- + +**FunctionConfig** is the custom resource that configures how a function image is executed: a pod, a binary, a Go call, or any combination of the three. +For how these resources are created during install, see [Installing Porch]({{% relref "/docs/3_getting_started/installing-porch.md" %}}) and [Catalog Deployment]({{% relref "/docs/6_configuration_and_deployments/deployments/catalog-deployment.md" %}}). + +## How components consume FunctionConfig + +The same CRD is watched independently by three processes. +Each process runs an embedded reconciler that copies matching FunctionConfig objects into its own in-memory store and records the generation it applied on the resource status: + +The **porch-server** reconciler (`ReconcilerForServer`) feeds the builtin Go runtime used when the Engine executes functions in-process. +The **function-runner** reconciler (`ReconcilerForFunctionRunner`) feeds the executable evaluator (binary substitution) and the pod evaluator (TTL, parallelism, and template overrides). +The **porch-controllers** reconciler (`ReconcilerForController`) is started with the PackageRevision controller and feeds that controller's builtin runtime. +porch-controllers also pre-loads every FunctionConfig into the store at startup so a pod restart does not leave the cache empty until the informer catches up. + +Each reconciler adds its own finalizer (`config.porch.kpt.dev/functionconfig-porch-server`, `...-function-runner`, `...-controller`) +so a FunctionConfig is not fully deleted until every component has dropped it from its store. +The function-runner only caches FunctionConfig objects in the namespace it uses for function pods (default `porch-fn-system`). + +Status columns on `kubectl get functionconfigs` show which generation each component has applied. +When those values match `.metadata.generation` and `.status.error` is empty, the spec is live in that component. +Spec changes are picked up without restarting Porch. The reconcilers filter on generation so status-only updates do not retrigger work. + +## Matching images to a FunctionConfig + +`spec.image` is the **base name** of the function image, without a registry prefix or tag (for example `set-namespace`). +The FunctionConfig `metadata.name` should match `spec.image`. +If two FunctionConfig objects claim the same `spec.image` under different names, the second one is ignored. + +`spec.prefixes` lists registry prefixes that should match. +An empty string in that list stands for the process default prefix +(`--default-image-prefix` on porch-server and function-runner, `DEFAULT_IMAGE_PREFIX` on porch-controllers; both default to `ghcr.io/kptdev/krm-functions-catalog`). +A function image is used with a given executor only when its registry prefix matches this list **and** its tag is listed on that executor. + +When a Kptfile function specifies a version constraint (the `tag` field) rather than a concrete tag on the image, +the binary and Go executors pick the highest cached tag that satisfies the constraint. +An exact `image:tag` with an empty constraint is looked up as a literal tag. + +## Executors + +At least one of `podExecutor`, `binaryExecutor`, or `goExecutor` must be set; the API server rejects a FunctionConfig with none of the three. + +### Pod executor + +`spec.podExecutor` configures function-runner pods for the matched tags: + +- `timeToLive` (default `30m`) is how long an idle pod is kept before garbage collection. The TTL is refreshed on each reuse. +- `maxParallelExecutions` caps how many pods may run for this function (function-runner flag `--max-parallel-pods-per-function` is the fallback). +- `preferredMaxQueueLength` is the waitlist length per pod (flag `--max-waitlist-length` is the fallback). + +`templateOverrides` are merged onto the base `PodTemplate` when a pod is created. +They can set `serviceAccountName`, a pod `securityContext`, and resource / env / envFrom overrides on the init container and the function container. +The base templates themselves are documented in [Pod Templates]({{% relref "pod-templates" %}}). + +If `--warm-up-pod-cache` is true (the default), the function-runner pre-creates one pod per FunctionConfig that has a `podExecutor` with at least one tag, using the first prefix and first tag. + +### Binary executor + +`spec.binaryExecutor` tells the function-runner executable evaluator to run a local binary instead of a pod for the listed tags: + +- `path` is either an absolute path or a path relative to the `--functions` directory (default `./functions`). +- The binary is invoked with the ResourceList on stdin; stdout is the transformed ResourceList. + +If the image is not in the binary cache, the executable evaluator returns `NotFoundError` and the multi-evaluator falls through to the pod evaluator. + +### Go executor + +`spec.goExecutor` tells porch-server and porch-controllers to run the function as an in-process Go `ResourceListProcessor` for the listed tags: + +- `id` is the key used in the builtin cache. If omitted, the FunctionConfig name is used. +- Only three processors are compiled into Porch today: `apply-replacements`, `set-namespace`, and `starlark`. +- A `goExecutor` on any other FunctionConfig is stored but has no processor to bind to. + +The Engine tries the builtin runtime first and falls back to the function-runner over gRPC when the image is not in the Go cache. + +## Example + +The default install includes a FunctionConfig that uses all three executors for `set-namespace`: + +```yaml +apiVersion: config.porch.kpt.dev/v1alpha1 +kind: FunctionConfig +metadata: + name: set-namespace + namespace: porch-fn-system +spec: + image: set-namespace + prefixes: + - "" + - ghcr.io/kptdev/krm-functions-catalog + podExecutor: + tags: + - v0.4.1 + timeToLive: 30m + binaryExecutor: + tags: + - v0.4.2 + path: set-namespace + goExecutor: + id: set-namespace + tags: + - v0.4 + - v0.4.5 +``` + +With this spec, a pipeline step that asks for `set-namespace:v0.4.5` (or a constraint such as `v0.4` that selects `v0.4.5`) runs in-process. +`set-namespace:v0.4.2` runs as a binary in the function-runner. `set-namespace:v0.4.1` runs in a pod with a 30-minute TTL. + +### Per-function pod resources + +Use `templateOverrides` when a particular function needs more memory or a different service account than the base template: + +```yaml +apiVersion: config.porch.kpt.dev/v1alpha1 +kind: FunctionConfig +metadata: + name: gatekeeper + namespace: porch-fn-system +spec: + image: gatekeeper + prefixes: + - "" + - ghcr.io/kptdev/krm-functions-catalog + podExecutor: + tags: + - v0.2.1 + timeToLive: 30m + maxParallelExecutions: 3 + preferredMaxQueueLength: 2 + templateOverrides: + container: + resources: + requests: + memory: "512Mi" + cpu: "500m" + limits: + memory: "1Gi" + cpu: "1000m" +``` + +## Status + +```yaml +status: + apiServerObservedGeneration: 1 + functionRunnerObservedGeneration: 1 + controllerObservedGeneration: 1 + error: "" +``` + +`apiServerObservedGeneration`, `functionRunnerObservedGeneration`, and `controllerObservedGeneration` are the `.metadata.generation` each component last applied. +`error` is set when that component failed to apply the spec. It is cleared on the next successful reconcile. + +## RBAC + +The default Porch roles already grant the required verbs. +porch-server (aggregated-apiserver ClusterRole) and the function-runner (`porch-function-executor` Role in `porch-fn-system`) +can get, list, watch, and patch FunctionConfig objects and update `functionconfigs/status`. +The function-runner also has get/list/watch/create/update/patch on `podtemplates` and `servicetemplates` so it can read and, if missing, create the base templates. diff --git a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md index 5937b4733..bf0529a67 100644 --- a/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md +++ b/docs/content/en/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md @@ -2,357 +2,179 @@ title: "Pod Templates" type: docs weight: 2 -description: "Customize function evaluator pod specifications using ConfigMap templates" +description: "Customize function evaluator pods with PodTemplate, ServiceTemplate, and FunctionConfig overrides" --- -The Function Runner supports customizing the pod specifications used for KRM function evaluation through ConfigMap-based templates. This allows you to configure resource limits, security contexts, node selectors, tolerations, and other pod-level settings for function execution pods. +Function evaluator pods are built from the **PodTemplate**/**ServiceTemplate** objects `base-pod-template` and `base-service-template` in the function-pod namespace (default `porch-fn-system`), plus per-function overrides from the matching [FunctionConfig]({{% relref "function-configuration" %}}). +There is no `--function-pod-template` flag and no ConfigMap template. -## Overview +For how those templates are used during pod creation, see [Pod Lifecycle Management]({{% relref "/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md" %}}). -By default, the Function Runner uses an inline pod template with sensible defaults. For advanced use cases requiring customization, you can provide a ConfigMap containing custom pod and service templates. The Function Runner will use these templates when creating function evaluator pods. +## How templates are applied -The pod template system provides: -- **Resource customization** - Configure CPU/memory limits for function pods -- **Security hardening** - Apply security contexts and pod security standards -- **Scheduling control** - Add node selectors, affinity rules, and tolerations -- **Network policies** - Customize service specifications for service mesh integration -- **Volume management** - Add additional volumes and volume mounts +On pod creation the function-runner: -For architectural details on how pod templates are used in the pod lifecycle, see [Pod Lifecycle Management]({{% relref "/docs/5_architecture_and_components/function-runner/functionality/pod-lifecycle-management.md" %}}). +1. Gets `base-pod-template` (`corev1.PodTemplate`) and `base-service-template` (`config.porch.kpt.dev/v1alpha1` ServiceTemplate) from the function-pod namespace (`--pod-namespace`, default `porch-fn-system`). If either is missing, it creates it from the inline default shipped in the binary. +2. Patches the function container with the requested image, the wrapper-server command, the original image entrypoint as arguments, and any image-pull secret required for private registries. +3. Patches pod metadata: `fn.kpt.dev/image` label and `fn.kpt.dev/template-version` set to the PodTemplate `resourceVersion`. +4. Merges `spec.podExecutor.templateOverrides` from the FunctionConfig for that image, if any. +5. Creates the pod and a ClusterIP Service from the ServiceTemplate. -## Template Contract +Cluster-wide defaults (node selectors, extra volumes, security context that every function should inherit) belong on the base PodTemplate. Per-function CPU/memory, env, or service account belong on `templateOverrides`. -Any custom pod template must fulfill the following requirements: +## Template contract -1. **Function container** - Must contain a container named `function` -2. **Wrapper server entrypoint** - The `function` container's entrypoint must start the wrapper gRPC server -3. **Image replacement** - The `function` container's image can be set to any KRM function image without breaking the wrapper server entrypoint -4. **Entrypoint arguments** - The `function` container's args can be appended with entries from the function image's Dockerfile ENTRYPOINT +Any custom `base-pod-template` must keep a container named `function`. +That container's command must start the wrapper gRPC server. The function-runner replaces the image and appends the original function entrypoint to `args`. +An init container named `copy-wrapper-server` is expected as the first init container when `templateOverrides.initContainer` is used, because overrides are merged by index. -The Function Runner automatically patches the template with function-specific configuration before creating pods. +The Function Runner patches the template before creating pods. Leave the function image as a placeholder. It is always replaced. -## Enabling Pod Templates +## Default templates -### Step 1: Configure RBAC - -The Function Runner requires read access to the pod template ConfigMap. Create a Role and RoleBinding in the Function Runner's namespace: +The default install deploys these objects (trimmed from `deployments/porch/22-function-templates.yaml`): ```yaml -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role +apiVersion: v1 +kind: PodTemplate metadata: - name: porch-fn-runner-configmap-reader - namespace: porch-system -rules: - - apiGroups: [""] - resources: ["configmaps"] - resourceNames: ["kpt-function-eval-pod-template"] - verbs: ["get"] + name: base-pod-template + namespace: porch-fn-system +template: + metadata: + annotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + spec: + initContainers: + - name: copy-wrapper-server + image: ghcr.io/kptdev/porch-wrapper-server:latest + command: + - cp + - -a + - /home/nonroot/wrapper-server/. + - /wrapper-server-tools + volumeMounts: + - name: wrapper-server-tools + mountPath: /wrapper-server-tools + containers: + - name: function + image: image-replaced-by-kpt-func-image + command: + - /wrapper-server-tools/wrapper-server + env: + - name: OTEL_METRICS_EXPORTER + value: prometheus + - name: OTEL_TRACES_EXPORTER + value: none + - name: OTEL_EXPORTER_PROMETHEUS_HOST + value: 0.0.0.0 + readinessProbe: + exec: + command: ["/wrapper-server-tools/grpc-health-probe", "-addr", "localhost:9446"] + volumeMounts: + - name: wrapper-server-tools + mountPath: /wrapper-server-tools + volumes: + - name: wrapper-server-tools + emptyDir: {} --- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: porch-fn-runner-configmap-reader - namespace: porch-system -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: porch-fn-runner-configmap-reader -subjects: - - kind: ServiceAccount - name: porch-fn-runner - namespace: porch-system -``` - -### Step 2: Configure Function Runner - -Add the `--function-pod-template` argument to the Function Runner deployment, specifying the ConfigMap name: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: function-runner - namespace: porch-system -spec: - template: - spec: - serviceAccountName: porch-fn-runner - containers: - - name: function-runner - image: ghcr.io/kptdev/porch-function-runner:latest - args: - - --port=9445 - - --pod-namespace=porch-fn-system - - --function-pod-template=kpt-function-eval-pod-template - env: - - name: WRAPPER_SERVER_IMAGE - value: ghcr.io/kptdev/porch-wrapper-server:latest -``` - -### Step 3: Create the ConfigMap - -Create a ConfigMap in the same namespace where the Function Runner is deployed (typically `porch-system`). The ConfigMap must contain two keys: - -- `template` - Pod specification in YAML format -- `serviceTemplate` - Service specification in YAML format - -```yaml -apiVersion: v1 -kind: ConfigMap +apiVersion: config.porch.kpt.dev/v1alpha1 +kind: ServiceTemplate metadata: - name: kpt-function-eval-pod-template - namespace: porch-system -data: - template: | - apiVersion: v1 - kind: Pod - metadata: - annotations: - cluster-autoscaler.kubernetes.io/safe-to-evict: "true" - spec: - initContainers: - - name: copy-wrapper-server - image: ghcr.io/kptdev/porch-wrapper-server:latest - command: - - cp - - -a - - /home/nonroot/wrapper-server/. - - /wrapper-server-tools - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: to-be-replaced - command: - - /wrapper-server-tools/wrapper-server - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} - serviceTemplate: | - apiVersion: v1 - kind: Service - spec: - type: ClusterIP - ports: + name: base-service-template + namespace: porch-fn-system +template: + spec: + ports: - port: 9446 protocol: TCP targetPort: 9446 - selector: - fn.kpt.dev/image: to-be-replaced + name: server + selector: + fn.kpt.dev/image: to-be-replaced + type: ClusterIP ``` -## Template Customization Examples +If you delete them, the function-runner recreates them from its inline defaults the next time it needs a pod. +Edits you make to the live objects are used for subsequent pod creates. -### Resource Limits +## Customizing the base PodTemplate -Add resource requests and limits to the function container: +Edit `base-pod-template` in `porch-fn-system` to change every function pod. +Typical additions are resource requests and limits on the `function` container, a pod `securityContext`, `nodeSelector` / `tolerations`, and extra volumes. ```yaml -data: - template: | - apiVersion: v1 - kind: Pod - spec: - initContainers: - - name: copy-wrapper-server - image: ghcr.io/kptdev/porch-wrapper-server:latest - command: [cp, -a, /home/nonroot/wrapper-server/., /wrapper-server-tools] - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: to-be-replaced - command: [/wrapper-server-tools/wrapper-server] - resources: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} +kubectl edit podtemplate base-pod-template -n porch-fn-system ``` -### Security Context - -Apply security contexts for enhanced security: +Example resource limits on the function container: ```yaml -data: - template: | - apiVersion: v1 - kind: Pod - spec: - securityContext: - runAsNonRoot: true - runAsUser: 65532 - fsGroup: 65532 - seccompProfile: - type: RuntimeDefault - initContainers: - - name: copy-wrapper-server - image: ghcr.io/kptdev/porch-wrapper-server:latest - command: [cp, -a, /home/nonroot/wrapper-server/., /wrapper-server-tools] - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: to-be-replaced - command: [/wrapper-server-tools/wrapper-server] - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "512Mi" + cpu: "500m" ``` -### Node Scheduling - -Add node selectors and tolerations: +Example pod security context: ```yaml -data: - template: | - apiVersion: v1 - kind: Pod - spec: - nodeSelector: - workload-type: functions - tolerations: - - key: "functions" - operator: "Equal" - value: "true" - effect: "NoSchedule" - initContainers: - - name: copy-wrapper-server - image: ghcr.io/kptdev/porch-wrapper-server:latest - command: [cp, -a, /home/nonroot/wrapper-server/., /wrapper-server-tools] - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: to-be-replaced - command: [/wrapper-server-tools/wrapper-server] - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} + securityContext: + runAsNonRoot: true + runAsUser: 65532 + fsGroup: 65532 + seccompProfile: + type: RuntimeDefault ``` -## Template Versioning - -The Function Runner tracks the ConfigMap's `ResourceVersion` to detect template changes. When the ConfigMap is updated: - -1. The Function Runner detects the new version on the next pod creation -2. Existing pods with the old template version continue running -3. When an old pod is reused, the Function Runner detects the version mismatch -4. The old pod is deleted and a new pod is created with the updated template - -This ensures zero-downtime template updates while maintaining cache efficiency. - -## Default Template +## Per-function overrides -When no ConfigMap is specified, the Function Runner uses this inline default template: +FunctionConfig `spec.podExecutor.templateOverrides` is merged after the base template is patched. +Supported fields are `serviceAccountName`, pod `securityContext`, and `resources` / `env` / `envFrom` on the init container and the function container. +Scheduling fields such as `nodeSelector` are not part of `templateOverrides`; put those on the base PodTemplate. ```yaml -apiVersion: v1 -kind: Pod +apiVersion: config.porch.kpt.dev/v1alpha1 +kind: FunctionConfig metadata: - annotations: - cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + name: gatekeeper + namespace: porch-fn-system spec: - initContainers: - - name: copy-wrapper-server - image: ${WRAPPER_SERVER_IMAGE} - command: [cp, -a, /home/nonroot/wrapper-server/., /wrapper-server-tools] - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: to-be-replaced - command: [/wrapper-server-tools/wrapper-server] - env: - - name: OTEL_METRICS_EXPORTER - value: prometheus - - name: OTEL_TRACES_EXPORTER - value: none - - name: OTEL_EXPORTER_PROMETHEUS_HOST - value: 0.0.0.0 - readinessProbe: - exec: - command: - - /wrapper-server-tools/grpc-health-probe - - -addr - - localhost:9446 - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} + image: gatekeeper + podExecutor: + tags: + - v0.2.1 + templateOverrides: + serviceAccountName: function-sa + container: + resources: + limits: + memory: "1Gi" + cpu: "1000m" ``` -## Troubleshooting +## Template versioning -### Template Validation Errors +The Function Runner records the PodTemplate `resourceVersion` on each pod as `fn.kpt.dev/template-version`. +On the next reuse, a mismatch against the current template causes the old pod to be deleted and a new one created. +Existing pods keep serving until they are reused or garbage-collected, so template edits do not immediately disrupt in-flight evaluations. -If the Function Runner fails to parse the template: +## RBAC -```bash -kubectl logs -n porch-system deployment/function-runner | grep "unable to decode" -``` +The default `porch-function-executor` Role in `porch-fn-system` already allows get/list/watch/create/update/patch on `podtemplates` and `servicetemplates`. +No extra Role is required for the base templates. -Common issues: -- Invalid YAML syntax in the ConfigMap -- Missing required `function` container -- Incorrect indentation - -### RBAC Permission Errors - -If the Function Runner cannot read the ConfigMap: - -```bash -kubectl logs -n porch-system deployment/function-runner | grep "Could not get Configmap" -``` - -Verify the Role and RoleBinding are correctly configured and the ServiceAccount name matches. - -### Pod Creation Failures +## Troubleshooting -If function pods fail to start with custom templates: +If pods fail to start after a template edit: ```bash kubectl get pods -n porch-fn-system kubectl describe pod -n porch-fn-system +kubectl logs -n porch-system deployment/function-runner ``` -Check for: -- Resource quota violations -- Image pull errors -- Security policy violations -- Node selector mismatches +Common causes are invalid YAML on the PodTemplate, a missing `function` container, resource-quota or image-pull failures, and security-policy or node-selector mismatches. If the function-runner logs that it cannot get `base-pod-template` or `base-service-template`, check that the RoleBinding for `porch-function-executor` is present in `porch-fn-system`. diff --git a/docs/content/en/docs/6_configuration_and_deployments/configurations/opentelemetry.md b/docs/content/en/docs/6_configuration_and_deployments/configurations/opentelemetry.md index 5e6a32b7d..9863b8772 100644 --- a/docs/content/en/docs/6_configuration_and_deployments/configurations/opentelemetry.md +++ b/docs/content/en/docs/6_configuration_and_deployments/configurations/opentelemetry.md @@ -385,78 +385,68 @@ spec: ### Wrapper Server Configuration via Pod Templating -The wrapper-server component can be configured with OpenTelemetry settings through the pod templating mechanism used by the function runner. This is done by creating a ConfigMap with a pod template that includes the necessary environment variables. +The wrapper-server can pick up OpenTelemetry settings from the function-runner's base **PodTemplate**. +Edit `base-pod-template` in `porch-fn-system` (see [Pod Templates]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/pod-templates.md" %}})) so the `function` container env includes the exporters you want. +New function pods pick up the change on the next create or template-version replacement. Existing pods are not rewritten in place. -#### ConfigMap Pod Template with OpenTelemetry Configuration +#### PodTemplate with OpenTelemetry Configuration ```yaml apiVersion: v1 -kind: ConfigMap +kind: PodTemplate metadata: - name: kpt-function-eval-pod-template - namespace: porch-system -data: - template: | - apiVersion: v1 - kind: Pod - metadata: - annotations: - cluster-autoscaler.kubernetes.io/safe-to-evict: "true" - prometheus.io/scrape: "true" - prometheus.io/port: "9464" - prometheus.io/path: "/metrics" - spec: - initContainers: - - name: copy-wrapper-server - image: ghcr.io/kptdev/porch-wrapper-server:latest - command: - - cp - - -a - - /home/nonroot/wrapper-server/. - - /wrapper-server-tools - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - containers: - - name: function - image: image-replaced-by-kpt-func-image - command: - - /wrapper-server-tools/wrapper-server - env: - - name: OTEL_METRICS_EXPORTER - value: "prometheus" - - name: OTEL_EXPORTER_PROMETHEUS_HOST - value: "0.0.0.0" - - name: OTEL_EXPORTER_PROMETHEUS_PORT - value: "9464" - - name: OTEL_TRACES_EXPORTER - value: "otlp" - - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: "http://otel-collector.observability:4318" - - name: OTEL_EXPORTER_OTLP_PROTOCOL - value: "http/protobuf" - ports: - - name: metrics - containerPort: 9464 - protocol: TCP - volumeMounts: - - name: wrapper-server-tools - mountPath: /wrapper-server-tools - volumes: - - name: wrapper-server-tools - emptyDir: {} + name: base-pod-template + namespace: porch-fn-system +template: + metadata: + annotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + prometheus.io/scrape: "true" + prometheus.io/port: "9464" + prometheus.io/path: "/metrics" + spec: + initContainers: + - name: copy-wrapper-server + image: ghcr.io/kptdev/porch-wrapper-server:latest + command: + - cp + - -a + - /home/nonroot/wrapper-server/. + - /wrapper-server-tools + volumeMounts: + - name: wrapper-server-tools + mountPath: /wrapper-server-tools + containers: + - name: function + image: image-replaced-by-kpt-func-image + command: + - /wrapper-server-tools/wrapper-server + env: + - name: OTEL_METRICS_EXPORTER + value: "prometheus" + - name: OTEL_EXPORTER_PROMETHEUS_HOST + value: "0.0.0.0" + - name: OTEL_EXPORTER_PROMETHEUS_PORT + value: "9464" + - name: OTEL_TRACES_EXPORTER + value: "otlp" + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: "http://otel-collector.observability:4318" + - name: OTEL_EXPORTER_OTLP_PROTOCOL + value: "http/protobuf" + ports: + - name: metrics + containerPort: 9464 + protocol: TCP + volumeMounts: + - name: wrapper-server-tools + mountPath: /wrapper-server-tools + volumes: + - name: wrapper-server-tools + emptyDir: {} ``` -The function runner must be configured to use this template by specifying the `--function-pod-template` argument: - -```yaml -command: - - /server - - --config=/config.yaml - - --functions=/functions - - --pod-namespace=porch-fn-system - - --function-pod-template=kpt-function-eval-pod-template -``` +No extra function-runner flag is required. The runner always reads `base-pod-template` from `--pod-namespace`. ## Context Propagation diff --git a/docs/content/en/docs/6_configuration_and_deployments/deployments/catalog-deployment.md b/docs/content/en/docs/6_configuration_and_deployments/deployments/catalog-deployment.md index 002ea1ffa..13fdae085 100644 --- a/docs/content/en/docs/6_configuration_and_deployments/deployments/catalog-deployment.md +++ b/docs/content/en/docs/6_configuration_and_deployments/deployments/catalog-deployment.md @@ -87,6 +87,10 @@ kpt live init porch kpt live apply porch ``` +The catalog package includes FunctionConfig resources for common KRM functions (apply-replacements, set-namespace, starlark, and others) +and the base `PodTemplate` / `ServiceTemplate` used for pod-based execution. +See [Function Configuration]({{% relref "/docs/6_configuration_and_deployments/configurations/components/function-runner-config/function-configuration.md" %}}). + ## Verification ### Check Pod Status @@ -114,6 +118,34 @@ Confirm Porch CRDs are registered: kubectl api-resources | grep porch ``` +### Check FunctionConfig resources + +```bash +kubectl get functionconfigs -n porch-fn-system +``` + +You should see one FunctionConfig per bundled catalog function. +The printer columns report which generation porch-server, function-runner, and porch-controllers have applied: + +``` +NAME SERVER APPLIED FNRUNNER APPLIED CONTROLLER APPLIED +apply-replacements 1 1 1 +apply-setters 1 1 1 +create-setters 1 1 1 +set-namespace 1 1 1 +starlark 1 1 1 +... +``` + +### Check ServiceTemplate and PodTemplate resources + +```bash +kubectl get servicetemplates,podtemplates -n porch-fn-system +``` + +The default install provides `base-service-template` and `base-pod-template`. +The function-runner uses these as the starting spec for every function pod, then merges per-function `templateOverrides` from the matching FunctionConfig. + ## Troubleshooting @@ -130,6 +162,24 @@ kubectl logs -n porch-system -l app=porch-server kubectl get crd | grep porch ``` +**FunctionConfig resources not applied:** + +Confirm the objects exist in `porch-fn-system` and inspect their status: + +```bash +kubectl get functionconfigs -n porch-fn-system +kubectl get functionconfigs -n porch-fn-system -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status}{"\n"}{end}' +``` + +Each of porch-server, function-runner, and porch-controllers runs its own FunctionConfig reconciler. +Search the component logs if a generation column stays at `0` or `.status.error` is set: + +```bash +kubectl logs -n porch-system -l app=function-runner | grep -i functionconfig +kubectl logs -n porch-system -l app=porch-server | grep -i functionconfig +kubectl logs -n porch-system -l k8s-app=porch-controllers | grep -i functionconfig +``` + ### Getting Help For additional support: diff --git a/func/internal/executableevaluator_test.go b/func/internal/executableevaluator_test.go index 3647d5f20..1697f3cb8 100644 --- a/func/internal/executableevaluator_test.go +++ b/func/internal/executableevaluator_test.go @@ -124,7 +124,7 @@ func TestEvaluateExecutableFunction(t *testing.T) { req := &pb.EvaluateFunctionRequest{ ResourceList: []byte("req-rl"), - // This image is not included in the config.yaml -> function not found + // This image is not in the FunctionConfig store -> function not found Image: imageutil.Join(defaultKRMImagePrefix, testImageName), Tag: "> 0.1.3 < 0.2.0", // This is a valid semver constraint syntax } @@ -195,7 +195,7 @@ kind: ResourceList items: [] ` - // This constraint matches both v0.1.2 and v0.1.3 from config.yaml + // This constraint matches both v0.1.2 and v0.1.3 from the FunctionConfig binaryExecutor tags // We expect v0.1.3 to be selected as it's the greatest version req := &pb.EvaluateFunctionRequest{ ResourceList: []byte(resourceList), diff --git a/func/internal/podcachemanager.go b/func/internal/podcachemanager.go index 1feb54205..33795cd7c 100644 --- a/func/internal/podcachemanager.go +++ b/func/internal/podcachemanager.go @@ -357,7 +357,7 @@ func (pcm *podCacheManager) retrieveFunctionPods(ctx context.Context) error { return nil } -// warmupCache starts preloading 1 pod in the background for each function specified in podCacheConfig +// warmupCache starts preloading 1 pod in the background for each FunctionConfig that has a podExecutor func (pcm *podCacheManager) warmupCache(defaultImagePrefix string) error { start := time.Now() defer func() { diff --git a/func/internal/podevaluator.go b/func/internal/podevaluator.go index 7771430ba..3d37d8ca9 100644 --- a/func/internal/podevaluator.go +++ b/func/internal/podevaluator.go @@ -63,7 +63,7 @@ type PodEvaluatorOptions struct { WrapperServerImage string // Container image name of the wrapper server GcScanInterval time.Duration // Time interval between Garbage Collector scans PodTTL time.Duration // Time-to-live for pods before GC - WarmUpPodCacheOnStartup bool // If true, pod-cache-config image pods will be deployed at startup + WarmUpPodCacheOnStartup bool // If true, pre-create pods for FunctionConfig podExecutor images at startup EnablePrivateRegistries bool // If true enables the use of private registries and their authentication RegistryAuthSecretPath string // The path of the secret used for authenticating to custom registries RegistryAuthSecretName string // The name of the secret used for authenticating to custom registries @@ -181,7 +181,7 @@ func NewPodEvaluator(ctx context.Context, o PodEvaluatorOptions, cl client.Clien } if o.WarmUpPodCacheOnStartup { - // TODO(mengqiy): add watcher that support reloading the cache when the config file was changed. + // TODO: re-warm pods when FunctionConfig objects change after startup. err = pe.podCacheManager.warmupCache(o.DefaultImagePrefix) // If we can't warm up the cache, we can still proceed without it. if err != nil { diff --git a/func/internal/podevaluator_podmanager_test.go b/func/internal/podevaluator_podmanager_test.go index 92dfd3897..29d2daae4 100644 --- a/func/internal/podevaluator_podmanager_test.go +++ b/func/internal/podevaluator_podmanager_test.go @@ -45,17 +45,16 @@ import ( ) const ( - defaultImageName = "apply-replacements" - defaultPodName = "apply-replacements-latest-1-5245a527" - defaultNamespace = "porch-fn-system" - defaultServiceName = defaultPodName - defaultEndpointName = defaultServiceName - defaultFunctionImageLabel = defaultPodName - defaultWrapperServerImage = "wrapper-server" - defaultPodIP = "10.10.10.10" - defaultServiceIP = "20.10.10.10" - defaultFunctionPodTemplateName = "function-pod-template" - defaultRegistryAuthSecret = "authsecret" + defaultImageName = "apply-replacements" + defaultPodName = "apply-replacements-latest-1-5245a527" + defaultNamespace = "porch-fn-system" + defaultServiceName = defaultPodName + defaultEndpointName = defaultServiceName + defaultFunctionImageLabel = defaultPodName + defaultWrapperServerImage = "wrapper-server" + defaultPodIP = "10.10.10.10" + defaultServiceIP = "20.10.10.10" + defaultRegistryAuthSecret = "authsecret" ) type fakeFunctionEvalServer struct { diff --git a/func/server/server.go b/func/server/server.go index fd9da7fb1..fff300c27 100644 --- a/func/server/server.go +++ b/func/server/server.go @@ -83,7 +83,7 @@ func main() { // flags for the exec runtime flag.StringVar(&o.exec.FunctionCacheDir, "functions", "./functions", "Path to cached functions.") // flags for the pod runtime - flag.BoolVar(&o.pod.WarmUpPodCacheOnStartup, "warm-up-pod-cache", true, "if true, pod-cache-config image pods will be deployed at startup") + flag.BoolVar(&o.pod.WarmUpPodCacheOnStartup, "warm-up-pod-cache", true, "if true, pre-create pods for FunctionConfig podExecutor images at startup") flag.StringVar(&o.pod.PodNamespace, "pod-namespace", "porch-fn-system", "Namespace to run KRM functions pods.") flag.DurationVar(&o.pod.PodTTL, "pod-ttl", 30*time.Minute, "TTL for pods before GC.") flag.DurationVar(&o.pod.GcScanInterval, "scan-interval", time.Minute, "The interval of GC between scans.")