Kubernetes Services: Exposing Your Applications

In the dynamic world of containerized applications, ensuring stable network access to your workloads is paramount. Kubernetes Services are the cornerstone of this stability, providing a consistent way to expose applications running within your cluster, even as the underlying Pods fluctuate. A Kubernetes Service acts as an abstraction layer, decoupling application access from the ephemeral nature of Pods. This document delves into the concept of Services in Kubernetes, exploring their purpose, types, and how they empower robust and scalable application deployments.

At its core, a Kubernetes Service provides a stable endpoint for accessing applications deployed as one or more Pods within your Kubernetes cluster. This abstraction is crucial because Pods, the fundamental units of deployment in Kubernetes, are designed to be ephemeral. They can be created, destroyed, and rescheduled dynamically by Kubernetes to maintain the desired application state. This inherent dynamism means relying directly on Pod IPs for application access is unreliable.

Imagine deploying your application using a Deployment. Deployments ensure a specified number of Pod replicas are running and healthy. However, these Pods are constantly being managed – scaled up, scaled down, updated, or replaced due to failures. Each Pod has its own IP address, assigned by Kubernetes’ networking plugins. This IP address is not persistent and can change when a Pod is recreated. Therefore, client applications, especially those residing within the cluster (frontends), face the challenge of consistently locating and connecting to backend Pods. They need a mechanism to discover and track the IP addresses of these backend Pods, even as they change.

This is where Kubernetes Services step in, offering a solution to this service discovery problem. They provide a stable, abstract IP address and DNS name that clients can use to access the application, regardless of the underlying Pod changes.

Understanding Services in Kubernetes

The Kubernetes Service API is a powerful abstraction designed to expose groups of Pods as a network service. Each Service object defines a logical set of endpoints, typically Pods, and specifies policies for accessing these Pods. This decoupling is essential for building resilient and scalable applications.

Consider a stateless image processing backend scaled to three replicas. These replicas are interchangeable; the frontend application doesn’t need to target a specific backend instance. The set of Pods comprising the backend might change over time due to scaling or updates, but the frontend clients shouldn’t be affected by these changes. They need a consistent way to access the backend functionality without managing the fluctuating Pod IPs.

Kubernetes Services facilitate this decoupling. They abstract away the complexities of Pod management and provide a stable access point for client applications. Typically, the set of Pods targeted by a Service is determined by a selector that you define. This selector matches labels on Pods, automatically including them as endpoints for the Service. For scenarios requiring more control over endpoints, Kubernetes also supports Services without selectors.

For applications using HTTP, Ingress offers a more advanced way to manage external access. While not a Service type itself, Ingress acts as a smart reverse proxy and load balancer, providing features like SSL termination, path-based routing, and name-based virtual hosting. It consolidates routing rules for multiple services, allowing you to expose various application components behind a single entry point.

The Gateway API extends Kubernetes networking capabilities beyond Ingress and Services. Implemented using CustomResourceDefinitions, Gateway provides richer features for managing and securing access to services within your cluster.

Cloud-Native Service Discovery

For applications designed to leverage Kubernetes APIs directly, service discovery becomes even more integrated. Applications can query the API server to discover and monitor EndpointSlices associated with a Service. Kubernetes automatically updates these EndpointSlices whenever the set of Pods backing a Service changes, providing real-time service discovery information.

For applications not natively designed for Kubernetes, Services offer mechanisms to introduce a network port or load balancer between the application and the backend Pods. This allows even legacy applications to benefit from Kubernetes service discovery without requiring code modifications.

These service discovery mechanisms empower workloads to reliably find and connect to their dependencies within the Kubernetes environment.

Defining a Kubernetes Service

A Service, like Pods or ConfigMaps, is a Kubernetes object. You interact with Services through the Kubernetes API, typically using tools like kubectl. You can create, inspect, and modify Service definitions using YAML or JSON manifests.

Consider a scenario where you have Pods running an application, each listening on TCP port 9376 and labeled with app.kubernetes.io/name=MyApp. To expose this application via a Service, you can define a Service manifest like this:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Applying this manifest using kubectl apply -f service/simple-service.yaml creates a Service named “my-service” of the default ClusterIP service type. This Service targets TCP port 9376 on any Pod matching the label app.kubernetes.io/name: MyApp.

Kubernetes assigns a cluster IP to this Service, a virtual IP address used within the cluster network. Details about this mechanism are available in Virtual IPs and Service Proxies.

