Setting up Scaling in Kubernetes

Github: https://github.com/blazingraptor/getting-started-k8s.git

Docker Hub: https://hub.docker.com/repository/docker/blazingraptor/getting-started-k8s/general

Getting Started

Clone the repo

blazingraptor@galactica:~/docker/scale-example$ git clone https://github.com/blazingraptor/getting-started-k8s.git
Cloning into 'getting-started-k8s'...
remote: Enumerating objects: 102, done.
remote: Counting objects: 100% (37/37), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 102 (delta 16), reused 10 (delta 10), pack-reused 65 (from 1)
Receiving objects: 100% (102/102), 71.75 KiB | 1.84 MiB/s, done.
Resolving deltas: 100% (33/33), done.

Apply the LoadBalancer

blazingraptor@galactica:~/docker/getting-started-k8s$ cd Services/
blazingraptor@galactica:~/docker/getting-started-k8s/Services$ kubectl apply -f svc-lb.yml 
service/ps-lb created

Now the LoadBalancer is running

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Services$ kubectl get services
NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1      <none>        443/TCP        211d
ps-lb        LoadBalancer   10.107.92.89   localhost     80:31789/TCP   52s

Apply deployment

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl apply -f deploy.yml
deployment.apps/web-deploy created

See replicas

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get pods
NAME                          READY   STATUS    RESTARTS   AGE
web-deploy-55bdf4c5d5-2vt78   1/1     Running   0          20m
web-deploy-55bdf4c5d5-k92lh   1/1     Running   0          20m
web-deploy-55bdf4c5d5-n5kgp   1/1     Running   0          20m
web-deploy-55bdf4c5d5-r7fzx   1/1     Running   0          20m
web-deploy-55bdf4c5d5-xxgrk   1/1     Running   0          20m

Deployments are API resources
Inspect them

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get deploy
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
web-deploy   5/5     5            5           39m
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get deployments.apps
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
web-deploy   5/5     5            5           39m

Get Replica Sets

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get rs
NAME                    DESIRED   CURRENT   READY   AGE
web-deploy-55bdf4c5d5   5         5         5       47m
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get replicasets.apps
NAME                    DESIRED   CURRENT   READY   AGE
web-deploy-55bdf4c5d5   5         5         5       47m

See the file that set the replica sets

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ cat deploy.yml;echo
# Simple deployment used to deploy and manage the app in nigelpoulton/getting-started-k8s:1.0
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deploy
  labels:
    app: web
spec:
  replicas: 5
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      terminationGracePeriodSeconds: 1
      containers:
      - name: hello-pod
        image: nigelpoulton/getting-started-k8s:1.0
        imagePullPolicy: Always
        ports:
        - containerPort: 8080

Hash is based on this


We have 5 pods running. To connect to them, we need a service...

  • We kept ours running
  • It's the LoadBalancer

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl get services
NAME          TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes    ClusterIP      10.96.0.1        <none>        443/TCP        10d
ps-lb         LoadBalancer   10.99.228.61     localhost     80:31787/TCP   58m
ps-nodeport   NodePort       10.100.201.228   <none>        80:31111/TCP   40h

It's actually this

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get services | grep ps-lb
ps-lb        LoadBalancer   10.99.228.61   localhost     80:31789/TCP   58m

Describe it

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl describe services ps-lb 
Name:                     ps-lb
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=web
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.99.228.61
IPs:                      10.99.228.61
LoadBalancer Ingress:     localhost
Port:                     <unset>  80/TCP
TargetPort:               8080/TCP
NodePort:                 <unset>  31787/TCP
Endpoints:                10.1.0.19:8080,10.1.0.20:8080,10.1.0.21:8080 + 2 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl describe services ps-lb | grep Selector
Selector:                 app=web

* Says send traffic to all pods in the cluster with the "app=web" label


Check label on the 5 replicas we have running

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl get pods  --show-labels
NAME                          READY   STATUS    RESTARTS   AGE   LABELS
web-deploy-55bdf4c5d5-252qn   1/1     Running   0          55m   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-czcfc   1/1     Running   0          55m   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-hj65t   1/1     Running   0          55m   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-pjr6w   1/1     Running   0          55m   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-t95hq   1/1     Running   0          55m   app=web,pod-template-hash=55bdf4c5d5

Same label here as well

Labels are dynamic

See the endpoints

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl get endpoints ps-lb 
NAME    ENDPOINTS                                                  AGE
ps-lb   10.1.0.19:8080,10.1.0.20:8080,10.1.0.21:8080 + 2 more...   80m

Describe endpoints

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl describe endpoints ps-lb
Name:         ps-lb
Namespace:    default
Labels:       <none>
Annotations:  endpoints.kubernetes.io/last-change-trigger-time: 2024-06-01T18:01:13Z
Subsets:
  Addresses:          10.1.0.19,10.1.0.20,10.1.0.21,10.1.0.22,10.1.0.23
  NotReadyAddresses:  <none>
  Ports:
    Name     Port  Protocol
    ----     ----  --------
    <unset>  8080  TCP

Events:
  Type     Reason                  Age   From                 Message
  ----     ------                  ----  ----                 -------
  Warning  FailedToUpdateEndpoint  60m   endpoint-controller  Failed to update endpoint default/ps-lb: Operation cannot be fulfilled on endpoints "ps-lb": the object has been modified; please apply your changes to the latest version and try again

