Cloud Content Delivery Network

This page shows how to use a BackendConfig custom resource to configure Cloud Content Delivery Network (Cloud CDN) in Google Kubernetes Engine.

Overview

In a GKE cluster, incoming traffic is handled by HTTP(S) Load Balancing, which is a component of Cloud Load Balancing. Typically, the HTTP(S) load balancer is configured by the GKE Ingress controller, which gets configuration information from a Kubernetes Ingress object. The Ingress is associated with one or more Service objects. Each Service holds routing information that is used to direct an incoming request to a particular Pod and port.

Beginning with Kubernetes version 1.10.5-gke.3, you can provide additional configuration for the load balancer by associating a Service port with a custom resource named BackendConfig.

The GKE Ingress controller reads configuration information from the BackendConfig and sets up the load balancer accordingly. A BackendConfig holds configuration information that is specific to Cloud Load Balancing. Kubernetes Ingress and Service resources do not offer a way to configure provider-specific features like Cloud CDN; BackendConfig provides a way for you to do that configuration.

Here's the big picture of how you set up a BackendConfig in this exercise:

  1. Create a BackendConfig.
  2. Create a Service, and associate one of its ports with the BackendConfig.
  3. Create an Ingress, and associate the Ingress with the (Service, port) pair.

Before you begin

To prepare for this task, perform the following steps:

  • Ensure that you have enabled the Google Kubernetes Engine API.
  • Enable Google Kubernetes Engine API
  • Ensure that you have installed the Cloud SDK.
  • Set your default project ID:
    gcloud config set project [PROJECT_ID]
  • If you are working with zonal clusters, set your default compute zone:
    gcloud config set compute/zone [COMPUTE_ZONE]
  • If you are working with regional clusters, set your default compute region:
    gcloud config set compute/region [COMPUTE_REGION]
  • Update gcloud to the latest version:
    gcloud components update

Creating a namespace

Create a Kubernetes namespace for the objects in this guide:

kubectl create namespace cdn-how-to

Creating a Deployment

This Deployment manifest declares that you want to run two replicas of the hello-app web application:

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: cdn-how-to
  name: my-deployment
spec:
  selector:
    matchLabels:
      app: hello-app
  replicas: 2
  template:
    metadata:
      labels:
        app: hello-app
    spec:
      containers:
      - name: hello-app-container
        image: gcr.io/google-samples/hello-app:1.0
        ports:
        - containerPort: 8080

Copy the manifest to a file named my-deployment.yaml, and create the Deployment:

kubectl apply -f my-deployment.yaml

Creating a BackendConfig

Here's a manifest for a BackendConfig. The manifest specifies a Cloud CDN cache policy and declares that Cloud CDN should be enabled:

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  namespace: cdn-how-to
  name: my-backend-config
spec:
  cdn:
    enabled: true
    cachePolicy:
      includeHost: true
      includeProtocol: true
      includeQueryString: false

Copy the manifest to a file named my-backend-config.yaml, and create the BackendConfig:

kubectl apply -f my-backend-config.yaml

View the BackendConfig:

kubectl get backendconfig my-backend-config --output yaml --namespace cdn-how-to

You can see the Cloud CDN cache policy in the output:

apiVersion: cloud.google.com/v1beta1
kind: BackendConfig
metadata:
  name: my-backend-config
  namespace: cdn-how-to
  ...
spec:
  cdn:
    cachePolicy:
      includeHost: true
      includeProtocol: true
      includeQueryString: false
    enabled: true

Creating a Service

Here's a manifest for a Service:

apiVersion: v1
kind: Service
metadata:
  namespace: cdn-how-to
  name: my-service
  labels:
    app: hello-app
  annotations:
    beta.cloud.google.com/backend-config: '{"ports": {"80":"my-backend-config"}}'
spec:
  type: NodePort
  selector:
    app: hello-app
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080

Save the manifest to a file named my-service.yaml, and create the Service:

kubectl apply -f my-service.yaml

View the Service:

kubectl get service my-service --namespace cdn-how-to --output yaml

The output is similar to this:

apiVersion: v1
kind: Service
metadata:
  annotations:
    beta.cloud.google.com/backend-config: '{"ports": {"80":"my-backend-config"}}'
    ...
  labels:
    app: hello-app
  name: my-service
  namespace: cdn-how-to
  ...
spec:
  clusterIP: 10.35.251.29
  externalTrafficPolicy: Cluster
  ports:
  - nodePort: 31488
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: hello-app
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

For the purposes of this exercise, these are the important things to note about your Service:

  • Port 80 of the Service is associated with a BackendConfig named my-backend-config. The beta.cloud.google.com/backend-config annotation specifies this.

  • The Service has type NodePort. This is the required type for Services that are going to be associated with an Ingress.

  • Any Pod that has the label app: hello-app is a member of the Service. The selector field specifies this.

  • Traffic directed to the service on TCP port 80 is routed to TCP port 8080 in one of the member Pods. The port and targetPort fields specify this.

Reserving a static external IP address

Reserve a static external IP address:

gcloud compute addresses create cdn-how-to-address --global

View your static external IP address:

gcloud compute addresses list --filter "name=cdn-how-to-address"

The output shows the name and value of the address:

NAME                ...     ADDRESS        STATUS
cdn-how-to-address          203.0.113.1    RESERVED

Creating an Ingress

Here's a manifest for an Ingress:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: cdn-how-to
  name: my-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "cdn-how-to-address"
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: my-service
          servicePort: 80

Copy the manifest to a file named my-ingress.yaml, and create the Ingress:

kubectl create -f my-ingress.yaml

Wait a few minutes for the Kubernetes Ingress controller to configure a Cloud load balancer, and then view the Ingress:

kubectl get ingress my-ingress --output yaml --namespace cdn-how-to

The output is similar to this:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  ...
  name: my-ingress
  namespace: cdn-how-to
  ...
spec:
  rules:
  - http:
      paths:
      - backend:
          serviceName: my-service
          servicePort: 80
        path: /*
status:
  loadBalancer:
    ingress:
    - ip: 201.0.113.1

For the purpose of this exercise, here are the important things to note about your Ingress:

  • The IP address for incoming traffic is listed under loadBalancer:ingress:.

  • The Ingress has one rule that applies to incoming HTTP requests from any host. This is because there is no host field in the rule. So by default, the rule applies to all hosts.

  • All incoming requests are treated the same, regardless of the URL path. This is specified by the path value /*.

  • Incoming requests are routed to a Pod that is a member of my-service. In this exercise, the member pods have the label app: hello-app.

  • Requests are routed to the Pod on the target port specified in my-service. In this exercise, the Pod target port is 8080.

Viewing the web app

In your browser, enter your static external IP address.

The page displays the response from the hello-app web application that is running in one of the Pods of your Deployment:

Hello, world!
Version: 1.0.0
Hostname: my-deployment-574ddbdf88-f9fbj

Use curl to view the web app:

curl -v [STATIC_ADDRESS]

where [STATIC_ADDRESS] is your static external IP address.

The output is the response from hello-app:

Hello, world!
Version: 1.0.0
Hostname: my-deployment-574ddbdf88-zpb94

Limitations

Cloud CDN and Cloud Identity-Aware Proxy cannot be enabled for the same HTTP(S) Load Balancing backend service.

Troubleshooting

BackendConfig not found

This error occurs when a BackendConfig for a Service port is specified in the Service annotation, but the actual BackendConfig resource could not be found. This can occur if you have not created the BackendConfig resource at all, created it in the wrong namespace, or misspelled the reference in the Service annotation.

kubectl get event
KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: error getting BackendConfig for port 80 on service “default/my-service”:
no BackendConfig for service port exists

Cloud CDN and Cloud IAP both enabled

This error occurs when you have enabled both Cloud IAP and Cloud CDN in a BackendConfig.

kubectl get event
KIND    ... SOURCE
Ingress ... loadbalancer-controller

MESSAGE
Error during sync: BackendConfig default/config-default is not valid:
iap and cdn cannot be enabled at the same time.

Content not being cached

If you find that your content is not being cached, make sure that your application is properly configured to enable caching of content. For more information, see cacheability.

Cleaning up

After completing the exercises on this page, follow these steps to remove the resources to prevent unwanted charges incurring on your account:

Delete the Kubernetes objects that you created for this exercise:

kubectl delete ingress my-ingress --namespace cdn-how-to
kubectl delete service my-service --namespace cdn-how-to
kubectl delete backendconfig my-backend-config --namespace cdn-how-to
kubectl delete deployment my-deployment --namespace cdn-how-to
kubectl delete namespace cdn-how-to

Delete your static external IP address:

gcloud compute addresses delete cdn-how-to-address --global

What's next

Was this page helpful? Let us know how we did:

Send feedback about...

Kubernetes Engine