[
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, sex characteristics, gender identity and expression,\nlevel of experience, education, socio-economic status, nationality, personal\nappearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or\n advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic\n address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or\nreject comments, commits, code, wiki edits, issues, and other contributions\nthat are not aligned to this Code of Conduct, or to ban temporarily or\npermanently any contributor for other behaviors that they deem inappropriate,\nthreatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at dgkanatsios@outlook.com. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an incident.\nFurther details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,\navailable at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see\nhttps://www.contributor-covenant.org/faq\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Dimitris-Ilias Gkanatsios\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n[![unofficial Google Analytics for GitHub](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises)](https://github.com/dgkanatsios/gaforgithub)\n\n# CKAD Exercises\n\nA set of exercises that helped me prepare for the [Certified Kubernetes Application Developer](https://www.cncf.io/certification/ckad/) exam, offered by the Cloud Native Computing Foundation, organized by curriculum domain.\nThey may as well serve as learning and practicing with Kubernetes.\n\nMake a mental note of the breadcrumb at the start of the excercise section, to quickly locate the relevant document in kubernetes.io.\nIt is recommended that you read the official documents before attempting exercises below it.\nDuring the exam, you are only allowed to refer to official documentation from a browser window within the exam VM.\nA Quick Reference box will contain helpful links for each exam exercise as well.\n\n## Contents\n\n- [Core Concepts - 13%](a.core_concepts.md)\n- [Multi-container pods - 10%](b.multi_container_pods.md)\n- [Pod design - 20%](c.pod_design.md)\n- [Configuration - 18%](d.configuration.md)\n- [Observability - 18%](e.observability.md)\n- [Services and networking - 13%](f.services.md)\n- [State persistence - 8%](g.state.md)\n- [helm](h.helm.md)\n- [Custom Resource Definitions](i.crd.md)\n\n> If your work is related to multiplayer game servers, checkout out [thundernetes, a brand new project to host game servers on Kubernetes](https://github.com/PlayFab/thundernetes)!\n\n### Can I PR? There is an error/an alternative way/an extra question/solution I can offer\n\nAbsolutely! Feel free to PR and edit/add questions and solutions, but please stick to the existing format.\n\nIf this repo has helped you in any way, feel free to post on [discussions](https://github.com/dgkanatsios/CKAD-exercises/discussions) or buy me a coffee!\n\n<a href=\"https://www.buymeacoffee.com/dgkanatsios\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"41\" width=\"174\"></a>\n"
  },
  {
    "path": "a.core_concepts.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/core_concepts&empty)\n# Core Concepts (13%)\n\nkubernetes.io > Documentation > Reference > kubectl CLI > [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/)\n\nkubernetes.io > Documentation > Tasks > Monitoring, Logging, and Debugging > [Get a Shell to a Running Container](https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/)\n\nkubernetes.io > Documentation > Tasks > Access Applications in a Cluster > [Configure Access to Multiple Clusters](https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/)\n\nkubernetes.io > Documentation > Tasks > Access Applications in a Cluster > [Accessing Clusters](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/) using API\n\nkubernetes.io > Documentation > Tasks > Access Applications in a Cluster > [Use Port Forwarding to Access Applications in a Cluster](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/)\n\n### Create a namespace called 'mynamespace' and a pod with image nginx called nginx on this namespace\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create namespace mynamespace\nkubectl run nginx --image=nginx --restart=Never -n mynamespace\n```\n\n</p>\n</details>\n\n### Create the pod that was just described using YAML\n\n<details><summary>show</summary>\n<p>\n\nEasily generate YAML with:\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --dry-run=client -n mynamespace -o yaml > pod.yaml\n```\n\n```bash\ncat pod.yaml\n```\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\n  namespace: mynamespace\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\n```\n\nAlternatively, you can run in one line\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml | kubectl create -n mynamespace -f -\n```\n\n</p>\n</details>\n\n### Create a busybox pod (using kubectl command) that runs the command \"env\". Run it and see the output\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --image=busybox --command --restart=Never -it --rm -- env # -it will help in seeing the output, --rm will immediately delete the pod after it exits\n# or, just run it without -it\nkubectl run busybox --image=busybox --command --restart=Never -- env\n# and then, check its logs\nkubectl logs busybox\n```\n\n</p>\n</details>\n\n### Create a busybox pod (using YAML) that runs the command \"env\". Run it and see the output\n\n<details><summary>show</summary>\n<p>\n\n```bash\n# create a  YAML template with this command\nkubectl run busybox --image=busybox --restart=Never --dry-run=client -o yaml --command -- env > envpod.yaml\n# see it\ncat envpod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: busybox\n  name: busybox\nspec:\n  containers:\n  - command:\n    - env\n    image: busybox\n    name: busybox\n    resources: {}\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\n# apply it and then see the logs\nkubectl apply -f envpod.yaml\nkubectl logs busybox\n```\n\n</p>\n</details>\n\n### Get the YAML for a new namespace called 'myns' without creating it\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create namespace myns -o yaml --dry-run=client\n```\n\n</p>\n</details>\n\n### Create the YAML for a new ResourceQuota called 'myrq' with hard limits of 1 CPU, 1G memory and 2 pods without creating it\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create quota myrq --hard=cpu=1,memory=1G,pods=2 --dry-run=client -o yaml\n```\n\n</p>\n</details>\n\n### Get pods on all namespaces\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po --all-namespaces\n```\nAlternatively \n\n```bash\nkubectl get po -A\n```\n</p>\n</details>\n\n### Create a pod with image nginx called nginx and expose traffic on port 80\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --port=80\n```\n\n</p>\n</details>\n\n### Change pod's image to nginx:1.24.0. Observe that the container will be restarted as soon as the image gets pulled\n\n<details><summary>show</summary>\n<p>\n\n*Note*: The `RESTARTS` column should contain 0 initially (ideally - it could be any number)\n\n```bash\n# kubectl set image POD/POD_NAME CONTAINER_NAME=IMAGE_NAME:TAG\nkubectl set image pod/nginx nginx=nginx:1.24.0\nkubectl describe po nginx # you will see an event 'Container will be killed and recreated'\nkubectl get po nginx -w # watch it\n```\n\n*Note*: some time after changing the image, you should see that the value in the `RESTARTS` column has been increased by 1, because the container has been restarted, as stated in the events shown at the bottom of the `kubectl describe pod` command:\n\n```\nEvents:\n  Type    Reason     Age                  From               Message\n  ----    ------     ----                 ----               -------\n[...]\n  Normal  Killing    100s                 kubelet, node3     Container pod1 definition changed, will be restarted\n  Normal  Pulling    100s                 kubelet, node3     Pulling image \"nginx:1.24.0\"\n  Normal  Pulled     41s                  kubelet, node3     Successfully pulled image \"nginx:1.24.0\"\n  Normal  Created    36s (x2 over 9m43s)  kubelet, node3     Created container pod1\n  Normal  Started    36s (x2 over 9m43s)  kubelet, node3     Started container pod1\n```\n\n*Note*: you can check pod's image by running\n\n```bash\nkubectl get po nginx -o jsonpath='{.spec.containers[].image}{\"\\n\"}'\n```\n\n</p>\n</details>\n\n### Get nginx pod's ip created in previous step, use a temp busybox image to wget its '/'\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po -o wide # get the IP, will be something like '10.1.1.131'\n# create a temp busybox pod\nkubectl run busybox --image=busybox --rm -it --restart=Never -- wget -O- 10.1.1.131:80\n```\n\nAlternatively you can also try a more advanced option:\n\n```bash\n# Get IP of the nginx pod\nNGINX_IP=$(kubectl get pod nginx -o jsonpath='{.status.podIP}')\n# create a temp busybox pod\nkubectl run busybox --image=busybox --env=\"NGINX_IP=$NGINX_IP\" --rm -it --restart=Never -- sh -c 'wget -O- $NGINX_IP:80'\n``` \n\nOr just in one line:\n\n```bash\nkubectl run busybox --image=busybox --rm -it --restart=Never -- wget -O- $(kubectl get pod nginx -o jsonpath='{.status.podIP}:{.spec.containers[0].ports[0].containerPort}')\n```\n\n</p>\n</details>\n\n### Get pod's YAML\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po nginx -o yaml\n# or\nkubectl get po nginx -oyaml\n# or\nkubectl get po nginx --output yaml\n# or\nkubectl get po nginx --output=yaml\n```\n\n</p>\n</details>\n\n### Get information about the pod, including details about potential issues (e.g. pod hasn't started)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl describe po nginx\n```\n\n</p>\n</details>\n\n### Get pod logs\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl logs nginx\n```\n\n</p>\n</details>\n\n### If pod crashed and restarted, get logs about the previous instance\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl logs nginx -p\n# or\nkubectl logs nginx --previous\n```\n\n</p>\n</details>\n\n### Execute a simple shell on the nginx pod\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl exec -it nginx -- /bin/sh\n```\n\n</p>\n</details>\n\n### Create a busybox pod that echoes 'hello world' and then exits\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --image=busybox -it --restart=Never -- echo 'hello world'\n# or\nkubectl run busybox --image=busybox -it --restart=Never -- /bin/sh -c 'echo hello world'\n```\n\n</p>\n</details>\n\n### Do the same, but have the pod deleted automatically when it's completed\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --image=busybox -it --rm --restart=Never -- /bin/sh -c 'echo hello world'\nkubectl get po # nowhere to be found :)\n```\n\n</p>\n</details>\n\n### Create an nginx pod and set an env value as 'var1=val1'. Check the env value existence within the pod\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --env=var1=val1\n# then\nkubectl exec -it nginx -- env\n# or\nkubectl exec -it nginx -- sh -c 'echo $var1'\n# or\nkubectl describe po nginx | grep val1\n# or\nkubectl run nginx --restart=Never --image=nginx --env=var1=val1 -it --rm -- env\n# or\nkubectl run nginx --image nginx --restart=Never --env=var1=val1 -it --rm -- sh -c 'echo $var1'\n```\n\n</p>\n</details>\n"
  },
  {
    "path": "b.multi_container_pods.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/multi_container&empty)\n# Multi-container Pods (10%)\n\n### Create a Pod with two containers, both with image busybox and command \"echo hello; sleep 3600\". Connect to the second container and run 'ls'\n\n<details><summary>show</summary>\n<p>\n\nThe easiest way to do it is create a pod with a single container and save its definition in a YAML file:\n\n```bash\nkubectl run busybox --image=busybox --restart=Never -o yaml --dry-run=client -- /bin/sh -c 'echo hello;sleep 3600' > pod.yaml\nvi pod.yaml\n```\n\nCopy/paste the container related values, so your final YAML should contain the following two containers (make sure those containers have a different name):\n\n```YAML\ncontainers:\n  - args:\n    - /bin/sh\n    - -c\n    - echo hello;sleep 3600\n    image: busybox\n    imagePullPolicy: IfNotPresent\n    name: busybox\n    resources: {}\n  - args:\n    - /bin/sh\n    - -c\n    - echo hello;sleep 3600\n    image: busybox\n    name: busybox2\n```\n\n```bash\nkubectl create -f pod.yaml\n# Connect to the busybox2 container within the pod\nkubectl exec -it busybox -c busybox2 -- /bin/sh\nls\nexit\n\n# or you can do the above with just a one-liner\nkubectl exec -it busybox -c busybox2 -- ls\n\n# you can do some cleanup\nkubectl delete po busybox\n```\n\n</p>\n</details>\n\n### Create a pod with an nginx container exposed on port 80. Add a busybox init container which downloads a page using 'echo \"Test\" > /work-dir/index.html'. Make a volume of type emptyDir and mount it in both containers. For the nginx container, mount it on \"/usr/share/nginx/html\" and for the initcontainer, mount it on \"/work-dir\". When done, get the IP of the created pod and create a busybox pod and run \"wget -O- IP\"\n\n<details><summary>show</summary>\n<p>\n\nThe easiest way to do it is create a pod with a single container and save its definition in a YAML file:\n\n```bash\nkubectl run box --image=nginx --restart=Never --port=80 --dry-run=client -o yaml > pod-init.yaml\n```\n\nCopy/paste the container related values, so your final YAML should contain the volume and the initContainer:\n\nVolume:\n\n```YAML\ncontainers:\n- image: nginx\n...\n  volumeMounts:\n  - name: vol\n    mountPath: /usr/share/nginx/html\nvolumes:\n- name: vol\n  emptyDir: {}\n```\n\ninitContainer:\n\n```YAML\n...\ninitContainers:\n- args:\n  - /bin/sh\n  - -c\n  - echo \"Test\" > /work-dir/index.html\n  image: busybox\n  name: box\n  volumeMounts:\n  - name: vol\n    mountPath: /work-dir\n```\n\nIn total you get:\n\n```YAML\n\napiVersion: v1\nkind: Pod\nmetadata:\n  labels:\n    run: box\n  name: box\nspec:\n  initContainers: \n  - args: \n    - /bin/sh \n    - -c \n    - echo \"Test\" > /work-dir/index.html\n    image: busybox \n    name: box \n    volumeMounts: \n    - name: vol \n      mountPath: /work-dir \n  containers:\n  - image: nginx\n    name: nginx\n    ports:\n    - containerPort: 80\n    volumeMounts: \n    - name: vol \n      mountPath: /usr/share/nginx/html \n  volumes: \n  - name: vol \n    emptyDir: {} \n```\n\n```bash\n# Apply pod\nkubectl apply -f pod-init.yaml\n\n# Get IP\nkubectl get po -o wide\n\n# Execute wget\nkubectl run box-test --image=busybox --restart=Never -it --rm -- /bin/sh -c \"wget -O- $(kubectl get pod box -o jsonpath='{.status.podIP}')\"\n\n# you can do some cleanup\nkubectl delete po box\n```\n\n</p>\n</details>\n\n"
  },
  {
    "path": "c.pod_design.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/pod_design&empty)\n# Pod design (20%)\n\n[Labels And Annotations](#labels-and-annotations)\n\n[Deployments](#deployments)\n\n[Jobs](#jobs)\n\n[Cron Jobs](#cron-jobs)\n\n## Labels and Annotations\nkubernetes.io > Documentation > Concepts > Overview > Working with Kubernetes Objects > [Labels and Selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors)\n\n### Create 3 pods with names nginx1,nginx2,nginx3. All of them should have the label app=v1\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx1 --image=nginx --restart=Never --labels=app=v1\nkubectl run nginx2 --image=nginx --restart=Never --labels=app=v1\nkubectl run nginx3 --image=nginx --restart=Never --labels=app=v1\n# or\nfor i in `seq 1 3`; do kubectl run nginx$i --image=nginx -l app=v1 ; done\n```\n\n</p>\n</details>\n\n### Show all labels of the pods\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po --show-labels\n```\n\n</p>\n</details>\n\n### Change the labels of pod 'nginx2' to be app=v2\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl label po nginx2 app=v2 --overwrite\n# or edit the pod yaml\nkubectl edit po nginx2\n```\n\n</p>\n</details>\n\n### Get the label 'app' for the pods (show a column with APP labels)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po -L app\n# or\nkubectl get po --label-columns=app\n```\n\n</p>\n</details>\n\n### Get only the 'app=v2' pods\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po -l app=v2\n# or\nkubectl get po -l 'app in (v2)'\n# or\nkubectl get po --selector=app=v2\n```\n\n</p>\n</details>\n\n### Get 'app=v2' and not 'tier=frontend' pods\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po -l app=v2,tier!=frontend\n# or\nkubectl get po -l 'app in (v2), tier notin (frontend)'\n# or\nkubectl get po --selector=app=v2,tier!=frontend\n```\n\n</p>\n</details>\n\n### Add a new label tier=web to all pods having 'app=v2' or 'app=v1' labels\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl label po -l \"app in(v1,v2)\" tier=web\n```\n</p>\n</details>\n\n\n### Add an annotation 'owner: marketing' to all pods having 'app=v2' label\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl annotate po -l \"app=v2\" owner=marketing\n```\n</p>\n</details>\n\n### Remove the 'app' label from the pods we created before\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl label po nginx1 nginx2 nginx3 app-\n# or\nkubectl label po nginx{1..3} app-\n# or\nkubectl label po -l app app-\n```\n\n</p>\n</details>\n\n### Annotate pods nginx1, nginx2, nginx3 with \"description='my description'\" value\n\n<details><summary>show</summary>\n<p>\n\n\n```bash\nkubectl annotate po nginx1 nginx2 nginx3 description='my description'\n\n#or\n\nkubectl annotate po nginx{1..3} description='my description'\n```\n\n</p>\n</details>\n\n### Check the annotations for pod nginx1\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl annotate pod nginx1 --list\n\n# or\n\nkubectl describe po nginx1 | grep -i 'annotations'\n\n# or\n\nkubectl get po nginx1 -o custom-columns=Name:metadata.name,ANNOTATIONS:metadata.annotations.description\n```\n\nAs an alternative to using `| grep` you can use jsonPath like `kubectl get po nginx1 -o jsonpath='{.metadata.annotations}{\"\\n\"}'`\n\n</p>\n</details>\n\n### Remove the annotations for these three pods\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl annotate po nginx{1..3} description- owner-\n```\n\n</p>\n</details>\n\n### Remove these pods to have a clean state in your cluster\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl delete po nginx{1..3}\n```\n\n</p>\n</details>\n\n## Pod Placement\n\n### Create a pod that will be deployed to a Node that has the label 'accelerator=nvidia-tesla-p100'\n\n<details><summary>show</summary>\n<p>\n\nAdd the label to a node:\n\n```bash\nkubectl label nodes <your-node-name> accelerator=nvidia-tesla-p100\nkubectl get nodes --show-labels\n```\n\nWe can use the 'nodeSelector' property on the Pod YAML:\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  name: cuda-test\nspec:\n  containers:\n    - name: cuda-test\n      image: \"k8s.gcr.io/cuda-vector-add:v0.1\"\n  nodeSelector: # add this\n    accelerator: nvidia-tesla-p100 # the selection label\n```\n\nYou can easily find out where in the YAML it should be placed by:\n\n```bash\nkubectl explain po.spec\n```\n\nOR:\nUse node affinity (https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/#schedule-a-pod-using-required-node-affinity)\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  name: affinity-pod\nspec:\n  affinity:\n    nodeAffinity:\n      requiredDuringSchedulingIgnoredDuringExecution:\n        nodeSelectorTerms:\n        - matchExpressions:\n          - key: accelerator\n            operator: In\n            values:\n            - nvidia-tesla-p100\n  containers:\n    ...\n```\n\n</p>\n</details>\n\n### Create a pod that will be placed on node `node01` using `nodeName`\n\n<details><summary>show</summary>\n<p>\n\n`nodeName` forces the Pod to be bound to a specific node (bypassing the scheduler). For more details, see the official docs: [https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodename)\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: nodename-pod\nspec:\n  nodeName: node01\n  containers:\n  - name: nodename-con\n    image: nginx  \n```\n\nVerify which node it landed on:\n\n```bash\nkubectl get pod nodename-pod -o wide\n```\n\n</p>\n</details>\n\n### Taint a node with key `tier` and value `frontend` with the effect `NoSchedule`. Then, create a pod that tolerates this taint.\n\n<details><summary>show</summary>\n<p>\n\nTaint a node:\n\n```bash\nkubectl taint node node1 tier=frontend:NoSchedule # key=value:Effect\nkubectl describe node node1 # view the taints on a node\n```\n\nAnd to tolerate the taint:\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: frontend\nspec:\n  containers:\n  - name: nginx\n    image: nginx\n  tolerations:\n  - key: \"tier\"\n    operator: \"Equal\"\n    value: \"frontend\"\n    effect: \"NoSchedule\"\n```\n\n</p>\n</details>\n\n### Create a pod that will be placed on node `controlplane`. Use nodeSelector and tolerations.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nvi pod.yaml\n```\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: frontend\nspec:\n  containers:\n  - name: nginx\n    image: nginx\n  nodeSelector:\n    kubernetes.io/hostname: controlplane\n  tolerations:\n  - key: \"node-role.kubernetes.io/control-plane\"\n    operator: \"Exists\"\n    effect: \"NoSchedule\"\n```\n\n```bash\nkubectl create -f pod.yaml\n```\n\n</p>\n</details>\n\n## Deployments\n\nkubernetes.io > Documentation > Concepts > Workloads > Workload Resources > [Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment)\n\n### Create a deployment with image nginx:1.18.0, called nginx, having 2 replicas, defining port 80 as the port that this container exposes (don't create a service for this deployment)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create deployment nginx  --image=nginx:1.18.0  --dry-run=client -o yaml > deploy.yaml\nvi deploy.yaml\n# change the replicas field from 1 to 2\n# add this section to the container spec and save the deploy.yaml file\n# ports:\n#   - containerPort: 80\nkubectl apply -f deploy.yaml\n```\n\nor, do something like:\n\n```bash\nkubectl create deployment nginx  --image=nginx:1.18.0  --dry-run=client -o yaml | sed 's/replicas: 1/replicas: 2/g'  | sed 's/image: nginx:1.18.0/image: nginx:1.18.0\\n        ports:\\n        - containerPort: 80/g' | kubectl apply -f -\n```\n\nor,\n```bash\nkubectl create deploy nginx --image=nginx:1.18.0 --replicas=2 --port=80\n```\n\n</p>\n</details>\n\n### View the YAML of this deployment\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get deploy nginx -o yaml\n```\n\n</p>\n</details>\n\n### View the YAML of the replica set that was created by this deployment\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl describe deploy nginx # you'll see the name of the replica set on the Events section and in the 'NewReplicaSet' property\n# OR you can find rs directly by:\nkubectl get rs -l run=nginx # if you created deployment by 'run' command\nkubectl get rs -l app=nginx # if you created deployment by 'create' command\n# you could also just do kubectl get rs\nkubectl get rs nginx-7bf7478b77 -o yaml\n```\n\n</p>\n</details>\n\n### Get the YAML for one of the pods\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po # get all the pods\n# OR you can find pods directly by:\nkubectl get po -l run=nginx # if you created deployment by 'run' command\nkubectl get po -l app=nginx # if you created deployment by 'create' command\nkubectl get po nginx-7bf7478b77-gjzp8 -o yaml\n```\n\n</p>\n</details>\n\n### Check how the deployment rollout is going\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout status deploy nginx\n```\n\n</p>\n</details>\n\n### Update the nginx image to nginx:1.19.8\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl set image deploy nginx nginx=nginx:1.19.8\n# alternatively...\nkubectl edit deploy nginx # change the .spec.template.spec.containers[0].image\n```\n\nThe syntax of the 'kubectl set image' command is `kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N [options]`\n\n</p>\n</details>\n\n### Check the rollout history and confirm that the replicas are OK\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout history deploy nginx\nkubectl get deploy nginx\nkubectl get rs # check that a new replica set has been created\nkubectl get po\n```\n\n</p>\n</details>\n\n### Undo the latest rollout and verify that new pods have the old image (nginx:1.18.0)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout undo deploy nginx\n# wait a bit\nkubectl get po # select one 'Running' Pod\nkubectl describe po nginx-5ff4457d65-nslcl | grep -i image # should be nginx:1.18.0\n```\n\n</p>\n</details>\n\n### Do an on-purpose update of the deployment with a wrong image nginx:1.91\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl set image deploy nginx nginx=nginx:1.91\n# or\nkubectl edit deploy nginx\n# change the image to nginx:1.91\n# vim tip: type (without quotes) '/image' and press Enter, to navigate quickly\n```\n\n</p>\n</details>\n\n### Verify that something's wrong with the rollout\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout status deploy nginx\n# or\nkubectl get po # you'll see 'ErrImagePull' or 'ImagePullBackOff'\n```\n\n</p>\n</details>\n\n\n### Return the deployment to the second revision (number 2) and verify the image is nginx:1.19.8\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout undo deploy nginx --to-revision=2\nkubectl describe deploy nginx | grep Image:\nkubectl rollout status deploy nginx # Everything should be OK\n```\n\n</p>\n</details>\n\n### Check the details of the fourth revision (number 4)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout history deploy nginx --revision=4 # You'll also see the wrong image displayed here\n```\n\n</p>\n</details>\n\n### Scale the deployment to 5 replicas\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl scale deploy nginx --replicas=5\nkubectl get po\nkubectl describe deploy nginx\n```\n\n</p>\n</details>\n\n### Autoscale the deployment, pods between 5 and 10, targeting CPU utilization at 80%\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl autoscale deploy nginx --min=5 --max=10 --cpu-percent=80\n# view the horizontalpodautoscalers.autoscaling for nginx\nkubectl get hpa nginx\n```\n\n</p>\n</details>\n\n### Pause the rollout of the deployment\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout pause deploy nginx\n```\n\n</p>\n</details>\n\n### Update the image to nginx:1.19.9 and check that there's nothing going on, since we paused the rollout\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl set image deploy nginx nginx=nginx:1.19.9\n# or\nkubectl edit deploy nginx\n# change the image to nginx:1.19.9\nkubectl rollout history deploy nginx # no new revision\n```\n\n</p>\n</details>\n\n### Resume the rollout and check that the nginx:1.19.9 image has been applied\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl rollout resume deploy nginx\nkubectl rollout history deploy nginx\nkubectl rollout history deploy nginx --revision=6 # insert the number of your latest revision\n```\n\n</p>\n</details>\n\n### Delete the deployment and the horizontal pod autoscaler you created\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl delete deploy nginx\nkubectl delete hpa nginx\n\n# or\nkubectl delete deploy/nginx hpa/nginx\n```\n</p>\n</details>\n\n### Implement canary deployment by running two instances of nginx marked as version=v1 and version=v2 so that the load is balanced at 75%-25% ratio\n\n<details><summary>show</summary>\n<p>\n\nDeploy 3 replicas of v1:\n```\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: my-app-v1\n  labels:\n    app: my-app\nspec:\n  replicas: 3\n  selector:\n    matchLabels:\n      app: my-app\n      version: v1\n  template:\n    metadata:\n      labels:\n        app: my-app\n        version: v1\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n        ports:\n        - containerPort: 80\n        volumeMounts:\n        - name: workdir\n          mountPath: /usr/share/nginx/html\n      initContainers:\n      - name: install\n        image: busybox:1.28\n        command:\n        - /bin/sh\n        - -c\n        - \"echo version-1 > /work-dir/index.html\"\n        volumeMounts:\n        - name: workdir\n          mountPath: \"/work-dir\"\n      volumes:\n      - name: workdir\n        emptyDir: {}\n```\n\nCreate the service:\n```\napiVersion: v1\nkind: Service\nmetadata:\n  name: my-app-svc\n  labels:\n    app: my-app\nspec:\n  type: ClusterIP\n  ports:\n  - name: http\n    port: 80\n    targetPort: 80\n  selector:\n    app: my-app\n```\n\nTest if the deployment was successful from within a Pod:\n```\n# run a wget to the Service my-app-svc\nkubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox --command -- wget -qO- my-app-svc\n\nversion-1\n```\n\nDeploy 1 replica of v2:\n```\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: my-app-v2\n  labels:\n    app: my-app\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: my-app\n      version: v2\n  template:\n    metadata:\n      labels:\n        app: my-app\n        version: v2\n    spec:\n      containers:\n      - name: nginx\n        image: nginx\n        ports:\n        - containerPort: 80\n        volumeMounts:\n        - name: workdir\n          mountPath: /usr/share/nginx/html\n      initContainers:\n      - name: install\n        image: busybox:1.28\n        command:\n        - /bin/sh\n        - -c\n        - \"echo version-2 > /work-dir/index.html\"\n        volumeMounts:\n        - name: workdir\n          mountPath: \"/work-dir\"\n      volumes:\n      - name: workdir\n        emptyDir: {}\n```\n\nObserve that calling the ip exposed by the service the requests are load balanced across the two versions:\n```\n# run a busyBox pod that will make a wget call to the service my-app-svc and print out the version of the pod it reached.\nkubectl run -it --rm --restart=Never busybox --image=gcr.io/google-containers/busybox -- /bin/sh -c 'while sleep 1; do wget -qO- my-app-svc; done'\n\nversion-1\nversion-1\nversion-1\nversion-2\nversion-2\nversion-1\n```\n\nIf the v2 is stable, scale it up to 4 replicas and shutdown the v1:\n```\nkubectl scale --replicas=4 deploy my-app-v2\nkubectl delete deploy my-app-v1\nwhile sleep 0.1; do curl $(kubectl get svc my-app-svc -o jsonpath=\"{.spec.clusterIP}\"); done\nversion-2\nversion-2\nversion-2\nversion-2\nversion-2\nversion-2\n```\n\n</p>\n</details>\n\n## Jobs\n\n### Create a job named pi with image perl:5.34 that runs the command with arguments \"perl -Mbignum=bpi -wle 'print bpi(2000)'\"\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create job pi  --image=perl:5.34 -- perl -Mbignum=bpi -wle 'print bpi(2000)'\n```\n\n</p>\n</details>\n\n### Wait till it's done, get the output\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get jobs -w # wait till 'SUCCESSFUL' is 1 (will take some time, perl image might be big)\nkubectl get po # get the pod name\nkubectl logs pi-**** # get the pi numbers\nkubectl delete job pi\n```\nOR\n\n```bash\nkubectl get jobs -w # wait till 'SUCCESSFUL' is 1 (will take some time, perl image might be big)\nkubectl logs job/pi\nkubectl delete job pi\n```\nOR\n\n```bash\nkubectl wait --for=condition=complete --timeout=300s job pi\nkubectl logs job/pi\nkubectl delete job pi\n```\n\n</p>\n</details>\n\n### Create a job with the image busybox that executes the command 'echo hello;sleep 30;echo world'\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create job busybox --image=busybox -- /bin/sh -c 'echo hello;sleep 30;echo world'\n```\n\n</p>\n</details>\n\n### Follow the logs for the pod (you'll wait for 30 seconds)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po # find the job pod\nkubectl logs busybox-ptx58 -f # follow the logs\n```\n\n</p>\n</details>\n\n### See the status of the job, describe it and see the logs\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get jobs\nkubectl describe jobs busybox\nkubectl logs job/busybox\n```\n\n</p>\n</details>\n\n### Delete the job\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl delete job busybox\n```\n\n</p>\n</details>\n\n### Create the same job, make it run 5 times, one after the other. Verify its status and delete it\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create job busybox --image=busybox --dry-run=client -o yaml -- /bin/sh -c 'echo hello;sleep 30;echo world' > job.yaml\nvi job.yaml\n```\n\nAdd job.spec.completions=5\n\n```YAML\napiVersion: batch/v1\nkind: Job\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: busybox\n  name: busybox\nspec:\n  completions: 5 # add this line\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        run: busybox\n    spec:\n      containers:\n      - args:\n        - /bin/sh\n        - -c\n        - echo hello;sleep 30;echo world\n        image: busybox\n        name: busybox\n        resources: {}\n      restartPolicy: OnFailure\nstatus: {}\n```\n\n```bash\nkubectl create -f job.yaml\n```\n\nVerify that it has been completed:\n\n```bash\nkubectl get job busybox -w # will take two and a half minutes\nkubectl delete jobs busybox\n```\n\n</p>\n</details>\n\n### Create the same job, but make it run 5 parallel times\n\n<details><summary>show</summary>\n<p>\n\n```bash\nvi job.yaml\n```\n\nAdd job.spec.parallelism=5\n\n```YAML\napiVersion: batch/v1\nkind: Job\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: busybox\n  name: busybox\nspec:\n  parallelism: 5 # add this line\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        run: busybox\n    spec:\n      containers:\n      - args:\n        - /bin/sh\n        - -c\n        - echo hello;sleep 30;echo world\n        image: busybox\n        name: busybox\n        resources: {}\n      restartPolicy: OnFailure\nstatus: {}\n```\n\n```bash\nkubectl create -f job.yaml\nkubectl get jobs\n```\n\nIt will take some time for the parallel jobs to finish (>= 30 seconds)\n\n```bash\nkubectl delete job busybox\n```\n\n</p>\n</details>\n\n### Create a job but ensure that it will be automatically terminated by kubernetes if it takes more than 30 seconds to execute\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create job busybox --image=busybox --dry-run=client -o yaml -- /bin/sh -c 'while true; do echo hello; sleep 10;done' > job.yaml\nvi job.yaml\n```\n\nAdd job.spec.activeDeadlineSeconds=30\n\n```bash\napiVersion: batch/v1\nkind: Job\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: busybox\n  name: busybox\nspec:\n  activeDeadlineSeconds: 30 # add this line\n  template:\n    metadata:\n      creationTimestamp: null\n      labels:\n        run: busybox\n    spec:\n      containers:\n      - args:\n        - /bin/sh\n        - -c\n        - while true; do echo hello; sleep 10;done\n        image: busybox\n        name: busybox\n        resources: {}\n      restartPolicy: OnFailure\nstatus: {}\n```\n</p>\n</details>\n\n## Cron jobs\n\nkubernetes.io > Documentation > Tasks > Run Jobs > [Running Automated Tasks with a CronJob](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/)\n\n### Create a cron job with image busybox that runs on a schedule of \"*/1 * * * *\" and writes 'date; echo Hello from the Kubernetes cluster' to standard output\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cronjob busybox --image=busybox --schedule=\"*/1 * * * *\" -- /bin/sh -c 'date; echo Hello from the Kubernetes cluster'\n```\n\n</p>\n</details>\n\n### See its logs and delete it\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get po # copy the ID of the pod whose container was just created\nkubectl logs <busybox-***> # you will see the date and message \nkubectl delete cj busybox # cj stands for cronjob\n```\n\n</p>\n</details>\n\n### Create the same cron job again, and watch the status. Once it ran, check which job ran by the created cron job. Check the log, and delete the cron job\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get cj\nkubectl get jobs --watch\nkubectl get po --show-labels # observe that the pods have a label that mentions their 'parent' job\nkubectl logs busybox-1529745840-m867r\n# Bear in mind that Kubernetes will run a new job/pod for each new cron job\nkubectl delete cj busybox\n```\n\n</p>\n</details>\n\n### Create a cron job with image busybox that runs every minute and writes 'date; echo Hello from the Kubernetes cluster' to standard output. The cron job should be terminated if it takes more than 17 seconds to start execution after its scheduled time (i.e. the job missed its scheduled time).\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cronjob time-limited-job --image=busybox --restart=Never --dry-run=client --schedule=\"* * * * *\" -o yaml -- /bin/sh -c 'date; echo Hello from the Kubernetes cluster' > time-limited-job.yaml\nvi time-limited-job.yaml\n```\nAdd cronjob.spec.startingDeadlineSeconds=17\n\n```bash\napiVersion: batch/v1\nkind: CronJob\nmetadata:\n  creationTimestamp: null\n  name: time-limited-job\nspec:\n  startingDeadlineSeconds: 17 # add this line\n  jobTemplate:\n    metadata:\n      creationTimestamp: null\n      name: time-limited-job\n    spec:\n      template:\n        metadata:\n          creationTimestamp: null\n        spec:\n          containers:\n          - args:\n            - /bin/sh\n            - -c\n            - date; echo Hello from the Kubernetes cluster\n            image: busybox\n            name: time-limited-job\n            resources: {}\n          restartPolicy: Never\n  schedule: '* * * * *'\nstatus: {}\n```\n\n</p>\n</details>\n\n### Create a cron job with image busybox that runs every minute and writes 'date; echo Hello from the Kubernetes cluster' to standard output. The cron job should be terminated if it successfully starts but takes more than 12 seconds to complete execution.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cronjob time-limited-job --image=busybox --restart=Never --dry-run=client --schedule=\"* * * * *\" -o yaml -- /bin/sh -c 'date; echo Hello from the Kubernetes cluster' > time-limited-job.yaml\nvi time-limited-job.yaml\n```\nAdd cronjob.spec.jobTemplate.spec.activeDeadlineSeconds=12\n\n```bash\napiVersion: batch/v1\nkind: CronJob\nmetadata:\n  creationTimestamp: null\n  name: time-limited-job\nspec:\n  jobTemplate:\n    metadata:\n      creationTimestamp: null\n      name: time-limited-job\n    spec:\n      activeDeadlineSeconds: 12 # add this line\n      template:\n        metadata:\n          creationTimestamp: null\n        spec:\n          containers:\n          - args:\n            - /bin/sh\n            - -c\n            - date; echo Hello from the Kubernetes cluster\n            image: busybox\n            name: time-limited-job\n            resources: {}\n          restartPolicy: Never\n  schedule: '* * * * *'\nstatus: {}\n```\n\n</p>\n</details>\n\n### Create a job from cronjob.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create job --from=cronjob/sample-cron-job sample-job\n```\n</p>\n</details>\n"
  },
  {
    "path": "d.configuration.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/configuration&empty)\n# Configuration (18%)\n\n[ConfigMaps](#configmaps)\n\n[SecurityContext](#securitycontext)\n\n[Resource Requests and Limits](#resource-requests-and-limits)\n\n[Limit Ranges](#limit-ranges)\n\n[Resource Quotas](#resource-quotas)\n\n[Secrets](#secrets)\n\n[Service Accounts](#serviceaccounts)\n\n<br>#Tips, export to variable<br>\n<br>export ns=\"-n secret-ops\"</br>\n<br>export do=\"--dry-run=client -oyaml\"</br>\n## ConfigMaps\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Configure a Pod to Use a ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/)\n\n### Create a configmap named config with values foo=lala,foo2=lolo\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create configmap config --from-literal=foo=lala --from-literal=foo2=lolo\n```\n\n</p>\n</details>\n\n### Display its values\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get cm config -o yaml\n# or\nkubectl describe cm config\n```\n\n</p>\n</details>\n\n### Create and display a configmap from a file\n\nCreate the file with\n\n```bash\necho -e \"foo3=lili\\nfoo4=lele\" > config.txt\n```\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cm configmap2 --from-file=config.txt\nkubectl get cm configmap2 -o yaml\n```\n\n</p>\n</details>\n\n### Create and display a configmap from a .env file\n\nCreate the file with the command\n\n```bash\necho -e \"var1=val1\\n# this is a comment\\n\\nvar2=val2\\n#anothercomment\" > config.env\n```\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cm configmap3 --from-env-file=config.env\nkubectl get cm configmap3 -o yaml\n```\n\n</p>\n</details>\n\n### Create and display a configmap from a file, giving the key 'special'\n\nCreate the file with\n\n```bash\necho -e \"var3=val3\\nvar4=val4\" > config4.txt\n```\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cm configmap4 --from-file=special=config4.txt\nkubectl describe cm configmap4\nkubectl get cm configmap4 -o yaml\n```\n\n</p>\n</details>\n\n### Create a configMap called 'options' with the value var5=val5. Create a new nginx pod that loads the value from variable 'var5' in an env variable called 'option'\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create cm options --from-literal=var5=val5\nkubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    env:\n    - name: option # name of the env variable\n      valueFrom:\n        configMapKeyRef:\n          name: options # name of config map\n          key: var5 # name of the entity in config map\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl exec -it nginx -- env | grep option # will show 'option=val5'\n```\n\n</p>\n</details>\n\n### Create a configMap 'anotherone' with values 'var6=val6', 'var7=val7'. Load this configMap as env variables into a new nginx pod\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create configmap anotherone --from-literal=var6=val6 --from-literal=var7=val7\nkubectl run --restart=Never nginx --image=nginx -o yaml --dry-run=client > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    envFrom: # different than previous one, that was 'env'\n    - configMapRef: # different from the previous one, was 'configMapKeyRef'\n        name: anotherone # the name of the config map\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl exec -it nginx -- env \n```\n\n</p>\n</details>\n\n### Create a configMap 'cmvolume' with values 'var8=val8', 'var9=val9'. Load this as a volume inside an nginx pod on path '/etc/lala'. Create the pod and 'ls' into the '/etc/lala' directory.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create configmap cmvolume --from-literal=var8=val8 --from-literal=var9=val9\nkubectl run nginx --image=nginx --restart=Never -o yaml --dry-run=client > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  volumes: # add a volumes list\n  - name: myvolume # just a name, you'll reference this in the pods\n    configMap:\n      name: cmvolume # name of your configmap\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    volumeMounts: # your volume mounts are listed here\n    - name: myvolume # the name that you specified in pod.spec.volumes.name\n      mountPath: /etc/lala # the path inside your container\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl exec -it nginx -- /bin/sh\ncd /etc/lala\nls # will show var8 var9\ncat var8 # will show val8\n```\n\n</p>\n</details>\n\n## SecurityContext\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Configure a Security Context for a Pod or Container](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)\n\n### Create the YAML for an nginx pod that runs with the user ID 101. No need to create the pod\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  securityContext: # insert this line\n    runAsUser: 101 # UID for the user\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n</p>\n</details>\n\n\n### Create the YAML for an nginx pod that has the capabilities \"NET_ADMIN\", \"SYS_TIME\" added to its single container\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    securityContext: # insert this line\n      capabilities: # and this\n        add: [\"NET_ADMIN\", \"SYS_TIME\"] # this as well\n    resources: {}\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n</p>\n</details>\n\n## Resource requests and limits\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Assign CPU Resources to Containers and Pods](https://kubernetes.io/docs/tasks/configure-pod-container/assign-cpu-resource/)\n\n### Create an nginx pod with requests cpu=100m,memory=256Mi and limits cpu=200m,memory=512Mi\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --dry-run=client -o yaml > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    name: nginx\n    resources:\n      requests:\n        memory: \"256Mi\"\n        cpu: \"100m\"\n      limits:    \n        memory: \"512Mi\"\n        cpu: \"200m\"\n  dnsPolicy: ClusterFirst\n  restartPolicy: Always\nstatus: {}\n``` \n\n</p>\n</details>\n\n## Limit Ranges\nkubernetes.io > Documentation > Concepts > Policies > Limit Ranges (https://kubernetes.io/docs/concepts/policy/limit-range/)\n\n### Create a namespace named limitrange with a LimitRange that limits pod memory to a max of 500Mi and min of 100Mi\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create ns limitrange\n```\n\nvi 1.yaml\n```YAML\napiVersion: v1\nkind: LimitRange\nmetadata:\n  name: ns-memory-limit\n  namespace: limitrange\nspec:\n  limits:\n  - max: # max and min define the limit range\n      memory: \"500Mi\"\n    min:\n      memory: \"100Mi\"\n    type: Pod\n```\n\n```bash\nkubectl apply -f 1.yaml\n```\n</p>\n</details>\n\n### Describe the namespace limitrange\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl describe limitrange ns-memory-limit -n limitrange\n```\n</p>\n</details>\n\n### Create an nginx pod that requests 250Mi of memory in the limitrange namespace\n\n<details><summary>show</summary>\n<p>\n\nvi 2.yaml\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\n  namespace: limitrange\nspec:\n  containers:\n  - image: nginx\n    name: nginx\n    resources:\n      requests:\n        memory: \"250Mi\"\n      limits:\n        memory: \"500Mi\" # limit has to be specified and be <= limitrange\n  dnsPolicy: ClusterFirst\n  restartPolicy: Always\nstatus: {}\n``` \n\n```bash\nkubectl apply -f 2.yaml\n```\n</p>\n</details>\n\n\n## Resource Quotas\nkubernetes.io > Documentation > Concepts > Policies > Resource Quotas (https://kubernetes.io/docs/concepts/policy/resource-quotas/)\n\n### Create ResourceQuota in namespace `one` with hard requests `cpu=1`, `memory=1Gi` and hard limits `cpu=2`, `memory=2Gi`.\n\n<details><summary>show</summary>\n<p>\n\nCreate the namespace:\n```bash\nkubectl create ns one\n```\n\nCreate the ResourceQuota\n```bash\nvi rq-one.yaml\n```\n\n```YAML\napiVersion: v1\nkind: ResourceQuota\nmetadata:\n  name: my-rq\n  namespace: one\nspec:\n  hard:\n    requests.cpu: \"1\"\n    requests.memory: 1Gi\n    limits.cpu: \"2\"\n    limits.memory: 2Gi\n```\n\n```bash\nkubectl apply -f rq-one.yaml\n```\n\nor\n```bash\nkubectl create quota my-rq --namespace=one --hard=requests.cpu=1,requests.memory=1Gi,limits.cpu=2,limits.memory=2Gi\n```\n</p>\n</details>\n\n### Attempt to create a pod with resource requests `cpu=2`, `memory=3Gi` and limits `cpu=3`, `memory=4Gi` in namespace `one`\n\n<details><summary>show</summary>\n<p>\n\n```bash\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\n  namespace: one\nspec:\n  containers:\n  - image: nginx\n    name: nginx\n    resources:\n      requests:\n        memory: \"3Gi\"\n        cpu: \"2\"\n      limits:\n        memory: \"4Gi\"\n        cpu: \"3\"\n  dnsPolicy: ClusterFirst\n  restartPolicy: Always\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\n```\n\nExpected error message:\n```bash\nError from server (Forbidden): error when creating \"pod.yaml\": pods \"nginx\" is forbidden: exceeded quota: my-rq, requested: limits.cpu=3,limits.memory=4Gi,requests.cpu=2,requests.memory=3Gi, used: limits.cpu=0,limits.memory=0,requests.cpu=0,requests.memory=0, limited: limits.cpu=2,limits.memory=2Gi,requests.cpu=1,requests.memory=1Gi\n```\n</p>\n</details>\n\n### Create a pod with resource requests `cpu=0.5`, `memory=1Gi` and limits `cpu=1`, `memory=2Gi` in namespace `one`\n\n<details><summary>show</summary>\n<p>\n\n```bash\nvi pod2.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\n  namespace: one\nspec:\n  containers:\n  - image: nginx\n    name: nginx\n    resources:\n      requests:\n        memory: \"1Gi\"\n        cpu: \"0.5\"\n      limits:\n        memory: \"2Gi\"\n        cpu: \"1\"\n  dnsPolicy: ClusterFirst\n  restartPolicy: Always\nstatus: {}\n```\n\n```bash\nkubectl create -f pod2.yaml\n```\n\nShow the ResourceQuota usage in namespace `one`\n```bash\nkubectl get resourcequota -n one\n```\n\n```\nNAME    AGE   REQUEST                                          LIMIT\nmy-rq   10m   requests.cpu: 500m/1, requests.memory: 1Gi/1Gi   limits.cpu: 1/2, limits.memory: 2Gi/2Gi\n```\n</p>\n</details>\n\n\n## Secrets\n\nkubernetes.io > Documentation > Concepts > Configuration > [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/)\n\nkubernetes.io > Documentation > Tasks > Inject Data Into Applications > [Distribute Credentials Securely Using Secrets](https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/)\n\n### Create a secret called mysecret with the values password=mypass\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create secret generic mysecret --from-literal=password=mypass\n```\n\n</p>\n</details>\n\n### Create a secret called mysecret2 that gets key/value from a file\n\nCreate a file called username with the value admin:\n\n```bash\necho -n admin > username\n```\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create secret generic mysecret2 --from-file=username\n```\n\n</p>\n</details>\n\n### Get the value of mysecret2\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get secret mysecret2 -o yaml\necho -n YWRtaW4= | base64 -d # on MAC it is -D, which decodes the value and shows 'admin'\n```\n\nAlternative using `--jsonpath`:\n\n```bash\nkubectl get secret mysecret2 -o jsonpath='{.data.username}' | base64 -d  # on MAC it is -D\n```\n\nAlternative using `--template`:\n\n```bash\nkubectl get secret mysecret2 --template '{{.data.username}}' | base64 -d  # on MAC it is -D\n```\n\nAlternative using `jq`:\n\n```bash\nkubectl get secret mysecret2 -o json | jq -r .data.username | base64 -d  # on MAC it is -D\n```\n\n</p>\n</details>\n\n### Create an nginx pod that mounts the secret mysecret2 in a volume on path /etc/foo\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never -o yaml --dry-run=client > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  volumes: # specify the volumes\n  - name: foo # this name will be used for reference inside the container\n    secret: # we want a secret\n      secretName: mysecret2 # name of the secret - this must already exist on pod creation\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    volumeMounts: # our volume mounts\n    - name: foo # name on pod.spec.volumes\n      mountPath: /etc/foo #our mount path\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl exec -it nginx -- /bin/bash\nls /etc/foo  # shows username\ncat /etc/foo/username # shows admin\n```\n\n</p>\n</details>\n\n### Delete the pod you just created and mount the variable 'username' from secret mysecret2 onto a new nginx pod in env variable called 'USERNAME'\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl delete po nginx\nkubectl run nginx --image=nginx --restart=Never -o yaml --dry-run=client > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    env: # our env variables\n    - name: USERNAME # asked name\n      valueFrom:\n        secretKeyRef: # secret reference\n          name: mysecret2 # our secret's name\n          key: username # the key of the data in the secret\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl exec -it nginx -- env | grep USERNAME | cut -d '=' -f 2 # will show 'admin'\n```\n\n</p>\n</details>\n\n### Create a Secret named 'ext-service-secret' in the namespace 'secret-ops'. Then, provide the key-value pair API_KEY=LmLHbYhsgWZwNifiqaRorH8T as literal.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nexport ns=\"-n secret-ops\"\nexport do=\"--dry-run=client -oyaml\"\nk create secret generic ext-service-secret --from-literal=API_KEY=LmLHbYhsgWZwNifiqaRorH8T $ns $do > sc.yaml\nk apply -f sc.yaml\n```\n\n</p>\n</details>\n\n### Consuming the Secret. Create a Pod named 'consumer' with the image 'nginx' in the namespace 'secret-ops' and consume the Secret as an environment variable. Then, open an interactive shell to the Pod, and print all environment variables.\n<details><summary>show</summary>\n<p>\n\n```bash\nexport ns=\"-n secret-ops\"\nexport do=\"--dry-run=client -oyaml\"\nk run consumer --image=nginx $ns $do > nginx.yaml\nvi nginx.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: consumer\n  name: consumer\n  namespace: secret-ops\nspec:\n  containers:\n  - image: nginx\n    name: consumer\n    resources: {}\n    env:\n    - name: API_KEY\n      valueFrom:\n        secretKeyRef:\n          name: ext-service-secret\n          key: API_KEY\n  dnsPolicy: ClusterFirst\n  restartPolicy: Always\nstatus: {}\n```\n\n```bash\nk exec -it $ns consumer -- /bin/sh\n#env\n```\n</p>\n</details>\n\n### Create a Secret named 'my-secret' of type 'kubernetes.io/ssh-auth' in the namespace 'secret-ops'. Define a single key named 'ssh-privatekey', and point it to the file 'id_rsa' in this directory.\n<details><summary>show</summary>\n<p>\n\n```bash\n#Tips, export to variable\nexport do=\"--dry-run=client -oyaml\"\nexport ns=\"-n secret-ops\"\n\n#if id_rsa file didn't exist.\nssh-keygen\n\nk create secret generic my-secret $ns --type=\"kubernetes.io/ssh-auth\" --from-file=ssh-privatekey=id_rsa $do > sc.yaml\nk apply -f sc.yaml\n```\n</p>\n</details>\n\n### Create a Pod named 'consumer' with the image 'nginx' in the namespace 'secret-ops', and consume the Secret as Volume. Mount the Secret as Volume to the path /var/app with read-only access. Open an interactive shell to the Pod, and render the contents of the file.\n<details><summary>show</summary>\n<p>\n\n```bash\n#Tips, export to variable\nexport ns=\"-n secret-ops\"\nexport do=\"--dry-run=client -oyaml\"\nk run consumer --image=nginx $ns $do > nginx.yaml\nvi nginx.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: consumer\n  name: consumer\n  namespace: secret-ops\nspec:\n  containers:\n    - image: nginx\n      name: consumer\n      resources: {}\n      volumeMounts:\n        - name: foo\n          mountPath: \"/var/app\"\n          readOnly: true\n  volumes:\n    - name: foo\n      secret:\n        secretName: my-secret\n        optional: true\n  dnsPolicy: ClusterFirst\n  restartPolicy: Always\nstatus: {}\n```\n\n```bash\nk exec -it $ns consumer -- /bin/sh\n# cat /var/app/ssh-privatekey\n# exit\n```\n</p>\n</details>\n\n## ServiceAccounts\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Configure Service Accounts for Pods](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)\n\n### See all the service accounts of the cluster in all namespaces\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get sa --all-namespaces\n```\nAlternatively \n\n```bash\nkubectl get sa -A\n```\n\n</p>\n</details>\n\n### Create a new serviceaccount called 'myuser'\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create sa myuser\n```\n\nAlternatively:\n\n```bash\n# let's get a template easily\nkubectl get sa default -o yaml > sa.yaml\nvim sa.yaml\n```\n\n```YAML\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: myuser\n```\n\n```bash\nkubectl create -f sa.yaml\n```\n\n</p>\n</details>\n\n### Create an nginx pod that uses 'myuser' as a service account\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never -o yaml --dry-run=client > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  serviceAccountName: myuser # we use pod.spec.serviceAccountName\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\nor\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  serviceAccount: myuser # we use pod.spec.serviceAccount\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl get pod nginx -o jsonpath='{.spec.serviceAccountName}' # output: myuser\n\nkubectl exec -it nginx -- cat /var/run/secrets/kubernetes.io/serviceaccount/token # to check if the ServiceAccount token is mounted inside the Pod. Starting from Kubernetes 1.24+, the token is dynamically projected into the Pod\n\n```\n\n</p>\n</details>\n\n### Generate an API token for the service account 'myuser'\n\n<details><summary>show</summary>\n<p>\n  \n```bash\nkubectl create token myuser\n```\n\n</p>\n</details>\n"
  },
  {
    "path": "e.observability.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/observability&empty)\n# Observability (18%)\n\n## Liveness, readiness and startup probes\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Configure Liveness, Readiness and Startup Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/)\n\n### Create an nginx pod with a liveness probe that just runs the command 'ls'. Save its YAML in pod.yaml. Run it, check its probe status, delete it.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    livenessProbe: # our probe\n      exec: # add this line\n        command: # command definition\n        - ls # ls command\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl describe pod nginx | grep -i liveness # run this to see that liveness probe works\nkubectl delete -f pod.yaml\n```\n\n</p>\n</details>\n\n### Modify the pod.yaml file so that liveness probe starts kicking in after 5 seconds whereas the interval between probes would be 5 seconds. Run it, check the probe, delete it.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl explain pod.spec.containers.livenessProbe # get the exact names\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    livenessProbe:\n      initialDelaySeconds: 5 # add this line\n      periodSeconds: 5 # add this line as well\n      exec:\n        command:\n        - ls\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl describe po nginx | grep -i liveness\nkubectl delete -f pod.yaml\n```\n\n</p>\n</details>\n\n### Create an nginx pod (that includes port 80) with an HTTP readinessProbe on path '/' on port 80. Again, run it, check the readinessProbe, delete it.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --dry-run=client -o yaml --restart=Never --port=80 > pod.yaml\nvi pod.yaml\n```\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: nginx\n  name: nginx\nspec:\n  containers:\n  - image: nginx\n    imagePullPolicy: IfNotPresent\n    name: nginx\n    resources: {}\n    ports:\n    - containerPort: 80 # Note: Readiness probes runs on the container during its whole lifecycle. Since nginx exposes 80, containerPort: 80 is not required for readiness to work.\n    readinessProbe: # declare the readiness probe\n      httpGet: # add this line\n        path: / #\n        port: 80 #\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\nstatus: {}\n```\n\n```bash\nkubectl create -f pod.yaml\nkubectl describe pod nginx | grep -i readiness # to see the pod readiness details\nkubectl delete -f pod.yaml\n```\n\n</p>\n</details>\n\n### Lots of pods are running in `qa`,`alan`,`test`,`production` namespaces.  All of these pods are configured with liveness probe.  Please list all pods whose liveness probe are failed in the format of `<namespace>/<pod name>` per line.\n\n<details><summary>show</summary>\n<p>\n\nA typical liveness probe failure event\n```\nLAST SEEN   TYPE      REASON      OBJECT              MESSAGE\n22m         Warning   Unhealthy   pod/liveness-exec   Liveness probe failed: cat: can't open '/tmp/healthy': No such file or directory\n```\n\ncollect failed pods namespace by namespace\n\n```sh\nkubectl get events -o json | jq -r '.items[] | select(.message | contains(\"Liveness probe failed\")).involvedObject | .namespace + \"/\" + .name'\n```\n\n</p>\n</details>\n\n## Logging\n\n### Create a busybox pod that runs `i=0; while true; do echo \"$i: $(date)\"; i=$((i+1)); sleep 1; done`. Check its logs\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --image=busybox --restart=Never -- /bin/sh -c 'i=0; while true; do echo \"$i: $(date)\"; i=$((i+1)); sleep 1; done'\nkubectl logs busybox -f # follow the logs\n```\n\n</p>\n</details>\n\n## Debugging\n\n### Create a busybox pod that runs 'ls /notexist'. Determine if there's an error (of course there is), see it. In the end, delete the pod\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --restart=Never --image=busybox -- /bin/sh -c 'ls /notexist'\n# show that there's an error\nkubectl logs busybox\nkubectl describe po busybox\nkubectl delete po busybox\n```\n\n</p>\n</details>\n\n### Create a busybox pod that runs 'notexist'. Determine if there's an error (of course there is), see it. In the end, delete the pod forcefully with a 0 grace period\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --restart=Never --image=busybox -- notexist\nkubectl logs busybox # will bring nothing! container never started\nkubectl describe po busybox # in the events section, you'll see the error\n# also...\nkubectl get events | grep -i error # you'll see the error here as well\nkubectl delete po busybox --force --grace-period=0\n```\n\n</p>\n</details>\n\n\n### Get CPU/memory utilization for nodes ([metrics-server](https://github.com/kubernetes-incubator/metrics-server) must be running)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl top nodes\n```\n\n</p>\n</details>\n"
  },
  {
    "path": "f.services.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/services&empty)\n# Services and Networking (13%)\n\n### Create a pod with image nginx called nginx and expose its port 80\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run nginx --image=nginx --restart=Never --port=80 --expose\n# observe that a pod as well as a service are created\n```\n\n</p>\n</details>\n\n\n### Confirm that ClusterIP has been created. Also check endpoints\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get svc nginx # services\nkubectl get ep # endpoints\n```\n\n</p>\n</details>\n\n### Get service's ClusterIP, create a temp busybox pod and 'hit' that IP with wget\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get svc nginx # get the IP (something like 10.108.93.130)\nkubectl run busybox --rm --image=busybox -it --restart=Never --\nwget -O- [PUT THE POD'S IP ADDRESS HERE]:80\nexit\n```\n\n</p>\nor\n<p>\n\n```bash\nIP=$(kubectl get svc nginx --template={{.spec.clusterIP}}) # get the IP (something like 10.108.93.130)\nkubectl run busybox --rm --image=busybox -it --restart=Never --env=\"IP=$IP\" -- wget -O- $IP:80 --timeout 2\n# Tip: --timeout is optional, but it helps to get answer more quickly when connection fails (in seconds vs minutes)\n```\n\n</p>\n</details>\n\n### Convert the ClusterIP to NodePort for the same service and find the NodePort port. Hit service using Node's IP. Delete the service and the pod at the end.\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl edit svc nginx\n```\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  creationTimestamp: 2018-06-25T07:55:16Z\n  name: nginx\n  namespace: default\n  resourceVersion: \"93442\"\n  selfLink: /api/v1/namespaces/default/services/nginx\n  uid: 191e3dac-784d-11e8-86b1-00155d9f663c\nspec:\n  clusterIP: 10.97.242.220\n  ports:\n  - port: 80\n    protocol: TCP\n    targetPort: 80\n  selector:\n    run: nginx\n  sessionAffinity: None\n  type: NodePort # change cluster IP to nodeport\nstatus:\n  loadBalancer: {}\n```\n\nor\n\n```bash\nkubectl patch svc nginx -p '{\"spec\":{\"type\":\"NodePort\"}}' \n```\n\n```bash\nkubectl get svc\n```\n\n```\n# result:\nNAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE\nkubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        1d\nnginx        NodePort    10.107.253.138   <none>        80:31931/TCP   3m\n```\n\n```bash\nwget -O- NODE_IP:31931 # if you're using Kubernetes with Docker for Windows/Mac, try 127.0.0.1\n#if you're using minikube, try minikube ip, then get the node ip such as 192.168.99.117\n```\n\n```bash\nkubectl delete svc nginx # Deletes the service\nkubectl delete pod nginx # Deletes the pod\n```\n</p>\n</details>\n\n### Create a deployment called foo using image 'dgkanatsios/simpleapp' (a simple server that returns hostname) and 3 replicas. Label it as 'app=foo'. Declare that containers in this pod will accept traffic on port 8080 (do NOT create a service yet)\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create deploy foo --image=dgkanatsios/simpleapp --port=8080 --replicas=3\nkubectl label deployment foo --overwrite app=foo #This is optional since kubectl create deploy foo will create label app=foo by default\n```\n</p>\n</details>\n\n### Get the pod IPs. Create a temp busybox pod and try hitting them on port 8080\n\n<details><summary>show</summary>\n<p>\n\n\n```bash\nkubectl get pods -l app=foo -o wide # 'wide' will show pod IPs\nkubectl run busybox --image=busybox --restart=Never -it --rm -- sh\nwget -O- <POD_IP>:8080 # do not try with pod name, will not work\n# try hitting all IPs generated after running 1st command to confirm that hostname is different\nexit\n# or\nkubectl get po -o wide -l app=foo | awk '{print $6}' | grep -v IP | xargs -L1 -I '{}' kubectl run --rm -ti tmp --restart=Never --image=busybox -- wget -O- http://\\{\\}:8080\n# or\nkubectl get po -l app=foo -o jsonpath='{range .items[*]}{.status.podIP}{\"\\n\"}{end}' | xargs -L1 -I '{}' kubectl run --rm -ti tmp --restart=Never --image=busybox -- wget -O- http://\\{\\}:8080\n```\n\n</p>\n</details>\n\n### Create a service that exposes the deployment on port 6262. Verify its existence, check the endpoints\n\n<details><summary>show</summary>\n<p>\n\n\n```bash\nkubectl expose deploy foo --port=6262 --target-port=8080\nkubectl get service foo # you will see ClusterIP as well as port 6262\nkubectl get endpoints foo # you will see the IPs of the three replica pods, listening on port 8080\n```\n\n</p>\n</details>\n\n### Create a temp busybox pod and connect via wget to foo service. Verify that each time there's a different hostname returned. Delete deployment and services to cleanup the cluster\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl get svc # get the foo service ClusterIP\nkubectl run busybox --image=busybox -it --rm --restart=Never -- sh\nwget -O- foo:6262 # DNS works! run it many times, you'll see different pods responding\nwget -O- <SERVICE_CLUSTER_IP>:6262 # ClusterIP works as well\n# you can also kubectl logs on deployment pods to see the container logs\nkubectl delete svc foo\nkubectl delete deploy foo\n```\n\n</p>\n</details>\n\n### Create an nginx deployment of 2 replicas, expose it via a ClusterIP service on port 80. Create a NetworkPolicy so that only pods with labels 'access: granted' can access the pods in this deployment and apply it\n\nkubernetes.io > Documentation > Concepts > Services, Load Balancing, and Networking > [Network Policies](https://kubernetes.io/docs/concepts/services-networking/network-policies/)\n\n> Note that network policies may not be enforced by default, depending on your k8s implementation. E.g. Azure AKS by default won't have policy enforcement, the cluster must be created with an explicit support for `netpol` https://docs.microsoft.com/en-us/azure/aks/use-network-policies#overview-of-network-policy  \n  \n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl create deployment nginx --image=nginx --replicas=2\nkubectl expose deployment nginx --port=80\n\nkubectl describe svc nginx # see the 'app=nginx' selector for the pods\n# or\nkubectl get svc nginx -o yaml\n\nvi policy.yaml\n```\n\n```YAML\nkind: NetworkPolicy\napiVersion: networking.k8s.io/v1\nmetadata:\n  name: access-nginx # pick a name\nspec:\n  podSelector:\n    matchLabels:\n      app: nginx # selector for the pods\n  ingress: # allow ingress traffic\n  - from:\n    - podSelector: # from pods\n        matchLabels: # with this label\n          access: granted\n```\n\n```bash\n# Create the NetworkPolicy\nkubectl create -f policy.yaml\n\n# Check if the Network Policy has been created correctly\n# make sure that your cluster's network provider supports Network Policy (https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy/#before-you-begin)\nkubectl run busybox --image=busybox --rm -it --restart=Never -- wget -O- http://nginx:80 --timeout 2                          # This should not work. --timeout is optional here. But it helps to get answer more quickly (in seconds vs minutes)\nkubectl run busybox --image=busybox --rm -it --restart=Never --labels=access=granted -- wget -O- http://nginx:80 --timeout 2  # This should be fine\n```\n\n</p>\n</details>\n"
  },
  {
    "path": "g.state.md",
    "content": "![](https://gaforgithub.azurewebsites.net/api?repo=CKAD-exercises/state&empty)\n# State Persistence (8%)\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Configure a Pod to Use a Volume for Storage](https://kubernetes.io/docs/tasks/configure-pod-container/configure-volume-storage/)\n\nkubernetes.io > Documentation > Tasks > Configure Pods and Containers > [Configure a Pod to Use a PersistentVolume for Storage](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/)\n\n## Define volumes \n\n### Create busybox pod with two containers, each one will have the image busybox and will run the 'sleep 3600' command. Make both containers mount an emptyDir at '/etc/foo'. Connect to the second busybox, write the first column of '/etc/passwd' file to '/etc/foo/passwd'. Connect to the first busybox and write '/etc/foo/passwd' file to standard output. Delete pod.\n\n<details><summary>show</summary>\n<p>\n\n*This question is probably a better fit for the 'Multi-container-pods' section but I'm keeping it here as it will help you get acquainted with state*\n\nThe easiest way to do this is to create a template pod with:\n\n```bash\nkubectl run busybox --image=busybox --restart=Never -o yaml --dry-run=client -- /bin/sh -c 'sleep 3600' > pod.yaml\nvi pod.yaml\n```\nCopy paste the container definition and type the lines that have a comment in the end:\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: busybox\n  name: busybox\nspec:\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\n  containers:\n  - args:\n    - /bin/sh\n    - -c\n    - sleep 3600\n    image: busybox\n    imagePullPolicy: IfNotPresent\n    name: busybox\n    resources: {}\n    volumeMounts: #\n    - name: myvolume #\n      mountPath: /etc/foo #\n  - args:\n    - /bin/sh\n    - -c\n    - sleep 3600\n    image: busybox\n    name: busybox2 # don't forget to change the name during copy paste, must be different from the first container's name!\n    volumeMounts: #\n    - name: myvolume #\n      mountPath: /etc/foo #\n  volumes: #\n  - name: myvolume #\n    emptyDir: {} #\n```\nIn case you forget to add ```bash -- /bin/sh -c 'sleep 3600'``` in template pod create command, you can include command field in config file\n\n```YAML\nspec:\n  containers:\n  - image: busybox\n    name: busybox\n    command: [\"/bin/sh\", \"-c\", \"sleep 3600\"]\n```\n\nConnect to the second container:\n\n```bash\nkubectl exec -it busybox -c busybox2 -- /bin/sh\ncat /etc/passwd | cut -f 1 -d ':' > /etc/foo/passwd # instead of cut command you can use awk -F \":\" '{print $1}'\ncat /etc/foo/passwd # confirm that stuff has been written successfully\nexit\n```\n\nConnect to the first container:\n\n```bash\nkubectl exec -it busybox -c busybox -- /bin/sh\nmount | grep foo # confirm the mounting\ncat /etc/foo/passwd\nexit\nkubectl delete po busybox\n```\n\n</p>\n</details>\n\n\n### Create a PersistentVolume of 10Gi, called 'myvolume'. Make it have accessMode of 'ReadWriteOnce' and 'ReadWriteMany', storageClassName 'normal', mounted on hostPath '/etc/foo'. Save it on pv.yaml, add it to the cluster. Show the PersistentVolumes that exist on the cluster\n\n<details><summary>show</summary>\n<p>\n\n```bash\nvi pv.yaml\n```\n\n```YAML\nkind: PersistentVolume\napiVersion: v1\nmetadata:\n  name: myvolume\nspec:\n  storageClassName: normal\n  capacity:\n    storage: 10Gi\n  accessModes:\n    - ReadWriteOnce\n    - ReadWriteMany\n  hostPath:\n    path: /etc/foo\n```\n\nShow the PersistentVolumes:\n\n```bash\nkubectl create -f pv.yaml\n# will have status 'Available'\nkubectl get pv\n```\n\n</p>\n</details>\n\n### Create a PersistentVolumeClaim for this PersistentVolume, called 'mypvc', a request of 4Gi and an accessMode of ReadWriteOnce, with the storageClassName of normal, and save it on pvc.yaml. Create it on the cluster. Show the PersistentVolumeClaims of the cluster. Show the PersistentVolumes of the cluster\n\n<details><summary>show</summary>\n<p>\n\n```bash\nvi pvc.yaml\n```\n\n```YAML\nkind: PersistentVolumeClaim\napiVersion: v1\nmetadata:\n  name: mypvc\nspec:\n  storageClassName: normal\n  accessModes:\n    - ReadWriteOnce\n  resources:\n    requests:\n      storage: 4Gi\n```\n\nCreate it on the cluster:\n\n```bash\nkubectl create -f pvc.yaml\n```\n\nShow the PersistentVolumeClaims and PersistentVolumes:\n\n```bash\nkubectl get pvc # will show as 'Bound'\nkubectl get pv # will show as 'Bound' as well\n```\n\n</p>\n</details>\n\n### Create a busybox pod with command 'sleep 3600', save it on pod.yaml. Mount the PersistentVolumeClaim to '/etc/foo'. Connect to the 'busybox' pod, and copy the '/etc/passwd' file to '/etc/foo/passwd'\n\n<details><summary>show</summary>\n<p>\n\nCreate a skeleton pod:\n\n```bash\nkubectl run busybox --image=busybox --restart=Never -o yaml --dry-run=client -- /bin/sh -c 'sleep 3600' > pod.yaml\nvi pod.yaml\n```\n\nAdd the lines that finish with a comment:\n\n```YAML\napiVersion: v1\nkind: Pod\nmetadata:\n  creationTimestamp: null\n  labels:\n    run: busybox\n  name: busybox\nspec:\n  containers:\n  - args:\n    - /bin/sh\n    - -c\n    - sleep 3600\n    image: busybox\n    imagePullPolicy: IfNotPresent\n    name: busybox\n    resources: {}\n    volumeMounts: #\n    - name: myvolume #\n      mountPath: /etc/foo #\n  dnsPolicy: ClusterFirst\n  restartPolicy: Never\n  volumes: #\n  - name: myvolume #\n    persistentVolumeClaim: #\n      claimName: mypvc #\nstatus: {}\n```\n\nCreate the pod:\n\n```bash\nkubectl create -f pod.yaml\n```\n\nConnect to the pod and copy '/etc/passwd' to '/etc/foo/passwd':\n\n```bash\nkubectl exec busybox -it -- cp /etc/passwd /etc/foo/passwd\n```\n\n</p>\n</details>\n\n### Create a second pod which is identical with the one you just created (you can easily do it by changing the 'name' property on pod.yaml). Connect to it and verify that '/etc/foo' contains the 'passwd' file. Delete pods to cleanup. Note: If you can't see the file from the second pod, can you figure out why? What would you do to fix that?\n\n\n\n<details><summary>show</summary>\n<p>\n\nCreate the second pod, called busybox2:\n\n```bash\nvim pod.yaml\n# change 'metadata.name: busybox' to 'metadata.name: busybox2'\nkubectl create -f pod.yaml\nkubectl exec busybox2 -- ls /etc/foo # will show 'passwd'\n# cleanup\nkubectl delete po busybox busybox2\nkubectl delete pvc mypvc\nkubectl delete pv myvolume\n```\n\nIf the file doesn't show on the second pod but it shows on the first, it has most likely been scheduled on a different node.\n\n```bash\n# check which nodes the pods are on\nkubectl get po busybox -o wide\nkubectl get po busybox2 -o wide\n```\n\nIf they are on different nodes, you won't see the file, because we used the `hostPath` volume type.\nIf you need to access the same files in a multi-node cluster, you need a volume type that is independent of a specific node.\nThere are lots of different types per cloud provider [(see here)](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#types-of-persistent-volumes), a general solution could be to use NFS.\n\n</p>\n</details>\n\n### Create a busybox pod with 'sleep 3600' as arguments. Copy '/etc/passwd' from the pod to your local folder\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl run busybox --image=busybox --restart=Never -- sleep 3600\nkubectl cp busybox:/etc/passwd ./passwd # kubectl cp command\n# previous command might report an error, feel free to ignore it since copy command works\ncat passwd\n```\n\n</p>\n</details>\n"
  },
  {
    "path": "h.helm.md",
    "content": "# Managing Kubernetes with Helm\n\n- Note: Helm is part of the new CKAD syllabus. Here are a few examples of using Helm to manage Kubernetes.\n\n## Helm in K8s\n\n### Creating a basic Helm chart\n\n<details><summary>show</summary>\n<p>\n\n```bash\nhelm create chart-test ## this would create a helm \n```\n\n</p>\n</details>\n\n### Running a Helm chart\n\n<details><summary>show</summary>\n<p>\n\n```bash\nhelm install -f myvalues.yaml myredis ./redis\n```\n\n</p>\n</details>\n\n### Find pending Helm deployments on all namespaces\n\n<details><summary>show</summary>\n<p>\n\n```bash\nhelm list --pending -A\n```\n\n</p>\n</details>\n\n### Uninstall a Helm release\n\n<details><summary>show</summary>\n<p>\n\n```bash\nhelm uninstall -n namespace release_name\n```\n\n</p>\n</details>\n\n### Upgrading a Helm chart\n\n<details><summary>show</summary>\n<p>\n\n```bash\nhelm upgrade -f myvalues.yaml -f override.yaml redis ./redis\n```\n\n</p>\n</details>\n\n### Using Helm repo\n\n<details><summary>show</summary>\n<p>\n\nAdd, list, remove, update and index chart repos\n\n```bash\nhelm repo add [NAME] [URL]  [flags]\n\nhelm repo list / helm repo ls\n\nhelm repo remove [REPO1] [flags]\n\nhelm repo update / helm repo up\n\nhelm repo update [REPO1] [flags]\n\nhelm repo index [DIR] [flags]\n```\n\n</p>\n</details>\n\n### Download a Helm chart from a repository \n\n<details><summary>show</summary>\n<p>\n\n```bash\nhelm pull [chart URL | repo/chartname] [...] [flags] ## this would download a helm, not install \nhelm pull --untar [rep/chartname] # untar the chart after downloading it \n```\n\n</p>\n</details>\n\n### Add the Bitnami repo at https://charts.bitnami.com/bitnami to Helm\n<details><summary>show</summary>\n<p>\n    \n```bash\nhelm repo add bitnami https://charts.bitnami.com/bitnami\n```\n  \n</p>\n</details>\n\n### Write the contents of the values.yaml file of the `bitnami/node` chart to standard output\n<details><summary>show</summary>\n<p>\n    \n```bash\nhelm show values bitnami/node\n```\n  \n</p>\n</details>\n\n### Install the `bitnami/node` chart setting the number of replicas to 5\n<details><summary>show</summary>\n<p>\n\nTo achieve this, we need two key pieces of information:\n- The name of the attribute in values.yaml which controls replica count\n- A simple way to set the value of this attribute during installation\n\nTo identify the name of the attribute in the values.yaml file, we could get all the values, as in the previous task, and then grep to find attributes matching the pattern `replica`\n```bash\nhelm show values bitnami/node | grep -i replica\n```\nwhich returns\n```bash\n## @param replicaCount Specify the number of replicas for the application\nreplicaCount: 1\n```\n \nWe can use the `--set` argument during installation to override attribute values. Hence, to set the replica count to 5, we need to run\n```bash\nhelm install mynode bitnami/node --set replicaCount=5\n```\n\n</p>\n</details>\n\n\n"
  },
  {
    "path": "i.crd.md",
    "content": "# Extend the Kubernetes API with CRD (CustomResourceDefinition)\n\n- Note: CRD is part of the new CKAD syllabus. Here are a few examples of installing custom resource into the Kubernetes API by creating a CRD.\n\n## CRD in K8s\n\n### Create a CustomResourceDefinition manifest file for an Operator with the following specifications :\n* *Name* : `operators.stable.example.com`\n* *Group* : `stable.example.com`\n* *Schema*: `<email: string><name: string><age: integer>`\n* *Scope*: `Namespaced`\n* *Names*: `<plural: operators><singular: operator><shortNames: op>`\n* *Kind*: `Operator`\n\n<details><summary>show</summary>\n<p>\n\n```yaml\napiVersion: apiextensions.k8s.io/v1\nkind: CustomResourceDefinition\nmetadata:\n  # name must match the spec fields below, and be in the form: <plural>.<group>\n  name: operators.stable.example.com\nspec:\n  group: stable.example.com\n  versions:\n    - name: v1\n      served: true\n      # One and only one version must be marked as the storage version.\n      storage: true\n      schema:\n        openAPIV3Schema:\n          type: object\n          properties:\n            spec:\n              type: object\n              properties:\n                email:\n                  type: string\n                name:\n                  type: string\n                age:\n                  type: integer\n  scope: Namespaced\n  names:\n    plural: operators\n    singular: operator\n    # kind is normally the CamelCased singular type. Your resource manifests use this.\n    kind: Operator\n    shortNames:\n    - op\n```\n\n</p>\n</details>\n\n### Create the CRD resource in the K8S API\n\n<details><summary>show</summary>\n<p>\n\n```bash\nkubectl apply -f operator-crd.yml\n```\n\n</p>\n</details>\n\n### Create custom object from the CRD\n\n* *Name* : `operator-sample`\n* *Kind*: `Operator`\n* Spec:\n  * email: `operator-sample@stable.example.com`\n  * name: `operator sample`\n  * age: `30`\n\n<details><summary>show</summary>\n<p>\n\n```yaml\napiVersion: stable.example.com/v1\nkind: Operator\nmetadata:\n  name: operator-sample\nspec:\n  email: operator-sample@stable.example.com\n  name: \"operator sample\"\n  age: 30\n```\n\n```bash\nkubectl apply -f operator.yml\n```\n\n</p>\n</details>\n\n### Listing operator\n\n<details><summary>show</summary>\n<p>\n\nUse singular, plural and short forms\n\n```bash\nkubectl get operators\nor\nkubectl get operator\nor\nkubectl get op\n```\n\n</p>\n</details>\n"
  },
  {
    "path": "j.podman.md",
    "content": "# Define, build and modify container images\n\n- Note: The topic is part of the new CKAD syllabus. Here are a few examples of using **podman** to manage the life cycle of container images. The use of **docker** had been the industry standard for many years, but now large companies like [Red Hat](https://www.redhat.com/en/blog/say-hello-buildah-podman-and-skopeo) are moving to a new suite of open source tools: podman, skopeo and buildah. Also Kubernetes has moved in this [direction](https://kubernetes.io/blog/2022/02/17/dockershim-faq/). In particular, `podman` is meant to be the replacement of the `docker` command: so it makes sense to get familiar with it, although they are quite interchangeable considering that they use the same syntax.\n\n## Podman basics\n\n### Create a Dockerfile to deploy an Apache HTTP Server which hosts a custom main page\n\n<details><summary>show</summary>\n<p>\n\n```Dockerfile\nFROM docker.io/httpd:2.4\nRUN echo \"Hello, World!\" > /usr/local/apache2/htdocs/index.html\n```\n\n</p>\n</details>\n\n### Build and see how many layers the image consists of\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ podman build -t simpleapp .\nSTEP 1/2: FROM httpd:2.4\nSTEP 2/2: RUN echo \"Hello, World!\" > /usr/local/apache2/htdocs/index.html\nCOMMIT simpleapp\n--> ef4b14a72d0\nSuccessfully tagged localhost/simpleapp:latest\nef4b14a72d02ae0577eb0632d084c057777725c279e12ccf5b0c6e4ff5fd598b\n\n:~$ podman images\nREPOSITORY               TAG         IMAGE ID      CREATED        SIZE\nlocalhost/simpleapp      latest      ef4b14a72d02  8 seconds ago  148 MB\ndocker.io/library/httpd  2.4         98f93cd0ec3b  7 days ago     148 MB\n\n:~$ podman image tree localhost/simpleapp:latest\nImage ID: ef4b14a72d02\nTags:     [localhost/simpleapp:latest]\nSize:     147.8MB\nImage Layers\n├── ID: ad6562704f37 Size:  83.9MB\n├── ID: c234616e1912 Size: 3.072kB\n├── ID: c23a797b2d04 Size: 2.721MB\n├── ID: ede2e092faf0 Size: 61.11MB\n├── ID: 971c2cdf3872 Size: 3.584kB Top Layer of: [docker.io/library/httpd:2.4]\n└── ID: 61644e82ef1f Size: 6.144kB Top Layer of: [localhost/simpleapp:latest]\n```\n\n</p>\n</details>\n\n### Run the image locally, inspect its status and logs, finally test that it responds as expected\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ podman run -d --name test -p 8080:80 localhost/simpleapp\n2f3d7d613ea6ba19703811d30704d4025123c7302ff6fa295affc9bd30e532f8\n\n:~$ podman ps\nCONTAINER ID  IMAGE                       COMMAND           CREATED        STATUS            PORTS                 NAMES\n2f3d7d613ea6  localhost/simpleapp:latest  httpd-foreground  5 seconds ago  Up 6 seconds ago  0.0.0.0:8080->80/tcp  test\n\n:~$ podman logs test\nAH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.0.2.100. Set the 'ServerName' directive globally to suppress this message\nAH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.0.2.100. Set the 'ServerName' directive globally to suppress this message\n[Sat Jun 04 16:15:38.071377 2022] [mpm_event:notice] [pid 1:tid 139756978220352] AH00489: Apache/2.4.53 (Unix) configured -- resuming normal operations\n[Sat Jun 04 16:15:38.073570 2022] [core:notice] [pid 1:tid 139756978220352] AH00094: Command line: 'httpd -D FOREGROUND'\n\n:~$ curl 0.0.0.0:8080\nHello, World!\n```\n\n</p>\n</details>\n\n### Run a command inside the pod to print out the index.html file\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ podman exec -it test cat /usr/local/apache2/htdocs/index.html\nHello, World!\n```\n</p>\n</details>\n\n### Tag the image with ip and port of a private local registry and then push the image to this registry\n\n<details><summary>show</summary>\n<p>\n\n> Note: Some small distributions of Kubernetes (such as [microk8s](https://microk8s.io/docs/registry-built-in)) have a built-in registry you can use for this exercise. If this is not your case, you'll have to setup it on your own.\n\n```bash\n:~$ podman tag localhost/simpleapp $registry_ip:5000/simpleapp\n\n:~$ podman push $registry_ip:5000/simpleapp\n```\n\n</p>\n</details>\n\n Verify that the registry contains the pushed image and that you can pull it\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ curl http://$registry_ip:5000/v2/_catalog\n{\"repositories\":[\"simpleapp\"]}\n\n# remove the image already present\n:~$ podman rmi $registry_ip:5000/simpleapp\n\n:~$ podman pull $registry_ip:5000/simpleapp\nTrying to pull 10.152.183.13:5000/simpleapp:latest...\nGetting image source signatures\nCopying blob 643ea8c2c185 skipped: already exists\nCopying blob 972107ece720 skipped: already exists\nCopying blob 9857eeea6120 skipped: already exists\nCopying blob 93859aa62dbd skipped: already exists\nCopying blob 8e47efbf2b7e skipped: already exists\nCopying blob 42e0f5a91e40 skipped: already exists\nCopying config ef4b14a72d done\nWriting manifest to image destination\nStoring signatures\nef4b14a72d02ae0577eb0632d084c057777725c279e12ccf5b0c6e4ff5fd598b\n```\n\n</p>\n</details>\n\n### Create a container without running/starting it\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ podman create busybox # create\nResolved \"busybox\" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)\nTrying to pull docker.io/library/busybox:latest...\nGetting image source signatures\nCopying blob sha256:213a27df5921cd9ae24732504c590bb6408911c20fb50a597f2a40896d554a8f\nCopying config sha256:3fba0c87fcc8ba126bf99e4ee205b43c91ffc6b15bb052315312e71bc6296551\nWriting manifest to image destination\n51b613406e8889213c176523e1c430e4bd00047965b0c22cff5b1c9badfbc452\n\n:~$ podman container ls -a\nCONTAINER ID  IMAGE                             COMMAND     CREATED        STATUS      PORTS       NAMES\n51b613406e88  docker.io/library/busybox:latest  sh          2 minutes ago  Created                 adoring_almeida\n```\n\n</p>\n</details>\n\n### Export a container to output.tar file\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ podman container ls -a # pick the container id\nCONTAINER ID  IMAGE                             COMMAND     CREATED        STATUS      PORTS       NAMES\n51b613406e88  docker.io/library/busybox:latest  sh          2 minutes ago  Created                 adoring_almeida\n\n:~$ podman export <container id> --output=output.tar\n\n:~$ ls -al output.tar\n-rw-r--r--@ 1 limistah  wheel  4272640 28 Aug 13:48 output.tar\n```\n\n</p>\n</details>\n\n### Run a pod with the image pushed to the registry\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ kubectl run simpleapp --image=$registry_ip:5000/simpleapp --port=80\n\n:~$ curl ${kubectl get pods simpleapp -o jsonpath='{.status.podIP}'}\nHello, World!\n```\n\n</p>\n</details>\n\n### Log into a remote registry server and then read the credentials from the default file\n\n<details><summary>show</summary>\n<p>\n\n> Note: The two most used container registry servers with a free plan are [DockerHub](https://hub.docker.com/) and [Quay.io](https://quay.io/).\n\n```bash\n:~$ podman login --username $YOUR_USER --password $YOUR_PWD docker.io\n:~$ cat ${XDG_RUNTIME_DIR}/containers/auth.json\n{\n        \"auths\": {\n                \"docker.io\": {\n                        \"auth\": \"Z2l1bGl0JLSGtvbkxCcX1xb617251xh0x3zaUd4QW45Q3JuV3RDOTc=\"\n                }\n        }\n}\n```\n\n</p>\n</details>\n\n### Create a secret both from existing login credentials and from the CLI\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ kubectl create secret generic mysecret --from-file=.dockerconfigjson=${XDG_RUNTIME_DIR}/containers/auth.json --type=kubernetes.io/dockeconfigjson\nsecret/mysecret created\n:~$ kubectl create secret docker-registry mysecret2 --docker-server=https://index.docker.io/v1/ --docker-username=$YOUR_USR --docker-password=$YOUR_PWD\nsecret/mysecret2 created\n```\n\n</p>\n</details>\n\n### Create the manifest for a Pod that uses one of the two secrets just created to pull an image hosted on the relative private remote registry\n\n<details><summary>show</summary>\n<p>\n\n```yaml\napiVersion: v1\nkind: Pod\nmetadata:\n  name: private-reg\nspec:\n  containers:\n  - name: private-reg-container\n    image: $YOUR_PRIVATE_IMAGE\n  imagePullSecrets:\n  - name: mysecret\n```\n\n</p>\n</details>\n\n### Clean up all the images and containers\n\n<details><summary>show</summary>\n<p>\n\n```bash\n:~$ podman rm --all --force\n:~$ podman rmi --all\n:~$ kubectl delete pod simpleapp\n```\n\n</p>\n</details>\n"
  }
]