OpenMCF logoOpenMCF

Loading...

Kubernetes Service

Deploys a standalone Kubernetes Service for service discovery, load balancing, and external access to workloads running in a Kubernetes cluster. Supports all four service types (ClusterIP, NodePort, LoadBalancer, ExternalName), headless mode, session affinity, external traffic policies, and cloud-provider-specific annotations for fine-grained load balancer control.

What Gets Created

When you deploy a KubernetesService resource, OpenMCF provisions:

  • Service — a Kubernetes Service in the specified namespace with the configured type, port mappings, pod selector, and optional headless mode (clusterIP: None)
  • Labels — standard OpenMCF governance labels (managed-by, resource, resource-kind) merged with any user-provided labels
  • Annotations — user-provided annotations passed through to the Service resource, enabling cloud-provider-specific load balancer configuration (e.g., AWS NLB, GCP NEG, Azure internal LB)

Prerequisites

  • Kubernetes credentials configured via environment variables or OpenMCF provider config
  • An existing namespace in the target cluster where the service will be created
  • Target pods with labels matching the selector field (not required for ExternalName or selectorless services)
  • Cloud provider load balancer support if using loadBalancer type — the cluster must have a cloud controller manager or load balancer controller installed

Quick Start

Create a file service.yaml:

apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesService
metadata:
  name: my-service
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: my-org
    pulumi.openmcf.org/project: my-project
    pulumi.openmcf.org/stack.name: dev.KubernetesService.my-service
spec:
  namespace: my-namespace
  name: my-service
  selector:
    app: my-app
  ports:
    - name: http
      port: 80
      targetPort: "8080"

Deploy:

openmcf apply -f service.yaml

This creates a ClusterIP Service named my-service in the my-namespace namespace, routing TCP traffic on port 80 to port 8080 on pods matching label app: my-app.

Configuration Reference

Required Fields

FieldTypeDescriptionValidation
spec.namespacestringKubernetes namespace where the service will be created. Must already exist.1–63 characters, valid DNS label (lowercase alphanumeric and hyphens, no leading/trailing hyphens)
spec.namestringName of the Kubernetes Service resource. Used as metadata.name on the created Service.1–63 characters, valid DNS label
spec.portsKubernetesServicePort[]Ports exposed by the service. At least one port is required for all service types except ExternalName.Non-empty for non-ExternalName types
spec.ports[].portint32The port number exposed by the service. This is the port clients use to access the service.1–65535

Optional Fields

FieldTypeDefaultDescription
spec.targetCluster.clusterKindenum—Kubernetes cluster kind. Valid values: AwsEksCluster, GcpGkeCluster, AzureAksCluster, DigitalOceanKubernetesCluster, CivoKubernetesCluster.
spec.targetCluster.clusterNamestring—Name of the target Kubernetes cluster in the same environment.
spec.labelsmap<string, string>{}Additional labels merged with standard OpenMCF labels for governance and discoverability.
spec.annotationsmap<string, string>{}Annotations applied to the Service resource. Used for cloud-provider-specific load balancer configuration.
spec.typeenumcluster_ipService type. Valid values: cluster_ip, node_port, load_balancer, external_name.
spec.selectormap<string, string>{}Label selector for pods this service routes traffic to. Not required for ExternalName services or services with manually managed endpoints.
spec.headlessboolfalseWhen true, creates a headless service (clusterIP: None). DNS queries return pod IPs directly. Cannot be combined with node_port or load_balancer types.
spec.externalDnsNamestring—External DNS name for ExternalName-type services (e.g., my-database.example.com). Required when type is external_name.
spec.externalTrafficPolicyenumclusterControls how external traffic is routed. cluster distributes across all nodes (may add a hop). local routes only to node-local endpoints (preserves source IP). Only applicable for node_port and load_balancer types.
spec.sessionAffinityenumnoneSession affinity configuration. none routes each request to any backend. client_ip routes requests from the same client IP to the same pod.
spec.loadBalancerSourceRangesstring[][]CIDR ranges restricting load balancer access (e.g., 203.0.113.0/24). Only applicable for load_balancer type. Empty means accessible from any source.
spec.ports[].namestring—Optional port name. Must be unique within the port list. Required when the service exposes more than one port. Max 63 characters.
spec.ports[].protocolenumTCPIP protocol for this port. Valid values: TCP, UDP, SCTP.
spec.ports[].targetPortstringSame as portTarget port on the pod. Can be a numeric port (e.g., "8080") or a named port (e.g., "http") defined in the container's port list.
spec.ports[].nodePortint32Auto-allocatedPort on each node for NodePort and LoadBalancer services. Must be in range 30000–32767 when specified. Only applicable for node_port and load_balancer types.

Examples

Basic ClusterIP Service

