feat(#733)Support nginx bandwidth control
This commit is contained in:
parent
36cf018a71
commit
890c57f2ca
6 changed files with 84 additions and 12 deletions
|
@ -387,6 +387,8 @@ func NewDefault() Configuration {
|
||||||
CustomHTTPErrors: []int{},
|
CustomHTTPErrors: []int{},
|
||||||
WhitelistSourceRange: []string{},
|
WhitelistSourceRange: []string{},
|
||||||
SkipAccessLogURLs: []string{},
|
SkipAccessLogURLs: []string{},
|
||||||
|
LimitRate: 0,
|
||||||
|
LimitRateAfter: 0,
|
||||||
},
|
},
|
||||||
UpstreamKeepaliveConnections: 0,
|
UpstreamKeepaliveConnections: 0,
|
||||||
LimitConnZoneVariable: defaultLimitConnZoneVariable,
|
LimitConnZoneVariable: defaultLimitConnZoneVariable,
|
||||||
|
|
|
@ -404,6 +404,18 @@ func buildRateLimit(input interface{}) []string {
|
||||||
limits = append(limits, limit)
|
limits = append(limits, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if loc.RateLimit.LimitRateAfter > 0 {
|
||||||
|
limit := fmt.Sprintf("limit_rate_after %vk;",
|
||||||
|
loc.RateLimit.LimitRateAfter)
|
||||||
|
limits = append(limits, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loc.RateLimit.LimitRate > 0 {
|
||||||
|
limit := fmt.Sprintf("limit_rate %vk;",
|
||||||
|
loc.RateLimit.LimitRate)
|
||||||
|
limits = append(limits, limit)
|
||||||
|
}
|
||||||
|
|
||||||
return limits
|
return limits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,15 @@ import (
|
||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
|
|
||||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||||
|
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
limitIP = "ingress.kubernetes.io/limit-connections"
|
limitIP = "ingress.kubernetes.io/limit-connections"
|
||||||
limitRPS = "ingress.kubernetes.io/limit-rps"
|
limitRPS = "ingress.kubernetes.io/limit-rps"
|
||||||
limitRPM = "ingress.kubernetes.io/limit-rpm"
|
limitRPM = "ingress.kubernetes.io/limit-rpm"
|
||||||
|
limitRATE = "ingress.kubernetes.io/limit-rate"
|
||||||
|
limitRATEAFTER = "ingress.kubernetes.io/limit-rate-after"
|
||||||
|
|
||||||
// allow 5 times the specified limit as burst
|
// allow 5 times the specified limit as burst
|
||||||
defBurst = 5
|
defBurst = 5
|
||||||
|
@ -48,6 +51,10 @@ type RateLimit struct {
|
||||||
RPS Zone `json:"rps"`
|
RPS Zone `json:"rps"`
|
||||||
|
|
||||||
RPM Zone `json:"rpm"`
|
RPM Zone `json:"rpm"`
|
||||||
|
|
||||||
|
LimitRate int `json:"limit-rate"`
|
||||||
|
|
||||||
|
LimitRateAfter int `json:"limit-rate-after"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equal tests for equality between two RateLimit types
|
// Equal tests for equality between two RateLimit types
|
||||||
|
@ -67,6 +74,12 @@ func (rt1 *RateLimit) Equal(rt2 *RateLimit) bool {
|
||||||
if !(&rt1.RPS).Equal(&rt2.RPS) {
|
if !(&rt1.RPS).Equal(&rt2.RPS) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if rt1.LimitRate != rt2.LimitRate {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if rt1.LimitRateAfter != rt2.LimitRateAfter {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -106,16 +119,26 @@ func (z1 *Zone) Equal(z2 *Zone) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ratelimit struct {
|
type ratelimit struct {
|
||||||
|
backendResolver resolver.DefaultBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewParser creates a new ratelimit annotation parser
|
// NewParser creates a new ratelimit annotation parser
|
||||||
func NewParser() parser.IngressAnnotation {
|
func NewParser(br resolver.DefaultBackend) parser.IngressAnnotation {
|
||||||
return ratelimit{}
|
return ratelimit{br}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseAnnotations parses the annotations contained in the ingress
|
// ParseAnnotations parses the annotations contained in the ingress
|
||||||
// rule used to rewrite the defined paths
|
// rule used to rewrite the defined paths
|
||||||
func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
|
defBackend := a.backendResolver.GetDefaultBackend()
|
||||||
|
lr, err := parser.GetIntAnnotation(limitRATE, ing)
|
||||||
|
if err != nil {
|
||||||
|
lr = defBackend.LimitRate
|
||||||
|
}
|
||||||
|
lra, err := parser.GetIntAnnotation(limitRATEAFTER, ing)
|
||||||
|
if err != nil {
|
||||||
|
lra = defBackend.LimitRateAfter
|
||||||
|
}
|
||||||
|
|
||||||
rpm, _ := parser.GetIntAnnotation(limitRPM, ing)
|
rpm, _ := parser.GetIntAnnotation(limitRPM, ing)
|
||||||
rps, _ := parser.GetIntAnnotation(limitRPS, ing)
|
rps, _ := parser.GetIntAnnotation(limitRPS, ing)
|
||||||
|
@ -123,9 +146,11 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
|
|
||||||
if rpm == 0 && rps == 0 && conn == 0 {
|
if rpm == 0 && rps == 0 && conn == 0 {
|
||||||
return &RateLimit{
|
return &RateLimit{
|
||||||
Connections: Zone{},
|
Connections: Zone{},
|
||||||
RPS: Zone{},
|
RPS: Zone{},
|
||||||
RPM: Zone{},
|
RPM: Zone{},
|
||||||
|
LimitRate: lr,
|
||||||
|
LimitRateAfter: lra,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,5 +175,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
Burst: rpm * defBurst,
|
Burst: rpm * defBurst,
|
||||||
SharedSize: defSharedSize,
|
SharedSize: defSharedSize,
|
||||||
},
|
},
|
||||||
|
LimitRate: lr,
|
||||||
|
LimitRateAfter: lra,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
|
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||||
)
|
)
|
||||||
|
|
||||||
func buildIngress() *extensions.Ingress {
|
func buildIngress() *extensions.Ingress {
|
||||||
|
@ -61,9 +62,19 @@ func buildIngress() *extensions.Ingress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type mockBackend struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m mockBackend) GetDefaultBackend() defaults.Backend {
|
||||||
|
return defaults.Backend{
|
||||||
|
LimitRateAfter: 0,
|
||||||
|
LimitRate: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWithoutAnnotations(t *testing.T) {
|
func TestWithoutAnnotations(t *testing.T) {
|
||||||
ing := buildIngress()
|
ing := buildIngress()
|
||||||
_, err := NewParser().Parse(ing)
|
_, err := NewParser(mockBackend{}).Parse(ing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("unexpected error with ingress without annotations")
|
t.Error("unexpected error with ingress without annotations")
|
||||||
}
|
}
|
||||||
|
@ -78,7 +89,7 @@ func TestBadRateLimiting(t *testing.T) {
|
||||||
data[limitRPM] = "0"
|
data[limitRPM] = "0"
|
||||||
ing.SetAnnotations(data)
|
ing.SetAnnotations(data)
|
||||||
|
|
||||||
_, err := NewParser().Parse(ing)
|
_, err := NewParser(mockBackend{}).Parse(ing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error with invalid limits (0)")
|
t.Errorf("unexpected error with invalid limits (0)")
|
||||||
}
|
}
|
||||||
|
@ -87,9 +98,12 @@ func TestBadRateLimiting(t *testing.T) {
|
||||||
data[limitIP] = "5"
|
data[limitIP] = "5"
|
||||||
data[limitRPS] = "100"
|
data[limitRPS] = "100"
|
||||||
data[limitRPM] = "10"
|
data[limitRPM] = "10"
|
||||||
|
data[limitRATEAFTER] = "100"
|
||||||
|
data[limitRATE] = "10"
|
||||||
|
|
||||||
ing.SetAnnotations(data)
|
ing.SetAnnotations(data)
|
||||||
|
|
||||||
i, err := NewParser().Parse(ing)
|
i, err := NewParser(mockBackend{}).Parse(ing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -106,4 +120,10 @@ func TestBadRateLimiting(t *testing.T) {
|
||||||
if rateLimit.RPM.Limit != 10 {
|
if rateLimit.RPM.Limit != 10 {
|
||||||
t.Errorf("expected 10 in limit by rpm but %v was returend", rateLimit.RPM)
|
t.Errorf("expected 10 in limit by rpm but %v was returend", rateLimit.RPM)
|
||||||
}
|
}
|
||||||
|
if rateLimit.LimitRateAfter != 100 {
|
||||||
|
t.Errorf("expected 100 in limit by limitrateafter but %v was returend", rateLimit.LimitRateAfter)
|
||||||
|
}
|
||||||
|
if rateLimit.LimitRate != 10 {
|
||||||
|
t.Errorf("expected 10 in limit by limitrate but %v was returend", rateLimit.LimitRate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
||||||
"Whitelist": ipwhitelist.NewParser(cfg),
|
"Whitelist": ipwhitelist.NewParser(cfg),
|
||||||
"UsePortInRedirects": portinredirect.NewParser(cfg),
|
"UsePortInRedirects": portinredirect.NewParser(cfg),
|
||||||
"Proxy": proxy.NewParser(cfg),
|
"Proxy": proxy.NewParser(cfg),
|
||||||
"RateLimit": ratelimit.NewParser(),
|
"RateLimit": ratelimit.NewParser(cfg),
|
||||||
"Redirect": rewrite.NewParser(cfg),
|
"Redirect": rewrite.NewParser(cfg),
|
||||||
"SecureUpstream": secureupstream.NewParser(cfg),
|
"SecureUpstream": secureupstream.NewParser(cfg),
|
||||||
"ServiceUpstream": serviceupstream.NewParser(),
|
"ServiceUpstream": serviceupstream.NewParser(),
|
||||||
|
|
|
@ -88,4 +88,15 @@ type Backend struct {
|
||||||
// WhitelistSourceRange allows limiting access to certain client addresses
|
// WhitelistSourceRange allows limiting access to certain client addresses
|
||||||
// http://nginx.org/en/docs/http/ngx_http_access_module.html
|
// http://nginx.org/en/docs/http/ngx_http_access_module.html
|
||||||
WhitelistSourceRange []string `json:"whitelist-source-range,-"`
|
WhitelistSourceRange []string `json:"whitelist-source-range,-"`
|
||||||
|
|
||||||
|
// Limits the rate of response transmission to a client.
|
||||||
|
// The rate is specified in bytes per second. The zero value disables rate limiting.
|
||||||
|
// The limit is set per a request, and so if a client simultaneously opens two connections,
|
||||||
|
// the overall rate will be twice as much as the specified limit.
|
||||||
|
// http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate
|
||||||
|
LimitRate int `json:"limit-rate"`
|
||||||
|
|
||||||
|
// Sets the initial amount after which the further transmission of a response to a client will be rate limited.
|
||||||
|
// http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate_after
|
||||||
|
LimitRateAfter int `json:"limit-rate-after"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue