Deploying and Exposing Go Apps with Kubernetes Ingress, Part 1
Kubernetes is the go-to tool for managing containerized apps. While Services expose apps, they lack advanced traffic control. Ingress fills this gap with features like path-based routing, SSL, and load balancing. In this tutorial, we’ll explore Ingress and build a hands-on project using Go, Docker, and Kubernetes. What is Kubernetes Ingress In Kubernetes, Ingress is an API object that manages external access to services, typically for HTTP/HTTPS traffic. It functions as a reverse proxy, load balancer, and traffic router, defining rules to direct requests to different services based on hostnames, paths, or other HTTP parameters. Unlike Kubernetes Services, which expose applications internally or externally using simple port mappings, Ingress offers a more advanced layer for managing web traffic, making it ideal for microservices architectures and production environments. Why We Need Ingress: Services vs Ingress Feature Services Ingress Exposure Expose pods internally or externally via ports (e.g., ClusterIP for internal use, LoadBalancer for external). Adds HTTP/HTTPS routing, SSL termination, and load balancing. Routing Capabilities Limited to port-based routing with no HTTP-specific features. Enables path-based and host-based routing, offering a more sophisticated traffic management solution. Dependencies No additional components are required beyond the Service definition. Requires an Ingress controller (e.g., NGINX) to implement the defined rules. Evolution of Ingress in Kubernetes Ingress in Kubernetes was introduced to handle the growing need for better traffic control in container-based apps, especially those using HTTP/HTTPS. At first, Kubernetes only had Services for exposing applications, but these were basic and not enough for modern web apps that needed smarter routing and security. To solve this, Ingress was added in Kubernetes v1.1 (2015) as an experimental feature. It became more stable over time and was marked as stable in v1.19 (2020), showing how important it had become for real-world use. Ingress made it easier to manage traffic based on URLs, use SSL/TLS, and combine access points to save costs. Ingress works through a special API and relies on Ingress controllers like NGINX or Traefik. These controllers read Ingress rules and handle routing and load balancing. This gave developers more power and flexibility than just using LoadBalancer services, making Kubernetes better for running web apps at scale. Project Overview In this section, we’ll build and deploy two Go-based services (API and Web) using Docker and Kubernetes. We’ll use Kubernetes Ingress to route external HTTP traffic to these services based on URL paths (/api and /web). The project demonstrates how Ingress simplifies external access and routing in a microservices architecture, tested on a local Kind cluster. Project Structure We will build two services using Go: An API service that responds to HTTP requests on the /api path. A Web service that responds to HTTP requests on the /web path. Then, we’ll containerize these services with Docker, set up a Kubernetes cluster using Kind, deploy the services, and configure Ingress for routing. Tree Structure Here’s the directory structure of your project: ingress-same-route/ ├── api-service/ │ ├── Dockerfile │ └── main.go ├── web-service/ │ ├── Dockerfile │ └── main.go └── k8s/ ├── api-deployment.yaml ├── api-service.yaml ├── web-deployment.yaml ├── web-service.yaml ├── go-app-ingress.yaml └── kind-cluster-config-with-ingress.yaml API Service To initialize the Go module for this service, run: cd ~/k8s-learning/ingress/ingress-same-route/api-service go mod init ingress-api-service-same This command sets up the module for dependency management, using Go 1.24.2 or later. Developing the API with Go The API service is a simple Go application that responds to HTTP requests on the /api path. main.go: package main import ( "fmt" "net/http" ) func apiHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "API Service") } func main() { http.HandleFunc("/api", apiHandler) fmt.Println("API Service started at :8080") http.ListenAndServe(":8080", nil) } Dockerizing the API Service Containerize the API service using the provided Dockerfile: Dockerfile: # Use the official Golang image to build the app FROM golang:1.24-alpine as builder WORKDIR /app COPY go.mod ./ RUN go mod tidy COPY . . RUN go build -o api-service . FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/api-service . EXPOSE 8080 CMD ["./api-service"] Build and push the image: cd ~/k8s-learning/ingress/ingress-same-route/api-service d

