Introduction
Ingress is a Kubernetes API object that specifies and manages traffic routing rules for services inside a cluster. By using Ingress in a cluster, the process of traffic management goes through routing rules, and this removes the need to expose each service or create multiple load balancers.
While Ingress and load balancers are similar in functionality, they have a few distinct differences. Load balancers expose services to the internet, which is also an Ingress feature. But an Ingress Object includes a collection of rules. Ingress uses these rules and forwards all incoming traffic to a matching service which in return sends the request to a pod that can handle the request. A Kubernetes ingress object can offer multiple services such as load balancing, name-based virtual hosting, and TLS/SSL termination.
Kubernetes Ingress consists of the Ingress API object that specifies the applicable state for exposing services to inbound/incoming traffic, and the Ingress Controller. An Ingress Controller reads and processes the Ingress resource information and usually runs as a pod within the cluster.
This guide explains how you can implement Kubernetes Ingress processes to create rules that match traffic patterns within a cluster. You are to apply a Kubernetes Ingress Controller to act as a gateway to cluster services and securely handle client requests.
Prerequisites
Before you begin:
- Deploy a Rcs Kubernetes Engine (VKE) cluster
- Deploy a Ubuntu server to work as a management machine to test the Ingress operations
- Using SSH, access the server as a non-root user with sudo privileges
On the server:
- Install and configure Kubectl to access the cluster
- Install the Helm Package Manager
Kubernetes Ingress Controller Choices
To install a Kubernetes Ingress Controller on a VKE Cluster, you need to choose an implementation method based on your cluster services. Among the best choices, the Nginx Ingress Controller, Istio, Traefik, and HAProxy Ingress allow you to implement Ingress rules to control your services that route requests to the respective pods.
Nginx Ingress controller is feature-rich, highly customizable, and supports a wide range of Ingress resource annotations including SSL termination and path-based routing. The Istio Gateway Ingress controller commonly integrates with the Istio service mesh for advanced traffic management, security, and telemetry with good observability features. Likewise HAProxy Ingress is a controller that uses HAProxy as the underlying load balancer. It offers high performance and low latency features that make it suitable for demanding workloads with support for layer 7 routing using custom configurations.
In this guide, implement the Nginx Ingress Controller on your VKE cluster as it offers more flexibility and features to manage your Ingress resources.
Ingress Resources
Ingress resources on their own have no functionality outside of the Ingress controller, the controller handles the actual implementation of the rules specified by the resource. An Ingress resource is a native Kubernetes resource in which DNS routing rules map external traffic to internal Kubernetes service endpoints.
Ingress resources update the routing configurations within the Ingress controller, and below is an example of a resource file:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-demo
rules:
- host: demo.example.com
http:
paths:
- path: /demo-path
pathType: Prefix
backend:
service:
name: demo
port:
number: 8080Below is what the Ingress resource declarations represent:
The
apiVersion,kind,metadata, andspecfields are mandatory fields required by the Ingress resource.speccontains the information needed to configure a proxy server or load balancer, it contains the list of rules to match against each incoming request. The Ingress resource only supports rules for directing HTTP traffic, rule must include the following information:host: Defines the hostname to apply the set rules. When a host such as
demo.example.comis defined, the rules apply to that host. If no host is defined, the rules apply to all inbound HTTP traffic from any cluster IP address or domainpaths: Sets the incoming request endpoint associated with the backend. For example,
/demo-pathsets the URL request todemo.example.com/demo-path. The backend is defined with aservice.name,service.port.number, andservice.port.name. The host and path must match the contents of the incoming request before the Ingress controller directs traffic to the referenced servicepathType: Specify how a path should be matched and processed. It defines how the resource paths are interpreted and used when routing requests. Below are the supported types:
- Prefix: Compares the path prefix split by
/. For example, in the above resource, - the path/demo-pathmatches any derived path such as /demo-path/test1, /demo-path/test1/test2 when the pathtype is set to prefix - Exact: Matches the exact URL path. For example, the path /demo-path would only work for that path, any requests to other derived paths like /demo-path/ or /demo-path/test would not return any result
- ImplementationSpecific: Grant the Ingress Controller determination privileges on how to manage requests to the path.
- Prefix: Compares the path prefix split by
backend: Defines the backend Kubernetes Service and port name. Requests that match the host and path rules are sent to the defined backend resource for processing
Requests that do not match any of the resource rules are handled by the Ingress controller depending on the controller specifications.
Install a Kubernetes Ingress Controller to a VKE Cluster
You can install a Kubernetes Ingress controller of your choice depending on your cluster services. In this section, install the Nginx Ingress Controller to work as a gateway to your VKE cluster applications as described below.
Create a new Nginx Ingress Controller namespace
$ kubectl create namespace ingress-nginxUsing Helm, install the Nginx Ingress Controller to your cluster in a new
ingress-nginxnamespace$ helm upgrade --install ingress-nginx ingress-nginx \ --repo https://kubernetes.github.io/ingress-nginx \ --namespace ingress-nginxWait for at least 3 minutes for the Ingress controller to deploy on your cluster. Then, verify that the Nginx Ingress Controller Pod is running
$ kubectl get pods -n ingress-nginxOutput:
NAME READY STATUS RESTARTS AGE ingress-nginx-controller-5fcb5746fc-95smj 1/1 Running 0 10mView the external IP of your Ingress controller and point a domain record to the address
$ kubectl get services -n ingress-nginxOutput:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller LoadBalancer 10.109.53.1 192.0.2.100 80:32300/TCP,443:31539/TCP 132m ingress-nginx-controller-admission ClusterIP 10.105.248.187 <none> 443/TCP 132mAs displayed in the above output, point your domain record to the load balancer external IP
192.0.2.100
Deploy a Sample Application
To test your installed Kubernetes Nginx Ingress Controller, deploy a sample application to your cluster as described below.
Create a new namespace
devto separate the cluster services$ kubectl create namespace devUsing a text editor such as Nano, create a new
hello-app.yamldeployment manifest$ nano hello-app.yamlAdd the following contents to the file
apiVersion: apps/v1 kind: Deployment metadata: name: hello-app namespace: dev spec: selector: matchLabels: app: hello replicas: 3 template: metadata: labels: app: hello spec: containers: - name: hello image: "gcr.io/google-samples/hello-app:2.0"Save and close the file
Apply the new deployment to your cluster
$ kubectl create -f hello-app.yamlView the deployment status and verify that it's available and ready
$ kubectl get deployments -n devOutput:
NAME READY UP-TO-DATE AVAILABLE AGE hello-app 3/3 3 3 70sCreate a new service
hello-app-svc.yamlfile$ nano hello-app-svc.yamlAdd the following contents to the file
apiVersion: v1 kind: Service metadata: name: hello-svc namespace: dev labels: app: hello spec: type: ClusterIP selector: app: hello ports: - port: 80 targetPort: 8080 protocol: TCPSave and close the file
Create the service
$ kubectl create -f hello-app-svc.yamlVerify that the service is available in the
devnamespace$ kubectl get services -n devOutput:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-svc ClusterIP 10.100.32.138 <none> 80/TCP 66s
Define an Ingress Resource
Create a new ingress resource file
ingress-demo.yaml$ nano ingress-demo.yamlAdd the following contents to the file. Replace
example.comwith your actual domainapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-demo namespace: dev annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: example.com http: paths: - path: /hello pathType: Prefix backend: service: name: hello-svc port: number: 80Save and close the file
The above YAML file defines a new
ingress-demoIngress resource in thedevnamespace. All matching requests from theexample.com/helloURL are forwarded to thehello-svccluster resourceDeploy the Ingress resource
$ kubectl create -f ingress-demo.yamlVerify that the Ingress resource is available in your
devcluster namespace$ kubectl get ingress -n devUsing Curl, send a test request to your configured Ingress URL and verify that the linked service accepts your request
$ curl http://example.com/helloOutput:
Hello, world! Version: 2.0.0 Hostname: hello-app-5fb487d974-4rgsrAs returned by the URL request, the Ingress controller is correctly routing external traffic to the configured service resource
Connect Multiple Services to a Single Ingress Resource
Depending on your cluster resources, you can connect multiple services to a single ingress resource file. This allows you to configure a single domain with multiple path definitions. In this section, create new cluster services and define multiple paths to a single domain in an ingress resource as described in the steps below.
Create a new
foo.yamlservice definition file$ nano foo.yamlAdd the following contents to the file
kind: Pod apiVersion: v1 metadata: name: foo-app labels: app: foo spec: containers: - name: foo-app image: 'kicbase/echo-server:1.0' --- kind: Service apiVersion: v1 metadata: name: foo-service spec: selector: app: foo ports: - port: 8080Save and close the file.
The above resource definition file creates an
echo-serverservice that runs on port8080jut like to the sample application you deployed earlier.Apply the resource configuration to your cluster
$ kubectl create -f foo.yamlCreate another deployment file
bar.yaml$ nano bar.yamlAdd the following contents to the file
kind: Pod apiVersion: v1 metadata: name: bar-app labels: app: bar spec: containers: - name: bar-app image: 'kicbase/echo-server:1.0' --- kind: Service apiVersion: v1 metadata: name: bar-service spec: selector: app: bar ports: - port: 8080Save and close the file.
Apply the resource to your cluster
$ kubectl create -f bar.yamlVerify that the
foo-serviceandbar-servicecluster services are available$ kubectl get servicesOutput:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE bar-service ClusterIP 10.109.10.196 <none> 8080/TCP 10h foo-service ClusterIP 10.108.38.206 <none> 8080/TCP 10h kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 11hVerify that the respective service pods are available and running
$ kubectl get podsOutput:
NAME READY STATUS RESTARTS AGE bar-app 1/1 Running 0 10h foo-app 1/1 Running 0 10hAs displayed in the cluster resources output, you have deployed two additional pods and services to the default namespace which is different from the
devnamespace you applied earlier.To expose the Kubernetes services through a single host definition, create a new Ingress resource
foo-bar-ingress.yamlfile$ nano foo-bar-ingress.yamlAdd the following contents to the file
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-foo-bar-demo annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: example.com http: paths: - pathType: Prefix path: /foo backend: service: name: foo-service port: number: 8080 - pathType: Prefix path: /bar backend: service: name: bar-service port: number: 8080Save and close the file.
The above ingress resource defines rules that direct incoming traffic from the
example.comhost URL to thefoo-serviceandbar-servicecluster resources. The controller handles requests to each of the services based on the defined pathDeploy the Ingress resource
$ kubectl create -f foo-bar-ingress.yamlVerify that the Ingress resource is available in your cluster
$ kubectl get ingressOutput:
NAME CLASS HOSTS ADDRESS PORTS AGE ingress-foo-bar-demo nginx example.com 192.0.2.100 80 10hUsing Curl, test access to the
/foopath$ curl http://example.com/fooOutput:
Request served by foo-app HTTP/1.1 GET /foo Host: example.com Accept: */* ...As displayed in the above output, requests to the
/foopath are successfully handled by thefoo-serviceTest access to the
/barpath$ curl http://example.com/barOutput:
Request served by bar-app HTTP/1.1 GET /bar Host: example.com Accept: */* ...As displayed in the above output, requests to the
/barpath are successfully handled by thebar-app
You have implemented an Ingress resource that directs traffic from one host to multiple paths with different services. You can configure multiple resource definitions with different paths to forward external requests to cluster services.
Connect Your Services Across Different Namespaces
In production environments, it's recommended to separate cluster services in different namespaces. This isolates services and only resources within the same namespace can communicate together. When creating Ingress resources, you have to separate resources per namespace to reach the services. However, it's also possible to create a single Ingress resource file that spans across different cluster namespaces to route traffic to the respective services.
Using the Kubernetes ExternalNameService definition, you can bridge services from different namespaces within a single Ingress resource. For example, configure an Ingress resource that uses services from the dev namespace while referencing the deployed foo-service and bar-service in the default namespace as described below.
To create this bridge to the services in the default namespace from your dev namespace, \
Create a new
foo-bar-hello-bridge.yamlservice definition file$ nano foo-bar-hello-bridge.yamlAdd the following contents to the file
apiVersion: v1 kind: Service metadata: name: hello-foo-bridge namespace: dev spec: type: ExternalName externalName: foo-service.default --- apiVersion: v1 kind: Service metadata: name: hello-bar-bridge namespace: dev spec: type: ExternalName externalName: bar-service.defaultSave and close the file.
In the above configuration, the
.spec.externalNamedefinition creates a bridge to the linked service.bar-service.defaultpoints to thebar-serviceresource, andfoo-service.defaultpoints to thefoo-serviceresource from thedefaultnamespace.The
.metadata.namespacedefinition deploys thedefaultnamespace bridge service to thedevnamespaceDeploy the bridge service to your cluster
$ kubectl create -f foo-bar-hello-bridge.yamlVerify that the bridge services are available in the dev namespace
$ kubectl get services -n devOutput:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE hello-bar-bridge ExternalName <none> foo-service.default <none> 10h hello-foo-bridge ExternalName <none> bar-service.default <none> 10h hello-svc ClusterIP 10.102.14.114 <none> 80/TCP 11hAs displayed in the above output, you can use hello-foo-bridge and hello-bar-bridge as backends for Ingress resources deployed in the dev namespace. The services point to the foo-service and bar-service resources respectively in the default namespace.
To combine services in the
defaultnamespace with thedevnamespace, create a new Ingress resource filesingle-ingress.yaml$ nano single-ingress.yamlAdd the following contents to the file
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: single-ingress namespace: dev annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: example.com http: paths: - path: /hello pathType: Prefix backend: service: name: hello-svc port: number: 80 - path: /dev-foo pathType: Prefix backend: service: name: hello-foo-bridge port: number: 8080 - path: /dev-bar pathType: Prefix backend: service: name: hello-bar-bridge port: number: 8080Save and close the file.
To avoid conflicting rules on the
/hellopath, delete the previous ingress-demo resource in the dev namespace$ kubectl delete ingress ingress-demo -n devDeploy the new Ingress resource to your cluster
$ kubectl create -f single-ingress.yamlUsing Curl, test access to the
hello-svcin the dev namespace using the/hellopath$ curl http://example.com/helloOutput:
Hello, world! Version: 2.0.0 ...Test access to the
fooservice in the default namespace$ curl http://example.com/dev-fooOutput:
Request served by foo-app HTTP/1.1 GET / Host: example.com Accept: */* ...Test access to the
barservice in the default namespace$ curl http://example.com/dev-barOutput:
Request served by bar-app HTTP/1.1 GET / Host: example.com Accept: */* ...As displayed in the above outputs, the Ingress resource is correctly bridging services between the
devanddefaultnamespaces using the bridge services you created earlier.
Apply TLS/SSL Termination to Accept HTTPS Connections
TLS/SSL termination refers to the process of decrypting encrypted network traffic secured using the TLS/SSL protocols. This happens at a designated point within your network infrastructure such as a load balancer, server, or reverse proxy such as the Ingress controller. To apply TLS/SSL termination to your Ingress resources, follow the steps below to generate self-signed SSL certificates to apply to your cluster.
Using the
Opensslutility, generate a new self-signed certificate. Replaceexample.comwith your actual domain name and desired certificate details$ openssl req -x509 \ -sha256 -days 356 \ -nodes \ -newkey rsa:2048 \ -subj "/CN=example.com/C=US/L=San Fransisco" \ -keyout root.key -out root.crtThe above command generates a new certificate file
root.crt, and a corresponding private key fileroot.keyin your working directory.Create a new Kubernetes secret to upload your self-signed SSL certificate credentials to the cluster
$ kubectl create secret tls hello-app-tls \ --namespace dev \ --key root.key \ --cert root.crtCreate a new
ingress-tls.yamlIngress resource file$ nano ingress-tls.yamlAdd the following contents to the file. Verify that the
.spec.tlsfield and the.secretNamematch your Kubernetes Secret detailsapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-tls-demo namespace: dev annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx tls: - hosts: - example.com secretName: hello-app-tls rules: - host: example.com http: paths: - path: /hello-tls pathType: Prefix backend: service: name: hello-svc port: number: 80Save and close the file.
This above Ingress resource sets a new path
/hello-tlswith TLS termination that points to thehello-svcserviceApply the new Ingress resource to your cluster
$ kubectl apply -f ingress-tls.yamlSend an HTTPS request to the
/hello-tlspath on your configured host domain and verify that the cluster service responds to your request$ curl -kv https://example.com/hello-tlsOutput:
* Trying controller_ip:443... * Connected to controller_ip (controller_ip) port 443 * ALPN: curl offers h2,http/1.1 * TLSv1.3 (OUT), TLS handshake, Client hello (1): ... * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN: server accepted h2 * Server certificate: * subject: O=Acme Co; CN=Kubernetes Ingress Controller Fake Certificate * start date: Oct 4 16:25:10 2023 GMT * expire date: Oct 3 16:25:10 2024 GMT * issuer: O=Acme Co; CN=Kubernetes Ingress Controller Fake Certificate * SSL certificate verify result: self-signed certificate (18), continuing anyway. * using HTTP/2 * [HTTP/2] [1] OPENED stream for https://example.com/hello-tls * [HTTP/2] [1] [:method: GET] ... ... Hello, world! Version: 2.0.0 Hostname: hello-app-5fb487d974-4rgsr * Connection #0 to host example.com left intactFrom the output, you can see the TLS handshake process from start to finish, with the
hello-svcsuccessfully responding to the request. This shows that TLS termination is successful.
To apply trusted Let's Encrypt SSL certificates to your cluster, visit how to Set up Nginx Ingress Controller with SSL on VKE
Troubleshooting Ingress Controller Errors
When configuring your Ingress controller, you may encounter errors you need to investigate and resolve to have the controller running correctly. Below is how you can find and troubleshoot common Ingress controller errors.
View Ingress Controller Logs
Verify the Ingress controller pod status
$ kubectl get pods -n ingress-nginxOutput:
NAME READY STATUS RESTARTS AGE ingress-nginx-controller-c5c658699-65fk4 1/1 Running 0 41mView the Ingress controller pod logs to identify the potential source of an error
$ kubectl logs ingress-nginx-controller-<xxxxx>-<xxxxxx> -n ingress-nginxExamine the log entries and find the source of application errors that affect your controller performance
Verify DNS Records
When using a host such as example.com, verify that the domain resolves to your Ingress controller Load Balancer IP Address. Improper DNS resolution can lead to timeout errors when trying to access defined paths. Verify your domain address record using nslookup as described below.
$ nslookup www.example.com
Your output should look like the one below:
Server: ...
...
Non-authoritative answer:
...
Address: 192.0.2.100Error 404
View your Ingress resource
Describe the target Ingress resource and verify the configurations in the Rules: section
$ kubectl describe ingress single-ingress -n devOutput:
Name: single-ingress Labels: <none> Namespace: dev Address: 192.0.2.100 Ingress Class: nginx Default backend: <default> Rules: Host Path Backends ---- ---- -------- example.com /hello hello-svc:80 (10.244.161.195:8080,10.244.54.196:8080,10.244.91.198:8080) /dev-foo hello-foo-bridge:8080 (<error: endpoints "hello-foo-bridge" not found>) /dev-bar hello-bar-bridge:8080 (<error: endpoints "hello-bar-bridge" not found>) Annotations: nginx.ingress.kubernetes.io/rewrite-target: / Events: <none>In the above output, the Ingress resource includes the
/hello,/dev-foo, and/dev-barthat point to the backend services. To fix the 404 error, verify that your target paths point to the correct service and port. If not, edit your Ingress resource manifest to change the paths and service definitions.
Conclusion
In this guide, you implemented Kubernetes Ingress operations to correctly route and handle external requests in a VKE cluster. Depending on your cluster services, you can deploy an Ingress Controller to route incoming traffic to multiple services across all cluster namespaces. For more information about the Nginx Ingress Controller applied in this guide, visit the official documentation.
Next Steps
To implement more solutions in your Rcs Kubernetes Engine (VKE) cluster, visit the following resources: