Ingress

Make your HTTP (or HTTPS) network service available using a protocol-aware configuration mechanism, that understands web concepts like URIs, hostnames, paths, and more. The Ingress concept lets you map traffic to different backends based on rules you define via the Kubernetes API.

FEATURE STATE: Kubernetes v1.19 [stable]

An API object that manages external access to the services in a cluster, typically HTTP.

Ingress may provide load balancing, SSL termination and name-based virtual hosting.

Terminology

For clarity, this guide defines the following terms:

  • Node: A worker machine in Kubernetes, part of a cluster.
  • Cluster: A set of Nodes that run containerized applications managed by Kubernetes. For this example, and in most common Kubernetes deployments, nodes in the cluster are not part of the public internet.
  • Edge router: A router that enforces the firewall policy for your cluster. This could be a gateway managed by a cloud provider or a physical piece of hardware.
  • Cluster network: A set of links, logical or physical, that facilitate communication within a cluster according to the Kubernetes networking model.
  • Service: A Kubernetes Service that identifies a set of Pods using label selectors. Unless mentioned otherwise, Services are assumed to have virtual IPs only routable within the cluster network.

What is Ingress?

Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Traffic routing is controlled by rules defined on the Ingress resource.

Here is a simple example where an Ingress sends all its traffic to one Service:

ingress-diagram

Figure. Ingress

An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name-based virtual hosting. An Ingress controller is responsible for fulfilling the Ingress, usually with a load balancer, though it may also configure your edge router or additional frontends to help handle the traffic.

An Ingress does not expose arbitrary ports or protocols. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type Service.Type=NodePort or Service.Type=LoadBalancer.

Prerequisites

You must have an Ingress controller to satisfy an Ingress. Only creating an Ingress resource has no effect.

You may need to deploy an Ingress controller such as ingress-nginx. You can choose from a number of Ingress controllers.

Ideally, all Ingress controllers should fit the reference specification. In reality, the various Ingress controllers operate slightly differently.

Note: Make sure you review your Ingress controller’s documentation to understand the caveats of choosing it.

The Ingress resource

A minimal Ingress resource example:

service/networking/minimal-ingress.yaml Ingress - 图2

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: minimal-ingress
  5. annotations:
  6. nginx.ingress.kubernetes.io/rewrite-target: /
  7. spec:
  8. ingressClassName: nginx-example
  9. rules:
  10. - http:
  11. paths:
  12. - path: /testpath
  13. pathType: Prefix
  14. backend:
  15. service:
  16. name: test
  17. port:
  18. number: 80

An Ingress needs apiVersion, kind, metadata and spec fields. The name of an Ingress object must be a valid DNS subdomain name. For general information about working with config files, see deploying applications, configuring containers, managing resources. Ingress frequently uses annotations to configure some options depending on the Ingress controller, an example of which is the rewrite-target annotation. Different Ingress controllers support different annotations. Review the documentation for your choice of Ingress controller to learn which annotations are supported.

The Ingress spec has all the information needed to configure a load balancer or proxy server. Most importantly, it contains a list of rules matched against all incoming requests. Ingress resource only supports rules for directing HTTP(S) traffic.

If the ingressClassName is omitted, a default Ingress class should be defined.

There are some ingress controllers, that work without the definition of a default IngressClass. For example, the Ingress-NGINX controller can be configured with a flag --watch-ingress-without-class. It is recommended though, to specify the default IngressClass as shown below.

Ingress rules

Each HTTP rule contains the following information:

  • An optional host. In this example, no host is specified, so the rule applies to all inbound HTTP traffic through the IP address specified. If a host is provided (for example, foo.bar.com), the rules apply to that host.
  • A list of paths (for example, /testpath), each of which has an associated backend defined with a service.name and a service.port.name or service.port.number. Both the host and path must match the content of an incoming request before the load balancer directs traffic to the referenced Service.
  • A backend is a combination of Service and port names as described in the Service doc or a custom resource backend by way of a CRD. HTTP (and HTTPS) requests to the Ingress that match the host and path of the rule are sent to the listed backend.

A defaultBackend is often configured in an Ingress controller to service any requests that do not match a path in the spec.

DefaultBackend

An Ingress with no rules sends all traffic to a single default backend and .spec.defaultBackend is the backend that should handle requests in that case. The defaultBackend is conventionally a configuration option of the Ingress controller and is not specified in your Ingress resources. If no .spec.rules are specified, .spec.defaultBackend must be specified. If defaultBackend is not set, the handling of requests that do not match any of the rules will be up to the ingress controller (consult the documentation for your ingress controller to find out how it handles this case).

If none of the hosts or paths match the HTTP request in the Ingress objects, the traffic is routed to your default backend.

Resource backends

A Resource backend is an ObjectRef to another Kubernetes resource within the same namespace as the Ingress object. A Resource is a mutually exclusive setting with Service, and will fail validation if both are specified. A common usage for a Resource backend is to ingress data to an object storage backend with static assets.

service/networking/ingress-resource-backend.yaml Ingress - 图3

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: ingress-resource-backend
  5. spec:
  6. defaultBackend:
  7. resource:
  8. apiGroup: k8s.example.com
  9. kind: StorageBucket
  10. name: static-assets
  11. rules:
  12. - http:
  13. paths:
  14. - path: /icons
  15. pathType: ImplementationSpecific
  16. backend:
  17. resource:
  18. apiGroup: k8s.example.com
  19. kind: StorageBucket
  20. name: icon-assets

After creating the Ingress above, you can view it with the following command:

  1. kubectl describe ingress ingress-resource-backend
  1. Name: ingress-resource-backend
  2. Namespace: default
  3. Address:
  4. Default backend: APIGroup: k8s.example.com, Kind: StorageBucket, Name: static-assets
  5. Rules:
  6. Host Path Backends
  7. ---- ---- --------
  8. *
  9. /icons APIGroup: k8s.example.com, Kind: StorageBucket, Name: icon-assets
  10. Annotations: <none>
  11. Events: <none>

Path types

Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation. There are three supported path types:

  • ImplementationSpecific: With this path type, matching is up to the IngressClass. Implementations can treat this as a separate pathType or treat it identically to Prefix or Exact path types.

  • Exact: Matches the URL path exactly and with case sensitivity.

  • Prefix: Matches based on a URL path prefix split by /. Matching is case sensitive and done on a path element by element basis. A path element refers to the list of labels in the path split by the / separator. A request is a match for path p if every p is an element-wise prefix of p of the request path.

    Note: If the last element of the path is a substring of the last element in request path, it is not a match (for example: /foo/bar matches /foo/bar/baz, but does not match /foo/barbaz).

Examples

KindPath(s)Request path(s)Matches?
Prefix/(all paths)Yes
Exact/foo/fooYes
Exact/foo/barNo
Exact/foo/foo/No
Exact/foo//fooNo
Prefix/foo/foo, /foo/Yes
Prefix/foo//foo, /foo/Yes
Prefix/aaa/bb/aaa/bbbNo
Prefix/aaa/bbb/aaa/bbbYes
Prefix/aaa/bbb//aaa/bbbYes, ignores trailing slash
Prefix/aaa/bbb/aaa/bbb/Yes, matches trailing slash
Prefix/aaa/bbb/aaa/bbb/cccYes, matches subpath
Prefix/aaa/bbb/aaa/bbbxyzNo, does not match string prefix
Prefix/, /aaa/aaa/cccYes, matches /aaa prefix
Prefix/, /aaa, /aaa/bbb/aaa/bbbYes, matches /aaa/bbb prefix
Prefix/, /aaa, /aaa/bbb/cccYes, matches / prefix
Prefix/aaa/cccNo, uses default backend
Mixed/foo (Prefix), /foo (Exact)/fooYes, prefers Exact

Multiple matches

