Preparation for the Certified Kubernetes Application Developer V1.29 [CNCF] exam
1- Pod Design 20%
- Deployments, Rolling updates and Rollbacks
- Jobs and CronJobs
- Labels, Selectors and annotations
2- Configuration 18%
- ConfigMaps
- SecurityContexts
- Resource requirements
- Secrets
- ServiceAccounts
3- Observability 18%
- Probes (Liveness, Readiness) #Liveness
- Container Logging
- Application Monitoring
- Debugging
4- Core concepts 13%
- API primitives
Objects
- Basic pod creation and configuration
5- Services & Networking 13%
- Understand Services & Network Policies
6- Multi container pods 10%
- Ambassador
- Adapter
- Sidecar
7- State Persistence 8%
- PVs & PVCs
- Enable auto completion
source <(kubectl completion bash)
# To do it persistently store it in .bashrc
echo "source <(kubectl completion bash)" >> ~/.bashrc
# Handking k autocompletion https://github.com/kubernetes/kubectl/issues/120#issuecomment-344137671
source <(kubectl completion bash | sed 's/kubectl/k/g')
# alias kubectl to k (from https://github.com/krzko/awesome-cka)
alias k='kubectl'
alias kc='kubectl create'
alias kg="kubectl get"
alias kr="kubectl run"
alias kx="kubectl explain"
k completion -h # In case you forgot the source command listed above
Also check the cheatsheet from the documentation for easier access
- Delete all pods in a NS
kubectl delete --all po --namespace=<name>
# Don't wait for K8s to gracefully delete the object, force delete with grace period zero
k delete po --all --grace-period=0 --force
- kubectl
kubectl <verb> --help | less
kubectl | less
# Get a list of objects shortnames
kubectl api-resources
kubectl get po -Ao wide
kubectl get po -A --show-labels
kubectl get po -A -L <label>
# Sorting
# Sort by the creation time
kubectl get po -n kube-system --sort-by='{.metadata.creationTimestamp}'
# Check Authorization
kubectl auth can-i <verb> <object> # ex: kubectl auth can-i get namsepace
kubectl auth can-i <verb> <object> as <username>
# Get a shell into the container running
kubectl exec --it <name> -- /bin/bash
# Proxy
kubectl proxy --port=<number> &
# Display API Groups after using the proxy command
curl http://localhost:<number>
# Narrow down a selection from the shown API Groups
curl http://localhost:<number>/version
# Get list of pods in a specific NS
curl http://localhost:<number>/api/v1/namespaces/<Namespace>/pods
# Change context
k config use-context <context>
# Switch to a different NS in a context
k config set-context <context> --namespace=<namespace>
- Watch command (2 ways)
kubectl get
command supports the-w
option.- watch -n 1
kubectl get po
specifies a watch command that gets updated each 1 second
- Delete a block from the YAML file using Vim
# Approach 1
m then a # Start section
d then ' then a # End section
# Approach 2 Visual mode
shift and v (capital V) # Start section
d # End section
- Use the provided examples to quickly create yaml files
kubectl create <object> --help | less
kubectl create <object> <name> --image=<image-name> --dry-run=client -o yaml > file.yml
kubectl create cj --help | less # This shows the following example
# kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *"
# Dry run and redirect output as yaml
kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *" --dry-run=client -o yaml > cj.yml
# Same procedure with deployment
kubectl create deployment --help | less
# kubectl create deployment my-dep --image=busybox
kubectl create deployment my-dep --image=busybox --dry-run=client -o yaml > deployment.yml
# Create configMap
kubectl create cm --help | less
# Services are created with expose command
kubectl expose <object> --port=<number> --targetPort=<number>
# Pods
kubectl run <name> --image=<name>
# Run pod with specific service account
k run <name> --image=<name> --serviceaccount=<sa-name>
# Pod with requests and limits defined
kubectl run <name> --image=<name> --requests='cpu=100m,memory=256Mi' --limits='cpu=200m,memory=512Mi'
# Create a pod and expose it in the same command
kubectl run <name> --image=<name> --port=<number> --expose
- Use kubectl explain to find the
apiVersion
kubectl explain pod
# similar but 2 step approach
kubectl api-resources | less # /<object>
kubectl api-versions | less # /<term from previous command>
- Kubectl
- ~/.kube/config # kubectl config view
- The controller plane is generated using files at
/etc/kubernetes/manifests/
which contains- etcd.yaml
- kube-apiserver.yaml
- kube-controller-manager.yaml
- kube-scheduler.yaml
# Get all pod names
kubectl get pods -o=jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}'
# Get container names inside each pod
k get po -l key=value -o jsonpath='{.items[0].status.containerStatuses[*].name}'
# Get Pod IP a
k get po <name> -o jsonpath='{.status.podIP}'
# Get available Memory on the nodes
k get nodes -o jsonpath='{.items[].status.allocatable.memory}'
# Get available CPUs
k get nodes -o jsonpath='{.items.status.allocatable.cpus}'
kubectl cluster-info
# List kubernetes objects
kubectl api-resources
kubectl get <object>
# Copy a file to a pod
k cp /file/path/file_name <pod-name>:/path/inside/pod
# Troubleshooting commands
kubectl describe <object> # Used to also view resource requests and limits
# Use -C to render lines before and after the matched grep expression
k describe <object> | grep <term> -C 5
kubectl logs <pod-name>
# Get logs of the previous pod instance (if one existed)
kubectl logs <pod-name> -p
# Follow the logs using -f
kubectl logs <pod-name> -f
# When having more than 1 container in a pod use the -c flag
kubectl logs <pod> -c <container>
# Check events of specific object
# Found in events in stackdriver docs
kubectl get events --field-selector involvedObject.name=<object-name>
# Get All pods in the cluster
kubectl get po -A
# Updating existing object definition
kubectl apply -f file_name.yml
kubectl edit <object> <name>
kubectl exec -it <pod-name> -- command-here # ls /etc/conf for example
kubectl exec --it <pod-name> -c <container-name> -- /bin/bash # Getting into multi-containter pod
kubectl describe
command gets the current state of the object frometcd
kubectl logs
always go to a pod so no need to mention the type
- Use Namespaces, the linux kernel allows for strict isolation between different system components using NSs, isolation is as follows:
- File (a container is like a
chroot
environment and only contents of specific directories can be viewed) - IPCs
- network (every container can do its own networking)
- processes (Container can only see its own processes)
- users (Containers have their own users that don't exist on the host OS)
- File (a container is like a
- They also use
CGroups
which offers resource allocation and limition
The containers section helps identifying whether it's an init Container
or a normal one, imagePullSecrets
can be used to download from private registeries.
- Each pod has a unique IP address
- Containers within this pod share the same IP address
- Use
port forwarding
to access pods via localhost (For testing purposes only)
- Usually SVC or Ingress (DNS) is used for that purpose
kubectl port-forward pod/<name> <host-port>:<container-port>
# Verify that kubectl is listening
ss -tunap | grep <host-port>
curl localhost:<host-port>
Status | Meaning |
---|---|
Pending | When a pod is first created it's in a pending state which means that the scheduler is figuring out where to place the pod |
containerCreating | Images are being pulled so that the container starts |
Running | The pod is started, it keeps running till it does its job and then terminates |
- pod.spec
- .containers
- .resources
- .securityContext
- .env # Load a single env variable
- .name
- .valueFrom
- .configMapKeyRef
- .secretKeyRef
- .envFrom # Takes an array
- .configMapRef.name
- .secretRef.name
- .volumeMounts
- .mountPath (what you want to store from the container on the host)
- .volumes
- .emptyDir (creates temporary directory on the host)
- .hostPath (Mounts a directory located on the host to the container)
.gitrepo# Deprecated- .azureDisk, .awsElasticBlockStore, .gcePresistentDisk # Cloud and all
- .cephfs # ceph object storage
- .nfs
- .persistentVolumeClaim
- .claimName (Name of the PVC)
- configMap
- .name
- .items
- key
- path
- .secret.secretName
- .containers
# Init containers
k explain pod.spec.initContainers
- The
Scheduling
fields helps determine where the pod will be scheduled- nodeSelector schedules on a target node using provided labels
- nodeName schedules on a target node using just the node name
Tolerations
allow the pod to be scheduled on a node with a specifictaint
(The pod becomes tolerant to that node)Affinity
similar tonodeSelector
ornodeName
but with the capability of having conditionals to what nodes the pod can be scheduled onNodeAffinity
similar tonodeSelector
where we schedule using a label that matchespodAffinity
schedules the pod on a node where some other specific pods are already runningPodAntiAffinity
prevents the scheduling of a pod on a node containing some other specific pod
priorityClassName
specifies pod priority where if the pod can't be scheduled then the scheduler evict lower prioroty pods to allow the scheduling of this pod to happen
k create priorityclass --help
hostNetwork
controls whether the pod may use the node network NS (is set to False by default)
- Plain Key Value
- ConfigMap
- Secrets
- fieldRef
# Create a pod with an environment variable
# 1- Plain key value type
k run <pod-name> --image=busybox --o yaml --dry-run=client --env=EXAM=CKAD --command env
- When a ConfigMap is created
--from-env-file
the file must adhere to the formatkey=value
--from-file
Using single configMap env variable:
env:
- name:
valueFrom:
configMapKeyRef:
name: <configMap-object-name>
key: <key-from-cm>
Using single Secret env variable:
env:
- name: <name>
valueFrom:
secretKeyRef:
name: <secret-name>
key: <key-name>
- Note that if CM or Secret weren't found and they're being referenced then the object creation will fail, to mitigate that make them optional. Option CM example:
env:
- name: <name>
valueFrom:
configMapKeyRef:
name: <cm-name>
key: <key-name>
optional: True
FieldRef example:
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: NS
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: IP_A
valueFrom:
fieldRef:
fieldPath: status.podIP
# Create ns
kubectl create ns <name>
# Change the default namespace in ~/.kube/config
k config set-context --current --namespace <name>
# Verify using
k config view
# Revert back to default ns
k config set-context --current --namespace=
Name | Description |
---|---|
default | Used by default when no namespace for objects is specified |
kube-system | NS for objects created by K8s system |
kube-public | - Created automatically and is readable by all users. - Contains info like CA that helps kubeadm join and authenticate worker nodes |
kube-node-lease | Contains lease objects that are used to determine node health |
ConfigMaps and Secrets:
- Special type of volumes
- Just like volumes, they must be created before the pod that will use them
- Used to separate dynamic data from static data in the pod
- ConfigMaps can be mounted on the location where the application expects to find a configuration file
- ConfigMaps can be included either as a
env
variable or as avolume
- When mounting a ConfigMap as a volume, in the mount point files will be created with the name of the Key and inside it exists the value
Secrets
are just encoded ConfigMaps- Secrets allow stroing sensitive data as passwords, auth tokens and SSH keys
- Secrets are used so that the data doesn't have to be put in a pod this reducing risk of accidential exposure
- There are 3 types of secrets:
- docker-registry which is used for connecting to the docker registry ๐ฎ
- TLS which creates a TLS secret
- generic which creates a secret from a local file, a directory or a literal value
- Secrets can be used by pods in 2 ways:
- as environemnt variables
- Mounted as volumes
- ConfigMaps can be created from different sources
- Directories: uses multiple files in a directory
- Files: puts the contents of a file in ConfigMap
- Literal Values: provide variables and command arguments that are to be used by a pod
- The behavior of
--from-file
is that if nokey=<file-name
was specified then the file-name itself is set as the key name. - if a key was passed then it overrides the file_name that was going to be set by default as the key
# Command line ConfigMap specification
kubectl create cm --from-literal='key=value' --from-literal='key2=value2'
# From a file
kubectl create cm --from-file <file_name>
# From a file but the key is set to the desired name we want
kubectl create cm --from-file=desired_name=<file_name>
# Create a secret from file
kubectl create secret <type> <name> --from-file=/absolute/path
# Create a secret from literal type=generic
kubectl create secret <type> <name> --from-literal='key=encoded_value'
Secrets can also be created from YAML files but the values need to be base64
encrypted
echo <string> | base64 # encode the string
echo <base-64-encrypted-string> | base64 -d # decode
file_name ex:
VAR1=abc
VAR2=efg
.data.variables
- Define privilege and access control settings for pods and containers
- They're part of the Pod specification
- They can be specified either at pod level or at container level (although
capabilites
is only available at container level) - Running as privileged or unprivileged user
- Without a securityContext, the user running the commands in the container inside the pod will be
root
by default
- Without a securityContext, the user running the commands in the container inside the pod will be
- SELinux where security labels can be applied OR AppArmor
- Discretionary access control which is about permissions used to access an object
AllowPrivilegeEscalation
which controls if a process can gain more privileges than its parent process
kubectl explain pod.spec.securityContext
k explain pod.spec.containers.securityContext
-
runAsUser # Specifies the user of the running processes in the containers
-
runAsGroup # Specifies the primary group of all processes within the containers
-
fsGroup # Applies settings to volumes that support ownership management, these volumes are modified to be owned by the GID specified in the fsGroup
-
pod.spec.containers.securityContext
- .runAsUser
- .runAsGroup
- .runAsNonRoot
- .fsGroup
- .capabilities
Capabilities example:
containers:
- name: nginx
image: nginx
securityContext:
capabilities:
add: ["SYS_TIME"]
- Define container's memory and cpu requirements using
requests
andlimits
resource request
is the amount of resources necessary to run a container (The node must have enough available resources for the requests)resource limits
is the maximum value for the resource usage of a container- CPU core usage can be specified using
m
which meansmilli CPU
ormilli core
- 250m means a quarter of a cpu core
- 500m means half of a cpu core and so on
# Set requests and limits for a running nginx pod
k set resources po/nginx --requests=memory=50Mi,cpu=200m --limits=memory=256Mi,cpu=750m
- Used to limit resources in a
Namespace
k create quota --help
kubectl create quota <name> --hard=<comma separated list of hardware specs>
kubectl create quota my-quota --hard=cpu=1,memory=1G,pods=2
- ResourceQuota
- .metadata.namespace
- .spec
- .hard
- .pods # Max number of pod on this ns
- .requests.cpu
- .requests.memory
- .limits.cpu
- .limits.memory
- .hard
- Store sensitive data in a secure fashion (using base64 encoding lol)
k create secret generic <name> --from-literal=key=value
- There are 2 types of accounts in K8s, a
user
account and aservice
account. - User accounts are used by individuals (admin, developer, etc) while service accounts are used by the pods (Prometheus uses a service account to get performance metrics using the API, Jenkins also uses a service account to deploy apps on the cluster)
- A service account provides an identity for processes that run in a Pod
- Allows the container to interact with kubernetes API
- Image pull secrets authenticate with private container registeries
- When a
sa
is created it also generates a token which is stored as a secret
kubectl create serviceaccount <name>
# Shorter version
k create sa <name>
# Use the created SA with a pod
kubectl run <pod-name> --image=<name> --serviceaccounts=<SA-name>
just specify the serviceAccountName
in the pod spec
definition
Containers in the same pod can communicate with each other in 3 different ways:
- Through the shared network (http://localhost:containerPort)
- Through the use of a shared storage volume
- Throguh shared process namespace
shareProcessNamespace: true
in the pod spec
Multi container design patterns:
- Sidecar pod The sidecar container enhances the functionality of the main container. use cases:
- logging agents that collect and ship logs to a central aggregation system
- File sync services and watchers - ex: Git sync to a web server container
-
Ambassador pod It receives network traffic then pass it to the main container (ex: ambassador listens on a custom port then forwards traffic to the main hard-coded port) use cases - commonly used with DBs
-
Adapter pod Changes the output of the main container (for example the output is changed to fit some sort of a monitoring tool that will receive it like prometheus or logstash)
- In Multi container pod, the same namespace can be shared between containers using
shareProcessNamespace: True
in pod spec, this can be useful in certain use cases where we need access for inter process communication or to fetch metrics about app processes in the container.
- An init container is an additional container in a pod that completes a task before the main container is started
- The main container gets started only after the init container has finished its job
- Allows customizing how K8s determines container status
- Liveness probe indicates whether the container is running properly and decides whether the cluster will automatically stop or restart the container
- It just does health checks
- Readiness probe indicates whether the container is ready to receive requests (It makes sure that the pod is not published as available until it's able to access it)
- If the Readiness probe fails the pod doesn't get replaced, the pod will just stop receiving traffic and that's all.
- If Liveness probe fails then the pod is restarted
Types of probes:
- exec: a command that gets executed and returns a zero exit value
- httpGet: an HTTP request that returns a response code between 200 and 399
- tcpSocket: connectivity to a TCP socket (available port) is successfull
* pod.spec.containers.readinessProbe
* .periodSeconds
* .initialDelaySeconds
* .exec
* .command
* pod.spec.containers.livenessProbe
* .failureThreshold # Minimum consecutive failures for the probe to be considered failed.
readinessProbe:
- httpGet
readinessProbe:
httpGet:
port: <number>
path: </path/here>
- exec
readinessProbe:
exec:
command:
- cat
- </path/here>
- tcpSocket
readinessProbe:
tcpSocket:
port: <number>
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.7/components.yaml
# Verify server installation
kubectl get --raw /apis/metrics.k8s.io/
- This didn't work, the soultion was to add
- --kubelet-insecure-tls=true
- --kubelet-preferred-address-types=InternalIP
Metric server
comes pre-installed so no worries, just know the commands:
kubectl top po
# Narrow down to a specific pod
kubectl top po <name>
kubectl top nodes
- Check the status of
kubelet.service
usingsystemctl status kubelet
- Check the logs of the
kubelet
service usingjournalctl -u kubelet
# Get pods based on specific label
kubectl get po -l key=value
# The previous command is eqivalent to
k get po --selector k=v
# Get pods based on multiple labels
kubectl get po -l key=value,key=value
# Get pods that doesn't match the labels
kubectl get po -l key!=value
# Set based selectors
kubectl get po -l 'key in (val1,val2,val3)'
# Watch rollout and changes in the deployment
kubectl get po -w
# To label an object use the label command
kubectl label <object> <name> key=value
# Remove label
kubectl label po <name> key-
# Add anootation
kubectl annotate <object> <name> key=value
- The
selector
option is used to search for items that have a specific label setkubectl get po --selector='key=value'
- The previous command is also equivalent to using
kubectl get po -l key=value
- Annotations provide metadata ex: licences, maintainer etc
- Useful when dealing with external tools for automation where we need attach additional info to our pods
- Just add them in
metadata
section usingannotations:
then add key value pairs
- Represent multiple replicas of a Pod
- The
metadata
section of the deployment template doesn't need a name because k8s automatically generates a name for each replica - Any major change results in the deployment creating a new RS that uses new properties
- The old RS is kept but the number of replicas is set to 0 (Useful for rollbacks)
- Max number of pods = number of replicas + number specified in maxSurge
# Scale deployment using kubectl
kubectl scale deployment <name> --replicas=<number>
# Can also be done via kubectl edit
kubectl edit deployment <name>
# See rollout history of a deployment
kubectl rollout history <object> <name>
# Check details about specific rollout revision
kubectl rollout history <object> <name> --revision=<number>
# Undo previous change
kubectl rollout undo
# Rollback to specific revision
kubectl rollout undo deployment <name> --to-revision=<number>
# Create a deployment then set an environment variable (There's no --env option for deployment so it's a 2 step process)
k create deploy <name> --image=<name>
k set env deploy <name> --env=K=V --env=K=V
# Set from a configMap or from a secret
k set env deploy/<name> --from=configmap/<cm-name> --keys="key1,key2"
k set env deploy/<name> --from=secret/<secret-name> --keys="key1,key2"
# Both the previous approaches load single keys specified in the cm or secret, to load the whole cm or secret just use --from=<name>
# Load all configMap data
k set env deploy/<name> --from=configmap/<name>
# Load all secrets data
k set env deploy/<name> --from=secret/<name>
# Setting resource requests and limits for deployment
k set resources deploy/nginx --requests=memory=256Mi,cpu=500m --limits=memory=500Mi,cpu=750m
- deployment.spec
- .replicas
- .strategy.type
- .rollingUpdate (Updates pods one at a time to guarantee availability of the application)
- .maxUnavailable (Max number of pods that are upgraded at the same time)
- .maxSurge (Max number of pods that can be created over the desired number of pods, the value can be absolute number of a percentage of the desired pods)
- .recreate (All pods are killed and new Pods are created which results in temporary unavailability, useful if we can't run differnt versions of the application)
- .rollingUpdate (Updates pods one at a time to guarantee availability of the application)
- .selector
- .template
- Works by setting a target CPU along with min and max number of replicas
- Target CPU is expressed as a percantage of the Pod's CPU requests
Metrics server
is used to collect the metrics and based on it the desired state is specified
HorizontalPodAutoscaler yml example:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: redis-hpas
labels:
app: redis
spec:
maxReplicas: 5
minReplicas: 1
targetCPUUtilizationPercentage: 70
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: redis-deploy
Same with kubectl:
kubectl autoscale deployment <name> --max=<number> --min=<number> --cpu-percent=70
# Get HorizontalPodAutoscaler
kubectl get hpa
- Rollout is a process of updating and replacing replicas with replicas matching a new
Deployment
template - Deployments create RS by defaults
- Rolling updates provide a way to update a deployment to a new container version by gradually updating replicas thus no downtime is introduced
- Rollouts are only triggered when there's a change in the pod spec , if there's a change for number of replicas for example, this doesn't trigger a rollout.
kubectl set image deployment/<deployment-name> <container name>=<image name> --record # --record flag recrods info about the update so that it can be rolled back later
# If the --record was forgotten then manually annotate the deployment
k annotate deploy/<name> kubernetes.io/change-cause="Reason for rollout"
# Scale a deployment
kubectl scale deployment <name> --replicas <number>
kubectl rollout status deployment/<name>
# Check deployment versions
kubectl rollout history deployment/<name>
# Get more info about a specific revision
kubectl rollout history deployment/<name> --revision=<number>
# Pause the rollout of a deployment
kubectl rollout pause deployment <name>
# Resume the rollout of a deployment
kubectl rollout resume deployment <name>
# Rollback
kubectl rollout undo deployment/<name>
# Rollback to a specific revision
kubectl rollout undo deployment/<name> --to-revision=<number>
In rollingUpdate
strategy we can specify
maxSurge%
specifies how many replicas over the desired total are allowed during rollout, a higher surge % allows new pods to be created without waiting for the old ones to be deletedmaxUnavailable%
specifies how many of the old pods to be deleted without waiting for the new pods to be ready
Jobs
are like pods but they run for a specific job and when it's finished the container stops running- Useful for tasks like backup, calculation, batch processing
- A pod start by a job must have its
restartPolicy
set either to- OnFailure (re-run the failing container on the same pod)
- Never (re-run the failing container in a new pod)
- 3 Different job types can be started which is specified by completions (Number of pods that will be created) and Parallelism parameters
- Non parallel jobs: 1 Pod is started unless the pod fails
- Completions = 1
- Parallelism = 1
- Parallel jobs with a fixed completion count where the jobs is complete after running as many times as specified by
jobs.spec.completions
- Completions = n
- Parallelism = m
- Parallel jobs with a work queue where multiple jobs are started but all is finished when one job of those that were started is finished
- Completions = 1
- Parallelism = n
- Non parallel jobs: 1 Pod is started unless the pod fails
CronJobs
are your regular linux cron jobs, they run regularly according to the specified schedule- When running a CronJob a Job will be scheduled which will start a new pod
# get Jobs
kubectl get jobs
# get cron jobs
kubectl get cj
-
job.Spec
- .activeDeadlineSeconds # Durtation after which the job will be terminated
- .completions # number of times the job should be completed
- .parallelism # max number of pods that should run at any given time
- .template
-
cronjob.spec
- .schedule
- .jobTemplate
- .spec.template.spec.restartPolicy
-
CronJobs
createjobs
which in turn createpods
- Provide network access to a set of pods
- A service has a fixed IP address
- It handles Pod IP changes
- Distribute requests across pods in the group
- When left for k8s to allocate port number, it allocats within the range [30K - 32,767]
- Service types are:
- ClusterIP : Service is exposed within the cluster using Internal IP address, it's also accessible using cluster DNS,
kube-proxy
is responsible for proxying requests for the service (to one of itsep
s) - NodePort : Service is exposed externally via a port which listens on each node in the cluster (The
ClusterIP
is still given to the service) so each request to the NodePort gets routed to the ClusterIP - LoadBalancer: For cloud providers
- externalName: Enabled by DNS not proxying, we configure an external NameService with a DNS name and requests for the service return a CNAME record with the external DNS name (used by the services running outside of k8s such as DB as a service)
- ClusterIP : Service is exposed within the cluster using Internal IP address, it's also accessible using cluster DNS,
- NodePort creates ClusterIP
- LoadBalancer creates both NodePort and ClusterIP
# Get IP address of each replica
kubectl get ep <service-name>
- service.spec
- .ports
- .port (Port of the service itself)
- .targetPort (The port where the pod will forward traffic to)
- .nodePort (Port on the node where external traffic comes in)
- .ports
# 1- Create a pod
kubectl run nginx --image=nginx
# 2- Expose the pod (Create a SVC)
kubectl expose pod nginx --port=8888 --target-port=80
# Create a headless service
k create svc clusterip <name> --clusterip="None" --dry-run=client -o yaml
Running kubectl describe svc nginx
returns
Here any traffic from IP:Port
gets redirected to EP:TargetPort
- Allows the control of what traffic come in [
ingress
] and leave [egress
] the pod (Same functionality of a firewall) - Equivalent to the relation between a securityGroup and an EC2 instance in AWS
- NetworkPolicies are Namespaced so we can configure a NetworkPolicy for each NS
- Network isolation can be configured to block traffic to pods by running pods in dedicated namespaces
- NetworkPolicy depends on the support from the network provider (Not all providers offer support) and if there's no support then the policy applied won't have any effect
- Connections are stateful (just like SG) so allowing one direction is enough
- There are 2 kinds of pods when it comes to NetworkPolicies, a
non-isolated
pod and anisolated
one, the non-isolated receives traffic from any source but when we apply a NetworkPolicy to the pod it becomes isolated - If the
podSelector
section in the NetworkPolicy yaml definition is empty then the policy will be applied to all pods in that namespace - If only
ingress
was specified in the yaml description then all egress traffic will be allowed by the policy - If the
from
is removed from theingress
definition then all traffic is allowed to enter the port, ifports
list is removed then all traffic is allowed from the specified sources from
list can either be:- podSelector
- namespaceSelector
- ipBlock
k explain networkpolicy.spec.ingress.from
- In networkPolicies,
allow
rules overridedeny
rulespolicyTypes
sets whether the policy governs ingress, egress or both
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: nw-policy
spec:
podSelector: # 1
matchLabels:
key: value
policyTypes: # 2
- Ingress
- Egress
ingress:
- from:
ports:
kubectl get networkpolicies
# Use netpol for short
kubectl get netpol
kubectl describe networkpolicy <name>
- NetworkPolicy.spec
- .podSelector.matchLabels
- .policyTypes
-
- Ingress
-
- Egress
-
- .ingress
- .from
- .ports
- .egress
- .to
- .ports
- Tied to pod lifecycle (Different from
PV
) - Share data betwwen containers and tolerate container restarts
- Used for non durable storage that gets deleted when the pod is deleted
emptyDir
volume type allows the creation of a shared storage between the containers
# List containers inside pod in case there are more than 1 container
kubectl get po <name> -o jsonpath='{.spec.containers[*].name}'
Volume Types List:
- hostPath
- configMap
- emptyDir
- persistentVolumeClaim
- secret
- hostPath
volumes:
- hostPath:
path: </path/on/host/os>
type: Directory
- secret
volumes:
- name: mysecret
secret:
secretName: <secret-object-name>
-
Persistent Volume allows Pods to connect to external data storage
-
Independent of Pod's Lifetime
-
PVC access modes are:
- readWriteOnce (Allow volumes for RW operations only by a single node at a time)
- readOnlyMany
- readWriteMany
-
ConfigMaps are specific volume objects that connect to config files and variables
-
Secrets are like configMaps but the data is encoded
-
PVC will bind to a PV according to the availability of the requested
accessModes
andcapacity
-
pv.spec
- .capacity
- .storage
- .accessModes
- .ReadWriteOnce
- .ReadWriteMany
- .ReadOnlyMany
- . [.hostPath, .nfs, .cephfs]
- .capacity
-
pvc.spec
- .accessModes
- .resources
- .requests.storage
-
AccessModes
between PV and PVC must match so that the state becomesbound
PV example:
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
hostPath:
path: /tmp/data
PVC example:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
- Gives
Services
externally reachable URLs - Allows traffic load balancing across services
- Offers name based virtual hosting
- Ingress controller must be setup to allow the use of ingress resources
- Examples of ingress controllers are nginx, haproxy, traefik, kong, contour
- We commonly configure a
default
backend for the ingress controller so that the traffic that doesn't match anypath
gets redirected to it - ingress.spec
- .rules
- .host (optional and if not supplied then the rule applies to all HTTP traffic)
- .http.paths (List of paths each having its own backend)
- .path
- .backend (Service name + Service Port)
- .serviceName
- .servicePort
- .backend (Service name + Service Port)
- .backend
- .path
- .pathType
- .path
- .rules
- Environment Variables
- SVC address automatically injected in containers
- Environment vars follow naming conventions based on SVC name
- Hyphens (-) gets replaced with underscores
โญ Naming pattern for environment variables service discovery:
- IP address: <SVC_NAME_ALL_CAPS>_SERVICE_HOST
- Port: <SVC_NAME_ALL_CAPS>_SERVICE_PORT
- Named port: <SVC_NAME_ALL_CAPS>SERVICE_PORT<PORT_NAME_ALL_CAPS>
- DNS
- DNS records automatically created in the cluster DNS
- Containers automatically configured to query cluster DNS
โญ Naming pattern for DNS service discovery:
-
IP address: <SVC_NAME>.<SVC_NS>
-
Port: Needs to be extracted from SRV DNS record
-
When using Environment variables for service discovery, the service must be created before the pod, also both the svc + po must be in the same NS
1- SVC + Po example
# Pod definition section
apiVersion: v1
kind: Pod
metadata:
name: nginx-po
labels:
app: web-server
spec:
containers:
- name: nginx
image: nginx
---
# Service definition section
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
app: web-server
spec:
type: NodePort
ports:
- protocol: TCP
port: 8000
targetPort: 80
selector:
app: web-server
2- Multi container Po + NS
# 1- Create NS
apiVersion: v1
kind: Namespace
metadata:
name: multi-container-ns
labels:
app: counter
---
# 2- Create Pod
apiVersion: v1
kind: Pod
metadata:
name: multi-container-po
namespace: multi-container-ns
spec:
containers:
- name: redis
image: redis:latest
imagePullPolicy: IfNotPresent # Prevent k8s from always pulling the image
ports:
- containerPort: 6379
- name: server
image: lrakai/microservices:server-v1
ports:
- containerPort: 8080
env:
- name: REDIS_URL
value: redis://localhost:6379
- name: counter
image: lrakai/microservices:counter-v1
env:
- name: API_URL
value: http://localhost:8080
- name: poller
image: lrakai/microservices:poller-v1
env:
- name: API_URL
value: http://localhost:8080
3- Service Discovery
# 1- Create Namespace
apiVersion: v1
kind: Namespace
metadata:
name: discovery-ns
labels:
app: counter
---
# 2- Data tier
# 2.1 Data tier Service
apiVersion: v1
kind: Service
metadata:
name: data-tier
namespace: discovery-ns
labels:
app: microservice
spec:
type: ClusterIP
selector:
tier: data
ports:
- protocol: TCP
port: 6379
name: redis
---
# 2.2 Data tier Pod
apiVersion: v1
kind: Pod
metadata:
name: data-tier
labels:
tier: data
app: microservice
spec:
containers:
- name: redis
image: redis:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 6379
---
# 3- App tier
# 3.1 App tier SVC
apiVersion: v1
kind: Service
metadata:
name: app-tier
labels:
app: microservice
spec:
type: ClusterIP
selector:
tier: app
ports:
- protocol: TCP
port: 8080
---
# 3.2 App tier Po
apiVersion: v1
kind: Pod
metadata:
name: app-tier
labels:
app: microservice
spec:
containers:
- name: server
image: lrakai/microservices:server-v1
ports:
- containerPort: 8080
env:
- name: REDIS_URL
value: redis://$(DATA_TIER_SERVICE_HOST):$(DATA_TIER_SERVICE_PORT_REDIS)
---
# 4- Support tier
apiVersion: v1
kind: Pod
metadata:
name: support-tier
labels:
app: microservice
tier: support
spec:
containers:
- name: counter
image: lrakai/microservices:counter-v1
env:
- name: API_URL
value: http://app-tier.discovery-ns:$(APP_TIER_SERVICE_PORT)
- name: poller
image: lrakai/microservices:poller-v1
env:
- name: API_URL
value: http://app-tier.discovery-ns:$(APP_TIER_SERVICE_PORT)
Qs
- Create configMap named
ckad-cm
withEXAM=CKAD
and add it one time as a env variable in a pod and another time as a volume. 2-