Kubernetes
Greek means pilot of the ship. Successor to Borg used inside Google.
Basics
Nodes (prev. Minions) - A machine: Physical or virtual. - Which has K8 installed. - Where containers will be launched by K8s.
Clusters - Set of nodes grouped together. - Disaster Recovery. - Resilience. - Load sharing.
[!IMPORTANT] Cluster also has a special node called Master that controls all other nodes.
Components
K8 installs following things with it:
API Server
- kubectl is all we need. For cluster admin tasks, kubeadm utility.
- Acts as frontend for K8.
- CLI, UI.
- Interaction.
etcd key-store - stores all the data used to manage the cluster. - key-value; distributed manner. - ensures there are no conflicts b/w the master.
Scheduler - responsible for distributing load across multiple clusters.
Controller - DR head. - Decides what and how when cluster goes down.
Container Runtime - Used for running container. - Our case -> Docker
kubelet - runs on each node in the cluster. - makes sure containers are running on nodes as expected.
[!NOTE] Namespaces: Think of them as folders that holds a set of objects. (
--namespace)
Master vs Worker Nodes
Master has: - kube-apiserver - controller - etcd pair - scheduler
Worker has: - kubelet
[!IMPORTANT] kubectl is an important command for running k8. -
kubectl run hello-minikube-kubectl cluster-info-kubectl get nodes-kubectl logs pod_name -c init-containerget the name of the init-container by doingdescribe
Pods
Pods provide access to same storage and networking for the underlying containers. Instead of linking manually and maintaining the connection tables, all the containers inside a pod share same underlying networking.
- Minimal entity that can be managed by kubernetes.
- Typically, Pods are started only through a Deployment because "naked" pods are not rescheduled in case of a failure.
- Each Pod is assigned a unique IP address. Every container in the pod shares the same namespaces, IP and network ports and communicate with each other through
localhost(or the configurednameof the pod.
Single-Container Pods - Containers are encapsulated inside POD. - Single instance of an application. - One-to-one relationship. - Only has one container of same type. - If load increases, more pods are spun-up.
Multi-Container Pods (less used but still beneficial)
- More than one containers inside a pod but of different types.
- Like a python and .net (helper) container.
- Eg. Container 1 is a web server and container 2 is a Git sync containers sharing the same pod.
[!TIP] In general, the right question to ask yourself when designing Pods is “Will these containers work correctly if they land on different machines?” If the answer is no, a Pod is the correct grouping for the containers. If the answer is yes, using multiple Pods is probably the correct solution.
Commands
- Run a pod using:
kubectl run mynginx --image=nginx. - Get pods:
kubectl get pods - Describe pods:
kubectl describe pods - To get the yaml:
kubectl get pods mynginx -o yaml - Label a pod:
kubectl label pods name color=red
Setup
minikube
- MiniKube is scaled down but fully equivalent of K8 for dev purpose.
- First, install
kubectland thenminikube. - Check status of
minikubebyminikube status. - Start
minikubeby:minikube startand then check forkubectl cluster-infoandminikube status.
Commands
minikube dashboard: opens k8 dashboard in browserminikube delete: delets a clusterminikube ip: shows the currently used IP addressminikube start: startminikube stop: stopminikube status: statusminikube version: shows versionminikube service nameorminikube service name --url: open the service in browserminikube pause: pause k8 without impacting deployed appsminikube unpauseminikube delete --all: delete all minikubes clusters
[!NOTE] To log into minikube host, do
minikube ssh.
kubectl
To get the nodes running
- kubectl get nodes
[!TIP] To get the shortnames for the commands, run
kubectl api-resources.
Example 1 (running nginx server)
1) kubectl run mynginx --image=nginx
- --image should be a valid docker image
- This creates a pod for the container.
2) kubectl get pods
- Check the running pods.
- The READY column shows the no. of containers in the ready state.
- kubectl get pods -o wide: info about the node also
3) kubectl describe pod nginx
- More info on the pod
YAML
- Used by k8 for initializing various objects like pods, replicas, services, etc.
- A k8 configuration always consists of atleast below 4 top-level fields:
apiVersionkind: the type of object we are creatingmetadata: dictionary. about object. cannot add props not supported by k8.spec: about images and containercontainer: under this following can be defined:nameimagecommandargsenv
[!TIP] Know about apis using
kubectl explain pods/kubectl explain deploy/kubectl explain pods.spec | lessorkubectl explain --recursive pods.spec[better layout]
-
To get the YAML for existing service:
kubectl get pods <pod-name> -o yaml
-
Try to generate YAML files rather than creating them.
kubectl run mynginx --image=nginx --dry-run=client -o yaml > mynginx.yaml
Commands
kubectl create -f my.yamlkubectl apply -f my.yaml: create if doesn't exist and modify if exists.kubectl replace -f my.yaml: replace with new configurationkubectl delete -f my.yaml: remove everything specified in the file
[!TIP] To get into a running container using k8, we can do same things as docker:
kubectl exec -it <pod-name> -- /bin/bash.
example.yml
apiVersion: v1
kind: Pod
metadata:
name: myapp-prod
labels:
app: my-app
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
Then, start using kubectl create -f example.yml or kubectl apply -f example.yml and verify using kubectl describe pod nginx.
[!TIP] We can also generate a pre-built
ymlusing:kubectl run redis --image=redis --dry-run=client -o yaml > redis.yaml
Example 2 (ssh into Ubuntu image)
1) First, generate a yaml for ubuntu image using:
- kubectl run myubuntu --image=ubuntu --dry-run=client -o yaml > myubuntu.yaml
2) Now, because ubuntu is just a base image, if we just create the pod using the above, it will fall in a start-loop. So, we edit the myubuntu.yaml:
apiVersion: v1
kind: Pod
metadata:
labels:
run: ubuntu
name: ubuntu
spec:
containers:
- image: ubuntu
name: ubuntu
command: ["sleep"]
args: ["infinity"]
// and the default ones
3) Create Pod using: kubectl create -f myubuntu.yaml.
4) SSH into the image using: kubectl exec -it myubuntu -- /bin/bash.
Troubleshooting the errors
- Start by checking
kubectl get pods. - If some error, do
kubectl describe pod <name>. - If non-zero exit code, do
kubectl logs <name>.
[!NOTE] There's no way to stop the pod in k8. Stopping is just like terminating so terminate using
kubectl delete <pod>.
Health Checks
- When you we run our application as a container in Kubernetes, it is automatically kept alive for us using a process health check. This health check simply ensures that the main process of our application is always running. If it isn’t, Kubernetes restarts it.
- But there are application specific health checks to verify the functioning of the application.
- We define them in the Pod manifest.
apiVersion: v1
kind: Pod
metadata:
name: kuard
spec:
containers:
- image: gcr.io/kuar-demo/kuard-amd64:blue
name: kuard
livenessProbe: # health check
httpGet:
path: /healthy
port: 8080
initialDelaySeconds: 5 # not called for initial 5 seconds
timeoutSeconds: 1 # probe must respond within 1 sec timeout
periodSeconds: 10 # call the probe every 10 sec
failureThreshold: 3 # after 3 failures, restarts Pod
ports:
- containerPort: 8080
name: http
protocol: TCP
Port Forwarding
- The internal private IP of the pod can be exposed using a port mapping by doing:
kubectl port-forward nginx 8080:80 &(& makes this a background process).
[!NOTE] Check out for the advanced concept Security Context once familiar with basics.
Managing Jobs
- Pods are normally created to run forever.
- But sometimes we need a pod to terminate after the job completion. For this, we use Jobs.
- Jobs are useful for one-shot tasks like backup, calculation etc.
- Use
spec.ttlSecondsAfterFinishedto clean up completed Jobs automatically.
There are 3 types of Jobs specified by completions and parallelism:
1) Non-parallel Jobs: Default one. One pod is started, unless the pod fails. - completions: 1 - parallelism: 1
2) Parallel Jobs with fixed completion count: Job is complete after successfully running as many times as specified in jobs.spec.completions: - completion: n - parallelism: m
3) Parallel Jobs with a work queue: Multiple jobs are created, when one completes successfully, the Job is complete: - completion: 1 - parallelism: n
Example
1) Do this: kubectl create job myjob --image=busybox --dry-run=client -o yaml -- sleep 5 > myjob.yaml.
2) Then edit the yaml file:
C: 1 & P: 1
apiVersion: batch/v1
kind: Job
metadata:
creationTimestamp: null
name: myjob
spec:
completions: 1
parallelism: 1
ttlSecondsAfterFinished: 10 # takedown after 10 seconds of completion
template:
metadata:
===
C: 2 & P: 4 Change the relevant vars
CronJobs
- Jobs that run on a schedule.
1) kubectl create cronjob mycronjob --image=busybox --schedule="*/2 * * * *" -- echo greetings from your cluster.
Now the cronjob will run on its specified schedule but for testing, to run it before, do:
kubectl create job mycronjob --from=cronjob/mycronjob
Schedule The schedule syntax is defined as:
* * * * *
1) minute (0-59)
2) hour (0-23)
3) day of the month (1-31)
4) month (1-12)
5) day of the week (0-6: sun to sat)
Example
0 0 13 * 5: 13th of each Friday every week every month
[!NOTE] We can also set quotas or resource limits. Read it after basics.
Deleting
To delete everything:
- kubectl delete all --all
[!IMPORTANT] Don't force delete. Care about your job :/
Deployment
- Standard for running applications in K8.
- Adds to scalability and reliability of the application.
- Protects Pods and auto restarts them in case of failure.
Create a deployment
- kubectl create deployment name --image=<> or
- kubectl create deploy name --image=nginx --replicas=3
Get deployments
- kubectl get deployments or
- kubectl get deploy name
Expose the service
- kubectl expose deployment name --type=NodePort --port=8080
Delete deployment
- kubectl delete deployment name or
- kubectl delete deploy name
Replicas
- Helps in making sure that if one pod goes down, another one is brought up.
- Pod can be surrounded by replication controller.
- Makes sure that the specified no. of pods are always running as intended.
Load Balancing&Scalingcan be done with the help of replication controllers.- They can span over multiple nodes.
Replication Controller or Replica Set? - Replica Set is the new version for Replication Controller. - Replica Set can control the pods created earlier also but controller only controls those created using it.
Replication Controller
apiVersion: v1
kind: ReplicationController
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end # to tag pods of same category
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
- Run by:
kubectl create -f above.yml - Info by:
kubectl get replicationcontroller&get pods
ReplicaSet
apiVersion: apps/v1 # change here
kind: ReplicationSet # change here
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end # to tag pods of same category; shoule be same as below
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end # same
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector: # identify what pods fall under it
matchLabels:
type: front-end # all the pods; the ones not even created using replicaSet
- Info by:
kubectl get replicaset - Deployment uses
ReplicaSetfor scalability. Donot manage ReplicaSets directly, manage them through the deployment only.
Scale
We can scale by following ways:
1) Change replicas in yml file and run: kubectl replace -f above.yml.
2) Or do kubectl scale --replicas=6 -f above.yml.
3) Or run kubectl edit deployment name to edit the number of replicas manually.
[!NOTE] Both above scale as well as modify the replicas in the
ymlfiles.
4) Or do kubectl scale deployment name --replicas=6 or kubectl scale --replicas=6 replicaset <name-of-replica> -> doesnt modifies the yaml file.
Delete replicas
kubectl delete replicaset myapp-replicaset (also deletes underlying pods)
[!IMPORTANT] The names and labels of pods and under selector of replicaset should be the same.
[!NOTE] If we already have replicaset with a label, we cannot manually create a pod with the same label. ReplicaSet will automatically delete it.
Editing replicaset
- Use kubectl edit replicaset <replicaset-name>. Changes on-the-spot: running configuration.
[!TIP] To delete all, use
-allarg like:kubectl delete pod -all.[!TIP] To see all the available api versions, do
kubectl api-versions.
More about Deployment
- Controls the deployment aspects in k8s.
- Comes above the replica set
- Containers -> Pods -> Replica Set -> Deployment
apiVersion: apps/v1
kind: Deployment # change here
metadata:
name: myapp-rc
labels:
app: myapp
type: front-end
spec:
template:
metadata:
name: myapp-pod
labels:
app: myapp
type: front-end
spec:
containers:
- name: nginx-container
image: nginx
replicas: 3
selector:
matchLabels:
type: front-end
Then, do:
- kubectl create -f deployment.yaml.
- kubectl get deployments
- kubectl get replicaset
[!NOTE] Deployment auto creates the replica set.
[!TIP] Or get everything using:
kubectl get -all.[!TIP] To view specific info, use
--selectorattribute:kubectl get all --selector app=redis.
Deployment short-hand
kubectl create deployment <name> --image=nginx --replicas=3
Updates
-
Updates are used to change diff parts of the deployment.
- Image versions
- any param that can be used with
kubectl setcommand
-
When an update is applied, a new ReplicaSet is created with new properties.
- After successfull updates, the old replicaSets may be deleted.
- The
deployment.spec.revisionHistoryLimitis set to keep the 10 last ReplicaSets.
kubectl set
- Check kubectl set -h.
- For example, for changing the image, do: kubectl set image deploy mynginx nginx=nginx:1.17 => creates a new ReplicaSet. The pods are modified, old ones are deleted but the old replicaset still remains. Might come handy during rollbacks.
Labels
- key:value pairs
- Set in resources and use selectors to connect to related resources.
- Deployment finds its pods using a selector.
- Service finds its endpoint pods using a selector.
- Set a label:
kubectl run hello --labels="env=prod,ver=1" - To find all the resources matching a specific label, use
--selector-key=value.
Get the labels using: kubectl get all --show-labels.
Or do: kubectl get deploy -L canary.
Annotations
- Extra info.
- key-pair.
Example
Rollouts & Versioning
- Types of deployment strategies:
- Recreate (not recommended): Destroy all the old ones and then start the new ones.
- Rolling Update: Destroy one old and then bring new one in its place.
maxUnavailable: determines the max no of pods that are upgraded at the same time. By default, Kubernetes terminates one pod at a time while creating new pods, ensuring that the desired replica count is maintained.maxSurge: the no of pods that can run beyond the desired no of pods as specified in a replica to guarantee minimal availability. or maximum number of additional pods that can be created during a rolling update.
Rollout
kubectl rollout status deployment/myapp-deployment
Revisions or History of rollouts
-
kubectl rollout history deployment/myapp-deployment -
kubectl rollout history deployment rolling-nginx --revision=2
Rollback
-
kubectl rollout undo deployment/myapp-deployment -
kubectl rollout undo deployment rolling-nginx --to-revision=1 -
Rollout creates a new replicaset and then starts stopping the pods in old repicaset and spinning-up a new pod in the new replicaset.
Example 3 (create and update nginx)
1) kubectl create deployment proxy --image=nginx:1.9 --replicas=5 --dry-run=client -o yaml > mynginx.yaml
2) kubectl create -f mynginx.yaml
3) kubectl set image deployment proxy nginx=nginx:latest
AutoScaling
- In real clusters, Pods are often automatically scaled based on resource usage properties that are collected by metrics server.
-
The Horizontal Pod Autoscaler observes usage statistics and after passing a threshold, will add additional replicas.
-
Autoscale using:
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10(means if the cpu usage > 50%, scale up as desired but upto 10 pods only) -
Get pods using:
kubectl get hpa.
There are many addons of minikube, view them using: minikube addons list. Enable using minikube addons enable <add-on>.
Resource requests
- We can specify the resources limits in the k8.
- They are requested per containers, not per Pods. The final sum for Pod is the sum of all the resources requested for the containers.
Networking
- Each POD in a node is assigned its own IP.
- That IP is subject to change when PODs are recreated.
- K8 doesn't set up some networking fundamentals itself. It expects us to do the following:
- All containers/PODs can communicate to one another without NAT.
- All nodes can communicate with all containers and vice-versa without NAT.
- For setting that up, we can use external networking services like CISCO, VMWare NSX, cilium, flannel, etc.
Services (Service Discovery)
- Helps decouple groups and the connectivity between them.
- Eg. frontend attached to backend and backend attached to a db source.
- to expose a logical set of Pods.
- set of pods targeted by a service is determined by a selector (label)
- The kube-controller-manager will continuously scan for pods that match the selector and include these in the service
kube-proxyagent on the nodes watches the Kubernetes API for new services and endpoints. It works in the background & is reponsible for redirecting the traffic to the desired Pod.- We can use
kubectl exposeto create a service. After exposing, the service is assigned a virtual IP called a Cluster IP. This special IP address will load balance across all of the pods that are identified by the selector. - Then to interact with service, we perform the
--port-forwardto one of the pods.
Service DNS
- K8 provides a DNS service exposed to Pods running in the cluster.
- Installed by default. Managed by k8.
- It provides DNS names for cluster IPs.
- Service object operates at Layer 4 (TCP/IP). Check Ingress for HTTP/HTTPS aware connections.
- Format is:
service-name.namespace.svc.cluster.local.
cluster.local.: base domain name for the cluster
Types of Services
1) NodePort - For allowing new traffic in. - We have a pod inside a node. We can't directly access the pod as it is an internal IP. - We can expose the service on a port using Service. - It listens on exposed port and forwards the requests to pod back and forth. - allocates a specific node port on the node that forwards to the service cluster IP address.

