
Google Cloud Dataflow is a managed stream and batch processing service built on Apache Beam. It offers autoscaling, event-time processing, windowing, stateful computations, and exactly-once guarantees, but it ties deployments to Google Cloud services and ongoing managed service costs.
Apache Flink is an open-source distributed processing engine for stateful computations over bounded and unbounded data streams, and it covers the same ground as a self-hosted deployment without ongoing managed service costs. It supports stateful processing, checkpointing, savepoints, event-time windowing, and Apache Beam pipelines through the Flink Runner, while the Flink Kubernetes Operator deploys and manages Flink clusters using native Kubernetes resources.
This article explains how to deploy Apache Flink as an alternative to Google Cloud Dataflow. It covers Session and Application Cluster deployments using the Flink Kubernetes Operator, state management, checkpointing, exactly-once processing with Kafka, Apache Beam integration, monitoring, high availability, security, and migration strategies from Google Cloud Dataflow.
Apache Flink provides many of the stream and batch processing capabilities available in Google Cloud Dataflow through a self-managed Kubernetes deployment. The following table compares Dataflow components and features with their closest Apache Flink equivalents:
Key components of Apache Flink include:
Before you begin, you need to:
kubectl and configure access to the cluster.StorageClass for persistent Kafka storage.The Apache Flink Kubernetes Operator extends Kubernetes with custom resources that simplify the deployment and lifecycle management of Apache Flink clusters and applications. It automates operations such as upgrades, scaling, savepoint management, and recovery while allowing you to manage Flink deployments using native Kubernetes resources.
The Flink Kubernetes Operator uses admission webhooks that require TLS certificates. Install cert-manager before deploying the operator.
Verify the Kubernetes cluster connection.
Verify that all worker nodes are in the Ready state.
Add the Jetstack Helm repository.
Update the Helm repository cache.
Install cert-manager, create the cert-manager namespace, and install the required Custom Resource Definitions.
Verify that all cert-manager pods are running.
Output:
Add the Apache Flink Helm repository for version 1.15.0. This article uses the Flink Kubernetes Operator version 1.15.0. Visit the Flink Kubernetes Operator downloads page to confirm the latest release, and replace the version in the repository URL if a newer version is available.
Update the Helm repository cache.
Create a dedicated namespace for the Flink Kubernetes Operator.
Install version 1.15.0 of the Flink Kubernetes Operator in the flink-operator namespace.
Verify that the operator deployment is available.
Output:
Verify that the operator pod is running.
Output:
A Flink Session Cluster runs a long-lived JobManager and TaskManager deployment that can accept multiple Flink jobs. This mode is useful when you want to submit and manage several jobs without creating a separate cluster for each workload.
Create a working directory for the Flink manifests.
Switch to the working directory.
Create a namespace for Flink workloads.
Create a service account for Flink.
Create a role binding that allows the Flink service account to manage resources in the flink namespace.
Create a session-cluster.yaml manifest.
Add the following configuration to the file.
The local checkpoint and savepoint directories validate the Session Cluster configuration but do not preserve state after pod replacement. The Configure State Management section replaces these paths with durable Object Storage.
The configuration:
flink service account to manage resources in the flink namespace.Save and close the file.
Apply the Flink Session Cluster manifest.
Verify that the Flink deployment is created.
Verify that the flink-session-cluster resource displays a STABLE lifecycle state.
Verify that the Flink Session Cluster pods are running.
Verify the Flink services.
The output displays the services created for the Flink Session Cluster, including the REST service used to access the Flink Web UI.
A FlinkSessionJob submits an application to an existing Flink Session Cluster without creating a separate Flink cluster for each workload. The application is packaged as a Java Archive (JAR) file and managed as a Kubernetes custom resource, making deployments and updates consistent with standard Kubernetes workflows.
Create a directory for the sample Flink application.
Switch to the project directory.
Create the Maven project configuration file.
Add the following configuration to the file.
The configuration:
Save and close the file.
Create a sample streaming application.
Add the following application.
The application:
Save and close the file.
Build the application.
Verify that Maven creates the application JAR.
Verify that the output includes the flink-session-job-1.0.0.jar file.
The sample application demonstrates how to package a Flink job into a deployable JAR. The following steps submit an official Apache Flink example application hosted on Maven Central. Using a publicly accessible JAR provides a reproducible deployment without requiring additional artifact storage or container image customization. Later sections configure durable storage for Flink applications and state.
Switch to the Flink manifest directory.
Create the session job manifest.
Add the following configuration:
The configuration:
Save and close the file.
Apply the session job manifest.
Verify that the session job is created.
Output:
Verify that the Session Cluster runs the submitted job.
Set up port forwarding to access the Flink dashboard.
Open http://localhost:8081 in your web browser to access the Apache Flink dashboard.
The Flink dashboard displays running jobs, TaskManagers, checkpoints, and cluster resources. Verify that the submitted job appears in the Running Jobs section with a RUNNING status. Select the job to view execution details and task metrics.
A Flink Application Cluster creates a dedicated cluster for a single application. It deploys the application with a JobManager and TaskManagers. Each application uses dedicated resources and has an independent lifecycle.
Create a Kubernetes Secret using your Object Storage credentials. Replace YOUR_ACCESS_KEY with your Object Storage access key and YOUR_SECRET_KEY with your Object Storage secret key.
Verify that the Secret exists.
Output:
Create the application cluster manifest.
Add the following configuration. Replace YOUR_BUCKET_NAME with your Object Storage bucket name and YOUR_OBJECT_STORAGE_HOSTNAME with your Object Storage hostname.
The configuration:
Save and close the file.
Apply the manifest.
Verify that the FlinkDeployment reaches the STABLE lifecycle state.
Output:
Verify that the JobManager and TaskManager pods are running.
Output:
Verify the services created for the Application Cluster.
The flink-application-cluster-rest service exposes the Flink REST API and Web UI.
Apache Flink uses a state backend to manage application state during processing. The HashMap state backend keeps state in JVM memory and works well for smaller workloads. RocksDB stores working state on TaskManager disks and supports incremental checkpoints for larger workloads. The Application Cluster uses RocksDB with Object Storage to preserve checkpoints and savepoints outside the Kubernetes cluster.
Verify the state backend, checkpoint directory, and incremental checkpointing configured for the Application Cluster.
Output:
Verify that the application creates checkpoints successfully.
Output:
The output confirms that Flink creates checkpoints successfully. The checkpoint directory verified earlier stores the completed checkpoints in Object Storage.
Trigger a savepoint for the running application.
Monitor the state snapshots.
Wait until the latest snapshot reaches the COMPLETED state. Press Ctrl+C to stop monitoring.
Store the latest state snapshot name in a variable.
Verify the snapshot state.
Output:
View the savepoint location.
Output:
Apache Flink uses event timestamps and watermarks to process records that arrive out of order. Windowing groups records by time, while allowed lateness and side outputs handle delayed records.
Switch to the sample application directory.
Create an event-time windowing application.
Add the following application.
The application:
Save and close the file.
Build the application.
Verify that Maven creates the application JAR.
Output:
Apache Flink combines checkpoints with transactional sinks to provide end-to-end exactly-once processing. A Kafka transactional sink writes records inside a transaction and commits it only after the corresponding checkpoint completes, so a job that fails and restarts from the last checkpoint never emits duplicate output to downstream consumers.
End-to-end exactly-once processing requires a message broker with transactional support. Deploy a three-node Apache Kafka cluster with the Strimzi operator to provide the transactional source and sink for the Flink application.
Create a namespace for Kafka resources.
Add the Strimzi Helm repository.
Update the Helm repository information.
Install the Strimzi Kafka Operator.
Verify that the Strimzi operator is running.
Output:
Create a Kafka cluster manifest.
Add the following configuration.
The node pool runs three Kafka nodes with persistent storage. Each node acts as a broker and KRaft controller. The replication and in-sync replica settings allow the cluster to continue processing transactions when one broker is unavailable. Strimzi requires Kafka and KafkaNodePool resources for KRaft-based clusters.
Save and close the file.
Deploy the Kafka cluster.
Wait for the Kafka cluster to become ready.
Verify the Kafka pods.
Output:
Verify the internal Kafka bootstrap service.
Create the input topic for incoming events and the output topic for window results. Each topic uses three partitions and three replicas, and requires at least two in-sync replicas to acknowledge writes, so it tolerates one unavailable broker.
Create the Kafka topics manifest.
Add the following configuration.
Save and close the file.
Create both topics.
Wait for both topics to become ready.
Verify both topics.
Output:
Configure the Flink Session Cluster to create exactly-once checkpoints every 60 seconds.
Wait for the Session Cluster to become stable.
Verify the checkpointing mode and interval.
Output:
Switch to the sample application directory.
Open the Maven project configuration.
Append the following dependencies to the existing <dependencies> block.
The connector base dependency provides the DeliveryGuarantee class. The Kafka connector provides the transactional sink and supports Apache Flink 1.20.
Append the following plugin to the existing <plugins> block.
The Maven Shade plugin packages the Kafka connector and its required libraries in the application JAR. The Flink runtime and connector base dependencies remain provided by the Flink cluster.
Save and close the file.
Open the event-time windowing application.
Append the following imports to the existing import statements.
Locate the following class declaration:
Append the following constant immediately after the class declaration:
Locate the hardcoded event source that begins with:
Remove the baseTime declaration and the complete env.fromElements() block. Replace them with the following Kafka source configuration.
The Kafka source reads comma-separated event records from the flink-events topic. Flink stores the consumed Kafka offsets in checkpoints so the application can resume consistently after a restart or failure.
Locate the following existing watermark block:
Replace it with:
Locate the following constructor inside the Event class:
Append the following method after the constructor:
Locate the following declaration:
Append the following Kafka sink configuration after the declaration.
The bootstrap address connects the Flink application to the internal Kafka service. The transactional ID prefix must be unique for each running Flink application that writes to the Kafka cluster.
The transaction timeout is set to 15 minutes. This gives Flink enough time to recover and complete a checkpoint before Kafka expires an open transaction.
Locate the following statement:
Append the following sink after the statement and before env.execute().
The sink writes tumbling-window results to Kafka transactions. Flink commits each transaction only after the corresponding checkpoint completes. Consumers using the read_committed isolation level do not receive records from aborted transactions.
Save and close the file.
The application:
flink-window-results topic.read_committed isolation level to ignore uncommitted and aborted records.Build the application.
Verify that Maven compiled the application for Java 11.
The output shows major version: 55, which corresponds to Java 11.
Verify that the JAR bundles the Kafka connector classes.
The output lists the KafkaSink and KafkaSource classes, which confirms the connector is shaded into the JAR.
The Maven build stores the updated JAR on the local computer. Upload and run the JAR on the Flink Session Cluster.
Verify that the Flink REST service exists.
Forward local port 8081 to the Flink REST service.
Store the port-forward process ID.
Wait for the Flink REST endpoint.
Upload the application JAR.
Display the upload response.
Output:
Extract the uploaded JAR identifier.
Verify the JAR identifier.
Output:
Submit the event-time application.
Display the submission response.
Output:
Extract the Flink job ID.
Verify the job ID.
Output:
Check the application state.
Output:
A RUNNING state confirms that Flink submitted the application successfully and started its processing tasks.
Stop port forwarding.
Stopping the local port-forward does not stop the Flink job. It only closes the local connection to the Flink REST service.
Apache Beam separates pipeline code from the processing engine that runs it. The Flink Runner translates a Beam pipeline into a Flink job and submits it to an existing Flink cluster.
Switch to the main project directory.
Create a Maven project for the Beam pipeline.
Switch to the Beam project directory.
Create the Maven project configuration.
Add the following configuration.
The configuration:
Save and close the file.
Create the Beam application.
Add the following application.
The pipeline:
Save and close the file.
Build the application JAR.
The Maven Shade plugin may display warnings about overlapping manifests, licenses, classes, and metadata. The build is successful when Maven returns:
Verify that Maven created the application JAR.
Output:
Verify that Maven compiled the application for Java 11.
Output:
Verify that the application JAR contains the Flink Runner.
Output:
Verify that the Flink Session Cluster REST service exists.
Forward local port 8081 to the Flink REST service.
Store the port-forward process ID.
Wait for the Flink REST endpoint.
Create a unique identifier for the submission.
Create a unique Beam job name.
Verify the generated job name.
Output:
Submit the pipeline through the Flink Runner.
Do not stop the command with Ctrl+C. The Beam client translates the pipeline, submits it to Flink, and waits for the bounded job to finish.
Extract the Flink job ID from the submission log.
Verify the job ID.
Output:
Verify the name of the submitted job.
Check the job state.
Output:
A FINISHED state confirms that the Flink Runner translated and executed the bounded Beam pipeline successfully.
Stop port forwarding.
Prometheus collects metrics from Flink components and stores them as time-series data. Grafana uses these metrics to display dashboards, while Alertmanager processes alerts generated by Prometheus.
Create a namespace for the monitoring resources.
Add the Prometheus Community Helm repository.
Update the Helm repository information.
Install the kube-prometheus-stack Helm chart.
Do not stop the installation with Ctrl+C. The chart installs multiple workloads and Custom Resource Definitions and may take several minutes to complete.
This article pins version 72.0.0. If you change the version, confirm the Grafana sidecar loads dashboards, because some newer chart releases ship a sidecar image that fails TLS verification against the Kubernetes API server and silently loads no dashboards. Check the chart releases for the latest version.
The kube-prometheus-stack chart installs Prometheus Operator, Prometheus, Alertmanager, Grafana, Kubernetes metrics exporters, dashboards, and alerting rules.
Verify the Helm release.
Verify the monitoring pods.
Output:
Verify that the Prometheus Operator Custom Resource Definitions exist.
Output:
The Flink Prometheus reporter exposes JobManager and TaskManager metrics in a format that Prometheus can collect. The Flink image used by the Session Cluster already includes the Prometheus metrics plugin.
Verify that the Prometheus reporter JAR exists.
Output:
Configure the Session Cluster to expose Prometheus metrics on port 9249.
The configuration:
Flink loads metric reporters through the metrics.reporter.<name>.factory.class setting. The Prometheus reporter exposes a pull-based endpoint and uses port 9249 by default.
Wait for the FlinkDeployment to become stable.
Verify that the Session Cluster pods are running.
Output:
Verify the reporter configuration.
Output:
Verify that the Flink pods expose the metrics port.
Output:
Test the Prometheus endpoint on each Flink pod.
A ServiceMonitor-based configuration requires a Kubernetes Service to discover the Flink metrics endpoints. The ServiceMonitor then instructs the Prometheus Operator to scrape the named metrics port exposed by that Service.
Create a metrics Service manifest.
Add the following configuration.
The Service:
Save and close the file.
Create the metrics Service.
Verify the Service.
Output:
Verify that the Service discovered the Flink pods.
Output:
Create a ServiceMonitor manifest.
Add the following configuration.
The ServiceMonitor:
flink namespace.monitoring: flink label.release: monitoring label so Prometheus selects the resource.Save and close the file.
Create the ServiceMonitor.
Verify the ServiceMonitor.
Output:
Verify that its selector matches the metrics Service.
Output:
Wait for Prometheus to discover the new targets.
Forward local port 9090 to the Prometheus Service.
Store the port-forward process ID.
Wait for the Prometheus API.
Query the Flink scrape targets.
A value of "1" confirms that the target is reachable and healthy.
Verify that Prometheus stores Flink metrics.
A value greater than zero confirms that Prometheus is collecting Flink metrics.
Stop port forwarding.
Grafana visualizes the Flink metrics stored in Prometheus. The monitoring stack includes a preconfigured Prometheus data source, so Grafana can query the metrics without requiring an additional connection.
Retrieve the Grafana administrator password.
Display the password.
Copy the displayed password. Use it to sign in to Grafana.
Forward local port 3000 to the Grafana service.
Store the port-forward process ID.
Wait until Grafana becomes available.
Verify the Grafana health status.
Output:
Open http://localhost:3000 in a web browser. The Grafana login screen appears.
Sign in using the following credentials:
adminecho "$GRAFANA_PASSWORD" command.
Verify that the Prometheus data source is available.
Retrieve the actual Prometheus data source UID.
Query the Flink targets using the Prometheus data source UID.
Create a Grafana dashboard for Flink availability, failed checkpoints, job restarts, and backpressure. Store it in a labeled Kubernetes ConfigMap so Grafana loads it automatically.
Create the dashboard manifest.
Add the following configuration.
The dashboard:
Save and close the file.
Create the dashboard ConfigMap.
Verify the ConfigMap.
Output:
Verify that the dashboard label exists.
Output:
Wait for the Grafana dashboard sidecar to load the dashboard.
Verify that Grafana registered the dashboard.
Open the following URL in a web browser.
The Apache Flink Monitoring dashboard appears with panels for target availability, failed checkpoints, job restarts, and backpressure.
Create Prometheus alerting rules for failed checkpoints, job restarts, and sustained backpressure. Prometheus evaluates these rules and sends active alerts to Alertmanager.
Create the alert rules manifest.
Add the following configuration.
The alert rules:
release: monitoring label so Prometheus selects the resource. The backPressuredTimeMsPerSecond metric reports how many milliseconds during each second a task spent under backpressure. A value above 500 means the task remained backpressured for more than half of the measured period.
Save and close the file.
Create the PrometheusRule.
Verify the alert rules resource.
Output:
Verify the rule group and alert names.
Output:
Forward local port 9090 to Prometheus.
Store the port-forward process ID.
Wait until Prometheus becomes available.
Verify that Prometheus loaded the Flink rules.
Output:
Stop the Prometheus port-forward process.
Stop the Grafana port-forward process.
Flink requires an S3 filesystem plugin to access Object Storage paths. The Flink image includes the S3 plugin in the /opt/flink/opt directory, but Flink only loads plugins from /opt/flink/plugins.
Verify that the Object Storage credentials secret exists.
Output:
Verify that the Flink image includes the S3 filesystem plugin.
Output:
Configure an init container that copies the existing Flink plugins and the S3 filesystem plugin into a shared volume.
The configuration:
Wait for the Session Cluster to become stable.
Verify that the S3 filesystem plugin is available in the active plugin directory.
Output:
Verify that the Prometheus plugin remains available.
Output:
Verify that the Object Storage credentials are loaded without displaying their values.
Output:
Kubernetes high availability uses Kubernetes ConfigMaps for leader election. The configuration runs two JobManager replicas and stores recovery metadata in Object Storage so a standby JobManager can restore the cluster state after the active JobManager fails.
Configure high availability on the Session Cluster. Replace YOUR_BUCKET_NAME with your Object Storage bucket name and YOUR_OBJECT_STORAGE_HOSTNAME with your Object Storage hostname without the https:// prefix.
The configuration:
YOUR_BUCKET_NAME Object Storage bucket.Wait for the Session Cluster to become stable.
Verify the high-availability configuration.
Output:
Verify the JobManager deployment status.
Output:
Verify the JobManager pods.
Output:
Verify the ConfigMaps used for high-availability coordination.
Output:
Delete one JobManager pod to verify that Kubernetes restores the replica count and that the Flink REST service becomes available after the pod is replaced.
Store the name of one JobManager pod.
Display the stored pod name.
Output:
Delete the selected JobManager pod.
Wait for the JobManager Deployment to restore both replicas.
Verify the JobManager pods.
Output:
The newer pod replaces the deleted JobManager replica.
Verify that the FlinkDeployment remains stable.
Output:
Forward local port 8081 to the Flink REST service.
Keep this terminal open. Open a second terminal and query the cluster overview.
A successful response confirms that the Session Cluster REST endpoint is available after Kubernetes replaces the deleted JobManager pod.
Store checkpoints and savepoints in Object Storage so Flink can recover job state after a pod failure.
Configure Object Storage as the checkpoint and savepoint location. Replace YOUR_BUCKET_NAME with your Object Storage bucket name.
The configuration:
Wait for the Session Cluster to become stable.
Verify the checkpoint recovery configuration.
Output:
Verify that the local checkpoint and savepoint settings were removed.
The command returns two empty lines.
Forward local port 8081 to the Flink REST service.
Keep this terminal open. Open a second terminal and submit a long-running example job.
Display the submission result.
The output includes the submitted job ID.
Store the job ID.
Verify the stored job ID.
Wait for Flink to complete at least one checkpoint.
Query the latest completed checkpoint.
Output:
Cancel the example job after verification.
Return to the first terminal and press Ctrl+C to stop port forwarding.
The Flink JobManager uses the flink service account to manage TaskManager pods and high-availability ConfigMaps. Create namespace-scoped permissions so the service account can manage these resources only in the flink namespace.
Verify that the Flink service account exists.
Output:
Create an RBAC manifest.
Add the following configuration.
The configuration:
flink namespace.flink service account.Save and close the file.
Create the Role and RoleBinding.
Verify that the RoleBinding references the flink-runtime Role.
Output:
Verify that the service account can create TaskManager pods in the flink namespace.
Output:
Verify that the same permission does not apply in the default namespace.
Output:
Store Object Storage credentials in a Kubernetes Secret instead of adding them directly to the FlinkDeployment. The Session Cluster loads the Secret values as environment variables without displaying the credential values in the deployment configuration.
Verify that the Secret contains the required keys without displaying their values.
Output:
Verify that the Session Cluster references the Object Storage Secret.
Output:
Apply a Kubernetes NetworkPolicy to control inbound and outbound traffic for the Session Cluster. The policy allows Flink component communication, Prometheus metrics collection, Kafka access, DNS queries, and outbound HTTPS connections.
Verify that the Kubernetes NetworkPolicy API is available.
Output:
The NetworkPolicy API may be available even when the cluster network plugin does not enforce NetworkPolicy resources. Verify that your cluster uses a compatible network plugin before relying on the policy for traffic isolation.
Create a NetworkPolicy manifest.
Add the following configuration.
The policy:
flink namespace.monitoring namespace to collect Prometheus metrics on port 9249.kafka namespace on port 9092.Save and close the file.
Create the NetworkPolicy.
Verify the NetworkPolicy.
Output:
Wait for the Session Cluster to remain stable.
Verify that the JobManager and TaskManager pods remain ready.
Output:
Verify the Session Cluster resources and confirm that the configured processing capacity is available.
Verify that the Session Cluster has reached the stable state.
Output:
The empty third line confirms that the FlinkDeployment does not report an error.
Verify the JobManager and TaskManager pods.
Output:
Verify the Flink services.
Output:
Verify the configured TaskManager replicas and task slots.
Output:
The Session Cluster runs two TaskManager replicas with two task slots each, providing four task slots in total.
Create a temporary Kafka client pod and produce timestamped events to the flink-events topic.
Create a temporary Kafka client pod.
Wait for the pod to become ready.
Create a timestamp aligned with the current minute.
Produce test events to the flink-events topic.
The producer uses test as the Kafka record key. Each record value contains the event type, numeric value, and event-time timestamp. The final record advances event time so Flink can complete the earlier window.
Wait for the one-minute window and the next checkpoint to complete.
Read committed results from the output topic.
Confirm that the consumer displays the aggregated checkout and payment events. The read_committed isolation level hides incomplete and aborted Kafka transactions.
Delete a TaskManager pod to simulate a failure, and verify that Kubernetes replaces the pod and Flink returns the streaming job to the RUNNING state.
Store the name of a running TaskManager pod.
Delete the TaskManager pod to simulate a failure.
Wait for two TaskManager pods to reach the Running state.
Verify that the replacement TaskManager pod is running.
Forward local port 8081 to the Flink REST service.
Store the port-forward process ID.
Wait for the REST endpoint to become available.
Retrieve the running Flink job ID.
Verify the job ID.
Verify that the Flink job returned to the RUNNING state.
Stop port forwarding.
Delete the temporary Kafka client pod.
Migrating from GCP Dataflow to Apache Flink involves changing the pipeline runner, replacing Google Cloud-specific services, and adapting deployment, state, scaling, and monitoring workflows to Kubernetes.
Apache Beam pipelines can run on Flink when they use Beam-portable transforms and connectors.
DataflowRunner with FlinkRunner in the pipeline options.Dataflow state cells and timers must be reviewed before moving stateful processing to Flink.
ValueState, ListState, MapState, or ReducingState.keyBy() before accessing keyed state and confirm that the selected key produces the same state partitioning used by the Dataflow pipeline.Apache Beam window definitions are portable, but runner-specific timing behavior may differ.
Dataflow Templates package reusable pipelines for repeatable execution.
FlinkSessionJob resources, the Flink REST API, or dedicated FlinkDeployment application clusters.Dataflow workloads commonly depend on Google Cloud storage and messaging services.
You have successfully deployed Apache Flink on Kubernetes Engine as a self-hosted alternative to GCP Dataflow. The deployment includes Session and Application Clusters, durable state management with Object Storage, event-time windowing, exactly-once Kafka processing, Apache Beam pipelines, Prometheus and Grafana monitoring, high availability, security controls, and migration guidance for existing Dataflow workloads. For more information, refer to the official Apache Flink documentation and the official Apache Flink Kubernetes Operator documentation.
0 Comments
Be the first to comment and share your perspective with the community.