Add annotation to customize nginx location configuration
This commit is contained in:
parent
852b598c02
commit
a20c287614
11 changed files with 294 additions and 13 deletions
|
@ -331,6 +331,9 @@ http {
|
||||||
proxy_set_header Accept-Encoding "";
|
proxy_set_header Accept-Encoding "";
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
{{/* Add any additional configuration defined */}}
|
||||||
|
{{ $location.ConfigurationSnippet }}
|
||||||
|
|
||||||
{{ buildProxyPass $backends $location }}
|
{{ buildProxyPass $backends $location }}
|
||||||
{{ else }}
|
{{ else }}
|
||||||
#{{ $location.Denied }}
|
#{{ $location.Denied }}
|
||||||
|
|
42
core/pkg/ingress/annotations/snippet/main.go
Normal file
42
core/pkg/ingress/annotations/snippet/main.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package snippet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
|
||||||
|
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
annotation = "ingress.kubernetes.io/configuration-snippet"
|
||||||
|
)
|
||||||
|
|
||||||
|
type snippet struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewParser creates a new CORS annotation parser
|
||||||
|
func NewParser() parser.IngressAnnotation {
|
||||||
|
return snippet{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses the annotations contained in the ingress rule
|
||||||
|
// used to indicate if the location/s contains a fragment of
|
||||||
|
// configuration to be included inside the paths of the rules
|
||||||
|
func (a snippet) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||||
|
return parser.GetStringAnnotation(annotation, ing)
|
||||||
|
}
|
57
core/pkg/ingress/annotations/snippet/main_test.go
Normal file
57
core/pkg/ingress/annotations/snippet/main_test.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package snippet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
ap := NewParser()
|
||||||
|
if ap == nil {
|
||||||
|
t.Fatalf("expected a parser.IngressAnnotation but returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
annotations map[string]string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{map[string]string{annotation: "more_headers"}, "more_headers"},
|
||||||
|
{map[string]string{annotation: "false"}, "false"},
|
||||||
|
{map[string]string{}, ""},
|
||||||
|
{nil, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
ing := &extensions.Ingress{
|
||||||
|
ObjectMeta: api.ObjectMeta{
|
||||||
|
Name: "foo",
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
},
|
||||||
|
Spec: extensions.IngressSpec{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
ing.SetAnnotations(testCase.annotations)
|
||||||
|
result, _ := ap.Parse(ing)
|
||||||
|
if result != testCase.expected {
|
||||||
|
t.Errorf("expected %v but returned %v, annotations: %s", testCase.expected, result, testCase.annotations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ import (
|
||||||
"k8s.io/ingress/core/pkg/ingress/annotations/rewrite"
|
"k8s.io/ingress/core/pkg/ingress/annotations/rewrite"
|
||||||
"k8s.io/ingress/core/pkg/ingress/annotations/secureupstream"
|
"k8s.io/ingress/core/pkg/ingress/annotations/secureupstream"
|
||||||
"k8s.io/ingress/core/pkg/ingress/annotations/sessionaffinity"
|
"k8s.io/ingress/core/pkg/ingress/annotations/sessionaffinity"
|
||||||
|
"k8s.io/ingress/core/pkg/ingress/annotations/snippet"
|
||||||
"k8s.io/ingress/core/pkg/ingress/annotations/sslpassthrough"
|
"k8s.io/ingress/core/pkg/ingress/annotations/sslpassthrough"
|
||||||
"k8s.io/ingress/core/pkg/ingress/errors"
|
"k8s.io/ingress/core/pkg/ingress/errors"
|
||||||
"k8s.io/ingress/core/pkg/ingress/resolver"
|
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||||
|
@ -52,19 +53,20 @@ type annotationExtractor struct {
|
||||||
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
||||||
return annotationExtractor{
|
return annotationExtractor{
|
||||||
map[string]parser.IngressAnnotation{
|
map[string]parser.IngressAnnotation{
|
||||||
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||||
"ExternalAuth": authreq.NewParser(),
|
"ExternalAuth": authreq.NewParser(),
|
||||||
"CertificateAuth": authtls.NewParser(cfg),
|
"CertificateAuth": authtls.NewParser(cfg),
|
||||||
"EnableCORS": cors.NewParser(),
|
"EnableCORS": cors.NewParser(),
|
||||||
"HealthCheck": healthcheck.NewParser(cfg),
|
"HealthCheck": healthcheck.NewParser(cfg),
|
||||||
"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(),
|
||||||
"Redirect": rewrite.NewParser(cfg),
|
"Redirect": rewrite.NewParser(cfg),
|
||||||
"SecureUpstream": secureupstream.NewParser(),
|
"SecureUpstream": secureupstream.NewParser(),
|
||||||
"SessionAffinity": sessionaffinity.NewParser(),
|
"SessionAffinity": sessionaffinity.NewParser(),
|
||||||
"SSLPassthrough": sslpassthrough.NewParser(),
|
"SSLPassthrough": sslpassthrough.NewParser(),
|
||||||
|
"ConfigurationSnippet": snippet.NewParser(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,6 +277,9 @@ type Location struct {
|
||||||
// UsePortInRedirects indicates if redirects must specify the port
|
// UsePortInRedirects indicates if redirects must specify the port
|
||||||
// +optional
|
// +optional
|
||||||
UsePortInRedirects bool `json:"use-port-in-redirects"`
|
UsePortInRedirects bool `json:"use-port-in-redirects"`
|
||||||
|
// ConfigurationSnippet contains additional configuration for the backend
|
||||||
|
// to be considered in the configuration of the location
|
||||||
|
ConfigurationSnippet string `json:"configuration-snippet"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSLPassthroughBackend describes a SSL upstream server configured
|
// SSLPassthroughBackend describes a SSL upstream server configured
|
||||||
|
|
|
@ -80,3 +80,4 @@ Dummy | A simple dummy controller that logs updates | * | Advanced
|
||||||
Name | Description | Platform | Complexity Level
|
Name | Description | Platform | Complexity Level
|
||||||
-----| ----------- | ---------- | ----------------
|
-----| ----------- | ---------- | ----------------
|
||||||
custom-headers | set custom headers before send traffic to backends | nginx | Advanced
|
custom-headers | set custom headers before send traffic to backends | nginx | Advanced
|
||||||
|
configuration-snippets | customize nginx location configuration using annotations | nginx | Advanced
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Deploying the Nginx Ingress controller
|
||||||
|
|
||||||
|
This example aims to demonstrate the deployment of an nginx ingress controller and
|
||||||
|
with the use of an annotation in the Ingress rule be able to customize the nginx
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
## Default Backend
|
||||||
|
|
||||||
|
The default backend is a Service capable of handling all url paths and hosts the
|
||||||
|
nginx controller doesn't understand. This most basic implementation just returns
|
||||||
|
a 404 page:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl apply -f default-backend.yaml
|
||||||
|
deployment "default-http-backend" created
|
||||||
|
service "default-http-backend" created
|
||||||
|
|
||||||
|
$ kubectl -n kube-system get po
|
||||||
|
NAME READY STATUS RESTARTS AGE
|
||||||
|
default-http-backend-2657704409-qgwdd 1/1 Running 0 28s
|
||||||
|
```
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl create -f nginx-load-balancer-conf.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Controller
|
||||||
|
|
||||||
|
You can deploy the controller as follows:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ kubectl apply -f nginx-ingress-controller.yaml
|
||||||
|
deployment "nginx-ingress-controller" created
|
||||||
|
|
||||||
|
$ kubectl -n kube-system get po
|
||||||
|
NAME READY STATUS RESTARTS AGE
|
||||||
|
default-http-backend-2657704409-qgwdd 1/1 Running 0 2m
|
||||||
|
nginx-ingress-controller-873061567-4n3k2 1/1 Running 0 42s
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
Check the contents of the annotation is present in the nginx.conf file using:
|
||||||
|
`kubectl exec nginx-ingress-controller-873061567-4n3k2 -n kube-system cat /etc/nginx/nginx.conf`
|
|
@ -0,0 +1,51 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: default-http-backend
|
||||||
|
labels:
|
||||||
|
k8s-app: default-http-backend
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-app: default-http-backend
|
||||||
|
spec:
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
containers:
|
||||||
|
- name: default-http-backend
|
||||||
|
# Any image is permissable as long as:
|
||||||
|
# 1. It serves a 404 page at /
|
||||||
|
# 2. It serves 200 on a /healthz endpoint
|
||||||
|
image: gcr.io/google_containers/defaultbackend:1.0
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 8080
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 30
|
||||||
|
timeoutSeconds: 5
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 20Mi
|
||||||
|
requests:
|
||||||
|
cpu: 10m
|
||||||
|
memory: 20Mi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: default-http-backend
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
k8s-app: default-http-backend
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
k8s-app: default-http-backend
|
|
@ -0,0 +1,18 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: nginx-configuration-snippet
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: "nginx"
|
||||||
|
ingress.kubernetes.io/configuration-snippet: |
|
||||||
|
more_set_headers "Request-Id: $request_id";
|
||||||
|
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: custom.configuration.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: http-svc
|
||||||
|
servicePort: 80
|
||||||
|
path: /
|
|
@ -0,0 +1,53 @@
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: nginx-ingress-controller
|
||||||
|
labels:
|
||||||
|
k8s-app: nginx-ingress-controller
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-app: nginx-ingress-controller
|
||||||
|
spec:
|
||||||
|
# hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
|
||||||
|
# however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host
|
||||||
|
# that said, since hostPort is broken on CNI (https://github.com/kubernetes/kubernetes/issues/31307) we have to use hostNetwork where CNI is used
|
||||||
|
# like with kubeadm
|
||||||
|
# hostNetwork: true
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
containers:
|
||||||
|
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
|
||||||
|
name: nginx-ingress-controller
|
||||||
|
readinessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
livenessProbe:
|
||||||
|
httpGet:
|
||||||
|
path: /healthz
|
||||||
|
port: 10254
|
||||||
|
scheme: HTTP
|
||||||
|
initialDelaySeconds: 10
|
||||||
|
timeoutSeconds: 1
|
||||||
|
ports:
|
||||||
|
- containerPort: 80
|
||||||
|
hostPort: 80
|
||||||
|
- containerPort: 443
|
||||||
|
hostPort: 443
|
||||||
|
env:
|
||||||
|
- name: POD_NAME
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.name
|
||||||
|
- name: POD_NAMESPACE
|
||||||
|
valueFrom:
|
||||||
|
fieldRef:
|
||||||
|
fieldPath: metadata.namespace
|
||||||
|
args:
|
||||||
|
- /nginx-ingress-controller
|
||||||
|
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
|
||||||
|
- --configmap=$(POD_NAMESPACE)/nginx-load-balancer-conf
|
|
@ -0,0 +1,7 @@
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
proxy-set-headers: "kube-system/custom-headers"
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: nginx-load-balancer-conf
|
||||||
|
namespace: kube-system
|
Loading…
Reference in a new issue