The Service controller continuously monitors for Pods matching the Service’s selector. It then updates the associated EndpointSlices, ensuring the Service always points to the correct set of backend Pods.

Service names must adhere to RFC 1035 label name conventions, allowing lowercase alphanumeric characters, ‘-‘, and ‘.’.

Note: A Service can map an incoming port to a different targetPort on the Pods. If targetPort is not specified, it defaults to the same value as the port field for convenience.

Port Definitions in Services

Pods define named ports, and Services can reference these port names in their targetPort attribute. This enhances flexibility and allows Services to abstract port number changes in backend Pods. For example:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
    - containerPort: 80
      name: http-web-svc
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

In this example, the Service nginx-service targets Pods with the label app.kubernetes.io/name: proxy. The targetPort is set to http-web-svc, referencing the named port defined in the Pod manifest. This configuration works even if Pods behind the Service use different port numbers for the same named port. This allows for seamless updates and evolution of backend applications without breaking client connectivity.

The default protocol for Services is TCP, but Kubernetes supports other protocols like UDP and SCTP.

Services can expose multiple ports using multiple port definitions within a single Service object. Each port definition can specify a different protocol if needed.

Services Without Selectors

While Services typically use selectors to target Pods, they can also be defined without selectors. In this case, the Service directly abstracts a set of EndpointSlices objects, allowing you to expose backends running outside the Kubernetes cluster or in scenarios requiring manual endpoint management.

Common use cases for Services without selectors include:

  • External Databases: Using an external managed database in production while using in-cluster databases for testing.
  • Cross-Namespace or Cross-Cluster Services: Pointing a Service to a Service in a different Kubernetes Namespace or even a separate cluster.
  • Workload Migration: Gradually migrating applications to Kubernetes, with some backends still running outside the cluster during the transition.

Here’s an example of a Service definition without a selector:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376

Because this Service lacks a selector, Kubernetes won’t automatically create EndpointSlice objects. You need to manually define EndpointSlice objects to map this Service to network addresses and ports. For instance:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # by convention, use the service name as a prefix
  labels:
    kubernetes.io/service-name: my-service # Link to the Service
addressType: IPv4
ports:
- name: http # Match the service port name
  appProtocol: http
  protocol: TCP
  port: 9376
endpoints:
- addresses:
  - "10.4.5.6"
- addresses:
  - "10.1.2.3"

Custom EndpointSlices: When creating EndpointSlice objects for Services without selectors, you can choose any unique name within the Namespace. The crucial link between the EndpointSlice and the Service is established through the kubernetes.io/service-name label on the EndpointSlice, which must match the Service’s name.

Note: Endpoint IPs must not be loopback or link-local addresses. They also cannot be cluster IPs of other Kubernetes Services as kube-proxy doesn’t support virtual IPs as destinations.

For self-managed or custom controller-created EndpointSlices, it’s recommended to set the endpointslice.kubernetes.io/managed-by label. This label helps identify the entity managing the EndpointSlice, improving observability and management. Avoid using the reserved value "controller", which is used by Kubernetes’ control plane.

Accessing Services Without Selectors: Accessing a Service without a selector is identical to accessing a Service with a selector. Traffic is routed to the endpoints defined in the associated EndpointSlice objects. In the example above, traffic to my-service will be routed to either 10.1.2.3 or 10.4.5.6 on port 9376.

Note: The Kubernetes API server restricts proxying to endpoints not backed by Pods. Actions like kubectl port-forward service/<service-name> forwardedPort:servicePort will fail for services without selectors. This is a security measure to prevent unauthorized proxying.

An ExternalName Service is a specialized type of Service without selectors that utilizes DNS names instead of IPs for service resolution. More details are in the ExternalName section.

EndpointSlices: Efficient Endpoint Management

FEATURE STATE: Kubernetes v1.21 [stable]

EndpointSlices represent subsets of backend network endpoints for a Service, offering a more scalable and efficient way to manage endpoints compared to the legacy Endpoints API.

Kubernetes automatically manages the number of endpoints within each EndpointSlice. When a Service has a large number of backend endpoints, Kubernetes distributes them across multiple EndpointSlice objects. By default, a new EndpointSlice is created when existing ones reach a threshold of 100 endpoints. This sharding approach improves performance and reduces resource consumption, especially for Services with thousands of endpoints.

Refer to EndpointSlices for comprehensive information about this API.

Endpoints (Legacy)

