...
The tests will be performed on a cluster consisting of 4 nodes (1 master and 3 worker) with the same flavor. The flavor will also be modified in turn, remaining the same between the VMs in the cluster, multiplying the CPU and RAM by a factor of 2: passing from training medium (2 CPUs and 4GB RAM) to large (4 CPUs and 8GB RAM) and finally xlarge (8 CPUs and 16GB RAM). Finally, the clusters used for the tests are set to "factory settings", ie i.e. they will contain only the starting software of a typical k8s cluster just created. Before we continue, let's familiarize ourselves with a couple of tools suitable for our purposes: Metrics Server and Horizontal Pod Autoscaler.
...
Metrics Server deployment will likely not be Ready. If, analyzing the Pod logs, you see the error "unable to fully scrape metrics", then edit the deployment by inserting the flag
...
Run and expose php-apache server
To demonstrate HPA , we will use a custom docker image based on the php-apache image. Launch the docker build command, after copying the contents of the Dockerfile and the index.php Apply the following file, to install a simple PHP web application in the Kubernetes cluster. Then, verify the pods were created.
| Code Block | |||||||
|---|---|---|---|---|---|---|---|
| |||||||
FROMapiVersion: apps/v1 kind: Deployment metadata: name: php:5-apache spec: COPY index.php /var/www/html/index.php RUN chmod a+rx index.php // These lines of code define the index.php file, which performs some CPU intensive computations <?php $x = 0.0001; for ($i = 0; $i <= 1000000; $i++) { $x += sqrt($x); } echo "OK!"; ?> |
First, we will start a deployment running the image and expose it as a service using the following configuration
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
apiVersion: apps/v1 kind: Deployment metadata: name: php-apache spec: selector: matchLabels: run: php-apache replicas: 1 template: metadata: labels: run: php-apache spec: containers: - name: php-apache selector: matchLabels: run: php-apache replicas: 1 template: metadata: labels: run: php-apache spec: containers: - name: php-apache image: k8s.gcr.io/hpa-example ports: - containerPort: 80 resources: # <--- Pay attention limits: cpu: 500m image: k8s.gcr.io/hpa-example requests: ports: - containerPort: 80 resources: # <--- Pay attentioncpu: 200m --- apiVersion: v1 kind: Service metadata: name: php-apache labels: run: php-apache spec: limitsports: - port: 80 cpu: 500mselector: requests: cpu: 200m --- apiVersion: v1 kind: Service metadata: name: php-apache labels: run: php-apache spec: ports: - port: 80 selector: run: php-apacherun: php-apache |
The one just shown is a .yaml file containing a classic configuration of a deployment and a service. The only novelty is represented by the resources parameter, in the Container specifications. When you specify a Pod, you can optionally specify how much of each resource a Container needs. When you specify the resource request for Containers in a Pod, the scheduler uses this information to decide which node to place the Pod on. When you specify a resource limit for a Container, the kubelet enforces those limits The one just shown is a .yaml file containing a classic configuration of a deployment and a service. The only novelty is represented by the resources parameter, in the Container specifications. When you specify a Pod, you can optionally specify how much of each resource a Container needs. When you specify the resource request for Containers in a Pod, the scheduler uses this information to decide which node to place the Pod on. When you specify a resource limit for a Container, the kubelet enforces those limits so that the running container is not allowed to use more of that resource than the limit you set. The kubelet also reserves at least the request amount of that system resource specifically for that container to use. If the node where a Pod is running has enough of a resource available, it's possible (and allowed) for a container to use more resource than its request for that resource specifies. However, a container is not allowed to use more than its resource limit.
...
Now, we will see how the autoscaler reacts to increased load. We will start a container, and send an infinite loop of queries to the php-apache service (run it in a different terminal)increased load. Once the PHP web application is running in the cluster and we have set up an autoscaling deployment, introduce load on the web application. Here we use a BusyBox image in a container and infinite web requests running from BusyBox to the PHP web application. Copy and deploy the infinite-calls.yaml file.
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
apiVersion: apps/v1
kind: Deployment
metadata:
name: infinite-calls
labels:
app: infinite-calls
spec:
replicas: 1
selector:
matchLabels:
app: infinite-calls
template:
metadata:
name: infinite-calls
labels:
app: infinite-calls
spec:
containers:
- name: infinite-calls
image: busybox
command:
- /bin/sh
- -c
- "while true | ||||||
| Code Block | ||||||
| ||||||
$ kubectl run -i --tty load-generator --rm --image=busybox --restart=Never \ -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done" If you don't see a command prompt, try pressing enter. OK!OK!OK!OK!OK!OK!OK!OK!...apache; done" |
Within a minute or so, we should see the higher CPU load by executing
...
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
$ kubectl get deployment,hpa NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/infinite-calls 1/1 1 1 5m19s deployment.apps/php-apacheapache 7/7 7 7 2d1h71m NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE horizontalpodautoscaler.autoscaling/php-apache Deployment/php-apache 42%/50% 1 10 7 2d71m |
We also take a look at the resource consumption of the Pods, to check how the system reacts. In the php-apache.yaml file, seen above, we set requests.cpu: 200m in the container specification. Subsequently, we entrusted the management of the deployment to the HPA, requiring that the CPU consumption of the Pods does not exceed, on average, the value of 100 milli-cores. The system actually respects these dictates. In fact, by performing an arithmetic average of the CPU consumption by the php-apache Pods below, we obtain a value of about 84 milli-cores. Compare this result with the TARGETS column of the get hpa command above: 84 milli-cores correspond to 42% of the 200 milli-cores required for Pods.
| Code Block | ||||||
|---|---|---|---|---|---|---|
| ||||||
$ kubectl top pod NAME CPU(cores) MEMORY(bytes) load-generator infinite-calls-69f758db46-hxssq 7m 0Mi2Mi php-apache-d4cf67d68-26nnm 82m 8Mi php-apache-d4cf67d68-5cxkh 103m 8Mi php-apache-d4cf67d68-gn5l7 90m 8Mi php-apache-d4cf67d68-j229m 74m 8Mi php-apache-d4cf67d68-k9vqz 77m 8Mi php-apache-d4cf67d68-tlssltlssl 90m 8Mi php-apache-d4cf67d68-x76h2 75m 10Mi |
Stop load
We will finish our example by stopping the user load. In the terminal where we created the container with busybox image, terminate the load generation by typing <Ctrl> + Cstopping the process, simply deleting the deployment/infinite-calls component or, if you want to reuse it for further testing, scale it to zero replicas. Then, we verify the result state. After : after a minute or so, re-run the two get commands used earlier. You should get that CPU utilization dropped to 0, and so HPA autoscaled the number of replicas back down to 1.