Cloud Native 33 min read

Mastering Kubernetes Services: From Creation to External Exposure

This article explains Kubernetes services in depth, covering how to create services, configure session affinity, expose multiple ports, use named ports, discover services via environment variables and DNS, expose services externally with NodePort, LoadBalancer, and Ingress, and set up readiness probes for pods.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Mastering Kubernetes Services: From Creation to External Exposure

Preface

Previously we introduced the Kubernetes replica mechanism, which keeps deployments running and healthy automatically. This article continues with another powerful Kubernetes feature: services, which provide a stable access point between clients and pods.

Service

A Kubernetes Service offers a single, immutable access point for a set of identical pods. Its IP address and port remain constant, and client connections are load‑balanced across the pods selected by the service’s label selector.

1. Create a Service

Service connections are load‑balanced across all backend pods. The selector in the Service definition determines which pods belong to the service.

[d:\k8s]$ kubectl create -f kubia-rc.yaml
replicationcontroller/kubia created

[d:\k8s]$ kubectl get pod
NAME          READY   STATUS            RESTARTS   AGE
kubia-6dxn7   0/1     ContainerCreating 0          4s
kubia-fhxht   0/1     ContainerCreating 0          4s
kubia-fpvc7   0/1     ContainerCreating 0          4s

Using the previous pod YAML (label app: kubia), the Service YAML must specify the same label:

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia

The Service defines two ports: port (exposed by the Service) and targetPort (the pod’s listening port). Pods are selected by matching labels.

[d:\k8s]$ kubectl create -f kubia-svc.yaml
service/kubia created

[d:\k8s]$ kubectl get svc
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes ClusterIP  10.96.0.1        <none>        443/TCP   6d15h
kubia      ClusterIP  10.96.191.193    <none>        80/TCP    4s

[d:\k8s]$ kubectl exec kubia-6dxn7 -- curl -s http://10.96.191.193
You've hit kubia-fhxht

After creating the Service, a stable internal CLUSTER‑IP is assigned. Requests are routed to any pod; to force sticky sessions, set sessionAffinity: ClientIP.

1.1 Configure Session Affinity

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  sessionAffinity: ClientIP
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia

Only the sessionAffinity field changes.

[d:\k8s]$ kubectl delete svc kubia
service "kubia" deleted

[d:\k8s]$ kubectl create -f kubia-svc-client-ip-session-affinity.yaml
service/kubia created

[d:\k8s]$ kubectl get svc
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes ClusterIP  10.96.0.1        <none>        443/TCP   6d15h
kubia      ClusterIP  10.96.51.99      <none>        80/TCP    25s

1.2 Expose Multiple Ports

If a pod listens on multiple ports, a Service can expose them all:

apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - name: http
    port: 80
    targetPort: 8080
  - name: https
    port: 443
    targetPort: 8080
  selector:
    app: kubia

Both ports map to the same target port (8080) in this example.

[d:\k8s]$ kubectl create -f kubia-svc-named-ports.yaml
service/kubia created

[d:\k8s]$ kubectl get svc
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)            AGE
kubernetes ClusterIP  10.96.0.1        <none>        443/TCP            6d18h
kubia      ClusterIP  10.96.13.178     <none>        80/TCP,443/TCP    7s

Both ports are reachable.

1.3 Use Named Ports

Define a named port in the pod template and refer to it in the Service:

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    app: kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: ksfzhaohui/kubia
        ports:
        - name: http
          containerPort: 8080
apiVersion: v1
kind: Service
metadata:
  name: kubia
spec:
  ports:
  - port: 80
    targetPort: http
  selector:
    app: kubia

2. Service Discovery

2.1 Environment Variables

When a pod starts, Kubernetes injects environment variables for existing services. If the Service is created before the pod, the pod can read KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT to obtain the Service IP and port.

[d:\k8s]$ kubectl get svc
NAME      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes ClusterIP  10.96.0.1        <none>        443/TCP   7d14h
kubia      ClusterIP  10.96.106.37    <none>        80/TCP    14h

[d:\k8s]$ kubectl exec kubia-4m9nv env
HOSTNAME=kubia-4m9nv
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT=443
...

If the pod is recreated after the Service, additional variables such as KUBIA_SERVICE_HOST and KUBIA_SERVICE_PORT appear.

2.2 DNS

Kubernetes runs a DNS service (coredns) that resolves Service names to their Cluster IPs. Pods can reach a Service via its fully qualified domain name, e.g., kubia.default.svc.cluster.local.

[d:\k8s]$ kubectl exec kubia-599v9 -- curl -s http://kubia.default.svc.cluster.local
You've hit kubia-8s8j4

Within the same namespace, the short name kubia also works.

2.3 Run Shell Inside a Pod

d:\k8s> winpty kubectl exec -it kubia-599v9 -- sh
# curl -s http://kubia
You've hit kubia-dm6kr
# exit

Connecting to External Services

Kubernetes Services can also proxy to external endpoints via the Endpoint resource or ExternalName.

1. Endpoint Resource

Endpoints sit between Services and Pods, listing the IPs and ports of the actual backends.

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  ports:
  - port: 80

Because no selector is defined, the Service has no Endpoints initially.

[d:\k8s]$ kubectl create -f external-service.yaml
service/external-service created

[d:\k8s]$ kubectl describe svc external-service
Endpoints: <none>

Manually create matching Endpoints:

apiVersion: v1
kind: Endpoints
metadata:
  name: external-service
subsets:
- addresses:
  - ip: 172.17.0.9
  - ip: 172.17.0.10
  ports:
  - port: 8080
[d:\k8s]$ kubectl create -f external-service-endpoints.yaml
endpoints/external-service created

