Skip to main content

Command Palette

Search for a command to run...

AKS and Cloudflare

Published
6 min read

In this article, I will explain how we can route the incoming traffic to AKS Pods using Cloudflare. There are several options to route incoming traffic to AKS pods (nginx ingress, application gateway etc.) Cloudflare allows ingress to the pods by exposing the private resources without having to use a direct public IP.

In this setup, I have exposed the AKS pod traffic using a cloudflared connector and passing the required rules in the connector configuration.

My traffic flow is : Internet → cloudflare →AKS Cluster IP service → AKS application pods

Press enter or click to view image in full size

I have seen few articles where an ingress controller is used in this flow with cloudflare, however I don’t see a strong reason to use a nginx ingress controller in the flow because that does the same job as the cloudflare, but nginx also adds another public ip address to the flow which we want to avoid.

Prerequisites:

Cloudflare Account with DNS , Tunnel in Cloudflare, AKS Cluster.

Setting up the Cloudflare side:

My domain is hosted in godaddy, I have done a delegation to manage my domain from cloudflare.

How to delegate:

From cloudflare portal, click on Add and choose “Existing domain”

Enter the domain name and choose a plan (i.e free)

Click on “continue to activation”

In the next page get the cloudflare name servers and update this nameservers in the “NameServers” of the domain hosted in godaddy. AFter completing this, you will be able to manage domains from cloudflare.

My setup explained:

In my setup, I am using two different kubernetes deployment manifests each hosting different nginx applications. I will be using two url’s pointing to these two different application pods which can be accessed from internet. The two public url’s will be routing traffic to the applications pods using a cloudflared tunnel which is basically a cloudflared connector which is again a pod hosted in the AKS cluster.

The traffic from Internet will be sent to cloudflare hosted as AKS pod and the cloudflare connector/pod will send the traffic to the Private ClusterIPservice based on the ingress rules defined in the cloudflare pod configuration/ingress rules and the ClusterIPservice will send the traffic to the application pods as per the matchlabels.

Traffic flow : Internet → cloudflared →AKS Cluster IP service → AKS pods

AKS Cluster: I am using a basic AKS cluster with default nodepool, “bring your own vnet”, Azure cni overlay. The nodepools are assigned a different range, Pods get the IP range predefined by Azure.

AKS Deployments consists of:-

  1. Application deployment manifests

  2. ClusterIP deployment manifests

  3. Cloudflared deployment manifest

Now let us jump on to the practicals

first things first: —

on the cloudflare side, we need to create a tunnel

Press enter or click to view image in full size

→Choose cloudflared as tunnel type → give a name to the tunnel→savetunnel →Copy the token from next page (This will be used in cloudflared connector pod)

Once tunnel is created, we need to create C Names to point our application URL’s to the tunnel. For example, to host two different applications behind two url’s (app3.example.com, app4.example.com — where example.com is my primary domain) am creating two cname records pointing to the tunnel. The target for cname would be “tunnelid.cfargotunnel.com”. The tunnel id can be taken from tunnel.

Press enter or click to view image in full size

Cloudflare portion is done..

Lets jump on to the AKS side.

Am creating two different applications using two public docker images, two cluster ip services routing traffic to each application separately and another deployment for cloudflared connector

Application manifests:

one with nginx and another with nginxdemos/hello image

#app1

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginxd2s
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginxd2s
  template:
    metadata:
      labels:
        app: nginxd2s
    spec:
      containers:
      - name: ngin
        image: nginx
#app2

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginxd3s
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginxd3s
  template:
    metadata:
      labels:
        app: nginxd3s
    spec:
      containers:
      - name: ngin  
        image: nginxdemos/hello

Clusterip manifests:

1st cluster ip routing traffic to the 1st deployment using selector

2nd cluster ip routing traffic to 2nd deployment using selector

#clusterip1
apiVersion: v1
kind: Service 
metadata:
  name: nginx-lb
spec:
#   type: LoadBalancer # ClusterIp, # NodePort
  selector:
    app: nginxd2s
  ports: 
    - name: http
      port: 80 # Service Port
      targetPort: 80 # Container Port
#clusterip2
apiVersion: v1
kind: Service 
metadata:
  name: nginx-lb2
spec:
#   type: LoadBalancer # ClusterIp, # NodePort
  selector:
    app: nginxd3s
  ports: 
    - name: http
      port: 80 # Service Port
      targetPort: 80 # Container Port

When the above four manifests are deployed, we will have two different deployments (4 pods) and two clusterip services — all private.

Now comes the main pod which is the cloudflared connector pod. To create this pod, I have used the below manifest. cloudflared gives option to pass the ingress rules as part of an ingress configuration. We have used two urls to host two applications, the rules needs to be defined in the ingress of the cloudflared pod. i.e app3.example.com will route traffic to clusterip service1 (http://nginx-lb:80) and app4.example.com will route traffic to clusterip service2(http://nginx-lb2:80).

Cloudflared manifest:

option1: Passing the ingress rules inside the manifest directly

Note: the token needs to be passed correctly and routing rules needs to be defined at beginning, I faced several issues with routing rules and recreating tunnel fixes the issue when we want to add new rule. Also the service: http_status:404 is required, otherwise the connector fails.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cfd20
spec:
  replicas: 1 
  selector:
    matchLabels:
      app: cfd20
  template:
    metadata:
      labels:
        app: cfd20
    spec:
      containers:
      - name: cloudflared
        image: cloudflare/cloudflared:latest
        args:
        - tunnel
        - --config
        - /etc/cloudflared/config/config.yaml
        - run
        env:
        - name: TUNNEL_TOKEN
          value: "tobepassed as secret"
        volumeMounts:
        - name: config
          mountPath: /etc/cloudflared/config
          readOnly: true
      volumes:
      - name: config
        configMap:
          name: cloudflared
          items:
          - key: config.yaml
            path: config.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: cloudflared
data:
  config.yaml: |
    tunnel: version2
    credentials-file: /etc/cloudflared/creds/credentials.json
    metrics: 0.0.0.0:2000
    no-autoupdate: true
    ingress: 
      - hostname: app3.alwayslearn.in
        service: http://nginx-lb2:80 
      - hostname: app4.alwayslearn.in
        service: http://nginx-lb:80 
      - service: http_status:404

Once the above pod is deployed, the tunnel status on the cloudflared side becomes healthy.

Press enter or click to view image in full size

The Application works as expected.

When app3.alwayslearn.in is accessed, this is what happens:-

As per the cname record in DNS , the traffic is routed to cloudflared connector/tunnel and the ingress rules in the connector sends the traffic to the clusterip service and the clusterip service within AKS sends the traffic to pod

Press enter or click to view image in full size

Press enter or click to view image in full size

Option2: Create the rules inside the cloudflared instead of passing it in the cloudflared manifest

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cfd18
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cfd18
  template:
    metadata:
      labels:
        app: cfd18
    spec:
      containers:
      - name: cloudflared
        image: cloudflare/cloudflared:latest
        args:
        - tunnel
        - run
        env:
        - name: TUNNEL_TOKEN
          value: "pass it securely"
        - name: TUNNEL_NAME
          value: feb25
        - name: CREDENTIALS_FILE_PATH
          value: /etc/cloudflared/creds/credentials.json

In option2, after creation of the connector, rules needs to be defined inside the tunnel from cloudflared: Open the tunnel → edit → public hostnames and pass the values. This will automatically create required DNS records which we did in previous steps.

Press enter or click to view image in full size

Azure

Part 1 of 1