In the Kubernetes API, Endpoints (plural resource kind) define lists of network endpoints, traditionally used by Services to determine Pods for traffic routing.

While Endpoints are still supported, the EndpointSlice API is the recommended, more scalable replacement.

Over-Capacity Endpoints: The legacy Endpoints API has limitations on the number of endpoints it can handle within a single object. When a Service has over 1000 backend endpoints, Kubernetes truncates the Endpoints object, storing only a maximum of 1000 endpoints and adding the annotation endpoints.kubernetes.io/over-capacity: truncated.

In such cases, load balancing mechanisms relying on the legacy Endpoints API will only distribute traffic to a maximum of 1000 backend endpoints. This limit also prevents manual updates to Endpoints objects exceeding 1000 endpoints.

Traffic continues to be sent to all backends, but legacy Endpoint API based load balancing is limited to the first 1000 endpoints.

Application Protocol: Enhancing Service Behavior

FEATURE STATE: Kubernetes v1.20 [stable]

The appProtocol field in Service port definitions allows specifying the application-layer protocol for each Service port. This provides hints to Kubernetes implementations (like service proxies and load balancers) to enable richer protocol-specific behaviors. The appProtocol value is propagated to corresponding EndpointSlice objects.

Valid values for appProtocol follow standard Kubernetes label syntax and include:

  • IANA standard service names (e.g., http, https, grpc).

  • Implementation-defined prefixed names (e.g., mycompany.com/my-custom-protocol).

  • Kubernetes-defined prefixed names:

    Protocol Description
    kubernetes.io/h2c HTTP/2 over cleartext as defined in RFC 7540
    kubernetes.io/ws WebSocket over cleartext as defined in RFC 6455
    kubernetes.io/wss WebSocket over TLS as defined in RFC 6455

Multi-Port Services: Exposing Multiple Ports

Services can expose multiple ports for applications that require it. When defining multiple ports, it’s mandatory to name each port for clarity and unambiguous configuration. For example:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  - name: https
    protocol: TCP
    port: 443
    targetPort: 9377

Note: Port names must adhere to Kubernetes name conventions, using lowercase alphanumeric characters and ‘-‘. Port names must start and end with an alphanumeric character (e.g., http, web-port, app-123 are valid, while 123_abc, -web are not).

Kubernetes Service Types: Different Exposure Strategies

For applications needing external accessibility, Kubernetes offers various Service types to control how Services are exposed beyond the cluster’s internal network. The type field in the Service specification determines the Service’s exposure strategy.

Here’s a breakdown of available type values and their behaviors:

  • ClusterIP: (Default) Exposes the Service on an internal cluster IP. This type makes the Service accessible only from within the cluster. External access requires using Ingress or Gateway.
  • NodePort: Exposes the Service on each Node’s IP address at a static port (NodePort). Kubernetes also sets up a ClusterIP for internal cluster access. This type makes the service accessible externally via <NodeIP>:<NodePort>.
  • LoadBalancer: Exposes the Service externally using a cloud provider’s load balancer. Kubernetes integrates with cloud providers to provision external load balancers that route traffic to the Service. It often builds upon NodePort and ClusterIP types.
  • ExternalName: Maps the Service to an external DNS name defined in the externalName field (e.g., api.foo.bar.example). This type configures the cluster’s DNS server to return a CNAME record for the external hostname. No proxying or port forwarding is involved.

The Service type field is designed in a nested manner, with each level building upon the previous one. However, LoadBalancer Services can be configured to disable NodePort allocation in specific cloud provider implementations.

type: ClusterIP: Internal Cluster Access

ClusterIP is the default Service type. It assigns a cluster-internal IP address to the Service from a predefined IP address range. This IP address is stable for the lifetime of the Service and is only accessible from within the Kubernetes cluster.

ClusterIP serves as the foundation for other Service types.

Setting .spec.clusterIP to "None" creates a headless Service, which doesn’t get assigned a cluster IP address.

Custom Cluster IP Addresses

You can specify a custom cluster IP address during Service creation by setting the .spec.clusterIP field. This can be useful for reusing existing DNS entries or integrating with legacy systems configured for specific IP addresses.

The provided IP address must be a valid IPv4 or IPv6 address within the service-cluster-ip-range CIDR range configured for the API server. Invalid clusterIP values will result in a 422 HTTP error from the API server.

Refer to avoiding collisions for details on how Kubernetes minimizes IP address conflicts between Services.

