Master Kubernetes networking! Learn about Services, Ingress, and Network Policies to build robust and secure containerized applications.
Kubernetes networking can feel like a labyrinth at first. You’ve got pods zipping around, and you need to figure out how they talk to each other, how external traffic gets in, and how to keep things secure. It's a fundamental part of running anything non-trivial on Kubernetes, and frankly, it's where a lot of operational headaches can pop up if you don't get it right.
In this post, I want to break down the core components that make Kubernetes networking tick: Services, Ingress, and Network Policies. We'll go beyond just what they are and dive into how they work, why you'd choose one over the other, and some practical tips I've picked up from wrestling with these in production environments.
At its heart, a Kubernetes Service is an abstraction that defines a logical set of Pods and a policy by which to access them. Think of it as a stable IP address and DNS name that points to a dynamic group of Pods. Pods are ephemeral; they can be created, destroyed, rescheduled, and their IPs change. Services provide a consistent endpoint for other Pods (or external clients) to connect to, regardless of the underlying Pods' lifecycle.
There are a few types of Services, each suited for different scenarios:
This is the default Service type. It exposes the Service on an internal IP in the cluster. This IP is only reachable from within the cluster. It’s perfect for internal microservices that need to communicate with each other but don't need direct external access.
Let's say you have a frontend service that needs to talk to a backend service. The backend service would typically be exposed as a ClusterIP.
YAMLapiVersion: v1 kind: Service metadata: name: backend-service spec: selector: app: backend # This selects pods with the label app=backend ports: - protocol: TCP port: 80 # The port the service listens on targetPort: 8080 # The port your application container listens on type: ClusterIP
When another Pod within the cluster makes a request to backend-service:80, Kubernetes's kube-proxy (running on each node) intercepts this. It uses iptables or IPVS rules to dynamically route the traffic to one of the healthy Pods backing backend-service. This load balancing is automatic and transparent.
A NodePort Service exposes the Service on each Node's IP at a static port (the NodePort). You can then contact the Service from outside the cluster using <NodeIP>:<NodePort>. The range of ports is typically 30000-32767.
This is useful for development or simple deployments where you need direct access to a service from outside the cluster without setting up a full Ingress controller. However, managing external access this way can become cumbersome as you scale.
YAMLapiVersion: v1 kind: Service metadata: name: frontend-service spec: selector: app: frontend ports: - protocol: TCP port: 80 targetPort: 80 type: NodePort # Exposes the service on a static port on each node
This type is cloud-provider specific. When you create a Service of type LoadBalancer, Kubernetes interacts with your cloud provider (AWS, GCP, Azure, etc.) to provision an external load balancer. This load balancer gets a public IP address and routes traffic to your Service.
This is the most common way to expose HTTP(S) services to the internet in managed Kubernetes environments.
YAMLapiVersion: v1 kind: Service metadata: name: external-api-service spec: selector: app: api ports: - protocol: TCP port: 80 targetPort: 8000 type: LoadBalancer # Provisions an external cloud load balancer
This Service type maps the Service name to an external DNS name. It doesn't select Pods. It's essentially a CNAME record for your Service. This is useful when you want to expose an external service (like a managed database or an external API) as if it were a Service within your cluster.
YAMLapiVersion: v1 kind: Service metadata: name: external-db-service spec: type: ExternalName externalName: my-managed-db.example.com # The actual external DNS name
While LoadBalancer type Services are great for exposing a single application, what happens when you have multiple services that need to be accessible from the internet? You don't want to provision a separate load balancer for each one – that gets expensive and complex quickly. This is where Ingress comes in.
Kubernetes Ingress is an API object that manages external access to the services in a cluster, typically HTTP. It provides routing rules, SSL/TLS termination, and name-based virtual hosting. An Ingress resource itself doesn't do anything; it needs an Ingress Controller to fulfill the Ingress rules.
Common Ingress Controllers include:
You typically install an Ingress Controller as a Deployment within your cluster. This controller watches for Ingress resources and configures an underlying proxy (like NGINX) to route traffic according to the rules you've defined.
LoadBalancer Service or NodePort, giving it a stable external IP address. All external traffic for your HTTP applications then flows through this single entry point.Here's an example of an Ingress resource:
YAMLapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / # Example annotation for NGINX spec: ingressClassName: nginx # Specifies which ingress controller should handle this rules: - host: "api.example.com" http: paths: - path: "/users" pathType: Prefix backend: service: name: user-service port: number: 80 - path: "/products" pathType: Prefix backend: service: name: product-service port: number: 80 - host: "www.example.com" http: paths: - path: "/" pathType: Prefix backend: service: name: frontend-service port: number: 80
This Ingress resource tells the NGINX Ingress Controller:
api.example.com/users, send it to the user-service on port 80.api.example.com/products, send it to the product-service on port 80.www.example.com/ (any path on this host), send it to the frontend-service on port 80.Key benefits of Ingress:
Now that we can expose our services and route traffic, we need to think about security. By default, all Pods in a Kubernetes cluster can communicate with each other. This is often referred to as a "flat network." While convenient, it's not ideal for production environments. A compromised Pod could potentially access any other Pod in the cluster.
Kubernetes Network Policies allow you to specify how groups of Pods are allowed to communicate with each other and with other network endpoints. They act as firewalls at the Pod level.
A Network Policy resource selects a set of Pods and then defines rules for what traffic is allowed to those Pods (ingress rules) and what traffic is allowed from those Pods (egress rules).
Important: Network Policies are implemented by the Container Network Interface (CNI) plugin you're using. Not all CNI plugins support Network Policies. Popular ones that do include Calico, Cilium,