Kubernetes is the go-to tool for managing containerized apps. While Services expose apps, they lack advanced traffic control. Ingress fills this gap with features like path-based routing, SSL, and load balancing. In this tutorial, we’ll explore Ingress and build a hands-on project using Go, Docker, and Kubernetes.
What is Kubernetes Ingress
In Kubernetes, Ingress is an API object that manages external access to services, typically for HTTP/HTTPS traffic. It functions as a reverse proxy, load balancer, and traffic router, defining rules to direct requests to different services based on hostnames, paths, or other HTTP parameters. Unlike Kubernetes Services, which expose applications internally or externally using simple port mappings, Ingress offers a more advanced layer for managing web traffic, making it ideal for microservices architectures and production environments.
Why We Need Ingress: Services vs Ingress
Feature | Services | Ingress |
---|---|---|
Exposure | Expose pods internally or externally via ports (e.g., ClusterIP for internal use, LoadBalancer for external). | Adds HTTP/HTTPS routing, SSL termination, and load balancing. |
Routing Capabilities | Limited to port-based routing with no HTTP-specific features. | Enables path-based and host-based routing, offering a more sophisticated traffic management solution. |
Dependencies | No additional components are required beyond the Service definition. | Requires an Ingress controller (e.g., NGINX) to implement the defined rules. |
Evolution of Ingress in Kubernetes
Ingress in Kubernetes was introduced to handle the growing need for better traffic control in container-based apps, especially those using HTTP/HTTPS. At first, Kubernetes only had Services for exposing applications, but these were basic and not enough for modern web apps that needed smarter routing and security.
To solve this, Ingress was added in Kubernetes v1.1 (2015) as an experimental feature. It became more stable over time and was marked as stable in v1.19 (2020), showing how important it had become for real-world use. Ingress made it easier to manage traffic based on URLs, use SSL/TLS, and combine access points to save costs.
Ingress works through a special API and relies on Ingress controllers like NGINX or Traefik. These controllers read Ingress rules and handle routing and load balancing. This gave developers more power and flexibility than just using LoadBalancer services, making Kubernetes better for running web apps at scale.
Project Overview
In this section, we’ll build and deploy two Go-based services (API and Web) using Docker and Kubernetes. We’ll use Kubernetes Ingress to route external HTTP traffic to these services based on URL paths (/api
and /web
). The project demonstrates how Ingress simplifies external access and routing in a microservices architecture, tested on a local Kind cluster.
Project Structure
We will build two services using Go:
- An API service that responds to HTTP requests on the
/api
path. - A Web service that responds to HTTP requests on the
/web
path.
Then, we’ll containerize these services with Docker, set up a Kubernetes cluster using Kind, deploy the services, and configure Ingress for routing.
Tree Structure
Here’s the directory structure of your project:
ingress-same-route/
├── api-service/
│ ├── Dockerfile
│ └── main.go
├── web-service/
│ ├── Dockerfile
│ └── main.go
└── k8s/
├── api-deployment.yaml
├── api-service.yaml
├── web-deployment.yaml
├── web-service.yaml
├── go-app-ingress.yaml
└── kind-cluster-config-with-ingress.yaml
API Service
To initialize the Go module for this service, run:
cd ~/k8s-learning/ingress/ingress-same-route/api-service
go mod init ingress-api-service-same
This command sets up the module for dependency management, using Go 1.24.2 or later.
Developing the API with Go
The API service is a simple Go application that responds to HTTP requests on the /api
path.
main.go
:
package main
import (
"fmt"
"net/http"
)
func apiHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "API Service
")
}
func main() {
http.HandleFunc("/api", apiHandler)
fmt.Println("API Service started at :8080")
http.ListenAndServe(":8080", nil)
}
Dockerizing the API Service
Containerize the API service using the provided Dockerfile
:
Dockerfile
:
# Use the official Golang image to build the app
FROM golang:1.24-alpine as builder
WORKDIR /app
COPY go.mod ./
RUN go mod tidy
COPY . .
RUN go build -o api-service .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/api-service .
EXPOSE 8080
CMD ["./api-service"]
Build and push the image:
cd ~/k8s-learning/ingress/ingress-same-route/api-service
docker build -t olymahmudmugdho/ingress-api-service-same:latest .
docker push olymahmudmugdho/ingress-api-service-same:latest
Web Service
To initialize the Go module for this service, run:
cd ~/k8s-learning/ingress/ingress-same-route/web-service
go mod init ingress-web-service-same
This sets up the module for dependency management, using Go 1.24.2 or later.
Developing the Web Service with Go
The Web service responds to the /web
path.
main.go
:
package main
import (
"fmt"
"net/http"
)
func webHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Web Service
")
}
func main() {
http.HandleFunc("/web", webHandler)
fmt.Println("Web Service started at :8080")
http.ListenAndServe(":8080", nil)
}
Dockerizing the Web Service
Use a similar Dockerfile
, adjusted for the Web service:
Dockerfile
:
# Use the official Golang image to build the app
FROM golang:1.24-alpine as builder
WORKDIR /app
COPY go.mod ./
RUN go mod tidy
COPY . .
RUN go build -o web-service .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/web-service .
EXPOSE 8080
CMD ["./web-service"]
Build and push:
cd ~/k8s-learning/ingress/ingress-same-route/web-service
docker build -t olymahmudmugdho/ingress-web-service-same:latest .
docker push olymahmudmugdho/ingress-web-service-same:latest
Setting Up Kubernetes with KIND
Set up a local Kubernetes cluster using Kind.
kind-cluster-config-with-ingress.yaml
:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: kind-ingress
nodes:
- role: control-plane
image: kindest/node:v1.31.2
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
image: kindest/node:v1.31.2
- role: worker
image: kindest/node:v1.31.2
Create the cluster:
kind create cluster --config kind-cluster-config-with-ingress.yaml
Verify:
kubectl get nodes
Installing the Ingress Controller
Deploy the NGINX Ingress controller:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.1/deploy/static/provider/kind/deploy.yaml
Check status:
kubectl get pods -n ingress-nginx
API Deployment Configuration
api-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-deployment
spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: olymahmudmugdho/ingress-api-service-same:latest
ports:
- containerPort: 8080
Explanation: Creates a Deployment for the API service, ensuring one replica runs with the specified Docker image, exposing port 8080.
Apply:
kubectl apply -f k8s/api-deployment.yaml
API Service Definition
api-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: api-service
spec:
selector:
app: api
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
Explanation: Exposes the API pods internally via a ClusterIP Service, mapping external port 80 to the container’s port 8080.
Apply:
kubectl apply -f k8s/api-service.yaml
Web Deployment Configuration
web-deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-deployment
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: olymahmudmugdho/ingress-web-service-same:latest
ports:
- containerPort: 8080
Explanation: Similar to the API Deployment but for the Web service.
Web Service Definition
web-service.yaml
:
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
Explanation: Exposes the Web service internally, mirroring the API Service setup.
Creating Ingress Resource (go-app-ingress.yaml)
go-app-ingress.yaml
:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: go-app-ingress
spec:
rules:
- host: localhost
http:
paths:
- pathType: Prefix
path: /api
backend:
service:
name: api-service
port:
number: 80
- pathType: Prefix
path: /web
backend:
service:
name: web-service
port:
number: 80
Detailed Explanation:
-
Metadata: The
name: go-app-ingress
uniquely identifies this Ingress resource within the cluster, making it easy to reference and manage. -
Spec.Rules: Defines the routing rules. Here, we specify that all traffic to
host: localhost
should be handled by HTTP rules. -
Host: Set to
localhost
, meaning this Ingress applies to requests sent tolocalhost
. In a production environment, this could be a domain name (e.g.,example.com
). -
HTTP.Paths: Contains two path rules:
-
/api
: Matches any URL starting with/api
(due topathType: Prefix
). Traffic is routed to theapi-service
on port 80. -
/web
: Matches any URL starting with/web
. Traffic is routed to theweb-service
on port 80.
-
-
Backend: Specifies the target Service (
api-service
orweb-service
) and the port (80
) to forward traffic to. This ensures requests are directed to the correct Kubernetes Service, which then routes to the appropriate pods. -
pathType: Prefix: Indicates that the path is treated as a prefix. For example,
/api/health
would also match and be routed toapi-service
. Other options includeExact
(exact match) orImplementationSpecific
.
This configuration allows a single entry point (e.g., localhost
) to handle multiple services based on URL paths, demonstrating Ingress’s power in managing complex traffic patterns.
Apply:
kubectl apply -f k8s/go-app-ingress.yaml
Verifying Ingress Functionality
Ensure all components are running:
kubectl get deployments
kubectl get services
kubectl get ingress
kubectl describe ingress go-app-ingress
kubectl get pods -n ingress-nginx
Testing with cURL
Test the routing:
curl http://localhost/api
# Expected output: "API Service
"
curl http://localhost/web
# Expected output: "Web Service
"
If tests fail, troubleshoot:
- Check Ingress controller logs:
kubectl logs -n ingress-nginx
. - Ensure
localhost
resolves correctly in/etc/hosts
or network settings.
Thanks for reading.