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

May 10, 2025 - 13:55
 0
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
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 to localhost. 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 to pathType: Prefix). Traffic is routed to the api-service on port 80.
    • /web: Matches any URL starting with /web. Traffic is routed to the web-service on port 80.
  • Backend: Specifies the target Service (api-service or web-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 to api-service. Other options include Exact (exact match) or ImplementationSpecific.

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.