In some cases, multiple paths within an Ingress will match a request. In those cases precedence will be given first to the longest matching path. If two paths are still equally matched, precedence will be given to paths with an exact path type over prefix path type.

Hostname wildcards

Hosts can be precise matches (for example “foo.bar.com”) or a wildcard (for example “*.foo.com”). Precise matches require that the HTTP host header matches the host field. Wildcard matches require the HTTP host header is equal to the suffix of the wildcard rule.

HostHost headerMatch?
.foo.combar.foo.comMatches based on shared suffix
.foo.combaz.bar.foo.comNo match, wildcard only covers a single DNS label
*.foo.comfoo.comNo match, wildcard only covers a single DNS label

service/networking/ingress-wildcard-host.yaml Ingress - 图4

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: ingress-wildcard-host
  5. spec:
  6. rules:
  7. - host: "foo.bar.com"
  8. http:
  9. paths:
  10. - pathType: Prefix
  11. path: "/bar"
  12. backend:
  13. service:
  14. name: service1
  15. port:
  16. number: 80
  17. - host: "*.foo.com"
  18. http:
  19. paths:
  20. - pathType: Prefix
  21. path: "/foo"
  22. backend:
  23. service:
  24. name: service2
  25. port:
  26. number: 80

Ingress class

Ingresses can be implemented by different controllers, often with different configuration. Each Ingress should specify a class, a reference to an IngressClass resource that contains additional configuration including the name of the controller that should implement the class.

service/networking/external-lb.yaml Ingress - 图5

  1. apiVersion: networking.k8s.io/v1
  2. kind: IngressClass
  3. metadata:
  4. name: external-lb
  5. spec:
  6. controller: example.com/ingress-controller
  7. parameters:
  8. apiGroup: k8s.example.com
  9. kind: IngressParameters
  10. name: external-lb

The .spec.parameters field of an IngressClass lets you reference another resource that provides configuration related to that IngressClass.

The specific type of parameters to use depends on the ingress controller that you specify in the .spec.controller field of the IngressClass.

IngressClass scope

Depending on your ingress controller, you may be able to use parameters that you set cluster-wide, or just for one namespace.

The default scope for IngressClass parameters is cluster-wide.

If you set the .spec.parameters field and don’t set .spec.parameters.scope, or if you set .spec.parameters.scope to Cluster, then the IngressClass refers to a cluster-scoped resource. The kind (in combination the apiGroup) of the parameters refers to a cluster-scoped API (possibly a custom resource), and the name of the parameters identifies a specific cluster scoped resource for that API.

For example:

  1. ---
  2. apiVersion: networking.k8s.io/v1
  3. kind: IngressClass
  4. metadata:
  5. name: external-lb-1
  6. spec:
  7. controller: example.com/ingress-controller
  8. parameters:
  9. # The parameters for this IngressClass are specified in a
  10. # ClusterIngressParameter (API group k8s.example.net) named
  11. # "external-config-1". This definition tells Kubernetes to
  12. # look for a cluster-scoped parameter resource.
  13. scope: Cluster
  14. apiGroup: k8s.example.net
  15. kind: ClusterIngressParameter
  16. name: external-config-1

FEATURE STATE: Kubernetes v1.23 [stable]

If you set the .spec.parameters field and set .spec.parameters.scope to Namespace, then the IngressClass refers to a namespaced-scoped resource. You must also set the namespace field within .spec.parameters to the namespace that contains the parameters you want to use.

The kind (in combination the apiGroup) of the parameters refers to a namespaced API (for example: ConfigMap), and the name of the parameters identifies a specific resource in the namespace you specified in namespace.

Namespace-scoped parameters help the cluster operator delegate control over the configuration (for example: load balancer settings, API gateway definition) that is used for a workload. If you used a cluster-scoped parameter then either:

  • the cluster operator team needs to approve a different team’s changes every time there’s a new configuration change being applied.
  • the cluster operator must define specific access controls, such as RBAC roles and bindings, that let the application team make changes to the cluster-scoped parameters resource.

