Load Balancers allow you to expose your Kubernetes services to external traffic. This guide covers creating and configuring LoadBalancer services in Krutrim Kubernetes Service using OpenStack Cloud Controller Manager (OCCM).
What is a LoadBalancer Service?
A LoadBalancer service automatically provisions a cloud load balancer that routes external traffic to your application pods.
Benefits:
External Access: Expose services to the internet
Automatic Provisioning: Load balancer created automatically
Health Checking: Built-in health monitoring
High Availability: Traffic distributed across pods
Important: For Layer 7 (L7) features like HTTP path-based routing, host-based routing, SSL/TLS termination, and HTTP header manipulation, always use Kubernetes Ingress controllers (like HAProxy, Kong, NGINX, or Traefik) behind a LoadBalancer service. Do not rely on the LoadBalancer for L7 functionality — use it as a simple Layer 4 entry point to your Ingress controller.
How LoadBalancers Work in KKS
Krutrim Kubernetes Service uses OpenStack Cloud Controller Manager (OCCM) to manage LoadBalancer services:
┌─────────────────────────────────────────────────────────┐
│ Kubernetes LoadBalancer Service │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ OpenStack Cloud Controller Manager (OCCM) │
│ (Managed by Krutrim platform) │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ OpenStack Load Balancer (Octavia) │
│ Created in your specified subnet │
└─────────────────┬───────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ External LoadBalancer: Floating IP (public internet) │
│ Internal LoadBalancer: Subnet IP (private only) │
└─────────────────────────────────────────────────────────┘
IP Address Allocation
External LoadBalancers (Default):
Receive a VIP (Virtual IP) from the cluster subnet
Also get a Floating IP from the public IP pool (mapped to the VIP)
Accessible from the internet via the floating IP
Consume 1 IP from the cluster subnet
Internal LoadBalancers:
Receive a VIP (Virtual IP) from the cluster subnet
Only accessible within your VPC/private network
Consume 1 IP from the cluster subnet
Example: Your cluster subnet: 10.0.1.0/24 (for nodes, pods, and LoadBalancers)
10 nodes use: 10 IPs
5 external LoadBalancers: 5 IPs (each also gets a floating IP)
3 internal LoadBalancers: 3 IPs
Total used: 18 IPs from cluster subnet
Floating IPs (for external LoadBalancers only):
Allocated from public IP pool
Mapped to LoadBalancer VIPs
5 floating IPs for the 5 external LoadBalancers
Key Points:
LoadBalancers use IPs from your cluster subnet
Each LoadBalancer consumes 1 IP from the cluster subnet (for both internal and external)
External LoadBalancers get an additional floating IP for internet access (from public IP pool)
Plan your cluster subnet size to accommodate nodes, pods, and LoadBalancers
Layer 4 (L4) vs Layer 7 (L7) Load Balancing
Understanding the Recommended Architecture
Important: For any Layer 7 (L7) features, always use a Kubernetes Ingress controller - do not rely on the LoadBalancer for L7 functionality.
Layer 4 (L4) - Use LoadBalancer:
Use LoadBalancer as a simple entry point - based on IP address and TCP/UDP port only
Keep it simple: port forwarding to your Ingress controller or service
Fast and efficient for TCP/UDP traffic
Suitable for any TCP/UDP protocol (HTTP, HTTPS, database connections, etc.)
Layer 7 (L7) - Use Ingress Controllers:
Do not rely on LoadBalancer for L7 features
Use Ingress controllers for HTTP-based routing (URLs, headers, cookies)
Flexibility: Easy to add/remove services without new LoadBalancers
Control: Full control over L7 behavior instead of relying on platform defaults
Standard: Kubernetes-native API with wide ecosystem support
Key Recommendation: Always use LoadBalancer as a simple L4 entry point and let your Ingress controller handle all L7 functionality. Do not depend on the LoadBalancer for HTTP routing, SSL termination, or other L7 features.
Creating a LoadBalancer Service
Basic LoadBalancer Service
Create a simple LoadBalancer service:
Apply the service:
Check LoadBalancer status:
Timeline: 2-5 minutes for LoadBalancer to be provisioned
For internal LoadBalancer:
Understanding Service Ports
Port Mapping Flow:
LoadBalancer Annotations
OCCM supports extensive configuration through annotations. Below are commonly used annotations for Krutrim Cloud.
Note: Annotations that require OpenStack resource IDs (like floating-network-id, subnet-id, network-id, port-id, member-subnet-id) are managed by the Krutrim platform and should not be specified by users. The platform automatically configures the appropriate network resources.
Basic Annotations
Internal vs External LoadBalancer
External LoadBalancer (default):
Gets a VIP from the cluster subnet
Also gets a Floating IP from public IP pool (mapped to the VIP)
Accessible from the internet via floating IP
Consumes 1 IP from cluster subnet
Internal LoadBalancer (private network only):
Gets a VIP from the cluster subnet
Only accessible within your VPC
Consumes 1 IP from cluster subnet
Connection and Timeout Settings
Connection Limits
Max connections per LoadBalancer:
Default: -1 (unlimited)
Timeout Configuration
Member connection timeout (backend connection timeout):
Member data timeout (backend read timeout):
Client data timeout (idle connection timeout):
TCP inspection timeout:
Values: Milliseconds (e.g., 5000 = 5 seconds)
Load Balancing Algorithm
Choose how traffic is distributed:
1
ROUND_ROBIN (default)
Distributes requests evenly
Simple and effective
Good for most use cases
2
LEAST_CONNECTIONS
Sends to pod with fewest connections
Good for long-lived connections
Better for uneven request loads
3
SOURCE_IP
Same client → same backend
Session persistence
Good for stateful applications
4
SOURCE_IP_PORT
Same client IP and port → same backend
Enhanced session persistence
Example:
Health Check Configuration
Health monitors check backend pod health and automatically remove unhealthy pods from the load balancer pool.
Enable Health Monitor:
Default: If not specified, platform default behavior applies
Important: Health monitors are required for services with externalTrafficPolicy: Local
Health Check Parameters:
Parameters:
enable-health-monitor: Enable/disable health monitoring (default: platform default)
health-monitor-delay: Seconds between health checks (default: 10)
health-monitor-timeout: Health check timeout in seconds (default: 5)
health-monitor-max-retries: Consecutive successes to mark member healthy (default: 3)
health-monitor-max-retries-down: Consecutive failures to mark member down (default: 3)
Important Constraint: health-monitor-timeout must be less than health-monitor-delay
Health Check Types
TCP Health Check (default for TCP services):
Simply checks if TCP connection can be established
Good for most TCP services
HTTP Health Check (automatic for HTTP listeners):
Performs HTTP GET request
Checks for HTTP 200 response
Automatically used when X-Forwarded-For is enabled
X-Forwarded-For Header
Inserts client IP into HTTP headers for HTTP services.
Effects:
Forces creation of HTTP listener (instead of TCP)
Adds X-Forwarded-For header with client IP
Backend can read original client IP from header
Restrict Access to Specific IP Ranges
Restrict which client IPs can access your LoadBalancer service using the loadBalancerSourceRanges field in the Service spec:
Format: List of CIDR blocks in the Service spec
Requirements:
OpenStack Octavia API version >= v2.12
Ignored if Octavia doesn't support this feature
This field supports updates - you can modify it after Service creation
Additional Advanced Annotations
Set Custom Hostname:
Use case: Enable PROXY protocol with custom DNS
Filter Target Nodes:
Format: Comma-separated key=value pairs
All specified labels must match
Can specify key without value to check only for key existence
If not specified, all nodes are eligible targets
Common LoadBalancer Configurations
Example 1: Public Web Application (External with Health Checks)
Example 2: Internal Microservice (Private Network)
Example 3: Session-based Application (Source IP Affinity)
Example 4: High-Performance API (Connection Limits)
Example 5: HTTP Service with X-Forwarded-For
Effect: Creates HTTP listener that adds X-Forwarded-For header with client IP
Complete Annotation Reference
User-Configurable Annotations
The following annotations can be used by users to configure LoadBalancer behavior:
Converting from LoadBalancer to NodePort or ClusterIP
When you change a Service from type: LoadBalancer to type: NodePort or type: ClusterIP, the underlying OpenStack load balancer will be automatically deleted.
Important: If you want to convert the Service back to type: LoadBalancer, you must delete the following annotations from the Service:
Why is this necessary?
These annotations are automatically added by OCCM when a LoadBalancer is created
They point to the old (now deleted) LoadBalancer
If present when converting back to LoadBalancer type, OCCM will try to reuse the non-existent LoadBalancer
This will cause the Service to fail provisioning
Correct Procedure:
1
1. Change to NodePort/ClusterIP (LoadBalancer will be deleted)
Example:
2
2. Remove the annotations before converting back
Example:
3
3. Change back to LoadBalancer (new LoadBalancer will be created)
Example:
Alternative: Delete and recreate the Service:
Note: A new LoadBalancer will be created with a new IP address. Update your DNS records and client configurations accordingly.
Troubleshooting LoadBalancers
LoadBalancer Stuck in Pending
Symptoms:
Service external IP shows
LoadBalancer not created after 5+ minutes
Diagnosis Steps:
1
Invalid annotations
Review Service YAML for typos in annotation names
Verify annotation values are correct format
Remove unsupported annotations
Check service events for validation errors
2
Network or platform configuration issues
Service events will show specific error details
Contact Krutrim support with the event messages for assistance
If LoadBalancer remains in pending state:
Capture the output of kubectl describe service
Note any error messages in the Events section
Contact Krutrim support with the service description and events
LoadBalancer Created but Not Accessible
Symptoms:
External IP assigned
Cannot access service from internet or internal network
Diagnosis Steps:
Services with externalTrafficPolicy: Local
Special Requirements:
Health monitors are required for externalTrafficPolicy: Local
Without health monitor, traffic routing will fail
Best Practices
✅ Do's
Always Configure Health Checks:
Essential for production services
Required for externalTrafficPolicy: Local
Set realistic timeouts based on application behavior
Set Appropriate Timeouts:
Configure client timeouts for long-running requests
Consider application response times
Plan for slow clients
Use Internal LoadBalancers for Internal Services:
Reduce costs (no floating IP)
Better security (not exposed to internet)
Faster response times (no public network hop)
Restrict Access with Source Ranges:
Limit who can access your services
Use specific CIDRs instead of 0.0.0.0/0
Choose the Right Load Balancing Algorithm:
ROUND_ROBIN: Default, good for stateless apps
LEAST_CONNECTIONS: Better for uneven request loads
SOURCE_IP: Required for session affinity
Monitor and Log:
Monitor LoadBalancer health and performance
Check OCCM logs for issues
Set up alerts for LoadBalancer failures
Use Descriptive Names:
Name services clearly
Add labels for organization
Document LoadBalancer purpose
❌ Don'ts
Don't Over-Provision LoadBalancers:
Each LoadBalancer has a cost and consumes resources
Use Kubernetes Ingress controllers (NGINX, HAProxy, Kong, Traefik) for L7/HTTP routing
Treat LoadBalancers as L4 (transport layer) - use Ingress for L7 (application layer) features
One Ingress controller with one LoadBalancer can route to many services
Avoid creating separate LoadBalancers for each HTTP service
Don't Rely on LoadBalancers for L7 Features:
Always use Ingress controllers for L7 functionality
LoadBalancer should be a simple L4 entry point only
For path-based routing, host-based routing, SSL termination → use Ingress controllers
Don't depend on LoadBalancer for HTTP routing, header manipulation, or SSL termination
Don't Ignore Health Check Configuration:
Default values may not suit your application
Test health check behavior before production
Monitor health check failures
Required for externalTrafficPolicy: Local
Don't Use Unrealistic Timeouts:
Too short → healthy pods marked as down
Too long → slow failure detection
Consider network latency
Allow for application warmup time
Don't Expose Everything Externally:
Use internal LoadBalancers for internal-only services
External exposure increases attack surface
Follow principle of least privilege
Don't Modify Platform-Managed Annotations:
Don't manually edit load-balancer-id
Don't set OpenStack resource IDs (network-id, subnet-id, etc.)
Platform manages these automatically
Modifying can break LoadBalancer functionality
Don't Forget About Costs:
Floating IPs may have costs
LoadBalancers have ongoing costs
Clean up unused LoadBalancers
Use Ingress controllers to consolidate services behind one LoadBalancer
# Wait for external IP to be assigned
kubectl get service my-web-app -w
# Expected output for external LoadBalancer:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-web-app LoadBalancer 10.100.50.10 <pending> 80:32000/TCP 10s
my-web-app LoadBalancer 10.100.50.10 203.0.113.45 80:32000/TCP 2m
↑
Floating IP (public internet access)
# Internal LoadBalancer will show a private IP
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
internal-service LoadBalancer 10.100.50.20 10.200.5.10 80:32001/TCP 2m
↑
VIP (private network only)
ports:
- name: http
protocol: TCP
port: 80 # External port (LoadBalancer listens on)
targetPort: 8080 # Pod port (application listens on)
nodePort: 32000 # Node port (automatically assigned)
kubectl patch service my-service -p '{"spec":{"type":"NodePort"}}'
kubectl annotate service my-service \
loadbalancer.openstack.org/load-balancer-address- \
loadbalancer.openstack.org/load-balancer-id-
kubectl patch service my-service -p '{"spec":{"type":"LoadBalancer"}}'
# Export current service configuration
kubectl get service my-service -o yaml > service-backup.yaml
# Edit service-backup.yaml:
# - Change type to LoadBalancer
# - Remove load-balancer-address and load-balancer-id annotations
# Delete old service
kubectl delete service my-service
# Create new service
kubectl apply -f service-backup.yaml
# Check the Service events for error messages
kubectl describe service <service-name>
# Look for Events section at the bottom of the output
# Events will show specific error messages about provisioning failures
# 1. Check pod status
kubectl get pods -l app=<your-app>
# 2. Check service endpoints
kubectl get endpoints <service-name>
# 3. Test from within cluster
kubectl run -it --rm debug --image=busybox --restart=Never -- sh
wget -O- http://<service-name>.<namespace>.svc.cluster.local
# 4. Check service configuration
kubectl get service <service-name> -o yaml