service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80
port: 80
nodePort: 30008
selector: # to which pod to associate service with
app: myapp
type: frontend
- Run with:
kubectl create -f service.yaml - Get services:
kubectl get services
[!NOTE] Let's say we had 3 different pods with same labels. The above service would take all those as the endpoints and would balance the requests.
Algorithm: Random
SessionAffinity: Yes
Incase of multiple Nodes
Different IP for different nodes but with same port.

2) ClusterIP
- Communicate through Service (provides a uniform interface) Cluster IP.
- the default type exposes the service on an internal cluster IP address.

clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP # default; if not specified
ports:
- targetPort: 80
port: 80
selector:
app: myapp
type: back-end
3) LoadBalancer
- This is cloud-platform specific.
- Just set
kindtoLoadBalancer.
4) ExternalName
- works on DNS names;
- redirection is happening at DNS Level.
How to service
kubectl expose: for creating services; providing access to deployments, ReplicaSets, Pods or other services- In most cases,
kubectl exposeexposes a Deployment, which allocates its Pods as the service endpoint. kubectl create service: alternative way.- Specify
--portto indicate the service port.
While working with services, diff ports are specified: - targetport: port on container - port: port on service which is accessible - nodeport: port exposed externally (large range)
Example 4 (service for nginx)
1) kubectl create deploy nginxsvc --image=nginx --replicas=3
2) kubectl expose deploy nginxsvc --port=80
3) kubectl describe svc nginxsvc # look for endpoints
4) kubectl get svc nginxsvc -o=yaml
5) kubectl get svc
6) kubectl get endpoints (endpoints are dynamically fetched)
- ClusterIP is not reachable. Can
curlnginx by ssh'in into minikube:minikube sshand then doingcurl. ClusterIP is for internal access only.
To make it public, edit the service and change the following:
type:NodePort-
ports:targetPort: 80nodePort: 32000
-
Then check the
minikube ipand:32000.
[!NOTE] So, the frontend can be expose as NodePort while the backend can be exposed as ClusterIP (remains private), hence more secure.
NetworkingPolicy
- By default, there are no restrictions to network traffic in k8s. Pods can always communicate even if they are in other NameSpace.
- To limit it, NetworkPolicies can be used.
- If in a policy there is no match, traffic will be denied.
Three different identifiers can be used: - Pods: (podSelector) - Namespaces: (namespaceSelector) to grant access tospecific namespaces. - IP blocks: (ipBlock)
Selector labels are used heavily. And NetworkPolicies donot conflict, they are additive.
Ingress
- basically, when we expose services, we connect using ports say http://192.19.10.9:3000. This is good for testing but in prod, we need something like http://hello-app. Then we redirect to internal service i.e. route to ports based on paths.
- provide external access to internal k8 cluster resources.
- uses a load balancer present on the external cluster.
- uses selector lables to connect to Pods that are used as service endpoints.
- to access resources in the cluster, DNS must be configured to resolve to the ingress load balancer IP.
- exposes HTTP/HTTPS routes from outside the cluster to services inside.
- Can do:
- load balance traffic
- terminate SSL/TLS
- Need ingress controllers:
nginxhapropxykong
Ingress Controller
- Ingress requires ingress controller to be installed on the cluster to perform the rules evaluation.
- Entrypoint to the cluster.
- Eg. K8s Nginx Ingress Controller
- When we
minikube addons enable ingress, it automatically starts the nginx implementation of Ingress Controller. - Check with
kubectl get pod -n kube-system
Setup in minikube
minikube addons enable ingress: enable
[!IMPORTANT] Get all the namespaces in k8:
kubectl get ns
1) This continues on Example 4.
2) kubectl get deployment
3) kubectl get svc nginxsvc
4) curl http://$(minikube ip):32000
5) kubectl create ingress nginxsvc-ingress --rule="/=nginxsvc:80" --rule="/hello=newdep:8080" (we can even create a rule for the image not yet created)
6) kubectl describe ingress nginxsvc-ingress
7) minikube ip (note this ip)
8) Open /etc/hosts and add the line: 192.168.49.2 nginxsvc.info
9) curl nginxsvc.info
Ingress Rules
Each rule contains the following: - An optional host. If no host is specified, the rule applies to all inbound HTTP traffic. - A list of paths and each path has its own backend. - Backend which consists of either a service or a resource. Should configure a default backend for traffic that doesnt matches a specific path.
[!NOTE] Resource backends and service backends are mutually exclusive.
Ingress Path Types
-
pathType: how to deal with path requests.Exact: exact matchPrefix: should start with
-
single service:
kubectl create ingress single --rule="/files=filesservice:80" - simple fanout:
kubectl create ingress single --rule="/files=filesservice:80" --rule="/db=dbservice:80" - name-based (route requests based on the host header):
- make sure that there is a DNS entry for each host header
kubectl create ingress multihost --rule="my.example.com/files*=filesservice:80" --rule="my.example.org/data*=dataservice:80"
[!IMPORTANT] About
/etc/hostsThe /etc/hosts file in Linux is a plain text file that acts as a local directory for translating hostnames into IP addresses. It has following contents: 1) Lines: Each line typically consists of two parts separated by spaces or tabs. 2) IP Address: The first part is the IP address of the host. 3) Hostname(s): The second part is one or more hostnames (domain names) that you want to associate with that IP address.
For example, inside the file, the following
127.0.0.1 example.comtells your system to direct "example.com" to the loopback address (127.0.0.1), effectively blocking it.
Example 5 (name-based virtual hosting ingress)
1) kubectl create deploy mars --image=nginx
2) kubectl create deploy saturn --image=httpd
3) kubectl expose deploy mars --port=80
4) kubectl expose deploy saturn --port=80
5) Add entries to /etc/hosts:
- $(minikube ip) mars.example.com
- $(minikube ip) saturn.example.com
6) kubectl create ingress multihost --rule="mars.example.com/=mars:80" --rule="saturn.example.com/=saturn:80"
7) kubectl edit ingress multihost and change pathType to Prefix.
8) curl saturn.example.com & curl mars.example.com
Sample Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx-example
rules:
- host: my-app.com # this corresponds to the dns of the main master node server (the entrypoint)
http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
Default Backend
There is a default backend for all the requests that don't match. It is specified in the kubectl describe ingress.
We can configure it by running an internal service with the exact default-backend name and exposing it on the defined port in the ingress default backend.
Storage Options
- Files stored in containers are ephemeral.
- Pod Volumes are used to allocate storage that outlives a container and stays available during Pod lifetime.
- Pod Volumes can directly bind to a specific storage type.
- Pod Volumes can also use Persistent Volume Claims (PVC) to decouple the Pod from the requested site-specific storage.
- A persistent volume defines access to external storage available in a specific cluser.
- A wide range of PV types are available.
- Persistent Volume Claims (PVC) are used to connect to Persistent Volumes.
- When a PVC is created, it will search for an available PV that matches the storage request.
- If a perfect match doesnot exist, StorageClass can automatically create it.
Configuring Volumes
- To configure Pod Volumes, the volume is defined in
pod.spec.volumes. This array defines all the volumes that may be accessed by containers in the Pod manifest. - The volume points to a specific volume type:
- For testing purposes,
emptyDir: temp storagehostPath: persistent one
- Other types are also available like s3 or other cloud providers.
- For testing purposes,
- Then, the volume is mounted in the containers through
pod.spec.containers.volumeMounts. This array defines the vols that are mounted into a particular container and the path where each volume should be mounted.
Set up Pod Local Storage
1) kubectl explain pod.spec.volumes
2) Create the following yaml:
apiVersion: v1
kind: Pod
metadata:
name: "morevol2"
labels:
app: "morevol2"
spec:
containers:
- name: centos1
image: centos:7
command:
- sleep
- "3600"
volumeMounts:
- name: test
mountPath: /centos1
- name: centos2
image: centos:7
command:
- sleep
- "3600"
volumeMounts:
- name: test
mountPath: /centos2
volumes:
- name: test
emptyDir: {}
restartPolicy: Always
---
3) kubectl create -f morevolumes.yaml
4) kubectl get pods morevol
5) kubectl describe pods morevol | less
6) kubectl exec -ti morevol2 -c centos1 --touch /centos1/test
7) kubectl exec -it morevol2 -c centos2 -- ls /centos2
8) kubectl exec -ti morevol2 -c centos2 --touch /centos2
Persistent Volumes
- An independent resource that connects to external storage.
- Can point to all types of storages
- Use PVC to connect to it.
- The PVC talks to the available backend storage provider and dynamically uses volumes that are available on that storage type
- The PVC will bind to a PV according to the availability of the requeste volume
accessModesandcapacity.
[!NOTE] The above will create on the
host. In our case, it's not gonna be our system but theminikubehost. So,sshinto it to verify.
Setting up
1) Create the following yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-volume
labels:
app: pv-volume
spec:
# AKS: default,managed-premium
# GKE: standard
# EKS: gp2 (custom)
# Rook: rook-ceph-block,rook-ceph-fs
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mydata"
---
2) Create it.
PVC Binds
- After connecting to a PV, the PVC will shows as bound.
- A PVC is bind exclusive; after successfully binding to a PV, the same PV cannot be used by other PVC anymore.
- Say you got a 100gb storage. If PVC only needs 1gb of it and is bound to it, the 99gb is a waste.
- If a perfect match is not available, StorageClass may kick in and dynamically create a PV that does exactly match the request.
Create a PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pv-claim
namespace: default
labels:
app: pv-claim
spec:
# AKS: default,managed-premium
# GKE: standard
# EKS: gp2 (custom)
# Rook: rook-ceph-block,rook-ceph-fs
storageClassName: default
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
Using PVCs for Pods
Example
1) Create the following:
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-pvc-pod
labels:
app: "nginx-pvc-pod"
spec:
volumes:
- name: site-storage
persistentVolumeClaim:
claimName: nginx-pvc
containers:
- name: pv-container
image: nginx
ports:
- containerPort: 80
name: webserver
volumeMounts:
- name: site-storage
mountPath: "/usr/share/nginx/html"
2) kubectl get pvc,pv
3) kubectl describe pv pvc-xxx-yyy
4) kubectl exec nginx-pvc-pod -- touch /usr/share/nginx/html/testfile
5) minikube ssh
6) ls /tmp/hostpath-provisioner/default/nginx-pvc/
StorageClass
- Allows for auto provisioning of PVs when a PVC request for storage comes in
- Must be backed by a storage provisioner, which takes care of the volume configuration
- K8 comes with some internal provisioners, alternatively external provisioners can be installed using Operators
[!IMPORTANT] - Typical lab env is not backed by a k8 provisioner. - But minikube comes with a provisioner.
- A PVC will only bind to a PV that has the same StorageClass setting. If such a PV is not available, then it stays in pending state.
Know about the storage class by: kubectl get storageclass.
MORE ABOUT STORAGECLASS LATER ON
ConfigMaps
Providing Variables
-
K8 doesnt offers a cmd option to provide vars while running a deployment with
kubectl create deploy.- First create the deploy
- Then, do
kubectl set env deploy deploy_name VAR=VALUE
-
While running a POD, we can provide env but we shouldnt run naked pods.
kubectl run mydb --image=mysql --env="ENV_VAR=VALUE"
Need for decoupling
- Better management
- portability
- Variables are site-specific & should not be provided in the deployment config.
- K8 provide ConfigMap.
- It is used to define vars and the deployment just points to ConfigMap. Site-specific info is included in the ConfigMap which allows to keep the deployment manifest files generic.
- ConfigMap is stored in
etcd. - They can be used for:
- Vars
- Config files
- CMD args
- If an app refers to a ConfigMap, then the ConfigMap should exist in the cluster before running the application.
Providing Vars using ConfigMaps
1) Two ways using kubectl create cm mycm:
- Using: --from-env-file=file-name (cant use multiple times)
- Using: --from-literal=VAR=VALUE (can use multiple times)
2) Then, use kubectl set env --from=configmap/mycm deploy/myapp (create the said deployment)
3) Use --dry-run=client on kubectl create deploy & kubectl set env to generate yaml file using ConfigMap.
- eg. kubectl get deployment.apps mydb -o yaml | less.
Providing ConfigMaps for ConfigFiles
kubectl create cm mycm --from=file=/my/file.conf.- If dir is specified, all the
confs are included in the ConfigMap. - To use the
conffile in an app, ConfigMap must be mounted in the app.
Mounting a ConfigMap in an App - Generate the base YAML code, and add the ConfigMap mount to it later on. - In the app manifest, define a vol using ConfigMap type. - Mount this vol on a specific dir. - The ConfigMap file will appear inside the directory.
Example 6 (Using ConfigMap with a conf)
echo "hello world" > index.htmlkubectl create cm myindex --from-file=index.htmlkubectl describe cm myindexkubectl create deploy myweb --image=nginx-
kubectl edit deploy myweb-
spec.template.spec
yaml volumes: - name: cmvol configMap: name: myindex -
spec.template.spec.containers
-
Secrets
- senstive data
- secrets are not encrypted; they are base64 encoded.
- By default, k8 secrets are stored in plain text in the
etcdstorage for the cluster. - 3 types of secrets offered:
- docker-registry: used for conn to docker registry
- TLS: used to store TLS key material
- generic: creates a secret from local file, dir or literal value
- When defining secret, the type must be specified:
kubectl create secret generic ... - To access the K8 API, all k8 resources need access to TLS keys.
- These keys are provided by Secrets and used through ServiceAccounts.
- By using ServiceAccount, the application gets access to its Secret.
Demo
1) do kubectl get pods -n kube-system
2) do kubectl get pods -n kube-system coredns-<tab> -o yaml | less: we'll see serviceAccount: coredns
3) do kubectl get sa -n kube-system coredns -o yaml | less: we see the secret name
4) do kubectl get secret -n kube-system coredns-token-<tab> -o yaml: we see the secrets
5) for more: try to decode the namespace by: echo <namespace> | base64 -d.
6) Normally this is not easily accessible in real-world secure K8 cluster. It's the kubeadmin level.
Using Secrets in Apps
- To provide TLS keys:
kubectl create secret tls name --cert=tls/my.crt --key=tls/my.key - To provide security to passwords:
kubectl create secret generic my-secret-pw --from-literal=password=verysecret - To provide access to SSH private key:
kubectl create secret generic my-ssh-key --from-file=ssh-private-key=.ssh/id_rsa - To provide access to senstive file, which would be mounted in the app with root access only:
kubectl create secret generic my-sec-file --from-file=/my/secretfile
[!IMPORTANT] - As a secret is an encode ConfigMap, it is used in a way similar to using ConfigMaps. - If it contains vars, use
kubectl set env. - If it contains files, then the same mounting way like ConfigMap. - While mounting the Secret in the Pod spec, consider using defaultMode: 0400.
Demo
1) Create a secret: kubectl create secret generic dbpw --from-literal=ROOT_PASSWORD=password
2) kubectl describe secret dbpw
3) kubectl get secret dbpw -o yaml
4) kubectl create deploy mynewdb --image=mariadb
5) kubectl set env deploy mynewdb --from=secret/dbpw --prefix=MYSQL_
Configuring docker-registry seceret
kubectl create secret docker-registry my-docker-cred --docker-username=$NAME --docker-password=$PASS --docker-email=$EMAIL --docker-server=myregistry:3000
- Registry can be any cloud registry or local registry.
Example 7 (ConfigMaps & Secrets)
ToDo
- Create index.html with "Hello world"
- Create a ConfigMap that has the contents of the index.html
- Create a Secret that contains the var definition MYPASSWORD=verysecret
- Create a Manifest file that starts an Nginx Deployment that mounts the index.html file from the ConfigMap on /usr/share/nginx/html and sets the env var from the secret.
Way
1) Create index.html
2) kubectl create cm mycm --from-file=index.html
3) kubectl get cm mycm
4) kubectl get cm mycm -o yaml
5) kubectl create secret generic mysec --from-literal=MYPASSWORD=verysecret
6) kubectl get secret mysec -o yaml
7) echo <secret> | base64 -d
8) kubectl get cm,secret,deploy
9) kubectl create deploy mynginx --image=nginx
10) kubectl set env deploy mynginx --from=secret/mysec
11) kubectl edit deploy mynginx
12) Do the following edit:
under spec.template
under spec.template.spec.containers
13) kubectl get deploy mynginx -o yaml > mynginxconfig.yaml
14) kubectl delete deploy mynginx
15) kubectl create -f mynginxconfig.yaml
16) Check using:
- kubectl exec mynginx-<pod-name> -- cat /usr/share/nginx/html/index.html
- kubectl exec mynginx-<pod-name> -- env
kube-proxy
- K8 api's are RESTful means they can be accessed using direct HTTP traffic.
kubectlusescurlbehind the scenes. But in a secure way.- To make our own API requests using
curl, appropriate certs are required. - Use
kube-proxyto provide a secure interface for curl. - We use
curlto connect tokube-proxywhich in turn connects tokubectl's.kube.configand uses TLS by deafult.
To access the API using curl, start the kube-proxy on the k8 user workstation:
- kubectl proxy --port=8001&
- curl http://localhost:8001 and many other apis.
[!IMPORTANT] With new k8 release, old API versions may be deleted and will be supported for a min of 2 more k8 releases.
Authentication & Authorization
Authentication
- Authentication is about where k8 users come from.
- By default, a local k8 admin account is used and auth is not required.
- We can create our own user accounts.
- The
kubectlconfig specified to which cluster to authenticate.- Use
kubectl config view: to see current settings - Config is read from
~/.kube/config
- Use
Authorization
- Authorization is what users can do.
- Behind authorization, there is Role Based Access Control (RBAC) to take care of diff options
- Use
kubectl auth can-i get podsto find out about the authorization status - Can also check for another user:
kubectl auth can-i get pods --as arnav@example.com
Understanding RBAC
- It is used to provide access to API Resources.
- 3 elements are used:
- Role: defines access permissions to specific resources
- User or ServiceAccount: to work with the API
- RoleBinding: connects a user or ServiceAccount to a specific Role
ServiceAccounts
- All actions in k8 cluster need to be authenticated and authorized
- ServiceAccounts are used for basic authentication from within the k8 cluster
- RBAC is used to connect SA to a specific Role
- Every Pod uses the default SA to contact the API server
- Each SA uses a secret to automount API creds
Like this: Pod -> SA -> Role-binding -> Role -> api-server -> etcd
- SA comes with a secret which contains API creds and is auto mounted.
- Get it with:
kubectl get saorkubectl get sa -o yaml. - To get all the sa available:
kubectl get sa -A
More RBAC
- A cluster admin can configure RBAC authorization to specify what exactly can be accessed by a SA or user
- Custom SA can be created and configured with additional permissions for enhanced access from Pods to cluster resources
- For additional permissions, Namespace or cluster scope Roles and RoleBindings are used
- ClusterRole and ClusterRoleBinding are used to configure access to cluster resources
Example 8 (Creating Role and its binding and the SA)
1) Role yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: list-pods
namespace: default
rules:
- apiGroups:
- ''
resources:
- pods
verbs:
- list
2) Role binding yaml:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: list-pods-mysa-binding
namespace: default
roleRef:
kind: Role
name: list-pods
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: mysa
namespace: default
3) But it requires the SA mysa. It is not created yet. Create it using: kubectl create sa mysa.
Managing ServiceAccounts
- Every namespace has a default SA called default
- Additional SA can be created to provide additional access to resources
- After creating additional SA, it needs to get permissions through RBAC
- SA can be created using the imperative way:
kubectl create serviceaccoubt mysa - While creating SA, a Secret is automatically created to have the SA connect to the API
- This secret is mounted in Pods using the SA and used as an access token
- After creating the SA, RBAC must be configured
Example 9 (Configuring & Using SA)
ToDo
- Create a Pod using Standard SA
- Try to access the pod using
curl(https://kubernetes/api/v1 --insecure: will be forbidden) - Try to access using the default SA token
- Try again but this time to list the pods
- Then create a new SA with new Role and RoleBinding that is able to list the pods
Way
DevOps Way
Helm
- To streamline installing and managing k8 apps
- Consists of the helm tool and a chart
- A chart is a helm package, which contains:
- A desc of the package
- One or more temps containing the k8 manifest files
- Charts can be stored locally or accessed from Helm repos.
- Helm also supports templating engine meaning we can have a dynamic yaml file (can replace the vars on the fly)
- better suited for CI/CD pipeline where in our build we can replace the values on the fly
- same apps in different environments
Installation
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
Helm Charts
- Main site for finding them: https://artifacthub.io.
- Search the required and install like:
helm repo add kubernetes-dashboard https://kubernetes.github.io/dashboardhelm install kubernetes-dashboard kubernetes-dashboard/kubernetes-dashboard
[!NOTE] In minikube, use
minikube addons enable dashboard.
We can:
- helm repo add name url
- helm repo list
- helm search repo name
- helm repo update
- helm install <release_name> name
- helm list
- helm delete
- helm upgrade <release_name> name
- helm rollback <release_name> <revision_no>
Helm Demo (mysql helm chart)
1) See this: https://artifacthub.io/packages/helm/bitnami/mysql?modal=install.
2) kubectl get all
3) helm show chart bitnami/mysql
4) helm list
5) helm status <name>
But there's a way to customize before installing (RECOMMENDED)
- A Helm chart consists of templates to which specific values are applied.
- Stored in
values.yamlfile within the helm chart. - So, to modify, begin with
helm pullto fetch a local copy of the helm chart - Then edit
chartname/values.yamlto change the values
1) helm show values bitnami/nginx
2) helm pull bitnami/nginx: downloads the tar
3) tar xvf nginx-xxx
4) vim nginx/values.yaml
5) helm template --debug nginx (to check what will be applied; do in the directory where nginx is subdir or extracted)
6) helm install -f nginx/values.yaml my-nginx nginx/
Create Helm Chart
1) helm create <name>
2) helm list -a
3) helm install <release_name> <dir>
4) helm install <release_name> --debug --dry-run <dir>: Validate and verify the helm chart
5) helm template <dir>: for only validating the yamls without connecting to k8 api server
6) helm lint <dir>
7) Then, configure out the values.yaml, Charts.yaml and if required the templates files in the dir.
HelmFile: automate Helm
- pre-requisite:
Helm Chartinstalled
Installation
- Download the binary release of Helm.
- Untar and
chmod 777 helmfile. mv helmfile /usr/local/bin- Ready:
helmfile --version
Then, once we create our helm chart:
1) Create helmfile.yaml at the base of the helm chart folder.
2) Then write:
---
releases:
- name: helloworld # name of the release
chart: ./helloworld # name of the chart
installed: true # install; false: uninstall
helmfile sync.
4) Also, we can specify helm chart from github:
---
repositories:
- name: helloworld
url: git+https://github.com/rahulwagh/helmchart@helloworld?ref=master&sparse=0
releases:
- name: helloworld
chart: helloworld/helloworld
installed: false
---
releases:
- name: helloworld1
chart: ./helloworld1
installed: true
- name: helloworld2
chart: ./helloworld2
installed: true
Helm Tests
- Under
testsundertemplates. - pre-requisite: helm chart must be installed.
- Then, do
helm test <release-name>
Kustomize
- k8 feature (better decoupling)
- file name should be:
kustomization.yamlto apply changes to a set of resoruces - convenient for applying changes to input files that the user doesnt control himself and whose contents may change because of new versions appearing in Git
- Use
kubectl apply -k ./in the dir with thekustomization.yamland the file it refers to apply changes - Use
kubectl delete -k ./to undo
Kustomization Overlays
- To define a base configuration as well as multiple deployment scenarios (overlays) as in dev, staging and prod for instance.
- In such a config, the main
kustomization.yamldefines the structure:
- base
- deployment.yaml
- service.yaml
- kustomization.yaml
- overlays
- dev
- kustomization.yaml
- staging
- kustomization.yaml
- prod
- kustomization.yaml
- In each of the overlays/{dev,staging,prod}/kustomization.yaml, users would reference the base config in the resources field, and specify changes for that specific env.
kustomization.yaml
resources:
- ../../base
namePrefix: dev-
namespace: development
commonLabels:
environment: development
- Then after applying:
kubectl apply -k . - We can also do:
kubectl get all --selector environment=development
DIVE MORE INTO KUSTOMIZATION
Vids left: Implementing Canary & Blue-Green deployments in CKAD course. Do it.
Modules
- repeatble ones.
- For getting them, do:
terraform get
Example 9 (AWS EKS Setup)
1) Set up all the images on ECR.
[!NOTE] To make the aws ecr cli take the iam sso credentials, do
export=PROFILE_NAMEbeforehand.
2) Download eksctl.
3) For creating the cluster based on ec2, do:
eksctl create cluster \
--name tf-cluster \
--region us-east-1 \
--nodegroup-name tf-nodes \
--node-type t2.micro \
--nodes 2
--zones us-east-1a,us-east-1b
[!IMPORTANT] If the env var
AWS_PROFILEis not working, prefix the command above withAWS_PROFILE=arnav <rest-code>[!IMPORTANT] Cluster creation is not supported in
us-east-1e. (atleast when i tested)[!NOTE] For fargate type, do:
--fargateafter--region. And for fargate type, we need to define a fargate profile beforehand i.e.
4) Check the nodes with: kubectl get nodes
5) Now, we have everything i.e. our helm chart. The only thing to configure is the alb for our service's ClusterIP. For that the ingress annotations are:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fastapi-ingress
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing # internet-facing for internet facing pod
alb.ingress.kubernetes.io/target-type: ip # ip for cluster-ip; instance for nodeport
spec:
ingressClassName: alb
rules:
#- host: detect.com # custom domain
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fastapi-service
port:
number: 8000
aws eks update-kubeconfig --name tf-cluster --region us-east-1
7) Then apply the manifests.
8) Now, if we do: kubectl get ingress, we dont get the address. For that we need to create the alb ingress controller.
- In order for our cluster to communicate with AWS service, we need to configure IAM OIDC provider. Do:
- eksctl utils associate-iam-oidc-provider --cluster <cluster-name> --approve
- Now, we need our ingress controller to communicate with AWS ALB. For that we need to create an iam role and a policy, do:
- curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json
- aws iam create-policy --policy-name <ANY_NAME> --policy-document file://<the_above_file>
- Now we need to attach the policy to the service account of our pods so that they can access:
- eksctl create iamserviceaccount --cluster=<your-cluster-name> --namespace=kube-system --name=aws-load-balancer-controller --role-name AmazonEKSLoadBalancerControllerRole --attach-policy-arn=arn:aws:iam::<your-aws-account-id>:policy/AWSLoadBalancerControllerIAMPolicy --approve
- Now add the ingress controller:
- helm repo add eks https://aws.github.io/eks-charts
- helm repo update eks
- helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=<your-cluster-name> --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller --set region=<region> --set vpcId=<your-vpc-id>
- Now check the ALB controller (they will be deployed in 2 AZ for redundancy):
- kubectl get deploy -n kube-system
10) For deleting cluster, do: eksctl delete cluster --name <cluster-name>
[!NOTE] When pod is on aws eks, if need to ssh into pod, do:
kubectl run -n YOUR_NAMESPACE troubleshoot -it --rm --image=amazonlinux -- /bin/bash. Afterwards access with:kubectl attach troubleshoot -c troubleshoot -i -t.[!NOTE] In case of AWS Fargate, CoreDNS gets in pending state if Fargate profile is not created. Fargate Profile allows EKS to automatically create nodes for our application based on kubernetes namespace and optionally pod labels. Check: https://docs.aws.amazon.com/eks/latest/userguide/fargate-getting-started.html#fargate-gs-coredns for more.