Last active
December 21, 2021 00:04
-
-
Save lynsei/cdc4d9ef390b82b77eedf3b48f9fa340 to your computer and use it in GitHub Desktop.
Yes dear lord, let me revel in this amazing K8s documentation. If I could only have just one more thing on earth, it would be simply this! more k8s documentation. I love how concise this one is though, like for real only 2906 pages? Awww man show your boss he would totally be impressed!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Kubernetes Architecture | |
~~~~~~~~~~~~~~~~~~~~~~~ | |
As you may have noticed, I do a feel a bit snarky and frankly, well, misanthropic and completely | |
irreverent when it comes to the state of container orchestration in 2021. You might havev also | |
noticed that I've built more than just a few container clouds in my most recent 5 year foray | |
into container-space. Anyhow, the reason I get all this angst is because I remember what | |
things were like prior to Kubernetes and how simple everything was. Those were "The Good days". | |
In keeping with that sentiment, I love WASM and Rust and all things FaaS. It's my current mission | |
in life to abstract away the magic that is kubernetes by wrapping my k8s clusters inside a docker | |
container and sticking them where the sun don't shine (inside a VM designed specifically for FaaS). | |
Anyhow I know that preface might have been a bit more than you were expecting, but hey... | |
I hope my humor is appreciated in some region of the world by unknown cybernetic aliens who understand both English and... Kubernetes. ~.~ | |
-Lynsei Asynynivynya | |
Nodes | |
----- | |
. A node is a machine, physical or virtual on which Kubernetes is installed. | |
. A node is a worker machine and that is where containers will be launched by Kubernetes. | |
- A worker node is also called as minions | |
Cluster | |
------- | |
. A set of nodes grouped together. So if one fails you have your application still accessible from the other nodes. | |
. Having multiple nodes helps in sharing load as well | |
Master Node | |
----------- | |
. Master is another node with Kubernetes installed in it and is configured as master. | |
. The master watches over the nodes in the cluster and is responsible for the actual orchestration of the containers | |
in the worker nodes. | |
. The information about the members of the cluster is stored in the master node | |
. Master node is responsible for monitoring other nodes | |
. When a node fails, it is the master node which is responsible for moving the workload of the failed node to another | |
worker node | |
Components installed as part of Kubernetes installation | |
-------------------------------------------------------- | |
. API Server : acts as the front end for Kubernetes. The users, management devices, command line interfaces all talk to | |
the API server to interact with the Kubernetes cluster. | |
. etcd service : etcd is a distributedm reliable key value store used by Kubernetes to store all data used to manage | |
the cluster. When you have multiple worker nodes and multiple masters in your cluster, etcd stores all that | |
information on all the nodes in the cluster in a distributed manner. etcd is responsible for implementing locks | |
within the cluster to ensure that there are no conflicts between the Masters. | |
. kubelet service : Kubelet is the agent that runs on each node in the cluster. The agent is responsible for making | |
sure that the containers are running on the nodes as expected. | |
. Container Runtime : is the underlying software that is used to run containers. Eg. Docker | |
. Controllers : are the brain behind orchestration. They are responsible for noticing and responding when nodes, | |
containers or end points goes down | |
. Schedulers : is responsible for distributing the work or containers across multiple nodes. It looks for newly created | |
containers and assign them to nodes. The controller make decision to bring up new containers in such cases. | |
|---------------------| |----------------------| | |
| kube-api server<---|-------------------------------|--->kubelet | | |
| | | | | |
| etcd | | | | |
| | | | | |
| controller | | | | |
| | | | | |
| scheduler | | container runtime | | |
|---------------------| |----------------------| | |
Master Worker | |
kubectl | |
------- | |
. kubectl tool is used to deploy and manage applications on a Kubernetes cluster. | |
. To get cluster information, to get the status of other nodes in the cluster and to manage many other things. | |
. kubectl run command is used to deploy an application on the cluster | |
. kubectl cluster info command is used to view information about the cluster | |
. kubectl get nodes is used to list all the nodes part of the cluster | |
>> kubectl run hello-minikube | |
>> kubectl cluster-info | |
>> kubectl get nodes | |
====================================================================================================================== | |
PODS | |
~~~~ | |
. Kubernetes doesnot deploy containers directly on the worker nodes. | |
. Containers are encapsulted into a kubernetes object known as pods. | |
. A pod is a single instance of an application. | |
. A pod is the smallest object that you can create in Kubernetes. | |
Create a pod using an image >> kubectl run <pod_name> --image <image_name> | |
Eg. kubectl run nginx --image nginx | |
List all pods >> kubectl get pods | |
List all pods with labels >> kubectl get pods --show-labels | |
Get pods info >> kubectl describe pods | |
List all pods with ip & node info >> kubectl get pods -o wide | |
List all nodes >> kubectl get nodes | |
Get node info >> kubectl describe node <nodename> | |
To datatype info of each key under pods >> kubectl explain pod --recursive | less | |
====================================================================================================================== | |
PODS with YAML | |
~~~~~~~~~~~~~~ | |
. Kubernetes definition file always contain four top level fields | |
>> vim pod-definition.yml ==> |----------------| | |
| apiVersion: | | |
| kind: | | |
| metadata: | | |
| spec: | | |
|________________| | |
. These are the top level or root level properties | |
. These are also the required files, so you must have them in your configuration file | |
Explaing the pod-definition file in detail | |
------------------------------------------ | |
>> vim pod-definition.yml | |
apiVersion: v1 (Other values : apps/v1, extensions/v1Beta) | |
kind: Pod (Other values : ReplicaSet, Deployment or Service) | |
metadata: | |
name: myapp-pod | |
labels: | |
app: myapp | |
type: front-end | |
spec: | |
containers: | |
- name: nginx-container | |
image: nginx | |
>> kubectl create -f pod-definition.yaml | |
. apiVersion | |
- This is the version of the Kubernetes API you are using to create the objects. | |
. kind | |
- Kind refers to the type of object we are trying to create which in this case happens to be Pod | |
. metadata | |
- Metadata is the data about the object like its name, labels etc | |
- This will be in the form of dictionary where as the apiVersion and kind shoud always be String | |
- 'name' and 'label' properties are of sibling tags, so they should be of same level while writing yaml file | |
- Under metadata, name is a string value where as the labels is a dictionary within the metadata dictionary | |
- labels can have any key value pairs | |
. spec : | |
- Spec is a dictionary | |
- Information regarding the container is specified under spec | |
- containers is a list since we can build multiple containers under same pod | |
Creating the pod >> kubectl create -f pod-definition.yml | |
Get the list of pods >> kubectl get pods | |
To see details about a pod >> kubectl describe pod <my_pod_name> | |
To see list of pods with its node and ip info >> kubectl get pods -o wide | |
To generate pod definition file from a running pod >> kubectl get pod <pod_name> -o yaml > pod-def.yaml | |
To delete a pod >> kubectl delete pod <pod_name> | |
To delete a pod without delay >> kubectl delete pod <pod_name> --grace-period=0 --force | |
To edit a pod >> kubectl edit pod <pod_name> | |
To apply a change to a running pod >> First update the pod definition file. Then | |
>> kubectl apply -f pod-def.yaml | |
Kubernetes Objects Version based on Kind >> .__________________________. | |
| Kind | Version | | |
|--------------------------| | |
| POD | v1 | | |
| Service | v1 | | |
| ReplicaSet | apps/v1 | | |
| Deployment | apps/v1 | | |
| DaemonSets | apps/v1 | | |
|_____________|____________| | |
Edit PODs | |
~~~~~~~~~ | |
If you are given a pod definition file, edit that file and use it to create a new pod. | |
If you are not given a pod definition file, you may extract the definition to a file using the below command: | |
kubectl get pod <pod-name> -o yaml > pod-definition.yaml | |
Then edit the file to make the necessary changes, delete and re-create the pod. | |
OR | |
Use the kubectl edit pod <pod-name> command to edit pod properties. | |
====================================================================================================================== | |
Replication Controllers and ReplicaSets | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Replication Controller | |
---------------------- | |
- helps us run multiple instance of a single pod in the kubernetes cluster thus providing high availability | |
- Even if you have a single pod, the replication controller can help by automatically bringing up a new pod when the | |
existing one fails | |
- A Replication Controller is a structure that enables you to easily create multiple pods, then make sure that that | |
number of pods always exists. If a pod does crash, the Replication Controller replaces it. | |
- Replication Controllers also provide other benefits, such as the ability to scale the number of pods, and to update | |
or delete multiple pods with a single command. | |
>> vim replcontroller-definiton.yml | |
apiVersion: v1 | |
kind: ReplicationController | |
metadata: # metadata for Replication Controller | |
name: myapp-rc | |
labels: | |
name: myapp | |
type: front-end | |
spec: # spec for Replication Controller | |
template: | |
metadata: # metadata for Pods | |
name: myapp-pod | |
labels: | |
app: myapp | |
type: frontend-app | |
spec: # spec for Pods | |
containers: | |
- name: nginx-container | |
image: nginx | |
replicas: 5 | |
When the replication controller is created using the below syntax, it first creates the pod using the pod definition | |
template as many as required, which is five in this case. | |
Create Replication Controller >> kubectl create -f repcontroller-definiton.yml | |
To view the list of created Replication Controller >> kubectl get replicationcontroller | |
To view the Pods created using Replication Controller >> kubectl get pods | |
Replica Set | |
----------- | |
- The Replication Controller is the original form of replication in Kubernetes. It’s being replaced by Replica Sets, | |
but it’s still in wide use. | |
- Replica Sets are declared in essentially the same way as Replication Controllers, except that they have more | |
options for the selector. | |
>> vim replicaset-definiton.yml | |
apiVersion: apps/v1 | |
kind: ReplicaSet | |
metadata: | |
name: myapp-replicaset | |
labels: | |
app: myapp | |
type: front-end | |
spec: | |
template: | |
metadata: | |
name: myapp-pod | |
labels: | |
app: myapp <----| | |
type: front-end <----| | |
spec: | | |
containers: | | |
- name: nginx-controller | | |
image: nginx | | |
| | |
replicas: 3 | | |
selector: | | |
matchLabels: | | |
type: front-end # The value should be the label defined in the pod definition. | |
# which could be app or type in this case. | |
When the replica set is created using the above syntax, it first creates the pod using the pod definition template as | |
many as required, which is three in this case. | |
Create Replica Set >> kubectl create -f replicaset-definiton.yml | |
To view the list of created Replica Set >> kubectl get replicaset | |
To view the Pods created using Replica Set >> kubectl get pods | |
To delete Replica Set >> kubectl delete replicaset myapp-replicaset | |
>> kubectl delete rs myapp-replicaset | |
To know more about the Replica Set >> kubectl describe replicaset | |
>> kubectl describe rs | |
To fix an existing Replica Set >> kubectl get replicaset | |
>> kubectl edit replicaset <replicaset-name> | |
Now do edit and save and then delete the existing pods. | |
Create replicaset def file from running replicaset >> kubectl get replicaset <rs_name> -o yaml > rep-set-def.yaml | |
This will export/generate the replicaset definition file | |
Scale ReplicaSet to x number | |
---------------------------- | |
There are multiple ways | |
1. Update the number of replicas in the definition file to 'x'. Eg : 3 and then run the following command | |
>> kubctl replace -f replicaset-definiton.yml | |
2. The second way to do scaling is to run the kubectl scale command | |
>> kubectl scale --replicas=6 -f replicaset-definition.yml | |
>> kubectl scale --replicas=6 replicaset myapp-replicaset | |
| | | |
Type name mentioned in metadata | |
3. The third way is to edit the replicaset | |
>> kubectl get replicaset | |
>> kubectl edit replicaset <replicaset-name> | |
>> Now modify the replicas and save the file | |
====================================================================================================================== | |
Deployments | |
~~~~~~~~~~~ | |
Deployment provide the capability to upgrade the underlying instances seamlessly using rolling updates, undo changes and | |
pause and resume changes as required. | |
>> vim deployment-definiton.yml | |
apiVersion: apps/v1 | |
kind: Deployment | |
metadata: | |
name: myapp-deployment | |
labels: | |
app: myapp | |
type: front-end | |
spec: | |
template: | |
metadata: | |
name: myapp-pod | |
labels: | |
app: myapp <----| | |
type: front-end <----| | |
spec: | | |
containers: | | |
- name: nginx-controller | | |
image: nginx | | | |
| | |
replicas: 3 | | |
| | |
selector: | | |
matchLabels: | | |
type: front-end # The value should be the label defined in the pod definition. | |
Create Deployment >> kubectl create -f deployment-definiton.yml | |
To view the Deployments >> kubectl get deployments | |
To view the ReplicaSet >> kubectl get replicaset | |
To view all the objects >> kubectl get all | |
To edit an existing deployment >> kubectl edit deployment <deployment_name> | |
To know more about the Deployment >> kubectl describe deployment | |
To view a specific deployment info >> kubectl describe deployment <deployment_name> | |
Note1 : Deployment autmatically creates ReplicaSet and ReplicaSet ultimately create pods | |
Note2 : In production use cases, you will not creating pod definion files or replica set definition files. | |
Instead you will be creating deployment definition files. | |
====================================================================================================================== | |
A quick note on editing PODs and Deployments | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Edit a POD | |
---------- | |
. Extract the pod definition in YAML format to a file using the command | |
. Then make the changes to the exported file using an editor (vi editor). Save the changes | |
. Then delete the existing pod | |
. Then create a new pod with the edited file | |
>> kubectl get pod webapp -o yaml > my-new-pod.yaml | |
>> vim my-new-pod.yaml | |
>> kubectl delete pod webapp | |
>> kubectl create -f my-new-pod.yaml | |
Edit Deployments | |
---------------- | |
>> kubectl edit deployment my-deployment | |
====================================================================================================================== | |
Certification Tip | |
~~~~~~~~~~~~~~~~~ | |
Reference :: https://kubernetes.io/docs/reference/kubectl/conventions/ | |
Create an NGINX Pod and expose a port | |
------------------------------------- | |
>> kubectl run nginx --image=nginx --port=<port_id> | |
Create an NGINX Pod on a specific namespace | |
------------------------------------------- | |
>> kubectl run nginx --image=nginx -n <namespace_name> | |
>> kubectl run nginx --image=nginx --namespace=<namespace_name> | |
Generate POD Manifest YAML file (-o yaml). Don't create it(--dry-run=client) | |
--------------------------------------------------------------------- | |
>> kubectl run nginx --image=nginx --dry-run=client -o yaml | |
Create a busybox pod with sleep command for 3600 sec | |
---------------------------------------------------- | |
>> kubectl run busybox --image=busybox --dry-run=client -o yaml --command -- sleep 3600 | |
>> kubectl run busybox --image=busybox --dry-run=client -o yaml --command -- /bin/sh -c ls /root | |
Create a static pod named static-busybox that uses the busybox image and the command sleep 1000 | |
----------------------------------------------------------------------------------------------- | |
>> kubectl run --restart=Never --image=busybox static-busybox --dry-run=client -o yaml --command -- sleep 1000 > busybox.yaml | |
Deploy a redis pod using the redis:alpine image with the labels set to tier=db | |
------------------------------------------------------------------------------ | |
>> kubectl run redis --image=redis:alpine -l tier=db | |
OR | |
>> Use imperative commands to generate the pod definition file, | |
Then add the labels before creating the pod using the file. | |
Create a deployment | |
------------------- | |
>> kubectl create deployment nginx --image=nginx | |
Generate Deployment YAML file (-o yaml). Don't create it(--dry-run) | |
------------------------------------------------------------------- | |
>> kubectl create deployment nginx --image=nginx --dry-run=client -o yaml | |
Generate Deployment YAML file (-o yaml). Don't create it(--dry-run) with 4 Replicas (--replicas=4) | |
-------------------------------------------------------------------------------------------------- | |
>> kubectl create deployment nginx --image=nginx --replicas=4 --dry-run=client -o yaml | |
# kubectl create deployment does not have a --replicas option. --> Nolonger a valid case. | |
# You could first create it and then scale it using the kubectl scale command. --> Nolonger a valid case. | |
Save it to a file - (If you need to modify or add some other details) | |
--------------------------------------------------------------------- | |
>> kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > nginx-deploy.yaml | |
Create a Service named redis-service of type ClusterIP to expose pod redis on port 6379 | |
--------------------------------------------------------------------------------------- | |
>> kubectl expose pod redis --port=6379 --name redis-service --dry-run -o yaml | |
(This will automatically use the pod's labels as selectors) | |
OR | |
>> kubectl create service <servicetype> <servicename> --tcp=port:targetport --dry-run=client -o yaml (RECOMMENDED WAY) | |
>> kubectl create service clusterip redis --tcp=6379:6379 --dry-run=client -o yaml (RECOMMENDED WAY) | |
(This will not use the pods labels as selectors, instead it will assume selectors as app=redis. You cannot pass in | |
selectors as an option. So it does not work very well if your pod has a different label set. So generate the file | |
and modify the selectors before creating the service) | |
Create a Service named nginx of type NodePort and expose it on port 30080 on the nodes | |
-------------------------------------------------------------------------------------- | |
>> kubectl expose pod nginx --port=80 --name nginx-service --dry-run -o yaml | |
(This will automatically use the pod's labels as selectors, but you cannot specify the node port. You have to | |
generate a definition file and then add the node port in manually before creating the service with the pod.) | |
Here we have created service using pod nginx. Like that we could create service using deployment and the syntax is | |
>> kubectl expose deployment <deployment-name> --port=80 --name service-name --dry-run -o yaml | |
OR | |
>> kubectl create service <ser_type> <ser_name> --tcp=port:targetport --node-port=port_id --dry-run -o yaml (RECOMMENDED) | |
>> kubectl create service nodeport nginx --tcp=80:80 --node-port=30080 --dry-run -o yaml | |
(This will not use the pods labels as selectors) | |
Both the above commands have their own challenges. While one of it cannot accept a selector the other cannot accept | |
a node port. I would recommend going with the `kubectl expose` cmd. If you need to specify a node port, generate a | |
definition file using the same command and manually input the nodeport before creating the service. | |
>> kubectl expose deployment webapp --name=webapp-service --type=NodePort --port=8080 --dry-run -o yaml | |
Edit the file and add node port | |
Create a job | |
------------ | |
>> kubectl create job <job_name> --image=<image_name> | |
>> kubectl create job my-job --image=busybox | |
Create a job with command | |
------------------------- | |
>> kubectl create job my-job --image=busybox -- date | |
Create a job from a CronJob named "a-cronjob" | |
--------------------------------------------- | |
>> kubectl create job test-job --from=cronjob/a-cronjob | |
Create a cronjob | |
---------------- | |
>> kubectl create cronjob my-job --image=busybox | |
Create a cronjob with command | |
----------------------------- | |
>> kubectl create cronjob my-job --image=busybox -- date | |
Create a cronjob with schedule | |
------------------------------ | |
>> kubectl create cronjob test-job --image=busybox --schedule="*/1 * * * *" | |
Create a cronjob with restart option 'OnFailure, Never' | |
------------------------------------------------------- | |
>> kubectl create cronjob test-job --image=busybox --restart="Never" | |
>> kubectl create cronjob test-job --image=busybox --restart="OnFailure" | |
Create a job with the image busybox that executes the command 'echo hello;sleep 30;echo world' | |
---------------------------------------------------------------------------------------------- | |
>> kubectl create job busybox --image=busybox -- /bin/sh -c 'echo hello;sleep 30;echo world' | |
====================================================================================================================== | |
Namespaces | |
~~~~~~~~~~ | |
In every cluste, there will be 3 namespaces exist by default. | |
. default | |
. kube-system | |
. kube-public | |
Assume we have 3 pods in default name space and 2 in dev name space. | |
Default dev | |
------- --- | |
. web-pod . web-pod | |
. db-service . db-service | |
. web-deployment | |
Note : The resources within a namespace can refer to each other simply by their names. | |
Eg: The web app pod can reach the db service simply using the hostname db service. (Both in default) | |
>> mysql.connect("db-service") | |
Resources in 2 different namespaces can communicate and for that we need to apped the name of namespace along with | |
service name. | |
Eg: The web pod in default namespace to connect to the database in the dev namespace, | |
For that use the servicename.namespace.svc.cluster.local format => db-service.dev.svc.cluster.local | |
>> mysql.connect("db-service.dev.svc.cluster.local") | |
| | | |__________| | |
| | | |-----------> domain | |
| | |--------------------> Service | |
| |-------------------------> Namespace | |
|-----------------------------------> Service Name | |
To get all namespaces >> kubectl get namespaces | |
To get all pods in default or current namespace >> kubectl get pods | |
To get all pods in different namespace eg 'kube-system' >> kubectl get pods --namespace=kube-system | |
To create pods >> kubectl create -f pod-definition.yml | |
To create pods in a different name space >> kubectl create -f pod-definition.yml --namespace=dev | |
(or keep the namespace in pod-definition.yml.) | |
| | |
>> vi pod-definition.yml <-| | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: myapp-pod | |
namespace: dev ---> NAMESPACE | |
labels: | |
app: myapp | |
type: front-end | |
spec: | |
containers: | |
- name: nginx-container | |
image: nginx | |
To create a new name space | |
-------------------------- | |
>> vim namespace-dev.yml | |
apiVersion: v1 | |
kind: Namespace | |
metadata: | |
name: dev | |
>> kubectl create -f namespace-dev.yml | |
OR | |
>> kubectl create namespace <namespace_name> | |
Eg. kubectl create namespace dev | |
Eg. kubectl create namespace qa | |
Eg. kubectl create namespace prod | |
Switch namespace to dev >> kubectl config set-context $(kubectl config current-context) --namespace=dev | |
>> kubectl config set-context <cluster_name> --namespace=dev | |
Get pods in all namespaces >> kubectl get pods --all-namespaces | |
To limit resources in a namespaces, create a resource quota | |
----------------------------------------------------------- | |
>> vim compute-quota.yml | |
apiVersion: v1 | |
kind: ResourceQuota | |
metadata: | |
name: compute-quota | |
namespace: dev | |
spec: | |
hard: | |
pods: "10" | |
requests.cpu: "4" | |
requests.memory: 5Gi | |
limits.cpu: "10" | |
limits.memory: 10Gi | |
>> kubectl create -f compute-quota.yml | |
====================================================================================================================== | |
Configurations | |
~~~~~~~~~~~~~~ | |
Prerequisite - Commands and Arguments in Docker | |
----------------------------------------------- | |
Assume the docker file for the ubuntu-sleeper looks as follows. | |
FROM ubuntu | |
ENTRYPOINT ["sleep"] | |
CMD ["5"] | |
Create a pod which sleeps 10 seconds by default | |
----------------------------------------------- | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: ubuntu-sleeper-pod | |
spec: | |
containers: | |
- name: ubuntu | |
image: ubuntu | |
command: ["sleep"] <<====== Replaces the ENTRYPOINT command in dockerfile | |
args: ["10"] <<====== Replaces the CMD command in dockerfile | |
Create a pod with the ubuntu image to run a container to sleep for 5000 seconds | |
------------------------------------------------------------------------------- | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: ubuntu-sleeper-2 | |
spec: | |
containers: | |
- name: ubuntu | |
image: ubuntu | |
command: --| | |
- "sleep" |- This can also write like this --> command: ["sleep", "5000"] | |
- "5000" --| | |
Create a pod with given specs. By default it displays a 'blue' background. | |
Set the given cmd line arguments to change it to 'green' | |
------------------------------------------------------------------------- | |
>> vim pod-def.yaml | |
apiVersion: v1 Pod Name: webapp-green | |
kind: Pod Image: kodekloud/webapp-color | |
metadata: Command line arguments: --color=green | |
name: webapp-green | |
spec: | |
containers: | |
- name: simple-webapp | |
image: kodekloud/webapp-color | |
args: ["--color","green"] --> This can also write as follows args: | |
- "--color" | |
- "green" | |
====================================================================================================================== | |
Environment Variables | |
~~~~~~~~~~~~~~~~~~~~~ | |
ENV Variables in Kubernetes | |
--------------------------- | |
>> vim pod-def.yaml | |
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 <== ENV_NAME | |
value: pink <== ENV_VALUE | |
Other way of adding value from Configmaps and Secrets | |
----------------------------------------------------- | |
env: | |
- name: APP_COLOR | |
valueFrom: | |
configMapKeyRef: ==> CONFIGMAP | |
env: | |
- name: APP_COLOR | |
valueFrom: | |
secretKeyRef: ==> SECRETS | |
====================================================================================================================== | |
ConfigMaps | |
~~~~~~~~~~ | |
When you have lot of pod definiton files, it will become difficult to manage the environment data stored within the | |
query files. We can take this information out of pod definition file & manage it centrally using Configuration maps. | |
ConfigMaps are used to pass configuration data in the form of key value pairs in Kubernetes. | |
When a pod is created inject the config map into the pod, so the key value pairs that are available as env variable | |
for the application hosted inside the container in the pod. There are 2 phases involved in configuring ConfigMaps. | |
1. Create the ConfigMaps | |
2. Inject them into the pod | |
2 ways to create a ConfigMaps | |
a. The imperative way without using a ConfigMap definition file | |
b. The declarative way by using a ConfigMap definition file | |
If you donot wish to create a configmap definition file, you could simple use the kubectl create configmap command and | |
specify the required arguments. | |
Creating configmap using Imperative way >> kubectl create configmap <config-name> --from-literal=<key>=<value> | |
Eg. kubectl create configmap app-config --from-literal=APP_COLOR=blue | |
Note :: from-literal is used to specify the key value pair in the command itself. | |
Create configmap with muliple keyvalue pair >> kubectl create configmap <config-name> --from-literal=<key1>=<value1> | |
--from-literal=<key2>=<value2> | |
Eg. kubectl create configmap app-config --from-literal=APP_COLOR=blue | |
--from-literal=APP_MODE=prod | |
Create configmap with input data from a file >> kubectl create configmap <config-name> --from-file=<path_to_file> | |
Eg. kubectl create configmap app-config --from-file=app_config.properties | |
Creating configmap using Declarative way | |
---------------------------------------- | |
>> vim config-map.yaml | |
apiVersion: v1 | |
kind: ConfigMap | |
metadata: | |
name: app-config | |
data: | |
APP_COLOR: blue | |
APP_MODE: prod | |
>> kubectl create -f config-map.yaml | |
To view configmaps >> kubectl get configmaps | |
To describe configmaps >> kubectl describe configmaps | |
Injecting the configmap into pod | |
-------------------------------- | |
For the use the key 'envFrom' under the container info in your pod-def file | |
>> vim pod-def.yaml | |
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 | |
So the different ways to inject config data into pod are | |
-------------------------------------------------------- | |
1. Using ConfigMap to inject environment variables | |
envFrom: | |
- configMapRef: | |
name: app-config | |
2. Inject as single environment variable | |
env: | |
- name: APP_COLOR | |
valueFrom: | |
ConfigMapKeyRef: | |
name: app-config | |
key: APP_COLOR | |
3. Inject the whole data as files in a volume | |
volumes: | |
- name: app-config-volume | |
configMap: | |
name: app-config | |
====================================================================================================================== | |
Secrets | |
~~~~~~~ | |
. Secrets are similar to configmaps except they are stored in an encoded or hashed format. | |
. Secreats are used to store sensitive data such as passwords.. | |
There are 2 two steps involved in working with secrets | |
1. Create Secret | |
2. Inject them into pod | |
There are 2 ways to create a Secret | |
a. Imperative way | |
b. Declarative way | |
Creating Secrets using Imperative way >> kubectl create secret generic <secret-name> --from-literal=<key>=<value> | |
Eg. kubectl create secret generic app-secret --from-literal=DB_Host=mysql | |
Note :: from-literal is used to specify the key value pair in the command itself. | |
Create Secret with multi keyvalue pair >> kubectl create secret generic <secret-name> --from-literal=<key>=<value> | |
--from-literal=<key2>=<value2> | |
Eg. kubectl create secret generic app-secret --from-literal=DB_Host=mysql | |
--from-literal=DB_User=root | |
--from-literal=DB_Password=pwd | |
Create secret with i/p data from a file >> kubectl create secret generic <secret-name> --from-file=<path_to_file> | |
Eg. kubectl create secret generic app-secret --from-file=app_secret.properties | |
Creating secret using Declarative way >> kubectl create -f secret-data.yaml | |
>> vim secret-data.yaml | |
apiVersion: v1 | |
kind: Secret | |
metadata: | |
name: app-secret | |
data: | |
DB_Host: bXlzcWw= <-- Here the data is in encoded form, which was converted to | |
DB_User: cm9vdA== base 64 before passing it here and is explained below. | | |
DB_Password: cGFzd3Jk | | |
| | |
Convert data to base64 on linux host | | |
------------------------------------ <<-----| | |
>> echo -n 'mysql' | base64 --> bXlzcWw= | |
>> echo -n 'root' | base64 --> cm9vdA== | |
>> echo -n 'pwd' | base64 --> cGFzd3Jk | |
Convert encoded data to original value on linux host | |
---------------------------------------------------- | |
>> echo -n 'bXlzcWw=' | base64 --decode --> mysql | |
>> echo -n 'cm9vdA==' | base64 --decode --> root | |
>> echo -n 'cGFzd3Jk' | base64 --decode --> pwd | |
To view secrets >> kubectl get secrets | |
To view more info about a secret >> kubectl describe secrets | |
To view more info about a secret with value >> kubectl get secrets <secret_name> -o yaml | |
Eg. kubectl get secrets app_secret -o yaml | |
Injecting the secret into pod | |
-------------------------------- | |
For the use the key 'envFrom' under the container info in your pod-def file | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: simple-webapp-color | |
spec: | |
containers: | |
- name: simple-webapp-color | |
image: simple-webapp-color | |
ports: | |
- containerPort: 8080 | |
envFrom: | |
- secretRef: | |
name: app-secret | |
So the different ways to inject secret data into pod are | |
-------------------------------------------------------- | |
1. Using Secrets to inject environment variables | |
envFrom: | |
- secretRef: | |
name: app-secret | |
2. Inject secret as single environment variable | |
env: | |
- name: DB_Password --> Environment variable set | |
valueFrom: | |
SecretKeyRef: | |
name: app-secret --> Name of the secret we created | |
key: DB_Password --> Name of the key from the secret-data which we need to use. | |
3. Inject the whole secret as files in a volume | |
volumes: | |
- name: app-secret-volume | |
secret: | |
secretName: app-secret | |
If we are mounting secrets as a volume in the pod, each attribute in the secret is created as a file with the value of | |
the secret as its content. | |
In the above mentioned case three files get create since we have 3 attributes | |
>> ls /opt/app-secret-volumes | |
DB_Host DB_Password DB_User | |
>> cat /opt/app-secret-volumes/DB_Host | |
mysql | |
====================================================================================================================== | |
A note about Secrets | |
~~~~~~~~~~~~~~~~~~~~ | |
Remember that secrets encode data in base64 format. Anyone with the base64 encoded secret can easily decode it. As such | |
the secrets can be considered as not very safe. | |
The concept of safety of the Secrets is a bit confusing in Kubernetes. The kubernetes documentation page and a lot of | |
blogs out there refer to secrets as a "safer option" to store sensitive data. They are safer than storing in plain text | |
as they reduce the risk of accidentally exposing passwords and other sensitive data. In my opinion it's not the secret | |
itself that is safe, it is the practices around it. | |
Secrets are not encrypted, so it is not safer in that sense. However, some best practices around using secrets make it | |
safer. As in best practices like: | |
. Not checking-in secret object definition files to source code repositories. | |
. Enabling Encryption at Rest for Secrets so they are stored encrypted in ETCD. | |
Also the way kubernetes handles secrets. Such as: | |
. A secret is only sent to a node if a pod on that node requires it. | |
. Kubelet stores the secret into a tmpfs so that the secret is not written to disk storage. | |
. Once the Pod that depends on the secret is deleted, kubelet will delete its local copy of the secret data as well. | |
Read about the protections and risks of using secrets here | |
Having said that, there are other better ways of handling sensitive data like passwords in Kubernetes, such as using | |
tools like Helm Secrets, HashiCorp Vault. | |
Kubernetes Secret Ref Doc >> https://kubernetes.io/docs/concepts/configuration/secret/ | |
Encrypting Secret Data at Rest Ref Doc >> https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/ | |
Protection and Risk of using Secret Ref Doc >> https://kubernetes.io/docs/concepts/configuration/secret/#protections | |
====================================================================================================================== | |
Security Contexts | |
~~~~~~~~~~~~~~~~~ | |
When we run a docker container, you have the option to define a set of security standards such as 'ID of the user used | |
to run the container', the linux capabilities that can be added or removed form the container etc. | |
Eg1: docker container run --user=1001 ubuntu sleep 3600 | |
Eg2: docker container run --cap-add MAC_ADMIN ubuntu | |
This can be configured in kubernetes as well. In kubernetes containers are encapsulated in pods. You may choose to | |
configure the security settings at a container lever or at a pod level. | |
If you configure it at a pod level, the settings will carry over to all the container within the pod. | |
If you configure it at both pod & container level, the settings on the container will override the settings on the pod. | |
>> vim pod-def.yaml | |
Security Context on Pod Level Security Context on Container Level | |
----------------------------- ----------------------------------- | |
apiVersion: v1 apiVersion: v1 | |
kind: Pod kind: Pod | |
metadata: metadata: | |
name: web-pod name: web-pod | |
spec: spec: | |
securityContext: <=== POD LEVEL containers: | |
runAsUser: 1000 - name: ubuntu | |
containers: image: ubuntu | |
- name: ubuntu command: ["sleep", "3600"] | |
image: ubuntu securityContext: <=== CONTAINER LEVEL | |
command: ["sleep", "3600"] runAsUser: 1000 | |
capabilities: | |
add: ["MAC_ADMIN"] | |
Note : Capabilites are only supported at the container level and not at the POD level. | |
====================================================================================================================== | |
Service Account | |
~~~~~~~~~~~~~~~ | |
There are two types of accounts in Kubernetes - a user account and a service account. | |
. User account is used by humans, eg. for an administrator accessing the cluster to perform administrative tasks or a | |
developer accessing the cluster to deploy applications etc.. | |
. Service account could be an account used by an application to interact with a Kubernetes cluster. For example a | |
monitoring application like Prometheus is used as a service account to pull the Kubernetes API for performance | |
metrics. An automated build tool like Jenkin's uses service accounts to deploy applications on the Kubernetes | |
cluster. | |
Create Service Account | |
---------------------- | |
>> kubectl create serviceaccount <service-account-name> | |
Eg. kubectl create serviceaccount dashboard-sa | |
View Service Accounts | |
--------------------- | |
>> kubectl get serviceaccount | |
When the service account is created, it also creates a token automatically. The service account token is what must be | |
used by the external application while authenticating to the Kubernetes API. The token however is stored as a secret | |
object. | |
>> kubectl describe serviceaccount dashboard-sa | |
Name: dashboard-sa | |
Namespace: default | |
Labels: <none> | |
Image pull secrets: <none> | |
Mountable secrets: dashboard-sa-token-kbbdm | |
Tokens: dashboard-sa-token-kbbdm <===== In this case it's named dashboard-sa-token-kbbdm. | |
Events: <none> | |
So when a service account is created, it first creates the service account object and then generates a token for the | |
service account. It then creates a secret object and stores that token inside the secret object. The secret object is | |
then linked to the service account. | |
To view the token view of the secret object | |
------------------------------------------- | |
>> kubectl describe secret <secret-name> | |
Eg. kubectl describe secret dashboard-sa-token-kbbdm | |
You can create a service account, assign the right permissions using role-based access control mechanisms and export | |
your service account tokens and use it to configure your third party application to authenticate to the Kubernetes API. | |
But what if your third party application is hosted on the Kubernetes cluster itself. For example we can have the | |
Prometheus application deployed on the Kubernetes cluster itself. In that case this whole process of exporting the | |
service account token and configuring the third party application to use it can be made simple by automatically | |
mounting the service token secret as a volume inside the pod hosting the third party application. That way the token to | |
access the Kubernetes API is already placed inside the pod and can be easily read by the application. You don't have to | |
provide it manually. | |
If you go back and look at the list of service accounts, you will see that there is a default service account that | |
exists already. For every namespace in Kubernetes, a service account named "default" is automatically created. Each | |
namespace has its own default service account. Whenever a pod is created, the default service account and it's token | |
are automatically mounted to that pod as a volume mount. | |
>> kubectl get serviceaccount | |
NAME SECRETS AGE | |
default 1 218d | |
dashboard-sa 1 4d | |
For example we have a simple pod definition file that creates a pod using my custom Kubernetes dashboard image. We | |
haven't specified any secrets or Volume mounts in the definition file. However when the pod is created if you look at | |
the details of the pod by running the kubectl describe pod command you'll see that a volume is automatically created | |
from the secret named "default-token" which is in fact the secret containing the token for this default service | |
account. The secret token is mounted at location /var/run/secrets/kubernetes.io/service/account inside the pod. | |
So from inside the pod if you run the ls command to list the contents of the directory you will see the secret | |
monitored as three separate files. The one with the actual token is the file named "token". | |
>> kubectl exec -it <image-name> ls /var/run/secrets/kubernetes.io/serviceaccount | |
ca.crt namespace token | |
If you view contents of that file you will see the token to be used for accessing the Kubernetes API. A user can use a | |
different service account such as the one we created by modified the pod definition file to include a service account | |
field and specify the name of the new service account. | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: my-kubernetes-dashboard | |
spec: | |
containers: | |
- name: my-kubernetes-dashboard | |
image: my-kubernetes-dashboard | |
serviceAccount: dashboard-sa | |
Remember, you cannot edit the service account of an existing pod. You must delete and recreate the pod. However in case | |
of a deployment you will be able to get the service account as any changes to pod definition file will automatically | |
trigger a new rollout for the deployment. So the deployment will take care of deleting and recreating new pods with the | |
right service account. | |
When you look at the pod details now you'll see that the new service account is being used. So remember Kubernetes | |
automatically mount the default service account. | |
If you haven't explicitly specified any you may choose not to mount a service account automatically by setting the auto | |
mount service account token field to false in the pod spec section. | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: my-kubernetes-dashboard | |
spec: | |
containers: | |
- name: my-kubernetes-dashboard | |
image: my-kubernetes-dashboard | |
automountServiceAccountToken: false | |
====================================================================================================================== | |
Resource Requirement and Limits | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
. Each node in a kubernetes cluster has a set of CPU, Memory and Disk resources. | |
. Every pods consume certain amount of CPU, Memory and Disk spaces. | |
Resource requirement for each pod | |
--------------------------------- | |
By default, kubernetes assume a pod requires 0.5 CPU and 256 Mi of memory. This is known as the resource request for a | |
container, the minimum amount of CPU or memory requested by the container. | |
By default kubernetes sets a usage limit of 1 vCPU of CPU and 512 Mi of memory for a container. | |
You can modify these values by modifying them in your pod or definition files | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: simple-webapp-color | |
spec: | |
containers: | |
- name: simple-webapp-color | |
image: simple-webapp-color | |
ports: | |
- containerPort: 8080 | |
resources: | |
requests: | |
memory: "1Gi" (if in Mebibyte, it will be something like "256Mi") | |
cpu: 1 | |
limits: | |
memory: "2Gi" | |
cpu: 2 | |
1 count of cpu ==> 1 AWS vCPU | |
1 GCP Core | |
1 Azure Core | |
1 Hyperthread | |
====================================================================================================================== | |
Taints and Toleration : are only meant to restrict nodes from accepting certain pods. | |
~~~~~~~~~~~~~~~~~~~~~ | |
Taints(Applied on Nodes) | |
------ | |
>> kubectl taint nodes node-name key=value:taint-effect | |
Eg kubectl taint nodes node1 app=blue:NoSchedule | |
There are 3 taint-effect. | |
1. NoSchedule : means pods will not be scheduled on the node. | |
2. PreferNoSchedule : means system will try to avoid placing the pods on the node. | |
3. NoExecute : means that new pods will not be scheduled on the node and existing pods on the node if any will | |
be evicted if they do not tolerate the taint. | |
Toleration(Applied on Pods) | |
---------- | |
To add toleration to the pod, the pod definition file should have the section called 'tolerations' move the same values | |
used while creating the taint under this section. | |
>> vim pod-definition.yml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: myapp-pod | |
spec: | |
containers: | |
- name: nginx-container | |
image: nginx | |
tolerations: | |
- key: "app" ----> Note :: Remember to keep the values in quotes. | |
operator: "Equal" | |
value: "blue" | |
effect: "NoSchedule" | |
Master nodes in cluster is tainted by default with taint effect 'NoSchedule' during the setup to prevent application | |
pods running on it. | |
To verify this, execute the following command | |
--------------------------------------------- | |
>> kubectl describe node <name_of_master_node> | grep Taint | |
Taints: node-role.kubernetes.io/master:NoSchedule | |
Remove Taints(master node) | |
------------- | |
>> kubectl taint nodes master node-role.kubernetes.io/master:NoSchedule- | |
Remember taints & tolerations are only meant to restrict nodes from accepting certain pods. So remember taints and | |
tolerations does not tell the pod to go to a particular node. Instead it tells the node to only accept pods with | |
certain tolerations. | |
If your requirement is to restrict a pod to certain nodes it is achieved through a concept called as node affinity | |
====================================================================================================================== | |
Node Selectors | |
~~~~~~~~~~~~~~ | |
Node Selectors are used to make sure that pods run on a particular node. And it include 2 steps | |
Step 1. Label Node | |
Step 2. Create pods or deployments and add the label using 'nodeSelector' in the manifest file. | |
1. Label Nodes(applied on nodes) | |
-------------------------------- | |
>> kubectl label nodes <node-name> <label-key>=<label-value> | |
Eg kubectl label nodes node01 size=Large | |
2. While creating the pods, we need to apply the node selector on the pod by adding it on the definiton file. | |
------------------------------------------------------------------------------------------------------------- | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: myapp-pod | |
spec: | |
containers: | |
- name: data-processor | |
image: data-processor | |
nodeSelector: | |
size: Large | |
When the pod is created, it will be placed on node01 baced on the nodeSelector key value pair | |
====================================================================================================================== | |
Node Affinity | |
------------- | |
Primary purpose of node affinity feature is to ensure that pods are hosted on particular nodes. And it include 2 steps | |
Step 1. Label Node | |
Step 2. Create pods or deployments and add 'nodeAffinity' in its manifest file. | |
Types of nodeAffinity | |
--------------------- | |
1. requiredDuringSchedulingIgnoredDuringExecution (means the key value match should be checked during scheduling | |
2. preferredDuringSchedulingIgnoredDuringExecution and not during execution) | |
3. requiredDuringSchedulingRequiredDuringExecution | |
Step 1. Label Nodes(applied on nodes) | |
------------------------------------- | |
>> kubectl label nodes <node-name> <label-key>=<label-value> | |
Eg kubectl label nodes node01 size=Large | |
Step 2. Creating manifest file with nodeAffinity info | |
----------------------------------------------------- | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: myapp-pod | |
spec: | |
containers: | |
- name: data-processor | |
image: data-processor | |
affinity: | |
nodeAffinity: | |
requiredDuringSchedulingIgnoredDuringExecution: | |
nodeSelectorTerms: | |
- matchExpressions: | |
- key: size | |
operator: In (Available values for operators : In, NotIn, Exists, DoesNotExist) | |
values: (If you use the operator value 'Exists', in that case you dont need to pass values) | |
- Large | |
- Small | |
====================================================================================================================== | |
CERTIFICATION TIPS | |
~~~~~~~~~~~~~~~~~~ | |
Refer : https://www.linkedin.com/pulse/my-ckad-exam-experience-atharva-chauthaiwale/ | |
Refer : https://medium.com/@harioverhere/ckad-certified-kubernetes-application-developer-my-journey-3afb0901014 | |
Refer : https://github.com/lucassha/CKAD-resources | |
====================================================================================================================== | |
Multi Container PODS | |
~~~~~~~~~~~~~~~~~~~~ | |
Multi container PODs means means multiple containers with in a POD and they share the same lifecycle which means they | |
are created together and destroyed together. They share the same network space which means they can refer to each other | |
as localhost and they have access to the same storage volumes. This way they donot have to establish the volume sharing | |
or services between the pods to enable communication between them. | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: simple-webapp | |
spec: | |
containers: | |
- name: simple-webapp | |
image: simple-webapp | |
ports: | |
- containerPort: 8080 | |
- name: log-agent | |
image: log-agent | |
Multi Container PODs Design Pattern | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
There are 3 common patterns, when it comes to designing multi-container PODs | |
. SideCar | |
. Adapter | |
. Ambassador | |
====================================================================================================================== | |
Observability | |
~~~~~~~~~~~~~ | |
Readiness and Liveness Probes | |
----------------------------- | |
A POD has a pod status and some conditions. The POD status tells us where the POD is in its lifecycle. | |
When a POD is first created it is in a pending state. Once the POD scheduled, it goes into a container creating status | |
and once all the containers in a POD starts it goes into a running state | |
To see the state of POD conditions, Run the kubectl describe pod command and look for the conditions section. | |
By default, kubernetes assumes that as soon as the container is created, it is ready to serve user traffic. But if the | |
application within the container took longer to get ready, the service is unaware of it and sends traffic through as | |
the container is already in a ready state causing users to hit a POD that isn't yet running a live application. | |
What we need here is a way to tie the ready condition to the actual state of the application inside the container means | |
for the application to be ready. There are different ways that you can define if an application inside a container is | |
actually ready. You can set up different kinds of tests. It could be even the API server is up and running. So you | |
could run it a HTTP test to see if the API server response. In case of a database, you may test to see if a particular | |
TCP socket is listening or you may simply execute a command within the container to run a custom script that will exit | |
successfully if the application is ready. | |
So how do you configure that test. In the POD definition file, Add a new field called readiness probe and use the HTTP | |
get option. Specify the port and the ready API. Now when the container is created Kubernetes does not immediately set | |
the ready condition on the container to true. Instead it performs a test to see if the API responds positively. Until | |
then the service does not forward any traffic to the pod as it sees that the pod is not ready. | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: simple-webapp | |
labels: | |
name: simple-webapp | |
spec: | |
containers: | |
- name: simple-webapp | |
image: simple-webapp | |
ports: | |
- containerPorts: 8080 | |
readinessProbe: | |
httpGet: | |
path: /api/ready | |
port: 8080 | |
There are different ways a probe can be configured. | |
. For a http use httpGet option with the path and the port. | |
. For TCP use the tcpSocket option with the port | |
. For executing a command specify the exec option with the command and options in an array format. | |
HTTP Test : /api/ready TCP Test - 3306 Exec Command | |
---------------------- --------------- ------------ | |
readinessProbe: readinessProbe: readinessProbe: | |
httpGet: tcpSocket: exec: | |
path: /api/ready port: 3306 command: | |
port: 8080 - cat | |
- /app/is_ready | |
There are some additional options as well. | |
. If you know that your application will take a minimum of say 10 seconds to warm up, you can add an additional delay | |
to the probe using 'initialDelaySeconds' option. | |
. If you would like to specify how often to probe, you can do that using the 'periodSeconds' option. | |
. By default, if the application is not ready after three attempts,the probe will stop. If you would like to make more | |
attempts use the 'failureThreshold' option. | |
readinessProbe: | |
httpGet: | |
path: /api/ready | |
port: 8080 | |
initialDelaySeconds: 10 | |
periodSeconds: 5 | |
failureThreshold: 8 | |
====================================================================================================================== | |
Liveness Probes | |
~~~~~~~~~~~~~~~ | |
Assume an application is not really working but the container continues to stay alive. Say for example due to a bug in | |
the code the application is stuck in an infinite loop. As far as the kubernetes is concerned, the container is up so | |
the application is assumed to be up but the user hitting the container are not served. In that case the container needs | |
to be restarted or destroyed and a new container is to be brought up. That is where the liveness probe can help us. | |
A liveness probe can be configured on the container to periodically test whether the application within the container | |
is actually healthy. If the test fails, the container is considered unhealthy and is destroyed and recreated. But again | |
as a developer you get to define what it means for an application to be healthy. In case of a web application, it could | |
be when the API server is up and running. In case of database you may test to see if a particular TCP socket is | |
listening or you may simply execute a command to perform a test. The liveness is configured in the pod definition file | |
similar to readinessprobe. | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: simple-webapp | |
labels: | |
name: simple-webapp | |
spec: | |
containers: | |
- name: simple-webapp | |
image: simple-webapp | |
ports: | |
- containerPorts: 8080 | |
livenessProbe: | |
httpGet: | |
path: /api/healthy | |
port: 8080 | |
HTTP Test : /api/healthy TCP Test - 3306 Exec Command | |
------------------------ --------------- ------------ | |
livenessProbe: readinessProbe: readinessProbe: | |
httpGet: tcpSocket: exec: | |
path: /api/healthy port: 3306 command: | |
port: 8080 - cat | |
initialDelaySeconds: 10 - /app/is_ready | |
periodSeconds: 5 | |
failureThreshold: 8 | |
====================================================================================================================== | |
Container Logging | |
~~~~~~~~~~~~~~~~~ | |
>> kubectl logs -f <name_of_the_pod> | |
If you are running a multi pod container, then to see the logs, you should specify the container name also | |
>> kubectl logs -f <name_of_the_pod> <name_of_the_container> | |
====================================================================================================================== | |
Monitor and Debug Applications | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Metric Server is used as a monitoring tool in kubernetes. There will be one metric server per kubernetes cluster. The | |
Metrics Server retrieves metrics from each of the Kubernetes nodes and pods, aggregates them and store them in memory. | |
Note that the metric server is only an in-memory monitoring solution and does not store the metrics on the disk and as | |
a result you cannot see historical performance data. | |
So how were the metrics generated for the PODs on these nodes? | |
Kubernetes runs an agent on each node known as the kubelet which is responsible for receiving instructions | |
from the kubernetes API Master server and running PODs on the nodes. The Kubelet also contains a subcomponent known as | |
the cAdvisor or Container aAdvisor. cAdvisor is responsible for retrieving performance metrics from pods and exposing | |
them through the kubelet API to meet the metrics available for the metrics server. | |
If you're using minikube for your local cluster, run the command | |
>> minikube addons enable metrics-server. | |
For all other environments | |
. deploy the metric server by cloning the metric server deployment files from the Github repository | |
>> git clone https://github.com/kubernetes-incubator/metrics-server.git | |
. then deploying the required components using the kubectl create command. | |
>> kubectl create -f deploy/1.8+/ | |
This command deploys a set of pods, services and roles to enable the metric server to poll for performance metrics from | |
the nodes in the cluster. Once deployed, give the metrics server some time to collect and process data . | |
Once processed cluster performance can be viewed by running the command | |
>> kubectl top node (This provides the CPU and memory consumption of each of the nodes) | |
>> kubectl top pod (To view performance metrics of pods in Kubernetes.) | |
====================================================================================================================== | |
POD Design | |
~~~~~~~~~~ | |
Labels, Selectors and Annotations | |
--------------------------------- | |
. Labels and Selectors are a standard method to group things together. | |
. Labels are properties attached to each items. | |
. Selectors help to filter the items. | |
Each object in kubernetes (pods, replicaset, deployment etc) have their own labels and selectors. | |
In a pod definiton file under metadata, there is a section called labels. Under that add the labels in a key value | |
format. | |
>> vim pod-definition.yml(sample) | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: myapp-pod | |
labels: | |
app: App1 <---- | |
function: Front-end <---- | |
spec: | |
containers: | |
- name: nginx-container | |
image: nginx | |
To select a pod with the labels | |
------------------------------- | |
>> kubectl get pods --selector app=App1 | |
>> kubectl get pods --selector env=dev | |
To get pods with multiple labels(here we use 'prod' environment, finance bu and frontend tier) | |
-------------------------------- | |
>> kubectl get pods --selector env=prod,bu=finance,tier=frontend | |
To get all object with a specific label(here we use 'prod' environment) | |
--------------------------------------- | |
>> kubectl get all --selector env=prod | |
Annotations | |
----------- | |
Annotations are used to record other details for informatory purpose. For example, tool details like name, version, | |
build, information, etc. or contact details, phone numbers, email IDs, etc. that may be used for some kind of | |
integration purpose. | |
>> vim pod--def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: annotations-demo | |
annotations: | |
buildversion: 1.34 | |
spec: | |
containers: | |
- name: nginx | |
image: nginx:1.7.9 | |
ports: | |
- containerPort: 80 | |
===================================================================================================================== | |
Rolling Updates & Rollbacks in Deployments | |
------------------------------------------ | |
Rollout Status >> kubectl rollout status <deployment-name> | |
Eg. kubectl rollout status deployment/myapp-deployment | |
Rollout History >> kubectl rollout history <deployment-name> | |
Eg. kubectl rollout history deployment/myapp-deployment | |
Deployment Strategy | |
------------------- | |
. Recreate Strategy : Bring down all the running pods first and then bring up the new pods, causing some downtime. | |
. Rolling Update Strategy(default) : Bring down old pods and up new pods simultaneously results in no downtime | |
Upgrading your Deployments | |
-------------------------- | |
Update includes updating the application version, updating the version of docker container used, updating the labels or | |
updating the number of replicas etc.Since we have deployment-definition file, update the changes in it & then execute | |
the updation using the following cmd. | |
Kubectl apply >> kubectl apply -f deployment-definition.yml | |
Another way of doing this, say for eg : we need to update the nginx image version from 1.7.1 to 1.9.1 | |
>> kubectl set image <deployment_name> <container_name>=image:new_version | |
>> kubectl set image deployment/myapp-deployment nginx=nginx:1.9.1 | |
Note : This won't update the definition file, so who ever usig it might be careful about the updates if we use | |
kubectl set commands. | |
Rollback / Undo a change >> kubectl rollout undo <deployment-name> | |
Eg. kubectl rollout undo deployment/myapp-deployment | |
create deployment using kubectl run >> kubectl run nginx --image=nginx | |
This will create the deployment, replica set and pods. | |
Kubernetes Deployment command summary | |
------------------------------------- | |
Create >> kubectl create -f deployment-definition.yml | |
>> kubectl create -f deployment-definition.yml --record | |
(this will help to record the rollout history) | |
Get >> kubectl get deployments | |
Update >> kubectl edit deployment deployment_name | |
Update >> kubectl apply -f deplyment-definiton.yml | |
(Do this after updating definiton file) | |
Update >> kubectl set image deployment/myapp nginx=nginx:1.9.1 | |
(Used without updating definition file) | |
Status >> kubectl rollout status deployment/myapp | |
>> kubectl rollout history deployment/myapp | |
>> kubectl rollout history deployment/myapp --revision=1 | |
Rollback >> kubectl rollout undo deployment/myapp | |
Rollback to specific revision >> kubectl rollout undo deployment/myapp --to-revision=version_number | |
===================================================================================================================== | |
Jobs & CronJobs | |
~~~~~~~~~~~~~~~ | |
A Job is used to run a set of pods to perform a given task to completion. | |
>> vim job-def.yaml | |
apiVersion: batch/v1 | |
kind: Job | |
metadata: | |
name: math-add-job | |
spec: | |
template: | |
spec: | |
containers: | |
- name: math-add | |
image: ubuntu | |
command: ["expr", "1", "+", "2"] | |
restartPolicy: Never | |
>> kubectl create -f job-def.yaml | |
To see the newly created jobs >> kubectl get jobs | |
To see the pods >> kubectl get pods | |
To delete the job >> kubectl delete job <job_name> | |
Creating jobs will result in the creation of pods and deletion of job results in deletion of pods also. | |
. To run multiple pods, we set a value for 'completions' under the job 'spec' section. | |
. By default the jobs are done sequentially and so do the pods also. Instead of getting the pods created sequentially, | |
we can get them created in parallel. For this, add a property called parallelism under job 'spec' section | |
>> vim job-def.yaml | |
apiVersion: batch/v1 | |
kind: Job | |
metadata: | |
name: math-add-job | |
spec: | |
completions: 3 ==> this will result in 3 job creation sequentially by default | |
parallelism: 3 ==> this will override the default sequential behaviour | |
template: | |
spec: | |
containers: | |
- name: math-add | |
image: ubuntu | |
command: ["expr", "1", "+", "2"] | |
restartPolicy: Never | |
>> kubectl create -f job-def.yaml | |
To see the newly created jobs >> kubectl get jobs | |
To see the pods >> kubectl get pods | |
To delete the job >> kubectl delete job <job_name> | |
Cronjobs | |
-------- | |
A CronJob is a job that can be scheduled just like Cron tab in Linux. If you're familiar with it, say for example you | |
have a job that generates a report and sends an email. You can create the job using the Kube control create command, | |
but it runs instantly. Instead you could create a Cronjob to schedule and run it periodically. | |
>> vim cron-job-def.yaml | |
apiVersion: batch/v1beta1 | |
kind: CronJob | |
metadata: | |
name: reporting-cron-job | |
spec: ==> Spec for CronJob | |
schedule: "*/1 * * * *" | |
jobTemplate: | |
spec: ==> Spec for Job | |
completions: 3 | |
parallelism: 3 | |
backoffLimit: 100 # This is so the job does not quit before it succeeds. | |
template: | |
spec: ==> Spec for Pod | |
containers: | |
- name: reporting-tool | |
image: reporting-tool | |
restartPolicy: Never | |
>> kubectl create -f cron-job-def.yaml | |
To see the newly created cron jobs >> kubectl get cronjob | |
Schedule Syntax | |
--------------- | |
* * * * * command to execute | |
| | | | | | |
| | | | |-------> day of the week (0 - 6) (Sunday to Saturday, 7 is also Sunday on some systems.) | |
| | | |-----------> month (1 -12) | |
| | |---------------> day of the month (1 - 31) | |
| |-------------------> hour (0 - 23) | |
|-----------------------> minute (0 - 59) | |
===================================================================================================================== | |
Services and Networking | |
~~~~~~~~~~~~~~~~~~~~~~~ | |
Services | |
-------- | |
. Kubernetes services enable communication between various components within and outside of the application | |
. Kubernetes service is an object just like pods, replica set or deployments. | |
. One of its usecase is to listen to a port on the node and forward request on that port to a port on the pod running | |
the web application. This type of service is known as a NODE PORT service because the service listens to a port on | |
the node and forward requests to the pods. | |
Services Types | |
-------------- | |
3 Types. | |
- Node Port : where the service makes an internal pod accessible on a port on the node. | |
- Cluster IP : In this case, the service creates a virtual IP inside the cluster to enable communication between | |
different services such as a set of front end servers to a set of back end servers. | |
- Load Balancer : where it provisions a load balancer for our application in supported cloud providers. | |
NodePort: Exposes the service on each Node’s IP at a static port (the NodePort). A ClusterIP service, to which the | |
NodePort service will route, is automatically created. You’ll be able to contact the NodePort service, from outside the | |
cluster, by requesting <NodeIP>:<NodePort>. | |
ClusterIP: Exposes the service on a cluster-internal IP. Choosing this value makes the service only reachable from | |
within the cluster. This is the default ServiceType | |
LoadBalancer: Exposes the service externally using a cloud provider’s load balancer. NodePort & ClusterIP services, to | |
which the external load balancer will route, are automatically created. | |
Service - Node Port | |
------------------- | |
There are 3 ports involved. | |
- Target port : is the port on the pod. | |
- Port : is the port on the service itself | |
- Node port : is the port on the node itself, which we used to access the web server externally. | |
Its default value ranges from 30000 to 32767. | |
_____________________________________________________________ | |
| | | |
| | | |
|-----| ----------------- ------------- | | |
NODEPORT --> |30008|-*-*-*-| SERVICE | 80 |-*-*-*-*-*-| 80 | POD | | | |
|-----| ----------------- ------------- | | |
| ^ ^ | | |
| | | | | |
| PORT TARGETPORT | | |
| NODE | | |
|_____________________________________________________________| | |
>> vim service-definiton.yml | |
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: myapp-service | |
spec: | |
type: NodePort # ClusterIP(default), NodePort or LodeBalancer | |
ports: | |
- port: 80 # Port is a mandatory field | |
targetPort: 80 # If we dont allocate a port, it will assume a value same as port | |
nodePort: 30008 # Value ranges between 30000 to 32767. if we dont allocate, then a value with in the | |
range is automatically assigned. | |
selector: | |
app: myapp # Use the labels from pod definition file | |
type: frontend-app | |
Create Service >> kubectl create -f service-definiton.yml | |
To view the Service >> kubectl get services | |
====================================================================================================================== | |
Service - ClusterIP | |
------------------- | |
>> vim service-definiton.yml | |
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: back-end | |
spec: | |
type: ClusterIP # Default type is ClusterIP. So even if you dont specify it, it will be the default | |
ports: | |
- port: 80 # port is where the service is exposed | |
targetPort: 80 # targetPort is the port where the backend is exposed | |
selector: | |
app: myapp # To link the service to set of pods, we use selector | |
type: back-end | |
====================================================================================================================== | |
Ingress Networking | |
~~~~~~~~~~~~~~~~~~ | |
Ingress helps users access the application using a single externally accessible URL that you can configure to route to | |
different services within your cluster based on that URL path and at the same time implement SSL security as well. | |
Implementation includes 2 step. | |
1. Deploy Ingress Controller (Eg. NginX) | |
2. Configure Ingress Resources | |
Ingress resources are created using definition files, like the ones we use to create pods deployments and services. | |
Kubernetes cluster does not come with an ingress controller by default. So we must deploy one. Eg for ingress | |
controller are GCE(Google Cloud HTTP Loadbalancer), Nginx, Contour, HAPROXY, traefik, Istio.Out of this, GCE and NGINX | |
are currently being supported and maintained by the Kubernetes project | |
These ingress controllers are not just another load balancer or NGINX server. The load balancer components are just a | |
part of it. The ingress controllers have additional intelligence built into them to monitor the Kubernetes cluster for | |
new definitions or ingress resources and configure the NGINX server accordingly. | |
An NGINX controller is deployed as just another deployment in Kubernetes. So we start with their deployment definition | |
file named nginx-ingress-controller with one replica and a simple pod definition template. We will label it NGINX | |
ingress and the image used is nginx-ingress-controller with the right version. This is a special build of NGINX built | |
specifically to be used as an ingress controller in Kubernetes. The NGINX program is stored at location in | |
nginx-ingress-controller. So you must pass that as the command to start the nginx-controller service. | |
====================================================================================================================== | |
1. Deploy Ingress Controller | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Nginx Controlled is deployed as a deployment | |
-------------------------------------------- | |
>> vim nginx-controller.yaml | |
apiVersion: apps/v1 | |
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-controller | |
- --configmap=$(POD_NAMESPACE)/nginx-configuration ==> to store and configure logs, ssl-protocols... | |
env: | |
- name: POD_NAME | |
valueFrom: | |
fieldRef: | |
fieldPath: metadata.name | |
- name: POD_NAMESPACE | |
valueFrom: | |
fieldRef: | |
fieldPath: metadata.namespace | |
ports: ==> the ports used by the ingress controller : 80 and 443. | |
- name: http | |
containerPort: 80 | |
- name: https | |
containerPort: 443 | |
Configmap to feed nginx configuration ==> to store and configure logs, ssl-protocols... | |
-------------------------------------- | |
>> vim ingress-configmap-def.yaml | |
apiVersion: v1 | |
kind: ConfigMap | |
metadata: | |
name: nginx-configuration | |
Servie needed to expose the ingress controller to external world | |
---------------------------------------------------------------- | |
>> vim ingress-service-def.yaml | |
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 controller also require Service Account with right set of permissions to access all objects | |
--------------------------------------------------------------------------------------------------- | |
>> vim ingress-service-account-def.yaml | |
apiVersion: v1 | |
kind: ServiceAccount | |
metadata: | |
name: nginx-ingress-serviceaccount | |
====================================================================================================================== | |
2. Creating and Configuring Ingress Resources | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Ingress resources is a set of rules and configurations applied on the ingress controller. You configure rules to say | |
simply forward all incoming traffic to a single application or route traffic to different application based on the | |
url. So if a user goes to my-online-store.com/wear, then route to one app, or if the user visit the /watch url, then | |
route on to the video app. Or you could route user based on the domain name itself. For example if the user visits | |
wear.my-online-store.com then route the user to the wear application or else route him to the video app. | |
Lets look how to configure this in detail. | |
The ingress resource is created with kubernetes definition file. In this case, ingress-wear.yaml. | |
-------------------- | |
>> vim ingress-wear.yaml | |
apiVersion: extensions/v1beta1 | |
kind: Ingress | |
metadata: | |
name: ingress-wear | |
spec: | |
backend: | |
serviceName: wear-service (If it is single backend, you don't have any rules. you simple specify the service name | |
servicePort: 80 and port of the backend wear-service) | |
>> kubectl create -f ingress-wear.yaml | |
To view the created ingress resources | |
------------------------------------- | |
>> kubectl get ingress | |
We need to use rules, when we need to route based on conditions. | |
Assume we need to create ingress-resource based on url path (rules) | |
------------------------------------------------------------------- | |
>> vim ingress-wear.yaml | |
apiVersion: extensions/v1beta1 | |
kind: Ingress | |
metadata: | |
name: ingress-wear-watch | |
spec: | |
rules: | |
- http: | |
paths: | |
- path: /wear (Rule based on path : one for /wear.) | |
backend: | |
serviceName: wear-service | |
servicePort: 80 | |
- path: /watch (Rule based on path : one for /watch.) | |
backend: | |
serviceName: watch-service | |
servicePort: 80 | |
>> kubectl create -f ingress-wear.yaml | |
Once the ingress resource is created, view additional details abpout the ingress resource using the followong cmd | |
>> kubectl describe ingress ingress-wear-watch | |
In the o/p there is 'Default backend' ==> this need to be the backend redirect when the routes won't match. means right | |
now we have /wear and /watch routes. Say if we entered the /eat which is not a valid one, then we need to show the 404 | |
page that need to be configured here. | |
The third type of configuration is using domain names or host names. Say we have the following domain names | |
wear.my-online-store.com | |
watch.my-online-store.com | |
Assume we need to create ingress-resource based on domain names (rules) | |
------------------------------------------------------------------- | |
>> vim ingress-wear-watch.yaml | |
apiVersion: extensions/v1beta1 | |
kind: Ingress | |
metadata: | |
name: ingress-wear-watch | |
spec: | |
rules: | |
- host: wear.my-online-store.com (Rule based on domain name / host name : one for each hostname.) | |
http: | |
paths: | |
- backend: | |
serviceName: wear-service | |
servicePort: 80 | |
- host: watch.my-online-store.com (Rule based on domain name / host name : one for each hostname.) | |
http: | |
paths: | |
- backend: | |
serviceName: watch-service | |
servicePort: 80 | |
>> kubectl create -f ingress-wear-watch.yaml | |
So let's compare the two. Splitting traffic by URL, had just one rule and we split the traffic with two parts to split | |
traffic by hostname. We used two rules and one path specification in each rule. | |
Edit Ingress Resource | |
--------------------- | |
>> kubectl edit ingress <ingress_resource_name> -n <namespace_name> | |
===================================================================================================================== | |
FAQ - What is the rewrite-target option? | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Refer :: CKAD udemy course. | |
===================================================================================================================== | |
Network Policy | |
~~~~~~~~~~~~~~ | |
Ingress and Egress Traffic | |
-------------------------- | |
. An incoming request to a server is considered as Ingress Traffic | |
. An outgoing request from a server is considered as Egress Traffic | |
Say we have a web pod, api pod and db pod in a cluster. And if you want to block the web pod talking to db pod directly | |
and make it happen only via the api pod. Thats where we use the network policy to allow traffic to the DB Server only | |
from the API Server. | |
Network Policy is just another object in kubernetes namespace, just like pods, replicasets or services. | |
How do we apply network policy on a pod? | |
. Using labels and selectors. | |
|---------------------| |---------------------| |--------------------| | |
End User --> | Web POD (Port 80) | --> | API POD (Port 5000) | --> | DB POD (Port 3306) | | |
|---------------------| |---------------------| |--------------------| | |
>> vim network-policy-def.yaml | |
apiVersion: networking.k8s.io/v1 | |
kind: NetworkPolicy | |
metadata: | |
name: db-policy | |
spec: | |
podSelector: --> You should use the 'labels' from the pod for which you are writing the network policy. | |
matchLabels: | |
role: db | |
policyTypes: | |
- Ingress | |
ingress: | |
- from: | |
- podSelector: | |
matchLabels: | |
name: api-pod | |
ports: | |
- protocols: TCP | |
port 3306 | |
>> kubectl create -f network-policy-def.yaml | |
Make sure that you labeld the DB pod as follows before creaing the network policy. | |
labels: OR labels: | |
roles: db names: db | |
Note : | |
Solutions that support Network Policies are : Solutions that donot support Network Policies are : | |
- Kube-router - Flannel | |
- Calico | |
- Romana | |
- Wave-net | |
Get list of network policy >> kubectl get networkpolicy | |
SAMPLE | |
------ | |
apiVersion: networking.k8s.io/v1 | |
kind: NetworkPolicy | |
metadata: | |
name: internal-policy | |
namespace: default | |
spec: | |
podSelector: | |
matchLabels: | |
name: internal | |
policyTypes: | |
- Egress | |
- Ingress | |
ingress: | |
- {} | |
egress: | |
- to: | |
- podSelector: | |
matchLabels: | |
name: mysql | |
ports: | |
- protocol: TCP | |
port: 3306 | |
- to: | |
- podSelector: | |
matchLabels: | |
name: payroll | |
ports: | |
- protocol: TCP | |
port: 8080 | |
- ports: | |
- port: 53 | |
protocol: UDP | |
- port: 53 | |
protocol: TCP | |
===================================================================================================================== | |
Developing network policies | |
~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
Watch CKAD udemy video. | |
===================================================================================================================== | |
State Persistence | |
~~~~~~~~~~~~~~~~~~ | |
Volumes | |
------- | |
Just as in Docker, the PODs created in kubernetes are transient in nature. When a POD is created to process data and | |
then deleted, the data processed by it gets deleted as well. For this we attach a volume to the POD. The data generated | |
by the POD is now stored in the volume. And even after the pod is deleted the data remains. | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: random-number-generator | |
spec: | |
containers: | |
- name: alpine | |
image: alpine | |
command: ["/bin/sh", "-c"] | |
args: ["shuf -i 0-100 in 1 >> /opt/number.out"] | |
volumeMounts: | |
- name: data-volume | |
mountPath: /opt <==== on the pod/container | |
volumes: | |
- name: data-volume | |
hostPath: | |
path: /data <==== on the host | |
type: Directory | |
When we have multiple nodes, we need to use one of the following tech to handle this. | |
. NFS . scaleio . FibreChannel | |
. GlusterFS . ceph . FlockerDisk | |
. public cloud solutions like AWS EBS, Azure disk or Google's Persistent | |
For eg. To configure an AWS Elastic Block Store volume as the storage or the volume, we need to replace the hostPAth | |
field of the volume with awsElasticBlockStore field along with the volume id | |
volumes: | |
- name: data-volume | |
awsElasticBlockStore: | |
volumeID: <volume_id> | |
fsType: ext4 (file system type) | |
===================================================================================================================== | |
Persistent Volumes | |
------------------ | |
A Persistent volume is a cluster wide pool of storage volumes configured by an administrator to be used by users | |
deploying applications on the cluster. The users can now select storage from this pool using persistent volume claims. | |
Creating Persistent Volume >> kubectl create -f pv-def.yaml | |
-------------------------- | |
>> vim pv-def.yaml | |
apiVersion: v1 | |
kind: PersistentVolume | |
metadata: | |
name: pv-vol1 | |
spec: | |
accessModifiers: | |
- ReadWriteOnce ---> Possible values :: ReadOnlyMany, ReadWriteOnce, ReadWriteMany | |
capacity: | |
storage: 1Gi | |
hostPath: | |
path: /tmp/data ---> This option is not to be used in a production environment. | |
List the Persistent Volume | |
-------------------------- | |
>> kubectl get persistentVolumes | |
>> kubectl get pv | |
Replace the hostPath option with one of the supported storage solutions like awsElasticBlockStore | |
------------------------------------------------------------------------------------------------- | |
>> vim pv-def.yaml | |
apiVersion: v1 | |
kind: PersistentVolume | |
metadata: | |
name: pv-vol1 | |
spec: | |
accessModes: | |
- ReadWriteOnce ---> Possible values :: ReadOnlyMany, ReadWriteOnce, ReadWriteMany | |
capacity: | |
storage: 1Gi | |
awsElasticBlockStore: | |
volumeID: <volume_id> | |
fsType: ext4 | |
===================================================================================================================== | |
Persistent Volume Claims | |
~~~~~~~~~~~~~~~~~~~~~~~~ | |
. Persistent Volume and PersistentVolumeClaims are two seperate objects in the Kubernetes namespace. | |
. An administrator creates a set of Persistent Volumes and a user creates Persistent Volume Claims to use the storage. | |
. Once the PersistentVolumeClaims are created, kubernetes binds the PersistentVolumes to Claims based on the request | |
and properties set on the volume. | |
. Every PersistentVolumeClaims is bound to single PersistentVolume. | |
. During the binding process, Kubernetes tries to find a PersistentVolume that has sufficient capaccity as requested by | |
the claim and any other request properties such as access modes, volume modes, storage class etc. | |
. However if there are multiple possible matches for a single claim and you would like to use a particular volume, you | |
could still use labels and selectors to bind to the right volumes. | |
. Also note that a smaller claims may get bound to a larger volume if all other criteria matches and there are no | |
better options. | |
. Also if there are no voulmes available, then the persistent volume claims will remain in a 'pending' state until | |
newer volumes are made available to the cluster. | |
. Once new volumes are available, the claim would automatically bound to the newly available volume. | |
Creating PVC | |
------------ | |
>> vim pvc-def.yaml | |
apiVersion: v1 | |
kind: PersistentVolumeClaim | |
metadata: | |
name: myclaim | |
spec: | |
accessModes: | |
- ReadWriteOnce | |
resources: | |
requests: | |
storage: 500Mi | |
>> kubectl create -f pvc-def.yaml | |
Get PersistentVolumeClaim | |
------------------------- | |
>> kubectl get persistentvolumeclaim (will be in pending state until a persistent volume is bound) | |
>> kubectl get pvc | |
Delete PersistentVolumeClaim | |
---------------------------- | |
>> kubectl delete persistentvolumeclaim <pvc_name> | |
>> kubectl delete pvc <pvc_name> | |
Eg. kubectl delete persistentvolumeclaim myclaim | |
Eg. kubectl delete pvc myclaim | |
When a PVC is deleted, by default the persistent volume is retained and it cannot be used with any other PVC. | |
persistentVolumeReclaimPolicy: Retain => means pv will remail until it is manually deleted and cannot be reused with | |
any other claims. | |
persistentVolumeReclaimPolicy: Delete => means pv will be deleted when the pvc is deleted. thus freeing up storage on | |
the storage device. | |
persistentVolumeReclaimPolicy: Recycle => means data in the pv will be scrubbed before it is made available to | |
another claims. | |
===================================================================================================================== | |
Using PVCs in PODs | |
~~~~~~~~~~~~~~~~~~ | |
Once you create a PVC use it in a POD definition file by specifying the PVC Claim name under persistentVolumeClaim | |
section in the volumes section like this: | |
>> vim pod-def.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: mypod | |
spec: | |
containers: | |
- name: myfrontend | |
image: nginx | |
volumeMounts: | |
- mountPath: "/var/www/html" | |
name: mypd | |
volumes: | |
- name: mypd | |
persistentVolumeClaim: | |
claimName: myclaim | |
The same is true for ReplicaSets or Deployments. Add this to the pod template section of a Deployment on ReplicaSet. | |
Ref : https://kubernetes.io/docs/concepts/storage/persistent-volumes/#claims-as-volumes | |
===================================================================================================================== | |
Storage Classes | |
~~~~~~~~~~~~~~~ | |
We use storage classes to provision the volumes automatically when the application requires it. With storage classes, you | |
can define a provisioner such as Google storage that can automatically provision storage on Google Cloud and attach that to | |
pods when a claim is made. That's called dynamic provisioning of volumes. | |
So once the storage class is created and when a user creates PVC, the storage class create PV dynamically and the PVC get | |
bound to the volume. | |
We used the GCE provisionar to create a volume on GCP. There are many other provisioner such as for AWSElastic Block Store, | |
AzureFile, AzureDisk, CephFS, Portworx, ScaleIO etc. | |
With each of these provisioner you can pass in additional parameters such as the type of desk to provision, app type etc. | |
Create StorageClass | |
------------------- | |
>> vi sc.yaml | |
apiVersion: storage.k8s.io/v1 | |
kind: StorageClass | |
metadata: | |
name: delayed-volume-sc | |
provisioner: kubernetes.io/no-provisioner | |
reclaimPolicy: Delete | |
allowVolumeExpansion: true | |
volumeBindingMode: WaitForFirstConsumer | |
This will create pv by itselt and wait for a user to create a pvc and pod using that pvc to bind it. So we dont need to | |
create PV but we should create PVC and deploy a POD using that PVC. | |
Creating PVC | |
------------- | |
>> vim pvc.yaml | |
apiVersion: v1 | |
kind: PersistentVolumeClaim | |
metadata: | |
name: local-pvc <---------- | |
spec: | |
accessModes: | |
- ReadWriteOnce | |
resources: | |
requests: | |
storage: 500Mi | |
storageClassName: local-storage | |
Creating Pod using PVC | |
---------------------- | |
>> vi nginx_pod.yaml | |
apiVersion: v1 | |
kind: Pod | |
metadata: | |
name: nginx | |
spec: | |
volumes: | |
- name: task-pv-storage | |
persistentVolumeClaim: | |
claimName: local-pvc <---------- | |
containers: | |
- name: nginx | |
image: nginx:alpine | |
volumeMounts: | |
- mountPath: "/var/www/html" | |
name: task-pv-storage | |
===================================================================================================================== | |
Stateful Sets | |
~~~~~~~~~~~~~ | |
Stateful Sets is used in place where we need to deploy pods sequentially, means second pod is created only after the | |
first pod is up and running. Also they are named in a unique way. eg: pod-01, pod-02 like that. | |
>> vim statefulset-def.yaml | |
apiVersion: apps/v1 | |
kind: StatefulSet | |
metadata: | |
name: mysql | |
spec: | |
template: | |
metadata: | |
labels: | |
app: mysql | |
spec: | |
containers: | |
- name: mysql | |
image: mysql | |
replicas: 3 | |
selector: | |
matchLabels: | |
app: mysql | |
serviceName: mysql-h ===> Headless service | |
podManagementPolicy: Parallel ===> Mentioned about this just before end of this section | |
>> kubectl create -f statefuleset-def.yaml | |
Just like how you created a deployment create a deployment definition file with a pod definition template inside it and | |
then all you need to do is change the kind to state full set instead of deployment. Note that both S's in the | |
statefullset is uppercase. Apart from that a statefullset also requires a service name specified You must specify the | |
name of a headless service. | |
When you create a state full set using this definition file it creates pods one after the other that's ordered graceful | |
deployment. Now each pod gets a stable unique DNS record on the network that any other application can use to access a | |
pod. | |
When we scale the state full set, it scales in an ordered graceful fashion where each pod comes up becomes ready and | |
only then the next one comes up. | |
Scale up/down StatefulSet | |
------------------------- | |
>> kubectl scale statefulset mysql --replicas=5 (Up) | |
>> kubectl scale statefulset mysql --replicas=2 (Down) | |
This helps when you want to scale MySQL databases, as each new instance can clone from the previous instance. It works | |
in the reverse order, when you scale it down. The last instance is removed first followed by the second last one. | |
Delete StatefulSet | |
------------------ | |
>> kubectl delete statefulset mysql | |
The same is true on termination. When you delete a state full set, the pods are deleted in the reverse order. That's | |
the default behavior of a statefullset but you can override that behavior to cause statefullset to not follow an order | |
launch but still have the other benefits of statefullset such as a stable and unique network id. | |
For that you could set the podManagementPolicy field to parallel to instruct state full set to not follow an ordered | |
approach, instead deploy all pods in parallel. The default value of this field is ordered ready. | |
===================================================================================================================== | |
Headless Services | |
~~~~~~~~~~~~~~~~~ | |
Ref :: https://www.udemy.com/course/certified-kubernetes-application-developer/learn/lecture/17478630#content | |
A headless service is a service created like a normal service. But it does not have an IP of its own like a cluster IP | |
for a normal service. It does not perform any load balancing. All it does is create DNS entries for each pod using the | |
pod name and a subdomain. So when you create a headless service say with the name my mysql-h, each pod gets a DNS | |
record created in the form podname.headless-servicename.namespace followed by the cluster domain. | |
ie: mysql-0.mysql-h.default.svc.cluster.local, mysql-1.mysql-h.default.svc.cluster.local etc | |
Create a headless service | |
------------------------- | |
>> vim headless-service.yaml | |
apiVersion: v1 | |
kind: Service | |
metadata: | |
name: mysql-h <================| | |
spec: | | |
ports: | |
- port: 3306 | |
selector: | |
app: mysql | |
clusterIP: None | |
What differentiates a headless service from a normal service is setting cluster IP to none. | |
When the headless service is created, the DNS entries are created for pods only if the two conditions are met while | |
creating the pod.Under the specs section of a pod definition file. You have two optional fields hostname and subdomain. | |
You must specify the subdomain to a value the same as the name of the service. When you do that it creates a DNS record | |
for the name of the service to point to the pod. However it still does not create a record for individual pods. For | |
that you must specify the hostname option in the pod definition file. Only then does it create a DNS record with a pod | |
name as well. So those are two properties that are required on the part for a headless service to create a DNS record | |
for the pod. | |
When creating a statefullset, you do not need to specify a subdomain or hostname. The statefullset automatically | |
assigns the right hostname for each pod based on the pod name and it automatically assigns the right subdomain based on | |
the headless service name | |
But wait How does a state full set know which headless service we are using. There could be many headless services | |
created for different applications. How would it know which is the headless service we created for this application. | |
When creating a state full set you must specify the service name explicitly using the service name option | |
in the statefullset definition file. That's how it knows what subdomain to assign to the pod. | |
The statefulset takes the names that we specified and add that to as a subdomain property when the pod is created. All | |
pods now get a separate DNS record created. | |
>> vim statefulset-def.yaml | |
apiVersion: apps/v1 | |
kind: StatefulSet | |
metadata: | |
name: mysql | |
labels: | |
app: mysql | |
spec: | | |
serviceName: mysql-h <=======| | |
replicas: 3 | |
matchLabels: | |
app: mysql | |
template: | |
metadata: | |
name: myapp-pod | |
labels: | |
app: mysql | |
spec: | |
containers: | |
- name: mysql | |
image: mysql | |
===================================================================================================================== | |
Storage in StatefulSet | |
~~~~~~~~~~~~~~~~~~~~~~ | |
ref :: https://www.udemy.com/course/certified-kubernetes-application-developer/learn/lecture/17478686#content | |
===================================================================================================================== |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment