Skip to main content

Application Deployment

Flyte provides infrastructure for deploying and managing long-running services and serverless applications using Knative. This system allows applications to scale to zero when idle and automatically manages their lifecycle within a Kubernetes cluster.

This tutorial guides you through configuring the deployment system, deploying a containerized application, managing its lifecycle, and streaming logs.

Prerequisites

To use Flyte's application deployment infrastructure, you need:

  • A Kubernetes cluster with Knative Serving installed.
  • The flyte namespace created in your cluster (managed by AppK8sClient.Deploy).
  • A controller-runtime client and cache initialized for your cluster.

1. Configure the Application Client

The AppK8sClient is the primary interface for managing Knative Services (KServices). It requires an InternalAppConfig to define how public URLs are generated and what default environment variables should be injected.

import (
"time"
"github.com/flyteorg/flyte/v2/app/config"
"github.com/flyteorg/flyte/v2/app/internal/k8s"
)

cfg := &config.InternalAppConfig{
Enabled: true,
BaseDomain: "apps.flyte.example.com",
Scheme: "https",
DefaultRequestTimeout: 5 * time.Minute,
MaxRequestTimeout: 1 * time.Hour,
NamespacedNameSuffixTemplate: "{{ project }}-{{ domain }}",
DefaultEnvVars: map[string]string{
"FLYTE_INTERNAL_API": "http://flyteadmin.flyte.svc.cluster.local",
},
}

// Initialize the client (requires controller-runtime client and cache)
appClient := k8s.NewAppK8sClient(k8sClient, ctrlCache, cfg)

The NamespacedNameSuffixTemplate is used to generate the INTERNAL_APP_ENDPOINT_PATTERN environment variable, which allows deployed apps to discover and communicate with each other internally using the pattern http://{app_fqdn}-project-domain.flyte.svc.cluster.local.

2. Define and Deploy an Application

Applications are defined using the flyteapp.App message. Currently, Flyte supports Container payloads; K8sPod payloads are not yet supported in the buildPodSpec implementation.

import (
"context"
flyteapp "github.com/flyteorg/flyte/v2/gen/go/flyteidl2/app"
)

app := &flyteapp.App{
Metadata: &flyteapp.Metadata{
Id: &flyteapp.Identifier{
Project: "my-project",
Domain: "development",
Name: "my-service",
},
},
Spec: &flyteapp.Spec{
AppPayload: &flyteapp.Spec_Container{
Container: &flyteapp.Container{
Image: "ghcr.io/myapp:latest",
Ports: []*flyteapp.Port{{ContainerPort: 8080}},
Env: []*flyteapp.KeyValuePair{
{Key: "LOG_LEVEL", Value: "debug"},
},
},
},
},
}

err := appClient.Deploy(context.Background(), app)

When you call Deploy, Flyte performs the following:

  1. Naming: Generates a unique KService name using KServiceName. If the name my-service-my-project-development exceeds 63 characters, it appends a deterministic SHA256 suffix to ensure uniqueness.
  2. Idempotency: Computes a SHA256 hash of the spec and stores it in the flyte.org/spec-sha annotation. If the spec hasn't changed, the update is skipped.
  3. Namespace: Ensures the flyte namespace exists.
  4. KService Creation: Builds and applies a servingv1.Service manifest to the cluster.

3. Manage the Application Lifecycle

Flyte allows you to stop an application without deleting its configuration. This is implemented as a "scale-to-zero" operation.

id := &flyteapp.Identifier{
Project: "my-project",
Domain: "development",
Name: "my-service",
}

// Scale to zero and make cluster-local
err := appClient.Stop(context.Background(), id)

The Stop operation is more aggressive than a standard Knative scale-down:

  • It applies a MergePatch to set autoscaling.knative.dev/min-scale and initial-scale to 0.
  • It sets networking.knative.dev/visibility to cluster-local to remove it from the public gateway.
  • It explicitly deletes the LatestReadyRevision to force immediate termination of running pods, bypassing the standard Knative stable window.

To completely remove the application, use Delete:

err := appClient.Delete(context.Background(), id)

4. Stream Application Logs

The K8sAppLogStreamer provides a way to stream logs from application replicas. It is designed to filter out Knative infrastructure sidecars like queue-proxy so you only see logs from your application container.

import (
"github.com/flyteorg/flyte/v2/app/internal/service"
)

logStreamer, _ := service.NewK8sAppLogStreamer(k8sRestConfig)

replicaID := &flyteapp.ReplicaIdentifier{
Name: "my-service-my-project-development-00001-deployment-5f7b",
}

err := logStreamer.TailLogs(ctx, replicaID, func(lines *flyteapp.LogLines) error {
for _, line := range lines.StructuredLines {
fmt.Println(line.Message)
}
return nil
})

The TailLogs function uses pickUserContainer to identify the correct container by iterating through the pod's containers and excluding any named queue-proxy. It also detaches the log stream from the inbound gRPC deadline to ensure that long-lived log follows are not interrupted by client timeouts.

Next Steps

  • Monitoring: Use appClient.Subscribe(appName) to receive a channel of WatchResponse events for real-time status updates.
  • Discovery: Use appClient.PublicIngress(id) to retrieve the deterministic public URL for an application based on your BaseDomain configuration.