[d:\k8s]$ kubectl describe svc external-service
Endpoints: 172.17.0.10:8080,172.17.0.9:8080

2. External Endpoint (External IP)

Endpoints can also point to an external IP, e.g., a Tomcat server at 10.13.82.21:8080.

apiVersion: v1
kind: Endpoints
metadata:
  name: external-service
subsets:
- addresses:
  - ip: 10.13.82.21
  ports:
  - port: 8080
[d:\k8s]$ kubectl create -f external-service-endpoints2.yaml
endpoints/external-service created

[d:\k8s]$ kubectl create -f external-service.yaml
service/external-service created

[d:\k8s]$ kubectl exec kubia-599v9 -- curl -s http://external-service
ok

3. ExternalName Service

Define a Service of type ExternalName that maps a DNS name to an external service.

apiVersion: v1
kind: Service
metadata:
  name: external-service
spec:
  type: ExternalName
  externalName: api.ksfzhaohui.com
  ports:
  - port: 80
[d:\k8s]$ kubectl create -f external-service-externalname.yaml
service/external-service created

[d:\k8s]$ kubectl exec kubia-599v9 -- curl -s http://external-service:8080
ok

Exposing Services to External Clients

1. NodePort

Expose a Service on a static port on every node.

apiVersion: v1
kind: Service
metadata:
  name: kubia-nodeport
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30123
  selector:
    app: kubia
[d:\k8s]$ kubectl create -f kubia-svc-nodeport.yaml
service/kubia-nodeport created

[d:\k8s]$ kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubia-nodeport  NodePort    10.96.59.16      <none>        80:30123/TCP    3s

Access the Service via the node’s IP (e.g., Minikube’s 192.168.99.108) and the NodePort.

2. LoadBalancer

Creates an external load balancer (cloud provider) and a NodePort.

apiVersion: v1
kind: Service
metadata:
  name: kubia-loadbalancer
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 8080
  selector:
    app: kubia
[d:\k8s]$ kubectl create -f kubia-svc-loadbalancer.yaml
service/kubia-loadbalancer created

[d:\k8s]$ kubectl get svc
NAME               TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubia-loadbalancer LoadBalancer   10.96.207.113    <pending>     80:30038/TCP    7s

In Minikube the EXTERNAL‑IP stays pending.

3. Reduce Unnecessary Network Hops

Set externalTrafficPolicy: Local so that external traffic is only forwarded to pods on the same node.

apiVersion: v1
kind: Service
metadata:
  name: kubia-nodeport-onlylocal
spec:
  type: NodePort
  externalTrafficPolicy: Local
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30124
  selector:
    app: kubia

4. Ingress

Ingress provides HTTP routing based on host and path, using a single external IP.

4.1 Ingress Controller

Enable the Ingress addon in Minikube and verify the controller pod is running.

minikube addons enable ingress
kubectl get pods -n kube-system
... nginx-ingress-controller ... Running ...

4.2 Ingress Resource

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubia
spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubia-nodeport
          servicePort: 80
[d:\k8s]$ kubectl create -f kubia-ingress.yaml
ingress.extensions/kubia created

[d:\k8s]$ kubectl get ingress
NAME   HOSTS               ADDRESS          PORTS   AGE
kubia  kubia.example.com  192.168.99.108   80      6m4s

Map kubia.example.com to the node IP in /etc/hosts to test.

4.3 Expose Multiple Services

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubia2
spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /v1
        backend:
          serviceName: kubia-nodeport
          servicePort: 80
      - path: /v2
        backend:
          serviceName: kubia-nodeport
          servicePort: 80
  - host: kubia2.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubia-nodeport
          servicePort: 80
[d:\k8s]$ kubectl create -f kubia-ingress2.yaml
ingress.extensions/kubia2 created

[d:\k8s]$ kubectl get ingress
NAME   HOSTS                                   ADDRESS          PORTS   AGE
kubia2  kubia.example.com,kubia2.example.com   192.168.99.108   80      15m

4.4 TLS Termination

Generate a TLS secret and reference it in the Ingress.

# openssl genrsa -out tls.key 2048
# openssl req -new -x509 -key tls.key -out tls.cert -days 360 -subj /CN=kubia.example.com
kubectl create secret tls tls-secret --cert=tls.cert --key=tls.key
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: kubia
spec:
  tls:
  - hosts:
    - kubia.example.com
    secretName: tls-secret
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubia-nodeport
          servicePort: 80
[d:\k8s]$ kubectl apply -f kubia-ingress-tls.yaml
ingress.extensions/kubia configured

Access via https://kubia.example.com to see the TLS‑secured endpoint.

Pod Readiness Probes

Readiness probes determine whether a pod can receive traffic. Types include exec, httpGet, and tcpSocket. Pods failing the probe are removed from Service endpoints until they become ready.

1. Types of Readiness Probes

Exec probe – runs a command inside the container; success is indicated by exit code 0.

HTTP GET probe – sends an HTTP request; success is indicated by a 2xx/3xx status code.

TCP socket probe – attempts a TCP connection; success if the connection is established.

2. Adding a Readiness Probe

Edit the ReplicationController to include an exec probe that checks for the file /var/ready:

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    app: kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: ksfzhaohui/kubia
        ports:
        - containerPort: 8080
        readinessProbe:
          exec:
            command:
            - ls
            - /var/ready

When a new pod is created, it will stay in READY 0/1 until the probe succeeds.

Summary

The article introduced the fundamentals of Kubernetes Services, how to create and discover them, how to link Services to internal and external endpoints, and three primary ways to expose Services to external clients (NodePort, LoadBalancer, Ingress). It also covered configuring session affinity, multiple ports, named ports, and readiness probes to ensure only ready pods receive traffic.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

KubernetesServiceIngressloadbalancer
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.