type: NodePort: Exposing Services on Node IPs

Setting type to NodePort exposes the Service on a static port on each Node’s IP address. Kubernetes allocates a port from the range specified by the --service-node-port-range flag (default: 30000-32767). Every Node in the cluster then proxies traffic on this port to the Service’s backend Pods. The allocated port is visible in the Service’s .spec.ports[*].nodePort field.

NodePort offers flexibility for setting up custom load balancing, environments not fully supported by Kubernetes load balancers, or direct access to Node IPs.

For a NodePort Service, Kubernetes allocates a port (TCP, UDP, or SCTP based on the Service’s protocol). Each Node listens on this port and forwards traffic to healthy endpoints associated with the Service. You can access the Service externally by connecting to any Node’s IP address and the allocated port.

Custom Node Ports

To specify a particular NodePort value, set the nodePort field in the Service manifest. Kubernetes will attempt to allocate this port or reject the request if the port is already in use or outside the valid range. Port collisions must be managed manually.

Example manifest for a type: NodePort Service with a custom NodePort (30007):

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30007 # Custom NodePort value

NodePort Range Reservation

To minimize port collisions, the NodePort range is divided into two bands. Dynamic port allocation primarily uses the upper band and utilizes the lower band only when the upper band is exhausted. Users can then allocate ports from the lower band with a reduced risk of conflicts.

Custom IP Configuration for NodePort Services

Nodes can be configured to use specific IP addresses for serving NodePort Services, especially in multi-network setups. The --nodeport-addresses flag for kube-proxy or the nodePortAddresses field in the kube-proxy configuration file allows specifying IP address ranges considered local to the Node.

For example, --nodeport-addresses=127.0.0.0/8 restricts kube-proxy to only using the loopback interface for NodePort Services. The default behavior (empty list) considers all available network interfaces.

Note: NodePort Services are accessible via <NodeIP>:<spec.ports[*].nodePort> and internally via .spec.clusterIP:<spec.ports[*].port>. When --nodeport-addresses is configured, the <NodeIP> might be a filtered Node IP address based on the specified ranges.

type: LoadBalancer: External Load Balancers

For cloud environments supporting external load balancers, setting type to LoadBalancer provisions a load balancer for the Service. Load balancer creation is asynchronous, and its details (like external IP) are published in the Service’s .status.loadBalancer field.

Example of a LoadBalancer Service status:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

Traffic from the external load balancer is routed to backend Pods. The cloud provider manages the load balancing algorithm and implementation.

Kubernetes typically implements LoadBalancer Services by first creating a NodePort Service and then configuring the external load balancer to forward traffic to the allocated NodePort.

Cloud providers may allow specifying a loadBalancerIP for a static external IP. If not specified, an ephemeral IP address is assigned. If loadBalancerIP is provided but unsupported by the cloud provider, it’s ignored.

Note: The .spec.loadBalancerIP field is deprecated in Kubernetes v1.24 and may be removed in future versions due to inconsistencies across implementations and limitations with dual-stack networking. Cloud provider-specific annotations or the Gateway API are recommended alternatives for specifying load balancer IPs.

Node Liveness and Load Balancer Health Checks

Load balancer health checks are crucial for modern applications. They determine which backend servers receive traffic. Kubernetes doesn’t define a standard health check mechanism for load balancers; cloud providers and integration implementations handle this. Load balancer health checks are heavily used in conjunction with the externalTrafficPolicy field for Services.

Load Balancers with Mixed Protocol Types

FEATURE STATE: Kubernetes v1.26 [stable] (enabled by default: true)

By default, LoadBalancer Services require all defined ports to use the same protocol. The MixedProtocolLBService feature gate (enabled by default since v1.24) allows using different protocols for multi-port LoadBalancer Services.

Note: The supported protocols for load balanced Services are cloud provider-dependent and may have restrictions beyond Kubernetes API enforcement.

Disabling Load Balancer NodePort Allocation

FEATURE STATE: Kubernetes v1.24 [stable]

You can disable NodePort allocation for LoadBalancer Services by setting spec.allocateLoadBalancerNodePorts to false. This is intended for load balancer implementations routing traffic directly to Pods, bypassing NodePorts. By default, spec.allocateLoadBalancerNodePorts is true. Disabling NodePort allocation on an existing Service will not automatically de-allocate existing NodePorts; they must be manually removed from the Service port definitions.

Specifying Load Balancer Implementation Class