The fix

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl apply -f deploy.yml 
deployment.apps/web-deploy unchanged
blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl describe endpoints ps-lb
Name:         ps-lb
Namespace:    default
Labels:       <none>
Annotations:  endpoints.kubernetes.io/last-change-trigger-time: 2024-06-01T18:01:13Z
Subsets:
  Addresses:          10.1.0.19,10.1.0.20,10.1.0.21,10.1.0.22,10.1.0.23
  NotReadyAddresses:  <none>
  Ports:
    Name     Port  Protocol
    ----     ----  --------
    <unset>  8080  TCP

Events:  <none>
  • Any time you create a service, you automagically get an associated endpoints object
  • with a list of pods
  • that match label selector

Get the public IP

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl get services
NAME          TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes    ClusterIP      10.96.0.1        <none>        443/TCP        10d
ps-lb         LoadBalancer   10.99.228.61     localhost     80:31787/TCP   92m
ps-nodeport   NodePort       10.100.201.228   <none>        80:31111/TCP   40h

blazingraptor@galactica:~/docker/getting-started-k8s/Deployments$ kubectl get services | grep ps-lb
ps-lb         LoadBalancer   10.99.228.61     localhost     80:31787/TCP   93m

localhost

Turn off Windows Firewall
I can load
172.16.0.12:31111
from ironman6 in Firefox

That means, I can port forward it to my router and visit it from any computer in the World.


Scale Up

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ grep replicas deploy.yml
  replicas: 5
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ vim deploy.yml
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ grep replicas deploy.yml
  replicas: 7
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl apply -f deploy.yml
deployment.apps/web-deploy configured
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get pods  --show-labels
NAME                          READY   STATUS    RESTARTS   AGE    LABELS
web-deploy-55bdf4c5d5-2vt78   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-6s57x   1/1     Running   0          7s     app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-k92lh   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-n5kgp   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-r7fzx   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-sjvxr   1/1     Running   0          7s     app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-xxgrk   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5

Scale Down

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ grep replicas deploy.yml
  replicas: 7
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ vim deploy.yml
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ grep replicas deploy.yml
  replicas: 4
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl apply -f deploy.yml
deployment.apps/web-deploy configured
blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ kubectl get pods  --show-labels
NAME                          READY   STATUS    RESTARTS   AGE    LABELS
web-deploy-55bdf4c5d5-2vt78   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-k92lh   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-n5kgp   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5
web-deploy-55bdf4c5d5-r7fzx   1/1     Running   0          6d2h   app=web,pod-template-hash=55bdf4c5d5

Retag it

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ docker tag nigelpoulton/getting-started-k8s:1.0 blazingraptor/getting-started-k8s:1.0

See the image

blazingraptor@galactica:~$ docker images | grep blazingraptor | grep -E '(getting-started-k8s)'
blazingraptor/getting-started-k8s           1.0                                                                           dc81d8418e19   21 months ago   263MB

Push the image

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s/Deployments$ docker push blazingraptor/getting-started-k8s:1.0
The push refers to repository [docker.io/blazingraptor/getting-started-k8s]
12ea4114bca0: Mounted from nigelpoulton/getting-started-k8s
5489c358f90b: Mounted from nigelpoulton/getting-started-k8s
70a5915a62dd: Mounted from nigelpoulton/getting-started-k8s
de6bcbc6ed42: Mounted from nigelpoulton/getting-started-k8s
daed7950b4bd: Mounted from nigelpoulton/getting-started-k8s
6f7801354ad0: Mounted from nigelpoulton/getting-started-k8s
ac4d164fef90: Mounted from nigelpoulton/getting-started-k8s
1.0: digest: sha256:259aca2f1c980dd44e77e84ee686c7bff59c030f1d08a0b99609e072cad3c5c0 size: 1787

Prepare to delete the cluster (see it)

blazingraptor@galactica:~/docker/mysql-repl-k8s$ kubectl describe deployment web-deploy
Name:                   web-deploy
Namespace:              default
CreationTimestamp:      Sat, 15 Feb 2025 11:47:59 -0500
Labels:                 app=web
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=web
Replicas:               4 desired | 4 updated | 4 total | 4 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=web
  Containers:
   hello-pod:
    Image:         nigelpoulton/getting-started-k8s:1.0
    Port:          8080/TCP
    Host Port:     0/TCP
    Environment:   <none>
    Mounts:        <none>
  Volumes:         <none>
  Node-Selectors:  <none>
  Tolerations:     <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Progressing    True    NewReplicaSetAvailable
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   web-deploy-55bdf4c5d5 (4/4 replicas created)
Events:          <none>

Delete the cluster

blazingraptor@galactica:~/docker/scale-example/getting-started-k8s$ kubectl delete all -l app=web
pod "web-deploy-55bdf4c5d5-2vt78" deleted
pod "web-deploy-55bdf4c5d5-k92lh" deleted
pod "web-deploy-55bdf4c5d5-n5kgp" deleted
pod "web-deploy-55bdf4c5d5-r7fzx" deleted
deployment.apps "web-deploy" deleted
replicaset.apps "web-deploy-55bdf4c5d5" deleted