The IngressClass API itself is always cluster-scoped.

Here is an example of an IngressClass that refers to parameters that are namespaced:

  1. ---
  2. apiVersion: networking.k8s.io/v1
  3. kind: IngressClass
  4. metadata:
  5. name: external-lb-2
  6. spec:
  7. controller: example.com/ingress-controller
  8. parameters:
  9. # The parameters for this IngressClass are specified in an
  10. # IngressParameter (API group k8s.example.com) named "external-config",
  11. # that's in the "external-configuration" namespace.
  12. scope: Namespace
  13. apiGroup: k8s.example.com
  14. kind: IngressParameter
  15. namespace: external-configuration
  16. name: external-config

Deprecated annotation

Before the IngressClass resource and ingressClassName field were added in Kubernetes 1.18, Ingress classes were specified with a kubernetes.io/ingress.class annotation on the Ingress. This annotation was never formally defined, but was widely supported by Ingress controllers.

The newer ingressClassName field on Ingresses is a replacement for that annotation, but is not a direct equivalent. While the annotation was generally used to reference the name of the Ingress controller that should implement the Ingress, the field is a reference to an IngressClass resource that contains additional Ingress configuration, including the name of the Ingress controller.

Default IngressClass

You can mark a particular IngressClass as default for your cluster. Setting the ingressclass.kubernetes.io/is-default-class annotation to true on an IngressClass resource will ensure that new Ingresses without an ingressClassName field specified will be assigned this default IngressClass.

Caution: If you have more than one IngressClass marked as the default for your cluster, the admission controller prevents creating new Ingress objects that don’t have an ingressClassName specified. You can resolve this by ensuring that at most 1 IngressClass is marked as default in your cluster.

There are some ingress controllers, that work without the definition of a default IngressClass. For example, the Ingress-NGINX controller can be configured with a flag --watch-ingress-without-class. It is recommended though, to specify the default IngressClass:

service/networking/default-ingressclass.yaml Ingress - 图6

  1. apiVersion: networking.k8s.io/v1
  2. kind: IngressClass
  3. metadata:
  4. labels:
  5. app.kubernetes.io/component: controller
  6. name: nginx-example
  7. annotations:
  8. ingressclass.kubernetes.io/is-default-class: "true"
  9. spec:
  10. controller: k8s.io/ingress-nginx

Types of Ingress

Ingress backed by a single Service

There are existing Kubernetes concepts that allow you to expose a single Service (see alternatives). You can also do this with an Ingress by specifying a default backend with no rules.

service/networking/test-ingress.yaml Ingress - 图7

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: test-ingress
  5. spec:
  6. defaultBackend:
  7. service:
  8. name: test
  9. port:
  10. number: 80

If you create it using kubectl apply -f you should be able to view the state of the Ingress you added:

  1. kubectl get ingress test-ingress
  1. NAME CLASS HOSTS ADDRESS PORTS AGE
  2. test-ingress external-lb * 203.0.113.123 80 59s

Where 203.0.113.123 is the IP allocated by the Ingress controller to satisfy this Ingress.

Note: Ingress controllers and load balancers may take a minute or two to allocate an IP address. Until that time, you often see the address listed as <pending>.

Simple fanout

A fanout configuration routes traffic from a single IP address to more than one Service, based on the HTTP URI being requested. An Ingress allows you to keep the number of load balancers down to a minimum. For example, a setup like:

ingress-fanout-diagram

Figure. Ingress Fan Out

would require an Ingress such as:

service/networking/simple-fanout-example.yaml Ingress - 图9

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: simple-fanout-example
  5. spec:
  6. rules:
  7. - host: foo.bar.com
  8. http:
  9. paths:
  10. - path: /foo
  11. pathType: Prefix
  12. backend:
  13. service:
  14. name: service1
  15. port:
  16. number: 4200
  17. - path: /bar
  18. pathType: Prefix
  19. backend:
  20. service:
  21. name: service2
  22. port:
  23. number: 8080