FEATURE STATE: Kubernetes v1.24 [stable]

The .spec.loadBalancerClass field for LoadBalancer Services allows selecting a specific load balancer implementation instead of the cloud provider’s default.

If .spec.loadBalancerClass is unset, the default cloud provider load balancer is used (if a cloud provider is configured). Setting .spec.loadBalancerClass indicates that a load balancer implementation matching the specified class is responsible for managing the Service. Default load balancer implementations will ignore Services with this field set. spec.loadBalancerClass can only be set during Service creation and cannot be changed afterward. The value should be a label-style identifier, optionally prefixed (e.g., “internal-vip“, “example.com/internal-vip“). Unprefixed names are reserved for end-users.

Load Balancer IP Address Mode

FEATURE STATE: Kubernetes v1.32 [stable] (enabled by default: true)

The .status.loadBalancer.ingress.ipMode field for LoadBalancer Services specifies how the load balancer IP address behaves. It can be set only when .status.loadBalancer.ingress.ip is also defined.

Possible values for .status.loadBalancer.ingress.ipMode are “VIP” (default) and “Proxy”. “VIP” indicates traffic is delivered to the Node with the destination IP and port set to the load balancer’s IP and port. “Proxy” indicates traffic delivery may involve DNAT to the Pod or direct delivery to the Pod, depending on the cloud provider’s load balancer implementation. Service implementations can use this information to optimize traffic routing.

Internal Load Balancers

In hybrid environments, routing traffic from within the same virtual network address block is often necessary. Split-horizon DNS setups might require separate Services for external and internal traffic routing.

To create an internal load balancer, use cloud provider-specific annotations on the Service:

[tabs]
[GCP]

metadata:
  name: my-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"

[AWS]

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-internal: "true"

[Azure]

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"

[IBM Cloud]

metadata:
  name: my-service
  annotations:
    service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"

[OpenStack]

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: "true"

[CCE (Huawei Cloud Container Engine)]

metadata:
  annotations:
    service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"

[Tencent Cloud]

metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx

[Alibaba Cloud]

metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"

[Oracle Cloud Infrastructure]

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-internal: true

[/tabs]

type: ExternalName: Mapping Services to External DNS Names

ExternalName Services map a Service to an external DNS name, not to Pod selectors. This is configured using the spec.externalName field.

Example mapping my-service in the prod Namespace to my.database.example.com:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

Note: While ExternalName Services accept IPv4 address strings, they treat them as DNS names composed of digits, not as IP addresses. Services with external names resembling IPv4 addresses are not resolved by DNS servers. For mapping to specific IP addresses, consider using headless Services.

When clients within the cluster resolve my-service.prod.svc.cluster.local, the cluster DNS service returns a CNAME record pointing to my.database.example.com. Accessing my-service functions similarly to other Services, but redirection occurs at the DNS level, not through proxying or forwarding. Migrating the database into the cluster later becomes easier by deploying Pods, adding selectors or endpoints, and changing the Service’s type.

Caution: ExternalName Services can cause issues with protocols like HTTP and HTTPS that rely on hostnames. The hostname used by clients within the cluster differs from the externalName reference, potentially leading to errors. For example, HTTP requests might have a Host: header not recognized by the origin server, and TLS servers may fail certificate verification due to hostname mismatch.

Headless Services: Direct Pod Access

In scenarios where load balancing and a single Service IP are not required, headless Services can be created by setting .spec.clusterIP to "None".

Headless Services are useful for integrating with external service discovery mechanisms or when direct Pod access is needed without Kubernetes’ service proxying.

For headless Services, no cluster IP is allocated, kube-proxy is not involved, and no load balancing or proxying is performed by Kubernetes. Clients connect directly to Pod IPs. Headless Services rely on internal DNS records for endpoint discovery. To define a headless Service, set .spec.type to ClusterIP (or leave it as default) and .spec.clusterIP to None.

The string value "None" is a special indicator for headless Services and is distinct from leaving .spec.clusterIP unset.

DNS configuration for headless Services depends on whether selectors are defined:

Headless Services with Selectors

For headless Services with selectors, the endpoints controller creates EndpointSlices, and the DNS configuration returns A or AAAA records pointing directly to the backend Pods.

Headless Services without Selectors

