Kubernetes Deepdive
Core Concepts
Kubernetes Components
API Server - Frontend to Kubernetes; Users, Mgmt Devices, CLI talk to this. etcd Key Store - Distributed, Reliable Key Values Store used to store cluster data, implements lock to avoid conflicts for multiple Master nodes scenario. Kubelet - Agent that runs on each Node; responsible to make sure containers are running on nodes as expected. Container Runtime - Underlying software used to run containers(eg: Docker, etc) Controller - Brains behind orchestration; Notice & respond when Nodes, Containers, end points goes down; make decisions to bring up new containers Scheduler - Distributes work or containers across multiple Nodes; looks for newly created containers and assigns them to nodes.
- Worker nodes were known as Minions earlier.
- Worker Nodes host the containers
Master Node Worker Nodes [Kube-ApiServer] -------> [Kubelet] [etcd] [controller] [scheduler] [Container Runtime] Docker, Rocket, CRI-O
- Kubectl used to deploy and manage applications on a Cluster to get cluster information.
kubectl run hello-minikube kubectl run nginx --image nginx kubectl cluster-info kubectl get nodes kubectl get pods kubectl describe pod myapp-pod
PODS
Containers are encapsulated into kubenetes objects call Pods.
Pod is a single Instance of Application. Pod is the smallest object that can be created in Kubernetes. If the no of users increases, we do not increase no of app instances(containers) inside pod, we add new Pods. You can deploy new pods in a new node if the capacity reaches for a single pod.
- Multi-Container PODs
Rarely used Helper Containers can run inside your same pod as the main application pod. {[Python] [Helper pod]}
We run 4 application instances:
docker run python-app docker run python-app docker run python-app docker run python-app
Then add 4 helper applications:
docker run helper -link app1 docker run helper -link app2 docker run helper -link app3 docker run helper -link app4
Need to take care of networking and shared volumes Also need to monitor the containers.
With Pods, all of this is done automatically.
YAML in Kubernetes
Used as input for creating objects like Pods, Replicasets, depoyments, services, etc.
Kubernetes definition file always contains 4 required root/top level properties:
apiVersion: --> version of Kubernetes API; eg: v1, apps/v1-beta, extensions/v1-beta, etc => String kind: --> Pod, Service, ReplicaSet, Deployment => String metadata: --> Data about object - Name, Label, etc; Spacing should be like python => Dictionary spec: --> Specify which image to be used, etc. Check documentation of Image => Dictionary
Kind Version POD v1 Service v1 ReplicaSet apps/v1 Deployment apps/v1
- API Version is specific to what we are creating.
- Example Definition file
> nano pod-definition.yaml
apiVersion: v1 kind: Pod metadata: name: my-app-pod labels: ==> Used to identify pods in case you have 100s of them, else it will be difficult to manage them app: myapp type: front-end spec: containers: --> List/Array - name: nginx-container --> '-' Indicates first item in the list. image: nginx
> kubectl create -f pod-definition.yml
ReplicaSets
- Helps run multiple instances of single Pod
- Provides High Availability.
- If only a single instance of POD is running, it will make sure that it continues to run.
- In case the instance fails, it will run another instance.
- Replication Controller vs Replica Set
- Replication Controller is older technology.
- Usage:
apiVersion: v1 kind: ReplicationController metadata: name: my-app-rc labels: app: myapp type: front-end spec: ==> Replication Controller template: ==> Below is data copied from another definition file metadata: name: my-app-pod labels: app: myapp type: front-end spec: ==> POD containers: - name: nginx-container image: nginx replicas: 3
Create Replication Controller:
kubectl create -f rc-definition.yml kubectl get replicationcontroller
- ReplicaSet is newer technology.
- Usage:
apiVersion: apps/v1 kind: ReplicaSet metadata: name: myapp-replicaset labels: app: myapp type: front-end spec: ==> Replication Controller template: ==> Below is data copied from another definition file metadata: name: my-app-pod labels: app: myapp type: front-end spec: ==> POD containers: - name: nginx-container image: nginx replicas: 3 selector: ==> Used because it can also manage pods not created during replication set creation; Mandatory matchLabels: type: front-end
Create ReplicaSet:
kubectl create -f rs-definition.yml kubectl get replicaset
Labels and Selectors
- Scaling
1st Method:
Change replicas: => 6 kubectl replace -f rs-definition.yml
2nd Method:
kubectl scale --replicas=6 -f rs-definition.yml
OR
kubectl scale --replicas=6 replicaset myapp-replicaset (type) (name)
3rd Method:
Scaling based on Load
Deployments
Use Case:
You do not want to update all of your Webservers at once. Rolling Updates - Upgrade webserver one by one. Deployments allow us to upgrade instances seamlessly using rolling updates, undo changes, pause and resume changes as required.
Containers < POD < Replica Set < Deployment
Usage:
apiVersion: apps/v1 kind: Deployment metadata: name: myapp-replicaset labels: app: myapp type: front-end spec: template: metadata: name: my-app-pod labels: app: myapp type: front-end spec: containers: - name: nginx-container image: nginx replicas: 3 selector: matchLabels: type: front-end
kubectl create -f deployment-definition.yml kubectl get deployments kubectl get relicaset kubectl get pods
Check all objects:
kubectl get all
NameSpaces
[Mark Smith] [Mark Williams]
Kubernetes creates a set of PODs and services for its internal purposes - required by Networking, DNS,etc. To isolate these from users & prevent accidental deletions or modification, kubernetes creates them under different Namespaces. These are created at cluster startup under namespace: kube-system kube-public is also created where resources available to all users are placed. For small environments you can continue using default namespace.
NameSpace Isolation:
Use the same cluster for Dev & Production. Isolate them. Also you can apply Quota of resources to them.
Within NameSpace a POD can reach another service by using its hostname:
mysql.connect("db-service")
For connecting into another NameSpace called 'dev':
mysql.connect("db-service.dev.svc.cluster.local") db-service => service-name dev => namespace svc => service cluster.local => domain
This command list pods from default name space only:
kubectl get pods
Specify namespace to see its pods:
kubectl get pods --namespace=kube-system
POD is created in default name space:
kubectl create -f pod-definition.yml
To create it in another namespace:
kubectl create -f pod-definition.yml --namespace=dev
Otherwise specify it in the Definition files:
apiVersion: v1 kind: Pod metadata: name: my-app-pod namespace: dev labels: app: myapp type: front-end spec: containers: - name: nginx-container image: nginx
- Create Namespace
1st Method:
apiVersion: v1 kind: Namespace metadata: name: dev
kubectl create -f namespace-dev.yml
2nd Method:
kubectl create namespace dev
- Permanently change Namespace
kubectl config set-context $(kubectl config current-context) --namespace=dev kubectl get pods ==> This will show pods under 'dev' kubectl get pods --namespace=default ==> This will show pods under 'default'
To see PODs in all namespaces:
kubectl get pods --all-namespaces
- Create Quota
apiVersion: v1 kind: ResourceQuota metadata: name: compute-qouta namespace: dev spec: hard: pods: "10" requests.cpu: "4" requests.memory: 5Gi limits.cpu: "10" limits.memory: 10Gi
kubectl create -f compute-qouta.yml
Configuration
Commands and Arguments in Docker
Containers are not meant to run OS Thats is why if you run below command it will fail to start container:
docker run ubuntu
Docker files of popular containers has:
Nginx => CMD ["nginx"] MySql => CMD ["mysqld"]
Ubuntu => CMD ["bash"]
Run Commands in Ubuntu bash:
docker run ubuntu [command]
E.g:
docker run ubuntu sleep 5
Making this change permanent:
FROM Ubuntu CMD sleep 5
Or:
FROM Ubuntu CMD ["sleep", "5"]
Create a custom image from above:
docker build -t ubuntu-sleeper . docker run ubuntu-sleeper
Changing the hard-coded values:
docker run ubuntu-sleeper sleep 10
Using Default values and applying custom value too:
FROM Ubuntu ENTRYPOINT ["sleep"] CMD ["5"]
docker run ubuntu-sleeper ==> value = 5 (default) docker run ubuntu-sleeper 20
For changing entrypoint on the fly:
docker run --entrypoint sleep2.0 ubuntu-sleeper 10 ==> Command run at startup = sleep2.0 10
Commands and Arguments in Kubernetes
Docker command:
docker run --name ubuntu-sleeper --entry-point sleep2.0 ubuntu-sleeper 10
Docker file:
FROM Ubuntu ENTRYPOINT ["sleep"] CMD ["5"]
Definition File:
apiVersion: v1 kind: Pod metadata: name: ubuntu-sleeper-pod spec: containers: - name: ubuntu-sleeper image: ubuntu-sleeper command: ["sleep2.0"] args: ["10"]
Environment Variables
Docker Command:
docker run -e APP_COLOR=pink simple-webapp-color
Definition file:
apiVersion: v1 kind: Pod metadata: name: simple-webapp-color spec: containers: - name: simple-webapp-color image: simple-webapp-color ports: - containerPort: 8080 env: - name: APP_COLOR value: pink
- Plain Key Value:
env: - name: APP_COLOR value: pink
- ConfigMap:
env: - name: APP_COLOR valueFrom: configMapKeyRef:
- Secrets:
env: - name: APP_COLOR valueFrom: secretKeyRef:
Config Maps
- Two methods to create a definition file
- Create using Commands line:
kubectl create configmap
- Definition file:
apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_COLOR: blue APP_MODE: prod
Create the config map:
kubectl create -f config-map.yml
- Using Config Maps for a Pod:
apiVersion: v1 kind: Pod metadata: name: simple-webapp-color spec: containers: - name: simple-webapp-color image: simple-webapp-color ports: - containerPort: 8080 envFrom: - configMapRef: name: app-config
kubectl create -f pod-definition.yml
- Check Config Maps:
kubectl get configmaps kubectl describe configmaps
Secrets
- Used to store passwords
- 2 ways to create them
Using CLI:
kubectl create secret generic\ app-secret --from-literal=DB_Host=mysql \ --from-literal=DB_user=root \ --from-literal=DB_Password=passwd
Using Definition file:
apiVersion: v1 kind: Secret metadata: name: app-secret data: DB_Host=mysql DB_user=root DB_Password=passwd
- Convert the secrets into encoded format:
$ echo -n 'mysql' | base64 bXlzcWw= $ echo -n 'root' | base64 cm9vdA== $ echo -n 'passwd' | base64 cGFzc3dk
- Now use these values in the Definition file:
apiVersion: v1 kind: Secret metadata: name: app-secret data: DB_Host=bXlzcWw= DB_user=cm9vdA== DB_Password=cGFzc3dk
- Using the secret in a Pod:
apiVersion: v1 kind: Pod metadata: name: simple-webapp-color labels: name: simple-webapp-color spec: containers: - name: simple-webapp-color image: simple-webapp-color ports: - containerPort: 8080 envFrom: - secretRef: name: app-secret
Create the Pod:
kubectl create -f pod-definition.yml
- Check Secrets:
kubectl get secrets kubectl describe secrets kubectl get secret app-secret -o yaml ==> This will show the hash values
- Decode the hash values from above command:
$ echo -n 'bXlzcWw=' | base64 --decode mysql
- Mount secrets as a Volume:
If you mount the secret as a volume, each attribute in secret is created as file with value of secret as its content.
volumes: - name: app-secret-volume secret: secretName: app-secret
Check Volumes:
ls /opt/app-secret-volume cat /opt/app-secret-volume/DB_Password
Docker Security
- Docker runs a process as user 'root' with PID = 1.
- This process runs on the host in different namespace.
- It can be seen on host with different PID.
- But this root account is not having all privileges as a normal process with root privileges.
docker run --user=1001 ubuntu sleep 3600 ==> run as a non root user docker run --cap-add MAC_ADMIN ubuntu sleep 3600 ==> enable custom privileges docker run --privileged ubuntu sleep 3600 ==> run with full fledged root privileges
- In Kubernetes you may choose to configure security at container level or Pod level.
- Security applied at container level overrides security applied at Pod level.
- Pod Level Security:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: securityContext: runAsUser: 1000 containers: - name: ubuntu image: ubuntu command: ["sleep","10"]
- Container Level Security:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: ubuntu image: ubuntu command: ["sleep","10"] securityContext: runAsUser: 1000 capabilities: add: ["MAC_ADMIN"]
Service Account
- There are two types of accounts in kubernetes:
User Account - Used by Humans; eg - to deploy admin task or to deploy service Service Account - Used by Machines; eg - account used by service to interact with Kubernetes cluster; like Prometheus to monitor Cluster; Jenkins to deploy services.
- Create service account:
kubectl create serviceaccount testacnt kubectl get serviceaccount kubectl describe serviceaccount testacnt => Shows Token object kubectl describe secret testacnt-token-kddbm => Shows Token
curl https://10.12.33.1:6443/api -insecure --header "Authorization: Bearer eyTdgf....."
- Steps to use Service Accounts in Application:
Create the Service Account Assign right permissions using RBAC - Role Based Access Control. Export tokens and use it in the application.
- For every namespace, there is a service account 'default' created by itself.
- In case the application is hosted inside a Pod in cluster itself, we can simplify the authentication mechanism:
Mount the Secret as a volume inside the Pod running Application.
- By default every Pod is have a secret - 'default' mounted in it:
kubectl describe pod my-pod
Mounts: /var/run/secrets/kubernetes.io/serviceaccount
kubectl exec -it my-pod ls /var/run/secrets/kubernetes.io/serviceaccount => ca.crt namespace token
- To view the Token:
kubectl exec -it my-pod cat /var/run/secrets/kubernetes.io/serviceaccount/token eyTdgf.....
- 'default' service account is very much restrictive; can be used to run very basic kubernetes APIs.
- Use a custom Service Account for a Pod:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: ubuntu image: ubuntu serviceAccount: testacnt
- You cannot change the Service Account for a POD. You must delete and recreate a POD.
- However you can change the Service Account for a deployment. as any change to the POD deployment file will automatically trigger out a new rollout for the deployment.
Resource Requirements
- Kubernetes Scheduler decides which node a Pod goes to.
- Schedular takes into account the amount of resources required and amount of resources required.
- If not enough resources are available, the Pod will remain in 'Pending' stuck state.
- By default Kubernetes assumes that a Pod or a Container within Pod requires: 0.5 CPU; 256MB RAM
- CPU => 0.1 == 100m (mili)
Minimum value = 1m
1 CPU => 1 AWS vCPU or 1 GCP Core or 1 Azure Core 1 Hyperthread
- Memory:
256Mi == 268435456 1G 1Gi
- Docker do not have any limit on the amount of resources it can consume, suffocating other nodes.
- By default there is a limit of 1 vCPU & 512Mi RAM to a container.
- Define Resource Request:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: ubuntu image: ubuntu resources: requests: memory: "1Gi" cpu: 1 limits: memory: "2Gi" cpu: 2
Taints and Tolerations
- Define what pods are placed on what nodes.
- If Taint applied to a node, no Pod can be placed on it except for ones where you apply Toleration.
- Apply Taint:
kubectl taint nodes node-name key=value:taint-effect kubectl taint nodes node1 app=blue:NoSchedule
taint-effect = NoSchedule => Do not place new Pods PreferNoSchedule => Try not to place new pods, no guarantees NoExecute => Evict any Pods that do not tolerate the taint
- Tolerations are added to Pods:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: ubuntu image: ubuntu tolerations: - keys: "app" operator: "Equal" value: "blue" effect: "NoSchedule"
- Master Node is by dafault run with Taint: NoSchedule
kubectl describe node kubemaster | grep Taint => Taints: node-role:kubernetes.io/master:NoSchedule
Node Selectors
- Used to run Heavy Applications on Bigger Nodes.
- Define a Pod to be assigned to a Large Node:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: ubuntu image: ubuntu nodeSelector: size: Large
- Label Nodes:
kubectl label node <node-name> <label-key>=<label-value> kubectl label node node1 size=Large
Node Affinity
Taints & Tolerations vs Node Affinity
Multi-Container PODs
- Used when you need 2 services: WebServer + Log Agent
- Both Apps have same life cycle - created together, destroyed together, Share Network space & have access to same Storage volumes
- Creating Mulit-Container Pods:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: web-app image: web-app - name: log-agent image: log-agent
- Design Patterns:
Sidecar Adapter Ambassador
Observability
- Pod Status
Pending - run 'kubectl describe pod' to find why it is in pending state. ContainerCreating Running
- Pod Conditions
PodScheduled Initialized ContainersReady Ready
To check the conditions:
kubectl describe pod kubectl get pods ==> READY -> Means -> ContainersReady & Ready = True
Readiness and Liveness Probes
- Some pods (like jenkins) take 15 seconds to become ready.
- But the Describe command shows it in Ready state during initialization too.
- This is not correct.
- Specify below readiness probe, kubernetes will not immediately set Ready state.
- HTTP Test - /api/ready:
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: web-app image: web-app ports: - containerPort: 8080 readynessProbe: httpGet: path: /api/ready port: 8080
- TCP Test - 3306
readynessProbe: tcpSocket: port: 3306
- Exec Command
readynessProbe: exec: command: - cat - /app/is_ready
Liveness Probes
- If web server crashes, Nginx process exists & Container exits as well.
docker ps -a container id 4ygre874hufew Exited(1) 41 seconds ago
- Since Dockers is not an orchestration engine, Container continues to stay dead & denies services to users until you manually create a new container.
- If Kubernetes Orchestration is used, every time the application crashes, Kubernetes makes an attempt to restart the container to restore service to the users.
with every crash, "Restart" count increases.
- Problem occurs if the application is not working but the container continues to stay alive.
e.g: Due to a bug, application is stuck in an infinite loop. For Kubernetes, container is up, so the application is assumed to be up, but the users are not served with application. Now container needs to be restarted or destroyed and a new container needs to be brought up.
- Liveness probe tests if the application within the container is healthy or not.
- Parameters to be tested:
Http Application - /api/healthy TCP Test - port 3306 Exec Command
- HTTP Test - /api/healthy:
nano pod-definitaion.yaml
apiVersion: v1 kind: Pod metadata: name: web-pod spec: containers: - name: web-app image: web-app ports: - containerPort: 8080 livenessProbe: httpGet: path: /api/healthy port: 8080 initialDelaySeconds: 10 periodSeconds: 5 failureThreshod: 8
TCP Test - 3306
livenessProbe: tcpSocket: port: 3306
Exec Command
livenessProbe: exec: command: - cat - /app/is_ready
Container Logging
- If you run an application which generates events & send them to standard output:
docker run kodecloud/event-simulator
- If you run the container in background in detached mode, tose logs are not visible:
docker run -d kodecloud/event-simulator
So you need to run below command to see logs:
docker logs <container-id> docker logs -f <container-id> shows Live logs
- In Kubernetes, we can see the logs:
kubectl logs -f event-simulator-pod shows Live logs
- If there are multiple containers in Pod, then you need to specify the name of the container you want to see the logs:
kubectl logs -f event-simulator-pod event-simulator
Monitor and Debug Applications
- Resource consumption monitoring
No of nodes in cluster How many are healthy Cpu,memory & network & Disk utilization No of pod Performance metrics of each pod - cpu, memory usage
- Kubernetes does not come with full monitoring tool.
- There are opensource tools to monitor these resources - Metrics Server, Prometheus, Elastic Stack
- Proprietary tools: - DataDog, Dynatrace
- Metrics Server:
Heapster was original monitoring tool for Kubernetes, which is not deprecated. Its slim-down version is called Metrics Server. We can have 1 metrics server per Kubernetes Cluster. It retrieves metrics from each Kubernetes Nodes & Pods, aggregates them & stores them in memory. We cannot get historical data from it, need to install one of the other tool for this.
- Kubelet is an agent which runs on each node, which takes instructions from kubernetes API master server & runs pods on nodes.
- Kubelet also contains a sub-component called cAdvisor or Container Advisor.
- cAdvisor is responsible for retreiving performance metrics from pods & exposing them through Kubelet API to make metrics available for the metrics server.
- If you use minikube for local cluster:
minikube addons enable metrics-server
- For other environments:
git clone https://github.com/kubernetes-incubator/metrics-serve kubectl create -f deploy/1.8+/
- Usage:
kubectl top node kubectl top pod
POD Design
Labels, Selectors and Annotations
- Labels & Selectors are used to group similar objects based on their class or kind.
- Can filter based on multiple items
- Labels are properties attached to each item - Class, Kind, Color, etc
- Selectors help to filter these items. e.g: Class=Mammal; Color=Green
- Kubernetes has objects like: Pods, Services, Replica Sets, Deployments,etc.
- Can group objects by their Types(Pods, services,etc), Application(App1, App2,etc), Functionality(Front-End, DB, Auth,etc)
- Apply labels & Using Selectors:
app = App1 function = Front-End
app = App2 function = Auth
app = App3 function = DB
Filter:
app=App1
- Applying Labels:
nano pod-definition.yaml
apiVersion: v1 kind: Pod metadata: name: simple-webapp labels: app: App1 function: Front-end spec: containers: - name: simple-webapp image: simple-webapp ports: - containerPort: 8080
- Using Selectors:
kubectl get pods --selector app=App1
- Applying Labels to ReplicaSets:
nano replicaset-definition.yaml
apiVersion: apps/v1 kind: ReplicaSet metadata: name: simple-webapp labels: ==> These are labels for Replica Sets app: App1 function: Front-end spec: replicas: 3 selector: ==> Selectors matchLabels: app: App1 template: ==> These are the labels for Pods metadata: labels: app:App1 function: Front-end spec: containers: - name: simple-webapp image: simple-webapp ports: - containerPort: 8080
Annotations
- Used to record tool details like Build, Version, name, contact details, etc
apiVersion: apps/v1 kind: ReplicaSet metadata: name: simple-webapp labels: ==> These are labels for Replica Sets app: App1 function: Front-end annotations: buildversions: 1.34
Rolling Updates & Rollbacks in Deployments
- New Rollout creates new revisions - Revision 1, Revision 2, etc
kubectl rollout status deployment/myapp-deployment Waiting for Rollout to finish: 0 of 10 updated replicas are available... Waiting for Rollout to finish: 1 of 10 updated replicas are available... Waiting for Rollout to finish: 2 of 10 updated replicas are available... ........................................................................ Waiting for Rollout to finish: 9 of 10 updated replicas are available... deployment "myapp-deployment" successfully rolled out.
kubectl rollout history deployment/myapp-deployment
- Deployment Strategy
Recreate Startegy - Delete all old version pods & create new ones - Event - Scaled down replica set from 5 to 0. Rollout Update - Default - Updates one by one. - scaled up/down 5,4,3,2,1
- Upgrades:
Creates a new replica set. Deploys new Pods in new Replica set Deletes old Pods one by one from old Replica set
- Rollback:
kubectl rollout undo deployment/myapp-deployment
Commands Summary
- Create Deployment by Kubectl run
kubectl run nginx --image=nginx
Create:
kubectl create -f deployment-definition.yml
Get:
kubectl get deployments
Update:
kubectl apply -f deployment-definition.yml kubectl set image deployment/myapp-deployment nginx=nginx:1.9.1
Status:
kubectl rollout status deployment/myapp-deployment kubectl rollout history deployment/myapp-deployment
Rollback
kubectl rollout undo deployment/myapp-deployment
Jobs
Normal Jobs run long time:
App Web DB
Below processes carry out specific task & then finish:
Batch Analytics Reporting
E.g of set of workloads meant for a short period of time:
Performing a computation. Image processing Performing analytics on a large data set Generating report and sending email
In Docker:
docker run ubuntu expr 3 + 2
After finishing this task:
docker ps -a STATUS: Exited (0) 41 seconds ago (0 = Task completed successfully)
For Kubernetes:
apiVersion: v1 kind: Pod metadata: name: math-pod spec: containers: - name: math-add image: ubuntu command: ['expr', '3', '+', '2'] restartPolicy: Never
kubectl create -f pod-definition.yml
kubectl get pods STATUS: complete RESTARTS: 1,2,3,4...... (continues to running because restartPolicy is set to Always by default)
- ReplicaSet - Helps make sure that specified no of Pods are running all the time.
- Jobs - Used to run a set of pods to perform a given task to completion.
nano job-definition.yaml
apiVersion: batch/v1 kind: Job metadata: name: math-add-job spec: template: spec: containers: - name: math-add image: ubuntu command: ['expr', '3', '+', '2'] restartPolicy: Never
Create Job:
kubectl create -f job0definition.yaml
Check Jobs:
kube get jobs
Check Pods:
kubectl get pods READY: 0/1 STATUS: Completed RESTARTS: 0 AGE: 2m
Check Output/logs:
kubectl logs math-add-job-ld87pn 5
Delete Job:
kubectl delete job math-add-job job.batch "math-add-job" deleted
Multiple Pods
nano job-definition.yaml
apiVersion: batch/v1 kind: Job metadata: name: random-error-job spec: completions: 3 ==> Pods run sequentially, one after another's completion parallelism: 3 ==> Creates all 3 pods in parallel template: spec: containers: - name: random-error image: kodekloud/random-error restartPolicy: Never
CronJobs
- CronJobs can be scheduled and run periodically.
nano cron-job-definition.yaml
apiVersion: batch/v1beta1 kind: CronJob metadata: name: reporting-cron-job spec: ==> for Cronjob schedule: "*/1 * * * *" jobTemplate: spec: ==> for Job completions: 3 parallelism: 3 template: spec: ==> for Pod containers: - name: reproting-tool image: reporting-tool restartPolicy: Never
kubectl create -f cron-job-definition.yaml kubectl get cronjob
Services & Networking
Services
- Enable communication withing and outside of Application.
- Help to connect application together with other applications or users
- They enable connectivity between Client & Frontend, Frontend with Backend, Backend with External Datasources.
- NodePort Service: Listens to a port on the node & forwards the requests to the pods.
- ClusterIP: Service creates a virtual IP inside the cluster to enable communication between different services such as set of frontend sevrers to a set of backend servers.
- LoadBalancer: Distributes load across nodes.
NodePort
[PC]-----------------{NodePort-30008}[Kube Node]{Port-80}--------------------{TargetPort-80}[POD] 192.168.1.10 192.168.1.2 10.106.1.12 10.244.0.1
NodePort Service[30008] curl http://192.168.1.2:30008 Node Port Range - 30000 - 32767
- Labels & Selectors used to connect TargetPort to Pod.
- Create a NodePort Service:
nano service-definition.yml
apiVersion:v1 kind: Service metadata: name: myapp-service spec: type: NodePort ports: - targetPort: 80 ==> If not provided, assumed to be same as targetPort port: 80 ==> Mandatory Field nodePort: 30008 ==> If not provided, automatically allocated from range(30000-32767) selector: app: myapp type: front-end
kubectl create -f service-definition.yml kubectl get services
Check service:
curl http://192.168.1.2:30008
- For Mapping multiple Pods, automatically mapping is created for all matching Pods with correct labels.
- In case pods are on multiple Nodes:
curl http://192.168.1.2:30008 curl http://192.168.1.3:30008 curl http://192.168.1.4:30008
Cluster IP
- Full Stack Web Application:
Front-end [POD-10.244.0.3] [POD-10.244.0.4] [POD-10.244.0.5] \ | / {back-end} Back-end [POD-10.244.0.6] [POD-10.244.0.7] [POD-10.244.0.8] \ | / {redis} Redis [POD-10.244.0.9] [POD-10.244.0.10] [POD-10.244.0.11]
- The IPs of PODs are not static, they may change at any time.
- Therefore Networking cannot be done on 1-1 IP/port basis.
- Also it is difficult to decide which front end POD connect to which back end POD.
- ClusterIP Service can be used to map each layer to an IP address/Port.
- Creating ClusterIP:
nano service-definition.yml
apiVersion: v1 kind: Service metadata: name: back-end spec: type: CLusterIP ports: - targetPort: 80 port: 80 selector: app: myapp type: back-end
kubectl create -f service-definition.yml kubectl get services
- Service can be accessed by the ClusterIP or the Service Name.
Ingress Networking
- Normal Access:
[MySQL-POD]---------------------[WebApp-POD]--------------------------[Client] [service-ClusterIP] [service-NodePort-38080]
Client access: http://192.168.1.10:38080 DNS Server : http://my-online-store.com:38080
- Replacing the Port number with standard port:
[MySQL-POD]---------------------[WebApp-POD]------------------------[Proxy-Server]--------------[Client] [service-ClusterIP] [service-NodePort-38080] [80]
Client access: http://my-online-store.com
- In case hosting is done on Public Cloud, create a service of type LoadBalance, then Cloud (e.g: GCP) will automatically create a GCP-Loadbalancer:
[MySQL-POD]---------------------[WebApp-POD]------------------------[GCP-LoadBalancer]--------------[Client] [service-ClusterIP] [service-NodePort-38080] [80]
Client access: http://my-online-store.com
- For multiple services you need multiple GCP-LoadBalncers, Need to pay more & re-configure for every additional service.
- Also you need to configure SSL for the applications, which becomes difficult to manage in case you have multiple applications, etc
- Also need to configure firewall rules for each application too.
- Ingress helps users access application using a single externally accessible URL.
- It can be configured to route different services and can also provide SSL configuration point.
- It can be thought of as a L7 LoadBalancer
- Still it needs to be published as a NodePort or cloud native LoadBalancer, but that is just a one time configuration.
- Components:
Ingress Controller - Nginx(Supported), HAProxy, Traefik, GCP HTTPS LoadBalancer(GCE)(Supported), Contour, Istio Ingress Resources: Rules
- Configuring Nginx Controller
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-ingress-controller spec: replicas: 1 selector: matchLabels: name: nginx-ingress template: metadata: labels: name: nginx-ingress spec: containers: - name: nginx-ingress-controller image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0 args: - /nginx-ingress-controllee - --configmap=$(POD_NAMESPACE)/nginx-configuration ==> This is below created ConfigMap env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - name: http containerPort: 80 - name: https containerPort: 443
- In order to decouple Nginx Config data like: error-log-path, keep-alive, ssl-protocols, Config-Map object is created and passed.
- This blank will also do, because you dont need to change the nginx config files later on.
- Config Map:
kind: ConfigMap apiVersion: v1 metadata: name: nginx-configuration
- Service to expose the ingress controller to the external world:
apiVersion: v1 kind: Service metadata: name: nginx-ingress spec: type: NodePort ports: - port: 80 targetPort: 80 protocol: TCP name: http - port: 443 targetPort: 443 protocol: TCP name: https selector: name: nginx-ingress
- Ingress Controllers monitor the kubernetes cluster for ingress resources and configure the underlying nginx server when something is changed.
- For this, a service account is needed which has - correct Roles, ClusterRoles & RoleBindings:
apiVersion: v1 kind: ServiceAccount metadata: name: nginx-ingress-serviceaccount
- Therefore below objects are needed for a simple Ingress Controller:
Deployment Service ConfigMap Auth
- Ingress Resources:
www.mysite.com/wear-------------->[wear-POD] www.mysite.com/video------------->[video-POD]
wear.mysite.com------------------>[wear-POD] video.mysite.com----------------->[video-POD]
nano ingress-wear.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-wear spec: backend: serviceName: wear-service servicePort: 80
kubectl create -f ingress-wear.yaml kubectl get ingress
- Using Paths:
nano ingress-wear-watch.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-wear-watch spec: rules: - http: paths: - path: /wear backend: serviceName: wear-service servicePort: 80 - path: /watch backend: serviceName: watch-service servicePort: 80
Check the Backends:
kubectl describe ingress ingress-wear-watch
- Using Host names:
nano ingress-wear-watch.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress-wear-watch spec: rules: - host: wear.mysite.com http: paths: - backend: serviceName: wear-service servicePort: 80 - host: video.mysite.com http: paths: - backend: serviceName: watch-service servicePort: 80
Network Policies
- Kubernetes is by default configured with "All Allow" rule.
- If you do not want Frontend POD to communicate directly with DB POD, need to create a Network Policy.
- Solutions that support Network Policy:
Kube-router Calico Romana Weave-net
- Solutions that DO NOT support Network Policy(can create policy, will will not have any impact):
Flannel
- Applying a Network Policy:
nano policy-definition.yaml
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: db-policy spec: podSelector: matchLabels: role: db policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: name: api-pod ports: - protocol: TCP port: 3306
kubectl create -f policy-definition.yaml
State Persistence
Volumes
- Docker container(& Kubernetes PODs) & its volumes are generally temporarily required.
- Container & volume is destroyed after termination.
- To persist Data, we attach a volume to the containers when they are created.
apiVersion:v1 kind: Pod metadata: name: random-number-generator spec: containers: - image: alpine name: alpine command: ["/bin/sh","-c"] args: ["shuf -i 0-100 -n 1 >> /opt/number.out;"] volumeMounts: - mountPath: /opt name: data-volume volumes: - name: data-volume hostpath: path: /data ==> mounting host path directly is fine for single POD, but not for multiple PODs type: Directory
- For multiple PODs use storage solutions - NFS, GlusterFS, Flocker, ceph, Scaleio, AWS Elastic Block Storage, AZURE, GCP storage
- For AWS EBS:
volumes: - name: data-volume awsElasticBlockStore: volumeID: <volume-id> fstype: ext4
Persistent Volumes
- With large environment, there is a need to configure a large number of volumes for each pod.
- For every change, they all need to be changed.
- Administrator can create a large pool of storage from where small volumes can be allocated to PODs.
- Persistent Volumes can help here, it is a cluster wide pool of storage volumes.
nano pv-definition.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: pv-vol1 spec: accessModes: - ReadWriteOnce capacity: storage: 1Gi hostPath: path: /tmp/data
kubectl create -f pv-definition.yaml kubectl get persistentvolume
- Access Modes:
ReadOnlyMany ReadWriteOnce ReadWriteMany
Persistent Volume Claims
- PVC is used to make the volume available to the Nodes.
- PV & PVC are two separate objects in Kubernetes name space.
- Administrator creates PV & users creates PVCs.
- Create a PVC:
nano pvc-definition.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: myclaim spec: accessModes: - ReadWriteOnce resources: requests: storage: 500Mi
kubectl create -f pvc-definition.yaml
kubectl get persistentvolumeclaim STATUS: Bound
Delete PVC:
kubectl delete persistenvolumeclaim myclaim
persistentVolumeReclaimPolicy: Retain ==> other options are Delete, Recycle
Kubernetes Challenge Series
Challenge - 1 - Wordpress MySQL
- References
{{#widget:DISQUS
|id=networkm
|uniqid=Kubernetes Deepdive
|url=https://aman.awiki.org/wiki/Kubernetes_Deepdive
}}