When you create the Ingress with kubectl apply -f:

  1. kubectl describe ingress simple-fanout-example
  1. Name: simple-fanout-example
  2. Namespace: default
  3. Address: 178.91.123.132
  4. Default backend: default-http-backend:80 (10.8.2.3:8080)
  5. Rules:
  6. Host Path Backends
  7. ---- ---- --------
  8. foo.bar.com
  9. /foo service1:4200 (10.8.0.90:4200)
  10. /bar service2:8080 (10.8.0.91:8080)
  11. Events:
  12. Type Reason Age From Message
  13. ---- ------ ---- ---- -------
  14. Normal ADD 22s loadbalancer-controller default/test

The Ingress controller provisions an implementation-specific load balancer that satisfies the Ingress, as long as the Services (service1, service2) exist. When it has done so, you can see the address of the load balancer at the Address field.

Note: Depending on the Ingress controller you are using, you may need to create a default-http-backend Service.

Name based virtual hosting

Name-based virtual hosts support routing HTTP traffic to multiple host names at the same IP address.

ingress-namebase-diagram

Figure. Ingress Name Based Virtual hosting

The following Ingress tells the backing load balancer to route requests based on the Host header.

service/networking/name-virtual-host-ingress.yaml Ingress - 图11

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: name-virtual-host-ingress
  5. spec:
  6. rules:
  7. - host: foo.bar.com
  8. http:
  9. paths:
  10. - pathType: Prefix
  11. path: "/"
  12. backend:
  13. service:
  14. name: service1
  15. port:
  16. number: 80
  17. - host: bar.foo.com
  18. http:
  19. paths:
  20. - pathType: Prefix
  21. path: "/"
  22. backend:
  23. service:
  24. name: service2
  25. port:
  26. number: 80

If you create an Ingress resource without any hosts defined in the rules, then any web traffic to the IP address of your Ingress controller can be matched without a name based virtual host being required.

For example, the following Ingress routes traffic requested for first.bar.com to service1, second.bar.com to service2, and any traffic whose request host header doesn’t match first.bar.com and second.bar.com to service3.

service/networking/name-virtual-host-ingress-no-third-host.yaml Ingress - 图12

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: name-virtual-host-ingress-no-third-host
  5. spec:
  6. rules:
  7. - host: first.bar.com
  8. http:
  9. paths:
  10. - pathType: Prefix
  11. path: "/"
  12. backend:
  13. service:
  14. name: service1
  15. port:
  16. number: 80
  17. - host: second.bar.com
  18. http:
  19. paths:
  20. - pathType: Prefix
  21. path: "/"
  22. backend:
  23. service:
  24. name: service2
  25. port:
  26. number: 80
  27. - http:
  28. paths:
  29. - pathType: Prefix
  30. path: "/"
  31. backend:
  32. service:
  33. name: service3
  34. port:
  35. number: 80

TLS

You can secure an Ingress by specifying a Secret that contains a TLS private key and certificate. The Ingress resource only supports a single TLS port, 443, and assumes TLS termination at the ingress point (traffic to the Service and its Pods is in plaintext). If the TLS configuration section in an Ingress specifies different hosts, they are multiplexed on the same port according to the hostname specified through the SNI TLS extension (provided the Ingress controller supports SNI). The TLS secret must contain keys named tls.crt and tls.key that contain the certificate and private key to use for TLS. For example:

  1. apiVersion: v1
  2. kind: Secret
  3. metadata:
  4. name: testsecret-tls
  5. namespace: default
  6. data:
  7. tls.crt: base64 encoded cert
  8. tls.key: base64 encoded key
  9. type: kubernetes.io/tls

Referencing this secret in an Ingress tells the Ingress controller to secure the channel from the client to the load balancer using TLS. You need to make sure the TLS secret you created came from a certificate that contains a Common Name (CN), also known as a Fully Qualified Domain Name (FQDN) for https-example.foo.com.

