{"id":25431986,"url":"https://github.com/vojtechmares/kubernetes-training","last_synced_at":"2026-02-16T17:33:29.577Z","repository":{"id":223386139,"uuid":"649602926","full_name":"vojtechmares/kubernetes-training","owner":"vojtechmares","description":"My course on Kubernetes (Work in Progress)","archived":false,"fork":false,"pushed_at":"2026-02-16T00:41:40.000Z","size":124,"stargazers_count":0,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-16T08:21:08.860Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vojtechmares.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-05T08:35:47.000Z","updated_at":"2025-06-12T14:11:16.000Z","dependencies_parsed_at":"2024-04-08T02:34:27.626Z","dependency_job_id":"e57059ed-0ad0-4b03-b9e3-9136e60cfdcb","html_url":"https://github.com/vojtechmares/kubernetes-training","commit_stats":null,"previous_names":["vojtechmares/kubernetes-training"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vojtechmares/kubernetes-training","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vojtechmares%2Fkubernetes-training","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vojtechmares%2Fkubernetes-training/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vojtechmares%2Fkubernetes-training/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vojtechmares%2Fkubernetes-training/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vojtechmares","download_url":"https://codeload.github.com/vojtechmares/kubernetes-training/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vojtechmares%2Fkubernetes-training/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29513989,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-16T09:05:14.864Z","status":"ssl_error","status_checked_at":"2026-02-16T08:55:59.364Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-17T04:42:41.805Z","updated_at":"2026-02-16T17:33:29.535Z","avatar_url":"https://github.com/vojtechmares.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003eKubernetes training\u003c/h1\u003e\n  \u003cp align=\"center\"\u003eLearn Kubernetes: from deploying your first application to deploying hundreds of microservices in no time.\u003c/p\u003e\n  \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://kubernetes.io/\"\u003e\u003cimg alt=\"GitLab\" src=\"https://img.shields.io/badge/TRAINING ON-KUBERNETES-326CE5?style=for-the-badge\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://www.vojtechmares.com/\"\u003e\u003cimg alt=\"SikaLabs\" src=\"https://img.shields.io/badge/TRAINING BY-VOJTĚCH MAREŠ-F59E0B?style=for-the-badge\"\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\n# About course\n\n- [Slides](https://training.vojtechmares.com/kubernetes/slides/)\n\n# About lector\n\nHi, my name is Vojtěch Mareš. I'm freelance DevOps engineer, consultant, and lector. For more, see my [website](https://vojtechmares.com/) or explore more [courses](https://vojtechmares.com/#skoleni).\n\nHaving questions? Contact me at email [`iam@vojtechmares.com`](mailto:iam@vojtechmares.com).\n\n# Before we start\n\n## Install tooling\n\n- `kubectl`\n- `helm`\n- `kubectx` \u0026 `kubens`\n- `k9s`\n- Docker: Official installation guide\n\n## Aliases\n\n### `k`\n\nFor simplicity and our sanity, let's create an alias for `kubectl`.\n\n#### Shell (bash, zsh,...)\n\n```shell\n# .bashrc / .zshrc / etc.\nalias k=\"kubectl\"\n```\n\n#### Windows (cmd.exe)\n\n```cmd\ndoskey k=kubectl $*\n```\n\n#### Windows (PowerShell)\n\n```powershell\nSet-Alias -Name k -Value kubectl\n```\n\n# Course\n\n## Cluster components\n\n![Kubernetes components](/assets/components-of-kubernetes.svg)\n\n### Control plane\n\nFormerly master.\n\nA node running components necessary to run the Kubernetes cluster.\n\n- kube-apiserver\n- etcd\n- kube-scheduler\n- kube-controller-manager\n- cloud-controller-manager (optional)\n\n### Node\n\nFormerly worker.\n\nMachine running our workload (applications).\n\n- kubelet\n- kube-proxy\n- container runtime (by default containerd)\n\n## Explain Kubernetes resources\n\n\u003e Manpage for Kubernetes resources.\n\nWhen you need to know something about resource and it's field.\n\n```shell\nkubectl explain node\nkubectl explain node.spec\n\nkubectl explain pod\nkubectl explain pod.spec\nkubectl explain pod.spec.containers.image\n```\n\n## Nodes\n\n```shell\nkubectl get nodes\n\n# or short form\nkubectl get no\n```\n\n## `kubectl`\n\nA command line tool to interact with the cluster.\n\n### `kubectl get`\n\nList resources of type.\n\n```shell\nkubectl get namespace\n```\n\n### `kubectl describe`\n\nDescribes resource including status, recent events and other information about it.\n\n```shell\nkubectl describe namespace default\n```\n\n### `kubectl create`\n\nCreates new resource either in terminal or from file.\n\n```shell\nkubectl create namespace example-ns\n\n# or from file\nkubectl create -f ./example-ns.yaml\n```\n\n### `kubectl delete`\n\n```shell\nkubectl delete namespace example-ns\n\n# or target resource from file\nkubectl delete -f ./example-ns.yaml\n```\n\n### `kubectl apply`\n\nCreates a resource if it does not exist or applies the configuration from file to an existing resource.\n\n```shell\nkubectl apply -f ./example-ns.yaml\n\n# supports URL\nkubectl apply -f https://raw.githubusercontent.com/vojtechmares/kubernetes-training/.../pod.yaml\n```\n\n## Pod\n\nSmallest deployable unit in Kubernetes. Can be made from multiple containers, usually one is enough.\n\n### List pods\n\n```shell\nkubectl get pods\n```\n\n### Describe pod\n\n```shell\nkubectl describe pod $POD_NAME\n```\n\n### See pod logs\n\n```shell\nkubectl logs -f $POD_NAME\n```\n\n### Connect to pod\n\n```shell\nkubectl port-forward pod/$POD_NAME $LOCAL_PORT:$POD_PORT\n```\n\n### Open bash in pod\n\n`kubectl exec` runs a binary (or shell script if shell is available) within a *Pod*. It is also useful to launch a shell session inside the *Pod*, when you need to debug some issue.\n\n```shell\nkubectl exec -it $POD_NAME -- bash\n```\n\n### Copy files from / to pod\n\n```shell\n# From local to pod\nkubectl cp ./path/to/file $POD_NAME:/remote/path\n\n# From pod to local\nkubectl cp $POD_NAME:/remote/path ./local/path\n```\n\n## Service\n\nService is a cluster abstraction a single in-cluster endpoint (DNS name and IP address) that distributes traffic to it's pods.\n\n### Create service\n\n```shell\nkubectl create -f ./examples/02-service/service.yaml\n```\n\n### List services\n\n```shell\nkubectl get service\n```\n\n### Describe service\n\n```shell\nkubectl describe service example-svc\n```\n\n### Connect to service\n\n```shell\nkubectl port-forward service/example-svc 8080:8080\n```\n\n### Delete service\n\n```shell\nkubectl delete service example-svc\n```\n\n### Exposing Service\n\nYou can expose *Service* outside of the cluster in two ways, with `.spec.type`:\n\n- `type=NodePort`\n- `type=LoadBalancer`\n\n*NodePort* opens a port on every node and Kubernetes routes all incoming traffic (on every node) on given port to this *Service*. The default *NodePort* range is from 30 000 to 32 767.\n\n*LoadBalancer* requires cloud integration to provision a managed load balancer (AWS, Azure, GCP, DigitalOcean, Hetzner Cloud, and others) or software like [**kube-vip**](https://kube-vip.io/) to managed Virtual IP attachment/announcement, usually over ARP (L2) or BGP (L3).\n\nAnother option to expose network interface outside the cluster of your workload is either to use an *Ingress* for HTTP, *Gateway* and given routes for other protocols like gRPC. Or in some cases you can expose *Pods* directly with `hostPort`.\n\n## Deployment\n\nDeploying our application to *Pod* might be easy, but not a good idea. To deploy our app to Kubernetes and run it, we use *Deployment*. It is a layer of abstraction on top of *Pods* (and *ReplicaSet*).\n\n### ReplicaSet\n\n*ReplicasSet* is a child resource to *Deployment*, which is used to keep track of revisions of pools of pods and allows to rollback to it if new revision of *Deployment* is failing.\n\nToday, ReplicaSet is usually not interacted with by users.\n\n### Updates\n\nKubernetes native:\n\n- Recreate (deletes all pods and creates new ones)\n- RollingUpdate (zero downtime)\n\nThe *Recreate* strategy works pretty much as you expect: deletes all running *Pods* and then creates new ones.\n\nOn the other hand, *Rolling Update* has a few configuration options, see example:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: nginx\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: nginx\n  template:\n    metadata:\n      labels:\n        app: nginx\n    spec:\n      containers:\n      - name: nginx\n        image: nginx:latest\n        ports:\n        - containerPort: 80\n      strategy:\n        type: RollingUpdate\n        rollingUpdate:\n          maxSurge: 1\n          maxUnavailable: 0\n```\n\nExtended:\n\n- Blue/Green\n- Canary\n- A/B testing\n\nSee: [Argo Rollouts](https://argoproj.github.io/rollouts/) or [Flagger](https://flagger.app/)\n\n## Ingress\n\n*Ingress* resource exposes our application network interface (HTTP, TCP,...) to public internet.\n\n### Ingress Controller\n\nKubernetes does not bring in an *Ingress Controller* by default and it is up to cluster administrator to choose and deploy one (or multiple).\n\nKubernetes project offers [Ingress NGINX](https://kubernetes.github.io/ingress-nginx/).\n\n### Ingress resource\n\n*Ingress* is a Kubernetes resource that exposes *Service* outside the cluster. The resource is managed by *Ingress Controller*\n\n### Ingress Class\n\n*IngressClass* is Kubernetes abstraction to map *Ingress* resources to given *Ingress Controller*, like [Ingress NGINX](https://kubernetes.github.io/ingress-nginx/).\n\n*IngressClass* is often managed for you by the installer (like Helm) of *Ingress Controller* or the controller itself.\n\n### Gateway API\n\n[Gateway API](https://gateway-api.sigs.k8s.io/) - a new standard for ingress traffic handling. Kubernetes extension made by SIG-Network. Only specification, implementation is up to users.\n\nGenerally Available implementations:\n\n- [Contour](https://projectcontour.io/guides/gateway-api/)\n- [Cilium](https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gateway-api/)\n- [Envoy Gateway](https://github.com/envoyproxy/gateway)\n- [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs/concepts/gateway-api)\n- [NGINX Gateway Fabric](https://github.com/nginxinc/nginx-gateway-fabric)\n\n## StatefulSet\n\nA special abstraction for running *Pods* running stateful applications like databases (for example MySQL or Redis) or message brokers like RabbitMQ and Apache Kafka.\n\nStatefulSet also needs a \"headless service\", which is defined in it's spec.\n\n### Headless service\n\nA service with `type=ClusterIP` and `clusterIP=None` configuration.\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: my-headless-service\n  labels:\n    app: my-app\nspec:\n  # type: ClusterIP # default\n  clusterIP: None\n  selector:\n    app: my-app\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 8080\n```\n\nThat creates a service without a Virtual IP for load balancing and instead DNS will return all IPs of *Pods*.\n\nThat is important for many reasons. This allows you to distinguish between running *Pods* (in another words preserving network identity of a process). Or to do client-side load balancing.\n\nWhy would we want to know to which *Pod* are we talking to? For example with databases, we want to connect to primary instance for writes, but reads are fine from replicas.\n\nAnd as for the case of client side load balancing. This eliminates the need of a dedicated load balancer such as HA Proxy. Making it cheaper to operate and allows clients more granular control over to which backends it connects to. For example in microservices, you want your clients to connect to multiple backends, but each client should connect to a subset of all backends available to increase resiliency.\n\n## Job\n\n*Deployment* and *StatefulSet* manage *Pods* that run indefinitely (not counting restarts or crashes).\n\nFor one-time workload or just anything that does not need to run 24/7, *Job* is the right resource.\n\n```yaml\napiVersion: batch/v1\nkind: Job\nmetadata:\n  name: my-job\nspec:\n  template:\n    spec:\n      containers:\n      - name: my-job-container\n        image: my-job-image\n        command: [\"bash\", \"-c\", \"echo Hello from the Kubernetes cluster\"]\n      restartPolicy: Never\n  backoffLimit: 4\n```\n\n### CronJob\n\n*CronJobs* allows for periodic scheduling of *Jobs* at given schedule ([cron expression](https://crontab.guru/)).\n\n*CronJob* embeds a `jobTemplate` in it's `spec`. Which is just a *Job* manifest without `metadata`. Therefore you can use it with a template engine like Helm, if you need to.\n\n```yaml\napiVersion: batch/v1beta1\nkind: CronJob\nmetadata:\n  name: my-cron-job\nspec:\n  schedule: \"*/1 * * * *\"\n  timeZone: Etc/UTC # Kubernetes 1.27+\n  jobTemplate:\n    spec:\n      template:\n        spec:\n          containers:\n          - name: my-cron-job-container\n            image: my-cron-job-image\n            command: [\"bash\", \"-c\", \"echo Hello from the Kubernetes cluster\"]\n          restartPolicy: OnFailure\n```\n\n## Configuration and secrets\n\nContents of either *ConfigMap* or *Secret* are key-value pairs.\n\nSince it's YAML, you can use even multiline strings with `|`.\n\nTypically, keys are names of environment variables or file names.\n\n### ConfigMap\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: my-config-env\ndata:\n  myKey: myValue\n  anotherKey: anotherValue\n```\n\nOr with values as a config file:\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: my-config-file\ndata:\n  config.ini: |\n    [DEFAULT]\n    SomeKey = someValue\n```\n\n### Secret\n\nIf you want YAML secret in readable form, use `stringData`.\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: my-apps-db\ntype: Opaque\nstringData:\n  DATABASE_URL: mysql://wordpress:wppass@mysql.example.com:3306/wordpress\n```\n\nOtherwise, if you use `data` (default), values are **base64** encoded.\n\n\u003e [!NOTE]\n\u003e Secrets are not actually encrypted, but only **base64** encoded!\n\u003e Therefore secrets are not actual secrets in security-sense, but the resource-level distinction allows RBAC for fine-gained access for applications and cluster administrator, application developers, and others.\n\n### Load environment variables from ConfigMap or Secret\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: my-app-pod\nspec:\n  containers:\n  - name: app\n    image: my-app-image\n    envFrom:\n    - configMapRef:\n        name: my-config-env\n    - secretRef:\n        name: my-apps-db\n```\n\n### Mount ConfigMap or Secret as volume\n\n```yaml\napiVersion: v1\nkind: Secret\nmetadata:\n  name: database-url-secret\n  namespace: my-app\nstringData:\n  database.ini: |\n    url = mysql://wordpress:wpapss@mysql.example.com:3306/wordpress\n---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: my-app-pod\n  namespace: my-app\nspec:\n  containers:\n  - name: app\n    image: my-app-image\n    volumeMounts:\n    - name: database-url-vol\n      mountPath: /etc/secrets\n      readOnly: true\n  volumes:\n  - name: database-url-vol\n    secret:\n      secretName: database-url-secret\n```\n\nThe application can then read the file `/etc/secrets/database.ini`.\n\n\u003e [!TIP]\n\u003e It is recommended to mount secrets to containers as volume mounts rather than using environment variables,\n\u003e since environment variables are sometimes included in error dumps (for example Sentry is doing that).\n\u003e And that would lead to secret exposure.\n\n## Persistent data storage\n\n### PersistentVolume and PersistentVolumeClaim\n\n*PersistentVolume* is a Kubernetes resource representing an actual volume.\n\n*PersistentVolumeClaim* is a Kubernetes resource, marking a *PersistentVolume* claimed for given workload (*Pod*). Not allowing anyone else claim the volume.\n\n### Access modes\n\n- `ReadWriteOnce` (RWO)\n- `ReadWriteMany` (RWX)\n- `ReadOnlyMany` (ROX)\n- `ReadWriteOncePod` (RWOP), Kubernetes 1.29+\n\n### Storage classes\n\nStorage class represents a storage backend, connected to Kubernetes with a *CSI Driver*.\n\nAt *StorageClass* you define if you allow for volume size expansion.\n\n*Volume binding mode* describes when a volume should be created when using dynamic provisioning. *Volume binding modes* are:\n\n- *Immediate* is the default, creates volume as soon as *PersistentVolumeClaim* resource is created, which can lead to provisioning issues like volume being created in wrong *Available Zone*\n- *WaitForFirstConsumer* solves issues of *Immediate* and delays volume provisioning until a *Pod* is created\n\n```shell\n$ kubectl get storageclass\nNAME                 PROVISIONER          RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE\nlonghorn (default)   driver.longhorn.io   Delete          Immediate           true                   92d\n```\n\n### Reclaim policy\n\n- Retain - volume is kept even after requesting resource (such as *StatefulSet*) is deleted, preventing data loss. It's up to cluster administrator to delete the volume.\n- Delete - volume is automatically deleted\n- Recycle (`rm -rf /mnt/volume/*`). On Kubernetes 1.30 only available for `nfs` and `hostPath` volume types.\n\n### Temporary storage\n\nNot persisted between *Pod* deletions, but persisted between *Pod* restarts.\n\nVolume with type `emptyDir`.\n\n### Local storage\n\n- `local-storage` storage class\n- `hostPath`\n\n\u003e [!WARNING]\n\u003e Do not use `hostPath` volumes in production!\n\u003e Since you are directly mounting a directory on host, it can lead to security issues or node errors.\n\n### CSI plugins\n\nKubernetes on it's own only implements APIs to support container storage, the implementation itself is left for vendors.\n\nThis brings the Container Storage Interface API. Allowing cluster administrators to install only what you need for your workload, if you need any.\n\nThe implementation is called a *Driver*, which is responsible for dynamically provisioning volumes, mounting them to nodes and setting up file system. Driver is typically\n\nCSI drivers for on-premise:\n\n- [Longhorn](https://longhorn.io/)\n- [Ceph](https://github.com/ceph/ceph-csi) (if you are running Ceph)\n- [NFS](https://github.com/kubernetes-csi/csi-driver-nfs)\n- [vSphere](https://github.com/kubernetes-sigs/vsphere-csi-driver)\n\nCSI drivers for cloud:\n\n- [AWS EBS](https://docs.aws.amazon.com/eks/latest/userguide/ebs-csi.html)\n- [AWS EFS](https://docs.aws.amazon.com/eks/latest/userguide/efs-csi.html) (NFS)\n- [GCE PD](https://github.com/kubernetes-sigs/gcp-compute-persistent-disk-csi-driver) (Google Compute Engine Persistent Disk)\n- [List of Azure CSIs](https://learn.microsoft.com/en-us/azure/aks/csi-storage-drivers)\n- [DigitalOcean](https://github.com/digitalocean/csi-digitalocean)\n- [Hetzner Cloud](https://github.com/hetznercloud/csi-driver)\n\n## Namespace\n\nNamespace is a way to separate resources from each other. By default this induces no boundaries between resources. But application (*Pod*) in one namespace can still connect to a *Service* in another namespace.\n\nThe DNS name then looks like so: `\u003cservice name\u003e.\u003cnamespace\u003e[.svc.\u003ccluster domain\u003e]`. The `.svc` and `.[cluster domain]` are both optional. Default cluster domain is `cluster.local`. So the valid names are like so (for service `my-service` and namespace `my-namespace`):\n\n- `my-service.my-namespace`\n- `my-service.my-namespace.svc`\n- `my-service.my-namespace.svc.cluster.local`\n\nYou can manage network access from *Pods* to another (*Pods*, *Namespaces*, *Services*, *Ingresses* and egress routing rules) with a *NetworkPolicy*.\n\nOnly some resources are \"namespaced\" and some are cluster-wide.\n\nNamespaced resources:\n\n- *Pods*\n- *Services*\n- *Deployments*\n- *StatefulSets*\n- *Ingresses*\n- *ConfigMap* and *Secrets*\n- *ServiceAccount*\n- *Role* and *RoleBinding*\n- *ResourceQuota*\n- and more\n\nCluster namespaces:\n\n- *PersistentVolume*\n- *StorageClass*\n- *IngressClass*\n- *PriorityClass*\n- *PriorityClass*\n- *RuntimeClass*\n- and more\n\nIn short, cluster namespaces are non-application specific and tied to cluster itself.\n\nCreate namespace:\n\n```shell\nkubectl create namespace \u003cname\u003e\n\n# or use ns instead of namespace for short\nkubectl create ns \u003cname\u003e\n```\n\nDelete namespace:\n\n```shell\nkubectl delete namespace \u003cname\u003e\n```\n\n## Kubeconfig\n\nKubeconfig is a configuration file on your workstation, which describes to *kubectl* how to talk to the cluster. It contains the kubeapi endpoint, cluster CA (since by default kubeapi is using self-signed certificate) and user credentials, either token or certificate.\n\n### Context\n\nContext represents a current cluster you are connected to, since Kubeconfig file can contain connection information to multiple clusters.\n\nFor example, imagine you are a DevOps engineer and you have a cluster per environment: dev, stage, qa, prod. That will leave you with four clusters in your kubeconfig (if you choose to have configuration in a single file).\n\n### Merge kubeconfig files\n\n```shell\n# Backup existing kubeconfig\ncp ~/.kube/config ~/.kube/config.bak\n\n# add new config and existing kubeconfig paths to $KUBECONFIG environment variable\n# note the colon character between the paths\n# RECOMMENDATION: use absolute paths\nexport KUBECONFIG=\"~/.kube/config.bak:/path/to/new/kubeconfig\"\n\n# merge and flatten kubeconfig, and save output to a file\nkubectl config view --flatten \u003e /tmp/new-kubeconfig\n\n# replace your kubeconfig with new kubeconfig\nmv /tmp/new-kubeconfig ~/.kube/config\n\n# cleanup: unset $KUBECONFIG variable\nunset KUBECONFIG\n```\n\n### kubectx\n\nEasily switch between contexts (clusters).\n\n```shell\nkubectx demo\n```\n\nRename context:\n\n```shell\nkubectx \u003cnew name\u003e=\u003cold name\u003e\n```\n\nDelete context:\n\n```shell\nkubectx -d \u003cname\u003e\n```\n\n### kubens\n\nEasily switch between namespaces.\n\n```shell\nkubens kube-system\n```\n\n## RBAC\n\nRBAC = Role Based Access Control\n\n### Impersonate ServiceAccount\n\n```shell\nkubectl auth can-i\n```\n\n## Resources\n\n### Configure resources\n\nYou can configure **requests** and **limits**.\n\nResource **requests** are used for when scheduling *Pods* and **limits** are enforced by *Kubelet* in form of CPU throttling or Out of Memory Kills (exit code 137).\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: my-app-pod\nspec:\n  containers:\n  - name: app\n    image: my-app-image\n    # Quality of Service class: Burstable\n    resources:\n      requests:\n        cpu: \"100m\"\n        memory: \"256Mi\"\n      limits:\n        cpu: \"500m\"\n        memory: \"1Gi\"\n```\n\nYou can also expand on resource types, for example with [NVIDIA Device Plugin](https://github.com/NVIDIA/k8s-device-plugin), you can use `nvidia.com/gpu` resource type. You can define values smaller than one. In that case your application must be ready for [\"GPU slicing\"](https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/gpu-sharing.html).\n\n### Quality of Service\n\nKubernetes has three Quality of Service classes:\n\n- Guaranteed: requests equals limits (both cpu and memory)\n- Burstable: requests are smaller than limits (at least cpu or memory or both)\n- Best Effort: no resources defined at all\n\nQuality of Service is used also when scheduling other *Pods* and mey trigger *Pod* eviction, see [Pod evictions](#pod-evictions) under [Pod Disruption Budget](#pod-disruption-budget).\n\nThe Quality of Service priority goes as follows: Guaranteed \u003e Burstable \u003e Best Effort.\n\nIf you want more fine gained control over *Pod* scheduling and it's priority, see [Priority Class](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) resource.\n\n\u003e [!TIP]\n\u003e Use the Guaranteed QoS class for stateful workloads or anything important, that you know needs to run.\n\u003e For example asynchronous operations usually do not need QoS Guaranteed.\n\n### Resource consumption\n\n\u003e [!NOTE]\n\u003e Requires metrics server to be installed in the cluster.\n\n```shell\nkubectl top pods\n\n# or\n\nkubectl top nodes\n```\n\n## Startup, liveness, and readiness probes\n\nProbes help Kubernetes determine the state/health of a *Pod*.\n\nKubernetes supports multiple types of probes:\n\n- Execute a binary/shell script\n- TCP connection\n- HTTP probe (status code based: 2xx vs 5xx, usually `200` and `503`)\n- [gRPC probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-grpc-liveness-probe), Kubernetes 1.27+\n\n### Startup probe\n\nWait for *Pod* to start, useful when application start takes time, for example Java applications or machine learning models.\n\nFor example, Keycloak takes quite a while to start up and is a great use of startup probe, in order to to overload liveness probe.\n\n### Liveness probe\n\nIs the program running? If not, restart the *Pod*.\n\n### Readiness probe\n\nIs the program ready to accept traffic? If not, do not send traffic to the *Pod*.\n\n### Best practices\n\n- liveness probe is not dependent on external dependencies (database, cache, downstream services,...)\n- different liveness and readiness probes\n- readiness probe should stop being ready as soon as possible after receiving `SIGTERM` signal, allowing service to gracefully shutdown\n\n## Pod autoscaling\n\nOne of great Kubernetes strengths is Kubernetes capability of scaling workload up and down.\n\n### Horizontal Pod Autoscaler\n\nChanging the number of *Pods* running to handle the incoming requests efficiently.\n\n### Vertical Pod Autoscaler\n\nUnlike *HPA*, *VerticalPodAutoscaler* does not come with Kubernetes by default, but it's a separate project [on GitHub](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler) that you need to install yourself.\n\nChanging the requested amount resources on *Pods*.\n\n\u003e [!NOTE]\n\u003e This will invoke a *Pod* restart to apply the new configuration.\n\n### KEDA\n\n[KEDA (Kubernetes Event-Driven Autoscaler)](https://keda.sh/) is custom controller allowing for scaling *Pods* on different criteria/triggers than just plain Kubernetes.\n\nKEDA offers many \"scalers\", that allows to trigger a scale up or down of *Pods*. You can scale both *Deployments* or *Jobs*.\n\n## Cluster autoscaling\n\nSecond available option, which goes hand-in-hand with automatically scaling *Pods*, to efficiently utilize the cluster. So we do not need to provision number of *Nodes* to cover maximum number of *Pods* during peak times.\n\n### Cluster Autoscaler\n\nDynamically add or remove nodes from the cluster based on resource consumption. Aka how many nodes do we need to efficiently schedule all *Pods*.\n\n### Karpenter\n\nA cluster autoscaler made by AWS, to achieve higher efficiency and reduce the time from determining that the cluster needs more nodes to actually nodes becoming ready within the cluster.\n\n## Pod Disruption Budget\n\n*PodDisruptionBudget* puts another constraint on *Scheduler*, since it specifies minimum number of available *Pods* (or maximum unavailable).\n\nWhen we are running multiple *Pods* across multiple *Nodes*, we want to determine some minimum required amount of *Pods*, that make service still available without failing.\n\nFor example, when we are scaling down the cluster and *Pods* are being reshuffled across nodes, not all *Pods* may be available. *Pod Disruption Budget* says how many *Pods* can be not ready, but our service is still functioning, perhaps with increased latency.\n\n```yaml\napiVersion: policy/v1beta1\nkind: PodDisruptionBudget\nmetadata:\n  name: my-pdb\nspec:\n  minAvailable: 2 # use absolute number or percentage\n  # or alternatively set maxUnavailable\n  #maxUnavailable: 33%\n  selector:\n    matchLabels:\n      app: my-app\n```\n\nThe *PodDisruptionBudget* is using a selector (in the example above a label selector), to select to which *Pods* it applies to.\n\n### Pod evictions\n\n- Preemption evictions\n  - Scheduling a *Pod* with higher *Priority Class*\n- Node pressure evictions\n  - Node drain\n  - Scheduling a *Pod* with higher *Quality of Service*\n  - API initiated (for example: deleting *Pod* via kubectl)\n  - Taint-based\n\n## Helm\n\n- [Website](https://helm.sh/)\n- [Docs](https://helm.sh/docs/)\n\nHelm is a package manager for Kubernetes.\n\n### Helm Chart\n\nA package of all manifest for an application. Containing everything, that you need to run the application. Chart can also determine a minimal Kubernetes version it supports, that is especially important when supporting multiple Kubernetes versions and you make breaking changes in the Chart.\n\n### Helm Repository\n\nSupports public and private repositories.\n\nCan be hosted on GitHub, GitLab, AWS S3, Google Cloud Storage, Azure Blob Storage, and more.\n\n### `helm install`\n\nInstalls Helm chart to the cluster, creating Helm \"release\".\n\n```shell\nhelm install my-release ./my-chart\n\n# upgrade\nhelm upgrade my-release ./my-chart\n\n# install and upgrade\nhelm upgrade --install my-release ./my-chart\n\n# install from repository\nhelm repo add stable https://charts.helm.sh/stable\nhelm repo update\nhelm install my-release stable/mysql\n\n# install from oci repository\nhelm install my-release oci://registry.example.com/some/chart\n\n# uninstall\nhelm uninstall my-release\n```\n\n### `helm rollback`\n\n```shell\nhelm rollback my-release 1\n```\n\n\u003e [!NOTE]\n\u003e Rollback will actually create a new release (incrementing sequence number) instead of going back.\n\u003e Values/configuration will be copied from the old release to the new one.\n\n### `helm uninstall`\n\n```shell\nhelm uninstall my-release\n```\n\n## Helm Controller\n\nSee: [Helm Controller GitHub repository](https://github.com/k3s-io/helm-controller).\n\nHelm Controller is an external addon not installed by Helm, you need to install it yourself.\n\nOr on Kubernetes distributions like k3s or RKE2, Helm controller is available by default.\n\nInstalls Helm release from Kubernetes Custom Resource.\n\n- `HelmRelease`\n- `HelmReleaseConfig`\n\n\u003e [!IMPORTANT]\n\u003e `k3s-io/helm-controller` and `fluxcd/helm-controller` are two different projects, even though they carry the same name!\n\n## Kustomize\n\n- [Website](https://kustomize.io/)\n- [Docs](https://kubectl.docs.kubernetes.io/)\n\n*Kustomize* is using overlays and hierarchy-based merging of manifests unlike Helm, which is creating packages.\n\n## GitOps\n\nStatic manifests, Helm charts, and *Kustomize* are stored in Git repository and are applied to the cluster from there on pull-based model. Usually a pro-active solution is hosted in the cluster.\n\n- [ArgoCD](https://argoproj.github.io/argo-cd/)\n- [Flux](https://fluxcd.io/)\n\n## Networking\n\nKubernetes by default runs two networks backed by CNI plugin and kube-proxy on each node.\n\nLocally on each node, networking is handled by [**iptables**](https://en.wikipedia.org/wiki/Iptables), altho there is an [ongoing effort](https://github.com/kubernetes/enhancements/blob/master/keps/sig-network/3866-nftables-proxy/README.md) to migrate to more modern and performant [**nftables**](https://nftables.org/).\n\n### Subnets\n\nIt is useful to know your CIDRs, when debugging issues, so you can spot where the network traffic is heading and if it's a correct location.\n\nSubnet CIDRs:\n\n- *Service* subnet (default CIDR: `10.43.0.0/16`)\n- *Pod* subnet (default CIDR: `10.42.0.0/16`)\n\n\u003e [!NOTE]\n\u003e CIDRs may vary depending on your Kubernetes distribution or cluster configuration.\n\n### Network Policy\n\n*NetworkPolicy* is a Kubernetes resource describing L4 (TCP/UDP) policies of what kind of workload can talk to what.\n\nIncluding in-cluster resources (other workload, DNS,...), ingress, and egress policies.\n\nFor example highly sensitive workload may not be allowed to connect to anything outside of the cluster, to prevent leaking of sensitive information in case of an attack.\n\n### Cluster DNS addon\n\n- kube-dns\n- coredns (mostly default today)\n\n### Cilium Network Policy\n\nIf you are using [Cilium](https://cilium.io/) as your CNI plugin, you can use the *CiliumNetworkPolicy*, which allows for more fine-grained control over the network traffic. Thanks to introducing L7 (HTTP) policies.\n\n### CNI plugins\n\nKubernetes offers an API interface, that allows vendors to develop custom networking solutions, that will handle networking between *Pods* and *Services*.\n\nList of commonly used CNI plugins:\n\n- [Flannel](https://github.com/flannel-io/flannel)\n- [Calico](https://www.projectcalico.org/)\n- [Cilium](https://cilium.io/)\n- [AWS VPC CNI](https://github.com/aws/amazon-vpc-cni-k8s)\n\n\u003e [!NOTE]\n\u003e AWS VPC CNI is CNI plugin that allows your nodes to use AWS Elastic Network Interface on your EC2 instances for Kubernetes networking. Using this is recommended to utilized existing systems and not creating another networking layer on top of it.\n\n## Security\n\n### Pod Security Standards\n\nKubernetes 1.26+\n\n\u003e The Pod Security Standards define three different policies to broadly cover the security spectrum. These policies are cumulative and range from highly-permissive to highly-restrictive. This guide outlines the requirements of each policy.\n\nPolicies:\n\n- **Privileged**: Unrestricted policy, providing the widest possible level of permissions. This policy allows for known privilege escalations.\n- **Baseline**: Minimally restrictive policy which prevents known privilege escalations. Allows the default (minimally specified) Pod configuration.\n- **Restricted**: Heavily restricted policy, following current Pod hardening best practices.\n\nThe *Privileged* policy is purposely-open, and entirely unrestricted. This type of policy is typically aimed at system- and infrastructure-level workloads managed by privileged, trusted users.\n\nThe *Baseline* policy is aimed at ease of adoption for common containerized workloads while preventing known privilege escalations. This policy is targeted at application operators and developers of non-critical applications.\n\nThe *Restricted* policy is aimed at enforcing current Pod hardening best practices, at the expense of some compatibility. It is targeted at operators and developers of security-critical applications, as well as lower-trust users.\n\nPolicy Installation:\n\n- using built-in PodSecurity Admission Controller\n- third party, [Kyverno](https://kyverno.io/policies/pod-security/)\n- third party, [OPA Gatekeeper](https://github.com/open-policy-agent/gatekeeper)\n- third party, [Kubewarden](https://github.com/kubewarden)\n\nFor correct setup, consult [Kubernetes documentation](https://kubernetes.io/docs/concepts/security/pod-security-standards/).\n\n### Pod Security Admission\n\nKubernetes 1.25+\n\n\u003e **Pod Security levels**\n\u003e\n\u003e Pod Security admission places requirements on a Pod's Security Context and other related fields according to the three levels defined by the Pod Security Standards: privileged, baseline, and restricted. Refer to the Pod Security Standards page for an in-depth look at those requirements.\n\u003e\n\u003e **Pod Security Admission labels for namespaces**\n\u003e\n\u003e Once the feature is enabled or the webhook is installed, you can configure namespaces to define the admission control mode you want to use for pod security in each namespace. Kubernetes defines a set of labels that you can set to define which of the predefined Pod Security Standard levels you want to use for a namespace. The label you select defines what action the control plane takes if a potential violation is detected:\n\u003e\n\u003e - enforce Policy violations will cause the pod to be rejected.\n\u003e - audit Policy violations will trigger the addition of an audit annotation to the event recorded in the audit log, but are otherwise allowed.\n\u003e - warn Policy violations will trigger a user-facing warning, but are otherwise allowed.\n\u003e\n\u003e ```yaml\n\u003e # MODE must be one of `enforce`, `audit`, or `warn`.\n\u003e # LEVEL must be one of `privileged`, `baseline`, or `restricted`.\n\u003e pod-security.kubernetes.io/\u003cMODE\u003e: \u003cLEVEL\u003e\n\u003e ```\n\nAs mentioned in [Pod Security Standards](#pod-security-standards), you can use different engines/implementations to enforce/audit/warn upon. For example, [Kyverno](https://kyverno.io/).\n\n### Pod Security Policy\n\n- Replaced with [Pod Security Standards](#pod-security-standards)\n- Removed in Kubernetes 1.25 (released: 23 August, 2022)\n\n## Metrics\n\nAll Kubernetes components are exposing metrics in Prometheus format.\n\n### Prometheus\n\nSee: [Prometheus website](https://prometheus.io/).\n\nPrometheus is open-source and CNCF Graduated project (like Kubernetes), built originally at SoundCloud. It is designed to scrape monitor applications metrics. Also evaluate user-defined rules and alert when rules are broken.\n\n### Prometheus Operator\n\nSee: [Prometheus Operator website](https://prometheus-operator.dev/).\n\nPrometheus Operator helps you managed Prometheus and it's monitoring targets on Kubernetes. Both *Pods* behind *Services* or *Pods* directly (stateful apps).\n\nOver time, Prometheus Operator (and it's CRDs) become widely supported by various projects and is de-factor open-source monitoring standard on Kubernetes today.\n\n### kube-prometheus-stack Helm Chart\n\nIf you need something quickly and out-of-the-box, [kube-prometheus-stack](https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack) is a great choice!\n\nThe Helm Chart installs by default the following components:\n\n- Prometheus Operator CRDs\n- Prometheus Operator\n- Prometheus\n- Alertmanager\n- Grafana\n- Node exporter\n\n## Logging\n\n### Elastic Cloud on Kubernetes (ECK)\n\n### Grafana Loki\n\n### Other\n\nCloud integrations like AWS CloudWatch, Azure Monitor, Google Cloud Operations Suite.\n\n- Fluentd\n- Splunk\n- DataDog\n\n## Extending Kubernetes\n\n### Operators\n\nOperators are extensions to Kubernetes that make use of custom resources to manage applications and their components.\n\nFor example, [*RedHat OpenShift*](https://www.redhat.com/en/technologies/cloud-computing/openshift) (Kubernetes distribution by RedHat) is heavily utilizing operators.\n\n### Kubebuilder\n\nKubernetes SDK for building *Operators* and *Controllers*.\n\n- [The Kubebuilder Book](https://book.kubebuilder.io/)\n- [GitHub repository](https://github.com/kubernetes-sigs/kubebuilder)\n\n### Operator Framework\n\nIf you want to use *Operator Lifecycle Manager* or integrate more with Kubernetes distribution like *RedHat OpenShift*, use *Operator Framework*.\n\n*Operator Framework* is built on top of *Kubebuilder*, so you do not need to learn new APIs, it just brings some more functionality and integration with *OLM*.\n\nThe Operator Framework projects offers [Operator SDK](https://github.com/operator-framework/operator-sdk).\n\n### Operator Lifecycle Manager (OLM)\n\nOperator to manage operators.\n\nIntegrated with *RedHat OpenShift*, making it easy installing operators from OpenShift Admin Console.\n\n### Cluster API\n\n[Cluster API website](https://cluster-api.sigs.k8s.io/)\n\nKubernetes project's solution to provisioning, operating, and upgrading multiple clusters.\n\n## Links\n\n- [Kubernetes documentation](https://kubernetes.io/docs/home/)\n- [Helm Controller GitHub repository](https://github.com/k3s-io/helm-controller)\n- [Ingress NGINX](https://kubernetes.github.io/ingress-nginx/)\n- [Gateway API](https://gateway-api.sigs.k8s.io/)\n- [KEDA (Kubernetes Event-Driven Autoscaler)](https://keda.sh/)\n- [Helm](https://helm.sh/)\n- [ArtifactHub (helm charts)](https://artifacthub.io/)\n- [OperatorHub](https://operatorhub.io/)\n- [Kyverno](https://kyverno.io/)\n- [RedHat OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift)\n- [Cluster API](https://cluster-api.sigs.k8s.io/)\n- [Prometheus](https://prometheus.io/)\n- [Prometheus Operator](https://prometheus-operator.dev/)\n\n## Questions?\n\n## Thank you, that's all 👋\n\n## Vojtěch Mareš\n\n- Email me at [iam@vojtechmares.com](mailto:iam@vojtechmares.com)\n- Website: [vojtechmares.com](https://www.vojtechmares.com)\n- My other trainings: [vojtechmares.com/#skoleni](https://www.vojtechmares.com/#skoleni)\n- X: [@vojtechmares](https://x.com/vojtechmares)\n- GitHub: [github.com/vojtechmares](https://github.com/vojtechmares)\n- LinkedIn: [linkedin.com/in/vojtech-mares](https://www.linkedin.com/in/vojtech-mares)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvojtechmares%2Fkubernetes-training","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvojtechmares%2Fkubernetes-training","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvojtechmares%2Fkubernetes-training/lists"}