Who Am I

Hello, fellow tech enthusiasts! 🌐 My name is Chris Stradtman, and I've been on a remarkable journey through the world of technology from an early age. From tinkering with early computers as a child to working as a network engineer, service architect, and even hacking drone firmware for inter-drone awareness, I’ve worked across the shifting frontiers of technology, always hands-on and curious. I'll be sharing my insights, experiences, and thoughts on the latest tech developments, as well as relating them to previous technology. But more than that, I'll be exploring how technology is a journey, not a destination – much like life itself. As I reflect on my career and look ahead to new opportunities, I hope this blog demonstrates my passion for innovation and my commitment to solving complex challenges. Join me as we navigate the ever-changing landscape of modern technology, uncovering the innovations that shape our world and the lessons learned along the way.

Implementing Edgio Delivery CDN as an Ingress Provider in Your Kubernetes Cluster

 

Foreword


The following is an example of integrating Kubernetes with Edgio Delivery CDN service for experienced Kubernetes practitioners. It assumes a level of knowledge in Kubernetes and knowledge of standard internet protocols such as TCP, DNS, and HTTP/HTTPS. The final objective is to demonstrate a way to make Edgio Delivery CDN service appear as standard ingress. The Kubernetes user may maintain the ingress configuration in familiar systems such as Portainer or Terraform.

Kubernetes


Kubernetes.io defines Kubernetes as “an open-source system for automating deployment, scaling, and management of containerized applications.” There are several ways to bring up a cluster. The quickest and easiest way is to leverage one of the many cloud providers that allow “one-click” deployments.

Kubernetes clusters contain many parts. In the demo, we will focus on a subset of the total.

Kubernetes Parts

  • Node: machine or VM that hosts the workloads and services.
  • Workload: Application simple or complex
  • Pod: containers that the application software is running within. One or more pods make up a workload.
  • Service: a mechanism that exposes a workload to other workloads or ingresses
  • Ingress: A mechanism to expose services to the outside world

Edgio


Edgio makes connected living faster, safer, and simpler to manage by powering unmatched speed, security and, simplicity at the edge with our seamlessly integrated delivery, applications and, streaming solutions. Our globally-scaled technology and expert services fuel the world’s top brands with the capacity to deliver the fastest, most dynamic and, frictionless education, entertainment, events and, applications to every user. Dedicated to providing unparalleled client care and extending value every step of the way, Edgio is a partner of choice, driving worldwide internet traffic to support the most popular shows, movies, sports, games and music, and instant-loading websites.

Architecture







Edgio Delivery CDN


For this demo, we use the Edgio python SDK at GitHub – llnw/llnw-sdk-python: Limelight Networks Python SDK. The basic configuration will be kept simple.

Configuring the CDN, the following pieces of data (other than authentication) are needed.

Shortname: A unique identifier within Edgio Delivery CDN that contains CDN rules.


Published hostname: The hostname that faces the Internet from the CDN. In the case of this demo, the hostname cstradtman.s.llnwi.net was previously configured.


Published Url Path: The path mapped into the specific rule in question.


Source Hostname: The hostname of the origin. In this case, a round-robin record points to the Kubernetes cluster.


Source Url Path: The destination path associated with the hostname. In this case, it will be blank since we describe it later in this document. In this demo, workloads will be differentiated by port number and not a path.

DNS


The demo uses the Vultr DNS API. Configuring the CDN using IP addresses as the origins is not a best practice. The demo uses an API to create or modify a round-robin A record pointing to the cluster nodes.

Cluster


The demo is in Vultr on a three-node cluster.

Namespaces


The demo will exist under the default namespace since it’s the only thing running on this cluster

Nodes

  • VKE cluster – 3 nodes each with
  • 4G ram
  • 80G disk
  • 2 vCPU
  • 1 G network

Workloads


The workload is a simple Python program that returns basic information about the HTTP connection. We deploy this demo inline, which is not a typical deployment method. However, it allows the demo to be fully self-contained rather than having external dependencies to build the containers and store them. The source code is in Appendix 1. The actual Python code is italicized, and the YAML code describing the deployment is not. The workload utilizes TCP port 8000.

Services


There are several service types available. We are using the “NodePort” service type. It exposes the service on the external IP addresses of the cluster on a randomly selected high port number. Kubernetes takes care of the connection to a node where the workload runs.

Ingress


Kubernetes ingress is a software object that describes the behavior of a collection of resources that make a Kubernetes service available outside of the cluster.

An Ingress controller combines software and/or hardware that instantiates the Ingress software object.

A CDN is a feature-rich distributed reverse proxy, besides the reverse proxy function, CDNs usually have flexible cache algorithms, geographic diversity, security features (ACLs (GEO, method, regex), Web applications Firewalls, watermarking, and other more advanced features not found in simple reverse proxies.

The APIs for Kubernetes object monitoring are well defined, and a number of HTTP reverse proxies and load balancers have been extended to work as Ingress controllers. For example, NGINX and HAProxy are old school proxies that have been extended to work as ingress controllers and ones like Traefik and Itsio were developed in parallel to Kubernetes . A CDN is a natural next progression from simple reverse proxies. In the case of this demo, we will create a custom ingress controller that controls Edgio Delivery CDN. The data typically required for describing an Ingress is

  • Public Hostname: The hostname that the Kubernetes service will be available on
  • Public path: the path associated with the Public hostname that the Kubernetes services will be available on
  • Backend service name: service-name defined by a Kubernetes services stanza
  • Backend Port: A name matching the port description in the service description

Edgio Delivery CDN asset mappings:

Kubernetes Edgio Delivery CDN
Public Hostname Published Hostname
Public Path Published URL Path
Backend Service Name Unique within the Kubernetes cluster (Not known/available to the outside world)
Source Hostname Fully qualified domain name (Not actually one-to-one)



The logic behind it is:

  • Listen for namespace ingress event from the cluster
    • If the event type is “added.”
      • Get the IP address of nodes in the cluster
      • Create DNS record in vultr
      • Create CDN rules in Edgio CDN pointing toward the created DNS records and the port allocated by the NodePort service.
    • If the event type is “modified.”
      • Find CDN rules in Edgio CDN that match the existing ingress
      • Modify rules in Edgio CDN to reflect new data in the event
    • If the event type is “deleted.”
      • Find CDN rules in Edgio CDN that match removed ingress
      • Remove DNS records in Vultr associated with this ingress
      • Remove the CDN rules in Edgio that match removed ingress

Configuration

Objects


Kubernetes objects are described in YAML files. In Kubernetes, every configuration is an object. You could place all the deployment configs in a single file. It’s common practice to break them up based on what portion of the deployment they describe.

Deployment


In Appendix 1, there is a large YAML file that is a combination of Python code and YAML. Everything after “spec.template.spec.containers.command” is the Python code that is turned into a container during the deployment. From the point of view of the Kubernetes object the lines that are important to us for the demo are:

  • Kind: Deployment – describes the object as a deployment
  • Metadata.name: name is used by the service object to create the link between the two.
  • Spec.selector.matchlabels.app: tells what pods the deployment will apply to.
  • Spec.template.spec.containers.image: points to the docker hub python 3 official image
  • Spec.template.spec.containers.ports.name: a label for the port to be referenced in other objects service in this case
  • Spec.template.spec.containers.ports.containerport: lists the TCP port exposed from the running container

Service

  • Kind: Deployment – describes the object as a deployment
  • Metadata.name: name is used by the ingress object to create the link between the two.
  • Spec.selector.app: points to the name of the deployment – pywai-inline-service as named in the deployment object.
  • Spec.selector.type: Nodeport this defines this service as type Nodeport as described above
  • Spec.ports.name: the label for the port to be referenced by other objects(ingress in this case)
  • Spec.ports.protocol: defines IP protocol in use – TCP
  • Spec.ports.port: defines port exposed by this service
  • spec.ports.targetPort: points to port exposed by the deployment – pywai-inline-deployment in this case

Ingress


In Appendix 3, you will see the YAML file describing the Kubernetes object for the Edgio ingress controller. The objects to be concerned with are:

  • Kind: Ingress – this tells Kubernetes that it is an Ingress object
  • Medatdata.name – everything in Kubernetes must have a name
  • Metadata.labels.cdn – this tag is used to signal the ingress controller that this object is for the edgio ingress controller
  • Spec.rules.host – public hostname to expose the service.
  • Spec.rules.http.paths.path – path associated with the service.
  • Spec.rules.http.paths.backend.service.name – label of the service to expose
  • Spec.rules.http.paths.backend.service.port.name – label of port of the service to expose

Config Maps

Secrets


Kubernetes has a built-in concept of secrets for the storage of sensitive data. This demo leverages this for the usernames and shared keys for both Edgio SDK and the Vultr DNS API

Code examples


The code for this demonstration included in the git repos


Future additions and enhancements to the current design

In a future post, we’ll show how to leverage other Edgio solutions for example Applications. We could use the Kubernetes cluster as a way to leverage and sync configurations among multiple Edgio CDN offerings for different workflows.


Appendix 1 – pywai-inline-deployment.yaml


apiVersion: apps/v1

kind: Deployment

metadata:

name: pywai-inline-deployment

spec: selector: matchLabels: app: pywai-inline-deployment replicas: 1 template: metadata: labels: app: pywai-inline-deployment spec: containers: - name: python image: python:3 command: - /bin/sh - "-c" - | cat > /test.py <<EOF #!/usr/bin/env python3 """ Very simple HTTP server in python for logging requests Usage:: ./server.py [<port>] """ from http.server import BaseHTTPRequestHandler, HTTPServer import logging import pprint class S(BaseHTTPRequestHandler): def _set_response(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() def do_GET(self): logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(pprint.pformat(self.headers))) self._set_response() headerinfo = pprint.pformat(self.headers.items()) clientinfo = pprint.pformat(self.client_address) lineinfo = pprint.pformat(self.requestline) versioninfo = pprint.pformat(self.request_version) self.wfile.write("<pre> {} </pre>".format(headerinfo).encode('utf-8')) self.wfile.write("<pre> {} </pre>".format(clientinfo).encode('utf-8')) self.wfile.write("<pre> {} </pre>".format(lineinfo).encode('utf-8')) self.wfile.write("<pre> {} </pre>".format(versioninfo).encode('utf-8')) def run(server_class=HTTPServer, handler_class=S, port=8080): logging.basicConfig(level=logging.INFO) server_address = ('', port) httpd = server_class(server_address, handler_class) logging.info('Starting httpd...\n') try: httpd.serve_forever() except KeyboardInterrupt: pass httpd.server_close() logging.info('Stopping httpd...\n') if __name__ == '__main__': from sys import argv if len(argv) == 2: run(port=int(argv[1])) else: run() EOF exec python /test.py 8000 ports: - name: http containerPort: 8000

Appendix 2 – pywai-inline-service.yaml


apiVersion: v1
kind: Service
metadata:
  name: pywai-inline-service
spec:
  selector:
    app: pywai-inline-deployment
  type: NodePort
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8000

Appendix 3 – pywai-edgio-ingress.yaml

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: external-pywai-inline-ingress annotations: kubernetes.io/ingress.class: edgio labels: cdn: edgio spec: rules: - host: <Enter unique shortname>.s.llnwi.net http: paths: - path: /fred pathType: Exact backend: service: name: external-pywai-inline-service port: name: pywai-port

No comments:

Post a Comment

Little bit of tech history

 A little bit of IBM history preserved in brass I was digging through some of my grandfather's belongings recently and came across this ...