Note: Keep in mind that TLS will not work on the default rule because the certificates would have to be issued for all the possible sub-domains. Therefore, hosts in the tls section need to explicitly match the host in the rules section.

service/networking/tls-example-ingress.yaml Ingress - 图13

  1. apiVersion: networking.k8s.io/v1
  2. kind: Ingress
  3. metadata:
  4. name: tls-example-ingress
  5. spec:
  6. tls:
  7. - hosts:
  8. - https-example.foo.com
  9. secretName: testsecret-tls
  10. rules:
  11. - host: https-example.foo.com
  12. http:
  13. paths:
  14. - path: /
  15. pathType: Prefix
  16. backend:
  17. service:
  18. name: service1
  19. port:
  20. number: 80

Note: There is a gap between TLS features supported by various Ingress controllers. Please refer to documentation on nginx, GCE, or any other platform specific Ingress controller to understand how TLS works in your environment.

Load balancing

An Ingress controller is bootstrapped with some load balancing policy settings that it applies to all Ingress, such as the load balancing algorithm, backend weight scheme, and others. More advanced load balancing concepts (e.g. persistent sessions, dynamic weights) are not yet exposed through the Ingress. You can instead get these features through the load balancer used for a Service.

It’s also worth noting that even though health checks are not exposed directly through the Ingress, there exist parallel concepts in Kubernetes such as readiness probes that allow you to achieve the same end result. Please review the controller specific documentation to see how they handle health checks (for example: nginx, or GCE).

Updating an Ingress

To update an existing Ingress to add a new Host, you can update it by editing the resource:

  1. kubectl describe ingress test
  1. Name: test
  2. Namespace: default
  3. Address: 178.91.123.132
  4. Default backend: default-http-backend:80 (10.8.2.3:8080)
  5. Rules:
  6. Host Path Backends
  7. ---- ---- --------
  8. foo.bar.com
  9. /foo service1:80 (10.8.0.90:80)
  10. Annotations:
  11. nginx.ingress.kubernetes.io/rewrite-target: /
  12. Events:
  13. Type Reason Age From Message
  14. ---- ------ ---- ---- -------
  15. Normal ADD 35s loadbalancer-controller default/test
  1. kubectl edit ingress test

This pops up an editor with the existing configuration in YAML format. Modify it to include the new Host:

  1. spec:
  2. rules:
  3. - host: foo.bar.com
  4. http:
  5. paths:
  6. - backend:
  7. service:
  8. name: service1
  9. port:
  10. number: 80
  11. path: /foo
  12. pathType: Prefix
  13. - host: bar.baz.com
  14. http:
  15. paths:
  16. - backend:
  17. service:
  18. name: service2
  19. port:
  20. number: 80
  21. path: /foo
  22. pathType: Prefix
  23. ..

After you save your changes, kubectl updates the resource in the API server, which tells the Ingress controller to reconfigure the load balancer.

Verify this:

  1. kubectl describe ingress test
  1. Name: test
  2. Namespace: default
  3. Address: 178.91.123.132
  4. Default backend: default-http-backend:80 (10.8.2.3:8080)
  5. Rules:
  6. Host Path Backends
  7. ---- ---- --------
  8. foo.bar.com
  9. /foo service1:80 (10.8.0.90:80)
  10. bar.baz.com
  11. /foo service2:80 (10.8.0.91:80)
  12. Annotations:
  13. nginx.ingress.kubernetes.io/rewrite-target: /
  14. Events:
  15. Type Reason Age From Message
  16. ---- ------ ---- ---- -------
  17. Normal ADD 45s loadbalancer-controller default/test

You can achieve the same outcome by invoking kubectl replace -f on a modified Ingress YAML file.

Failing across availability zones

Techniques for spreading traffic across failure domains differ between cloud providers. Please check the documentation of the relevant Ingress controller for details.

Alternatives

You can expose a Service in multiple ways that don’t directly involve the Ingress resource:

What’s next