Create a Kubernetes NGINX controller with Dapr support
Tags: kubernetes, nginx, dapr, helm
Published: 2021-05-30 22:20:55

![NGINX dapr architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e6skw8gb5b3rmg4616p5.png "NGINX controller with Dapr support")

In this post, I will show how to create an NGINX controller that exposes the Dapr enabled applications in a Kubernetes cluster. Essentially, the NGINX controller will be configured with the same standard Dapr annotations to get injected with the daprd sidecar. By exposing this sidecar, it will allow external applications to communicate with the Dapr enabled applications in the cluster, see the [Dapr API reference](https://docs.dapr.io/reference/api/).

**Note:** There have been changes to what repo to use for the NGINX installation, the spec requirements for Kubernetes ingress resources, and the Dapr annotation names, so the information in this post is the updated version that should be followed.

## Prerequisites
* Kubernetes 1.19+ cluster with [Dapr](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-deploy/) configured.
* [Helm](https://github.com/helm/helm/releases) CLI 3x installed.
* [Kubectl](https://kubernetes.io/docs/tasks/tools/) CLI installed and configured to access the cluster.
* Optional: [OpenSSL](https://wiki.openssl.org/index.php/Binaries) for creating self-signed certificates.

## Prepare helm
Add the latest helm chart repo for the NGINX controller by running the following commands:

```bash
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
```

## Create an ingress namespace

Ensure that the current kubectl context is pointed to the correct Kubernetes cluster, and run the following command:

```bash
kubectl create namespace nginx
```

## Install NGINX controller with Dapr support

Create a file called `dapr-annotations.yaml` with the following content to set the annotations on the NGINX ingress controller pods:

```yaml
controller:
  podAnnotations:
    dapr.io/enabled: "true"
    dapr.io/app-id: "nginx-ingress"
    dapr.io/app-port: "80"
```

**Note:** The port above tells the daprd sidecar which port the NGINX controller is listening on. See [Dapr Kubernetes pod annotations spec](https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-annotations/) for a complete list of supported annotations.

Run the following command, which references the file above:

```bash
helm install nginx-ingress ingress-nginx/ingress-nginx \
    -f dapr-annotations.yaml \
    --set controller.replicaCount=2 \
    --namespace nginx
```

Add `--set controller.service.loadBalancerIP=0.0.0.0` to the above command if a static IP is needed, for example, the last IP from a peered subnet with on-premise.

## Create NGINX's daprd sidecar ingress resource

Create a file called `ingress-dapr.yaml` with the following content:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-dapr
  namespace: nginx
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
      - api.example.com
    secretName: tls-secret
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-ingress-dapr
            port:
              number: 80
```

Dapr automatically creates the `nginx-ingress-dapr` service being referenced above. Make sure to modify the hostname placeholders. 

Remove the `tls` section if TLS termination is not needed, otherwise, run the following commands to create a self-signed certificate for testing:

```bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout tls.key -out tls.crt \
    -subj "/CN=api.example.com/O=TLS Example"

kubectl create secret tls tls-secret \
    --key tls.key --cert tls.cert -n nginx
```

Optionally, inspect the created certificate with the following command:

```bash
openssl x509 -in tls.crt -text -noout
```

It is recommended to use the FREE Let's Encrypt certificates that are globally recognized by following the steps defined in the [Securing NGINX-ingress](https://cert-manager.io/next-docs/tutorials/acme/ingress/) or [Encrypting the certificate for Kubernetes (Let’s Encrypt)](https://medium.com/avmconsulting-blog/encrypting-the-certificate-for-kubernetes-lets-encrypt-805d2bf88b2a) articles.

Now, run the following command to create the ingress resource:

```bash
kubectl create -f ingress-dapr.yaml
```

**Note:** This will target the sidecar's service, not the NGINX's service for the load balancer.

## Testing externally

If a static IP was not specified earlier, then get the external IP created as part of the NGINX controller setup running the following command:

```bash
kubectl get svc nginx-ingress-ingress-nginx-controller -n nginx
```

or,

```bash
kubectl get service --selector=app=nginx-ingress,component=controller \
    -o jsonpath='{.items[*].status.loadBalancer.ingress[0].ip}' \
    -n nginx
```

With the IP, run the follow test using cURL to see if the Dapr API for the cluster is reachable from outside the cluster:

```bash
curl http://<ingress ip>/v1.0/invoke/mathService/method/add \
  -H "Content-Type: application/json"
  -d '{ "arg1": 10, "arg2": 23}'
```

The above assumes that there is a microservice running using the Dapr app-id `mathService` that has a POST endpoint called `add`, which is an example taken from the [Service invocation API reference](https://docs.dapr.io/reference/api/service_invocation_api/).

To test without building an app, install redis, and use the redis state store building block following the [How-To: Configure state store and pub/sub message broker](https://docs.dapr.io/getting-started/configure-state-pubsub/) article. After, perform the curl commands in this [example](https://docs.dapr.io/reference/api/state_api/#example) to create and retrieve the key-value data in redis.

## Delete the NGINX controller

For convenience, here is the command to delete the NGINX controller:

```bash
helm delete nginx-ingress -n nginx
```

Don't forget to delete the ingress resources and the `nginx` namespace created earlier.

## Limiting access

If additional security is required around which Dapr APIs or services should be exposed, then the scope of this can be configured globally, or at a very granular level as needed. For more information, see the following articles:
* [Security](https://docs.dapr.io/concepts/security-concept/)
* [How-To: Selectively enable Dapr APIs on the Dapr sidecar](https://docs.dapr.io/operations/configuration/api-allowlist/)
* [How-To: Apply access control list configuration for service invocation](https://docs.dapr.io/operations/configuration/invoke-allowlist/)
* [Enable API token authentication in Dapr](https://docs.dapr.io/operations/security/api-token/)