For headless Services without selectors, EndpointSlice objects are not automatically created. Instead, the DNS system configures:

  • CNAME records for type: ExternalName Services.
  • A/AAAA records for all IP addresses of the Service’s ready endpoints for other Service types (excluding ExternalName). A records for IPv4 endpoints and AAAA records for IPv6 endpoints are created.

For headless Services without selectors, the port and targetPort must be identical.

Discovering Kubernetes Services

Clients running within the Kubernetes cluster can discover Services using two primary methods: environment variables and DNS.

Environment Variables

When a Pod starts on a Node, the kubelet injects environment variables for each active Service. Variables are created in the format {SVCNAME}_SERVICE_HOST and {SVCNAME}_SERVICE_PORT, where the Service name is uppercased and dashes are replaced with underscores.

For a Service named redis-primary exposing TCP port 6379 with cluster IP 10.0.0.11, the following environment variables would be created:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

Note: When using environment variables for service discovery, the Service must be created before client Pods. Otherwise, the client Pods won’t have the environment variables populated. DNS-based service discovery eliminates this ordering dependency.

Kubernetes also supports environment variables compatible with Docker Engine’s legacy container links feature. See makeLinkVariables for implementation details.

DNS-Based Service Discovery

Utilizing a DNS service within your Kubernetes cluster is the recommended and most common approach for service discovery. Add-ons like CoreDNS provide cluster-aware DNS resolution.

A cluster DNS server monitors the Kubernetes API for new Services and automatically creates DNS records for each Service. With DNS enabled cluster-wide, Pods can resolve Services by their DNS names.

For a Service named my-service in the my-ns Namespace, DNS will resolve my-service.my-ns to the Service’s cluster IP address. Pods within my-ns can typically access the service using just my-service. Pods in other Namespaces need to use the fully qualified name my-service.my-ns.

Kubernetes DNS also supports SRV (Service) records for named ports. For a Service my-service.my-ns with a port named http using TCP protocol, an SRV query for _http._tcp.my-service.my-ns will return the port number and IP address.

Kubernetes DNS is the sole method for accessing ExternalName Services. Detailed information about ExternalName resolution is in DNS for Services and Pods.

Virtual IP Addressing Mechanism

For a deeper understanding of how Kubernetes exposes Services using virtual IP addresses, refer to Virtual IPs and Service Proxies.

Traffic Policies: Controlling Traffic Routing

The .spec.internalTrafficPolicy and .spec.externalTrafficPolicy fields offer fine-grained control over how Kubernetes routes traffic to healthy backend Pods.

For more details on traffic routing policies, see Traffic Policies.

Traffic Distribution: Optimizing Routing Preferences

FEATURE STATE: Kubernetes v1.31 [beta] (enabled by default: true)

The .spec.trafficDistribution field allows expressing preferences for traffic routing within a Service, such as routing to topologically closer endpoints. This can improve performance, reduce costs, or enhance reliability. This feature requires enabling the ServiceTrafficDistribution feature gate on your cluster and Nodes. In Kubernetes 1.32, the supported value is:

PreferClose: Indicates a preference for routing traffic to endpoints topologically closer to the client. “Topologically proximate” can vary by implementation and might include endpoints on the same Node, rack, zone, or region. Setting this allows implementations to prioritize proximity over equal load distribution. Avoid using this value if such tradeoffs are unacceptable.

If .spec.trafficDistribution is unset, the default routing strategy is applied.

More information on traffic distribution is available in Traffic Distribution.

Session Stickiness: Maintaining Client Sessions

To ensure consistent client-to-Pod connections, session affinity based on client IP addresses can be configured. Refer to session affinity for configuration details.

External IPs: Exposing Services on External IPs

Kubernetes Services can be exposed on externally routable IP addresses (externalIPs) that route to cluster Nodes. When traffic arrives at a Node with a destination IP and port matching a Service’s externalIPs and port, Kubernetes routes the traffic to one of the Service’s endpoints.

externalIPs can be defined for any service type.

Example Service using externalIPs:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 49152
  externalIPs:
  - 198.51.100.32

Clients can access this Service using TCP on 198.51.100.32:80.

Note: Kubernetes does not manage the allocation of externalIPs; cluster administrators are responsible for managing and configuring these IPs.

Service API Object

Service is a core resource in the Kubernetes REST API. Detailed information about the Service API object is available in the Kubernetes API documentation.

What’s Next

To further your understanding of Kubernetes Services and their role in Kubernetes:


Diagram illustrating the Kubernetes overview.


Diagram depicting service traffic management within Kubernetes.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *