DEV Community

Split Blog for Split Software

Posted on • Originally published at split.io on

Kubernetes and Split

Introduction

Kubernetes is a tool used to orchestrate and manage containerized applications. Split provides three containerized applications that can be run on your own infrastructure. These apps can handle specific use cases for feature flagging and experimentation with our SDKs and APIs.

All three Kubernetes related examples have a NodePort service to allow communication outside the cluster for the app. Once you have this up and running, if you are using minikube you can get the NodePort URL by calling:

minikube service pyapp-svc -url
Enter fullscreen mode Exit fullscreen mode

Then enter the returned URL in a browser, and you will see a page showing the pod name and the split treatment. It’s time to embrace Kubernetes and Split.

Then enter the returned URL in a browser, and you will see a page showing the pod name and the split treatment. It’s time to embrace Kubernetes and Split.

The Evaluator

Use Case

Use the split evaluator when there is no Split SDK for the language you have in your application backend. The evaluator makes HTTP APIs available. The APIs exposed by the evaluator have most of the SDK functionality. You can use it to get treatments with dynamic config, and track events. See here for more information on individual commands and usage to deploy the split evaluator.

Sizing and Scaling

Internal benchmarks on an AWS c5.2xlarge EC2 instance have shown that an evaluator instance running on a single thread can process 1,200 transactions per second. A transaction can retrieve multiple split treatments if using the getTreatments endpoint. In this specific benchmark, the evaluator was serving two treatments, so this was 2,400 treatments per second.

The evaluator is stateless. It can be scaled in and out as needed by spinning up a new instance and routing requests to it. The general recommendation is to spin up a new instance at 50% CPU or 50 percent memory usage.

Health Check

The evaluator has a health check endpoint that can be used to determine if the evaluator is healthy: /admin/healthcheck.

Sample Usage

Here are sample YAML files for creating an evaluator. This includes using a ConfigMap to store the SDK key used as well as health checks and readiness checks. The split-name in the config map allows you to use your Split organization for testing and bringing up this application.

Architecture Diagram:

YAML Files

ConfigMap:

apiVersion: v1
data:
 api-key: <insert your api key here>
 split-name: <insert your split name here>
kind: ConfigMap
metadata:
 name: eval-configmap
Enter fullscreen mode Exit fullscreen mode

Evaluator Service:

apiVersion: v1
kind: Service
metadata:
 labels:
   app: evaluator-deployment
 name: evaluator-svc
spec:
 ports:
 - name: 7548-7548
   port: 7548
   protocol: TCP
   targetPort: 7548
 selector:
   app: evaluator-deployment
Enter fullscreen mode Exit fullscreen mode

Evaluator Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: evaluator-deployment
 name: evaluator-deployment
spec:
 replicas: 2
 selector:
   matchLabels:
     app: evaluator-deployment
 template:
   metadata:
     labels:
       app: evaluator-deployment
   spec:
     containers:
     - image: splitsoftware/split-evaluator
       name: split-evaluator
       startupProbe:
         httpGet:
           path: /admin/healthcheck
           port: 7548
         initialDelaySeconds: 1
         timeoutSeconds: 1
       livenessProbe:
         httpGet:
           path: /admin/healthcheck
           port: 7548
         initialDelaySeconds: 1
         periodSeconds: 5
       env:
       - name: SPLIT_EVALUATOR_API_KEY
         valueFrom:
           configMapKeyRef:
             name: eval-configmap
             key: api-key
Enter fullscreen mode Exit fullscreen mode

App NodePort Service:

apiVersion: v1
kind: Service
metadata:
 labels:
   app: pyapp-svc
 name: pyapp-svc
spec:
 ports:
 - name: “5000”
   port: 5000
   protocol: TCP
   targetPort: 5000
 selector:
   app: pyapp-demo
 type: NodePort
Enter fullscreen mode Exit fullscreen mode

App Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: pyapp-demo
 name: pyapp-demo
spec:
 replicas: 3
 selector:
   matchLabels:
     app: pyapp-demo
 template:
   metadata:
     labels:
       app: pyapp-demo
   spec:
     containers:
     - image: kleinjoshuaa/py-split-demo-evaluator
       name: evaluator-demo
       env:
       - name: SPLIT_NAME 
         valueFrom:
           configMapKeyRef:
             name: eval-configmap
             key: split-name
       - name: EVALUATOR_URL
         value: "http://evaluator-svc:7548"
       - name: POD_ID 
         valueFrom:
           fieldRef:
             fieldPath: metadata.name
Enter fullscreen mode Exit fullscreen mode

The Proxy

Use Case

There are two main scenarios where the Split Proxy is useful. The first is when using a client-side SDK as a workaround to adblockers. Using the Split Proxy will allow the SDK to connect to URL endpoints within your domain. As a result, an ad-blocker won’t see the connection to Split’s servers that the Proxy is doing on your backend.

The second use case for the Proxy is with server-side SDKs: in a microservices environment or otherwise where multiple Split SDKs are used in multiple different services. The Split Proxy can be used so that those SDKs communicate across your internal network infrastructure. This is better than having to establish a connection to the Split Cloud on their own. As a result, this can speed up SDK initialization time significantly. In addition, this allows for monitoring Split Proxy as a single point of contact with Split Cloud.

This second case is what we will be using for the example here. For the first use case, one could expose the proxies directly to the internet rather than only to the app within the cluster. For more information see the Split Proxy page in Split’s help center.

Sizing and Scaling

To ensure SDKs have no issues initializing even when facing high demand, you could consider having separate Proxy instances. They can handle split and segments, events and impressions. Since events and impressions are more memory-intensive tasks for the Proxy, serving a high amount of these requests may affect the responsiveness of the more performant splits and segments requests—which are the ones involved in the SDKs’ initialization. Having separate instances of the Proxy–one for impressions and one for events–can significantly reduce latency and allow for fine tuning based upon events and impressions traffic you have.

Additionally, since split and segment requests are less demanding, having dedicated instances for initialization will likely allow you to serve more SDKs with fewer instances.

For reference, these are the sizing recommendations from our internal testing. A Proxy serving 1M JS SDK clients with default polling rates for it to stabilize at ~50 percent CPU and lower RAM usage:

  • 1 instance with 32 cores, 32GB memory, 10 Gigabit network
  • 4 instances with 8 cores, 8GB memory, 10 Gigabit network

Health Check

The URL for the healthcheck is /health/application.For the sake of this sample application, we are using a curl command on the pod itself to do the healthcheck, since Kubernetes does not have native JSON parsing capability.

Sample Usage

Here are sample YAML files for creating a Proxy. This includes using a ConfigMap to store the SDK key used and health and readiness checks. This has a NodePort service to allow the app to communicate outside the cluster. We have separate deployments for a Proxy for the events.split.io events and impressions endpoint, and one for sdk.split.io. The split-name in the ConfigMap allows you to use your own Split organization for testing and bringing up this application.

YAML Files

ConfigMap:

apiVersion: v1
data:
 api-key: <insert the SDK key here to connect to split cloud>
 client-api-key: <insert the ‘client’ SDK key for your SDK clients to use>
 split-name: <insert your split name here>
kind: ConfigMap
metadata:
 name: proxy-configmap
Enter fullscreen mode Exit fullscreen mode

Events Proxy Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: proxy-events-deployment
 name: proxy-events-deployment
spec:
 replicas: 3
 selector:
   matchLabels:
     app: proxy-events-deployment
 template:
   metadata:
     labels:
       app: proxy-events-deployment
   spec:
     containers:
     - image: splitsoftware/split-proxy
       name: split-proxy
       startupProbe:
         exec:
           command:
           - /bin/bash
           - -c
           - health=`curl -s localhost:3010/health/application | grep '"healthy":true' -o | wc -l`; if test $health -ne 3; then exit 0; else exit 1; fi
         initialDelaySeconds: 3
         timeoutSeconds: 1
       livenessProbe:
         exec:
           command:
           - /bin/bash
           - -c
           - health=`curl -s localhost:3010/health/application | grep '"healthy":true' -o | wc -l`; if test $health -ne 3; then exit 0; else exit 1; fi
         initialDelaySeconds: 1
         periodSeconds: 5
       env:
       - name: SPLIT_PROXY_APIKEY 
         valueFrom:
           configMapKeyRef:
             name: proxy-configmap
             key: api-key
       - name: SPLIT_PROXY_CLIENT_APIKEYS 
         valueFrom:
           configMapKeyRef:
             name: proxy-configmap
             key: client-api-key
Enter fullscreen mode Exit fullscreen mode

Events Proxy Service:

apiVersion: v1
kind: Service
metadata:
 labels:
   app: proxy-events-svc
 name: proxy-events-svc
spec:
 ports:
 - name: proxy
   port: 3000
   protocol: TCP
   targetPort: 3000
 - name: admin
   port: 3010
   protocol: TCP
   targetPort: 3010
 selector:
   app: proxy-events-deployment
Enter fullscreen mode Exit fullscreen mode

SDK Proxy Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: proxy-sdk-deployment
 name: proxy-sdk-deployment
spec:
 replicas: 2
 selector:
   matchLabels:
     app: proxy-sdk-deployment
 template:
   metadata:
     labels:
       app: proxy-sdk-deployment
   spec:
     containers:
     - image: splitsoftware/split-proxy
       name: split-proxy
       startupProbe:
         exec:
           command:
           - /bin/bash
           - -c
           - health=`curl -s localhost:3010/health/application | grep '"healthy":true' -o | wc -l`; if test $health -ne 3; then exit 0; else exit 1; fi
         initialDelaySeconds: 3
         timeoutSeconds: 1
       livenessProbe:
         exec:
           command:
           - /bin/bash
           - -c
           - health=`curl -s localhost:3010/health/application | grep '"healthy":true' -o | wc -l`; if test $health -ne 3; then exit 0; else exit 1; fi
         initialDelaySeconds: 1
         periodSeconds: 5
       env:
       - name: SPLIT_PROXY_APIKEY 
         valueFrom:
           configMapKeyRef:
             name: proxy-configmap
             key: api-key
       - name: SPLIT_PROXY_CLIENT_APIKEYS 
         valueFrom:
           configMapKeyRef:
             name: proxy-configmap
             key: client-api-key
Enter fullscreen mode Exit fullscreen mode

SDK Proxy Service:

apiVersion: v1
kind: Service
metadata:
 labels:
   app: proxy-sdk-svc
 name: proxy-sdk-svc
spec:
 ports:
 - name: proxy
   port: 3000
   protocol: TCP
   targetPort: 3000
 - name: admin
   port: 3010
   protocol: TCP
   targetPort: 3010
 selector:
   app: proxy-sdk-deployment
Enter fullscreen mode Exit fullscreen mode

App Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: pyapp-demo
 name: pyapp-demo
spec:
 replicas: 2
 selector:
   matchLabels:
     app: pyapp-demo
 template:
   metadata:
     labels:
       app: pyapp-demo
   spec:
     containers:
     - image: kleinjoshuaa/py-split-demo-proxy
       name: proxy-demo
       env:
       - name: SPLIT_NAME 
         valueFrom:
           configMapKeyRef:
             name: proxy-configmap
             key: split-name
       - name: API_KEY
         valueFrom:
           configMapKeyRef:
             name: proxy-configmap
             key: client-api-key
       - name: EVENTS_PROXY_URL
         value: "http://proxy-events-svc:3000/api"
       - name: SDK_PROXY_URL
         value: "http://proxy-sdk-svc:3000/api"
       - name: POD_ID 
         valueFrom:
           fieldRef:
             fieldPath: metadata.name
Enter fullscreen mode Exit fullscreen mode

App Service:

apiVersion: v1
kind: Service
metadata:
 labels:
   app: pyapp-svc
 name: pyapp-svc
spec:
 ports:
 - name: "5000"
   port: 5000
   protocol: TCP
   targetPort: 5000
 selector:
   app: pyapp-demo
 type: NodePort
Enter fullscreen mode Exit fullscreen mode

The Synchronizer

Use Case

The Split Synchronizer coordinates the sending and receiving of data to a remote datastore (Redis). All of your processes can share to this datastorefor the evaluation of treatments, acting as the cache for your SDKs. The Synchronizer, Redis, and your app can all be orchestrated in a Kubernetes cluster. We will show a basic example of this.

Sizing and Scaling

The Synchronizer is multithreaded and can handle production-level traffic by increasing the number of threads posting events and impressions. For more information on sizing Redis and the Synchronizer for production-level workloads, review our Synchronizer Runbook.

Health Check

The URL for the healthcheck is /health/application. For the sake of this sample application, we are using a curl command on the pod itself to do the healthcheck, as Kubernetes does not have native JSON parsing capability.

Sample Usage

Here are sample YAML files for creating the demo app, as well as Synchronizer and Redis. This includes using a ConfigMap to store the SDK key used and health and readiness checks. This has a NodePort service to allow the app using the SDK to communicate outside the cluster. The split-name in the config map allows you to use your own Split organization for testing and bringing up this application.

Architecture Diagram

YAML Files

App Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
 labels:
   app: pyapp
 name: pyapp
spec:
 replicas: 3
 selector:
   matchLabels:
     app: pyapp
 template:
   metadata:
     labels:
       app: pyapp
   spec:
     containers:
     - image: kleinjoshuaa/py-split-demo
       name: split-demo
       startupProbe:
         exec:
           command:
           - /bin/bash
           - -c
           - health=`curl -s localhost:3010/health/application | grep '"healthy":true' -o | wc -l`; if test $health -ne 3; then exit 0; else exit 1; fi
         initialDelaySeconds: 3
         timeoutSeconds: 1
       livenessProbe:
         exec:
           command:
           - /bin/bash
           - -c
           - health=`curl -s localhost:3010/health/application | grep '"healthy":true' -o | wc -l`; if test $health -ne 3; then exit 0; else exit 1; fi
         initialDelaySeconds: 1
         periodSeconds: 5
       env:
       - name: API_KEY
         valueFrom:
           configMapKeyRef:
             name: app-configmap
             key: api-key
       - name: SPLIT_NAME 
         valueFrom:
           configMapKeyRef:
             name: app-configmap
             key: split-name
       - name: USE_REDIS
         value: "True"
       - name: REDIS_HOST 
         value: "redis-svc"
       - name: REDIS_PORT 
         value: "6379"
       - name: POD_ID 
         valueFrom:
           fieldRef:
             fieldPath: metadata.name
status: {}
Enter fullscreen mode Exit fullscreen mode

App Service:

apiVersion: v1
kind: Service
metadata:
 labels:
   app: pyapp-svc
 name: pyapp-svc
spec:
 ports:
 - name: "5000"
   port: 5000
   protocol: TCP
   targetPort: 5000
 selector:
   app: pyapp
 type: NodePort
Enter fullscreen mode Exit fullscreen mode

Redis Pod:

apiVersion: v1
kind: Pod
metadata:
   name: redis
   labels:
     app: redis
spec:
   containers:
   - name: redis
     image: redis
Enter fullscreen mode Exit fullscreen mode

Redis Service:

apiVersion: v1
kind: Service
metadata:
 name: redis-svc
spec:
 selector:
   app: redis
 ports:
 - port: 6379
   targetPort: 6379
Enter fullscreen mode Exit fullscreen mode

Synchronizer Pod:

apiVersion: v1
kind: Pod
metadata:
   name: split-sync
   labels:
     app: split-sync
spec:
   containers:
   - name: split-sync
     image: splitsoftware/split-synchronizer
     env:
     - name: SPLIT_SYNC_APIKEY
       valueFrom:
         configMapKeyRef:
           name: app-configmap
           key: api-key
     - name: SPLIT_SYNC_REDIS_HOST
       value: redis-svc
     - name: SPLIT_SYNC_REDIS_PORT
       value: "6379"
Enter fullscreen mode Exit fullscreen mode

ConfigMap:

apiVersion: v1
data:
 api-key: <insert your SDK Key here>
 split-name: <insert your split name here>
kind: ConfigMap
metadata:
 name: app-configmap
Enter fullscreen mode Exit fullscreen mode

Start using Split and Kubernetes

Got further questions about Split and Kubernetes? Ask us. Want to take your feature management and experimentation to the next level? Schedule a demo with our sales team today.

Top comments (0)