Merge branch 'master' into nginx_health_check
This commit is contained in:
commit
7d1eac302f
62 changed files with 2641 additions and 158 deletions
|
@ -51,7 +51,7 @@ __Lines 5-7__: Ingress Spec has all the information needed to configure a GCE L7
|
|||
|
||||
__Lines 8-9__: Each http rule contains the following information: A host (eg: foo.bar.com, defaults to `*` in this example), a list of paths (eg: `/hostless`) each of which has an associated backend (`test:80`). Both the `host` and `path` must match the content of an incoming request before the L7 directs traffic to the `backend`.
|
||||
|
||||
__Lines 10-12__: A `backend` is a service:port combination. It selects a group of pods capable of servicing traffic sent to the path specified in the parent rule.
|
||||
__Lines 10-12__: A `backend` is a service:port combination. It selects a group of pods capable of servicing traffic sent to the path specified in the parent rule. The `port` is the desired `spec.ports[*].port` from the Service Spec -- Note, though, that the L7 actually directs traffic to the corresponding `NodePort`.
|
||||
|
||||
__Global Prameters__: For the sake of simplicity the example Ingress has no global parameters. However, one can specify a default backend (see examples below) in the absence of which requests that don't match a path in the spec are sent to the default backend of glbc. Though glbc doesn't support HTTPS yet, security configs would also be global.
|
||||
|
||||
|
|
|
@ -109,6 +109,29 @@ func TestProbeGetter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProbeGetterNamedPort(t *testing.T) {
|
||||
cm := NewFakeClusterManager(DefaultClusterUID)
|
||||
lbc := newLoadBalancerController(t, cm, "")
|
||||
nodePortToHealthCheck := map[int64]string{
|
||||
3001: "/healthz",
|
||||
}
|
||||
addPods(lbc, nodePortToHealthCheck, api.NamespaceDefault)
|
||||
for _, p := range lbc.podLister.Indexer.List() {
|
||||
pod := p.(*api.Pod)
|
||||
pod.Spec.Containers[0].Ports[0].Name = "test"
|
||||
pod.Spec.Containers[0].ReadinessProbe.Handler.HTTPGet.Port = intstr.IntOrString{Type: intstr.String, StrVal: "test"}
|
||||
}
|
||||
for p, exp := range nodePortToHealthCheck {
|
||||
got, err := lbc.tr.HealthCheck(p)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get health check for node port %v: %v", p, err)
|
||||
} else if got.RequestPath != exp {
|
||||
t.Errorf("Wrong health check for node port %v, got %v expected %v", p, got.RequestPath, exp)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestProbeGetterCrossNamespace(t *testing.T) {
|
||||
cm := NewFakeClusterManager(DefaultClusterUID)
|
||||
lbc := newLoadBalancerController(t, cm, "")
|
||||
|
@ -191,7 +214,7 @@ func addPods(lbc *LoadBalancerController, nodePortToHealthCheck map[int64]string
|
|||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Ports: []api.ContainerPort{{ContainerPort: 80}},
|
||||
Ports: []api.ContainerPort{{Name: "test", ContainerPort: 80}},
|
||||
ReadinessProbe: &api.Probe{
|
||||
Handler: api.Handler{
|
||||
HTTPGet: &api.HTTPGetAction{
|
||||
|
|
|
@ -410,14 +410,6 @@ func (t *GCETranslator) ListZones() ([]string, error) {
|
|||
return zones.List(), nil
|
||||
}
|
||||
|
||||
// isPortEqual compares the given IntOrString ports
|
||||
func isPortEqual(port, targetPort intstr.IntOrString) bool {
|
||||
if targetPort.Type == intstr.Int {
|
||||
return port.IntVal == targetPort.IntVal
|
||||
}
|
||||
return port.StrVal == targetPort.StrVal
|
||||
}
|
||||
|
||||
// geHTTPProbe returns the http readiness probe from the first container
|
||||
// that matches targetPort, from the set of pods matching the given labels.
|
||||
func (t *GCETranslator) getHTTPProbe(svc api.Service, targetPort intstr.IntOrString) (*api.Probe, error) {
|
||||
|
@ -443,11 +435,21 @@ func (t *GCETranslator) getHTTPProbe(svc api.Service, targetPort intstr.IntOrStr
|
|||
continue
|
||||
}
|
||||
for _, p := range c.Ports {
|
||||
cPort := intstr.IntOrString{IntVal: p.ContainerPort, StrVal: p.Name}
|
||||
if isPortEqual(cPort, targetPort) {
|
||||
if isPortEqual(c.ReadinessProbe.Handler.HTTPGet.Port, targetPort) {
|
||||
return c.ReadinessProbe, nil
|
||||
if targetPort.Type == intstr.Int && targetPort.IntVal == p.ContainerPort ||
|
||||
targetPort.Type == intstr.String && targetPort.StrVal == p.Name {
|
||||
|
||||
readinessProbePort := c.ReadinessProbe.Handler.HTTPGet.Port
|
||||
switch readinessProbePort.Type {
|
||||
case intstr.Int:
|
||||
if readinessProbePort.IntVal == p.ContainerPort {
|
||||
return c.ReadinessProbe, nil
|
||||
}
|
||||
case intstr.String:
|
||||
if readinessProbePort.StrVal == p.Name {
|
||||
return c.ReadinessProbe, nil
|
||||
}
|
||||
}
|
||||
|
||||
glog.Infof("%v: found matching targetPort on container %v, but not on readinessProbe (%+v)",
|
||||
logStr, c.Name, c.ReadinessProbe.Handler.HTTPGet.Port)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,97 @@
|
|||
Changelog
|
||||
|
||||
### 0.9
|
||||
### 0.9-beta.1
|
||||
|
||||
**Image:** `gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.1`
|
||||
|
||||
*New Features:*
|
||||
|
||||
- SSL Passthrough
|
||||
- New Flag `--publish-service` that set the Service fronting the ingress controllers
|
||||
- Ingress status shows the correct IP/hostname address without duplicates
|
||||
- Custom body sizes per Ingress
|
||||
- Prometheus metrics
|
||||
|
||||
|
||||
*Breaking changes:*
|
||||
|
||||
- Flag `--nginx-configmap` was replaced with `--configmap`
|
||||
- Configmap field `body-size` was replaced with `proxy-body-size`
|
||||
|
||||
*Changes:*
|
||||
|
||||
- [X] [#184](https://github.com/kubernetes/ingress/pull/184) Fix template error
|
||||
- [X] [#179](https://github.com/kubernetes/ingress/pull/179) Allows the usage of Default SSL Cert
|
||||
- [X] [#178](https://github.com/kubernetes/ingress/pull/178) Add initialization of proxy variable
|
||||
- [X] [#177](https://github.com/kubernetes/ingress/pull/177) Refactoring sysctlFSFileMax helper
|
||||
- [X] [#176](https://github.com/kubernetes/ingress/pull/176) Fix TLS does not get updated when changed
|
||||
- [X] [#174](https://github.com/kubernetes/ingress/pull/174) Update nginx to 1.11.9
|
||||
- [X] [#172](https://github.com/kubernetes/ingress/pull/172) add some unit test cases for some packages under folder "core.pkg.ingress"
|
||||
- [X] [#168](https://github.com/kubernetes/ingress/pull/168) Changes the SSL Temp file to something inside the same SSL Directory
|
||||
- [X] [#165](https://github.com/kubernetes/ingress/pull/165) Fix rate limit issue when more than 2 servers enabled in ingress
|
||||
- [X] [#161](https://github.com/kubernetes/ingress/pull/161) Document some missing parameters and their defaults for NGINX controller
|
||||
- [X] [#158](https://github.com/kubernetes/ingress/pull/158) prefect unit test cases for annotation.proxy
|
||||
- [X] [#156](https://github.com/kubernetes/ingress/pull/156) Fix issue for ratelimit
|
||||
- [X] [#154](https://github.com/kubernetes/ingress/pull/154) add unit test cases for core.pkg.ingress.annotations.cors
|
||||
- [X] [#151](https://github.com/kubernetes/ingress/pull/151) Port in redirect
|
||||
- [X] [#150](https://github.com/kubernetes/ingress/pull/150) Add support for custom header sizes
|
||||
- [X] [#149](https://github.com/kubernetes/ingress/pull/149) Add flag to allow switch off the update of Ingress status
|
||||
- [X] [#148](https://github.com/kubernetes/ingress/pull/148) Add annotation to allow custom body sizes
|
||||
- [X] [#145](https://github.com/kubernetes/ingress/pull/145) fix wrong links and punctuations
|
||||
- [X] [#144](https://github.com/kubernetes/ingress/pull/144) add unit test cases for core.pkg.k8s
|
||||
- [X] [#143](https://github.com/kubernetes/ingress/pull/143) Use protobuf instead of rest to connect to apiserver host and add troubleshooting doc
|
||||
- [X] [#142](https://github.com/kubernetes/ingress/pull/142) Use system fs.max-files as limits instead of hard-coded value
|
||||
- [X] [#141](https://github.com/kubernetes/ingress/pull/141) Add reuse port and backlog to port 80 and 443
|
||||
- [X] [#138](https://github.com/kubernetes/ingress/pull/138) reference to const
|
||||
- [X] [#136](https://github.com/kubernetes/ingress/pull/136) Add content and descriptions about nginx's configuration
|
||||
- [X] [#135](https://github.com/kubernetes/ingress/pull/135) correct improper punctuation
|
||||
- [X] [#134](https://github.com/kubernetes/ingress/pull/134) fix typo
|
||||
- [X] [#133](https://github.com/kubernetes/ingress/pull/133) Add TCP and UDP services removed in migration
|
||||
- [X] [#132](https://github.com/kubernetes/ingress/pull/132) Document nginx controller configuration tweaks
|
||||
- [X] [#128](https://github.com/kubernetes/ingress/pull/128) Add tests and godebug to compare structs
|
||||
- [X] [#126](https://github.com/kubernetes/ingress/pull/126) change the type of imagePullPolicy
|
||||
- [X] [#123](https://github.com/kubernetes/ingress/pull/123) Add resolver configuration to nginx
|
||||
- [X] [#119](https://github.com/kubernetes/ingress/pull/119) add unit test case for annotations.service
|
||||
- [X] [#115](https://github.com/kubernetes/ingress/pull/115) add default_server to listen statement for default backend
|
||||
- [X] [#114](https://github.com/kubernetes/ingress/pull/114) fix typo
|
||||
- [X] [#113](https://github.com/kubernetes/ingress/pull/113) Add condition of enqueue and unit test cases for task.Queue
|
||||
- [X] [#108](https://github.com/kubernetes/ingress/pull/108) annotations: print error and skip if malformed
|
||||
- [X] [#107](https://github.com/kubernetes/ingress/pull/107) fix some wrong links of examples which to be used for nginx
|
||||
- [X] [#103](https://github.com/kubernetes/ingress/pull/103) Update the nginx controller manifests
|
||||
- [X] [#101](https://github.com/kubernetes/ingress/pull/101) Add unit test for strings.StringInSlice
|
||||
- [X] [#99](https://github.com/kubernetes/ingress/pull/99) Update nginx to 1.11.8
|
||||
- [X] [#97](https://github.com/kubernetes/ingress/pull/97) Fix gofmt
|
||||
- [X] [#96](https://github.com/kubernetes/ingress/pull/96) Fix typo PassthrougBackends -> PassthroughBackends
|
||||
- [X] [#95](https://github.com/kubernetes/ingress/pull/95) Deny location mapping in case of specific errors
|
||||
- [X] [#94](https://github.com/kubernetes/ingress/pull/94) Add support to disable server_tokens directive
|
||||
- [X] [#93](https://github.com/kubernetes/ingress/pull/93) Fix sort for catch all server
|
||||
- [X] [#92](https://github.com/kubernetes/ingress/pull/92) Refactoring of nginx configuration deserialization
|
||||
- [X] [#91](https://github.com/kubernetes/ingress/pull/91) Fix x-forwarded-port mapping
|
||||
- [X] [#90](https://github.com/kubernetes/ingress/pull/90) fix the wrong link to build/test/release
|
||||
- [X] [#89](https://github.com/kubernetes/ingress/pull/89) fix the wrong links to the examples and developer documentation
|
||||
- [X] [#88](https://github.com/kubernetes/ingress/pull/88) Fix multiple tls hosts sharing the same secretName
|
||||
- [X] [#86](https://github.com/kubernetes/ingress/pull/86) Update X-Forwarded-Port
|
||||
- [X] [#82](https://github.com/kubernetes/ingress/pull/82) Fix incorrect X-Forwarded-Port for TLS
|
||||
- [X] [#81](https://github.com/kubernetes/ingress/pull/81) Do not push containers to remote repo as part of test-e2e
|
||||
- [X] [#78](https://github.com/kubernetes/ingress/pull/78) Fix #76: hardcode X-Forwarded-Port due to SSL Passthrough
|
||||
- [X] [#77](https://github.com/kubernetes/ingress/pull/77) Add support for IPV6 in dns resolvers
|
||||
- [X] [#66](https://github.com/kubernetes/ingress/pull/66) Start FAQ docs
|
||||
- [X] [#65](https://github.com/kubernetes/ingress/pull/65) Support hostnames in Ingress status
|
||||
- [X] [#64](https://github.com/kubernetes/ingress/pull/64) Sort whitelist list to avoid random orders
|
||||
- [X] [#62](https://github.com/kubernetes/ingress/pull/62) Fix e2e make targets
|
||||
- [X] [#61](https://github.com/kubernetes/ingress/pull/61) Ignore coverage profile files
|
||||
- [X] [#58](https://github.com/kubernetes/ingress/pull/58) Fix "invalid port in upstream" on nginx controller
|
||||
- [X] [#57](https://github.com/kubernetes/ingress/pull/57) Fix invalid port in upstream
|
||||
- [X] [#54](https://github.com/kubernetes/ingress/pull/54) Expand developer docs
|
||||
- [X] [#52](https://github.com/kubernetes/ingress/pull/52) fix typo in variable ProxyRealIPCIDR
|
||||
- [X] [#44](https://github.com/kubernetes/ingress/pull/44) Bump nginx version to one higher than that in contrib
|
||||
- [X] [#36](https://github.com/kubernetes/ingress/pull/36) Add nginx metrics to prometheus
|
||||
- [X] [#34](https://github.com/kubernetes/ingress/pull/34) nginx: also listen on ipv6
|
||||
- [X] [#32](https://github.com/kubernetes/ingress/pull/32) Restart nginx if master process dies
|
||||
- [X] [#31](https://github.com/kubernetes/ingress/pull/31) Add healthz checker
|
||||
- [X] [#25](https://github.com/kubernetes/ingress/pull/25) Fix a data race in TestFileWatcher
|
||||
- [X] [#12](https://github.com/kubernetes/ingress/pull/12) Split implementations from generic code
|
||||
- [X] [#10](https://github.com/kubernetes/ingress/pull/10) Copy Ingress history from kubernetes/contrib
|
||||
- [X] [#1498](https://github.com/kubernetes/contrib/pull/1498) Refactoring of template handling
|
||||
- [X] [#1571](https://github.com/kubernetes/contrib/pull/1571) use POD_NAMESPACE as a namespace in cli parameters
|
||||
- [X] [#1591](https://github.com/kubernetes/contrib/pull/1591) Always listen on port 443, even without ingress rules
|
||||
|
@ -57,7 +147,7 @@ Changelog
|
|||
- [X] [#1136](https://github.com/kubernetes/contrib/pull/1136) Fix nginx rewrite rule order
|
||||
- [X] [#1144](https://github.com/kubernetes/contrib/pull/1144) Add cidr whitelist support
|
||||
- [X] [#1230](https://github.com/kubernetes/contrib/pull/1130) Improve docs and examples
|
||||
- [X] [#1258](https://github.com/kubernetes/contrib/pull/1258) Avoid sync without a reachable
|
||||
- [X] [#1258](https://github.com/kubernetes/contrib/pull/1258) Avoid sync without a reachable
|
||||
- [X] [#1235](https://github.com/kubernetes/contrib/pull/1235) Fix stats by country in nginx status page
|
||||
- [X] [#1236](https://github.com/kubernetes/contrib/pull/1236) Update nginx to add dynamic TLS records and spdy
|
||||
- [X] [#1238](https://github.com/kubernetes/contrib/pull/1238) Add support for dynamic TLS records and spdy
|
||||
|
|
|
@ -3,7 +3,7 @@ all: push
|
|||
BUILDTAGS=
|
||||
|
||||
# Use the 0.0 tag for testing, it shouldn't clobber any release builds
|
||||
RELEASE?=0.8.4
|
||||
RELEASE?=0.9.0-beta.1
|
||||
PREFIX?=gcr.io/google_containers/nginx-ingress-controller
|
||||
GOOS?=linux
|
||||
|
||||
|
@ -20,10 +20,10 @@ build: clean
|
|||
-ldflags "-s -w -X ${PKG}/pkg/version.RELEASE=${RELEASE} -X ${PKG}/pkg/version.COMMIT=${COMMIT} -X ${PKG}/pkg/version.REPO=${REPO_INFO}" \
|
||||
-o rootfs/nginx-ingress-controller ${PKG}/pkg/cmd/controller
|
||||
|
||||
container:
|
||||
container: build
|
||||
docker build -t $(PREFIX):$(RELEASE) rootfs
|
||||
|
||||
push:
|
||||
push: container
|
||||
gcloud docker push $(PREFIX):$(RELEASE)
|
||||
|
||||
fmt:
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
There are 3 ways to customize NGINX:
|
||||
|
||||
1. [ConfigMap](#allowed-parameters-in-configuration-configmap): create a stand alone ConfigMap, use this if you want a different global configuration
|
||||
2. [annotations](#annotations): use this if you want a specific configuration for the site defined in the Ingress rule
|
||||
1. [ConfigMap](#allowed-parameters-in-configuration-configmap): create a stand alone ConfigMap, use this if you want a different global configuration.
|
||||
2. [annotations](#annotations): use this if you want a specific configuration for the site defined in the Ingress rule.
|
||||
3. custom template: when more specific settings are required, like [open_file_cache](http://nginx.org/en/docs/http/ngx_http_core_module.html#open_file_cache), custom [log_format](http://nginx.org/en/docs/http/ngx_http_log_module.html#log_format), adjust [listen](http://nginx.org/en/docs/http/ngx_http_core_module.html#listen) options as `rcvbuf` or when is not possible to change an through the ConfigMap.
|
||||
|
||||
|
||||
|
@ -97,7 +97,7 @@ Please check the [custom upstream check](examples/custom-upstream-check/README.m
|
|||
|
||||
### Authentication
|
||||
|
||||
Is possible to add authentication adding additional annotations in the Ingress rule. The source of the authentication is a secret that contains usernames and passwords inside the the key `auth`
|
||||
Is possible to add authentication adding additional annotations in the Ingress rule. The source of the authentication is a secret that contains usernames and passwords inside the the key `auth`.
|
||||
|
||||
The annotations are:
|
||||
|
||||
|
@ -154,16 +154,16 @@ The annotations `ingress.kubernetes.io/limit-connections` and `ingress.kubernete
|
|||
If you specify both annotations in a single Ingress rule, `limit-rps` takes precedence.
|
||||
|
||||
|
||||
### Secure upstreams
|
||||
### Secure backends
|
||||
|
||||
By default NGINX uses `http` to reach the services. Adding the annotation `ingress.kubernetes.io/secure-backends: "true"` in the Ingress rule changes the protocol to `https`.
|
||||
|
||||
|
||||
### Server-side HTTPS enforcement through redirect
|
||||
|
||||
By default the controller redirects (301) to `HTTPS` if TLS is enabled for that ingress. If you want to disable that behaviour globally, you can use `ssl-redirect: "false"` in the NGINX config map
|
||||
By default the controller redirects (301) to `HTTPS` if TLS is enabled for that ingress. If you want to disable that behaviour globally, you can use `ssl-redirect: "false"` in the NGINX config map.
|
||||
|
||||
To configure this feature for specific ingress resources, you can use the `ingress.kubernetes.io/ssl-redirect: "false"` annotation in the particular resource
|
||||
To configure this feature for specific ingress resources, you can use the `ingress.kubernetes.io/ssl-redirect: "false"` annotation in the particular resource.
|
||||
|
||||
|
||||
### Whitelist source range
|
||||
|
@ -177,9 +177,9 @@ To configure this setting globally for all Ingress rules, the `whitelist-source-
|
|||
Please check the [whitelist](examples/whitelist/README.md) example.
|
||||
|
||||
|
||||
### **Allowed parameters in configuration ConfigMap:**
|
||||
### **Allowed parameters in configuration ConfigMap**
|
||||
|
||||
**body-size:** Sets the maximum allowed size of the client request body. See NGINX [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size).
|
||||
**proxy-body-size:** Sets the maximum allowed size of the client request body. See NGINX [client_max_body_size](http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size).
|
||||
|
||||
|
||||
**custom-http-errors:** Enables which HTTP codes should be passed for processing with the [error_page directive](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page).
|
||||
|
@ -188,6 +188,9 @@ Setting at least one code also enables [proxy_intercept_errors](http://nginx.org
|
|||
Example usage: `custom-http-errors: 404,415`
|
||||
|
||||
|
||||
**enable-dynamic-tls-records:** Enables dynamically sized TLS records to improve time-to-first-byte. Enabled by default. See [CloudFlare's blog](https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency) for more information.
|
||||
|
||||
|
||||
**enable-sticky-sessions:** Enables sticky sessions using cookies. This is provided by [nginx-sticky-module-ng](https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng) module.
|
||||
|
||||
|
||||
|
@ -222,7 +225,7 @@ http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
|
|||
**max-worker-connections:** Sets the maximum number of simultaneous connections that can be opened by each [worker process](http://nginx.org/en/docs/ngx_core_module.html#worker_connections).
|
||||
|
||||
|
||||
**proxy-buffer-size:** Sets the size of the buffer used for [reading the first part of the response](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) received from the proxied server. This part usually contains a small response header.`
|
||||
**proxy-buffer-size:** Sets the size of the buffer used for [reading the first part of the response](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) received from the proxied server. This part usually contains a small response header.
|
||||
|
||||
|
||||
**proxy-connect-timeout:** Sets the timeout for [establishing a connection with a proxied server](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_connect_timeout). It should be noted that this timeout cannot usually exceed 75 seconds.
|
||||
|
@ -248,8 +251,10 @@ http://nginx.org/en/docs/http/ngx_http_core_module.html#server_names_hash_bucket
|
|||
http://nginx.org/en/docs/hash.html
|
||||
|
||||
|
||||
**server-tokens:** Send NGINX Server header in responses and display NGINX version in error pages. Enabled by default.
|
||||
|
||||
**map-hash-bucket-size:** Sets the bucket size for the [map variables hash tables](http://nginx.org/en/docs/http/ngx_http_map_module.html#map_hash_bucket_size). The details of setting up hash tables are provided in a separate [document](http://nginx.org/en/docs/hash.html)
|
||||
|
||||
**map-hash-bucket-size:** Sets the bucket size for the [map variables hash tables](http://nginx.org/en/docs/http/ngx_http_map_module.html#map_hash_bucket_size). The details of setting up hash tables are provided in a separate [document](http://nginx.org/en/docs/hash.html).
|
||||
|
||||
|
||||
**ssl-buffer-size:** Sets the size of the [SSL buffer](http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_buffer_size) used for sending data.
|
||||
|
@ -268,7 +273,7 @@ The recommendation above prioritizes algorithms that provide perfect [forward se
|
|||
Please check the [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/).
|
||||
|
||||
|
||||
**ssl-dh-param:** sets the Base64 string that contains Diffie-Hellman key to help with "Perfect Forward Secrecy."
|
||||
**ssl-dh-param:** sets the Base64 string that contains Diffie-Hellman key to help with "Perfect Forward Secrecy".
|
||||
https://www.openssl.org/docs/manmaster/apps/dhparam.html
|
||||
https://wiki.mozilla.org/Security/Server_Side_TLS#DHE_handshake_and_dhparam
|
||||
http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_dhparam
|
||||
|
@ -319,6 +324,9 @@ The default mime type list to compress is: `application/atom+xml application/jav
|
|||
**use-proxy-protocol:** Enables or disables the [PROXY protocol](https://www.nginx.com/resources/admin-guide/proxy-protocol/) to receive client connection (real IP address) information passed through proxy servers and load balancers such as HAProxy and Amazon Elastic Load Balancer (ELB).
|
||||
|
||||
|
||||
**whitelist-source-range:** Sets the default whitelisted IPs for each `server` block. This can be overwritten by an annotation on an Ingress rule. See [ngx_http_access_module](http://nginx.org/en/docs/http/ngx_http_access_module.html).
|
||||
|
||||
|
||||
**worker-processes:** Sets the number of [worker processes](http://nginx.org/en/docs/ngx_core_module.html#worker_processes). The default of "auto" means number of available CPU cores.
|
||||
|
||||
|
||||
|
@ -330,14 +338,16 @@ The following table shows the options, the default value and a description.
|
|||
|---------------------------|------|
|
||||
|body-size|1m|
|
||||
|custom-http-errors|" "|
|
||||
|enable-dynamic-tls-records|"true"|
|
||||
|enable-sticky-sessions|"false"|
|
||||
|enable-vts-status|"false"|
|
||||
|error-log-level|notice|
|
||||
|gzip-types||
|
||||
|gzip-types|see use-gzip description above|
|
||||
|hsts|"true"|
|
||||
|hsts-include-subdomains|"true"|
|
||||
|hsts-max-age|"15724800"|
|
||||
|keep-alive|"75"|
|
||||
|map-hash-bucket-size|"64"|
|
||||
|max-worker-connections|"16384"|
|
||||
|proxy-connect-timeout|"5"|
|
||||
|proxy-read-timeout|"60"|
|
||||
|
@ -346,8 +356,10 @@ The following table shows the options, the default value and a description.
|
|||
|retry-non-idempotent|"false"|
|
||||
|server-name-hash-bucket-size|"64"|
|
||||
|server-name-hash-max-size|"512"|
|
||||
|server-tokens|"true"|
|
||||
|ssl-buffer-size|4k|
|
||||
|ssl-ciphers||
|
||||
|ssl-dh-param|value from openssl|
|
||||
|ssl-protocols|TLSv1 TLSv1.1 TLSv1.2|
|
||||
|ssl-session-cache|"true"|
|
||||
|ssl-session-cache-size|10m|
|
||||
|
@ -356,6 +368,7 @@ The following table shows the options, the default value and a description.
|
|||
|use-gzip|"true"|
|
||||
|use-http2|"true"|
|
||||
|vts-status-zone-size|10m|
|
||||
|whitelist-source-range|permit all|
|
||||
|worker-processes|number of CPUs|
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
|
@ -58,7 +60,10 @@ func newNGINXController() ingress.Controller {
|
|||
if ngx == "" {
|
||||
ngx = binary
|
||||
}
|
||||
n := NGINXController{binary: ngx}
|
||||
n := NGINXController{
|
||||
binary: ngx,
|
||||
configmap: &api.ConfigMap{},
|
||||
}
|
||||
|
||||
var onChange func()
|
||||
onChange = func() {
|
||||
|
@ -87,13 +92,15 @@ Error loading new template : %v
|
|||
|
||||
go n.Start()
|
||||
|
||||
return n
|
||||
return ingress.Controller(&n)
|
||||
}
|
||||
|
||||
// NGINXController ...
|
||||
type NGINXController struct {
|
||||
t *ngx_template.Template
|
||||
|
||||
configmap *api.ConfigMap
|
||||
|
||||
binary string
|
||||
}
|
||||
|
||||
|
@ -170,11 +177,31 @@ func (n NGINXController) Reload(data []byte) ([]byte, bool, error) {
|
|||
|
||||
// BackendDefaults returns the nginx defaults
|
||||
func (n NGINXController) BackendDefaults() defaults.Backend {
|
||||
if n.configmap == nil {
|
||||
d := config.NewDefault()
|
||||
return d.Backend
|
||||
}
|
||||
|
||||
return n.backendDefaults()
|
||||
}
|
||||
|
||||
func (n *NGINXController) backendDefaults() defaults.Backend {
|
||||
d := config.NewDefault()
|
||||
config := &mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
WeaklyTypedInput: true,
|
||||
Result: &d,
|
||||
TagName: "json",
|
||||
}
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
if err != nil {
|
||||
glog.Warningf("unexpected error merging defaults: %v", err)
|
||||
}
|
||||
decoder.Decode(n.configmap.Data)
|
||||
return d.Backend
|
||||
}
|
||||
|
||||
// IsReloadRequired check if the new configuration file is different
|
||||
// isReloadRequired check if the new configuration file is different
|
||||
// from the current one.
|
||||
func (n NGINXController) isReloadRequired(data []byte) bool {
|
||||
in, err := os.Open(cfgPath)
|
||||
|
@ -249,6 +276,11 @@ Error: %v
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetConfig ...
|
||||
func (n *NGINXController) SetConfig(cmap *api.ConfigMap) {
|
||||
n.configmap = cmap
|
||||
}
|
||||
|
||||
// OnUpdate is called by syncQueue in https://github.com/aledbf/ingress-controller/blob/master/pkg/ingress/controller/controller.go#L82
|
||||
// periodically to keep the configuration in sync.
|
||||
//
|
||||
|
@ -257,7 +289,7 @@ Error: %v
|
|||
// write the configuration file
|
||||
// returning nill implies the backend will be reloaded.
|
||||
// if an error is returned means requeue the update
|
||||
func (n NGINXController) OnUpdate(cmap *api.ConfigMap, ingressCfg ingress.Configuration) ([]byte, error) {
|
||||
func (n *NGINXController) OnUpdate(ingressCfg ingress.Configuration) ([]byte, error) {
|
||||
var longestName int
|
||||
var serverNames int
|
||||
for _, srv := range ingressCfg.Servers {
|
||||
|
@ -267,7 +299,7 @@ func (n NGINXController) OnUpdate(cmap *api.ConfigMap, ingressCfg ingress.Config
|
|||
}
|
||||
}
|
||||
|
||||
cfg := ngx_template.ReadConfig(cmap)
|
||||
cfg := ngx_template.ReadConfig(n.configmap.Data)
|
||||
|
||||
// NGINX cannot resize the has tables used to store server names.
|
||||
// For this reason we check if the defined size defined is correct
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/sysctl"
|
||||
|
||||
|
@ -42,14 +43,14 @@ func sysctlSomaxconn() int {
|
|||
// sysctlFSFileMax returns the value of fs.file-max, i.e.
|
||||
// maximum number of open file descriptors
|
||||
func sysctlFSFileMax() int {
|
||||
maxConns, err := sysctl.New().GetSysctl("fs/file-max")
|
||||
var rLimit syscall.Rlimit
|
||||
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
|
||||
if err != nil {
|
||||
glog.Errorf("unexpected error reading system maximum number of open file descriptors (fs.file-max): %v", err)
|
||||
glog.Errorf("unexpected error reading system maximum number of open file descriptors (RLIMIT_NOFILE): %v", err)
|
||||
// returning 0 means don't render the value
|
||||
return 0
|
||||
}
|
||||
|
||||
return maxConns
|
||||
return int(rLimit.Max)
|
||||
}
|
||||
|
||||
func diff(b1, b2 []byte) ([]byte, error) {
|
||||
|
|
|
@ -78,15 +78,16 @@ var (
|
|||
type Configuration struct {
|
||||
defaults.Backend `json:",squash"`
|
||||
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
|
||||
// Sets the maximum allowed size of the client request body
|
||||
BodySize string `json:"body-size,omitempty"`
|
||||
|
||||
// EnableDynamicTLSRecords enables dynamic TLS record sizes
|
||||
// https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency
|
||||
// By default this is enabled
|
||||
EnableDynamicTLSRecords bool `json:"enable-dynamic-tls-records"`
|
||||
|
||||
// ClientHeaderBufferSize allows to configure a custom buffer
|
||||
// size for reading client request header
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_header_buffer_size
|
||||
ClientHeaderBufferSize string `json:"client-header-buffer-size"`
|
||||
|
||||
// EnableSPDY enables spdy and use ALPN and NPN to advertise the availability of the two protocols
|
||||
// https://blog.cloudflare.com/open-sourcing-our-nginx-http-2-spdy-code
|
||||
// By default this is enabled
|
||||
|
@ -132,6 +133,12 @@ type Configuration struct {
|
|||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
|
||||
KeepAlive int `json:"keep-alive,omitempty"`
|
||||
|
||||
// LargeClientHeaderBuffers Sets the maximum number and size of buffers used for reading
|
||||
// large client request header.
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#large_client_header_buffers
|
||||
// Default: 4 8k
|
||||
LargeClientHeaderBuffers string `json:"large-client-header-buffers"`
|
||||
|
||||
// Maximum number of simultaneous connections that can be opened by each worker process
|
||||
// http://nginx.org/en/docs/ngx_core_module.html#worker_connections
|
||||
MaxWorkerConnections int `json:"max-worker-connections,omitempty"`
|
||||
|
@ -230,7 +237,7 @@ type Configuration struct {
|
|||
// NewDefault returns the default nginx configuration
|
||||
func NewDefault() Configuration {
|
||||
cfg := Configuration{
|
||||
BodySize: bodySize,
|
||||
ClientHeaderBufferSize: "1k",
|
||||
EnableDynamicTLSRecords: true,
|
||||
EnableSPDY: false,
|
||||
ErrorLogLevel: errorLevel,
|
||||
|
@ -239,6 +246,7 @@ func NewDefault() Configuration {
|
|||
HSTSMaxAge: hstsMaxAge,
|
||||
GzipTypes: gzipTypes,
|
||||
KeepAlive: 75,
|
||||
LargeClientHeaderBuffers: "4 8k",
|
||||
MaxWorkerConnections: 16384,
|
||||
MapHashBucketSize: 64,
|
||||
ProxyRealIPCIDR: defIPCIDR,
|
||||
|
@ -259,6 +267,7 @@ func NewDefault() Configuration {
|
|||
UseHTTP2: true,
|
||||
UseUpstreamHealthChecks: false,
|
||||
Backend: defaults.Backend{
|
||||
ProxyBodySize: bodySize,
|
||||
ProxyConnectTimeout: 5,
|
||||
ProxyReadTimeout: 60,
|
||||
ProxySendTimeout: 60,
|
||||
|
@ -267,6 +276,7 @@ func NewDefault() Configuration {
|
|||
CustomHTTPErrors: []int{},
|
||||
WhitelistSourceRange: []string{},
|
||||
SkipAccessLogURLs: []string{},
|
||||
UsePortInRedirects: false,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,6 @@ import (
|
|||
"github.com/golang/glog"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/net/dns"
|
||||
)
|
||||
|
@ -36,13 +34,21 @@ const (
|
|||
)
|
||||
|
||||
// ReadConfig obtains the configuration defined by the user merged with the defaults.
|
||||
func ReadConfig(conf *api.ConfigMap) config.Configuration {
|
||||
func ReadConfig(src map[string]string) config.Configuration {
|
||||
conf := map[string]string{}
|
||||
if src != nil {
|
||||
// we need to copy the configmap data because the content is altered
|
||||
for k, v := range src {
|
||||
conf[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
errors := make([]int, 0)
|
||||
skipUrls := make([]string, 0)
|
||||
whitelist := make([]string, 0)
|
||||
|
||||
if val, ok := conf.Data[customHTTPErrors]; ok {
|
||||
delete(conf.Data, customHTTPErrors)
|
||||
if val, ok := conf[customHTTPErrors]; ok {
|
||||
delete(conf, customHTTPErrors)
|
||||
for _, i := range strings.Split(val, ",") {
|
||||
j, err := strconv.Atoi(i)
|
||||
if err != nil {
|
||||
|
@ -52,12 +58,12 @@ func ReadConfig(conf *api.ConfigMap) config.Configuration {
|
|||
}
|
||||
}
|
||||
}
|
||||
if val, ok := conf.Data[skipAccessLogUrls]; ok {
|
||||
delete(conf.Data, skipAccessLogUrls)
|
||||
if val, ok := conf[skipAccessLogUrls]; ok {
|
||||
delete(conf, skipAccessLogUrls)
|
||||
skipUrls = strings.Split(val, ",")
|
||||
}
|
||||
if val, ok := conf.Data[whitelistSourceRange]; ok {
|
||||
delete(conf.Data, whitelistSourceRange)
|
||||
if val, ok := conf[whitelistSourceRange]; ok {
|
||||
delete(conf, whitelistSourceRange)
|
||||
whitelist = append(whitelist, strings.Split(val, ",")...)
|
||||
}
|
||||
|
||||
|
@ -84,7 +90,7 @@ func ReadConfig(conf *api.ConfigMap) config.Configuration {
|
|||
if err != nil {
|
||||
glog.Warningf("unexpected error merging defaults: %v", err)
|
||||
}
|
||||
err = decoder.Decode(conf.Data)
|
||||
err = decoder.Decode(conf)
|
||||
if err != nil {
|
||||
glog.Warningf("unexpected error merging defaults: %v", err)
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/net/dns"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func TestFilterErrors(t *testing.T) {
|
||||
|
@ -34,17 +33,15 @@ func TestFilterErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMergeConfigMapToStruct(t *testing.T) {
|
||||
conf := &api.ConfigMap{
|
||||
Data: map[string]string{
|
||||
"custom-http-errors": "300,400,demo",
|
||||
"proxy-read-timeout": "1",
|
||||
"proxy-send-timeout": "2",
|
||||
"skip-access-log-urls": "/log,/demo,/test",
|
||||
"use-proxy-protocol": "true",
|
||||
"use-gzip": "true",
|
||||
"enable-dynamic-tls-records": "false",
|
||||
"gzip-types": "text/html",
|
||||
},
|
||||
conf := map[string]string{
|
||||
"custom-http-errors": "300,400,demo",
|
||||
"proxy-read-timeout": "1",
|
||||
"proxy-send-timeout": "2",
|
||||
"skip-access-log-urls": "/log,/demo,/test",
|
||||
"use-proxy-protocol": "true",
|
||||
"use-gzip": "true",
|
||||
"enable-dynamic-tls-records": "false",
|
||||
"gzip-types": "text/html",
|
||||
}
|
||||
def := config.NewDefault()
|
||||
def.CustomHTTPErrors = []int{300, 400}
|
||||
|
@ -68,7 +65,7 @@ func TestMergeConfigMapToStruct(t *testing.T) {
|
|||
|
||||
def = config.NewDefault()
|
||||
def.Resolver = h
|
||||
to = ReadConfig(&api.ConfigMap{})
|
||||
to = ReadConfig(map[string]string{})
|
||||
if diff := pretty.Compare(to, def); diff != "" {
|
||||
t.Errorf("unexpected diff: (-got +want)\n%s", diff)
|
||||
}
|
||||
|
@ -76,10 +73,8 @@ func TestMergeConfigMapToStruct(t *testing.T) {
|
|||
def = config.NewDefault()
|
||||
def.Resolver = h
|
||||
def.WhitelistSourceRange = []string{"1.1.1.1/32"}
|
||||
to = ReadConfig(&api.ConfigMap{
|
||||
Data: map[string]string{
|
||||
"whitelist-source-range": "1.1.1.1/32",
|
||||
},
|
||||
to = ReadConfig(map[string]string{
|
||||
"whitelist-source-range": "1.1.1.1/32",
|
||||
})
|
||||
|
||||
if diff := pretty.Compare(to, def); diff != "" {
|
||||
|
|
|
@ -26,6 +26,8 @@ import (
|
|||
"strings"
|
||||
text_template "text/template"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
|
@ -328,11 +330,11 @@ func buildProxyPass(b interface{}, loc interface{}) string {
|
|||
// rate limiting of request. Each Ingress rule could have up to two zones, one
|
||||
// for connection limit by IP address and other for limiting request per second
|
||||
func buildRateLimitZones(input interface{}) []string {
|
||||
zones := []string{}
|
||||
zones := sets.String{}
|
||||
|
||||
servers, ok := input.([]*ingress.Server)
|
||||
if !ok {
|
||||
return zones
|
||||
return zones.List()
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
|
@ -342,20 +344,24 @@ func buildRateLimitZones(input interface{}) []string {
|
|||
zone := fmt.Sprintf("limit_conn_zone $binary_remote_addr zone=%v:%vm;",
|
||||
loc.RateLimit.Connections.Name,
|
||||
loc.RateLimit.Connections.SharedSize)
|
||||
zones = append(zones, zone)
|
||||
if !zones.Has(zone) {
|
||||
zones.Insert(zone)
|
||||
}
|
||||
}
|
||||
|
||||
if loc.RateLimit.RPS.Limit > 0 {
|
||||
zone := fmt.Sprintf("limit_conn_zone $binary_remote_addr zone=%v:%vm rate=%vr/s;",
|
||||
loc.RateLimit.Connections.Name,
|
||||
loc.RateLimit.Connections.SharedSize,
|
||||
loc.RateLimit.Connections.Limit)
|
||||
zones = append(zones, zone)
|
||||
zone := fmt.Sprintf("limit_req_zone $binary_remote_addr zone=%v:%vm rate=%vr/s;",
|
||||
loc.RateLimit.RPS.Name,
|
||||
loc.RateLimit.RPS.SharedSize,
|
||||
loc.RateLimit.RPS.Limit)
|
||||
if !zones.Has(zone) {
|
||||
zones.Insert(zone)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return zones
|
||||
return zones.List()
|
||||
}
|
||||
|
||||
// buildRateLimit produces an array of limit_req to be used inside the Path of
|
||||
|
@ -376,7 +382,7 @@ func buildRateLimit(input interface{}) []string {
|
|||
|
||||
if loc.RateLimit.RPS.Limit > 0 {
|
||||
limit := fmt.Sprintf("limit_req zone=%v burst=%v nodelay;",
|
||||
loc.RateLimit.Connections.Name, loc.RateLimit.Connections.Burst)
|
||||
loc.RateLimit.RPS.Name, loc.RateLimit.RPS.Burst)
|
||||
limits = append(limits, limit)
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,9 @@ http {
|
|||
|
||||
keepalive_timeout {{ $cfg.KeepAlive }}s;
|
||||
|
||||
client_header_buffer_size {{ $cfg.ClientHeaderBufferSize }};
|
||||
large_client_header_buffers {{ $cfg.LargeClientHeaderBuffers }};
|
||||
|
||||
types_hash_max_size 2048;
|
||||
server_names_hash_max_size {{ $cfg.ServerNameHashMaxSize }};
|
||||
server_names_hash_bucket_size {{ $cfg.ServerNameHashBucketSize }};
|
||||
|
@ -70,9 +73,7 @@ http {
|
|||
gzip_proxied any;
|
||||
{{ end }}
|
||||
|
||||
server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }};
|
||||
|
||||
client_max_body_size "{{ $cfg.BodySize }}";
|
||||
server_tokens {{ if $cfg.ShowServerTokens }}on{{ else }}off{{ end }};
|
||||
|
||||
log_format upstreaminfo '{{ if $cfg.UseProxyProtocol }}$proxy_protocol_addr{{ else }}$remote_addr{{ end }} - '
|
||||
'[$proxy_add_x_forwarded_for] - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" '
|
||||
|
@ -209,7 +210,7 @@ http {
|
|||
server_name {{ $server.Hostname }};
|
||||
listen [::]:80{{ if $cfg.UseProxyProtocol }} proxy_protocol{{ end }}{{ if eq $index 0 }} ipv6only=off{{end}}{{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $backlogSize }}{{end}};
|
||||
{{/* Listen on 442 because port 443 is used in the stream section */}}
|
||||
{{ if not (empty $server.SSLCertificate) }}listen 442 {{ if $cfg.UseProxyProtocol }}proxy_protocol{{ end }} ssl {{ if $cfg.UseHTTP2 }}http2{{ end }};
|
||||
{{ if not (empty $server.SSLCertificate) }}listen 442 {{ if $cfg.UseProxyProtocol }}proxy_protocol{{ end }} {{ if eq $server.Hostname "_"}} default_server reuseport backlog={{ $backlogSize }}{{end}} ssl {{ if $cfg.UseHTTP2 }}http2{{ end }};
|
||||
{{/* comment PEM sha is required to detect changes in the generated configuration and force a reload */}}
|
||||
# PEM sha: {{ $server.SSLPemChecksum }}
|
||||
ssl_certificate {{ $server.SSLCertificate }};
|
||||
|
@ -250,6 +251,8 @@ http {
|
|||
{{ end }}
|
||||
|
||||
location {{ $path }} {
|
||||
set $proxy_upstream_name "{{ $location.Backend }}";
|
||||
|
||||
{{ if isLocationAllowed $location }}
|
||||
{{ if gt (len $location.Whitelist.CIDR) 0 }}
|
||||
{{ range $ip := $location.Whitelist.CIDR }}
|
||||
|
@ -257,6 +260,8 @@ http {
|
|||
deny all;
|
||||
{{ end }}
|
||||
|
||||
port_in_redirect {{ if $location.UsePortInRedirects }}on{{ else }}off{{ end }};
|
||||
|
||||
{{ if not (empty $authPath) }}
|
||||
# this location requires authentication
|
||||
auth_request {{ $authPath }};
|
||||
|
@ -288,6 +293,8 @@ http {
|
|||
{{ template "CORS" }}
|
||||
{{ end }}
|
||||
|
||||
client_max_body_size "{{ $location.Proxy.BodySize }}";
|
||||
|
||||
proxy_set_header Host $host;
|
||||
|
||||
# Pass Real IP
|
||||
|
@ -321,7 +328,6 @@ http {
|
|||
proxy_set_header Accept-Encoding "";
|
||||
{{ end }}
|
||||
|
||||
set $proxy_upstream_name "{{ $location.Backend }}";
|
||||
{{ buildProxyPass $backends $location }}
|
||||
{{ else }}
|
||||
#{{ $location.Denied }}
|
||||
|
|
62
core/pkg/ingress/annotations/cors/main_test.go
Normal file
62
core/pkg/ingress/annotations/cors/main_test.go
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
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 cors
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
const (
|
||||
notCorsAnnotation = "ingress.kubernetes.io/enable-not-cors"
|
||||
)
|
||||
|
||||
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 bool
|
||||
}{
|
||||
{map[string]string{annotation: "true"}, true},
|
||||
{map[string]string{annotation: "false"}, false},
|
||||
{map[string]string{notCorsAnnotation: "true"}, false},
|
||||
{map[string]string{}, false},
|
||||
{nil, false},
|
||||
}
|
||||
|
||||
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 %t but returned %t, annotations: %s", testCase.expected, result, testCase.annotations)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ func (a ingAnnotations) parseBool(name string) (bool, error) {
|
|||
if ok {
|
||||
b, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
return false, errors.NewInvalidAnnotationContent(name)
|
||||
return false, errors.NewInvalidAnnotationContent(name, val)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func (a ingAnnotations) parseInt(name string) (int, error) {
|
|||
if ok {
|
||||
i, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return 0, errors.NewInvalidAnnotationContent(name)
|
||||
return 0, errors.NewInvalidAnnotationContent(name, val)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
|
48
core/pkg/ingress/annotations/portinredirect/main.go
Normal file
48
core/pkg/ingress/annotations/portinredirect/main.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
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 portinredirect
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
annotation = "ingress.kubernetes.io/use-port-in-redirects"
|
||||
)
|
||||
|
||||
type portInRedirect struct {
|
||||
backendResolver resolver.DefaultBackend
|
||||
}
|
||||
|
||||
// NewParser creates a new port in redirect annotation parser
|
||||
func NewParser(db resolver.DefaultBackend) parser.IngressAnnotation {
|
||||
return portInRedirect{db}
|
||||
}
|
||||
|
||||
// Parse parses the annotations contained in the ingress
|
||||
// rule used to indicate if the redirects must
|
||||
func (a portInRedirect) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||
up, err := parser.GetBoolAnnotation(annotation, ing)
|
||||
if err != nil {
|
||||
return a.backendResolver.GetDefaultBackend().UsePortInRedirects, nil
|
||||
}
|
||||
|
||||
return up, nil
|
||||
}
|
120
core/pkg/ingress/annotations/portinredirect/main_test.go
Normal file
120
core/pkg/ingress/annotations/portinredirect/main_test.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
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 portinredirect
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/util/intstr"
|
||||
|
||||
"fmt"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
)
|
||||
|
||||
func buildIngress() *extensions.Ingress {
|
||||
defaultBackend := extensions.IngressBackend{
|
||||
ServiceName: "default-backend",
|
||||
ServicePort: intstr.FromInt(80),
|
||||
}
|
||||
|
||||
return &extensions.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: extensions.IngressSpec{
|
||||
Backend: &extensions.IngressBackend{
|
||||
ServiceName: "default-backend",
|
||||
ServicePort: intstr.FromInt(80),
|
||||
},
|
||||
Rules: []extensions.IngressRule{
|
||||
{
|
||||
Host: "foo.bar.com",
|
||||
IngressRuleValue: extensions.IngressRuleValue{
|
||||
HTTP: &extensions.HTTPIngressRuleValue{
|
||||
Paths: []extensions.HTTPIngressPath{
|
||||
{
|
||||
Path: "/foo",
|
||||
Backend: defaultBackend,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type mockBackend struct {
|
||||
usePortInRedirects bool
|
||||
}
|
||||
|
||||
func (m mockBackend) GetDefaultBackend() defaults.Backend {
|
||||
return defaults.Backend{UsePortInRedirects: m.usePortInRedirects}
|
||||
}
|
||||
|
||||
func TestPortInRedirect(t *testing.T) {
|
||||
tests := []struct {
|
||||
title string
|
||||
usePort *bool
|
||||
def bool
|
||||
exp bool
|
||||
}{
|
||||
{"false - default false", newFalse(), false, false},
|
||||
{"false - default true", newFalse(), true, false},
|
||||
{"no annotation - default false", nil, false, false},
|
||||
{"no annotation - default true", nil, true, true},
|
||||
{"true - default true", newTrue(), true, true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
if test.usePort != nil {
|
||||
data[annotation] = fmt.Sprintf("%v", *test.usePort)
|
||||
}
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
i, err := NewParser(mockBackend{test.def}).Parse(ing)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error parsing a valid")
|
||||
}
|
||||
p, ok := i.(bool)
|
||||
if !ok {
|
||||
t.Errorf("expected a bool type")
|
||||
}
|
||||
|
||||
if p != test.exp {
|
||||
t.Errorf("%v: expected \"%v\" but \"%v\" was returned", test.title, test.exp, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newTrue() *bool {
|
||||
b := true
|
||||
return &b
|
||||
}
|
||||
|
||||
func newFalse() *bool {
|
||||
b := false
|
||||
return &b
|
||||
}
|
|
@ -24,6 +24,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
bodySize = "ingress.kubernetes.io/proxy-body-size"
|
||||
connect = "ingress.kubernetes.io/proxy-connect-timeout"
|
||||
send = "ingress.kubernetes.io/proxy-send-timeout"
|
||||
read = "ingress.kubernetes.io/proxy-read-timeout"
|
||||
|
@ -32,6 +33,7 @@ const (
|
|||
|
||||
// Configuration returns the proxy timeout to use in the upstream server/s
|
||||
type Configuration struct {
|
||||
BodySize string `json:"bodySize"`
|
||||
ConnectTimeout int `json:"conectTimeout"`
|
||||
SendTimeout int `json:"sendTimeout"`
|
||||
ReadTimeout int `json:"readTimeout"`
|
||||
|
@ -66,10 +68,15 @@ func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
|
|||
rt = defBackend.ProxyReadTimeout
|
||||
}
|
||||
|
||||
bs, err := parser.GetStringAnnotation(bufferSize, ing)
|
||||
if err != nil || bs == "" {
|
||||
bs = defBackend.ProxyBufferSize
|
||||
bufs, err := parser.GetStringAnnotation(bufferSize, ing)
|
||||
if err != nil || bufs == "" {
|
||||
bufs = defBackend.ProxyBufferSize
|
||||
}
|
||||
|
||||
return &Configuration{ct, st, rt, bs}, nil
|
||||
bs, err := parser.GetStringAnnotation(bodySize, ing)
|
||||
if err != nil || bs == "" {
|
||||
bs = defBackend.ProxyBodySize
|
||||
}
|
||||
|
||||
return &Configuration{bs, ct, st, rt, bufs}, nil
|
||||
}
|
||||
|
|
|
@ -65,7 +65,14 @@ type mockBackend struct {
|
|||
}
|
||||
|
||||
func (m mockBackend) GetDefaultBackend() defaults.Backend {
|
||||
return defaults.Backend{UpstreamFailTimeout: 1}
|
||||
return defaults.Backend{
|
||||
UpstreamFailTimeout: 1,
|
||||
ProxyConnectTimeout: 10,
|
||||
ProxySendTimeout: 15,
|
||||
ProxyReadTimeout: 20,
|
||||
ProxyBufferSize: "10k",
|
||||
ProxyBodySize: "3k",
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxy(t *testing.T) {
|
||||
|
@ -76,15 +83,16 @@ func TestProxy(t *testing.T) {
|
|||
data[send] = "2"
|
||||
data[read] = "3"
|
||||
data[bufferSize] = "1k"
|
||||
data[bodySize] = "2k"
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
i, err := NewParser(mockBackend{}).Parse(ing)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error parsing a valid")
|
||||
t.Fatalf("unexpected error parsing a valid")
|
||||
}
|
||||
p, ok := i.(*Configuration)
|
||||
if !ok {
|
||||
t.Errorf("expected a Configuration type")
|
||||
t.Fatalf("expected a Configuration type")
|
||||
}
|
||||
if p.ConnectTimeout != 1 {
|
||||
t.Errorf("expected 1 as connect-timeout but returned %v", p.ConnectTimeout)
|
||||
|
@ -98,4 +106,38 @@ func TestProxy(t *testing.T) {
|
|||
if p.BufferSize != "1k" {
|
||||
t.Errorf("expected 1k as buffer-size but returned %v", p.BufferSize)
|
||||
}
|
||||
if p.BodySize != "2k" {
|
||||
t.Errorf("expected 2k as body-size but returned %v", p.BodySize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyWithNoAnnotation(t *testing.T) {
|
||||
ing := buildIngress()
|
||||
|
||||
data := map[string]string{}
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
i, err := NewParser(mockBackend{}).Parse(ing)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error parsing a valid")
|
||||
}
|
||||
p, ok := i.(*Configuration)
|
||||
if !ok {
|
||||
t.Fatalf("expected a Configuration type")
|
||||
}
|
||||
if p.ConnectTimeout != 10 {
|
||||
t.Errorf("expected 10 as connect-timeout but returned %v", p.ConnectTimeout)
|
||||
}
|
||||
if p.SendTimeout != 15 {
|
||||
t.Errorf("expected 15 as send-timeout but returned %v", p.SendTimeout)
|
||||
}
|
||||
if p.ReadTimeout != 20 {
|
||||
t.Errorf("expected 20 as read-timeout but returned %v", p.ReadTimeout)
|
||||
}
|
||||
if p.BufferSize != "10k" {
|
||||
t.Errorf("expected 10k as buffer-size but returned %v", p.BufferSize)
|
||||
}
|
||||
if p.BodySize != "3k" {
|
||||
t.Errorf("expected 3k as body-size but returned %v", p.BodySize)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
|||
RPS: Zone{
|
||||
Name: fmt.Sprintf("%v_rps", zoneName),
|
||||
Limit: rps,
|
||||
Burst: conn * defBurst,
|
||||
Burst: rps * defBurst,
|
||||
SharedSize: defSharedSize,
|
||||
},
|
||||
}, nil
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"k8s.io/ingress/core/pkg/ingress/annotations/healthcheck"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/portinredirect"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/proxy"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/rewrite"
|
||||
|
@ -50,17 +51,18 @@ type annotationExtractor struct {
|
|||
func newAnnotationExtractor(cfg extractorConfig) annotationExtractor {
|
||||
return annotationExtractor{
|
||||
map[string]parser.IngressAnnotation{
|
||||
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||
"ExternalAuth": authreq.NewParser(),
|
||||
"CertificateAuth": authtls.NewParser(cfg),
|
||||
"EnableCORS": cors.NewParser(),
|
||||
"HealthCheck": healthcheck.NewParser(cfg),
|
||||
"Whitelist": ipwhitelist.NewParser(cfg),
|
||||
"Proxy": proxy.NewParser(cfg),
|
||||
"RateLimit": ratelimit.NewParser(),
|
||||
"Redirect": rewrite.NewParser(cfg),
|
||||
"SecureUpstream": secureupstream.NewParser(),
|
||||
"SSLPassthrough": sslpassthrough.NewParser(),
|
||||
"BasicDigestAuth": auth.NewParser(auth.AuthDirectory, cfg),
|
||||
"ExternalAuth": authreq.NewParser(),
|
||||
"CertificateAuth": authtls.NewParser(cfg),
|
||||
"EnableCORS": cors.NewParser(),
|
||||
"HealthCheck": healthcheck.NewParser(cfg),
|
||||
"Whitelist": ipwhitelist.NewParser(cfg),
|
||||
"UsePortInRedirects": portinredirect.NewParser(cfg),
|
||||
"Proxy": proxy.NewParser(cfg),
|
||||
"RateLimit": ratelimit.NewParser(),
|
||||
"Redirect": rewrite.NewParser(cfg),
|
||||
"SecureUpstream": secureupstream.NewParser(),
|
||||
"SSLPassthrough": sslpassthrough.NewParser(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,13 @@ import (
|
|||
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
annotation_secureUpstream = "ingress.kubernetes.io/secure-backends"
|
||||
annotation_upsMaxFails = "ingress.kubernetes.io/upstream-max-fails"
|
||||
annotation_upsFailTimeout = "ingress.kubernetes.io/upstream-fail-timeout"
|
||||
annotation_passthrough = "ingress.kubernetes.io/ssl-passthrough"
|
||||
)
|
||||
|
||||
type mockCfg struct {
|
||||
}
|
||||
|
||||
|
@ -90,3 +97,85 @@ func buildIngress() *extensions.Ingress {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureUpstream(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
er bool
|
||||
}{
|
||||
{map[string]string{annotation_secureUpstream: "true"}, true},
|
||||
{map[string]string{annotation_secureUpstream: "false"}, false},
|
||||
{map[string]string{annotation_secureUpstream + "_no": "true"}, false},
|
||||
{map[string]string{}, false},
|
||||
{nil, false},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.SecureUpstream(ing)
|
||||
if r != foo.er {
|
||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthCheck(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
eumf int
|
||||
euft int
|
||||
}{
|
||||
{map[string]string{annotation_upsMaxFails: "3", annotation_upsFailTimeout: "10"}, 3, 10},
|
||||
{map[string]string{annotation_upsMaxFails: "3"}, 3, 0},
|
||||
{map[string]string{annotation_upsFailTimeout: "10"}, 0, 10},
|
||||
{map[string]string{}, 0, 0},
|
||||
{nil, 0, 0},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.HealthCheck(ing)
|
||||
if r == nil {
|
||||
t.Errorf("Returned nil but expected a healthcheck.Upstream")
|
||||
continue
|
||||
}
|
||||
|
||||
if r.FailTimeout != foo.euft {
|
||||
t.Errorf("Returned %d but expected %d for FailTimeout", r.FailTimeout, foo.euft)
|
||||
}
|
||||
|
||||
if r.MaxFails != foo.eumf {
|
||||
t.Errorf("Returned %d but expected %d for MaxFails", r.MaxFails, foo.eumf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSLPassthrough(t *testing.T) {
|
||||
ec := newAnnotationExtractor(mockCfg{})
|
||||
ing := buildIngress()
|
||||
|
||||
fooAnns := []struct {
|
||||
annotations map[string]string
|
||||
er bool
|
||||
}{
|
||||
{map[string]string{annotation_passthrough: "true"}, true},
|
||||
{map[string]string{annotation_passthrough: "false"}, false},
|
||||
{map[string]string{annotation_passthrough + "_no": "true"}, false},
|
||||
{map[string]string{}, false},
|
||||
{nil, false},
|
||||
}
|
||||
|
||||
for _, foo := range fooAnns {
|
||||
ing.SetAnnotations(foo.annotations)
|
||||
r := ec.SSLPassthrough(ing)
|
||||
if r != foo.er {
|
||||
t.Errorf("Returned %v but expected %v", r, foo.er)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
|
@ -49,6 +50,7 @@ import (
|
|||
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||
"k8s.io/ingress/core/pkg/ingress/status"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
ssl "k8s.io/ingress/core/pkg/net/ssl"
|
||||
local_strings "k8s.io/ingress/core/pkg/strings"
|
||||
"k8s.io/ingress/core/pkg/task"
|
||||
)
|
||||
|
@ -134,6 +136,8 @@ type Configuration struct {
|
|||
// Backend is the particular implementation to be used.
|
||||
// (for instance NGINX)
|
||||
Backend ingress.Controller
|
||||
|
||||
UpdateStatus bool
|
||||
}
|
||||
|
||||
// newIngressController creates an Ingress controller
|
||||
|
@ -182,6 +186,7 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
ic.syncQueue.Enqueue(obj)
|
||||
},
|
||||
UpdateFunc: func(old, cur interface{}) {
|
||||
oldIng := old.(*extensions.Ingress)
|
||||
curIng := cur.(*extensions.Ingress)
|
||||
if !IsValidClass(curIng, config.IngressClass) {
|
||||
return
|
||||
|
@ -190,6 +195,24 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
if !reflect.DeepEqual(old, cur) {
|
||||
upIng := cur.(*extensions.Ingress)
|
||||
ic.recorder.Eventf(upIng, api.EventTypeNormal, "UPDATE", fmt.Sprintf("Ingress %s/%s", upIng.Namespace, upIng.Name))
|
||||
// the referenced secret is different?
|
||||
if diff := pretty.Compare(curIng.Spec.TLS, oldIng.Spec.TLS); diff != "" {
|
||||
for _, secretName := range curIng.Spec.TLS {
|
||||
secKey := fmt.Sprintf("%v/%v", curIng.Namespace, secretName.SecretName)
|
||||
go func() {
|
||||
glog.Infof("TLS section in ingress %v/%v changed (secret is now %v)", upIng.Namespace, upIng.Name, secKey)
|
||||
// we need to wait until the ingress store is updated
|
||||
time.Sleep(10 * time.Second)
|
||||
key, err := ic.GetSecret(secKey)
|
||||
if err != nil {
|
||||
glog.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if key != nil {
|
||||
ic.secretQueue.Enqueue(key)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
ic.syncQueue.Enqueue(cur)
|
||||
}
|
||||
},
|
||||
|
@ -227,10 +250,22 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
}
|
||||
|
||||
mapEventHandler := cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: func(obj interface{}) {
|
||||
upCmap := obj.(*api.ConfigMap)
|
||||
mapKey := fmt.Sprintf("%s/%s", upCmap.Namespace, upCmap.Name)
|
||||
if mapKey == ic.cfg.ConfigMapName {
|
||||
glog.V(2).Infof("adding configmap %v to backend", mapKey)
|
||||
ic.cfg.Backend.SetConfig(upCmap)
|
||||
}
|
||||
},
|
||||
UpdateFunc: func(old, cur interface{}) {
|
||||
if !reflect.DeepEqual(old, cur) {
|
||||
upCmap := cur.(*api.ConfigMap)
|
||||
mapKey := fmt.Sprintf("%s/%s", upCmap.Namespace, upCmap.Name)
|
||||
if mapKey == ic.cfg.ConfigMapName {
|
||||
glog.V(2).Infof("updating configmap backend (%v)", mapKey)
|
||||
ic.cfg.Backend.SetConfig(upCmap)
|
||||
}
|
||||
// updates to configuration configmaps can trigger an update
|
||||
if mapKey == ic.cfg.ConfigMapName || mapKey == ic.cfg.TCPConfigMapName || mapKey == ic.cfg.UDPConfigMapName {
|
||||
ic.recorder.Eventf(upCmap, api.EventTypeNormal, "UPDATE", fmt.Sprintf("ConfigMap %v", mapKey))
|
||||
|
@ -270,11 +305,15 @@ func newIngressController(config *Configuration) *GenericController {
|
|||
cache.ResourceEventHandlerFuncs{},
|
||||
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
|
||||
ic.syncStatus = status.NewStatusSyncer(status.Config{
|
||||
Client: config.Client,
|
||||
PublishService: ic.cfg.PublishService,
|
||||
IngressLister: ic.ingLister,
|
||||
})
|
||||
if config.UpdateStatus {
|
||||
ic.syncStatus = status.NewStatusSyncer(status.Config{
|
||||
Client: config.Client,
|
||||
PublishService: ic.cfg.PublishService,
|
||||
IngressLister: ic.ingLister,
|
||||
})
|
||||
} else {
|
||||
glog.Warning("Update of ingress status is disabled (flag --update-status=false was specified)")
|
||||
}
|
||||
|
||||
ic.annotations = newAnnotationExtractor(ic)
|
||||
|
||||
|
@ -318,8 +357,14 @@ func (ic GenericController) GetSecret(name string) (*api.Secret, error) {
|
|||
}
|
||||
|
||||
func (ic *GenericController) getConfigMap(ns, name string) (*api.ConfigMap, error) {
|
||||
// TODO: check why ic.mapLister.Store.GetByKey(mapKey) is not stable (random content)
|
||||
return ic.cfg.Client.ConfigMaps(ns).Get(name)
|
||||
s, exists, err := ic.mapLister.Store.GetByKey(fmt.Sprintf("%v/%v", ns, name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("configmap %v was not found", name)
|
||||
}
|
||||
return s.(*api.ConfigMap), nil
|
||||
}
|
||||
|
||||
// sync collects all the pieces required to assemble the configuration file and
|
||||
|
@ -337,20 +382,6 @@ func (ic *GenericController) sync(key interface{}) error {
|
|||
return fmt.Errorf("deferring sync till endpoints controller has synced")
|
||||
}
|
||||
|
||||
// by default no custom configuration
|
||||
cfg := &api.ConfigMap{}
|
||||
|
||||
if ic.cfg.ConfigMapName != "" {
|
||||
// search for custom configmap (defined in main args)
|
||||
var err error
|
||||
ns, name, _ := k8s.ParseNameNS(ic.cfg.ConfigMapName)
|
||||
cfg, err = ic.getConfigMap(ns, name)
|
||||
if err != nil {
|
||||
// requeue
|
||||
return fmt.Errorf("unexpected error searching configmap %v: %v", ic.cfg.ConfigMapName, err)
|
||||
}
|
||||
}
|
||||
|
||||
upstreams, servers := ic.getBackendServers()
|
||||
var passUpstreams []*ingress.SSLPassthroughBackend
|
||||
for _, server := range servers {
|
||||
|
@ -370,7 +401,7 @@ func (ic *GenericController) sync(key interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
data, err := ic.cfg.Backend.OnUpdate(cfg, ingress.Configuration{
|
||||
data, err := ic.cfg.Backend.OnUpdate(ingress.Configuration{
|
||||
Backends: upstreams,
|
||||
Servers: servers,
|
||||
TCPEndpoints: ic.getTCPServices(),
|
||||
|
@ -873,6 +904,7 @@ func (ic *GenericController) createServers(data []interface{}, upstreams map[str
|
|||
|
||||
bdef := ic.GetDefaultBackend()
|
||||
ngxProxy := proxy.Configuration{
|
||||
BodySize: bdef.ProxyBodySize,
|
||||
ConnectTimeout: bdef.ProxyConnectTimeout,
|
||||
SendTimeout: bdef.ProxySendTimeout,
|
||||
ReadTimeout: bdef.ProxyReadTimeout,
|
||||
|
@ -881,9 +913,30 @@ func (ic *GenericController) createServers(data []interface{}, upstreams map[str
|
|||
|
||||
dun := ic.getDefaultUpstream().Name
|
||||
|
||||
// This adds the Default Certificate to Default Backend and also for vhosts missing the secret
|
||||
var defaultPemFileName, defaultPemSHA string
|
||||
defaultCertificate, err := ic.getPemCertificate(ic.cfg.DefaultSSLCertificate)
|
||||
// If no default Certificate was supplied, tries to generate a new dumb one
|
||||
if err != nil {
|
||||
var cert *ingress.SSLCert
|
||||
defCert, defKey := ssl.GetFakeSSLCert()
|
||||
cert, err = ssl.AddOrUpdateCertAndKey("system-snake-oil-certificate", defCert, defKey, []byte{})
|
||||
if err != nil {
|
||||
glog.Fatalf("Error generating self signed certificate: %v", err)
|
||||
} else {
|
||||
defaultPemFileName = cert.PemFileName
|
||||
defaultPemSHA = cert.PemSHA
|
||||
}
|
||||
} else {
|
||||
defaultPemFileName = defaultCertificate.PemFileName
|
||||
defaultPemSHA = defaultCertificate.PemSHA
|
||||
}
|
||||
|
||||
// default server
|
||||
servers[defServerName] = &ingress.Server{
|
||||
Hostname: defServerName,
|
||||
Hostname: defServerName,
|
||||
SSLCertificate: defaultPemFileName,
|
||||
SSLPemChecksum: defaultPemSHA,
|
||||
Locations: []*ingress.Location{
|
||||
{
|
||||
Path: rootLocation,
|
||||
|
@ -953,7 +1006,9 @@ func (ic *GenericController) createServers(data []interface{}, upstreams map[str
|
|||
servers[host].SSLPemChecksum = cert.PemSHA
|
||||
}
|
||||
} else {
|
||||
glog.Warningf("secret %v does not exists", key)
|
||||
|
||||
servers[host].SSLCertificate = defaultPemFileName
|
||||
servers[host].SSLPemChecksum = defaultPemSHA
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1054,7 +1109,9 @@ func (ic GenericController) Stop() error {
|
|||
close(ic.stopCh)
|
||||
go ic.syncQueue.Shutdown()
|
||||
go ic.secretQueue.Shutdown()
|
||||
ic.syncStatus.Shutdown()
|
||||
if ic.syncStatus != nil {
|
||||
ic.syncStatus.Shutdown()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -1075,7 +1132,9 @@ func (ic GenericController) Start() {
|
|||
go ic.secretQueue.Run(5*time.Second, ic.stopCh)
|
||||
go ic.syncQueue.Run(5*time.Second, ic.stopCh)
|
||||
|
||||
go ic.syncStatus.Run(ic.stopCh)
|
||||
if ic.syncStatus != nil {
|
||||
go ic.syncStatus.Run(ic.stopCh)
|
||||
}
|
||||
|
||||
<-ic.stopCh
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ import (
|
|||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
client "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/healthz"
|
||||
kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
|
@ -29,6 +29,12 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
var (
|
||||
flags = pflag.NewFlagSet("", pflag.ExitOnError)
|
||||
|
||||
apiserverHost = flags.String("apiserver-host", "", "The address of the Kubernetes Apiserver "+
|
||||
"to connect to in the format of protocol://address:port, e.g., "+
|
||||
"http://localhost:8080. If not specified, the assumption is that the binary runs inside a "+
|
||||
"Kubernetes cluster and local discovery is attempted.")
|
||||
kubeConfigFile = flags.String("kubeconfig", "", "Path to kubeconfig file with authorization and master location information.")
|
||||
|
||||
defaultSvc = flags.String("default-backend-service", "",
|
||||
`Service used to serve a 404 page for the default backend. Takes the form
|
||||
namespace/name. The controller uses the first node port of this Service for
|
||||
|
@ -73,11 +79,13 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
|
||||
defHealthzURL = flags.String("health-check-path", "/healthz", `Defines
|
||||
the URL to be used as health check inside in the default server in NGINX.`)
|
||||
|
||||
updateStatus = flags.Bool("update-status", true, `Indicates if the
|
||||
ingress controller should update the Ingress status IP/hostname. Default is true`)
|
||||
)
|
||||
|
||||
flags.AddGoFlagSet(flag.CommandLine)
|
||||
flags.Parse(os.Args)
|
||||
clientConfig := kubectl_util.DefaultClientConfig(flags)
|
||||
|
||||
flag.Set("logtostderr", "true")
|
||||
|
||||
|
@ -91,17 +99,9 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
glog.Fatalf("Please specify --default-backend-service")
|
||||
}
|
||||
|
||||
kubeconfig, err := restclient.InClusterConfig()
|
||||
kubeClient, err := createApiserverClient(*apiserverHost, *kubeConfigFile)
|
||||
if err != nil {
|
||||
kubeconfig, err = clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
glog.Fatalf("error configuring the client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
kubeClient, err := clientset.NewForConfig(kubeconfig)
|
||||
if err != nil {
|
||||
glog.Fatalf("failed to create client: %v", err)
|
||||
handleFatalInitError(err)
|
||||
}
|
||||
|
||||
_, err = k8s.IsValidService(kubeClient, *defaultSvc)
|
||||
|
@ -134,6 +134,7 @@ func NewIngressController(backend ingress.Controller) *GenericController {
|
|||
os.MkdirAll(ingress.DefaultSSLDirectory, 0655)
|
||||
|
||||
config := &Configuration{
|
||||
UpdateStatus: *updateStatus,
|
||||
Client: kubeClient,
|
||||
ResyncPeriod: *resyncPeriod,
|
||||
DefaultService: *defaultSvc,
|
||||
|
@ -186,3 +187,55 @@ func registerHandlers(enableProfiling bool, port int, ic *GenericController) {
|
|||
}
|
||||
glog.Fatal(server.ListenAndServe())
|
||||
}
|
||||
|
||||
const (
|
||||
// High enough QPS to fit all expected use cases. QPS=0 is not set here, because
|
||||
// client code is overriding it.
|
||||
defaultQPS = 1e6
|
||||
// High enough Burst to fit all expected use cases. Burst=0 is not set here, because
|
||||
// client code is overriding it.
|
||||
defaultBurst = 1e6
|
||||
)
|
||||
|
||||
// createApiserverClient creates new Kubernetes Apiserver client. When kubeconfig or apiserverHost param is empty
|
||||
// the function assumes that it is running inside a Kubernetes cluster and attempts to
|
||||
// discover the Apiserver. Otherwise, it connects to the Apiserver specified.
|
||||
//
|
||||
// apiserverHost param is in the format of protocol://address:port/pathPrefix, e.g.http://localhost:8001.
|
||||
// kubeConfig location of kubeconfig file
|
||||
func createApiserverClient(apiserverHost string, kubeConfig string) (*client.Clientset, error) {
|
||||
|
||||
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
|
||||
&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeConfig},
|
||||
&clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: apiserverHost}})
|
||||
|
||||
cfg, err := clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.QPS = defaultQPS
|
||||
cfg.Burst = defaultBurst
|
||||
cfg.ContentType = "application/vnd.kubernetes.protobuf"
|
||||
|
||||
glog.Infof("Creating API server client for %s", cfg.Host)
|
||||
|
||||
client, err := client.NewForConfig(cfg)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles fatal init error that prevents server from doing any work. Prints verbose error
|
||||
* message and quits the server.
|
||||
*/
|
||||
func handleFatalInitError(err error) {
|
||||
glog.Fatalf("Error while initializing connection to Kubernetes apiserver. "+
|
||||
"This most likely means that the cluster is misconfigured (e.g., it has "+
|
||||
"invalid apiserver certificates or service accounts configuration). Reason: %s\n"+
|
||||
"Refer to the troubleshooting guide for more information: "+
|
||||
"https://github.com/kubernetes/ingress/blob/master/docs/troubleshooting.md", err)
|
||||
}
|
||||
|
|
|
@ -19,10 +19,25 @@ package controller
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/auth"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/authreq"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/proxy"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/ratelimit"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/rewrite"
|
||||
"k8s.io/ingress/core/pkg/ingress/resolver"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type fakeError struct{}
|
||||
|
||||
func (fe *fakeError) Error() string {
|
||||
return "fakeError"
|
||||
}
|
||||
|
||||
func TestIsValidClass(t *testing.T) {
|
||||
ing := &extensions.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
|
@ -48,3 +63,99 @@ func TestIsValidClass(t *testing.T) {
|
|||
t.Errorf("Expected invalid class but %v returned", b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsHostValid(t *testing.T) {
|
||||
fkCert := &ingress.SSLCert{
|
||||
CAFileName: "foo",
|
||||
PemFileName: "foo.cr",
|
||||
PemSHA: "perha",
|
||||
CN: []string{
|
||||
"*.cluster.local", "default.local",
|
||||
},
|
||||
}
|
||||
|
||||
fooTests := []struct {
|
||||
cr *ingress.SSLCert
|
||||
host string
|
||||
er bool
|
||||
}{
|
||||
{nil, "foo1.cluster.local", false},
|
||||
{fkCert, "foo1.cluster.local", true},
|
||||
{fkCert, "default.local", true},
|
||||
{fkCert, "foo2.cluster.local.t", false},
|
||||
{fkCert, "", false},
|
||||
}
|
||||
|
||||
for _, foo := range fooTests {
|
||||
r := isHostValid(foo.host, foo.cr)
|
||||
if r != foo.er {
|
||||
t.Errorf("Returned %v but expected %v for foo=%v", r, foo.er, foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchHostnames(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
pattern string
|
||||
host string
|
||||
er bool
|
||||
}{
|
||||
{"*.cluster.local.", "foo1.cluster.local.", true},
|
||||
{"foo1.cluster.local.", "foo2.cluster.local.", false},
|
||||
{"cluster.local.", "foo1.cluster.local.", false},
|
||||
{".", "foo1.cluster.local.", false},
|
||||
{"cluster.local.", ".", false},
|
||||
}
|
||||
|
||||
for _, foo := range fooTests {
|
||||
r := matchHostnames(foo.pattern, foo.host)
|
||||
if r != foo.er {
|
||||
t.Errorf("Returned %v but expected %v for foo=%v", r, foo.er, foo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeLocationAnnotations(t *testing.T) {
|
||||
// initial parameters
|
||||
loc := ingress.Location{}
|
||||
annotations := map[string]interface{}{
|
||||
"Path": "/checkpath",
|
||||
"IsDefBackend": true,
|
||||
"Backend": "foo_backend",
|
||||
"BasicDigestAuth": auth.BasicDigest{},
|
||||
DeniedKeyName: &fakeError{},
|
||||
"EnableCORS": true,
|
||||
"ExternalAuth": authreq.External{},
|
||||
"RateLimit": ratelimit.RateLimit{},
|
||||
"Redirect": rewrite.Redirect{},
|
||||
"Whitelist": ipwhitelist.SourceRange{},
|
||||
"Proxy": proxy.Configuration{},
|
||||
"CertificateAuth": resolver.AuthSSLCert{},
|
||||
"UsePortInRedirects": true,
|
||||
}
|
||||
|
||||
// create test table
|
||||
type fooMergeLocationAnnotationsStruct struct {
|
||||
fName string
|
||||
er interface{}
|
||||
}
|
||||
fooTests := []fooMergeLocationAnnotationsStruct{}
|
||||
for name, value := range annotations {
|
||||
fva := fooMergeLocationAnnotationsStruct{name, value}
|
||||
fooTests = append(fooTests, fva)
|
||||
}
|
||||
|
||||
// execute test
|
||||
mergeLocationAnnotations(&loc, annotations)
|
||||
|
||||
// check result
|
||||
for _, foo := range fooTests {
|
||||
fv := reflect.ValueOf(loc).FieldByName(foo.fName).Interface()
|
||||
if !reflect.DeepEqual(fv, foo.er) {
|
||||
t.Errorf("Returned %v but expected %v for the field %s", fv, foo.er, foo.fName)
|
||||
}
|
||||
}
|
||||
if _, ok := annotations[DeniedKeyName]; ok {
|
||||
t.Errorf("%s should be removed after mergeLocationAnnotations", DeniedKeyName)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,17 @@ import "net"
|
|||
// The reason of this requirements is the annotations are generic. If some implementation do not supports
|
||||
// one or more annotations it just can provides defaults
|
||||
type Backend struct {
|
||||
|
||||
// enables which HTTP codes should be passed for processing with the error_page directive
|
||||
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_intercept_errors
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page
|
||||
// By default this is disabled
|
||||
CustomHTTPErrors []int `json:"custom-http-errors,-"`
|
||||
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size
|
||||
// Sets the maximum allowed size of the client request body
|
||||
ProxyBodySize string `json:"proxy-body-size"`
|
||||
|
||||
// Defines a timeout for establishing a connection with a proxied server.
|
||||
// It should be noted that this timeout cannot usually exceed 75 seconds.
|
||||
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_connect_timeout
|
||||
|
@ -44,6 +49,10 @@ type Backend struct {
|
|||
// Enables or disables the redirect (301) to the HTTPS port
|
||||
SSLRedirect bool `json:"ssl-redirect"`
|
||||
|
||||
// Enables or disables the specification of port in redirects
|
||||
// Default: false
|
||||
UsePortInRedirects bool `json:"use-port-in-redirects"`
|
||||
|
||||
// Number of unsuccessful attempts to communicate with the server that should happen in the
|
||||
// duration set by the fail_timeout parameter to consider the server unavailable
|
||||
// http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
|
||||
|
|
|
@ -37,9 +37,9 @@ var (
|
|||
)
|
||||
|
||||
// NewInvalidAnnotationContent returns a new InvalidContent error
|
||||
func NewInvalidAnnotationContent(name string) error {
|
||||
func NewInvalidAnnotationContent(name string, val interface{}) error {
|
||||
return InvalidContent{
|
||||
Name: fmt.Sprintf("the annotation %v does not contains a valid value", name),
|
||||
Name: fmt.Sprintf("the annotation %v does not contains a valid value (%v)", name, val),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ func TestInvalidContent(t *testing.T) {
|
|||
if IsInvalidContent(ErrMissingAnnotations) {
|
||||
t.Error("expected false")
|
||||
}
|
||||
err := NewInvalidAnnotationContent("demo")
|
||||
err := NewInvalidAnnotationContent("demo", "")
|
||||
if !IsInvalidContent(err) {
|
||||
t.Error("expected true")
|
||||
}
|
||||
|
|
379
core/pkg/ingress/sort_ingress_test.go
Normal file
379
core/pkg/ingress/sort_ingress_test.go
Normal file
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
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 ingress
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func buildBackendByNameServers() BackendByNameServers {
|
||||
return []*Backend{
|
||||
{
|
||||
Name: "foo1",
|
||||
Secure: true,
|
||||
Endpoints: []Endpoint{},
|
||||
},
|
||||
{
|
||||
Name: "foo2",
|
||||
Secure: false,
|
||||
Endpoints: []Endpoint{},
|
||||
},
|
||||
{
|
||||
Name: "foo3",
|
||||
Secure: true,
|
||||
Endpoints: []Endpoint{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendByNameServersLen(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
backends BackendByNameServers
|
||||
el int
|
||||
}{
|
||||
{[]*Backend{}, 0},
|
||||
{buildBackendByNameServers(), 3},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.backends.Len()
|
||||
if r != fooTest.el {
|
||||
t.Errorf("returned %v but expected %v for the len of BackendByNameServers: %v", r, fooTest.el, fooTest.backends)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendByNameServersSwap(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
backends BackendByNameServers
|
||||
i int
|
||||
j int
|
||||
}{
|
||||
{buildBackendByNameServers(), 0, 1},
|
||||
{buildBackendByNameServers(), 2, 1},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
fooi := fooTest.backends[fooTest.i]
|
||||
fooj := fooTest.backends[fooTest.j]
|
||||
fooTest.backends.Swap(fooTest.i, fooTest.j)
|
||||
if fooi.Name != fooTest.backends[fooTest.j].Name || fooj.Name != fooTest.backends[fooTest.i].Name {
|
||||
t.Errorf("failed to swap for ByNameServers, foo: %v", fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackendByNameServersLess(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
backends BackendByNameServers
|
||||
i int
|
||||
j int
|
||||
er bool
|
||||
}{
|
||||
// order by name
|
||||
{buildBackendByNameServers(), 0, 2, true},
|
||||
{buildBackendByNameServers(), 1, 0, false},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.backends.Less(fooTest.i, fooTest.j)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %v but expected %v for the foo: %v", r, fooTest.er, fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildEndpointByAddrPort() EndpointByAddrPort {
|
||||
return []Endpoint{
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8080",
|
||||
MaxFails: 3,
|
||||
FailTimeout: 10,
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8081",
|
||||
MaxFails: 3,
|
||||
FailTimeout: 10,
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.1",
|
||||
Port: "8082",
|
||||
MaxFails: 3,
|
||||
FailTimeout: 10,
|
||||
},
|
||||
{
|
||||
Address: "127.0.0.2",
|
||||
Port: "8082",
|
||||
MaxFails: 3,
|
||||
FailTimeout: 10,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointByAddrPortLen(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
endpoints EndpointByAddrPort
|
||||
el int
|
||||
}{
|
||||
{[]Endpoint{}, 0},
|
||||
{buildEndpointByAddrPort(), 4},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.endpoints.Len()
|
||||
if r != fooTest.el {
|
||||
t.Errorf("returned %v but expected %v for the len of EndpointByAddrPort: %v", r, fooTest.el, fooTest.endpoints)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointByAddrPortSwap(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
endpoints EndpointByAddrPort
|
||||
i int
|
||||
j int
|
||||
}{
|
||||
{buildEndpointByAddrPort(), 0, 1},
|
||||
{buildEndpointByAddrPort(), 2, 1},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
fooi := fooTest.endpoints[fooTest.i]
|
||||
fooj := fooTest.endpoints[fooTest.j]
|
||||
fooTest.endpoints.Swap(fooTest.i, fooTest.j)
|
||||
if fooi.Port != fooTest.endpoints[fooTest.j].Port ||
|
||||
fooi.Address != fooTest.endpoints[fooTest.j].Address ||
|
||||
fooj.Port != fooTest.endpoints[fooTest.i].Port ||
|
||||
fooj.Address != fooTest.endpoints[fooTest.i].Address {
|
||||
t.Errorf("failed to swap for EndpointByAddrPort, foo: %v", fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointByAddrPortLess(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
endpoints EndpointByAddrPort
|
||||
i int
|
||||
j int
|
||||
er bool
|
||||
}{
|
||||
// 1) order by name
|
||||
// 2) order by port(if the name is the same one)
|
||||
{buildEndpointByAddrPort(), 0, 1, true},
|
||||
{buildEndpointByAddrPort(), 2, 1, false},
|
||||
{buildEndpointByAddrPort(), 2, 3, true},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.endpoints.Less(fooTest.i, fooTest.j)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %v but expected %v for the foo: %v", r, fooTest.er, fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildServerByName() ServerByName {
|
||||
return []*Server{
|
||||
{
|
||||
Hostname: "foo1",
|
||||
SSLPassthrough: true,
|
||||
SSLCertificate: "foo1_cert",
|
||||
SSLPemChecksum: "foo1_pem",
|
||||
Locations: []*Location{},
|
||||
},
|
||||
{
|
||||
Hostname: "foo2",
|
||||
SSLPassthrough: true,
|
||||
SSLCertificate: "foo2_cert",
|
||||
SSLPemChecksum: "foo2_pem",
|
||||
Locations: []*Location{},
|
||||
},
|
||||
{
|
||||
Hostname: "foo3",
|
||||
SSLPassthrough: false,
|
||||
SSLCertificate: "foo3_cert",
|
||||
SSLPemChecksum: "foo3_pem",
|
||||
Locations: []*Location{},
|
||||
},
|
||||
{
|
||||
Hostname: "_",
|
||||
SSLPassthrough: false,
|
||||
SSLCertificate: "foo4_cert",
|
||||
SSLPemChecksum: "foo4_pem",
|
||||
Locations: []*Location{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerByNameLen(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
servers ServerByName
|
||||
el int
|
||||
}{
|
||||
{[]*Server{}, 0},
|
||||
{buildServerByName(), 4},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.servers.Len()
|
||||
if r != fooTest.el {
|
||||
t.Errorf("returned %v but expected %v for the len of ServerByName: %v", r, fooTest.el, fooTest.servers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerByNameSwap(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
servers ServerByName
|
||||
i int
|
||||
j int
|
||||
}{
|
||||
{buildServerByName(), 0, 1},
|
||||
{buildServerByName(), 2, 1},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
fooi := fooTest.servers[fooTest.i]
|
||||
fooj := fooTest.servers[fooTest.j]
|
||||
fooTest.servers.Swap(fooTest.i, fooTest.j)
|
||||
if fooi.Hostname != fooTest.servers[fooTest.j].Hostname ||
|
||||
fooj.Hostname != fooTest.servers[fooTest.i].Hostname {
|
||||
t.Errorf("failed to swap for ServerByName, foo: %v", fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerByNameLess(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
servers ServerByName
|
||||
i int
|
||||
j int
|
||||
er bool
|
||||
}{
|
||||
{buildServerByName(), 0, 1, true},
|
||||
{buildServerByName(), 2, 1, false},
|
||||
{buildServerByName(), 2, 3, false},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.servers.Less(fooTest.i, fooTest.j)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %v but expected %v for the foo: %v", r, fooTest.er, fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildLocationByPath() LocationByPath {
|
||||
return []*Location{
|
||||
{
|
||||
Path: "a",
|
||||
IsDefBackend: true,
|
||||
Backend: "a_back",
|
||||
},
|
||||
{
|
||||
Path: "b",
|
||||
IsDefBackend: true,
|
||||
Backend: "b_back",
|
||||
},
|
||||
{
|
||||
Path: "c",
|
||||
IsDefBackend: true,
|
||||
Backend: "c_back",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocationByPath(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
locations LocationByPath
|
||||
el int
|
||||
}{
|
||||
{[]*Location{}, 0},
|
||||
{buildLocationByPath(), 3},
|
||||
{nil, 0},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.locations.Len()
|
||||
if r != fooTest.el {
|
||||
t.Errorf("returned %v but expected %v for the len of LocationByPath: %v", r, fooTest.el, fooTest.locations)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocationByPathSwap(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
locations LocationByPath
|
||||
i int
|
||||
j int
|
||||
}{
|
||||
{buildLocationByPath(), 0, 1},
|
||||
{buildLocationByPath(), 2, 1},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
fooi := fooTest.locations[fooTest.i]
|
||||
fooj := fooTest.locations[fooTest.j]
|
||||
fooTest.locations.Swap(fooTest.i, fooTest.j)
|
||||
if fooi.Path != fooTest.locations[fooTest.j].Path ||
|
||||
fooj.Path != fooTest.locations[fooTest.i].Path {
|
||||
t.Errorf("failed to swap for LocationByPath, foo: %v", fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocationByPathLess(t *testing.T) {
|
||||
fooTests := []struct {
|
||||
locations LocationByPath
|
||||
i int
|
||||
j int
|
||||
er bool
|
||||
}{
|
||||
// sorts location by path in descending order
|
||||
{buildLocationByPath(), 0, 1, false},
|
||||
{buildLocationByPath(), 2, 1, true},
|
||||
}
|
||||
|
||||
for _, fooTest := range fooTests {
|
||||
r := fooTest.locations.Less(fooTest.i, fooTest.j)
|
||||
if r != fooTest.er {
|
||||
t.Errorf("returned %v but expected %v for the foo: %v", r, fooTest.er, fooTest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetObjectKindForSSLCert(t *testing.T) {
|
||||
fk := &SSLCert{
|
||||
ObjectMeta: api.ObjectMeta{},
|
||||
CAFileName: "ca_file",
|
||||
PemFileName: "pemfile",
|
||||
PemSHA: "pem_sha",
|
||||
CN: []string{},
|
||||
}
|
||||
|
||||
r := fk.GetObjectKind()
|
||||
if r == nil {
|
||||
t.Errorf("Returned nil but expected a valid ObjectKind")
|
||||
}
|
||||
}
|
|
@ -68,7 +68,6 @@ type Controller interface {
|
|||
// Notifications of type Add, Update and Delete:
|
||||
// https://github.com/kubernetes/kubernetes/blob/master/pkg/client/cache/controller.go#L164
|
||||
//
|
||||
// ConfigMap content of --configmap
|
||||
// Configuration returns the translation from Ingress rules containing
|
||||
// information about all the upstreams (service endpoints ) "virtual"
|
||||
// servers (FQDN) and all the locations inside each server. Each
|
||||
|
@ -79,7 +78,9 @@ type Controller interface {
|
|||
//
|
||||
// The returned configuration is then passed to test, and then to reload
|
||||
// if there is no errors.
|
||||
OnUpdate(*api.ConfigMap, Configuration) ([]byte, error)
|
||||
OnUpdate(Configuration) ([]byte, error)
|
||||
// ConfigMap content of --configmap
|
||||
SetConfig(*api.ConfigMap)
|
||||
// BackendDefaults returns the minimum settings required to configure the
|
||||
// communication to endpoints
|
||||
BackendDefaults() defaults.Backend
|
||||
|
@ -245,6 +246,9 @@ type Location struct {
|
|||
// external authentication
|
||||
// +optional
|
||||
CertificateAuth resolver.AuthSSLCert `json:"certificateAuth,omitempty"`
|
||||
// UsePortInRedirects indicates if redirects must specify the port
|
||||
// +optional
|
||||
UsePortInRedirects bool `json:"use-port-in-redirects"`
|
||||
}
|
||||
|
||||
// SSLPassthroughBackend describes a SSL upstream server configured
|
||||
|
|
|
@ -36,7 +36,7 @@ func AddOrUpdateCertAndKey(name string, cert, key, ca []byte) (*ingress.SSLCert,
|
|||
pemName := fmt.Sprintf("%v.pem", name)
|
||||
pemFileName := fmt.Sprintf("%v/%v", ingress.DefaultSSLDirectory, pemName)
|
||||
|
||||
tempPemFile, err := ioutil.TempFile("", pemName)
|
||||
tempPemFile, err := ioutil.TempFile(ingress.DefaultSSLDirectory, pemName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create temp pem file %v: %v", tempPemFile.Name(), err)
|
||||
}
|
||||
|
|
|
@ -2,4 +2,6 @@
|
|||
|
||||
This is a non-comprehensive list of existing ingress controllers.
|
||||
|
||||
* [Dummy controller backend](/examples/custom-controller)
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# Writing Ingress controllers
|
||||
|
||||
This doc outlines the basic steps needed to write an Ingress controller.
|
||||
If you want the tl;dr version, skip straight to the [example](/examples/custom-controller).
|
||||
|
|
|
@ -85,7 +85,7 @@ as well as in [this](/examples/pipeline) example.
|
|||
|
||||
First check the [catalog](#is-there-a-catalog-of-existing-ingress-controllers), to make sure you really need to write one.
|
||||
|
||||
1. Write a [generic backend](https://github.com/kubernetes/ingress/blob/master/core/pkg/ingress/doc.go)
|
||||
1. Write a [generic backend](/examples/custom-controller)
|
||||
2. Keep it in your own repo, make sure it passes the [conformance suite](https://github.com/kubernetes/kubernetes/blob/master/test/e2e/ingress_utils.go#L112)
|
||||
3. Submit an example(s) in the appropriate subdirectories [here](/examples/README.md)
|
||||
4. Add it to the catalog
|
||||
|
|
293
docs/troubleshooting.md
Normal file
293
docs/troubleshooting.md
Normal file
|
@ -0,0 +1,293 @@
|
|||
<!--
|
||||
-----------------NOTICE------------------------
|
||||
This file is referenced in code as
|
||||
https://github.com/kubernetes/ingress/blob/master/docs/troubleshooting.md
|
||||
Do not move it without providing redirects.
|
||||
-----------------------------------------------
|
||||
-->
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
|
||||
## Authentication to the Kubernetes API Server
|
||||
|
||||
|
||||
A number of components are involved in the authentication process and the first step is to narrow
|
||||
down the source of the problem, namely whether it is a problem with service authentication or with the kubeconfig file.
|
||||
Both authentications must work:
|
||||
|
||||
```
|
||||
+-------------+ service +------------+
|
||||
| | authentication | |
|
||||
+ apiserver +<-------------------+ ingress |
|
||||
| | | controller |
|
||||
+-------------+ +------------+
|
||||
|
||||
```
|
||||
|
||||
__Service authentication__
|
||||
|
||||
The Ingress controller needs information from apiserver. Therefore, authentication is required, which can be achieved in two different ways:
|
||||
|
||||
1. _Service Account:_ This is recommended, because nothing has to be configured. The Ingress controller will use information provided by the system to communicate with the API server. See 'Service Account' section for details.
|
||||
|
||||
2. _Kubeconfig file:_ In some Kubernetes environments service accounts are not available. In this case a manual configuration is required. The Ingress controller binary can be started with the `--kubeconfig` flag. The value of the flag is a path to a file specifying how to connect to the API server.
|
||||
The format of the file is identical to `~/.kube/config` which is used by kubectl to connect to the API server. See 'kubeconfig' section for details.
|
||||
|
||||
In the diagram below you can see the full authentication flow with all options, starting with the browser
|
||||
on the lower left hand side.
|
||||
```
|
||||
|
||||
Kubernetes Workstation
|
||||
+---------------------------------------------------+ +------------------+
|
||||
| | | |
|
||||
| +-----------+ apiserver +------------+ | | +------------+ |
|
||||
| | | proxy | | | | | | |
|
||||
| | apiserver | | ingress | | | | ingress | |
|
||||
| | | | controller | | | | controller | |
|
||||
| | | | | | | | | |
|
||||
| | | | | | | | | |
|
||||
| | | service account/ | | | | | | |
|
||||
| | | kubeconfig | | | | | | |
|
||||
| | +<-------------------+ | | | | | |
|
||||
| | | | | | | | | |
|
||||
| +------+----+ kubeconfig +------+-----+ | | +------+-----+ |
|
||||
| |<--------------------------------------------------------| |
|
||||
| | | |
|
||||
+---------------------------------------------------+ +------------------+
|
||||
```
|
||||
|
||||
|
||||
## Service Account
|
||||
If using a service account to connect to the API server, Dashboard expects the file
|
||||
`/var/run/secrets/kubernetes.io/serviceaccount/token` to be present. It provides a secret
|
||||
token that is required to authenticate with the API server.
|
||||
|
||||
Verify with the following commands:
|
||||
|
||||
```shell
|
||||
# start a container that contains curl
|
||||
$ kubectl run test --image=tutum/curl -- sleep 10000
|
||||
|
||||
# check that container is running
|
||||
$ kubectl get pods
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
test-701078429-s5kca 1/1 Running 0 16s
|
||||
|
||||
# check if secret exists
|
||||
$ kubectl exec test-701078429-s5kca ls /var/run/secrets/kubernetes.io/serviceaccount/
|
||||
ca.crt
|
||||
namespace
|
||||
token
|
||||
|
||||
# get service IP of master
|
||||
$ kubectl get services
|
||||
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
kubernetes 10.0.0.1 <none> 443/TCP 1d
|
||||
|
||||
# check base connectivity from cluster inside
|
||||
$ kubectl exec test-701078429-s5kca -- curl -k https://10.0.0.1
|
||||
Unauthorized
|
||||
|
||||
# connect using tokens
|
||||
$ TOKEN_VALUE=$(kubectl exec test-701078429-s5kca -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
|
||||
$ echo $TOKEN_VALUE
|
||||
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3Mi....9A
|
||||
$ kubectl exec test-701078429-s5kca -- curl --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $TOKEN_VALUE" https://10.0.0.1
|
||||
{
|
||||
"paths": [
|
||||
"/api",
|
||||
"/api/v1",
|
||||
"/apis",
|
||||
"/apis/apps",
|
||||
"/apis/apps/v1alpha1",
|
||||
"/apis/authentication.k8s.io",
|
||||
"/apis/authentication.k8s.io/v1beta1",
|
||||
"/apis/authorization.k8s.io",
|
||||
"/apis/authorization.k8s.io/v1beta1",
|
||||
"/apis/autoscaling",
|
||||
"/apis/autoscaling/v1",
|
||||
"/apis/batch",
|
||||
"/apis/batch/v1",
|
||||
"/apis/batch/v2alpha1",
|
||||
"/apis/certificates.k8s.io",
|
||||
"/apis/certificates.k8s.io/v1alpha1",
|
||||
"/apis/extensions",
|
||||
"/apis/extensions/v1beta1",
|
||||
"/apis/policy",
|
||||
"/apis/policy/v1alpha1",
|
||||
"/apis/rbac.authorization.k8s.io",
|
||||
"/apis/rbac.authorization.k8s.io/v1alpha1",
|
||||
"/apis/storage.k8s.io",
|
||||
"/apis/storage.k8s.io/v1beta1",
|
||||
"/healthz",
|
||||
"/healthz/ping",
|
||||
"/logs",
|
||||
"/metrics",
|
||||
"/swaggerapi/",
|
||||
"/ui/",
|
||||
"/version"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If it is not working, there are two possible reasons:
|
||||
|
||||
1. The contents of the tokens is invalid. Find the secret name with `kubectl get secrets | grep service-account` and
|
||||
delete it with `kubectl delete secret <name>`. It will automatically be recreated.
|
||||
|
||||
2. You have a non-standard Kubernetes installation and the file containing the token
|
||||
may not be present. The API server will mount a volume containing this file, but
|
||||
only if the API server is configured to use the ServiceAccount admission controller.
|
||||
If you experience this error, verify that your API server is using the ServiceAccount
|
||||
admission controller. If you are configuring the API server by hand, you can set
|
||||
this with the `--admission-control` parameter. Please note that you should use
|
||||
other admission controllers as well. Before configuring this option, you should
|
||||
read about admission controllers.
|
||||
|
||||
More information:
|
||||
|
||||
* [User Guide: Service Accounts](http://kubernetes.io/docs/user-guide/service-accounts/)
|
||||
* [Cluster Administrator Guide: Managing Service Accounts](http://kubernetes.io/docs/admin/service-accounts-admin/)
|
||||
|
||||
## kubeconfig
|
||||
If you want to use a kubeconfig file for authentication, create a deployment file similar to the one below:
|
||||
|
||||
*Note:* the important part is the flag `--kubeconfig=/etc/kubernetes/kubeconfig.yaml`.
|
||||
|
||||
```
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: nginx-default-backend
|
||||
labels:
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
selector:
|
||||
app: nginx-default-backend
|
||||
|
||||
---
|
||||
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: nginx-default-backend
|
||||
labels:
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
app: nginx-default-backend
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- name: default-http-backend
|
||||
image: gcr.io/google_containers/defaultbackend:1.0
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
limits:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 20Mi
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
|
||||
---
|
||||
|
||||
kind: ConfigMap
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: ingress-nginx
|
||||
labels:
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
|
||||
---
|
||||
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: ingress-nginx
|
||||
labels:
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
spec:
|
||||
type: LoadBalancer
|
||||
selector:
|
||||
app: ingress-nginx
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
targetPort: http
|
||||
- name: https
|
||||
port: 443
|
||||
targetPort: https
|
||||
|
||||
---
|
||||
|
||||
kind: Deployment
|
||||
apiVersion: extensions/v1beta1
|
||||
metadata:
|
||||
name: ingress-nginx
|
||||
labels:
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
spec:
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: ingress-nginx
|
||||
k8s-addon: ingress-nginx.addons.k8s.io
|
||||
spec:
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0
|
||||
name: ingress-nginx
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
protocol: TCP
|
||||
- name: https
|
||||
containerPort: 443
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 10254
|
||||
scheme: HTTP
|
||||
initialDelaySeconds: 30
|
||||
timeoutSeconds: 5
|
||||
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)/nginx-default-backend
|
||||
- --configmap=$(POD_NAMESPACE)/ingress-nginx
|
||||
- --kubeconfig=/etc/kubernetes/kubeconfig.yaml
|
||||
volumes:
|
||||
- name: "kubeconfig"
|
||||
hostPath:
|
||||
path: "/etc/kubernetes/"
|
||||
```
|
131
examples/PREREQUISITES.md
Normal file
131
examples/PREREQUISITES.md
Normal file
|
@ -0,0 +1,131 @@
|
|||
# Prerequisites
|
||||
|
||||
Many of the examples in this directory have common prerequisites.
|
||||
|
||||
## Deploying a controller
|
||||
|
||||
Unless you're running on a cloudprovider that supports Ingress out of the box
|
||||
(eg: GCE/GKE), you will need to deploy a controller. You can do so following
|
||||
[these instructions](/examples/deployment).
|
||||
|
||||
## Firewall rules
|
||||
|
||||
If you're using a bare-metal controller (eg the nginx ingress controller), you
|
||||
will need to create a firewall rule that targets port 80/443 on the specific VMs
|
||||
the nginx controller is running on. On cloudproviders, the respective backend
|
||||
will auto-create firewall rules for your Ingress.
|
||||
|
||||
## TLS certificates
|
||||
|
||||
Unless otherwise mentioned, the TLS secret used in examples is a 2048 bit RSA
|
||||
key/cert pair with an arbitrarily chosen hostname, created as follows
|
||||
|
||||
```console
|
||||
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc"
|
||||
Generating a 2048 bit RSA private key
|
||||
......................................................................................................................................+++
|
||||
....................................................................+++
|
||||
writing new private key to 'tls.key'
|
||||
-----
|
||||
|
||||
$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
|
||||
secret "tls-secret" created
|
||||
```
|
||||
|
||||
## Test HTTP Service
|
||||
|
||||
All examples that require a test HTTP Service use the standard echoheaders pod,
|
||||
which you can deploy as follows
|
||||
|
||||
```console
|
||||
$ kubectl create -f http-svc.yaml
|
||||
service "http-svc" created
|
||||
replicationcontroller "http-svc" created
|
||||
|
||||
$ kubectl get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
echoheaders-p1t3t 1/1 Running 0 1d
|
||||
|
||||
$ kubectl get svc
|
||||
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
echoheaders 10.0.122.116 <none> 80/TCP 1d
|
||||
```
|
||||
|
||||
You can test that the HTTP Service works by exposing it temporarily
|
||||
```console
|
||||
$ kubectl patch svc echoheaders -p '{"spec":{"type": "LoadBalancer"}}'
|
||||
"echoheaders" patched
|
||||
|
||||
$ kubectl get svc echoheaders
|
||||
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
echoheaders 10.0.122.116 <pending> 80:32100/TCP 1d
|
||||
|
||||
$ kubectl describe svc echoheaders
|
||||
Name: echoheaders
|
||||
Namespace: default
|
||||
Labels: app=echoheaders
|
||||
Selector: app=echoheaders
|
||||
Type: LoadBalancer
|
||||
IP: 10.0.122.116
|
||||
LoadBalancer Ingress: 108.59.87.136
|
||||
Port: http 80/TCP
|
||||
NodePort: http 32100/TCP
|
||||
Endpoints: 10.180.1.6:8080
|
||||
Session Affinity: None
|
||||
Events:
|
||||
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
|
||||
--------- -------- ----- ---- ------------- -------- ------ -------
|
||||
1m 1m 1 {service-controller } Normal Type ClusterIP -> LoadBalancer
|
||||
1m 1m 1 {service-controller } Normal CreatingLoadBalancer Creating load balancer
|
||||
16s 16s 1 {service-controller } Normal CreatedLoadBalancer Created load balancer
|
||||
|
||||
$ curl 108.59.87.126
|
||||
CLIENT VALUES:
|
||||
client_address=10.240.0.3
|
||||
command=GET
|
||||
real path=/
|
||||
query=nil
|
||||
request_version=1.1
|
||||
request_uri=http://108.59.87.136:8080/
|
||||
|
||||
SERVER VALUES:
|
||||
server_version=nginx: 1.9.11 - lua: 10001
|
||||
|
||||
HEADERS RECEIVED:
|
||||
accept=*/*
|
||||
host=108.59.87.136
|
||||
user-agent=curl/7.46.0
|
||||
BODY:
|
||||
-no body in request-
|
||||
|
||||
$ kubectl patch svc echoheaders -p '{"spec":{"type": "NodePort"}}'
|
||||
"echoheaders" patched
|
||||
```
|
||||
|
||||
## Ingress Class
|
||||
|
||||
If you have multiple Ingress controllers in a single cluster, you can pick one
|
||||
by specifying the `ingress.class` annotation, eg creating an Ingress with an
|
||||
annotation like
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "gce"
|
||||
```
|
||||
|
||||
will target the GCE controller, forcing the nginx controller to ignore it, while
|
||||
an annotation like
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
name: foo
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
```
|
||||
|
||||
will target the nginx controller, forcing the GCE controller to ignore it.
|
||||
|
||||
__Note__: Deploying multiple ingress controller and not specifying the
|
||||
annotation will result in both controllers fighting to satisfy the Ingress.
|
|
@ -1,6 +1,8 @@
|
|||
# Ingress examples
|
||||
|
||||
A catalog of examples on how to run, configure and scale Ingress.
|
||||
This directory contains a catalog of examples on how to run, configure and
|
||||
scale Ingress. Please review the [prerequisities](prerequisites.md) before
|
||||
trying them.
|
||||
|
||||
## Basic cross platform
|
||||
|
||||
|
@ -67,4 +69,10 @@ Websockets | websockets loadbalancing | nginx | Intermediate
|
|||
HTTP/2 | HTTP/2 loadbalancing | * | Intermediate
|
||||
Proxy protocol | leverage the proxy protocol for source IP | nginx | Advanced
|
||||
|
||||
## Custom controllers
|
||||
|
||||
Name | Description | Platform | Complexity Level
|
||||
-----| ----------- | ---------- | ----------------
|
||||
Dummy | A simple dummy controller that logs updates | * | Advanced
|
||||
|
||||
|
||||
|
|
20
examples/custom-controller/Dockerfile
Normal file
20
examples/custom-controller/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Copyright 2017[<0;55;12M The Kubernetes Authors. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# TODO(ingress#191): Change this to something more appropriate, like busybox
|
||||
From ubuntu:15.10
|
||||
MAINTAINER Prashanth B <beeps@google.com>
|
||||
RUN apt-get update && apt-get install ssl-cert -y
|
||||
COPY server /
|
||||
ENTRYPOINT ["/server"]
|
39
examples/custom-controller/Makefile
Normal file
39
examples/custom-controller/Makefile
Normal file
|
@ -0,0 +1,39 @@
|
|||
# Copyright 2017 The Kubernetes Authors All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# Build the default backend binary or image for amd64, arm, arm64 and ppc64le
|
||||
#
|
||||
# Usage:
|
||||
# [PREFIX=gcr.io/google_containers/dummy-ingress-controller] [ARCH=amd64] [TAG=1.1] make (server|container|push)
|
||||
|
||||
all: push
|
||||
|
||||
TAG=0.1
|
||||
PREFIX?=bprashanth/dummy-ingress-controller
|
||||
ARCH?=amd64
|
||||
GOLANG_VERSION=1.6
|
||||
TEMP_DIR:=$(shell mktemp -d)
|
||||
|
||||
server: server.go
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) GOARM=6 godep go build -a -installsuffix cgo -ldflags '-w' -o server ./server.go
|
||||
|
||||
container: server
|
||||
docker build --pull -t $(PREFIX)-$(ARCH):$(TAG) .
|
||||
|
||||
push: container
|
||||
gcloud docker push $(PREFIX)-$(ARCH):$(TAG)
|
||||
|
||||
clean:
|
||||
rm -f server
|
||||
|
29
examples/custom-controller/README.md
Normal file
29
examples/custom-controller/README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Dummy controller
|
||||
|
||||
This example contains the source code of a simple dummy controller. If you want
|
||||
more details on the interface, or what the generic controller is actually doing,
|
||||
please read [this doc](/docs/dev/devel.md). You can deploy the controller as
|
||||
follows:
|
||||
|
||||
```console
|
||||
$ kubectl create -f deployment.yaml
|
||||
service "default-backend" created
|
||||
deployment "dummy-ingress-controller" created
|
||||
|
||||
$ kubectl get po
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
dummy-ingress-controller-3685541482-082nl 1/1 Running 0 10m
|
||||
|
||||
$ kubectl logs dummy-ingress-controller-3685541482-082nl
|
||||
I0131 02:29:02.462123 1 launch.go:92] &{dummy 0.0.0 git-00000000 git://foo.bar.com}
|
||||
I0131 02:29:02.462513 1 launch.go:221] Creating API server client for https://10.0.0.1:443
|
||||
I0131 02:29:02.494571 1 launch.go:111] validated default/default-backend as the default backend
|
||||
I0131 02:29:02.503180 1 controller.go:1038] starting Ingress controller
|
||||
I0131 02:29:02.513528 1 leaderelection.go:247] lock is held by dummy-ingress-controller-3685541482-50jh0 and has not yet expired
|
||||
W0131 02:29:03.510699 1 queue.go:87] requeuing kube-system/kube-scheduler, err deferring sync till endpoints controller has synced
|
||||
W0131 02:29:03.514445 1 queue.go:87] requeuing kube-system/node-controller-token-826dl, err deferring sync till endpoints controller has synced
|
||||
2017/01/31 02:29:12 Received OnUpdate notification
|
||||
2017/01/31 02:29:12 upstream-default-backend: 10.180.1.20
|
||||
```
|
||||
|
||||
|
51
examples/custom-controller/deployment.yaml
Normal file
51
examples/custom-controller/deployment.yaml
Normal file
|
@ -0,0 +1,51 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: default-backend
|
||||
namespace: default
|
||||
labels:
|
||||
name: default-backend
|
||||
app: dummy-ingress-controller
|
||||
spec:
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 10254
|
||||
selector:
|
||||
# Point back the the dummy controller's
|
||||
# healthz port
|
||||
app: dummy-ingress-controller
|
||||
---
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: dummy-ingress-controller
|
||||
namespace: default
|
||||
labels:
|
||||
app: dummy-ingress-controller
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: dummy-ingress-controller
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: dummy-ingress-controller
|
||||
spec:
|
||||
containers:
|
||||
- name: server
|
||||
image: bprashanth/dummy-ingress-controller-amd64:0.1
|
||||
imagePullPolicy: Always
|
||||
ports:
|
||||
- containerPort: 10254
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
args:
|
||||
- /server
|
||||
- --default-backend-service=$(POD_NAMESPACE)/default-backend
|
82
examples/custom-controller/server.go
Normal file
82
examples/custom-controller/server.go
Normal file
|
@ -0,0 +1,82 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
nginxconfig "k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/controller"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dc := newDummyController()
|
||||
ic := controller.NewIngressController(dc)
|
||||
defer func() {
|
||||
log.Printf("Shutting down ingress controller...")
|
||||
ic.Stop()
|
||||
}()
|
||||
ic.Start()
|
||||
}
|
||||
|
||||
func newDummyController() ingress.Controller {
|
||||
return &DummyController{}
|
||||
}
|
||||
|
||||
type DummyController struct{}
|
||||
|
||||
func (dc DummyController) SetConfig(cfgMap *api.ConfigMap) {
|
||||
log.Printf("Config map %+v", cfgMap)
|
||||
}
|
||||
|
||||
func (dc DummyController) Reload(data []byte) ([]byte, bool, error) {
|
||||
out, err := exec.Command("echo", string(data)).CombinedOutput()
|
||||
if err != nil {
|
||||
log.Printf("Reloaded new config %s", out)
|
||||
} else {
|
||||
return out, false, err
|
||||
}
|
||||
return out, true, err
|
||||
}
|
||||
|
||||
func (dc DummyController) Test(file string) *exec.Cmd {
|
||||
return exec.Command("echo", file)
|
||||
}
|
||||
|
||||
func (dc DummyController) OnUpdate(updatePayload ingress.Configuration) ([]byte, error) {
|
||||
log.Printf("Received OnUpdate notification")
|
||||
for _, b := range updatePayload.Backends {
|
||||
eps := []string{}
|
||||
for _, e := range b.Endpoints {
|
||||
eps = append(eps, e.Address)
|
||||
}
|
||||
log.Printf("%v: %v", b.Name, strings.Join(eps, ", "))
|
||||
}
|
||||
return []byte(`<string containing a configuration file>`), nil
|
||||
}
|
||||
|
||||
func (dc DummyController) BackendDefaults() defaults.Backend {
|
||||
// Just adopt nginx's default backend config
|
||||
return nginxconfig.NewDefault().Backend
|
||||
}
|
||||
|
||||
func (n DummyController) Name() string {
|
||||
return "dummy Controller"
|
||||
}
|
||||
|
||||
func (n DummyController) Check(_ *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc DummyController) Info() *ingress.BackendInfo {
|
||||
return &ingress.BackendInfo{
|
||||
Name: "dummy",
|
||||
Release: "0.0.0",
|
||||
Build: "git-00000000",
|
||||
Repository: "git://foo.bar.com",
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ spec:
|
|||
hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.8.3
|
||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.1
|
||||
name: nginx-ingress-controller
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
|
@ -19,7 +19,7 @@ spec:
|
|||
# hostNetwork: true
|
||||
terminationGracePeriodSeconds: 60
|
||||
containers:
|
||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.8.3
|
||||
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.1
|
||||
name: nginx-ingress-controller
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
|
|
3
examples/health-checks/gce.md
Normal file
3
examples/health-checks/gce.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Health checks for the GCE controller
|
||||
|
||||
Placeholder
|
35
examples/http-svc.yaml
Normal file
35
examples/http-svc.yaml
Normal file
|
@ -0,0 +1,35 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: http-svc
|
||||
labels:
|
||||
app: http-svc
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
# This port needs to be available on all nodes in the cluster
|
||||
nodePort: 30301
|
||||
targetPort: 8080
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: http-svc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: http-svc
|
||||
spec:
|
||||
replicas: 2
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: http-svc
|
||||
spec:
|
||||
containers:
|
||||
- name: http-svc
|
||||
image: gcr.io/google_containers/echoserver:1.3
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
15
examples/tls-termination/gce-tls-ingress.yaml
Normal file
15
examples/tls-termination/gce-tls-ingress.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: test
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "gce"
|
||||
spec:
|
||||
tls:
|
||||
# This assumes tls-secret exists.
|
||||
- secretName: tls-secret
|
||||
backend:
|
||||
# This assumes http-svc exists and routes to healthy endpoints.
|
||||
serviceName: http-svc
|
||||
servicePort: 80
|
||||
|
78
examples/tls-termination/gce.md
Normal file
78
examples/tls-termination/gce.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# TLS termination
|
||||
|
||||
This example demonstrates how to terminate TLS through the GCE Ingress controller.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need a [TLS cert](/examples/PREREQUISITES.md#tls-certificates) and a [test HTTP service](/examples/PREREQUISITES.md#test-http-service) for this example.
|
||||
You will also need to make sure you Ingress targets exactly one Ingress
|
||||
controller by specifying the [ingress.class annotation](/examples/PREREQUISITES.md#ingress-class).
|
||||
|
||||
## Deployment
|
||||
|
||||
The following command instructs the controller to terminate traffic using
|
||||
the provided TLS cert, and forward un-encrypted HTTP traffic to the test
|
||||
HTTP service.
|
||||
|
||||
```console
|
||||
$ kubectl create -f gce-tls-ingress.yaml
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
You can confirm that the Ingress works.
|
||||
|
||||
```console
|
||||
$ kubectl describe ing gce-test
|
||||
Name: gce-test
|
||||
Namespace: default
|
||||
Address: 35.186.221.137
|
||||
Default backend: http-svc:80 (10.180.1.9:8080,10.180.3.6:8080)
|
||||
TLS:
|
||||
tls-secret terminates
|
||||
Rules:
|
||||
Host Path Backends
|
||||
---- ---- --------
|
||||
* * http-svc:80 (10.180.1.9:8080,10.180.3.6:8080)
|
||||
Annotations:
|
||||
target-proxy: k8s-tp-default-gce-test--32658fa96c080068
|
||||
url-map: k8s-um-default-gce-test--32658fa96c080068
|
||||
backends: {"k8s-be-30301--32658fa96c080068":"Unknown"}
|
||||
forwarding-rule: k8s-fw-default-gce-test--32658fa96c080068
|
||||
https-forwarding-rule: k8s-fws-default-gce-test--32658fa96c080068
|
||||
https-target-proxy: k8s-tps-default-gce-test--32658fa96c080068
|
||||
static-ip: k8s-fw-default-gce-test--32658fa96c080068
|
||||
Events:
|
||||
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
|
||||
--------- -------- ----- ---- ------------- -------- ------ -------
|
||||
2m 2m 1 {loadbalancer-controller } Normal ADD default/gce-test
|
||||
1m 1m 1 {loadbalancer-controller } Normal CREATE ip: 35.186.221.137
|
||||
1m 1m 3 {loadbalancer-controller } Normal Service default backend set to http-svc:30301
|
||||
|
||||
$ curl 35.186.221.137 -k
|
||||
curl 35.186.221.137 -L
|
||||
curl: (60) SSL certificate problem: self signed certificate
|
||||
More details here: http://curl.haxx.se/docs/sslcerts.html
|
||||
|
||||
$ curl 35.186.221.137 -kl
|
||||
CLIENT VALUES:
|
||||
client_address=10.240.0.3
|
||||
command=GET
|
||||
real path=/
|
||||
query=nil
|
||||
request_version=1.1
|
||||
request_uri=http://35.186.221.137:8080/
|
||||
|
||||
SERVER VALUES:
|
||||
server_version=nginx: 1.9.11 - lua: 10001
|
||||
|
||||
HEADERS RECEIVED:
|
||||
accept=*/*
|
||||
connection=Keep-Alive
|
||||
host=35.186.221.137
|
||||
user-agent=curl/7.46.0
|
||||
via=1.1 google
|
||||
x-cloud-trace-context=bfa123130fd623989cca0192e43d9ba4/8610689379063045825
|
||||
x-forwarded-for=104.132.0.80, 35.186.221.137
|
||||
x-forwarded-proto=https
|
||||
```
|
18
examples/tls-termination/nginx-tls-ingress.yaml
Normal file
18
examples/tls-termination/nginx-tls-ingress.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: nginx-test
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
spec:
|
||||
tls:
|
||||
# This assumes tls-secret exists.
|
||||
- secretName: tls-secret
|
||||
rules:
|
||||
- http:
|
||||
paths:
|
||||
- backend:
|
||||
# This assumes http-svc exists and routes to healthy endpoints.
|
||||
serviceName: http-svc
|
||||
servicePort: 80
|
||||
|
74
examples/tls-termination/nginx.md
Normal file
74
examples/tls-termination/nginx.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# TLS termination
|
||||
|
||||
This example demonstrates how to terminate TLS through the nginx Ingress controller.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need a [TLS cert](/examples/PREREQUISITES.md#tls-certificates) and a [test HTTP service](/examples/PREREQUISITES.md#test-http-service) for this example.
|
||||
You will also need to make sure you Ingress targets exactly one Ingress
|
||||
controller by specifying the [ingress.class annotation](/examples/PREREQUISITES.md#ingress-class).
|
||||
|
||||
## Deployment
|
||||
|
||||
The following command instructs the controller to terminate traffic using
|
||||
the provided TLS cert, and forward un-encrypted HTTP traffic to the test
|
||||
HTTP service.
|
||||
|
||||
```console
|
||||
$ kubectl create -f nginx-tls-ingress.yaml
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
You can confirm that the Ingress works.
|
||||
|
||||
```console
|
||||
$ kubectl describe ing nginx-test
|
||||
Name: nginx-test
|
||||
Namespace: default
|
||||
Address: 104.198.183.6
|
||||
Default backend: default-http-backend:80 (10.180.0.4:8080,10.240.0.2:8080)
|
||||
TLS:
|
||||
tls-secret terminates
|
||||
Rules:
|
||||
Host Path Backends
|
||||
---- ---- --------
|
||||
*
|
||||
http-svc:80 (<none>)
|
||||
Annotations:
|
||||
Events:
|
||||
FirstSeen LastSeen Count From SubObjectPath Type Reason Message
|
||||
--------- -------- ----- ---- ------------- -------- ------ -------
|
||||
7s 7s 1 {nginx-ingress-controller } Normal CREATE default/nginx-test
|
||||
7s 7s 1 {nginx-ingress-controller } Normal UPDATE default/nginx-test
|
||||
7s 7s 1 {nginx-ingress-controller } Normal CREATE ip: 104.198.183.6
|
||||
7s 7s 1 {nginx-ingress-controller } Warning MAPPING Ingress rule 'default/nginx-test' contains no path definition. Assuming /
|
||||
|
||||
$ curl 104.198.183.6 -L
|
||||
curl: (60) SSL certificate problem: self signed certificate
|
||||
More details here: http://curl.haxx.se/docs/sslcerts.html
|
||||
|
||||
$ curl 104.198.183.6 -Lk
|
||||
CLIENT VALUES:
|
||||
client_address=10.240.0.4
|
||||
command=GET
|
||||
real path=/
|
||||
query=nil
|
||||
request_version=1.1
|
||||
request_uri=http://35.186.221.137:8080/
|
||||
|
||||
SERVER VALUES:
|
||||
server_version=nginx: 1.9.11 - lua: 10001
|
||||
|
||||
HEADERS RECEIVED:
|
||||
accept=*/*
|
||||
connection=Keep-Alive
|
||||
host=35.186.221.137
|
||||
user-agent=curl/7.46.0
|
||||
via=1.1 google
|
||||
x-cloud-trace-context=f708ea7e369d4514fc90d51d7e27e91d/13322322294276298106
|
||||
x-forwarded-for=104.132.0.80, 35.186.221.137
|
||||
x-forwarded-proto=https
|
||||
BODY:
|
||||
|
||||
```
|
5
images/OWNERS
Normal file
5
images/OWNERS
Normal file
|
@ -0,0 +1,5 @@
|
|||
approvers:
|
||||
- aledbf
|
||||
reviewers:
|
||||
- bprashanth
|
||||
- aledbf
|
29
images/nginx-slim/Dockerfile
Normal file
29
images/nginx-slim/Dockerfile
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Copyright 2015 The Kubernetes Authors. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
FROM gcr.io/google_containers/ubuntu-slim:0.6
|
||||
|
||||
COPY build.sh /tmp
|
||||
|
||||
RUN /tmp/build.sh
|
||||
|
||||
# Create symlinks to redirect nginx logs to stdout and stderr docker log collector
|
||||
# This only works if nginx is started with CMD or ENTRYPOINT
|
||||
RUN ln -sf /dev/stdout /var/log/nginx/access.log
|
||||
RUN ln -sf /dev/stderr /var/log/nginx/error.log
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
14
images/nginx-slim/Makefile
Normal file
14
images/nginx-slim/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
all: push
|
||||
|
||||
# 0.0.0 shouldn't clobber any released builds
|
||||
TAG = 0.13
|
||||
PREFIX = gcr.io/google_containers/nginx-slim
|
||||
|
||||
container:
|
||||
docker build --pull -t $(PREFIX):$(TAG) .
|
||||
|
||||
push: container
|
||||
gcloud docker push $(PREFIX):$(TAG)
|
||||
|
||||
clean:
|
||||
docker rmi -f $(PREFIX):$(TAG) || true
|
24
images/nginx-slim/README.md
Normal file
24
images/nginx-slim/README.md
Normal file
|
@ -0,0 +1,24 @@
|
|||
|
||||
nginx 1.11.x base image using [ubuntu-slim](https://github.com/kubernetes/contrib/tree/master/images/ubuntu-slim)
|
||||
|
||||
nginx [engine x] is an HTTP and reverse proxy server, a mail proxy server, and a generic TCP proxy server.
|
||||
|
||||
This custom nginx image contains:
|
||||
- [lua](https://github.com/openresty/lua-nginx-module) support
|
||||
- [stream](http://nginx.org/en/docs/stream/ngx_stream_core_module.html) tcp support for upstreams
|
||||
- nginx stats [nginx-module-vts](https://github.com/vozlt/nginx-module-vts)
|
||||
- [Dynamic TLS record sizing](https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/)
|
||||
|
||||
|
||||
**How to use this image:**
|
||||
This image does provides a default configuration file with no backend servers.
|
||||
|
||||
*Using docker*
|
||||
```
|
||||
$ docker run -v /some/nginx.con:/etc/nginx/nginx.conf:ro gcr.io/google_containers/nginx-slim:0.12
|
||||
```
|
||||
|
||||
*Creating a replication controller*
|
||||
```
|
||||
$ kubectl create -f ./rc.yaml
|
||||
```
|
223
images/nginx-slim/build.sh
Executable file
223
images/nginx-slim/build.sh
Executable file
|
@ -0,0 +1,223 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright 2015 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.
|
||||
|
||||
|
||||
set -e
|
||||
|
||||
export NGINX_VERSION=1.11.9
|
||||
export NDK_VERSION=0.3.0
|
||||
export VTS_VERSION=0.1.11
|
||||
export SETMISC_VERSION=0.31
|
||||
export LUA_VERSION=0.10.7
|
||||
export STICKY_SESSIONS_VERSION=08a395c66e42
|
||||
export LUA_CJSON_VERSION=2.1.0.4
|
||||
export LUA_RESTY_HTTP_VERSION=0.07
|
||||
export LUA_UPSTREAM_VERSION=0.06
|
||||
export MORE_HEADERS_VERSION=0.32
|
||||
export NGINX_DIGEST_AUTH=7955af9c77598c697ac292811914ce1e2b3b824c
|
||||
export NGINX_SUBSTITUTIONS=bc58cb11844bc42735bbaef7085ea86ace46d05b
|
||||
|
||||
export BUILD_PATH=/tmp/build
|
||||
|
||||
get_src()
|
||||
{
|
||||
hash="$1"
|
||||
url="$2"
|
||||
f=$(basename "$url")
|
||||
|
||||
curl -sSL "$url" -o "$f"
|
||||
echo "$hash $f" | sha256sum -c - || exit 10
|
||||
tar xzf "$f"
|
||||
rm -rf "$f"
|
||||
}
|
||||
|
||||
mkdir "$BUILD_PATH"
|
||||
cd "$BUILD_PATH"
|
||||
|
||||
# install required packages to build
|
||||
apt-get update && apt-get install --no-install-recommends -y \
|
||||
bash \
|
||||
build-essential \
|
||||
curl ca-certificates \
|
||||
libgeoip1 \
|
||||
libgeoip-dev \
|
||||
patch \
|
||||
libpcre3 \
|
||||
libpcre3-dev \
|
||||
libssl-dev \
|
||||
zlib1g \
|
||||
zlib1g-dev \
|
||||
libaio1 \
|
||||
libaio-dev \
|
||||
luajit \
|
||||
openssl \
|
||||
libluajit-5.1 \
|
||||
libluajit-5.1-dev \
|
||||
linux-headers-generic || exit 1
|
||||
|
||||
# download, verify and extract the source files
|
||||
get_src dc22b71f16b551705930544dc042f1ad1af2f9715f565187ec22c7a4b2625748 \
|
||||
"http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz"
|
||||
|
||||
get_src 88e05a99a8a7419066f5ae75966fb1efc409bad4522d14986da074554ae61619 \
|
||||
"https://github.com/simpl/ngx_devel_kit/archive/v$NDK_VERSION.tar.gz"
|
||||
|
||||
get_src 97946a68937b50ab8637e1a90a13198fe376d801dc3e7447052e43c28e9ee7de \
|
||||
"https://github.com/openresty/set-misc-nginx-module/archive/v$SETMISC_VERSION.tar.gz"
|
||||
|
||||
get_src 31db853251a631a6b6a0b96b10806c9c32eda3c3d08fe46a38ff944b22dba636 \
|
||||
"https://github.com/vozlt/nginx-module-vts/archive/v$VTS_VERSION.tar.gz"
|
||||
|
||||
get_src c21c8937dcdd6fc2b6a955f929e3f4d1388610f47180e60126e6dcab06786f77 \
|
||||
"https://github.com/openresty/lua-nginx-module/archive/v$LUA_VERSION.tar.gz"
|
||||
|
||||
get_src 5417991b6db4d46383da2d18f2fd46b93fafcebfe87ba87f7cfeac4c9bcb0224 \
|
||||
"https://github.com/openresty/lua-cjson/archive/$LUA_CJSON_VERSION.tar.gz"
|
||||
|
||||
get_src 1c6aa06c9955397c94e9c3e0c0fba4e2704e85bee77b4512fb54ae7c25d58d86 \
|
||||
"https://github.com/pintsized/lua-resty-http/archive/v$LUA_RESTY_HTTP_VERSION.tar.gz"
|
||||
|
||||
get_src c6d9dab8ea1fc997031007e2e8f47cced01417e203cd88d53a9fe9f6ae138720 \
|
||||
"https://github.com/openresty/headers-more-nginx-module/archive/v$MORE_HEADERS_VERSION.tar.gz"
|
||||
|
||||
get_src 55475fe4f9e4b5220761269ccf0069ebb1ded61d7e7888f9c785c651cff3d141 \
|
||||
"https://github.com/openresty/lua-upstream-nginx-module/archive/v$LUA_UPSTREAM_VERSION.tar.gz"
|
||||
|
||||
get_src 53e440737ed1aff1f09fae150219a45f16add0c8d6e84546cb7d80f73ebffd90 \
|
||||
"https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/$STICKY_SESSIONS_VERSION.tar.gz"
|
||||
|
||||
get_src 9b1d0075df787338bb607f14925886249bda60b6b3156713923d5d59e99a708b \
|
||||
"https://github.com/atomx/nginx-http-auth-digest/archive/$NGINX_DIGEST_AUTH.tar.gz"
|
||||
|
||||
get_src 8eabbcd5950fdcc718bb0ef9165206c2ed60f67cd9da553d7bc3e6fe4e338461 \
|
||||
"https://github.com/yaoweibin/ngx_http_substitutions_filter_module/archive/$NGINX_SUBSTITUTIONS.tar.gz"
|
||||
|
||||
|
||||
#https://blog.cloudflare.com/optimizing-tls-over-tcp-to-reduce-latency/
|
||||
curl -sSL -o nginx__dynamic_tls_records.patch https://raw.githubusercontent.com/cloudflare/sslconfig/master/patches/nginx__1.11.5_dynamic_tls_records.patch
|
||||
|
||||
# build nginx
|
||||
cd "$BUILD_PATH/nginx-$NGINX_VERSION"
|
||||
|
||||
echo "Applying tls nginx patches..."
|
||||
patch -p1 < $BUILD_PATH/nginx__dynamic_tls_records.patch
|
||||
|
||||
./configure \
|
||||
--prefix=/usr/share/nginx \
|
||||
--conf-path=/etc/nginx/nginx.conf \
|
||||
--http-log-path=/var/log/nginx/access.log \
|
||||
--error-log-path=/var/log/nginx/error.log \
|
||||
--lock-path=/var/lock/nginx.lock \
|
||||
--pid-path=/run/nginx.pid \
|
||||
--http-client-body-temp-path=/var/lib/nginx/body \
|
||||
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
|
||||
--http-proxy-temp-path=/var/lib/nginx/proxy \
|
||||
--http-scgi-temp-path=/var/lib/nginx/scgi \
|
||||
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
|
||||
--with-debug \
|
||||
--with-pcre-jit \
|
||||
--with-http_ssl_module \
|
||||
--with-http_stub_status_module \
|
||||
--with-http_realip_module \
|
||||
--with-http_auth_request_module \
|
||||
--with-http_addition_module \
|
||||
--with-http_dav_module \
|
||||
--with-http_geoip_module \
|
||||
--with-http_gzip_static_module \
|
||||
--with-http_sub_module \
|
||||
--with-http_v2_module \
|
||||
--with-stream \
|
||||
--with-stream_ssl_module \
|
||||
--with-stream_ssl_preread_module \
|
||||
--with-threads \
|
||||
--with-file-aio \
|
||||
--without-mail_pop3_module \
|
||||
--without-mail_smtp_module \
|
||||
--without-mail_imap_module \
|
||||
--without-http_uwsgi_module \
|
||||
--without-http_scgi_module \
|
||||
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic' \
|
||||
--add-module="$BUILD_PATH/ngx_devel_kit-$NDK_VERSION" \
|
||||
--add-module="$BUILD_PATH/set-misc-nginx-module-$SETMISC_VERSION" \
|
||||
--add-module="$BUILD_PATH/nginx-module-vts-$VTS_VERSION" \
|
||||
--add-module="$BUILD_PATH/lua-nginx-module-$LUA_VERSION" \
|
||||
--add-module="$BUILD_PATH/headers-more-nginx-module-$MORE_HEADERS_VERSION" \
|
||||
--add-module="$BUILD_PATH/nginx-goodies-nginx-sticky-module-ng-$STICKY_SESSIONS_VERSION" \
|
||||
--add-module="$BUILD_PATH/nginx-http-auth-digest-$NGINX_DIGEST_AUTH" \
|
||||
--add-module="$BUILD_PATH/ngx_http_substitutions_filter_module-$NGINX_SUBSTITUTIONS" \
|
||||
--add-module="$BUILD_PATH/lua-upstream-nginx-module-$LUA_UPSTREAM_VERSION" || exit 1 \
|
||||
&& make || exit 1 \
|
||||
&& make install || exit 1
|
||||
|
||||
echo "Installing CJSON module"
|
||||
cd "$BUILD_PATH/lua-cjson-$LUA_CJSON_VERSION"
|
||||
make LUA_INCLUDE_DIR=/usr/include/luajit-2.0 && make install
|
||||
|
||||
echo "Installing lua-resty-http module"
|
||||
# copy lua module
|
||||
cd "$BUILD_PATH/lua-resty-http-$LUA_RESTY_HTTP_VERSION"
|
||||
sed -i 's/resty.http_headers/http_headers/' $BUILD_PATH/lua-resty-http-$LUA_RESTY_HTTP_VERSION/lib/resty/http.lua
|
||||
cp $BUILD_PATH/lua-resty-http-$LUA_RESTY_HTTP_VERSION/lib/resty/http.lua /usr/local/lib/lua/5.1
|
||||
cp $BUILD_PATH/lua-resty-http-$LUA_RESTY_HTTP_VERSION/lib/resty/http_headers.lua /usr/local/lib/lua/5.1
|
||||
|
||||
echo "Cleaning..."
|
||||
|
||||
cd /
|
||||
|
||||
apt-mark unmarkauto \
|
||||
bash \
|
||||
curl ca-certificates \
|
||||
libgeoip1 \
|
||||
libpcre3 \
|
||||
zlib1g \
|
||||
libaio1 \
|
||||
luajit \
|
||||
libluajit-5.1-2 \
|
||||
xz-utils \
|
||||
geoip-bin \
|
||||
openssl
|
||||
|
||||
apt-get remove -y --purge \
|
||||
build-essential \
|
||||
gcc-5 \
|
||||
cpp-5 \
|
||||
libgeoip-dev \
|
||||
libpcre3-dev \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libaio-dev \
|
||||
libluajit-5.1-dev \
|
||||
linux-libc-dev \
|
||||
perl-modules-5.22 \
|
||||
linux-headers-generic
|
||||
|
||||
apt-get autoremove -y
|
||||
|
||||
mkdir -p /var/lib/nginx/body /usr/share/nginx/html
|
||||
|
||||
mv /usr/share/nginx/sbin/nginx /usr/sbin
|
||||
|
||||
rm -rf "$BUILD_PATH"
|
||||
rm -Rf /usr/share/man /usr/share/doc
|
||||
rm -rf /tmp/* /var/tmp/*
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
rm -rf /var/cache/apt/archives/*
|
||||
|
||||
# Download of GeoIP databases
|
||||
curl -sSL -o /etc/nginx/GeoIP.dat.gz http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz \
|
||||
&& curl -sSL -o /etc/nginx/GeoLiteCity.dat.gz http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz \
|
||||
&& gunzip /etc/nginx/GeoIP.dat.gz \
|
||||
&& gunzip /etc/nginx/GeoLiteCity.dat.gz
|
34
images/nginx-slim/rc.yaml
Normal file
34
images/nginx-slim/rc.yaml
Normal file
|
@ -0,0 +1,34 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginxslimsvc
|
||||
labels:
|
||||
app: nginxslim
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
app: nginxslim
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: nginxslim
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
app: nginxslim
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginxslim
|
||||
name: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: nginxslim
|
||||
image: gcr.io/google_containers/nginx-slim:0.12
|
||||
ports:
|
||||
- containerPort: 80
|
5
images/ubuntu-slim/Dockerfile
Normal file
5
images/ubuntu-slim/Dockerfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
FROM scratch
|
||||
|
||||
ADD rootfs.tar /
|
||||
|
||||
CMD ["/bin/bash"]
|
45
images/ubuntu-slim/Dockerfile.build
Normal file
45
images/ubuntu-slim/Dockerfile.build
Normal file
|
@ -0,0 +1,45 @@
|
|||
FROM ubuntu:16.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
COPY excludes /etc/dpkg/dpkg.cfg.d/excludes
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get dist-upgrade -y
|
||||
|
||||
COPY runlevel /sbin/runlevel
|
||||
|
||||
# hold required packages to avoid breaking the installation of packages
|
||||
RUN apt-mark hold apt gnupg adduser passwd libsemanage1
|
||||
|
||||
# dpkg --get-selections | grep -v deinstall
|
||||
RUN echo "Yes, do as I say!" | apt-get purge \
|
||||
libcap2-bin \
|
||||
libkmod2 \
|
||||
libsmartcols1 \
|
||||
libudev1 \
|
||||
tzdata
|
||||
|
||||
# cleanup
|
||||
RUN apt-get autoremove -y && \
|
||||
apt-get clean -y && \
|
||||
tar -czf /usr/share/copyrights.tar.gz /usr/share/common-licenses /usr/share/doc/*/copyright && \
|
||||
rm -rf \
|
||||
/usr/share/doc \
|
||||
/usr/share/man \
|
||||
/usr/share/info \
|
||||
/usr/share/locale \
|
||||
/var/lib/apt/lists/* \
|
||||
/var/log/* \
|
||||
/var/cache/debconf/* \
|
||||
/usr/share/common-licenses* \
|
||||
~/.bashrc \
|
||||
/etc/systemd \
|
||||
/lib/lsb \
|
||||
/lib/udev \
|
||||
/usr/lib/x86_64-linux-gnu/gconv/IBM* \
|
||||
/usr/lib/x86_64-linux-gnu/gconv/EBC* && \
|
||||
mkdir -p /usr/share/man/man1 /usr/share/man/man2 \
|
||||
/usr/share/man/man3 /usr/share/man/man4 \
|
||||
/usr/share/man/man5 /usr/share/man/man6 \
|
||||
/usr/share/man/man7 /usr/share/man/man8
|
21
images/ubuntu-slim/Makefile
Executable file
21
images/ubuntu-slim/Makefile
Executable file
|
@ -0,0 +1,21 @@
|
|||
all: push
|
||||
|
||||
TAG ?= 0.6
|
||||
PREFIX ?= gcr.io/google_containers/ubuntu-slim
|
||||
BUILD_IMAGE ?= ubuntu-build
|
||||
TAR_FILE ?= rootfs.tar
|
||||
|
||||
container: clean
|
||||
docker build --pull -t $(BUILD_IMAGE) -f Dockerfile.build .
|
||||
docker create --name $(BUILD_IMAGE) $(BUILD_IMAGE)
|
||||
docker export $(BUILD_IMAGE) > $(TAR_FILE)
|
||||
docker build --pull -t $(PREFIX):$(TAG) .
|
||||
|
||||
push: container
|
||||
docker push $(PREFIX):$(TAG)
|
||||
|
||||
clean:
|
||||
docker rmi -f $(PREFIX):$(TAG) || true
|
||||
docker rmi -f $(BUILD_IMAGE) || true
|
||||
docker rm -f $(BUILD_IMAGE) || true
|
||||
rm -f $(TAR_FILE)
|
22
images/ubuntu-slim/README.md
Normal file
22
images/ubuntu-slim/README.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
Small Ubuntu 16.04 docker image
|
||||
|
||||
The size of this image is ~56MB (less than half than `ubuntu:16.04).
|
||||
This is possible by the removal of packages that are not required in a container:
|
||||
- dmsetup
|
||||
- e2fsprogs
|
||||
- init
|
||||
- initscripts
|
||||
- libcap2-bin
|
||||
- libcryptsetup4
|
||||
- libdevmapper1.02.1
|
||||
- libkmod2
|
||||
- libsmartcols1
|
||||
- libudev1
|
||||
- mount
|
||||
- procps
|
||||
- systemd
|
||||
- systemd-sysv
|
||||
- tzdata
|
||||
- udev
|
||||
- util-linux
|
10
images/ubuntu-slim/excludes
Normal file
10
images/ubuntu-slim/excludes
Normal file
|
@ -0,0 +1,10 @@
|
|||
path-exclude /usr/share/doc/*
|
||||
path-include /usr/share/doc/*/copyright
|
||||
path-exclude /usr/share/man/*
|
||||
path-exclude /usr/share/groff/*
|
||||
path-exclude /usr/share/info/*
|
||||
path-exclude /usr/share/locale/*
|
||||
path-include /usr/share/locale/en_US*
|
||||
path-include /usr/share/locale/locale.alias
|
||||
path-exclude /usr/share/i18n/locales/*
|
||||
path-include /usr/share/i18n/locales/en_US*
|
3
images/ubuntu-slim/runlevel
Executable file
3
images/ubuntu-slim/runlevel
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
exit 0
|
Loading…
Reference in a new issue