A gateway controller is the central component that implements the Gateway API, responsible for managing all Gateway resources. With the Gateway API, you can not only route traffic like you would with Ingress, but you also have better control over Gateway API resources. When you use a gateway controller and Gateway API resources, one or more IP addresses can be used to route traffic to multiple services in a Kubernetes cluster.
This article shows you how to deploy the NGINX Gateway Fabric in an Azure Kubernetes Service (AKS) cluster. Two applications will run in different namespaces within the AKS cluster, both accessible via a single IP address. Furthermore, an SSL/TLS certificate will be configured using Kubernetes Secret, cert-manager or Azure Key Vault Provider for Secrets Store CSI Driver.
To learn more about differences between the Ingress API and the Gateway API, see also: Migrating from Ingress.
However, note that some of the Gateway APIs are still not in General Availability (GA) and are subject to change. When using the managed Gateway API, consider the potential impact of version changes when upgrading your AKS cluster version.
TIP
Before using Azure managed Gateway API, you probably want to read the following sections under support policies (Archive: archive.today):
Unsupported alpha and beta Kubernetes features (Key point: AKS only supports stable and beta features within the upstream Kubernetes project)
Preview features or feature flags (Key point: features in public preview fall under best effort support)
Upstream bugs and issues
Install NGINX Gateway Fabric (gateway controller) as below:
Note that we will use NGINX Gateway Fabric as the gateway controller in this example. If you want to use a different gateway controller, check out: Gateway Controller Implementation Status
To use NodePort instead of LoadBalancer, using command below:
To see the gateway controller in action, run two demo applications in your AKS cluster. In this example, you will use kubectl apply to deploy two instances of a simple Hello world application.
Create namespace called “default-service-and-gateway” for gateway resource and default application deployment
Terminal window
1
cat<<EOF|kubectlapply-f-
2
apiVersion: v1
3
kind: Namespace
4
metadata:
5
name: default-service-and-gateway
6
labels:
7
app.kubernetes.io/part-of: default-service
8
app.kubernetes.io/component: gateway-api
9
EOF
Deploy default appliation (service-one) in “default-service-and-gateway” namespace
Both applications are now running in your Kubernetes cluster. Next, we will create a Gateway resource using the NGINX Gateway Fabric.
Set your hostname as variable
Terminal window
1
# Set your hostname
2
routeDomain=<your-own-domain>
Deploy Gateway resource
Terminal window
1
cat<<EOF|kubectlapply-f-
2
apiVersion: gateway.networking.k8s.io/v1
3
kind: Gateway
4
metadata:
5
name: the-sole-unique-gateway
6
namespace: default-service-and-gateway
7
labels:
8
app.kubernetes.io/component: gateway-api
9
spec:
10
gatewayClassName: nginx
11
listeners:
12
- name: http
13
port: 80
14
protocol: HTTP
15
# Optional: set hostname
16
hostname: ${routeDomain}
17
infrastructure:
18
labels:
19
app.kubernetes.io/part-of: gateway-api
20
EOF
NOTE
The “hostname” field is optional; however, it is still being used in this demonstration as I want to add a sense of seriousness. I hope you won’t be misled to think that “hostname must be used”.
Set up DNS record using the public IP you received
You will need to set up the DNS record on your own. To obtain the current public IP used for Gateway, use the command below:
To route traffic to each application, create a Kubernetes HTTPRoute resource. The HTTPRoute resource configures the rules that route traffic to one of the two applications.
In the following example, HTTPRoute will be created in the samespace with Gateway resource. Traffic to
${routeDomain}/default is routed to the service named service-one (which is in the same namespace with Gateway resource).
${routeDomain}/external is routed to the service named service-two (which is in a different namespace).
${routeDomain}/static is routed to the service named service-one for static assets.
Since service-two is located in a different namespace than the HTTPRoute resource, we need to use “ReferenceGrant” to enable cross namespace references within Gateway API.
Terminal window
1
cat<<EOF|kubectlapply-f-
2
apiVersion: gateway.networking.k8s.io/v1
3
kind: ReferenceGrant
4
metadata:
5
name: external-service-reference
6
namespace: external-service
7
labels:
8
app.kubernetes.io/part-of: external-service
9
app.kubernetes.io/component: gateway-api
10
spec:
11
from:
12
- group: gateway.networking.k8s.io
13
kind: HTTPRoute
14
namespace: default-service-and-gateway
15
to:
16
- group: ""
17
kind: Service
18
name: service-two
19
EOF
NOTE
There is no need to create “ReferenceGrant” resource for service-one, as its HTTPRoute and Service resources reside within the same namespace.
TIP
When the application is deployed in a different namespace than the Gateway resource, you can alternatively choose to allow the Gateway to accept HTTPRoute resources from a different namespace while placing the HTTPRoute in the same namespace as the service, depending on your management architecture.
In this scenario, the use of “ReferenceGrant” is unnecessary. This approach will be demonstrated in the section of SSL/TLS certificate setup using cert-manager.
SSL/TLS certificates facilitate SSL/TLS encryption when a client communicates with a website. You can either bring your own certificates and store them in a Kubernetes Secret, integrate them with the Secrets Store CSI driver, or choose to use cert-manager to generate a publicly-trusted certificate.
The following manifest is a simple example of referencing a certificate from the secret “www-cert,” which is located in the “certificate” namespace:
1
apiVersion:gateway.networking.k8s.io/v1
2
kind:Gateway
3
metadata:
4
name:gateway
5
namespace:gateway-example
6
spec:
7
gatewayClassName:nginx
8
listeners:
9
-name:http
10
port:80
11
protocol:HTTP
12
hostname:${routeDomain}
13
allowedRoutes:
14
namespaces:
15
from:All
16
-name:https
17
port:443
18
protocol:HTTPS
19
hostname:${routeDomain}
20
tls:
21
mode:Terminate
22
certificateRefs:
23
-kind:Secret
24
name:www-cert
25
namespace:certificate
Remember to create a “ReferenceGrant” to enable cross-namespace references when the certificate secret is not in the same namespace as Gateway resource:
1
apiVersion:gateway.networking.k8s.io/v1
2
kind:ReferenceGrant
3
metadata:
4
name:www-cert-reference
5
namespace:certificate
6
spec:
7
to:
8
-group:""
9
kind:Secret
10
name:www-cert
11
from:
12
-group:gateway.networking.k8s.io
13
kind:Gateway
14
namespace:gateway-example
Using cert-manager to generate and configure publicly-trusted certificate#
If you need an application to generate and renew certificates for you, consider using cert-manager.
In this section, we will continue from the showcase above. Note that it is assumed that you have cert-manager installed in the cluster before proceeding.
Create an namespace called “cert-issuer” for issuing certificate
For the list of well-known ACME servers, refer to: acme.sh wiki
Additionally, check out External Account Bindings when using a CA other than Let’s Encrypt.
NOTE
You do can use ClusterIssuer instead of an Issuer to make it cluster-scoped, depending on your architecture. In this case, a namespace-scoped Issuer will be used.
Create Certificate resource to define request specifications
Terminal window
1
cat<<EOF|kubectlapply-f-
2
apiVersion: cert-manager.io/v1
3
kind: Certificate
4
metadata:
5
name: domain-cert
6
namespace: cert-issuer
7
spec:
8
issuerRef:
9
name: domain-cert-issuer
10
dnsNames:
11
- ${routeDomain}
12
privateKey:
13
algorithm: ECDSA
14
encoding: PKCS1
15
size: 256
16
secretName: domain-cert-secret
17
secretTemplate:
18
labels:
19
app.kubernetes.io/component: cert-issuer
20
EOF
The certificate will be stored in the secret “domain-cert-secret” within the “cert-issuer” namespace.
NOTE
After the Gateway references the Issuer (which will be done in a later step), a HTTPRoute resource will be automatically created as shown below:
1
apiVersion:gateway.networking.k8s.io/v1
2
kind:HTTPRoute
3
metadata:
4
name:cm-acme-http-solver-abcde
5
namespace:cert-issuer
6
spec:
7
parentRefs:
8
-name:the-sole-unique-gateway
9
namespace:default-service-and-gateway
10
kind:Gateway
11
hostnames:
12
-${routeDomain}
13
rules:
14
-backendRefs:
15
-port:8089
16
name:cm-acme-http-solver-abcde
17
weight:1
18
matches:
19
-path:
20
type:Exact
21
value:/.well-known/acme-challenge/<TOKEN>
By default, Gateway only allows HTTPRoute resources from the namespace where it is located. In the next step, we will modify “allowedRoutes” to accept HTTPRoute resources from other namespaces, allowing the CA issuer to verify the request via HTTP.
Grant permission to allow the Gateway to reference certificate Secret in different namespace
Terminal window
1
cat<<EOF|kubectlapply-f-
2
apiVersion: gateway.networking.k8s.io/v1
3
kind: ReferenceGrant
4
metadata:
5
name: domain-cert-reference
6
namespace: cert-issuer
7
spec:
8
from:
9
- group: gateway.networking.k8s.io
10
kind: Gateway
11
namespace: default-service-and-gateway
12
to:
13
- group: ""
14
kind: Secret
15
name: domain-cert-secret
16
EOF
Modify Gateway to use Issuer, referencing uncreated certificate Secret and extra HTTPRoute
Terminal window
1
cat<<EOF|kubectlapply-f-
2
apiVersion: gateway.networking.k8s.io/v1
3
kind: Gateway
4
metadata:
5
name: the-sole-unique-gateway
6
namespace: default-service-and-gateway
7
labels:
8
app.kubernetes.io/component: gateway-api
9
spec:
10
gatewayClassName: nginx
11
listeners:
12
- name: http
13
port: 80
14
protocol: HTTP
15
hostname: ${routeDomain}
16
allowedRoutes:
17
namespaces:
18
from: Selector
19
selector:
20
matchExpressions:
21
- key: app.kubernetes.io/component
22
operator: In
23
values:
24
- gateway-api
25
- cert-issuer
26
- name: https
27
port: 443
28
protocol: HTTPS
29
hostname: ${routeDomain}
30
tls:
31
mode: Terminate
32
certificateRefs:
33
- name: domain-cert-secret
34
namespace: cert-issuer
35
kind: Secret
36
group: ""
37
infrastructure:
38
labels:
39
app.kubernetes.io/part-of: gateway-api
40
EOF
Now, Gateway will accept HTTPRoute resources from namespaces that have the label “app.kubernetes.io/component: gateway-api” or “app.kubernetes.io/component: cert-issuer”. This enables Gateway to route traffic to both cert-manager and applications.
TIP
You can also choose to allow HTTPRoute resources from different namespaces based on their names, as shown below:
1
allowedRoutes:
2
namespaces:
3
from:Selector
4
selector:
5
matchExpressions:
6
-key:kubernetes.io/metadata.name
7
operator:In
8
values:
9
-default-service-and-gateway
10
-cert-issuer
Edit existing HTTPRoute to allow traffic to be routed under HTTPS communication
Wait for the certificate to be generated. Use the command below to check its existence:
Terminal window
1
kubectlgetsecretdomain-cert-secret-ncert-issuer
It usually takes about 1 minute. Proceed to the next step if the certificate Secret is created.
Test if traffic works under HTTPS communication
Terminal window
1
curl-shttps://${routeDomain}/default|grep"Default Service Page</div>"
2
curl-shttps://${routeDomain}/external|grep"External Service Page</div>"
Output:
1
<div id="logo">Welcome to AKS Default Service Page</div>
2
<div id="logo">Welcome to AKS External Service Page</div>
Using Secrets Store CSI Driver with your own certificates#
If you are deeply using managed service in Azure, then using Secrets Store CSI Driver will be a better choice.
In this section, we will continue using the same showcase from the previous section. Whether you skipped to this section or are reading from the start, I will provide the steps to reset the Gateway for consistency.
Note that it is assumed that you have relative add-ons installed on the cluster before proceeding.
Use the following command to reset HTTPRoute and Gateway resources
NOTE
If you haven’t executed any commands in the section “Using cert-manager to generate and configure publicly-trusted certificate”, you can skip this step. However, even if you have executed it, there is no harm done.
A secret can only be retrieved when the SecretProviderClass is referenced in volumes. SecretProviderClass must be in the same namespace as the Pod, and Secret will also be created in the same namespace as SecretProviderClass only.
This design necessitates the creation of a dummy Pod; otherwise, you can only place certificate Secrets within the “nginx-gateway” namespace.
Attaching volumes within the Nginx Fabric Gateway controller is not recommended, but if you choose to do so, use the following command:
This is the first article after my surgery and also the last article of the year.
Before writing this one, I had already written three drafts on different topics. However, one of them is pending a version release, another is stuck due to a bug, and for the last one, I don’t have a subscription to perform the lab. I could have released a new article earlier, but it never happened for various reasons.
This article also honors the archived document: Create an unmanaged ingress controller. This article is no longer being maintained, but it is a classic. To honor this article, I have imitated most of its style while trying to improve it. Most of the basic information can be found in this article.
NOTE
There was something wrong here when this article was initially published, but after clarification by the developer, I removed the incorrect content to maintain accuracy. The discussion history can be checked here.
Let’s set this topic aside for now. As the year comes to a close, let’s take a moment to say:
Happy New Year!
Wishing everyone all the best in the year ahead.
And my cat.
Create an unmanaged gateway controller in AKS with NGINX Gateway Fabric