Kubernetes Ingress
Introduction
This tutorial will walk you through the process of setting up an Ingress controller and defining Ingress resources to manage external access to your Kubernetes services. You should have a basic understanding of Kubernetes concepts like Pods, Services, and Deployments. You’ll also need a running Kubernetes cluster and kubectl configured to interact with it. We will use Minikube for a local deployment scenario, but the principles apply to any Kubernetes cluster.
Prerequisites
Before you begin, make sure you have the following:
- A running Kubernetes cluster (e.g., Minikube, Kind, or a cloud-based cluster).
kubectlinstalled and configured to connect to your cluster.- Basic knowledge of Kubernetes concepts.
Task 1: Setting Up Minikube
If you don’t already have a Kubernetes cluster, Minikube is a great option for local development.
-
Start Minikube:
NODE_TYPE // bashminikube startNODE_TYPE // output😄 minikube v1.32.0 on Darwin 14.4 (amd64) ✨ Using the docker driver based on user configuration 👍 Starting control plane node minikube in cluster minikube 🚜 Pulling base image ... 💾 Downloading Kubernetes v1.29.2 preload ... > preloaded.tar.lz4: 1.24 GiB / 1.24 GiB 100.00% 24.19 MiB p/s 00m53s 🔥 Creating docker container (CPUs=2, Memory=2200MB) ... 🐳 Preparing Kubernetes v1.29.2 on Docker 24.0.7 ... ▪ Generating certificates and keys ... ▪ Bootstrapping components ... ▪ Configuring cluster parameters ... ▪ Installing addons ... - dashboard: v2.7.0 is available - storage-provisioner: v1.0.0 🔎 Verifying Kubernetes ... 🌟 Enabled addons: storage-provisioner, default-storageclass 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default -
Enable the Ingress addon:
NODE_TYPE // bashminikube addons enable ingressNODE_TYPE // output😄 ingress is already enabledTheminikube addons enable ingresscommand sets up an Ingress controller within your Minikube cluster, making it possible to test Ingress resources locally.
Task 2: Deploying Sample Applications
Let’s deploy two simple applications to demonstrate Ingress routing.
graph TD
%% Admin Actions
Admin["Admin / CLI"]:::adminBox
subgraph K8s_Cluster["Kubernetes Cluster"]
direction TB
%% Application 1 Logic
subgraph App1_Stack["App 1 Stack"]
direction TB
D1["Deployment: app1-deployment
replicas: 1"]:::deployBox
S1["Service: app1-service
Type: ClusterIP"]:::serviceBox
P1["Pod: app1-pod
Label: app=app1"]:::podBox
end
%% Application 2 Logic
subgraph App2_Stack["App 2 Stack"]
direction TB
D2["Deployment: app2-deployment
replicas: 1"]:::deployBox
S2["Service: app2-service
Type: ClusterIP"]:::serviceBox
P2["Pod: app2-pod
Label: app=app2"]:::podBox
end
end
%% Deployment Actions
Admin ==>|kubectl apply| D1
Admin ==>|kubectl apply| D2
Admin ==>|kubectl apply| S1
Admin ==>|kubectl apply| S2
%% Relationships
D1 -.->|Manages| P1
D2 -.->|Manages| P2
%% Service Selectors
S1 -- "Selects (app=app1)" --> P1
S2 -- "Selects (app=app2)" --> P2
%% STYLING
classDef adminBox fill:#37474F,stroke:#263238,stroke-width:2px,rx:8,ry:8,color:#FFF;
classDef deployBox fill:#E8EAF6,stroke:#3F51B5,stroke-width:2px,rx:8,ry:8,color:#1A237E;
classDef serviceBox fill:#C7E6E2,stroke:#009688,stroke-width:3px,color:#004D40,rx:10,ry:10,font-weight:bold;
classDef podBox fill:#D8FDF8,stroke:#4ED8C7,stroke-width:2px,rx:8,ry:8,color:#222;
style App1_Stack fill:#F9F9F9,stroke:#CCC,stroke-width:1px,stroke-dasharray: 5 5;
style App2_Stack fill:#F9F9F9,stroke:#CCC,stroke-width:1px,stroke-dasharray: 5 5;
style K8s_Cluster fill:#FFF,stroke:#666,stroke-width:2px;
%% Link Styling
linkStyle 0,1,2,3 stroke:#37474F,stroke-width:2px;
linkStyle 4,5 stroke:#3F51B5,stroke-width:2px,stroke-dasharray: 3 3;
linkStyle 6,7 stroke:#009688,stroke-width:3px;
-
Create a deployment for the first application:
NODE_TYPE // yaml# app1-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: app1-deployment labels: app: app1 spec: replicas: 1 selector: matchLabels: app: app1 template: metadata: labels: app: app1 spec: containers: - name: app1 image: nginx:latest ports: - containerPort: 80Apply the deployment:
NODE_TYPE // bashkubectl apply -f app1-deployment.yamlNODE_TYPE // outputdeployment.apps/app1-deployment created -
Create a service for the first application:
NODE_TYPE // yaml# app1-service.yaml apiVersion: v1 kind: Service metadata: name: app1-service spec: selector: app: app1 ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIPApply the service:
NODE_TYPE // bashkubectl apply -f app1-service.yamlNODE_TYPE // outputservice/app1-service created -
Repeat the process for the second application:
NODE_TYPE // yaml# app2-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: app2-deployment labels: app: app2 spec: replicas: 1 selector: matchLabels: app: app2 template: metadata: labels: app: app2 spec: containers: - name: app2 image: nginx:latest ports: - containerPort: 80Apply the deployment:
NODE_TYPE // bashkubectl apply -f app2-deployment.yamlNODE_TYPE // outputdeployment.apps/app2-deployment createdNODE_TYPE // yaml# app2-service.yaml apiVersion: v1 kind: Service metadata: name: app2-service spec: selector: app: app2 ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIPApply the service:
NODE_TYPE // bashkubectl apply -f app2-service.yamlNODE_TYPE // outputservice/app2-service created
Task 3: Defining an Ingress Resource
Now, let’s create an Ingress resource to route traffic to these applications based on hostnames.
graph TD
%% Define External Users
User1["External User A
app1.example.com"]:::externalBox
User2["External User B
app2.example.com"]:::externalBox
%% Kubernetes Cluster
subgraph K8s_Cluster["Kubernetes Cluster"]
direction TB
%% Ingress Controller (The Smart Router)
IngressCtrl["Ingress Controller
(Nginx/ALB)
Evaluates Host Header"]:::ingressBox
%% Logical Ingress Rules
subgraph IngressRules["Ingress Resource: example-ingress"]
Rule1["Host: app1.example.com
Path: /"]:::ruleBox
Rule2["Host: app2.example.com
Path: /"]:::ruleBox
end
%% Backend Services
Svc1["Service: app1-service"]:::serviceBox
Svc2["Service: app2-service"]:::serviceBox
%% Target Pods
Pod1["App 1 Pods"]:::podBox
Pod2["App 2 Pods"]:::podBox
end
%% TRAFFIC FLOW
User1 ==> IngressCtrl
User2 ==> IngressCtrl
%% Routing based on Hostname
IngressCtrl -->|Matches Rule 1| Svc1
IngressCtrl -->|Matches Rule 2| Svc2
%% Final Destination
Svc1 -.-> Pod1
Svc2 -.-> Pod2
%% STYLING
classDef externalBox fill:#37474F,stroke:#263238,stroke-width:2px,rx:8,ry:8,color:#FFF;
classDef ingressBox fill:#E3F2FD,stroke:#1E88E5,stroke-width:3px,color:#0D47A1,rx:12,ry:12,font-weight:bold;
classDef ruleBox fill:#FFF,stroke:#7986CB,stroke-width:1px,color:#222,rx:5,ry:5,font-size:12px;
classDef serviceBox fill:#C7E6E2,stroke:#009688,stroke-width:3px,color:#004D40,rx:10,ry:10,font-weight:bold;
classDef podBox fill:#D8FDF8,stroke:#4ED8C7,stroke-width:2px,rx:8,ry:8,color:#222;
style IngressRules fill:#F1F1F1,stroke:#C6C6C6,stroke-width:1px,stroke-dasharray: 5 5;
style K8s_Cluster fill:#F9F9F9,stroke:#666,stroke-width:2px;
%% Link Styling
linkStyle 0,1 stroke:#1E88E5,stroke-width:4px;
linkStyle 2,3 stroke:#5C6BC0,stroke-width:2px;
linkStyle 4,5 stroke:#009688,stroke-width:2px,stroke-dasharray: 5 5;
-
Create an Ingress resource file:
NODE_TYPE // yaml# ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: rules: - host: app1.example.com http: paths: - path: / pathType: Prefix backend: service: name: app1-service port: number: 80 - host: app2.example.com http: paths: - path: / pathType: Prefix backend: service: name: app2-service port: number: 80Thenginx.ingress.kubernetes.io/rewrite-target: /annotation ensures that the entire path is rewritten to/when forwarding the request to the backend service. -
Apply the Ingress resource:
NODE_TYPE // bashkubectl apply -f ingress.yamlNODE_TYPE // outputingress.networking.k8s.io/example-ingress created
Task 4: Accessing the Applications
To access the applications through the Ingress, you need to configure your local machine to resolve the hostnames to the Ingress controller’s IP address.
-
Get the Ingress controller’s IP address in Minikube:
NODE_TYPE // bashminikube ipNODE_TYPE // output192.168.49.2 -
Edit your
/etc/hostsfile (or the equivalent on your operating system) and add the following entries, replacing192.168.49.2with the actual IP address from the previous step:NODE_TYPE // text192.168.49.2 app1.example.com 192.168.49.2 app2.example.comYou will need administrator privileges to modify the/etc/hostsfile. -
Now, you can access the applications using your web browser or
curl:NODE_TYPE // bashcurl http://app1.example.comNODE_TYPE // output<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>NODE_TYPE // bashcurl http://app2.example.comNODE_TYPE // output<!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> <style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } </style> </head> <body> <h1>Welcome to nginx!</h1> <p>If you see this page, the nginx web server is successfully installed and working. Further configuration is required.</p> <p>For online documentation and support please refer to <a href="http://nginx.org/">nginx.org</a>.<br/> Commercial support is available at <a href="http://nginx.com/">nginx.com</a>.</p> <p><em>Thank you for using nginx.</em></p> </body> </html>You should see the default Nginx welcome page for both applications, confirming that the Ingress is correctly routing traffic based on the hostnames.
Conclusion
In this tutorial, you learned how to set up an Ingress controller and define Ingress resources to route traffic to different services based on hostnames. This allows you to expose multiple applications through a single IP address, simplifying the management of external access to your Kubernetes cluster.