In this workshop, you will learn the mechanics behind Kubernetes Ingress Controllers (L7 routing, load balancing, service discovery) using Traefik, a cloud-native edge router.
💡 Glossary: Please refer to Reverse Proxy, Ingress, or Service Discovery in the Glossary for technical terms used in this workshop.
By completing this workshop, you will be able to:
- Understand basic L7 reverse proxy functions (path-based/host-based routing)
- Configure load balancing for multiple backend services
- Process requests using middleware (Basic Auth, header modification)
- Understand service discovery mechanisms that detect dynamic container changes
With traditional proxies like Nginx, you had to rewrite configuration files (nginx.conf) and reload the service every time backend servers were added or removed.
- Configuration changes required whenever container IPs change
- Risk of downtime and operational errors during updates
- Increased management cost in microservices environments
- Service Discovery: Watches Podman or Kubernetes API to automatically update configuration
- Dynamic Configuration: Applies routing rules immediately without restarts
- Middleware: Applies features like authentication and rate limiting as plugins
We will build dynamic routing based on container labels using Podman's Docker-compatible API provider. Traefik watches the Podman socket and immediately adds containers with labels to routing as they start.
graph LR
Client["Client (curl/Browser)"]
Traefik[Traefik Proxy]
subgraph Backends [Backend Containers]
App1[Whoami App A]
App2[Whoami App B]
App3[Nginx App]
end
Client -- "Host: app.local" --> Traefik
Client -- "Path: /api" --> Traefik
Traefik -- "Load Balancing" --> App1 & App2
Traefik -- "Routing" --> App3
Traefik -.->|Watch Events| PodmanSocket((Podman Socket))
infra/assets/traefik/
├── docker-compose.yml # Traefik and backend definition
└── Makefile # Operations commands
Create a docker-compose.yml with the following content. We will use the lightweight traefik/whoami image (an app that simply returns HTTP request info) as the backend.
version: "3"
services:
# Traefik Reverse Proxy
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080" # Dashboard
volumes:
- /run/podman/podman.sock:/var/run/docker.sock:ro
# Backend A (Whoami)
whoami-a:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
# Backend B (Whoami - LB as same service)
whoami-b:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.services.whoami.loadbalancer.server.port=80"podman compose up -d- Confirmed
traefik,whoami-a, andwhoami-bare running viapodman ps. - Confirmed Traefik Dashboard is accessible at
http://localhost:8080. - Confirmed the
whoamiHTTP Router is visible in the dashboard.
Note: This repository includes
infra/assets/traefikas a sample directory. Review and adjust the files as needed while following this guide.
Access http://localhost:8080 in your browser.
Verify that Traefik has automatically detected the services running on Podman (whoami-a, whoami-b). The key point is that they are recognized without writing a configuration file.
Send requests to whoami.localhost and confirm that Traefik distributes requests between the two containers.
# 1st request
curl -H "Host: whoami.localhost" http://localhost
# Output: Hostname: <Container-ID-A>
# 2nd request
curl -H "Host: whoami.localhost" http://localhost
# Output: Hostname: <Container-ID-B>Check Point: The output Hostname is the container ID. If a different ID is displayed for each request, load balancing is successful.
Note: Just by launching multiple containers with the same Host rule in docker-compose.yml, Traefik automatically handles load balancing.
Add a new Nginx container and make it accessible at the /nginx path. This can be done dynamically via podman run without editing docker-compose.yml.
podman run -d --name nginx-demo \
--label "traefik.enable=true" \
--label "traefik.http.routers.nginx.rule=Host(`localhost`) && PathPrefix(`/nginx`)" \
--label "traefik.http.services.nginx.loadbalancer.server.port=80" \
nginx:alpineVerify:
curl http://localhost/nginx- Confirmed different container IDs are returned by
curl -H "Host: whoami.localhost" http://localhost. - Confirmed the
nginx-demoservice immediately appears in the dashboard after starting it viapodman run. - Confirmed
/nginxreturns the Nginx welcome page or response.
Try Middleware, a powerful feature of Traefik. Apply Basic Auth to specific routes.
# Generate password (user:password)
htpasswd -nb user password
# user:$apr1$Sr...By adding labels and restarting the container, you can add an authentication layer without changing application code.
Kubernetes Ingress resources are standardized definitions of the "routing rules" configured in this workshop. Traefik acts as an Ingress Controller, reading Ingress resources from the K8s API and executing similar routing.
Traefik mainly handles "Edge (External to Internal)" traffic, while controlling internal service-to-service communication (East-West) is the role of the next step, "Service Mesh (Envoy)".
podman compose down
podman rm -f nginx-demo- HTTPS: Automatic certificate issuance with Let's Encrypt
- Deploy to Kubernetes: Installing Traefik using Helm Charts
- Service Mesh: Advanced traffic control using Envoy (Next Workshop)
Symptoms: Accessing http://localhost:8080 returns a 404 or connection error.
Causes and Solutions:
- API Not Enabled: Ensure
--api.insecure=trueis present in the Traefikcommandsection. - Port Mapping: Verify port
8080:8080is correctly mapped indocker-compose.yml.
Symptoms: whoami routers do not appear in the dashboard.
Causes and Solutions:
- Socket Permissions: Verify the
podman.sockpath and ensures it's mounted withro(read-only). - Missing Labels: Ensure the
traefik.enable=truelabel is present on your backend containers.
- If using Podman Desktop, the socket path might differ from
/var/run/docker.sock. Check yourDOCKER_HOSTenvironment variable if connection fails.
- Ensure localhost forwarding is enabled between WSL2 and Windows. If the browser fails to connect, try using
curldirectly within your WSL terminal.