A simple internal service routing HTTP traffic to backend pods:

apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesService
metadata:
  name: backend-api
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: my-org
    pulumi.openmcf.org/project: my-project
    pulumi.openmcf.org/stack.name: dev.KubernetesService.backend-api
spec:
  namespace: backend
  name: backend-api
  selector:
    app: backend-api
    tier: api
  ports:
    - name: http
      port: 80
      targetPort: "8080"
    - name: grpc
      port: 9090
      targetPort: "9090"

LoadBalancer with Source IP Restrictions

An externally accessible service using a cloud load balancer with traffic restricted to specific CIDR ranges and client source IP preservation:

apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesService
metadata:
  name: public-gateway
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: my-org
    pulumi.openmcf.org/project: my-project
    pulumi.openmcf.org/stack.name: staging.KubernetesService.public-gateway
spec:
  namespace: ingress
  name: public-gateway
  type: load_balancer
  selector:
    app: gateway
  ports:
    - name: https
      port: 443
      targetPort: "8443"
    - name: http
      port: 80
      targetPort: "8080"
  externalTrafficPolicy: local
  sessionAffinity: client_ip
  loadBalancerSourceRanges:
    - "10.0.0.0/8"
    - "203.0.113.0/24"
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"

Full-Featured Multi-Protocol Service with NodePort

A production service exposing multiple protocols on explicit node ports, with session affinity and custom labels:

apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesService
metadata:
  name: media-server
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: my-org
    pulumi.openmcf.org/project: my-project
    pulumi.openmcf.org/stack.name: prod.KubernetesService.media-server
spec:
  targetCluster:
    clusterKind: AwsEksCluster
    clusterName: prod-cluster
  namespace: media
  name: media-server
  type: node_port
  selector:
    app: media-server
    version: v3
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: "8080"
      nodePort: 30080
    - name: rtsp
      protocol: TCP
      port: 554
      targetPort: "8554"
      nodePort: 30554
    - name: stream
      protocol: UDP
      port: 5004
      targetPort: "5004"
      nodePort: 30504
  externalTrafficPolicy: local
  sessionAffinity: client_ip
  labels:
    team: media-platform
    cost-center: streaming

ExternalName Service

A CNAME alias pointing to an external database endpoint, with no pod selector or ports:

apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesService
metadata:
  name: external-db
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: my-org
    pulumi.openmcf.org/project: my-project
    pulumi.openmcf.org/stack.name: dev.KubernetesService.external-db
spec:
  namespace: backend
  name: external-db
  type: external_name
  externalDnsName: prod-db.c9kl3xq2.us-east-1.rds.amazonaws.com

Headless Service for StatefulSet

A headless service for direct pod addressing, commonly used with StatefulSets:

apiVersion: kubernetes.openmcf.org/v1
kind: KubernetesService
metadata:
  name: cassandra
  labels:
    openmcf.org/provisioner: pulumi
    pulumi.openmcf.org/organization: my-org
    pulumi.openmcf.org/project: my-project
    pulumi.openmcf.org/stack.name: prod.KubernetesService.cassandra
spec:
  namespace: data
  name: cassandra
  headless: true
  selector:
    app: cassandra
  ports:
    - name: cql
      port: 9042
      targetPort: "9042"
    - name: inter-node
      port: 7000
      targetPort: "7000"

Stack Outputs

After deployment, the following outputs are available in status.outputs:

OutputTypeDescription
serviceNamestringName of the created Kubernetes Service
namespacestringNamespace in which the service was created
typestringType of the created service (ClusterIP, NodePort, LoadBalancer, ExternalName)
clusterIpstringCluster-internal IP address assigned to the service. Empty for headless and ExternalName services.
loadBalancerIngressstringExternal IP or hostname assigned by the cloud load balancer. Only populated for LoadBalancer-type services after provisioning.
internalDnsNamestringFully qualified internal DNS name (e.g., my-service.my-namespace.svc.cluster.local)

Related Components

  • KubernetesDeployment — deploys the workload pods that this service routes traffic to
  • KubernetesNamespace — provides the target namespace for the service
  • KubernetesStatefulSet — commonly used with headless services for stable pod identity and direct addressing

Next article

Kubernetes SigNoz

Kubernetes SigNoz Deploys the SigNoz observability platform on Kubernetes using the official SigNoz Helm chart, providing unified logs, metrics, and traces through an OpenTelemetry-native stack with configurable SigNoz UI, OpenTelemetry Collector, self-managed or external ClickHouse database, optional Kubernetes Gateway API ingress for both the UI and OTel Collector endpoints, and custom Helm value overrides. What Gets Created When you deploy a KubernetesSignoz resource, OpenMCF provisions:...
Read next article
Presets
3 ready-to-deploy configurationsView presets →