Add configuration for retries in non-idempotent requests

This commit is contained in:
Manuel de Brito Fontes 2016-03-30 00:47:20 -03:00
parent c9f8a06399
commit 7abc7a77f6
8 changed files with 105 additions and 32 deletions

View file

@ -36,8 +36,8 @@ kubectl run echoheaders --image=gcr.io/google_containers/echoserver:1.3 --replic
Now we expose the same application in two different services (so we can create different Ingress rules) Now we expose the same application in two different services (so we can create different Ingress rules)
``` ```
kubectl expose rc echoheaders --port=80 --target-port=8080 --name=echoheaders-x kubectl expose deployment echoheaders --port=80 --target-port=8080 --name=echoheaders-x
kubectl expose rc echoheaders --port=80 --target-port=8080 --name=echoheaders-y kubectl expose deployment echoheaders --port=80 --target-port=8080 --name=echoheaders-y
``` ```
Next we create a couple of Ingress rules Next we create a couple of Ingress rules
@ -215,6 +215,13 @@ I0316 12:24:37.610073 1 command.go:69] change in configuration detected. R
- `--v=5` configures NGINX in [debug mode](http://nginx.org/en/docs/debugging_log.html) - `--v=5` configures NGINX in [debug mode](http://nginx.org/en/docs/debugging_log.html)
### Retries in no idempotent methods
Since 1.9.13 NGINX will not retry non-idempotent requests (POST, LOCK, PATCH) in case of an error.
The previous behavior can be restored using `retry-non-idempotent=true` in the configuration ConfigMap
## Limitations ## Limitations
TODO TODO

View file

@ -198,11 +198,11 @@ func (lbc *loadBalancerController) sync(key string) {
} }
ngxConfig := lbc.nginx.ReadConfig(cfg) ngxConfig := lbc.nginx.ReadConfig(cfg)
tcpServices := lbc.getTCPServices()
lbc.nginx.CheckAndReload(ngxConfig, nginx.IngressConfig{ lbc.nginx.CheckAndReload(ngxConfig, nginx.IngressConfig{
Upstreams: upstreams, Upstreams: upstreams,
Servers: servers, Servers: servers,
TCPUpstreams: tcpServices, TCPUpstreams: lbc.getTCPServices(),
UDPUpstreams: lbc.getUDPServices(),
}) })
} }
@ -285,12 +285,12 @@ func (lbc *loadBalancerController) getServices(data map[string]string, proto api
var endps []nginx.UpstreamServer var endps []nginx.UpstreamServer
targetPort, err := strconv.Atoi(svcPort[1]) targetPort, err := strconv.Atoi(svcPort[1])
if err != nil { if err != nil {
endps = lbc.getEndpoints(svc, intstr.FromString(svcPort[1])) endps = lbc.getEndpoints(svc, intstr.FromString(svcPort[1]), proto)
} else { } else {
// we need to use the TargetPort (where the endpoints are running) // we need to use the TargetPort (where the endpoints are running)
for _, sp := range svc.Spec.Ports { for _, sp := range svc.Spec.Ports {
if sp.Port == targetPort { if sp.Port == targetPort {
endps = lbc.getEndpoints(svc, sp.TargetPort) endps = lbc.getEndpoints(svc, sp.TargetPort, proto)
break break
} }
} }
@ -335,7 +335,7 @@ func (lbc *loadBalancerController) getDefaultUpstream() *nginx.Upstream {
svc := svcObj.(*api.Service) svc := svcObj.(*api.Service)
endps := lbc.getEndpoints(svc, svc.Spec.Ports[0].TargetPort) endps := lbc.getEndpoints(svc, svc.Spec.Ports[0].TargetPort, api.ProtocolTCP)
if len(endps) == 0 { if len(endps) == 0 {
glog.Warningf("service %v does no have any active endpoints", svcKey) glog.Warningf("service %v does no have any active endpoints", svcKey)
upstream.Backends = append(upstream.Backends, nginx.NewDefaultServer()) upstream.Backends = append(upstream.Backends, nginx.NewDefaultServer())
@ -383,7 +383,7 @@ func (lbc *loadBalancerController) getUpstreamServers(data []interface{}) ([]*ng
for _, servicePort := range svc.Spec.Ports { for _, servicePort := range svc.Spec.Ports {
if servicePort.Port == path.Backend.ServicePort.IntValue() { if servicePort.Port == path.Backend.ServicePort.IntValue() {
endps := lbc.getEndpoints(svc, servicePort.TargetPort) endps := lbc.getEndpoints(svc, servicePort.TargetPort, api.ProtocolTCP)
if len(endps) == 0 { if len(endps) == 0 {
glog.Warningf("service %v does no have any active endpoints", svcKey) glog.Warningf("service %v does no have any active endpoints", svcKey)
} }
@ -526,7 +526,7 @@ func (lbc *loadBalancerController) getPemsFromIngress(data []interface{}) map[st
} }
// getEndpoints returns a list of <endpoint ip>:<port> for a given service/target port combination. // getEndpoints returns a list of <endpoint ip>:<port> for a given service/target port combination.
func (lbc *loadBalancerController) getEndpoints(s *api.Service, servicePort intstr.IntOrString) []nginx.UpstreamServer { func (lbc *loadBalancerController) getEndpoints(s *api.Service, servicePort intstr.IntOrString, proto api.Protocol) []nginx.UpstreamServer {
glog.V(3).Infof("getting endpoints for service %v/%v and port %v", s.Namespace, s.Name, servicePort.String()) glog.V(3).Infof("getting endpoints for service %v/%v and port %v", s.Namespace, s.Name, servicePort.String())
ep, err := lbc.endpLister.GetServiceEndpoints(s) ep, err := lbc.endpLister.GetServiceEndpoints(s)
if err != nil { if err != nil {
@ -538,6 +538,11 @@ func (lbc *loadBalancerController) getEndpoints(s *api.Service, servicePort ints
for _, ss := range ep.Subsets { for _, ss := range ep.Subsets {
for _, epPort := range ss.Ports { for _, epPort := range ss.Ports {
if !reflect.DeepEqual(epPort.Protocol, proto) {
continue
}
var targetPort int var targetPort int
switch servicePort.Type { switch servicePort.Type {
case intstr.Int: case intstr.Int:

View file

@ -0,0 +1,13 @@
To configure which services and ports will be exposed
```
kubectl create -f udp-configmap-example.yaml
```
The file `udp-configmap-example.yaml` uses a ConfigMap where the key is the external port to use and the value is
`<namespace/service name>:<service port>`
It is possible to use a number or the name of the port.
```
kubectl create -f rc-udp.yaml
```

View file

@ -0,0 +1,56 @@
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-ingress-controller
labels:
k8s-app: nginx-ingress-lb
spec:
replicas: 1
selector:
k8s-app: nginx-ingress-lb
template:
metadata:
labels:
k8s-app: nginx-ingress-lb
name: nginx-ingress-lb
spec:
containers:
- image: aledbf/nginx-third-party:0.9
name: nginx-ingress-lb
imagePullPolicy: Always
livenessProbe:
httpGet:
path: /healthz
port: 10249
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
# use downward API
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- containerPort: 80
hostPort: 80
- containerPort: 443
hostPort: 4444
# we expose 8080 to access nginx stats in url /nginx-status
# this is optional
- containerPort: 8080
hostPort: 8081
- containerPort: 53
hostPort: 53
args:
- /nginx-ingress-controller
- --default-backend-service=default/default-http-backend
- --udp-services-configmap=default/udp-configmap-example

View file

@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: udp-configmap-example
data:
53: "kube-system/kube-dns:53"

View file

@ -1,8 +0,0 @@
SecRulesEnabled;
DeniedUrl "/RequestDenied";
## check rules
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;

View file

@ -24,11 +24,6 @@ http {
require("error_page") require("error_page")
} }
{{ if $cfg.enableWaf}}
# https://github.com/nbs-system/naxsi/wiki/basicsetup
include /etc/nginx/naxsi/*.rules;
{{ end }}
sendfile on; sendfile on;
aio threads; aio threads;
tcp_nopush on; tcp_nopush on;
@ -146,7 +141,7 @@ http {
error_page 504 = @custom_504; error_page 504 = @custom_504;
# In case of errors try the next upstream server before returning an error # In case of errors try the next upstream server before returning an error
proxy_next_upstream error timeout invalid_header http_502 http_503 http_504; proxy_next_upstream error timeout invalid_header http_502 http_503 http_504 {{ if $cfg.retryNonIdempotent }}non_idempotent{{ end }};
server { server {
listen 80 default_server{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }}; listen 80 default_server{{ if $cfg.useProxyProtocol }} proxy_protocol{{ end }};
@ -219,11 +214,6 @@ http {
proxy_pass http://{{ $location.Upstream.Name }}; proxy_pass http://{{ $location.Upstream.Name }};
} }
{{ end }} {{ end }}
{{ if $cfg.enableWaf}}
location /RequestDenied {
return 418;
}
{{ end }}
{{ template "CUSTOM_ERRORS" $cfg }} {{ template "CUSTOM_ERRORS" $cfg }}
} }
{{ end }} {{ end }}
@ -291,15 +281,15 @@ stream {
# UDP services # UDP services
{{ range $i, $udpServer := .udpUpstreams }} {{ range $i, $udpServer := .udpUpstreams }}
upstream udp-{{ $udpServer.Upstream.Name }} { upstream udp-{{ $udpServer.Upstream.Name }} {
{{ range $server := $tcpServer.Upstream.Backends }}server {{ $server.Address }}:{{ $server.Port }}; {{ range $server := $udpServer.Upstream.Backends }}server {{ $server.Address }}:{{ $server.Port }};
{{ end }} {{ end }}
} }
server { server {
listen {{ $tcpServer.Path }} udp; listen {{ $udpServer.Path }} udp;
proxy_timeout 1s; proxy_timeout 10s;
proxy_responses 1; proxy_responses 1;
proxy_pass udp-{{ $tcpServer.Upstream.Name }}; proxy_pass udp-{{ $udpServer.Upstream.Name }};
} }
{{ end }} {{ end }}

View file

@ -94,6 +94,10 @@ type nginxConfiguration struct {
VtsStatusZoneSize string `structs:"vts-status-zone-size,omitempty"` VtsStatusZoneSize string `structs:"vts-status-zone-size,omitempty"`
// RetryNonIdempotent since 1.9.13 NGINX will not retry non-idempotent requests (POST, LOCK, PATCH)
// in case of an error. The previous behavior can be restored using the value true
RetryNonIdempotent bool `structs:"retry-non-idempotent"`
// http://nginx.org/en/docs/ngx_core_module.html#error_log // http://nginx.org/en/docs/ngx_core_module.html#error_log
// Configures logging level [debug | info | notice | warn | error | crit | alert | emerg] // Configures logging level [debug | info | notice | warn | error | crit | alert | emerg]
// Log levels above are listed in the order of increasing severity // Log levels above are listed in the order of increasing severity