Kubernetes basics

Networking and add-ons

Service types, CoreDNS, CNI network plugins, Ingress controllers, and RKE2's default components.

6 min read

Networking and add-ons

In this section you will learn how traffic flows in a Kubernetes cluster: how Pods communicate, how an application is published with a Service and an Ingress, and which networking add-ons RKE2 installs by default. At the end you will expose an application to the outside world and test the cluster’s name service (CoreDNS) from inside a pod.


1. Kubernetes networking fundamentals

Kubernetes networking follows a few basic rules:

  • Every Pod has its own IP address. The containers inside a Pod share the same network and can talk to each other over localhost.
  • All Pods can reach each other without NAT — the cluster is one flat network.
  • Pod IPs are short-lived. When a Pod restarts, it gets a new IP. That is why traffic is not addressed directly to Pods but through a Service.

This means we need a stable way to reach a set of Pods — that is what a Service is for.


2. CNI network plugins

CNI (Container Network Interface) is the interface through which a network plugin implements Pod-to-Pod networking: it assigns IPs to Pods and routes traffic between nodes.

  • RKE2’s default CNI is Canal (a combination of Calico + Flannel): Flannel handles the Pod-to-Pod network and Calico the network policies (NetworkPolicy).
  • As alternatives, RKE2 also supports Cilium and Calico on its own.

CNI is essential: without it Pods get no network and do not reach the Ready state.


3. Service types

A Service gives a set of Pods a stable name and IP (virtual IP). It selects the backing Pods based on labels and distributes traffic to them. kube-proxy implements the routing on every node.

flowchart LR
    Client[Client] --> SVC[Service · ClusterIP]
    SVC --> P1[Pod 1]
    SVC --> P2[Pod 2]
    SVC --> P3[Pod 3]
TypeVisibilityPurpose
ClusterIPOnly inside the clusterDefault type; internal service-to-service traffic
NodePortNode IP + port (30000–32767)Simple external access for testing
LoadBalancerExternal load balancerProduction external access (cloud LB or RKE2’s svclb)
  • ClusterIP – reachable only from inside the cluster by the name <service>.<namespace>.svc.cluster.local.
  • NodePort – opens the same port on every node and routes it to the Service.
  • LoadBalancer – requests an external load balancer. In the cloud it is provided by the cloud LB; RKE2 also has a built-in svclb implementation.

💡 In practice, external HTTP traffic is usually published through an Ingress, not directly with a LoadBalancer Service for each application.


4. CoreDNS – the cluster name service

CoreDNS is the cluster’s internal DNS server. It gives Services and Pods names by which they find each other without hard-coded IP addresses (service discovery).

A Service’s DNS name is formed like this:

text
<service>.<namespace>.svc.cluster.local

For example, the Service web in the default namespace is found by the name web.default.svc.cluster.local — or, within the same namespace, by just the name web.

RKE2 installs CoreDNS by default as part of the cluster (rke2-coredns-* Pods in the kube-system namespace).


5. Ingress and Ingress controllers

An Ingress is a set of rules that routes HTTP(S) traffic coming from outside the cluster to the correct Services based on the host and the path. It also enables TLS termination (HTTPS).

An Ingress is just a rule definition — it is implemented by an Ingress controller:

  • RKE2’s default Ingress controller is ingress-nginx (rke2-ingress-nginx-*).
  • A single controller can serve many applications on different hosts and paths.
flowchart LR
    User[User] -->|shop.example.com| ING[Ingress-controller · nginx]
    ING -->|/| SVC1[Service: web]
    ING -->|/api| SVC2[Service: api]
    SVC1 --> P1[web pods]
    SVC2 --> P2[api pods]

An Ingress operates at OSI layer 7 (HTTP), so it can route based on the path and host. A Service (layer 4) routes based only on ports.


RKE2 installs the network and basic services automatically at startup (helm-controller handles the installation). The key components visible in the kube-system namespace:

  • Canal (CNI) – rke2-canal-*: Pod-to-Pod networking and network policies.
  • CoreDNS – rke2-coredns-*: the cluster name service.
  • ingress-nginx – rke2-ingress-nginx-controller-*: HTTP(S) routing from outside in.
  • kube-proxy – on every node: routing of Service traffic.
  • metrics-server – rke2-metrics-server-*: resource metrics (kubectl top).

7. Hands-on: publishing an application and name resolution

Publish an application to the outside world using a Service and an Ingress, and test CoreDNS name resolution from inside a pod. First make sure you have the cluster’s kubeconfig in use (see the exercise in the architecture section) and access to Rancher.

Create and switch to your own namespace to work in:

bash
# Replace <your-name> with your own name
kubectl create namespace <your-name> --dry-run=client -o yaml | kubectl apply -f -
kubectl config set-context --current --namespace=<your-name>

After this, all the commands below target your own namespace.

A) Deploy an application and create a Service

1. Create a Deployment and a Service from the command line:

bash
# Luo esimerkkisovellus
kubectl create deployment web --image=nginx

# Anna sille pysyvä osoite klusterin sisällä (ClusterIP)
kubectl expose deployment web --port=80 --target-port=80

# Tarkastele Serviceä ja sen valitsemia Podeja
kubectl get svc web -o wide
kubectl get endpoints web

2. Test the Service from inside the cluster with a temporary pod:

bash
kubectl run curl --image=curlimages/curl:8.10.1 --rm -it --restart=Never -- \
  curl -s http://web

The response is the HTML of nginx’s default page.

B) Test CoreDNS name resolution from inside a pod

bash
# Lyhyt nimi samassa nimiavaruudessa
kubectl run dnstest --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup web

# Täydellinen DNS-nimi (korvaa <your-name> nimiavaruudellasi)
kubectl run dnstest --image=busybox:1.36 --rm -it --restart=Never -- \
  nslookup web.<your-name>.svc.cluster.local

nslookup returns the Service’s ClusterIP — this is handled by CoreDNS.

C) Expose the application to the outside world with an Ingress

Option 1 – from the command line (declarative manifest):

bash
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web
spec:
  ingressClassName: nginx
  rules:
    - host: web.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 80
EOF

Inspect the Ingress and the address assigned to it:

bash
kubectl get ingress web
kubectl describe ingress web

Option 2 – from the Rancher UI:

  1. Open Cluster Explorer → Service Discovery → Ingresses.
  2. Select Create, set the host (e.g. web.example.com), the path /, and the backend Service to web on port 80.
  3. Save and verify that the Ingress also appears with the command kubectl get ingress.

🔎 Testing: If the host is not in real DNS, you can test the Ingress controller’s address directly and set the host in the request:

bash
curl -s -H "Host: web.example.com" http://<ingress-controller-ip>/

D) Clean up the exercise

Delete your namespace, which removes the Deployment, Service, and Ingress all at once:

bash
kubectl delete namespace <your-name>

Summary

  • Every Pod has its own IP; stable access is implemented with a Service, whose routing is handled by kube-proxy.
  • Service types: ClusterIP (internal), NodePort (node port), LoadBalancer (external load balancer).
  • CoreDNS gives Services names of the form <service>.<namespace>.svc.cluster.local.
  • CNI (in RKE2, Canal by default) implements the Pod-to-Pod network.
  • Ingress routes external HTTP(S) traffic based on the host and path; RKE2’s default controller is ingress-nginx.
  • RKE2 installs these networking components by default into the kube-system namespace.