Kubernetes Ingress and Ingress controller: Complete Guide with microservice
Exposing Golang microservice
What is Ingress?
Ingress is an API object in Kubernetes that manages external access to services inside your cluster.
Normally, Kubernetes services (
ClusterIP
,NodePort
,LoadBalancer
) expose pods, but they are limited:ClusterIP
→ accessible only inside the cluster.NodePort
→ exposes service on each node’s IP with a fixed port (not very flexible).LoadBalancer
→ creates a cloud provider load balancer per service (can be costly).
Instead, Ingress provides a smarter way:
It acts like a reverse proxy and defines rules (based on hostnames or paths) for routing external traffic to internal services.
Example:
myapp.com/api
→ forwards toapi-service
myapp.com/web
→ forwards tofrontend-service
What is an Ingress Controller?
The Ingress object itself is just a set of rules (YAML config).
To actually enforce those rules, you need an Ingress Controller, which is a piece of software running in the cluster.
It watches Ingress resources and configures a reverse proxy (like NGINX, HAProxy, Traefik, etc.) to handle incoming requests accordingly.
👉 Without an Ingress Controller, the Ingress object does nothing.
Why do we need Ingress + Ingress Controller?
To expose multiple services under the same IP/domain using rules.
To avoid creating a separate LoadBalancer per service (cost & complexity).
To handle TLS/SSL termination in one place.
To support advanced features:
Path-based routing (
/api
→ one service,/app
→ another)Host-based routing (
foo.example.com
,bar.example.com
)Load balancing and sticky sessions
Rate limiting, authentication, etc. (depending on controller).
Installing an Ingress Controller
Lets install first a famous ingress controller, nginx ingress controller - using public helm charts. As installed below, we need to install the ingress controller with the nodeport service type so that we can run in local cluster.
helm repo add nginx-stable https://helm.nginx.com/stable
"nginx-stable" has been added to your repositories
controlplane:~$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "nginx-stable" chart repository
Update Complete. ⎈Happy Helming!⎈
controlplane:~$
# Install NGINX Ingress Controller
helm install nginx-ingress nginx-stable/nginx-ingress \
--create-namespace \
--namespace nginx-ingress \
--set controller.service.type=NodePort \
--set controller.service.httpPort.nodePort=30080 \
--set controller.service.httpsPort.nodePort=30443
NAME: nginx-ingress
LAST DEPLOYED: Sat Sep 6 15:54:24 2025
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
NGINX Ingress Controller 5.1.1 has been installed.
# Verify installation
kubectl get pods -n nginx-ingress
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-654b4578d7-tlhjz 1/1 Running 0 85s
kubectl get svc -n nginx-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller NodePort 10.102.185.119 <none> 80:30080/TCP,443:30443/TCP 2m15s
controlplane:~$
In addition to the above resources, the helm chart also installs required ClusterRole, ClusterRolebinding and most importantly an ingress class name called “nginx”. This ingress class name is important while setting up ingress resource rules as mentioned below.
Creating an Microservice Deployment
This is the same deployment built and deployed in previous blog: Containerized microservice . Refer this on how its built.
# dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-helloworld
labels:
app: go-helloworld
spec:
replicas: 2
selector:
matchLabels:
app: go-helloworld
template:
metadata:
labels:
app: go-helloworld
spec:
containers:
- name: go-helloworld
image: alagesann/go-helloworld:1.0.0
ports:
- containerPort: 80
Verify
➜ ~ kubectl apply -f dep.yaml
deployment.apps/go-helloworld created
➜ ~ kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
go-helloworld 2/2 2 2 26s
Create ClusterIP Service, svc.yaml
Lets create a service of type clusterip to expose the above deployment. Also ingress controller will redirect external service call to internal clusterip service and then to deployments.
apiVersion: v1
kind: Service
metadata:
name: go-helloworld-service
labels:
app: go-helloworld
spec:
selector:
app: go-helloworld
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
kubectl apply -f svc.yaml
➜ ~ k get deploy,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/go-helloworld 2/2 2 2 5m35s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/go-helloworld-service ClusterIP 10.43.85.59 <none> 80/TCP 4d
Test Service is reachable inside the cluster
➜ ~ kubectl run -it --image=curlimages/curl curly -- /bin/sh
If you don't see a command prompt, try pressing enter.
~ $ curl http://go-helloworld-service/
Hello, World!
Welcome to our Go microservice!
Exposing the Service via Ingress
Create below ingress resource to expose clusterip service to external access. IngressClassName is important to autoconfigure this ingress resource with the nginx ingress controller installed in the earlier step. This ingress resource now will expose the service via a common domain called “example.com”.
# go-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: go-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: "example.com"
http:
paths:
- pathType: Prefix
path: "/go"
backend:
service:
name: go-helloworld-service
port:
number: 80
kubectl apply -f go-ingress.yaml
ingress.networking.k8s.io/go-ingress created
➜ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
go-ingress nginx example.com 192.168.5.15 80 13h
Now for the DNS resolution properly work on the domain name “example.com”, create following entry in the /etc/hosts file with the nginx-ingress service’s internal_ip
# /etc/hosts
10.102.185.119 example.com
now hit the service :
curl http://example.com/go
Hello, World!
Welcome to our Go microservice!
Congratulations, you have exposed a Go microservice on an ingress controller successfully.