Where the Problem Started
Creating a separate load balancer for every exposed Kubernetes service looks simple, but cost and quotas can grow quickly. Consolidating ingress through a gateway can reduce cost, but routing, certificates, and blast radius need more care. This post records an Istio Gateway approach to reducing load balancer cost.
When managing traffic for Kubernetes running on EKS, the original approach is to place one ALB per module to route traffic to each service and manage that traffic separately.
Implementation Path
ingress:
enabled: true
className: "alb"
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/subnets: {서브넷명시}
alb.ingress.kubernetes.io/certificate-arn: {arn명시}
alb.ingress.kubernetes.io/ssl-redirect: '443'
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
For example, an ALB-backed ingress often looks like this.
In the values file, you declare the arn, the certificate for SSL, ports, and so on, and launch an ALB on AWS.
The problem here is that every load balancer, or ALB, is charged hourly, so as the number of modules grows, the cost also increases. Even if a module has very little traffic, such as a development server or release server, if the LCU is not different, the load balancer usage cost is the same, so it is hard to shake the feeling that you are taking a large loss.
An alternative is an Istio Gateway + VirtualService pattern. By declaring one shared Gateway, traffic for multiple modules can be managed through one NLB.
Declare a global Gateway first. This example assumes Helm-generated values.
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: global-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- {host1}
- {host2}
tls:
httpsRedirect: true
- port:
number: 443
name: http-443
protocol: HTTP
hosts:
- {host1}
- {host2}
Put every host used by the modules into the hosts section.
Each module then gets a VirtualService.
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: {module-name}
spec:
hosts:
- {host1}
- {host2}
gateways:
- istio-system/global-gateway
http:
- match:
- headers:
Host:
exact: {host1}
route:
- destination:
host: {app1}
port:
number: 8080
- match:
- headers:
Host:
exact: {host2}
route:
- destination:
host: {app2}
port:
number: 8080
The load balancer also needs the certificates for each host. Because the NLB is shared, every required certificate ARN must be attached to the shared Gateway/NLB path.
{
"metadata": {
"annotations": {
"service.beta.kubernetes.io/aws-load-balancer-scheme": "internet-facing",
"service.beta.kubernetes.io/aws-load-balancer-ssl-cert": "{arn1}, {arn2} ....",
"service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "http",
"service.beta.kubernetes.io/aws-load-balancer-ssl-ports": "443"
}
}
}
Patch this with the following command.
kubectl patch svc istio-ingressgateway -n istio-system --patch-file {json명}
Ingress Takeaway
A gateway-based structure is risky if chosen only for cost reduction. The shared ingress plane, certificate and DNS ownership, quota limits, and target limits all need to be checked together. Infrastructure cost optimization is always a trade with operational complexity.
The main risks are straightforward.
The shared ingress path can become an SPOF. If the NLB or ingress gateway path fails, traffic for every module behind it is affected. Replicas and operational checks are mandatory.
Second, quota limits matter. Check the official documentation, https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-limits.html, and verify target limits, certificate limits, connection limits, and related constraints before consolidating too much traffic.