Kubernetes Egress Gateway with Antrea
Antrea is an open source Kubernetes CNI based on Open vSwitch that provides advanced networking features including Egress Gateway - completely free and open source. This post explores Antrea’s egress capabilities as a true open source alternative.
Egress Gateway Series
This series covers Kubernetes egress gateway solutions:
- Part 1: Istio Ingress/Egress Gateway - Service mesh approach with mTLS and advanced traffic management
- Part 2: Cilium Egress Gateway - eBPF-based networking with Hubble observability
- Part 3: Antrea Egress Gateway - Open vSwitch CNI with ExternalNode support
- Part 4: Kube-OVN Egress Gateway - OVN-based CNI with Floating IP support
- Part 5: Monzo Egress Operator - AWS NAT Gateway automation via Kubernetes CRDs
- Part 6: Custom Envoy Proxy - Self-hosted L7 egress proxy with advanced routing
- Part 7: Squid Proxy on Kubernetes - Traditional HTTP proxy with caching and ACLs
- Part 8: Cloud NAT Solutions - AWS NAT Gateway, GCP Cloud NAT, Azure Firewall/NAT Gateway
- Part 9: Comparison & Recommendations - Decision matrix and use case guide
✓ All parts complete!
Why Antrea for Egress?
Unlike Calico (where egress gateway requires Enterprise), Antrea includes full egress gateway functionality in the open source version.
Antrea vs Calico for Egress
| Feature | Antrea (Open Source) | Calico (Open Source) | Calico Enterprise |
|---|---|---|---|
| Egress Gateway | ✅ Included | ❌ Not available | ✅ Available |
| ExternalNode | ✅ Supported | ❌ Not available | ✅ Available |
| IP Pool Management | ✅ Included | ⚠️ Limited | ✅ Full |
| Network Policy | ✅ Kubernetes NP + Extensions | ✅ Kubernetes NP | ✅ Enhanced NP |
| Encryption | ✅ IPsec/WireGuard | ⚠️ WireGuard only | ✅ WireGuard |
| Cost | Free | Free | Paid |
Antrea Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Pod A │ │ Pod B │ │ Pod C │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ OVS Bridge br-int │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Antrea Agent │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Egress IP Pool │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ Gateway Node │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────┐ │
│ │ External Network │ │
│ └───────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Key Components
| Component | Purpose |
|---|---|
| Antrea Controller | Centralized configuration management |
| Antrea Agent | Per-node networking (runs as DaemonSet) |
| OVS Bridge | Open vSwitch for packet forwarding |
| Egress IP Pool | Pool of external IPs for NAT |
| Gateway Node | Node that handles egress traffic |
Prerequisites
| Component | Version | Notes |
|---|---|---|
| Kubernetes | 1.21+ | Tested on 1.28, 1.29 |
| Antrea | 1.14+ | Egress GA since 1.10 |
| Open vSwitch | 2.13+ | Included with Antrea |
| Nodes | 2+ | At least one gateway node |
Verify Antrea Installation
# Check Antrea version
kubectl get pods -n kube-system -l app=antrea -o jsonpath='{.items[0].spec.containers[0].image}'
# Verify Antrea components
kubectl get pods -n kube-system -l app=antrea
# Expected output:
# NAME READY STATUS
# antrea-agent-xxxxx 2/2 Running
# antrea-controller-xxxxx 1/1 Running
# Check Antrea feature gates
kubectl get configmap antrea-config -n kube-system -o yaml | \
grep -A10 featureGates
Step 1: Enable Egress Feature
Check Current Configuration
# View Antrea configuration
kubectl get configmap antrea-config -n kube-system -o yaml
Enable Egress FeatureGate
Edit the Antrea ConfigMap or use Helm:
# Using Helm (recommended for new installations)
helm upgrade antrea antrea/antrea \
-n kube-system \
--set featureGates.Egress=true \
--set featureGates.ExternalNode=true \
--wait
Manual Configuration
# Edit Antrea ConfigMap
kubectl edit configmap antrea-config -n kube-system
# Add/modify featureGates:
apiVersion: v1
kind: ConfigMap
metadata:
name: antrea-config
namespace: kube-system
data:
antrea.yaml: |
featureGates:
Egress: true
ExternalNode: true
Verify Feature is Enabled
# Restart Antrea pods to apply changes
kubectl rollout restart daemonset antrea-agent -n kube-system
kubectl rollout restart deployment antrea-controller -n kube-system
# Wait for pods to be ready
kubectl wait --for=condition=Ready pods -l app=antrea -n kube-system --timeout=2m
# Verify Egress is enabled
kubectl exec -n kube-system antrea-agent-xxxxx -- antctl get featuregates | grep Egress
Step 2: Configure Egress IP Pool
Create an ExternalIPPool for egress IPs:
apiVersion: crd.antrea.io/v1beta1
kind: ExternalIPPool
metadata:
name: egress-pool-1
spec:
# Range of IPs for egress NAT
ipRanges:
- start: 192.168.100.10
end: 192.168.100.20
# Nodes that can serve as egress gateways
nodeSelector:
matchLabels:
node-role.kubernetes.io/egress-gateway: ""
# Optional: Auto-assign IPs from pool
autoAssign: true
Apply IP Pool
kubectl apply -f external-ippool.yaml
Verify IP Pool
# List external IP pools
kubectl get externalippool
# Get detailed status
kubectl describe externalippool egress-pool-1
# Check allocated IPs
kubectl get externalippool egress-pool-1 -o yaml
Step 3: Create Egress Resource
Define which traffic should use the egress gateway:
apiVersion: crd.antrea.io/v1beta1
kind: Egress
metadata:
name: egress-production
spec:
# Reference the IP pool
externalIPPool: egress-pool-1
# Select pods/namespaces that use this egress
appliedTo:
- namespaceSelector:
matchLabels:
name: production
podSelector:
matchLabels:
app: api-server
# Optional: Specific external destinations
# If not specified, all external traffic uses egress
destination:
- ipBlock:
cidr: 0.0.0.0/0
# Exclude internal networks
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
Apply Egress Resource
kubectl apply -f egress.yaml
Verify Egress Status
# List egress resources
kubectl get egress
# Get detailed status
kubectl describe egress egress-production
# Check which gateway node is assigned
kubectl get egress egress-production -o jsonpath='{.status.egressIP}'
kubectl get egress egress-production -o jsonpath='{.status.gatewayNode}'
Step 4: Label Gateway Nodes
Designate which node(s) will serve as egress gateway:
# Label a node as egress gateway
kubectl label node worker-node-2 \
node-role.kubernetes.io/egress-gateway=
# Verify label
kubectl get nodes -l node-role.kubernetes.io/egress-gateway=
High Availability Configuration
For HA, label multiple nodes:
# Label multiple gateway nodes
kubectl label node worker-node-2 \
node-role.kubernetes.io/egress-gateway=
kubectl label node worker-node-3 \
node-role.kubernetes.io/egress-gateway=
# Antrea will automatically failover if primary gateway fails
Step 5: Test Egress Gateway
Create Test Pod
# Create test namespace
kubectl create namespace test-egress
kubectl label namespace test-egress name=test-egress
# Deploy test pod
kubectl run test-pod --image=curlimages/curl -it --rm --restart=Never \
--namespace test-egress \
--labels app=test-app \
-- curl -s ifconfig.me
Create Egress for Test
apiVersion: crd.antrea.io/v1beta1
kind: Egress
metadata:
name: egress-test
spec:
externalIPPool: egress-pool-1
appliedTo:
- namespaceSelector:
matchLabels:
name: test-egress
podSelector:
matchLabels:
app: test-app
Verify Source IP
# From pod with egress
kubectl exec -it test-pod -n test-egress -- curl -s ifconfig.me
# Should show egress IP from pool (e.g., 192.168.100.10)
# From pod without egress
kubectl run normal-pod --image=curlimages/curl -it --rm --restart=Never \
-- curl -s ifconfig.me
# Should show node IP (not from egress pool)
Advanced Configuration
Multiple Egress Pools
Create different pools for different purposes:
# Production egress pool
apiVersion: crd.antrea.io/v1beta1
kind: ExternalIPPool
metadata:
name: egress-pool-production
spec:
ipRanges:
- start: 192.168.100.10
end: 192.168.100.20
nodeSelector:
matchLabels:
node-role.kubernetes.io/egress-gateway: ""
---
# Staging egress pool
apiVersion: crd.antrea.io/v1beta1
kind: ExternalIPPool
metadata:
name: egress-pool-staging
spec:
ipRanges:
- start: 192.168.100.30
end: 192.168.100.40
nodeSelector:
matchLabels:
node-role.kubernetes.io/egress-gateway: ""
Namespace-Based Egress
Apply egress to entire namespace:
apiVersion: crd.antrea.io/v1beta1
kind: Egress
metadata:
name: egress-namespace-wide
spec:
externalIPPool: egress-pool-production
appliedTo:
- namespaceSelector:
matchLabels:
name: production
# No podSelector = all pods in namespace
ExternalNode Support
Antrea supports ExternalNode for VMs/bare metal:
apiVersion: crd.antrea.io/v1alpha1
kind: ExternalNode
metadata:
name: vm-server-1
namespace: external
spec:
interfaces:
- interfaceName: eth0
ips:
- 192.168.50.10/24
nodeAnnotations:
type: "vm"
Gateway Failover Configuration
apiVersion: crd.antrea.io/v1beta1
kind: Egress
metadata:
name: egress-ha
spec:
externalIPPool: egress-pool-1
appliedTo:
- namespaceSelector:
matchLabels:
name: production
# Antrea automatically handles failover
# When primary gateway fails, secondary takes over
# No additional configuration needed
Network Policy Integration
Restrict Egress Traffic
Combine Egress with NetworkPolicy:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-egress
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Egress
egress:
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow only specific external services
- to:
- ipBlock:
cidr: 52.0.0.0/8 # AWS
ports:
- protocol: TCP
port: 443
# All other traffic goes through egress gateway
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
Antrea Native Policy
Use Antrea’s enhanced NetworkPolicy:
apiVersion: crd.antrea.io/v1beta1
kind: ClusterNetworkPolicy
metadata:
name: egress-control
spec:
priority: 10
appliedTo:
- podSelector:
matchLabels:
app: api-server
egress:
- action: Allow
to:
- ipBlock:
cidr: 52.0.0.0/8 # AWS only
protocols:
- protocol: TCP
port: 443
- action: Drop
to:
- ipBlock:
cidr: 0.0.0.0/0
Monitoring and Observability
Antrea Controller Metrics
# Enable metrics in Antrea config
kubectl edit configmap antrea-config -n kube-system
# Add:
# flowExporter:
# enable: true
# flowCollectorAddr: "flow-collector:4739"
# Access Prometheus metrics
kubectl port-forward -n kube-system svc/antrea-metrics 9090:9090
Key Metrics
| Metric | Description |
|---|---|
antrea_egress_ip_allocations |
Number of allocated egress IPs |
antrea_egress_gateway_packets |
Packets processed by egress gateway |
antrea_egress_gateway_bytes |
Bytes processed by egress gateway |
antrea_egress_gateway_flows |
Active flow count |
Grafana Dashboard
apiVersion: v1
kind: ConfigMap
metadata:
name: antrea-egress-dashboard
namespace: monitoring
data:
antrea-egress.json: |
{
"dashboard": {
"title": "Antrea Egress Gateway",
"panels": [
{
"title": "Egress IP Usage",
"targets": [
{
"expr": "antrea_egress_ip_allocations"
}
]
},
{
"title": "Egress Traffic",
"targets": [
{
"expr": "rate(antrea_egress_gateway_bytes[5m])"
}
]
}
]
}
}
Flow Exporter
# Enable flow exporter for detailed traffic analysis
helm upgrade antrea antrea/antrea \
-n kube-system \
--set featureGates.FlowExporter=true \
--set flowExporter.enable=true \
--set flowExporter.flowCollectorAddr="flow-collector.monitoring:4739" \
--wait
Troubleshooting
Issue: Egress IP Not Assigned
# Check ExternalIPPool status
kubectl get externalippool
# Verify node selector matches
kubectl get nodes --show-labels | grep egress-gateway
# Check Egress resource status
kubectl describe egress egress-production
# Look for events
kubectl get events -n kube-system | grep -i egress
Issue: Traffic Not Using Egress
# Verify pod labels match Egress spec
kubectl get pods -n production --show-labels
# Check if Egress is applied
kubectl get egress -A
# Test from inside pod
kubectl exec -it test-pod -n production -- \
curl -s ifconfig.me
# Check OVS flows on gateway node
kubectl debug node/gateway-node -it --image=antrea/antrea-agent-ubuntu -- \
ovs-ofctl dump-flows br-int
Issue: Gateway Node Failure
# Check gateway node status
kubectl get nodes
# View egress status after failover
kubectl get egress egress-production -o yaml
# Check which node is now handling egress
kubectl get egress egress-production -o jsonpath='{.status.gatewayNode}'
# View Antrea agent logs
kubectl logs -n kube-system -l app=antrea-agent | grep -i egress
Common Problems and Solutions
| Problem | Cause | Solution |
|---|---|---|
| Egress IP not allocated | No gateway nodes labeled | kubectl label node <name> node-role.kubernetes.io/egress-gateway= |
| Pod not using egress | Labels don’t match Egress spec | Verify pod/namespace labels |
| ExternalIPPool empty | IP range exhausted | Expand IP range or add more pools |
| Failover not working | Only one gateway node | Label additional gateway nodes |
| OVS flow errors | Antrea agent issue | Restart antrea-agent pod |
Performance Considerations
Gateway Node Sizing
| Workload | CPU | Memory | Network |
|---|---|---|---|
| Small (< 50 pods) | 2 cores | 4GB | 1Gbps |
| Medium (50-200 pods) | 4 cores | 8GB | 10Gbps |
| Large (200+ pods) | 8 cores | 16GB | 10Gbps+ |
Optimization Tips
- Use dedicated gateway nodes - Don’t run workloads on egress nodes
- Enable IPsec only when needed - Adds encryption overhead
- Tune OVS parameters - Adjust flow table sizes
- Monitor flow expiration - Prevent flow table exhaustion
# Check OVS flow statistics
kubectl debug node/gateway-node -it --image=antrea/antrea-agent-ubuntu -- \
ovs-ofctl dump-flows br-int table=0
# Check Antrea agent metrics
kubectl port-forward -n kube-system svc/antrea-agent 9091:9091
curl localhost:9091/metrics | grep egress
Security Best Practices
1. Restrict Egress by Destination
apiVersion: crd.antrea.io/v1beta1
kind: Egress
metadata:
name: restricted-egress
spec:
externalIPPool: egress-pool-1
appliedTo:
- namespaceSelector:
matchLabels:
name: production
destination:
- ipBlock:
cidr: 52.0.0.0/8 # AWS only
- ipBlock:
cidr: 35.0.0.0/8 # GCP only
2. Use Separate Pools per Environment
# Production - dedicated IPs
apiVersion: crd.antrea.io/v1beta1
kind: ExternalIPPool
metadata:
name: egress-production
spec:
ipRanges:
- start: 192.168.100.10
end: 192.168.100.20
---
# Staging - separate IPs
apiVersion: crd.antrea.io/v1beta1
kind: ExternalIPPool
metadata:
name: egress-staging
spec:
ipRanges:
- start: 192.168.100.30
end: 192.168.100.40
3. Audit Egress Traffic
# Enable flow logging for audit
apiVersion: crd.antrea.io/v1alpha1
kind: Traceflow
metadata:
name: egress-trace
spec:
source:
namespace: production
pod: api-server-xxxxx
destination:
ip: "8.8.8.8"
packet:
ipHeader:
protocol: 6 # TCP
ttl: 128
Comparison with Other Solutions
| Feature | Antrea | Cilium | Istio |
|---|---|---|---|
| Egress Gateway | ✅ Open source | ✅ Open source | ✅ Open source |
| CNI Type | OVS-based | eBPF-based | Sidecar proxy |
| Performance | Good | Excellent | Good |
| ExternalNode | ✅ Supported | ❌ Limited | ❌ No |
| Network Policy | ✅ Enhanced NP | ✅ Cilium NP | ✅ Authorization Policy |
| Encryption | ✅ IPsec/WireGuard | ❌ No | ✅ mTLS |
| Complexity | Medium | Medium | High |
| Resource Usage | Low | Low | High (sidecars) |
When to Choose Antrea
Choose Antrea when:
- ✅ You want open source egress gateway (unlike Calico)
- ✅ OVS-based networking is acceptable
- ✅ You need ExternalNode support (VMs/bare metal)
- ✅ IPsec encryption is required
- ✅ Simpler than service mesh (vs Istio)
Consider alternatives when:
- 📋 You need eBPF performance (choose Cilium)
- 📋 You want full service mesh (choose Istio)
- 📋 You’re already using Calico Enterprise
Migration from Other CNIs
From Calico to Antrea
# 1. Install Antrea alongside Calico (dual-stack temporarily)
helm install antrea antrea/antrea \
-n kube-system \
--create-namespace
# 2. Configure egress in Antrea
kubectl apply -f external-ippool.yaml
kubectl apply -f egress.yaml
# 3. Test egress functionality
kubectl run test --image=curlimages/curl -- curl ifconfig.me
# 4. Remove Calico (after validation)
kubectl delete -f calico.yaml
From Flannel to Antrea
# Note: Requires cluster recreation or careful migration
# 1. Backup all resources
kubectl get all -A -o yaml > backup.yaml
# 2. Install Antrea
helm install antrea antrea/antrea -n kube-system
# 3. Remove Flannel
kubectl delete -f flannel.yaml
# 4. Restore workloads
kubectl apply -f backup.yaml
Next Steps
In the next post of this series:
- Kube-OVN Egress Gateway - OVN-based CNI with Floating IP support
- Comparison with Antrea and Cilium
- When to choose each solution
Conclusion
Antrea Egress Gateway provides:
Advantages:
- ✅ Fully open source (unlike Calico Egress)
- ✅ OVS-based packet processing
- ✅ ExternalNode support for VMs/bare metal
- ✅ IPsec and WireGuard encryption
- ✅ Enhanced NetworkPolicy CRDs
- ✅ Lower resource usage than service mesh
Considerations:
- 📋 Requires replacing existing CNI
- 📋 OVS adds kernel module dependency
- 📋 Performance slightly below eBPF (Cilium)
- 📋 Smaller community than Cilium/Calico
For organizations needing a truly open source egress gateway without vendor lock-in, Antrea is an excellent choice that doesn’t require enterprise licensing.