├── 01-ckad-design-build.md ├── 02-ckad-env-configuration-security.md ├── 03-ckad-deployment.md ├── 04-ckad-services-networking.md ├── 05-ckad-observability-maintenance.md ├── 06-ckad-miscellaneous.md ├── LICENSE └── README.md /01-ckad-design-build.md: -------------------------------------------------------------------------------- 1 | ## Sample CKAD Application Design and Build Q&A 2 | 3 | ### Application Design and Build – 20% 4 | 5 | - Define, build and modify container images [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/01-ckad-design-build.md#01-01-create-a-container-from-the-attached-dockerfile-and-indexhtml-name-the-image-my-image-name-the-container-my-container-run-the-container-exposing-port-8080-on-the-host-and-port-80-on-the-container-stop-the-container-delete-the-container) 6 | - Understand Jobs and CronJobs 7 | - Understand multi-container Pod design patterns (e.g. sidecar, init and others) 8 | - Utilize persistent and ephemeral volumes [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/01-ckad-design-build.md#01-03-create-a-namespace-called-storage-namespace-create-a-persistent-volume-called-my-pv-with-5gi-storage-using-hostpath-mntmy-host-create-a-persistent-volume-claim-called-my-pvc-with-2gi-storage-create-a-pod-called-storage-pod-using-the-nginx-image-mount-the-persistent-volume-claim-onto-my-mount-in-storage-pod) 9 |
10 | 11 | #### 01-01. Docker Question 12 | * Create a container from the attached `Dockerfile` and `index.html`. 13 | * Name the image `my-image`. 14 | * Run the container exposing port `8080` on the host and port `80` on the container. 15 | * Name the container `my-container`. Stop the container. Delete the container. 16 | 17 |
Docker Image Creation 18 |

19 | 20 | Create a file called index.html 21 | 22 | ```bash 23 | mkdir -p ~/ckad/ 24 | vi ~/ckad/index.html 25 | ``` 26 | 27 | Edit index.html with the following text. 28 | 29 | ```bash 30 | Hardships often prepare ordinary people for an extraordinary destiny. 31 | ``` 32 | 33 | Create a file called Dockerfile 34 | 35 | ```bash 36 | vi ~/ckad/Dockerfile 37 | ``` 38 | 39 | Edit the Docker with to include the text below 40 | : 41 | ```bash 42 | FROM nginx:latest 43 | COPY ./index.html /usr/share/nginx/html/index.html 44 | ``` 45 | 46 | ```bash 47 | cd ~/ckad/ 48 | clear 49 | # Build the docker image 50 | docker build -t my-image:v0.1 . 51 | docker images 52 | ``` 53 | 54 | ```bash 55 | # Create a TAR file from the image file 56 | docker save --output my-image.tar my-image:v0.1 57 | ls my-image* 58 | ``` 59 | 60 | 61 |

62 |
63 | 64 |
Docker Container Operations 65 |

66 | 67 | kubernetes.io bookmark: [docker run](https://kubernetes.io/docs/reference/kubectl/docker-cli-to-kubectl/) 68 | 69 | ```bash 70 | clear 71 | # Run the docker image 72 | docker run -it --rm -d -p 8080:80 --name my-container my-image:v0.1 73 | ``` 74 | 75 | ```bash 76 | clear 77 | # Verify Operation 78 | curl localhost:8080 79 | ``` 80 | 81 | ```bash 82 | clear 83 | # List all images 84 | docker ps -a 85 | ``` 86 | 87 | ```bash 88 | clear 89 | # Stop the Container 90 | docker container stop my-container 91 | ``` 92 | 93 | ```bash 94 | clear 95 | # Delete the Image 96 | docker image rm my-image:v0.1 97 | ``` 98 | 99 |

100 |
101 | 102 |
Docker Operations 103 |

104 | 105 | ```bash 106 | clear 107 | # Prune all dangling images 108 | docker image prune -a 109 | ``` 110 | 111 |

112 |
113 |
114 | 115 | #### 01-02. Name Container Question 116 | * Create a namespace called `pod-namespace`. 117 | * Create a pod called `pod-1` using `nginx` image. 118 | * The container in the pod should be named `container-1`. 119 | 120 |
Prerequisites 121 |

122 | 123 | ```bash 124 | clear 125 | # Create the namespace 126 | kubectl create namespace pod-namespace 127 | ``` 128 | 129 | ```bash 130 | clear 131 | # Switch context into the namespace so that all subsequent commands execute inside that namespace. 132 | kubectl config set-context --current --namespace=pod-namespace 133 | ``` 134 | 135 | ##### Help Examples 136 | 137 | ```bash 138 | clear 139 | # Run the help flag to get examples 140 | kubectl run -h | more 141 | ``` 142 | 143 | Output: 144 | 145 | ```console 146 | Examples: 147 | 148 | # Start a nginx pod 149 | 150 | kubectl run nginx --image=nginx 151 | 152 | # Start a hazelcast pod and let the container expose port 5701 153 | 154 | kubectl run hazelcast --image=hazelcast/hazelcast --port=5701 155 | 156 | # Start a hazelcast pod and set environment variables "DNS_DOMAIN=cluster" and "POD_NAMESPACE=default" in the 157 | 158 | container 159 | kubectl run hazelcast --image=hazelcast/hazelcast --env="DNS_DOMAIN=cluster" --env="POD_NAMESPACE=default" 160 | 161 | # Start a hazelcast pod and set labels "app=hazelcast" and "env=prod" in the container 162 | 163 | kubectl run hazelcast --image=hazelcast/hazelcast --labels="app=hazelcast,env=prod" 164 | 165 | # Dry run; print the corresponding API objects without creating them 166 | 167 | kubectl run nginx --image=nginx --dry-run=client 👈👈👈 This example matches most closely to the question. Just needs an output file. 168 | 169 | # Start a nginx pod, but overload the spec with a partial set of values parsed from JSON 170 | 171 | kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }' 172 | 173 | # Start a busybox pod and keep it in the foreground, don't restart it if it exits 174 | 175 | kubectl run -i -t busybox --image=busybox --restart=Never 176 | 177 | # Start the nginx pod using the default command, but use custom arguments (arg1 .. argN) for that command 178 | 179 | kubectl run nginx --image=nginx -- ... 180 | 181 | # Start the nginx pod using a different command and custom arguments 182 | 183 | kubectl run nginx --image=nginx --command -- ... 184 | ``` 185 | 186 |

187 |
188 | 189 |
Solution 190 |

191 | 192 | kubernetes.io bookmark: [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) 193 | 194 | ```bash 195 | clear 196 | # Using the best example that matches the question 197 | mkdir -p ~/ckad/ 198 | kubectl run pod-1 --image=nginx --dry-run=client -o yaml > ~/ckad/01-02.yml 199 | ``` 200 | 201 | ```bash 202 | clear 203 | # Edit the YAML file to make required changes 204 | # Use the Question number in case you want to return to the question for reference or for review 205 | vi ~/ckad/01-02.yml 206 | ``` 207 | 208 | ```yaml 209 | apiVersion: v1 210 | kind: Pod 211 | metadata: 212 | creationTimestamp: null 213 | labels: 214 | run: pod-1 215 | name: pod-1 216 | spec: 217 | containers: 218 | - image: nginx 219 | name: container-1 #👈👈👈 Change from pod-1 to container-1 220 | resources: {} 221 | dnsPolicy: ClusterFirst 222 | restartPolicy: Always 223 | status: {} 224 | ``` 225 | 226 | ```bash 227 | clear 228 | # Apply the YAML file to the Kubernetes API server 229 | kubectl apply -f ~/ckad/01-02.yml 230 | ``` 231 | 232 | ```bash 233 | clear 234 | # Quick verification that the pod was created and is working 235 | kubectl get pod --watch 236 | ``` 237 | 238 |

239 |
240 |
241 | 242 | #### 01-03. Storage Question 243 | * Create a namespace called `storage-namespace`. 244 | * Create a Persistent Volume called `my-pv` with `5Gi` storage using hostPath `/mnt/my-host`. 245 | * Create a Persistent Volume Claim called `my-pvc` with `2Gi` storage. 246 | * Create a pod called `storage-pod` using the nginx image. 247 | * Mount the Persistent Volume Claim onto `/my-mount` in `storage-pod`. 248 | 249 |
Overview 250 |

251 | 252 | ![06-pv-pvc-pod](https://user-images.githubusercontent.com/18049790/140639299-1b5a2c1b-139d-4b52-9a35-66d79de5fb71.jpg) 253 | 254 | Legend 255 | * PersistentVolume – the low level representation of a storage volume 256 | * PersistentVolumeClaim – the binding between a Pod and PersistentVolume 257 | * Pod – a running container that will consume a PersistentVolume 258 | * StorageClass – allows for dynamic provisioning of PersistentVolumes 259 | 260 | [Access Modes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes) 261 | * ReadWriteOnce(RWO) - volume can be mounted as read-write by a single node. 262 | * ReadOnlyMany(ROX) - volume can be mounted read-only by many nodes. 263 | * ReadWriteMany(RWX) - volume can be mounted as read-write by many nodes. 264 | * ReadWriteOncePod(RWOP) - volume can be mounted as read-write by a single Pod. 265 | 266 | Notes 267 | * Once a PV is bound to a PVC, that PV is essentially tied to the PVC and cannot be bound to by another PVC. 268 | * There is a one-to-one mapping of PVs and PVCs. 269 | * However, multiple pods in the same project can use the same PVC. 270 | * The link between PV and PVC is not explict, instead the PVC makes a some requests for storage. 271 | * Kubernetes will pick an appropriate PersistentVolume to meet that claim. 272 | * StorageClass provisions PV dynamically, when PVC claims it. 273 | * StorageClass allows for dynamically provisioned volumes for an incoming claim. 274 | 275 | 276 |

277 |
278 | 279 |
Prerequisites 280 |

281 | 282 | ```bash 283 | clear 284 | kubectl create namespace storage-namespace 285 | kubectl config set-context --current --namespace=storage-namespace 286 | ``` 287 | 288 |

289 |
290 | 291 | 292 |
Solution - PersistentVolume 293 |

294 | 295 | kubernetes.io bookmark: [Create a PersistentVolume](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolume) 296 | 297 | ```bash 298 | # Create a YAML file for the PV 299 | mkdir -p ~/ckad/ 300 | vi ~/ckad/01-03-pv.yml 301 | ``` 302 | 303 | ```yaml 304 | apiVersion: v1 305 | kind: PersistentVolume 306 | metadata: 307 | name: my-pv #👈👈👈 Change 308 | labels: 309 | type: local 310 | spec: 311 | storageClassName: manual 312 | capacity: 313 | storage: 5Gi #👈👈👈 Change 314 | accessModes: 315 | - ReadWriteOnce 316 | hostPath: 317 | path: "/mnt/my-host" #👈👈👈 Change 318 | ``` 319 | 320 | ```bash 321 | kubectl apply -f ~/ckad/01-03-pv.yml 322 | clear 323 | kubectl get pv 324 | ``` 325 | 326 | Output: 327 | 328 | ```console 329 | # Note the STATUS=Available 330 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM 331 | my-pv 5Gi RWO Retain Available 332 | ``` 333 | 334 |

335 |
336 | 337 |
Solution - PersistentVolumeClaim 338 |

339 | 340 | kubernetes.io bookmark: [Create a PersistentVolumeClaim](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim) 341 | 342 | ```bash 343 | # Create a YAML file for the PVC 344 | mkdir -p ~/ckad/ 345 | vi ~/ckad/01-03-pvc.yml 346 | ``` 347 | 348 | ```yaml 349 | apiVersion: v1 350 | kind: PersistentVolumeClaim 351 | metadata: 352 | name: my-pvc #👈👈👈 Change 353 | spec: 354 | storageClassName: manual 355 | accessModes: 356 | - ReadWriteOnce 357 | resources: 358 | requests: 359 | storage: 2Gi #👈👈👈 Change 360 | ``` 361 | 362 | ```bash 363 | kubectl apply -f ~/ckad/01-03-pvc.yml 364 | clear 365 | kubectl get pv 366 | kubectl get pvc 367 | ``` 368 | 369 | Output: 370 | 371 | ```console 372 | NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM 373 | my-pv 5Gi RWO Retain Bound storage-namespace/my-pvc # STATUS=Bound means the PV and PVC are linked 374 | 375 | NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE 376 | my-pvc Bound my-pv 5Gi RWO manual 6s # STATUS=Bound means the PV and PVC are linked 377 | ``` 378 | 379 |

380 |
381 | 382 |
Solution - Pod 383 |

384 | 385 | kubernetes.io bookmark: [Create a Pod](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-pod) 386 | 387 | ```bash 388 | # Create a YAML file for the Pod 389 | mkdir -p ~/ckad/ 390 | vi ~/ckad/01-03-pod.yml 391 | ``` 392 | 393 | ```yaml 394 | apiVersion: v1 395 | kind: Pod 396 | metadata: 397 | name: storage-pod #👈👈👈 Change 398 | spec: 399 | volumes: 400 | - name: my-volume 401 | persistentVolumeClaim: 402 | claimName: my-pvc #👈👈👈 Change 403 | containers: 404 | - name: my-container 405 | image: nginx 406 | ports: 407 | - containerPort: 80 408 | name: "http-server" 409 | volumeMounts: 410 | - mountPath: "/my-mount" #👈👈👈 Change 411 | name: my-volume 412 | 413 | ``` 414 | 415 | ```bash 416 | kubectl apply -f ~/ckad/01-03-pod.yml 417 | clear 418 | # Verify that the volume is mounted 419 | # Or just kubectl describe pod storage-pod 420 | kubectl describe pod storage-pod | grep -i Mounts -A1 421 | ``` 422 | 423 | Output: 424 | 425 | ```console 426 | Mounts: 427 | /my-mount from my-volume (rw) # Success 428 | ``` 429 | 430 |

431 |
432 |
433 | 434 | #### Clean Up 435 | 436 |
Clean Up 437 |

438 | 439 | ```bash 440 | cd 441 | yes | rm -R ~/ckad/ 442 | kubectl delete ns storage-namespace --force 443 | kubectl delete ns pod-namespace --force 444 | kubectl delete pv my-pv 445 | ``` 446 | 447 |

448 |
449 | 450 | _End of Section_ 451 | -------------------------------------------------------------------------------- /02-ckad-env-configuration-security.md: -------------------------------------------------------------------------------- 1 | ## Sample CKAD Environment, Configuration and Security Q&A 2 | 3 | ### Application Environment, Configuration and Security – 25% 4 | 5 | - Discover and use resources that extend Kubernetes (CRD) [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/02-ckad-env-configuration-security.md#02-01-list-all-the-custom-resource-definitions-installed-in-a-cluster-calico-is-a-crd-list-out-how-to-obtain-the-correct-resource-name-to-query-a-calico-network-policy-and-not-the-default-kubernetes-network-policy) 6 | - Understand authentication, authorization and admission control 7 | - Understanding and defining resource requirements, limits and quotas [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/02-ckad-env-configuration-security.md#02-02-create-a-namespace-called-quota-namespace-create-a-resource-quota-for-this-namespace-called-my-quota-set-a-memory-reservation-of-2gi-set-a-cpu-reservation-of-500mi) 8 | - Understand ConfigMaps 9 | - Create & consume Secrets [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/02-ckad-env-configuration-security.md#02-02-create-a-namespace-called-quota-namespace-create-a-resource-quota-for-this-namespace-called-my-quota-set-a-memory-reservation-of-2gi-set-a-cpu-reservation-of-500mi) 10 | - Understand ServiceAccounts [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/02-ckad-env-configuration-security.md#02-04-create-a-namespace-called-serviceaccount-namespace-create-a-pod-called-serviceaccount-pod-using-nginx-image-create-a-seviceaccount-called-my-serviceaccount-update-the-pod-to-use-the-new-serviceaccount-display-the-token-for-the-new-serviceaccount) 11 | - Understand SecurityContexts 12 |
13 | 14 | #### 02-01. Custom Resource Definition Question 15 | * List all the Custom Resource Definitions installed in a cluster. 16 | * Calico is a CRD. 17 | * List out how to obtain the correct resource name to query a Calico Network Policy and not the default Kubernetes Network Policy. 18 | 19 |
Overview 20 |

21 | 22 | ```bash 23 | clear 24 | # kubectl get Custom Resource Definitions 25 | kubectl get crds 26 | ``` 27 | 28 | Output: 29 | 30 | ```console 31 | NAME CREATED AT 32 | bgpconfigurations.crd.projectcalico.org 2021-09-24T05:26:26Z 33 | bgppeers.crd.projectcalico.org 2021-09-24T05:26:26Z 34 | blockaffinities.crd.projectcalico.org 2021-09-24T05:26:26Z 35 | clusterinformations.crd.projectcalico.org 2021-09-24T05:26:26Z 36 | extensionservices.projectcontour.io 2021-09-24T05:26:16Z 37 | felixconfigurations.crd.projectcalico.org 2021-09-24T05:26:26Z 38 | globalnetworkpolicies.crd.projectcalico.org 2021-09-24T05:26:26Z 39 | globalnetworksets.crd.projectcalico.org 2021-09-24T05:26:26Z 40 | hostendpoints.crd.projectcalico.org 2021-09-24T05:26:26Z 41 | httpproxies.projectcontour.io 2021-09-24T05:26:16Z 42 | ipamblocks.crd.projectcalico.org 2021-09-24T05:26:26Z 43 | ipamconfigs.crd.projectcalico.org 2021-09-24T05:26:26Z 44 | ipamhandles.crd.projectcalico.org 2021-09-24T05:26:26Z 45 | ippools.crd.projectcalico.org 2021-09-24T05:26:26Z 46 | kubecontrollersconfigurations.crd.projectcalico.org 2021-09-24T05:26:26Z 47 | networkpolicies.crd.projectcalico.org 2021-09-24T05:26:26Z 48 | networksets.crd.projectcalico.org 2021-09-24T05:26:26Z 49 | tlscertificatedelegations.projectcontour.io 2021-09-24T05:26:16Z 50 | ``` 51 | 52 |

53 |
54 | 55 |
Solution 56 |

57 | 58 | ```bash 59 | kubectl config set-context --current --namespace=default 60 | clear 61 | kubectl api-resources -o name | grep calico 62 | ``` 63 | 64 | Output: 65 | 66 | ```console 67 | bgpconfigurations.crd.projectcalico.org 68 | bgppeers.crd.projectcalico.org 69 | blockaffinities.crd.projectcalico.org 70 | clusterinformations.crd.projectcalico.org 71 | felixconfigurations.crd.projectcalico.org 72 | globalnetworkpolicies.crd.projectcalico.org 73 | globalnetworksets.crd.projectcalico.org 74 | hostendpoints.crd.projectcalico.org 75 | ipamblocks.crd.projectcalico.org 76 | ipamconfigs.crd.projectcalico.org 77 | ipamhandles.crd.projectcalico.org 78 | ippools.crd.projectcalico.org 79 | kubecontrollersconfigurations.crd.projectcalico.org 80 | networkpolicies.crd.projectcalico.org 👈👈👈 This is the Calico Resource Type that we want 81 | networksets.crd.projectcalico.org 82 | ``` 83 | 84 | ```bash 85 | clear 86 | # This is the command to get all Calico Network Policies 87 | # If you run this command it will return: "No resources found in storage-namespace namespace" 88 | # As we have not created any Calico Network Policies 89 | kubectl get networkpolicies.crd.projectcalico.org 90 | ``` 91 | 92 |

93 |
94 |
95 | 96 | #### 02-02. Quota and LimitRange Question 97 | * Create a namespace called `quota-namespace`. 98 | * Create a Resource Quota for this namespace called `my-quota`. 99 | * Set a hard memory reservation of `2Gi`. 100 | * Set a hard CPU reservation of `500m`. 101 | * Create a LimitRange for this namespace called `my-limit` which limits Pods to to a maximum of `1Gi` memory and `250m` CPU. 102 | 103 |
Prerequisites 104 |

105 | 106 | ```bash 107 | clear 108 | kubectl create namespace quota-namespace 109 | kubectl config set-context --current --namespace=quota-namespace 110 | ``` 111 | 112 |

113 |
114 | 115 |
Help 116 |

117 | 118 | ```bash 119 | clear 120 | kubectl create quota -h | more 121 | ``` 122 | 123 | Output 124 | 125 | ```console 126 | # Create a new resource quota named my-quota 127 | kubectl create quota my-quota --hard=cpu=1,memory=1G,pods=2,services=3,replicationcontrollers=2,resourcequotas=1,secrets=5,persistentvolumeclaims=10 👈👈👈 This example matches most closely to the question. 128 | 129 | # Create a new resource quota named best-effort 130 | kubectl create quota best-effort --hard=pods=100 --scopes=BestEffort 131 | ``` 132 | 133 |

134 |
135 | 136 |
Solution 137 |

138 | 139 | ```bash 140 | clear 141 | kubectl create quota my-quota --hard=cpu=500m,memory=2G 142 | kubectl get quota 143 | ``` 144 | 145 | Output: 146 | 147 | ```console 148 | NAME AGE REQUEST LIMIT 149 | my-quota 118s cpu: 0/500m, memory: 0/2G 150 | ``` 151 | 152 | ```bash 153 | # Try to run a Pod with resource requests exceeding the quota 154 | mkdir -p ~/ckad/ 155 | clear 156 | kubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml | kubectl set resources -f - --requests=cpu=1000m,memory=4Gi --limits=cpu=1000m,memory=4Gi --local -o yaml > ~/ckad/02-02-exceed.yml 157 | kubectl apply -f ~/ckad/02-02-exceed.yml 158 | ``` 159 | 160 | In English: 161 | 162 | | Value | Translation | 163 | | ------- | ----------------- | 164 | | REQUEST | Minimum (Request) | 165 | | LIMIT | Maximum (Limits) | 166 | 167 | 168 | LimitRange 169 | * The LimitRange object cannot be created from the command line 170 | * Use the [Configure LimitRange](https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/memory-constraint-namespace/#create-a-limitrange-and-a-pod) bookmark to obtain a code snippet 171 | 172 | ```yaml 173 | apiVersion: v1 174 | kind: LimitRange 175 | metadata: 176 | name: my-limit 177 | spec: 178 | limits: 179 | - max: 180 | memory: 1Gi # 👈👈👈 Change this value 181 | cpu: 250m # 👈👈👈 Change this value 182 | type: Pod # 👈👈👈 Change this value 183 | ``` 184 | 185 | 186 | ```bash 187 | # This Pod is within the resource requests of the Resource Quota and LimitRange 188 | mkdir -p ~/ckad/ 189 | clear 190 | kubectl run nginx --image=nginx --restart=Never --dry-run=client -o yaml | kubectl set resources -f - --requests=cpu=200m,memory=512Mi --limits=cpu=200m,memory=512Mi --local -o yaml > ~/ckad/02-02-succeed.yml 191 | kubectl apply -f ~/ckad/02-02-succeed.yml 192 | kubectl get all 193 | kubectl get quota 194 | ``` 195 | 196 | Output: 197 | 198 | ```console 199 | NAME AGE REQUEST LIMIT 200 | my-quota 19m cpu: 250m/500m, memory: 1Gi/2G 201 | ``` 202 | 203 | [Meaning of memory](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-memory) 204 | 205 | - Limits and requests for MEMORY are measured in bytes. 206 | - You can express memory as a plain integer or as a fixed-point number using one of these suffixes: E, P, T, G, M, k. 207 | - You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki. 208 | 209 | In English: 210 | 211 | | Suffix | Description | 212 | | ------ | ----------- | 213 | | E/Ei | Exabyte | 214 | | P/Pi | Petabyte | 215 | | T/Ti | Terrabyte | 216 | | G/Gi | Gigabyte | 217 | | M/Mi | Megabyte | 218 | | k/Ki | Kilobyte | 219 | 220 |

221 |
222 |
223 | 224 | #### 02-03. Secret Question 225 | * Create a namespace called `secret-namespace`. 226 | * Create a secret in this namespace called `my-secret`. 227 | * The secret should be immutable and contain the literal values `user=bob` and `password=123456`. 228 | * Create a pod called called `secret-pod` using the `nginx` image. 229 | * The pod should consume the secret as environmental variables `SECRET-ENV-USER` and `SECRET-ENV-PASSWORD`. 230 | 231 |
Prerequisites 232 |

233 | 234 | ```bash 235 | clear 236 | kubectl create namespace secret-namespace 237 | kubectl config set-context --current --namespace=secret-namespace 238 | ``` 239 | 240 |

241 |
242 | 243 |
Help 244 |

245 | 246 | ##### Help Examples 247 | 248 | Three types of secret: 249 | 250 | - generic 251 | - docker-registry 252 | - tls 253 | 254 | ##### Help Examples - GENERIC 255 | 256 | Create a secret from a local file, directory, or literal value 257 | 258 | ```bash 259 | clear 260 | # kubectl create secret -h 261 | kubectl create secret generic -h | more 262 | ``` 263 | 264 | Output: 265 | 266 | ```console 267 | Examples: 268 | # Create a new secret named my-secret with keys for each file in folder bar 269 | kubectl create secret generic my-secret --from-file=path/to/bar 270 | 271 | # Create a new secret named my-secret with specified keys instead of names on disk 272 | kubectl create secret generic my-secret --from-file=ssh-privatekey=path/to/id_rsa 273 | --from-file=ssh-publickey=path/to/id_rsa.pub 274 | 275 | # Create a new secret named my-secret with key1=supersecret and key2=topsecret 276 | kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret 👈👈👈 This example matches most closely to the question. 277 | 278 | # Create a new secret named my-secret using a combination of a file and a literal 279 | kubectl create secret generic my-secret --from-file=ssh-privatekey=path/to/id_rsa --from-literal=passphrase=topsecret 280 | 281 | # Create a new secret named my-secret from an env file 282 | kubectl create secret generic my-secret --from-env-file=path/to/bar.env 283 | ``` 284 | 285 | ##### Help Examples - TLS (Transport Layer Security) 286 | 287 | Create a TLS secret 288 | 289 | ```bash 290 | clear 291 | # kubectl create secret -h 292 | kubectl create secret tls -h | more 293 | ``` 294 | 295 | ```console 296 | Examples: 297 | # Create a new TLS secret named tls-secret with the given key pair 298 | kubectl create secret tls tls-secret --cert=path/to/tls.cert --key=path/to/tls.key 299 | ``` 300 | 301 |

302 |
303 | 304 |
Solution 305 |

306 | 307 | ```bash 308 | clear 309 | # Create a generic secret 310 | mkdir -p ~/ckad/ 311 | kubectl create secret generic my-secret --from-literal=user=bob --from-literal=password=123456 --dry-run=client -o yaml > ~/ckad/02-03-secret.yml 312 | vi ~/ckad/02-03-secret.yml 313 | ``` 314 | 315 | kubernetes.io bookmark: [Immutable Secrets](https://kubernetes.io/docs/concepts/configuration/secret/#secret-immutable) 316 | 317 | ```yaml 318 | apiVersion: v1 319 | data: 320 | password: MTIzNDU2 321 | user: Ym9i 322 | immutable: true #👈👈👈 From Immutable Secrets link above 323 | kind: Secret 324 | metadata: 325 | creationTimestamp: null 326 | name: my-secret 327 | ``` 328 | 329 | [my-secret](examples/02-03-secret.yml) 330 | 331 | ```bash 332 | clear 333 | # Apply the YAML file to the Kubernetes API server 334 | # The secret is available to all pods in the namespace 335 | kubectl apply -f ~/ckad/02-03-secret.yml 336 | clear 337 | # Verify that the secret got created 338 | kubectl get secret my-secret 339 | kubectl describe secret my-secret 340 | ``` 341 | 342 | ```bash 343 | clear 344 | # Now to create the pod that will consume the secret 345 | kubectl run secret-pod --image=nginx --restart=Never -n secret-namespace --dry-run=client -o yaml > ~/ckad/02-03-pod.yml 346 | vi ~/ckad/02-03-pod.yml 347 | ``` 348 | 349 | kubernetes.io bookmark: [Using Secrets as environment variables](https://kubernetes.io/docs/concepts/configuration/secret/#using-secrets-as-environment-variables) 350 | 351 | ```yaml 352 | apiVersion: v1 353 | kind: Pod 354 | metadata: 355 | creationTimestamp: null 356 | labels: 357 | run: secret-pod 358 | name: secret-pod 359 | namespace: secret-namespace 360 | spec: 361 | containers: 362 | - image: nginx 363 | name: secret-pod 364 | env: #👈👈👈 From Using Secrets as environment variables link above 365 | - name: SECRET-ENV-USER #👈👈👈 From Using Secrets as environment variables link above 366 | valueFrom: #👈👈👈 From Using Secrets as environment variables link above 367 | secretKeyRef: #👈👈👈 From Using Secrets as environment variables link above 368 | name: my-secret #👈👈👈 From Using Secrets as environment variables link above 369 | key: user #👈👈👈 From Using Secrets as environment variables link above 370 | - name: SECRET-ENV-PASSWORD #👈👈👈 From Using Secrets as environment variables link above 371 | valueFrom: #👈👈👈 From Using Secrets as environment variables link above 372 | secretKeyRef: #👈👈👈 From Using Secrets as environment variables link above 373 | name: my-secret #👈👈👈 From Using Secrets as environment variables link above 374 | key: password #👈👈👈 From Using Secrets as environment variables link above 375 | resources: {} 376 | dnsPolicy: ClusterFirst 377 | restartPolicy: Never 378 | status: {} 379 | ``` 380 | 381 | ```bash 382 | clear 383 | # Apply the YAML file to the Kubernetes API server 384 | kubectl apply -f ~/ckad/02-03-pod.yml 385 | ``` 386 | 387 | ```bash 388 | clear 389 | # Quick verification that the deployment was created and the secret is visible as an environmental variable 390 | kubectl exec secret-pod -- env 391 | ``` 392 | 393 | Output: 394 | 395 | ```console 396 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 397 | HOSTNAME=secret-pod 398 | NGINX_VERSION=1.21.3 399 | NJS_VERSION=0.6.2 400 | PKG_RELEASE=1~buster 401 | SECRET-ENV-USER=bob #👈👈👈 Success 402 | SECRET-ENV-PASSWORD=123456 #👈👈👈 Success 403 | KUBERNETES_PORT_443_TCP_PORT=443 404 | KUBERNETES_PORT_443_TCP_ADDR=10.245.0.1 405 | KUBERNETES_SERVICE_HOST=10.245.0.1 406 | KUBERNETES_SERVICE_PORT=443 407 | KUBERNETES_SERVICE_PORT_HTTPS=443 408 | KUBERNETES_PORT=tcp://10.245.0.1:443 409 | KUBERNETES_PORT_443_TCP=tcp://10.245.0.1:443 410 | KUBERNETES_PORT_443_TCP_PROTO=tcp 411 | TERM=xterm 412 | HOME=/root 413 | ``` 414 | 415 |

416 |
417 |
418 | 419 | #### 02-04. ServiceAccount Question 420 | * Create a namespace called `serviceaccount-namespace`. 421 | * Create a pod called `serviceaccount-pod` using `nginx` image. 422 | * Create a SeviceAccount called: `my-serviceaccount`. 423 | * Update the pod to use the new ServiceAccount. 424 | * Display the token for the new ServiceAccount. 425 | 426 |
Prerequisites 427 |

428 | 429 | ```bash 430 | clear 431 | kubectl create namespace serviceaccount-namespace 432 | kubectl config set-context --current --namespace=serviceaccount-namespace 433 | ``` 434 | 435 |

436 |
437 | 438 |
Solution - Create the serviceAccount and display the token 439 |

440 | 441 | ```bash 442 | clear 443 | # Create the serviceAccount 444 | kubectl create sa my-serviceaccount 445 | ``` 446 | 447 | ```bash 448 | # Get the corresponding secret created for the new serviceAccount 449 | clear 450 | kubectl get secret 451 | ``` 452 | 453 | ```bash 454 | # Get the token out of the secret 455 | clear 456 | kubectl describe secret my-serviceaccount-token-***** #👈👈👈 Replace ***** with your values 457 | ``` 458 | 459 | Output: 460 | 461 | ```console 462 | Name: my-serviceaccount-token-nptmw 463 | Namespace: serviceaccount-namespace 464 | Labels: 465 | Annotations: kubernetes.io/service-account.name: my-serviceaccount 466 | kubernetes.io/service-account.uid: c8e68650-5fcb-4654-a8f7-99fedaba4356 467 | 468 | Type: kubernetes.io/service-account-token 469 | 470 | Data 471 | ==== 472 | ca.crt: 1156 bytes 473 | namespace: 24 bytes 474 | token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkFEcU0xS1hKTTQtZFR6bjl3UHlJZ09rNWpobDkyTmxBV05GSmFvZnpjY2sifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJzZXJ2aWNlYWNjb3VudC1uYW1lc3BhY2UiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlY3JldC5uYW1lIjoibXktc2VydmljZWFjY291bnQtdG9rZW4tbnB0bXciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoibXktc2VydmljZWFjY291bnQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjOGU2ODY1MC01ZmNiLTQ2NTQtYThmNy05OWZlZGFiYTQzNTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6c2VydmljZWFjY291bnQtbmFtZXNwYWNlOm15LXNlcnZpY2VhY2NvdW50In0.2R9cp-NzmtTzgJFiMkU1e-UdhoH5pa1cUPZULjNvJzwxrY7jRRwlIlAfAMcUf5dYdVeDpVla8oIRR_p_6R-SyoP6QbzxVtUWU8ApgYn_daH6lRFFtjv3-t9cOBWzZeBXdnmLw2u6t8dgjZ-dh7ExIgRfYrJQ_E_m3B1GNl-XpRC2xQ_-zXMOyHbhs1_Tx3aL5sBzWxmaR_I7X-9S--66gVVjuXEZwooZQblX3vjv3xWrfMDQb0bNjuFe7SK9FpFeLCPFd_yqIfKfwVhNogubhXiSWSoN_MxlUCmD5dxjkVJNrdhQJ5NHhI9-tzpa3cqsUyL0pjm7OexF-woYp3p96g #👈👈👈 This is the token that can be used to authenticate to the API Server 475 | ``` 476 | 477 |

478 |
479 | 480 |
Solution - Create the pod and update the pod to use the new serviceAccount 481 |

482 | 483 | ```bash 484 | # Create the pod declaratively 485 | mkdir -p ~/ckad/ 486 | kubectl run serviceaccount-pod --image=nginx --dry-run=client -o yaml > ~/ckad/02-04.yml 487 | ``` 488 | 489 | ```bash 490 | # Edit the manifest file 491 | vi ~/ckad/02-04.yml 492 | ``` 493 | 494 | kubernetes.io bookmark: [Configure Service Accounts for Pods](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) 495 | 496 | ```yaml 497 | apiVersion: v1 498 | kind: Pod 499 | metadata: 500 | creationTimestamp: null 501 | labels: 502 | run: serviceaccount-pod 503 | name: serviceaccount-pod 504 | spec: 505 | serviceAccountName: my-serviceaccount #👈👈👈 Configure Service Accounts for Pods, catch the value is serviceAccountName and NOT serviceaccount 506 | containers: 507 | - image: nginx 508 | name: serviceaccount-pod 509 | resources: {} 510 | dnsPolicy: ClusterFirst 511 | restartPolicy: Always 512 | status: {} 513 | ``` 514 | 515 | ```bash 516 | # Apply the YAML file to the Kubernetes API server 517 | kubectl apply -f ~/ckad/02-04.yml 518 | ``` 519 | 520 | ```bash 521 | # Bonus Section # Verify your work by checking the serviceAccount in use via JSONPath 522 | kubectl get pod serviceaccount-pod -o jsonpath={.spec.serviceAccountName} 523 | ``` 524 | 525 | Output 526 | 527 | ```console 528 | my-serviceaccount 529 | ``` 530 | 531 |

532 |
533 |
534 | 535 | #### 02-05. RBAC Question 536 | * "Error from server (Forbidden): pod is forbidden: User `rbac-sa` cannot `delete` resource `pods` in API group `apps` in the namespace `rbac-namespace`" 537 | * Fix the problem. 538 | 539 |
Overview 540 |

541 | 542 | ![rbac](https://user-images.githubusercontent.com/18049790/145154335-3086bbce-14d9-4a7d-83b7-92a61d4a9456.jpg) 543 | 544 |

545 |
546 | 547 |
RBAC Combinations 548 |

549 | 550 | In English: 551 | 552 | | Combination | Allowed/Disallowed | 553 | | ------ | ----------- | 554 | |Role + RoleBinding| Allowed | 555 | |ClusterRole + ClusterRoleBinding | Allowed | 556 | | ClusterRole + RoleBinding | Allowed | 557 | | Role + ClusterRoleBinding | DISALLOWED | 558 | 559 |

560 |
561 | 562 |
Prerequisites 563 |

564 | 565 | ```bash 566 | clear 567 | kubectl create namespace rbac-namespace #👈👈👈 Create a namespace 568 | kubectl config set-context --current --namespace=rbac-namespace #👈👈👈Change directory into the namespace 569 | kubectl create sa rbac-sa #👈👈👈 Create a Service Account (Who) 570 | kubectl create deployment rbac-deployment --image=nginx --replicas=3 #👈👈👈 Create a deployment 571 | kubectl create role rbac-role --verb=get,watch --resource=pods,pods/status #👈👈👈Create a Role (What and Where) 572 | kubectl create rolebinding rbac-rolebinding --role=rbac-role --serviceaccount=rbac-namespace:rbac-sa #👈👈👈 Bind Account and Role 573 | ``` 574 | 575 | ```bash 576 | kubectl auth can-i get pods --as=system:serviceaccount:rbac-namespace:rbac-sa # yes to get verb 577 | kubectl auth can-i delete pods --as=system:serviceaccount:rbac-namespace:rbac-sa # no to delete verb 578 | ``` 579 | 580 |

581 |
582 | 583 |
Solution 584 |

585 | 586 | ```bash 587 | kubectl get role #👈👈👈 Get all the roles defined in the namespace 588 | ``` 589 | 590 | ```bash 591 | kubectl describe role rbac-role #👈👈👈 Describe the role 592 | ``` 593 | 594 | ```console 595 | Name: rbac-role 596 | Labels: 597 | Annotations: 598 | PolicyRule: 599 | Resources Non-Resource URLs Resource Names Verbs 600 | --------- ----------------- -------------- ----- 601 | pods/status [] [] [get watch] 602 | pods [] [] [get watch] 603 | ``` 604 | 605 | ```bash 606 | kubectl edit role rbac-role #👈👈👈 Edit the role 607 | ``` 608 | 609 | ```console 610 | # Please edit the object below. Lines beginning with a '#' will be ignored, 611 | # and an empty file will abort the edit. If an error occurs while saving this file will be 612 | # reopened with the relevant failures. 613 | # 614 | apiVersion: rbac.authorization.k8s.io/v1 615 | kind: Role 616 | metadata: 617 | creationTimestamp: "2021-12-08T03:29:23Z" 618 | name: rbac-role 619 | namespace: rbac-namespace 620 | resourceVersion: "7927" 621 | uid: 08464e32-4994-4db5-804c-61dabaa803b1 622 | rules: 623 | - apiGroups: 624 | - "" 625 | resources: 626 | - pods 627 | - pods/status 628 | verbs: 629 | - get 630 | - watch 631 | - delete #👈👈👈 Add the verb "delete" 632 | ``` 633 | 634 | ```bash 635 | kubectl describe role rbac-role #👈👈👈 Describe the role 636 | ``` 637 | 638 | ```console 639 | Name: rbac-role 640 | Labels: 641 | Annotations: 642 | PolicyRule: 643 | Resources Non-Resource URLs Resource Names Verbs 644 | --------- ----------------- -------------- ----- 645 | pods/status [] [] [get watch delete] 646 | pods [] [] [get watch delete] 647 | ``` 648 | 649 | ```bash 650 | kubectl auth can-i get pods --as=system:serviceaccount:rbac-namespace:rbac-sa # yes to get 651 | kubectl auth can-i delete pods --as=system:serviceaccount:rbac-namespace:rbac-sa # yes to delete 652 | ``` 653 | 654 |

655 |
656 |
657 | 658 | #### Clean Up 659 | 660 |
Clean Up 661 |

662 | 663 | ```bash 664 | yes | rm -R ~/ckad/ 665 | kubectl delete ns secret-namespace --force 666 | kubectl delete ns quota-namespace --force 667 | kubectl delete ns serviceaccount-namespace --force 668 | kubectl delete ns rbac-namespace --force 669 | ``` 670 | 671 |

672 |
673 | 674 | _End of Section_ 675 | -------------------------------------------------------------------------------- /03-ckad-deployment.md: -------------------------------------------------------------------------------- 1 | ## Sample CKAD Application Deployment Q&A 2 | 3 | ### Application Deployment – 20% 4 | 5 | - Understand Deployments and how to perform rolling updates [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/03-ckad-deployment.md#03-01-create-a-namespace-called-deployment-namespace-create-a-deployment-called-my-deployment-with-three-replicas-using-the-nginx-image-inside-the-namespace-expose-port-80-for-the-nginx-container-the-containers-should-be-named-my-container-each-container-should-have-a-memory-request-of-25mi-and-a-memory-limit-of-100mi) 6 | - Use the Helm package manager to deploy existing packages [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/03-ckad-deployment.md#03-04-use-helm-to-install-wordpress-into-a-namespace-called-wordpress-namespace) 7 | - Use Kubernetes primitives to implement common deployment strategies (e.g. blue/green or canary) [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/03-ckad-deployment.md#03-05-create-a-namespace-called-blue-green-namespace-create-a-deployment-called-blue-deployment-with-10-replicas-using-the-nginx-image-inside-the-namespace-expose-port-80-for-the-nginx-containers-label-the-pods-versionblue-and-tierweb-create-a-service-called-bsg-service-to-route-traffic-to-blue-deployment-verify-that-traffic-is-flowing-from-the-service-to-the-deployment-create-a-new-deployment-called-green-deployment--with-10-replicas-using-the-nginx-image-inside-the-namespace-expose-port-80-for-the-nginx-containers-label-the-pods-versiongreen-and-tierweb-once-the-green-deployment-is-active-split-traffic-between-blue-deployment70-and-green-deployment30) 8 |
9 | 10 | #### 03-01. Deployment Question 11 | * Create a namespace called `deployment-namespace`. 12 | * Create a Deployment called `my-deployment`, with `three` replicas, using the `nginx` image inside the namespace. 13 | * Expose `port 80` for the nginx container. 14 | * The containers should be named `my-container`. 15 | * Each container should have a `memory request` of 25Mi and a `memory limit` of 100Mi. 16 | 17 |
Prerequisites 18 |

19 | 20 | ```bash 21 | mkdir -p ~/ckad/ 22 | clear 23 | # Create the namespace 24 | kubectl create namespace deployment-namespace 25 | ``` 26 | 27 | ```bash 28 | clear 29 | # Switch context into the namespace so that all subsequent commands execute inside that namespace. 30 | kubectl config set-context --current --namespace=deployment-namespace 31 | ``` 32 | 33 | ##### Help Examples 34 | 35 | ```bash 36 | clear 37 | # Run the help flag to get examples 38 | # kubectl create deployment -h 39 | kubectl create deploy -h | more 40 | ``` 41 | 42 | Output: 43 | 44 | ```console 45 | Examples: 46 | # Create a deployment named my-dep that runs the busybox image 47 | kubectl create deployment my-dep --image=busybox 48 | 49 | # Create a deployment with a command 50 | kubectl create deployment my-dep --image=busybox -- date 51 | 52 | # Create a deployment named my-dep that runs the nginx image with 3 replicas 53 | kubectl create deployment my-dep --image=nginx --replicas=3 👈👈👈 This example matches most closely to the question: `three` replicas 54 | 55 | # Create a deployment named my-dep that runs the busybox image and expose port 5701 56 | kubectl create deployment my-dep --image=busybox --port=5701 👈👈👈 This example matches most closely to the question: `port 80` 57 | ``` 58 | 59 |

60 |
61 | 62 |
Solution 63 |

64 | 65 | ```bash 66 | clear 67 | # Using the best example that matches the question 68 | kubectl create deployment my-deployment --image=nginx --replicas=3 --port=80 --dry-run=client -o yaml > ~/ckad/03-01.yml 69 | ``` 70 | 71 | ```bash 72 | # Edit the YAML file to make required changes 73 | mkdir -p ~/ckad/ 74 | vi ~/ckad/03-01.yml 75 | ``` 76 | 77 | kubernetes.io bookmark: [Meaning of memory](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#meaning-of-memory) 78 | 79 | ```bash 80 | apiVersion: apps/v1 81 | kind: Deployment 82 | metadata: 83 | creationTimestamp: null 84 | labels: 85 | app: my-deployment 86 | name: my-deployment 87 | spec: 88 | replicas: 3 89 | selector: 90 | matchLabels: 91 | app: my-deployment 92 | strategy: {} 93 | template: 94 | metadata: 95 | creationTimestamp: null 96 | labels: 97 | app: my-deployment 98 | spec: 99 | containers: 100 | - image: nginx 101 | ports: 102 | - containerPort: 80 103 | name: my-container #👈👈👈 Change from nginx to my container 104 | resources: #👈👈👈 From Meaning of memory link above 105 | requests: #👈👈👈 From Meaning of memory link above 106 | memory: "25Mi" #👈👈👈 From Meaning of memory link above 107 | limits: #👈👈👈 From Meaning of memory link above 108 | memory: "100Mi" #👈👈👈 From Meaning of memory link above 109 | status: {} 110 | ``` 111 | 112 | ```bash 113 | clear 114 | # Apply the YAML file to the Kubernetes API server 115 | kubectl apply -f ~/ckad/03-01.yml 116 | ``` 117 | 118 | ```bash 119 | clear 120 | # Quick verification that the deployment was created and is working 121 | kubectl get all 122 | ``` 123 | 124 | Output: 125 | 126 | ```console 127 | NAME READY STATUS RESTARTS AGE 128 | pod/my-deployment-67fc8546-9b4bm 1/1 Running 0 16m 129 | pod/my-deployment-67fc8546-mjw24 1/1 Running 0 16m 130 | pod/my-deployment-67fc8546-tp5bk 1/1 Running 0 16m 131 | 132 | NAME READY UP-TO-DATE AVAILABLE AGE 133 | deployment.apps/my-deployment 3/3 3 3 16m 134 | 135 | NAME DESIRED CURRENT READY AGE 136 | replicaset.apps/my-deployment-67fc8546 3 3 3 16m 137 | ``` 138 | 139 |

140 |
141 |
142 | 143 | #### 03-02. Expose Deployment Question 144 | * In the previous question a Deployment called `my-deployment` was created. 145 | * Allow network traffic to flow to this deployment from inside the cluster on `port 8080`. 146 | 147 |
Help 148 |

149 | 150 | ```bash 151 | clear 152 | # Run the help flag to get examples 153 | kubectl expose -h | more 154 | ``` 155 | 156 | Output: 157 | 158 | ```console 159 | Examples: 160 | # Create a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000 161 | kubectl expose rc nginx --port=80 --target-port=8000 162 | 163 | # Create a service for a replication controller identified by type and name specified in "nginx-controller.yaml", 164 | which serves on port 80 and connects to the containers on port 8000 165 | kubectl expose -f nginx-controller.yaml --port=80 --target-port=8000 166 | 167 | # Create a service for a pod valid-pod, which serves on port 444 with the name "frontend" 168 | kubectl expose pod valid-pod --port=444 --name=frontend 169 | 170 | # Create a second service based on the above service, exposing the container port 8443 as port 443 with the name 171 | "nginx-https" 172 | kubectl expose service nginx --port=443 --target-port=8443 --name=nginx-https 173 | 174 | # Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named 'video-stream'. 175 | kubectl expose rc streamer --port=4100 --protocol=UDP --name=video-stream 176 | 177 | # Create a service for a replicated nginx using replica set, which serves on port 80 and connects to the containers on 178 | port 8000 179 | kubectl expose rs nginx --port=80 --target-port=8000 180 | 181 | # Create a service for an nginx deployment, which serves on port 80 and connects to the containers on port 8000 182 | kubectl expose deployment nginx --port=80 --target-port=8000 👈👈👈 This example matches most closely to the question. 183 | ``` 184 | 185 |

186 |
187 | 188 |
Solution 189 |

190 | 191 | ```bash 192 | clear 193 | # Using the best example that matches the question 194 | kubectl expose deployment my-deployment --port=8080 --target-port=80 195 | ``` 196 | 197 | Watch out for the statement from inside the Cluster so this is of type: ClusterIP 198 | 199 | Types include: 200 | 201 | - ClusterIP (default) 202 | - NodePort 203 | - LoadBalancer 204 | - ExternalName 205 | 206 | ```bash 207 | clear 208 | # Check that the Service was created 209 | # Inside the namespace: my-deployment 210 | # Outside the namespace: my-deployment.deployment-namespace.svc.cluster.local 211 | kubectl get service 212 | ``` 213 | 214 | Output: 215 | 216 | ```console 217 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 218 | my-deployment ClusterIP 10.245.79.74 80/TCP 103s 219 | ``` 220 | 221 | ```bash 222 | clear 223 | # A quicker check is to see if the Pod Endpoints are being load balanced 224 | kubectl get endpoints 225 | kubectl get pods -o wide 226 | ``` 227 | 228 | Output: 229 | 230 | ```console 231 | NAME ENDPOINTS AGE 232 | my-deployment 10.244.0.250:80,10.244.1.132:80,10.244.1.246:80 5m20s 233 | # The three replicas internal endpoints are registered 234 | ``` 235 | 236 |

237 |
238 |
239 | 240 | #### 03-03. Change Image Question 241 | * Create a namespace called `edit-namespace`. 242 | * Create a deployment called `edit-deployment` with `2` replicas using the `redis` image in namespace. 243 | * After the deployment is running, alter the containers to use the `nginx` image. 244 | * Then alter the containers to use the `nginx:1.14.2` image. 245 | 246 |
Prerequisites 247 |

248 | 249 | ```bash 250 | clear 251 | kubectl create namespace edit-namespace 252 | kubectl create deployment edit-deployment --image=redis --replicas=2 -n edit-namespace 253 | kubectl config set-context --current --namespace=edit-namespace 254 | ``` 255 | 256 |

257 |
258 | 259 |
Solution - kubectl edit 260 |

261 | 262 | ##### Solution 263 | 264 | ```bash 265 | kubectl edit deployment.apps/edit-deployment 266 | ``` 267 | 268 | ```yaml 269 | # Please edit the object below. Lines beginning with a '#' will be ignored, 270 | # and an empty file will abort the edit. If an error occurs while saving this file will be 271 | # reopened with the relevant failures. 272 | # 273 | apiVersion: apps/v1 274 | kind: Deployment 275 | metadata: 276 | annotations: 277 | deployment.kubernetes.io/revision: "1" 278 | creationTimestamp: "2021-09-24T06:23:27Z" 279 | generation: 1 280 | labels: 281 | app: edit-deployment 282 | name: edit-deployment 283 | namespace: edit-namespace 284 | resourceVersion: "7856" 285 | uid: d482067c-da5f-43ce-aa31-25defd2d0de3 286 | spec: 287 | progressDeadlineSeconds: 600 288 | replicas: 2 289 | revisionHistoryLimit: 10 290 | selector: 291 | matchLabels: 292 | app: edit-deployment 293 | strategy: 294 | rollingUpdate: 295 | maxSurge: 25% 296 | maxUnavailable: 25% 297 | type: RollingUpdate 298 | template: 299 | metadata: 300 | creationTimestamp: null 301 | labels: 302 | app: edit-deployment 303 | spec: 304 | containers: 305 | - image: redis #👈👈👈 Change this to nginx 306 | imagePullPolicy: Always 307 | name: redis #👈👈👈 This is the catch, when you created the deployment it used the image=redis to also name the container redis 308 | resources: {} 309 | terminationMessagePath: /dev/termination-log 310 | terminationMessagePolicy: File 311 | dnsPolicy: ClusterFirst 312 | restartPolicy: Always 313 | ``` 314 | 315 | ```bash 316 | clear 317 | # Check the image in the Deployment 318 | kubectl describe deployment edit-deployment | grep Image 319 | ``` 320 | 321 | This works but does not record what the change was. 322 | 323 |

324 |
325 | 326 |
Solution - kubectl set image 327 |

328 | 329 | kubernetes.io:[Updating a Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment) 330 | 331 | ```bash 332 | clear 333 | # Use the kubectl set image command 334 | kubectl set image deployment.apps/edit-deployment redis=nginx:1.14.2 335 | ``` 336 | 337 | ```bash 338 | clear 339 | # Check the image in the Deployment 340 | kubectl describe deployment edit-deployment | grep Image 341 | ``` 342 | 343 |

344 |
345 |
346 | 347 | #### 03-04. Helm Question 348 | * Use Helm to install WordPress into a namespace called `wordpress-namespace` 349 | 350 |
Prerequisites 351 |

352 | 353 | WordPress on Bitnami [details](https://github.com/bitnami/charts/tree/master/bitnami/wordpress/#installing-the-chart) 354 | 355 | ```bash 356 | clear 357 | kubectl create namespace wordpress-namespace 358 | kubectl config set-context --current --namespace=wordpress-namespace 359 | ``` 360 | 361 |

362 |
363 | 364 |
Solution 365 |

366 | 367 | ```bash 368 | clear 369 | # Add the Bitnami repo 370 | helm repo add bitnami https://charts.bitnami.com/bitnami 371 | ``` 372 | 373 | ```bash 374 | # Search the Bitnami repo for available software 375 | helm search repo bitnami 376 | ``` 377 | 378 | Output: 379 | 380 | ```console 381 | NAME CHART VERSION APP VERSION DESCRIPTION 382 | bitnami/bitnami-common 0.0.9 0.0.9 DEPRECATED Chart with custom templates used in ... 383 | bitnami/airflow 11.0.8 2.1.4 Apache Airflow is a platform to programmaticall... 384 | bitnami/apache 8.8.3 2.4.50 Chart for Apache HTTP Server 385 | bitnami/argo-cd 2.0.4 2.1.3 Declarative, GitOps continuous delivery tool fo... 386 | bitnami/argo-workflows 0.1.1 3.1.13 Argo Workflows is meant to orchestrate Kubernet... 387 | bitnami/aspnet-core 1.3.18 3.1.19 ASP.NET Core is an open-source framework create... 388 | bitnami/cassandra 8.0.6 4.0.1 Apache Cassandra is a free and open-source dist... 389 | bitnami/cert-manager 0.1.21 1.5.4 Cert Manager is a Kubernetes add-on to automate... 390 | bitnami/common 1.10.0 1.10.0 A Library Helm Chart for grouping common logic ... 391 | bitnami/concourse 0.1.7 7.5.0 Concourse is a pipeline-based continuous thing-... 392 | bitnami/consul 9.3.8 1.10.3 Highly available and distributed service discov... 393 | bitnami/contour 5.7.0 1.18.2 Contour Ingress controller for Kubernetes 394 | bitnami/contour-operator 0.1.1 1.18.2 The Contour Operator extends the Kubernetes API... 395 | bitnami/dataplatform-bp1 8.0.1 0.0.11 OCTO Data platform Kafka-Spark-Solr Helm Chart 396 | bitnami/dataplatform-bp2 8.0.3 0.0.10 OCTO Data platform Kafka-Spark-Elasticsearch He... 397 | bitnami/discourse 5.0.2 2.7.8 A Helm chart for deploying Discourse to Kubernetes 398 | bitnami/dokuwiki 11.2.8 20200729.0.0 DokuWiki is a standards-compliant, simple to us... 399 | bitnami/drupal 10.3.4 9.2.7 One of the most versatile open source content m... 400 | bitnami/ejbca 3.0.1 7.4.3-2 Enterprise class PKI Certificate Authority buil... 401 | bitnami/elasticsearch 17.1.0 7.14.2 A highly scalable open-source full-text search ... 402 | bitnami/etcd 6.8.4 3.5.0 etcd is a distributed key value store that prov... 403 | bitnami/external-dns 5.4.10 0.10.0 ExternalDNS is a Kubernetes addon that configur... 404 | bitnami/fluentd 4.2.3 1.14.1 Fluentd is an open source data collector for un... 405 | bitnami/geode 0.1.0 1.14.0 Apache Geode is a data management platform that... 406 | bitnami/ghost 14.0.22 4.17.1 A simple, powerful publishing platform that all... 407 | bitnami/grafana 6.3.2 8.2.0 Grafana is an open source, feature rich metrics... 408 | bitnami/grafana-operator 1.1.4 3.10.3 Kubernetes Operator based on the Operator SDK f... 409 | bitnami/grafana-tempo 0.2.7 1.1.0 Grafana Tempo is an open source, easy-to-use an... 410 | bitnami/haproxy 0.2.13 2.4.7 HAProxy is a TCP proxy and a HTTP reverse proxy... 411 | ... 412 | bitnami/phpmyadmin 8.2.16 5.1.1 phpMyAdmin is an mysql administration frontend 413 | bitnami/postgresql 10.12.2 11.13.0 Chart for PostgreSQL, an object-relational data... 414 | bitnami/postgresql-ha 7.10.1 11.13.0 Chart for PostgreSQL with HA architecture (usin... 415 | bitnami/prestashop 13.2.3 1.7.8-0 A popular open source ecommerce solution. Profe... 416 | bitnami/prometheus-operator 0.31.1 0.41.0 DEPRECATED The Prometheus Operator for Kubernet... 417 | bitnami/pytorch 2.3.14 1.9.0 Deep learning platform that accelerates the tra... 418 | bitnami/rabbitmq 8.22.4 3.9.7 Open source message broker software that implem... 419 | bitnami/rabbitmq-cluster-operator 0.1.6 1.9.0 The RabbitMQ Cluster Kubernetes Operator automa... 420 | bitnami/redis 15.4.1 6.2.6 Open source, advanced key-value store. It is of... 421 | bitnami/redis-cluster 6.3.9 6.2.6 Open source, advanced key-value store. It is of... 422 | bitnami/redmine 17.0.9 4.2.2 A flexible project management web application. 423 | bitnami/solr 2.0.7 8.9.0 Apache Solr is an open source enterprise search... 424 | bitnami/spark 5.7.4 3.1.2 Spark is a fast and general-purpose cluster com... 425 | bitnami/spring-cloud-dataflow 4.1.1 2.8.2 Spring Cloud Data Flow is a microservices-based... 426 | bitnami/sugarcrm 1.0.6 6.5.26 DEPRECATED SugarCRM enables businesses to creat... 427 | bitnami/suitecrm 9.3.25 7.11.22 SuiteCRM is a completely open source enterprise... 428 | bitnami/tensorflow-inception 3.3.2 1.13.0 DEPRECATED Open-source software library for ser... 429 | bitnami/tensorflow-resnet 3.2.15 2.6.0 Open-source software library serving the ResNet... 430 | bitnami/testlink 9.2.24 1.9.20 Web-based test management system that facilitat... 431 | bitnami/thanos 6.0.12 0.23.1 Thanos is a highly available metrics system tha... 432 | bitnami/tomcat 9.4.3 10.0.12 Chart for Apache Tomcat 433 | bitnami/wavefront 3.1.12 1.7.1 Chart for Wavefront Collector for Kubernetes 434 | bitnami/wavefront-adapter-for-istio 1.0.8 0.1.5 Wavefront Adapter for Istio is a lightweight Is... 435 | bitnami/wavefront-hpa-adapter 0.1.5 0.9.8 Wavefront HPA Adapter for Kubernetes is a Kuber... 436 | bitnami/wavefront-prometheus-storage-adapter 1.0.8 1.0.3 Wavefront Storage Adapter is a Prometheus integ... 437 | bitnami/wildfly 11.1.3 24.0.1 Chart for Wildfly 438 | bitnami/wordpress 12.1.20 5.8.1 Web publishing platform for building blogs and ... #👈👈👈 439 | bitnami/zookeeper 7.4.6 3.7.0 A centralized service for maintaining configura... 440 | ``` 441 | 442 | ```bash 443 | # Search the Bitnami repo for WordPress 444 | helm search repo bitnami | grep wordpress 445 | ``` 446 | 447 | Output: 448 | 449 | ```console 450 | bitnami/wordpress 12.1.20 5.8.1 Web publishing platform for building blogs and ... 451 | ``` 452 | 453 | ```bash 454 | # Install WordPress with Helm 455 | helm install my-wp-release \ 456 | --set wordpressUsername=admin \ 457 | --set wordpressPassword=password \ 458 | --set mariadb.auth.rootPassword=secretpassword \ 459 | --set service.type=ClusterIP \ 460 | bitnami/wordpress 461 | ``` 462 | 463 | Output: 464 | 465 | ```console 466 | NAME: my-wp-release 467 | LAST DEPLOYED: Fri Oct 8 15:44:30 2021 468 | NAMESPACE: wordpress-namespace 469 | STATUS: deployed 470 | REVISION: 1 471 | TEST SUITE: None 472 | NOTES: 473 | ** Please be patient while the chart is being deployed ** 474 | 475 | Your WordPress site can be accessed through the following DNS name from within your cluster: 476 | 477 | my-wp-release-wordpress.wordpress-namespace.svc.cluster.local (port 80) 478 | 479 | To access your WordPress site from outside the cluster follow the steps below: 480 | 481 | 1. Get the WordPress URL by running these commands: 482 | 483 | kubectl port-forward --namespace wordpress-namespace svc/my-wp-release-wordpress 80:80 & 484 | echo "WordPress URL: http://127.0.0.1//" 485 | echo "WordPress Admin URL: http://127.0.0.1//admin" 486 | 487 | 2. Open a browser and access WordPress using the obtained URL. 488 | 489 | 3. Login with the following credentials below to see your blog: 490 | 491 | echo Username: admin 492 | echo Password: $(kubectl get secret --namespace wordpress-namespace my-wp-release-wordpress -o jsonpath="{.data.wordpress-password}" | base64 --decode) 493 | ``` 494 | 495 | ```bash 496 | clear 497 | # Run a Busybox pod in the namespace to check your work 498 | kubectl run remote-run --image=busybox --restart=Never --rm -it 499 | # Check your work - curl the service to verify operation 500 | wget -qO- my-wp-release-wordpress 501 | ``` 502 | 503 | ```bash 504 | # List active helm releases 505 | helm ls 506 | ``` 507 | 508 | ```bash 509 | # Delete WordPress with Helm 510 | helm delete my-wp-release --purge 511 | ``` 512 | 513 | 514 |

515 | 516 |
Operations 517 |

518 | 519 | ##### Install a Release 520 | 521 | ```bash 522 | helm install happy-panda bitnami/wordpress 523 | ``` 524 | 525 | ##### Upgrade a Release 526 | 527 | ```bash 528 | helm upgrade --set foo=bar --set foo=newbar redis ./redis 529 | ``` 530 | 531 | ##### Delete a Release 532 | 533 | ```bash 534 | helm uninstall my-release 535 | ``` 536 | 537 | ##### Broken Release 538 | 539 | ```bash 540 | helm list 541 | helm uninstall my-release 542 | ``` 543 | 544 | 545 |

546 |
547 |
548 | 549 | #### 03-05. Canary Deployment Question 550 | * Create a namespace called `blue-green-namespace`. 551 | * Create a Deployment called `blue-deployment`, with `10` replicas, using the `nginx` image inside the namespace. 552 | * Expose `port 80` for the nginx containers. 553 | * Label the pods `app=blue-deployment` and `tier=web`. 554 | * Create a Service called `bg-service` to route traffic to `blue-deployment`. 555 | * Verify that traffic is flowing from the Service to the Deployment. 556 | * Create a new Deployment called `green-deployment` , with `10` replicas, using the `nginx` image inside the namespace. 557 | * Expose `port 80` for the nginx containers. 558 | * Label the pods `app=green-deployment` and `tier=web`. 559 | * Once the `green-deployment` is active split traffic between `blue-deployment`=70% and `green-deployment`=30% 560 | 561 |
Overview 562 |

563 | 564 | ![50-exec-blue-fixed](https://user-images.githubusercontent.com/18049790/162353407-3d6b296a-77c5-488b-8171-3262bf0f0d79.jpg) 565 | 566 | For clarity in the solution steps below i use images that return: 567 | * Green Deployment 568 | * Green !!! 569 | * Green !!! 570 | * Green !!! 571 | * Blue Deployment 572 | * Blue !!! 573 | * Blue !!! 574 | * Blue !!! 575 | 576 |

577 |
578 | 579 |
Prerequisites 580 |

581 | 582 | ```bash 583 | clear 584 | # Create the namespace 585 | kubectl create namespace blue-green-namespace 586 | ``` 587 | 588 | ```bash 589 | clear 590 | # Switch context into the namespace so that all subsequent commands execute inside that namespace. 591 | kubectl config set-context --current --namespace=blue-green-namespace 592 | ``` 593 | 594 |

595 |
596 | 597 |
Solution - Blue Deployment 598 |

599 | 600 | ```bash 601 | clear 602 | mkdir -p ~/ckad/ 603 | # Create the deployment as far as possible using the CLI (imperatively) 604 | kubectl create deployment blue-deployment --image=nginx --replicas=10 --port=80 --dry-run=client -o yaml > ~/ckad/03-05-deploy-blue.yml 605 | ``` 606 | 607 | ```bash 608 | clear 609 | # Edit the YAML file to make required changes 610 | # Use the Question number in case you want to return to the question for reference or for review 611 | vi ~/ckad/03-05-deploy-blue.yml 612 | ``` 613 | 614 | ```yaml 615 | apiVersion: apps/v1 616 | kind: Deployment 617 | metadata: 618 | creationTimestamp: null 619 | labels: 620 | app: blue-deployment 621 | name: blue-deployment 622 | spec: 623 | replicas: 10 624 | selector: 625 | matchLabels: 626 | app: blue-deployment 627 | strategy: {} 628 | template: 629 | metadata: 630 | creationTimestamp: null 631 | labels: 632 | app: blue-deployment 633 | tier: web #👈👈👈 Add the label: `tier=web` 634 | spec: 635 | containers: 636 | - image: docker.io/jamesbuckett/blue:latest 637 | name: blue 638 | ports: 639 | - containerPort: 80 640 | resources: {} 641 | status: {} 642 | ``` 643 | 644 | ```bash 645 | clear 646 | # Apply the YAML file to the Kubernetes API server 647 | kubectl apply -f ~/ckad/03-05-deploy-blue.yml 648 | ``` 649 | 650 | ```bash 651 | clear 652 | # Quick verification that the pod was created and is working 653 | kubectl get pod --watch 654 | # or kubectl get pod -w 655 | ``` 656 | 657 | ```bash 658 | clear 659 | # See all labels on Pods 660 | kubectl get pods --show-labels 661 | ``` 662 | 663 | ```bash 664 | clear 665 | # Check labels specific to this question 666 | kubectl get pods -L tier,version 667 | ``` 668 | 669 |

670 |
671 | 672 |
Solution - Service 673 |

674 | 675 | ```bash 676 | clear 677 | # Create the namespace 678 | kubectl expose deployment blue-deployment --port=80 --target-port=80 --name=bg-service --dry-run=client -o yaml > ~/ckad/03-05-bsg-service.yml 679 | ``` 680 | 681 | ```bash 682 | clear 683 | mkdir -p ~/ckad/ 684 | # Edit the YAML file to make required changes 685 | vi ~/ckad/03-05-bg-service.yml 686 | ``` 687 | 688 | ```yaml 689 | apiVersion: v1 690 | kind: Service 691 | metadata: 692 | creationTimestamp: null 693 | labels: 694 | app: blue-deployment 695 | name: bg-service 696 | spec: 697 | ports: 698 | - port: 80 699 | protocol: TCP 700 | targetPort: 80 701 | selector: 702 | # app: blue-deployment #👈👈👈 Delete this 703 | tier: web #👈👈👈 Add the label: `tier=web`. This is the sauce. One label pointing to both deployments 704 | status: 705 | loadBalancer: {} 706 | ``` 707 | 708 | ```bash 709 | clear 710 | # Apply the YAML file to the Kubernetes API server 711 | kubectl apply -f ~/ckad/03-05-bg-service.yml 712 | ``` 713 | 714 |

715 |
716 | 717 |
Solution - Green Deployment 718 |

719 | 720 | ```bash 721 | clear 722 | # Create the deployment as far as possible using the CLI (imperatively) 723 | kubectl create deployment green-deployment --image=nginx --replicas=10 --port=80 --dry-run=client -o yaml > ~/ckad/03-05-deploy-green.yml 724 | ``` 725 | 726 | An even faster way would be to copy the 03-05-deploy-blue.yml to 03-05-deploy-green.yml 727 | * Alter the labels 728 | * Alter the image 729 | * Save and Apply 730 | 731 | ```bash 732 | clear 733 | mkdir -p ~/ckad/ 734 | # Edit the YAML file to make required changes 735 | # Use the Question number in case you want to return to the question for reference or for review 736 | vi ~/ckad/03-05-deploy-green.yml 737 | ``` 738 | 739 | ```yaml 740 | apiVersion: apps/v1 741 | kind: Deployment 742 | metadata: 743 | creationTimestamp: null 744 | labels: 745 | app: green-deployment 746 | name: green-deployment 747 | spec: 748 | replicas: 10 749 | selector: 750 | matchLabels: 751 | app: green-deployment 752 | strategy: {} 753 | template: 754 | metadata: 755 | creationTimestamp: null 756 | labels: 757 | app: green-deployment 758 | tier: web #👈👈👈 Add the label: `tier=web` 759 | spec: 760 | containers: 761 | - image: docker.io/jamesbuckett/green:latest 762 | name: green 763 | ports: 764 | - containerPort: 80 765 | resources: {} 766 | status: {} 767 | ``` 768 | 769 | ```bash 770 | clear 771 | # Apply the YAML file to the Kubernetes API server 772 | kubectl apply -f ~/ckad/03-05-deploy-green.yml 773 | ``` 774 | 775 | ```bash 776 | clear 777 | # Quick verification that the pod was created and is working 778 | kubectl get pod --watch 779 | # or kubectl get pod -w 780 | ``` 781 | 782 | ```bash 783 | clear 784 | # Check labels specific to this question 785 | kubectl get pods -L tier,version 786 | ``` 787 | 788 |

789 |
790 | 791 |
Solution - Adjust Replicas 792 |

793 | 794 | ```bash 795 | clear 796 | # Scale Blue Deployment to 7=70% 797 | kubectl scale --replicas=7 deployment blue-deployment 798 | ``` 799 | 800 | ```bash 801 | clear 802 | # Scale Green Deployment to 3=30% 803 | kubectl scale --replicas=3 deployment green-deployment 804 | ``` 805 | 806 | ```bash 807 | clear 808 | # Check labels specific to this question 809 | kubectl get pods -L tier,version 810 | ``` 811 | 812 | Output: 813 | 814 | ```console 815 | NAME READY STATUS RESTARTS AGE VERSION TIER 816 | blue-deployment-5f855f68d6-295xb 1/1 Running 0 9m52s blue web 817 | blue-deployment-5f855f68d6-2b4wv 1/1 Running 0 9m52s blue web 818 | blue-deployment-5f855f68d6-2c9wn 1/1 Running 0 9m52s blue web 819 | blue-deployment-5f855f68d6-5d4kb 1/1 Running 0 9m52s blue web 820 | blue-deployment-5f855f68d6-7tqx7 1/1 Running 0 9m52s blue web 821 | blue-deployment-5f855f68d6-k4s4w 1/1 Running 0 9m52s blue web 822 | blue-deployment-5f855f68d6-vqv8m 1/1 Running 0 9m52s blue web 823 | green-deployment-5b9f998d46-dlmsx 1/1 Running 0 112s green web 824 | green-deployment-5b9f998d46-k6lpt 1/1 Running 0 112s green web 825 | green-deployment-5b9f998d46-tjxl6 1/1 Running 0 112s green web 826 | ``` 827 | 828 | ```bash 829 | clear 830 | # Check your work - curl the service to verify operation 831 | kubectl run remote-run --image=busybox --restart=Never --rm -it 832 | # Repeat this command to see different responses 833 | wget -qO- bg-service 834 | ``` 835 | 836 | Output 837 | 838 | ```console 839 | / # wget -qO- bg-service 840 | Green !!! 841 | Green !!! 842 | Green !!! 843 | / # wget -qO- bg-service 844 | Blue !!! 845 | Blue !!! 846 | Blue !!! 847 | / # wget -qO- bg-service 848 | Blue !!! 849 | Blue !!! 850 | Blue !!! 851 | / # wget -qO- bg-service 852 | Blue !!! 853 | Blue !!! 854 | Blue !!! 855 | / # wget -qO- bg-service 856 | Blue !!! 857 | Blue !!! 858 | Blue !!! 859 | / # wget -qO- bg-service 860 | Blue !!! 861 | Blue !!! 862 | Blue !!! 863 | / # wget -qO- bg-service 864 | Blue !!! 865 | Blue !!! 866 | Blue !!! 867 | / # wget -qO- bg-service 868 | Blue !!! 869 | Blue !!! 870 | Blue !!! 871 | / # wget -qO- bg-service 872 | Green !!! 873 | Green !!! 874 | Green !!! 875 | ``` 876 | 877 |

878 |
879 |
880 | 881 | #### Clean Up 882 | 883 |
Clean Up 884 |

885 | 886 | ```bash 887 | yes | rm -R ~/ckad/ 888 | kubectl delete ns deployment-namespace --force 889 | kubectl delete ns edit-namespace --force 890 | kubectl delete ns wordpress-namespace --force 891 | kubectl delete ns blue-green-namespace --force 892 | ``` 893 | 894 |

895 |
896 | 897 | _End of Section_ 898 | -------------------------------------------------------------------------------- /04-ckad-services-networking.md: -------------------------------------------------------------------------------- 1 | ## Sample CKAD Services and Networking Q&A 2 | 3 | ### Services and Networking – 20% 4 | 5 | - Demonstrate basic understanding of NetworkPolicies [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/04-ckad-services-networking.md#04-01-create-a-namespace-called-netpol-namespace-create-a-pod-called-web-pod-using-the-nginx-image-and-exposing-port-80-label-the-pod-tierweb-create-a-pod-called-app-pod-using-the-nginx-image-and-exposing-port-80-label-the-pod-tierapp-create-a-pod-called-db-pod-using-the-nginx-image-and-exposing-port-80-label-the-pod-tierdb-create-a-network-policy-called-my-netpol-that-allows-the-web-pod-to-only-egress-to-app-pod-on-port-80-in-turn-only-allow-app-pod-to-egress-to-db-pod-on-port-80) 6 | - Provide and troubleshoot access to applications via services [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/04-ckad-services-networking.md#04-02-create-a-namespace-called-service-namespace-create-a-pod-called-service-pod-using-the-nginx-image-and-exposing-port-80-label-the-pod-tierweb-create-a-service-for-the-pod-called-my-service-allowing-for-communication-inside-the-cluster-let-the-service-expose-port-8080) 7 | - Use Ingress rules to expose applications [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/04-ckad-services-networking.md#04-03-create-an-ingress-called-my-ingress-to-expose-the-service-my-service-outside-the-cluster) 8 |
9 | 10 | #### 04-01. Network Policy Question 11 | * Create a namespace called `netpol-namespace`. 12 | * Create a pod called `web-pod` using the `nginx` image and label the pod `tier=web`. 13 | * Create a pod called `app-pod` using the `nginx` image and label the pod `tier=app`. 14 | * Create a pod called `db-pod` using the `nginx` image and label the pod `tier=db`. 15 | * Create Network Policies that allow the `web-pod` to connect with the `app-pod` on port `80` only. 16 | 17 | ```diff 18 | Please NOTE: 19 | - Docker Desktop does not support CNI (container network interface) so the NetworkPolicy's define are ignored. 20 | - The commands work but the NetworkPolicy's are not enforced 21 | ``` 22 | 23 |
Overview 24 |

25 | 26 | ![05-netpol](https://user-images.githubusercontent.com/18049790/140638229-62871b17-bc71-4e51-a71c-4c75c178a78f.jpg) 27 | 28 | I use the notepad to sketch out the ingress and egress before starting 29 | 30 | Rules 31 | - `tier: web` > `tier: app` on port 80 32 | 33 | Use this link to visually solve the problem: 34 | * [Network Policy Editor](https://editor.cilium.io/) 35 | 36 | Use this link for common network policy recipes: 37 | * [Kubernetes Network Policy Recipes](https://github.com/ahmetb/kubernetes-network-policy-recipes) 38 | 39 | Notes 40 | * Network policies do not conflict; they are additive. 41 | * If any policy or policies select a pod, the pod is restricted to what is allowed by the union of those policies' ingress/egress rules. 42 | * Thus, order of evaluation does not affect the policy result. 43 | 44 |

45 |
46 | 47 | 48 |
Prerequisites 49 |

50 | 51 | For clarity in the solution steps below i use images that return: 52 | * web-pod 53 | * web-pod !!! 54 | * web-pod !!! 55 | * web-pod !!! 56 | * app-pod 57 | * app-pod !!! 58 | * app-pod !!! 59 | * app-pod !!! 60 | * db-pod 61 | * db-pod !!! 62 | * db-pod !!! 63 | * db-pod !!! 64 | 65 | ```bash 66 | mkdir ~/ckad/ 67 | clear 68 | # Create all the required resources 69 | kubectl create namespace netpol-namespace 70 | kubectl config set-context --current --namespace=netpol-namespace 71 | 72 | # tier: web 73 | kubectl run web-pod --image=docker.io/jamesbuckett/web:latest --port=80 --labels="tier=web" 74 | 75 | # tier: app 76 | kubectl run app-pod --image=docker.io/jamesbuckett/app:latest --port=80 --labels="tier=app" 77 | 78 | # tier: db 79 | kubectl run db-pod --image=docker.io/jamesbuckett/db:latest --port=80 --labels="tier=db" 80 | 81 | clear 82 | kubectl get all 83 | kubectl get pod -L tier 84 | ``` 85 | 86 | ```bash 87 | clear 88 | # Get the IP address of pods 89 | kubectl get pods -o wide 90 | ``` 91 | 92 | ```bash 93 | # Inside the web-pod try to curl to app and db pods 94 | kubectl exec --stdin --tty web-pod -- /bin/bash 95 | 96 | #curl 97 | #curl 98 | ``` 99 | 100 |

101 |
102 | 103 |
Solution 104 |

105 | 106 | kubernetes.io bookmark: [The NetworkPolicy resource](https://kubernetes.io/docs/concepts/services-networking/network-policies/#networkpolicy-resource) 107 | 108 | Deny All Traffic in Namespace from [here](https://kubernetes.io/docs/concepts/services-networking/network-policies/#default-deny-all-ingress-and-all-egress-traffic) 109 | 110 | ```bash 111 | cat << EOF | kubectl apply -f - 112 | apiVersion: networking.k8s.io/v1 113 | kind: NetworkPolicy 114 | metadata: 115 | name: default-deny-all 116 | namespace: netpol-namespace 117 | spec: 118 | podSelector: {} 119 | policyTypes: 120 | - Ingress 121 | - Egress 122 | EOF 123 | ``` 124 | 125 | ```bash 126 | mkdir -p ~/ckad/ 127 | vi ~/ckad/04-01-netpol.yml 128 | ``` 129 | 130 | ```yaml 131 | apiVersion: networking.k8s.io/v1 132 | kind: NetworkPolicy 133 | metadata: 134 | name: web-policy #👈👈👈 Change 135 | namespace: netpol-namespace 136 | spec: 137 | podSelector: 138 | matchLabels: 139 | tier: web #👈👈👈 Change - Which pod does this Network Policy Apply to i.e. any pod with label tier=web 140 | egress: 141 | - to: 142 | - podSelector: 143 | matchLabels: 144 | tier: app #👈👈👈 Egress - Traffic to pod with label tier=app 145 | ports: 146 | - port: 80 147 | --- 148 | apiVersion: networking.k8s.io/v1 149 | kind: NetworkPolicy 150 | metadata: 151 | name: app-policy #👈👈👈 Change 152 | namespace: netpol-namespace 153 | spec: 154 | podSelector: 155 | matchLabels: 156 | tier: app #👈👈👈 Change - Which pod does this Network Policy Apply to i.e. any pod with label tier=app 157 | ingress: 158 | - from: 159 | - podSelector: 160 | matchLabels: 161 | tier: web #👈👈👈 Ingress - Traffic from pod with label tier=web 162 | ports: 163 | - port: 80 164 | ``` 165 | 166 | ```bash 167 | kubectl apply -f ~/ckad/04-01-netpol.yml 168 | ``` 169 | 170 | ```bash 171 | clear 172 | # Get the IP address of pods 173 | kubectl get pods -o wide 174 | ``` 175 | 176 | ```bash 177 | # Inside the web-pod try to curl to app and db pods 178 | kubectl exec --stdin --tty web-pod -- /bin/bash 179 | 180 | #curl 181 | #curl 182 | ``` 183 | 184 | Output: This output is from Digital Ocean where the Network Policies are enforced. 185 | 186 | ```console 187 | [root@digital-ocean-droplet ~ (do-sgp1-digital-ocean-cluster:netpol-namespace)]# kubectl get pods -o wide 188 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES 189 | app-pod 1/1 Running 0 57s 10.244.1.197 digital-ocean-pool-uch0o 190 | db-pod 1/1 Running 0 2m13s 10.244.3.11 digital-ocean-pool-uch0j 191 | web-pod 1/1 Running 0 2m13s 10.244.3.3 digital-ocean-pool-uch0j 192 | 193 | [root@digital-ocean-droplet ~ (do-sgp1-digital-ocean-cluster:netpol-namespace)]# kubectl exec --stdin --tty web-pod -- /bin/bash 194 | root@web-pod:/# curl 10.244.1.197 195 | app-pod !!! 196 | app-pod !!! 197 | app-pod !!! 198 | root@web-pod:/# curl 10.244.3.11 199 | db-pod !!! 200 | db-pod !!! 201 | db-pod !!! 202 | root@web-pod:/# curl 10.244.3.3 203 | web-pod !!! 204 | web-pod !!! 205 | web-pod !!! 206 | ---------------------------------- #👈👈👈 Network Policy Applied 207 | root@web-pod:/# curl 10.244.3.3 208 | web-pod !!! 209 | web-pod !!! 210 | web-pod !!! 211 | root@web-pod:/# curl 10.244.3.11 #👈👈👈 Cannot curl db-pod 212 | ^C 213 | root@web-pod:/# curl 10.244.1.197 #👈👈👈 Can curl app-pod 214 | app-pod !!! 215 | app-pod !!! 216 | app-pod !!! 217 | ``` 218 | 219 |

220 |
221 |
222 | 223 | #### 04-02. Kubernetes Service Question 224 | * Create a namespace called `service-namespace`. 225 | * Create a pod called `service-pod` using the `nginx` image and exposing port `80`. 226 | * Label the pod `tier=web`. 227 | * Create a service for the pod called `my-service` allowing for communication inside the cluster. 228 | * Let the service expose port 8080. 229 | 230 |
Overview 231 |

232 | 233 | ![05-network-svc](https://user-images.githubusercontent.com/18049790/140637642-5ef46de4-4867-41a6-ba44-6333cd9441af.jpg) 234 | 235 |

236 |
237 | 238 |
Prerequisites 239 |

240 | 241 | ```bash 242 | mkdir -p ~/ckad/ 243 | clear 244 | kubectl create namespace service-namespace 245 | kubectl config set-context --current --namespace=service-namespace 246 | ``` 247 | 248 |

249 |
250 | 251 |
Solution - Create Pod 252 |

253 | 254 | ##### Help Examples 255 | 256 | ```bash 257 | clear 258 | kubectl run -h | more 259 | ``` 260 | 261 | Output: 262 | 263 | ```console 264 | Examples: 265 | # Start a nginx pod 266 | kubectl run nginx --image=nginx 267 | 268 | # Start a hazelcast pod and let the container expose port 5701 269 | kubectl run hazelcast --image=hazelcast/hazelcast --port=5701 👈👈👈 This example matches most closely to the question: exposing port `80` 270 | 271 | # Start a hazelcast pod and set environment variables "DNS_DOMAIN=cluster" and "POD_NAMESPACE=default" in the container 272 | kubectl run hazelcast --image=hazelcast/hazelcast --env="DNS_DOMAIN=cluster" --env="POD_NAMESPACE=default" 273 | 274 | # Start a hazelcast pod and set labels "app=hazelcast" and "env=prod" in the container 275 | kubectl run hazelcast --image=hazelcast/hazelcast --labels="app=hazelcast,env=prod" 👈👈👈 This example matches most closely to the question: Label the pod `tier=web` 276 | 277 | # Dry run; print the corresponding API objects without creating them 278 | kubectl run nginx --image=nginx --dry-run=client 279 | 280 | # Start a nginx pod, but overload the spec with a partial set of values parsed from JSON 281 | kubectl run nginx --image=nginx --overrides='{ "apiVersion": "v1", "spec": { ... } }' 282 | 283 | # Start a busybox pod and keep it in the foreground, don't restart it if it exits 284 | kubectl run -i -t busybox --image=busybox --restart=Never 285 | 286 | # Start the nginx pod using the default command, but use custom arguments (arg1 .. argN) for that command 287 | kubectl run nginx --image=nginx -- ... 288 | 289 | # Start the nginx pod using a different command and custom arguments 290 | kubectl run nginx --image=nginx --command -- ... 291 | ``` 292 | 293 | ##### Solution 294 | 295 | ```bash 296 | clear 297 | kubectl run service-pod --image=nginx --port=80 --labels="tier=web" 298 | kubectl get all 299 | ``` 300 | 301 |

302 |
303 | 304 |
Solution - Expose Service 305 |

306 | 307 | ##### Help Examples 308 | 309 | ```bash 310 | clear 311 | kubectl expose -h | more 312 | ``` 313 | 314 | Output: 315 | 316 | ```console 317 | Examples: 318 | # Create a service for a replicated nginx, which serves on port 80 and connects to the containers on port 8000 319 | kubectl expose rc nginx --port=80 --target-port=8000 320 | 321 | # Create a service for a replication controller identified by type and name specified in "nginx-controller.yaml", 322 | which serves on port 80 and connects to the containers on port 8000 323 | kubectl expose -f nginx-controller.yaml --port=80 --target-port=8000 324 | 325 | # Create a service for a pod valid-pod, which serves on port 444 with the name "frontend" 326 | kubectl expose pod valid-pod --port=444 --name=frontend 👈👈👈 This example matches most closely to the question: pod called `my-service` 327 | 328 | # Create a second service based on the above service, exposing the container port 8443 as port 443 with the name 329 | "nginx-https" 330 | kubectl expose service nginx --port=443 --target-port=8443 --name=nginx-https 331 | 332 | # Create a service for a replicated streaming application on port 4100 balancing UDP traffic and named 'video-stream'. 333 | kubectl expose rc streamer --port=4100 --protocol=UDP --name=video-stream 334 | 335 | # Create a service for a replicated nginx using replica set, which serves on port 80 and connects to the containers on 336 | port 8000 337 | kubectl expose rs nginx --port=80 --target-port=8000 👈👈👈 This example matches most closely to the question: service expose port 8080 338 | 339 | # Create a service for an nginx deployment, which serves on port 80 and connects to the containers on port 8000 340 | kubectl expose deployment nginx --port=80 --target-port=8000 341 | ``` 342 | 343 | ##### Solution 344 | 345 | ```bash 346 | clear 347 | kubectl expose pod service-pod --port=8080 --target-port=80 --name=my-service 348 | clear 349 | kubectl get pod -o wide 350 | kubectl get service 351 | kubectl get ep 352 | ``` 353 | 354 |

355 |
356 |
357 | 358 | #### 04-03. Ingress Question 359 | * Create an ingress called `my-ingress` to expose the service `my-service` from previous question, outside the cluster. 360 | 361 |
Overview 362 |

363 | 364 | ![05-network-ing](https://user-images.githubusercontent.com/18049790/140637548-d1a9ced9-7c66-406c-86d3-1a7001de2e75.jpg) 365 | 366 |

367 |
368 | 369 |
Prerequisites 370 |

371 | 372 | ##### Prerequisites 373 | 374 | Install the Ingress Controller if you have not already installed it: 375 | 376 | ```bash 377 | kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml 378 | ``` 379 | 380 |

381 |
382 | 383 |
Solution 384 |

385 | 386 | ##### Solution 387 | 388 | kubernetes.io bookmark: [The Ingress resource](https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource) 389 | 390 | ```bash 391 | mkdir -p ~/ckad/ 392 | vi ~/ckad/04-03-ing.yml 393 | ``` 394 | 395 | ```yaml 396 | apiVersion: networking.k8s.io/v1 397 | kind: Ingress 398 | metadata: 399 | name: my-ingress #👈👈👈 Change: `my-ingress` 400 | annotations: 401 | nginx.ingress.kubernetes.io/rewrite-target: / 402 | spec: 403 | ingressClassName: nginx 404 | rules: 405 | - http: 406 | paths: 407 | - path: / #👈👈👈 Change 408 | pathType: Prefix 409 | backend: 410 | service: 411 | name: my-service #👈👈👈 Change: `my-service` 412 | port: 413 | number: 8080 #👈👈👈 Change: --port=8080 414 | ``` 415 | 416 | ```bash 417 | clear 418 | kubectl apply -f ~/ckad/04-03-ing.yml 419 | 420 | # Describe the ingress 421 | # This must be present to continue: Address: localhost 422 | # If not shutdown Docker Desktop and reboot Windows 10 423 | kubectl describe ingress my-ingress 424 | ``` 425 | 426 | Output: 427 | 428 | ```console 429 | Name: my-ingress 430 | Namespace: service-namespace 431 | Address: localhost #👈👈👈 This must be present for the solution to work 432 | Default backend: default-http-backend:80 () 433 | Rules: 434 | Host Path Backends 435 | ---- ---- -------- 436 | * 437 | / my-service:8080 (10.1.1.37:80) 438 | Annotations: nginx.ingress.kubernetes.io/rewrite-target: / 439 | Events: 440 | ``` 441 | 442 | ```bash 443 | clear 444 | # Verify that the NGINX page is rendering via the Ingress endpoint 445 | # If you have trouble with this reboot 446 | curl localhost 447 | ``` 448 | 449 | Output: 450 | 451 | ```console 452 | 453 | 454 | 455 | Welcome to nginx! 456 | 461 | 462 | 463 |

Welcome to nginx!

464 |

If you see this page, the nginx web server is successfully installed and 465 | working. Further configuration is required.

466 | 467 |

For online documentation and support please refer to 468 | nginx.org.
469 | Commercial support is available at 470 | nginx.com.

471 | 472 |

Thank you for using nginx.

473 | 474 | 475 | ``` 476 | 477 |

478 |
479 |
480 | 481 | #### Clean Up 482 | 483 |
Clean Up 484 |

485 | 486 | ```bash 487 | yes | rm -R ~/ckad/ 488 | kubectl delete ns service-namespace --force 489 | kubectl delete ns netpol-namespace --force 490 | ``` 491 | 492 |

493 |
494 | 495 | _End of Section_ 496 | -------------------------------------------------------------------------------- /05-ckad-observability-maintenance.md: -------------------------------------------------------------------------------- 1 | ## Sample CKAD Observability and Maintenance Q&A 2 | 3 | ### Application Observability and Maintenance – 15% 4 | 5 | - Understand API deprecations 6 | - Implement probes and health checks 7 | - Use provided tools to monitor Kubernetes applications [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/05-ckad-observability-maintenance.md#05-01-first-list-all-the-pods-in-the-cluster-by-cpu-consumption-then-list-all-the-pods-in-the-cluster-by-memory-consumption) 8 | - Utilize container logs [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/05-ckad-observability-maintenance.md#05-02-create-a-pod-called-log-pod-using-image-nginx-in-namespace-log-namespace-create-the-namespace-obtain-the-logs-for-the-nginx-pod-for-the-last-hour) 9 | - Debugging in Kubernetes [\*\*](https://github.com/jamesbuckett/ckad-questions/blob/main/05-ckad-observability-maintenance.md#05-04-create-a-pod-called-json-pod-using-image-nginx-in-namespace-json-namespace-create-the-namespace-obtain-the-hostip-address-using-jsonpath) 10 |
11 | 12 | 13 | #### 05-01. CPU and Memory of Pods Question 14 | * First list **all** the pods in the cluster **sort by** CPU consumption. 15 | * Then list **all** the pods in the cluster **sort by** Memory consumption. 16 | 17 |
Prerequisite: Metrics Server - Kubernetes top command 18 |

19 | 20 | Metrics Server installs into Kubernetes 21 | 22 | By default the metrics server required for the `kubectl top` command is not present on Docker Desktop. 23 | 24 | Please install the [metrics server](https://github.com/kubernetes-sigs/metrics-server) with the following command: 25 | 26 | ```bash 27 | kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml 28 | ``` 29 | 30 | ```bash 31 | kubectl patch deployment metrics-server -n kube-system --type 'json' -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]' 32 | ``` 33 | 34 |

35 |
36 | 37 |
Optional - Install a Sample Microservices Application 38 |

39 | 40 | This application is useful to see CPU and Memory for a [microservices application](https://github.com/GoogleCloudPlatform/microservices-demo). 41 | 42 | ```bash 43 | kubectl create ns ns-demo 44 | kubectl apply -n ns-demo -f "https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/master/release/kubernetes-manifests.yaml" 45 | kubectl wait -n ns-demo deploy frontend --for condition=Available --timeout=90s 46 | ``` 47 |

48 |
49 | 50 |
Solution 51 |

52 | 53 | ```bash 54 | clear 55 | # Requires metrics server to be installed and working 56 | # Similar to Linux top command but for pods 57 | kubectl top pods -A --sort-by=cpu | more 58 | ``` 59 | 60 | Output: 61 | 62 | ```console 63 | NAMESPACE NAME CPU(cores) MEMORY(bytes) 64 | default falco-pxf8g 51m 👈👈👈 55Mi 65 | ns-loki loki-release-prometheus-server-6d4f4df478-9z2f8 38m 356Mi 66 | ns-demo adservice-68444cb46c-jvc86 23m 202Mi 67 | ns-loki loki-release-promtail-prvvn 13m 34Mi 68 | ns-demo recommendationservice-b4cf8f489-xwv49 13m 69Mi 69 | ... 70 | ``` 71 | 72 |

73 |
74 | 75 |
Solution 76 |

77 | 78 | ##### Solution 79 | 80 | ```bash 81 | clear 82 | # Requires metrics server to be installed and working 83 | # Similar to Linux top command but for pods 84 | kubectl top pods -A --sort-by=memory | more 85 | ``` 86 | 87 | Output: 88 | 89 | ```console 90 | NAMESPACE NAME CPU(cores) MEMORY(bytes) 91 | ns-loki loki-release-prometheus-server-6d4f4df478-9z2f8 11m 356Mi 👈👈👈 92 | ns-demo adservice-68444cb46c-jvc86 20m 202Mi 93 | kube-system cilium-gcnbl 6m 165Mi 94 | kube-system cilium-htrth 18m 163Mi 95 | kube-system cilium-8h6vd 5m 162Mi 96 | kube-system cilium-ml27n 11m 161Mi 97 | ... 98 | ``` 99 | 100 |

101 |
102 |
103 | 104 | #### 05-02. Pod Logging Question 105 | * Create a namespace called `log-namespace`. 106 | * Create a pod called `log-pod` using image `nginx` in namespace `log-namespace`. 107 | * Obtain the `logs` for the nginx pod for the `last hour`. 108 | 109 |
Prerequisites 110 |

111 | 112 | ```bash 113 | mkdir -p ~/ckad/ 114 | clear 115 | kubectl create namespace log-namespace 116 | kubectl run log-pod --image=nginx -n log-namespace 117 | kubectl config set-context --current --namespace=log-namespace 118 | kubectl get all 119 | ``` 120 | 121 |

122 |
123 | 124 |
Help 125 |

126 | 127 | ```bash 128 | clear 129 | kubectl logs -h | more 130 | ``` 131 | 132 | Output: 133 | 134 | ```console 135 | Examples: 136 | # Return snapshot logs from pod nginx with only one container 137 | kubectl logs nginx 138 | 139 | # Return snapshot logs from pod nginx with multi containers 140 | kubectl logs nginx --all-containers=true 141 | 142 | # Return snapshot logs from all containers in pods defined by label app=nginx 143 | kubectl logs -l app=nginx --all-containers=true 144 | 145 | # Return snapshot of previous terminated ruby container logs from pod web-1 146 | kubectl logs -p -c ruby web-1 147 | 148 | # Begin streaming the logs of the ruby container in pod web-1 149 | kubectl logs -f -c ruby web-1 150 | 151 | # Begin streaming the logs from all containers in pods defined by label app=nginx 152 | kubectl logs -f -l app=nginx --all-containers=true 153 | 154 | # Display only the most recent 20 lines of output in pod nginx 155 | kubectl logs --tail=20 nginx 156 | 157 | # Show all logs from pod nginx written in the last hour 158 | kubectl logs --since=1h nginx 👈👈👈 This example matches most closely to the question: for the `last hour` 159 | 160 | # Show logs from a kubelet with an expired serving certificate 161 | kubectl logs --insecure-skip-tls-verify-backend nginx 162 | 163 | # Return snapshot logs from first container of a job named hello 164 | kubectl logs job/hello 165 | 166 | # Return snapshot logs from container nginx-1 of a deployment named nginx 167 | kubectl logs deployment/nginx -c nginx-1 168 | ``` 169 | 170 |

171 |
172 | 173 |
Solution 174 |

175 | 176 | ```bash 177 | clear 178 | # Straight forward match in the examples 179 | kubectl logs --since=1h log-pod 180 | ``` 181 | 182 |

183 |
184 |
185 | 186 | #### 05-03. Kubernetes Events Question 187 | * Output all the events for **all** namespaces by creation date. 188 | 189 |
Solution 190 |

191 | 192 | kubernetes.io bookmark: [Viewing, finding resources](https://kubernetes.io/docs/reference/kubectl/cheatsheet/#viewing-finding-resources) 193 | 194 | ```bash 195 | clear 196 | kubectl get events -A --sort-by=.metadata.creationTimestamp 197 | ``` 198 | 199 |

200 |
201 |
202 | 203 | #### 05-04. JSONPath Question 204 | 205 | * Create a namespace called `json-namespace`. 206 | * Create a pod called `json-pod` using image `nginx` in namespace `json-namespace`. 207 | * Obtain the `hostIP` address using `JSONPath`. 208 | 209 |
Concepts 210 |

211 | 212 | * JSONPath is a query language for JSON. 213 | * It allows you to select and extract data from a JSON document. 214 | 215 |

216 |
217 | 218 |
Overview 219 |

220 | 221 | ![08-json-path](https://user-images.githubusercontent.com/18049790/141224991-a6e64661-685d-4b4f-8868-97dab51ad356.jpg) 222 | 223 |

224 |
225 | 226 |
Prerequisites 227 |

228 | 229 | ```bash 230 | clear 231 | kubectl create namespace json-namespace 232 | kubectl config set-context --current --namespace=json-namespace 233 | kubectl run json-pod --image=nginx 234 | kubectl get all 235 | ``` 236 | 237 |

238 |
239 | 240 |
Solution 241 |

242 | 243 | ```bash 244 | clear 245 | # kubectl explain pod.spec --recursive 246 | # kubectl explain pod.status --recursive 247 | kubectl explain pod.status | more 248 | ``` 249 | 250 | Output: 251 | 252 | ```console 253 | KIND: Pod 254 | VERSION: v1 255 | 256 | RESOURCE: status 👈👈👈 First element: =.status 257 | 258 | DESCRIPTION: 259 | Most recently observed status of the pod. This data may not be up to date. 260 | Populated by the system. Read-only. More info: 261 | https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status 262 | 263 | PodStatus represents information about the status of a pod. Status may 264 | trail the actual state of a system, especially if the node that hosts the 265 | pod cannot contact the control plane. 266 | 267 | FIELDS: 268 | conditions <[]Object> 269 | Current service state of pod. More info: 270 | https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-conditions 271 | 272 | containerStatuses <[]Object> 273 | The list has one entry per container in the manifest. Each entry is 274 | currently the output of `docker inspect`. More info: 275 | https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#pod-and-container-status 276 | 277 | ephemeralContainerStatuses <[]Object> 278 | Status for any ephemeral containers that have run in this pod. This field 279 | is alpha-level and is only populated by servers that enable the 280 | EphemeralContainers feature. 281 | 282 | hostIP 👈👈👈 Second element: =.status.hostIP 283 | IP address of the host to which the pod is assigned. Empty if not yet 284 | scheduled. 285 | 286 | ``` 287 | 288 |

289 | 290 | 291 |
Solution 292 |

293 | 294 | Construct the search query to `hostIP`. 295 | 296 | Search term: `JSONPath Support` on [kubernetes.io](https://kubernetes.io/) 297 | 298 | kubernetes.io bookmark: [JSONPath Support](https://kubernetes.io/docs/reference/kubectl/jsonpath/) 299 | 300 | ```bash 301 | kubectl get pod json-pod -o jsonpath={.status.hostIP} 302 | ``` 303 | OR 304 | ```bash 305 | kubectl get pod json-pod -o jsonpath={..hostIP} 306 | ``` 307 | 308 |

309 |
310 |
311 | 312 | #### 05-05. Deployment Revision History Question 313 | * Run the preparation steps. 314 | * A deployment called `my-revision-deployment` will be created in the namespace `revision-namespace`. 315 | * Check the status of this deployment. 316 | * Check the revision **history** of this deployment. 317 | * **Undo** to the last good working deployment. 318 | * Roll back to the earliest revision. 319 | * Verify that it is now working. 320 | 321 |
Prerequisites 322 |

323 | 324 | ```bash 325 | clear 326 | kubectl create namespace revision-namespace 327 | kubectl config set-context --current --namespace=revision-namespace 328 | kubectl create deployment my-revision-deployment --image=nginx:1.18.0 --replicas=2 329 | kubectl rollout status deployment my-revision-deployment 330 | kubectl set image deployment.apps/my-revision-deployment nginx=nginx:1.19.0 --record 331 | kubectl rollout status deployment my-revision-deployment 332 | kubectl set image deployment.apps/my-revision-deployment nginx=nginx:1.20.0 --record 333 | kubectl rollout status deployment my-revision-deployment 334 | kubectl set image deployment.apps/my-revision-deployment nginx=ngin:1.21.0 --record 335 | clear 336 | ``` 337 | 338 |

339 |
340 | 341 |
Solution 342 |

343 | 344 | ```bash 345 | clear 346 | #Situational Awareness 347 | kubectl get all 348 | ``` 349 | 350 | ```bash 351 | # Examine events from Deployment 352 | kubectl describe deployment.apps/my-revision-deployment 353 | ``` 354 | 355 | ```bash 356 | # Get Deployment Revisions 357 | kubectl rollout history deployment.apps/my-revision-deployment 358 | ``` 359 | 360 | ```bash 361 | # Fix the immediate problem 362 | kubectl rollout undo deployment.apps/my-revision-deployment 363 | ``` 364 | 365 | ```bash 366 | # Go back further to an earlier revision 367 | kubectl rollout undo deployment.apps/my-revision-deployment --to-revision=2 368 | ``` 369 | 370 |

371 |
372 |
373 | 374 | #### 05-06. Convert manifests between different API versions. 375 | * Updated the sample YAML file from `networking.k8s.io/v1beta1` to `networking.k8s.io/v1` 376 | 377 |
kubectl convert - Deal with deprecated API versions 378 |

379 | 380 | Search for `kubectl convert install` and scroll until you find the `Install kubectl convert plugin` section for installation instructions. 381 | 382 | The `kubectl convert` plugin installs into WSL Linux 383 | 384 | Please install the [`kubectl convert` plugin](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-convert-plugin) with the following commands: 385 | 386 | ```bash 387 | curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert 388 | curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256" 389 | echo "$( 395 |

396 | 397 |
Prerequisites 398 |

399 | 400 | Typical API deprecated warning message: 401 | ```console 402 | Warning: policy/v1beta1 PodDisruptionBudget is deprecated in v1.21+, unavailable in v1.25+; use policy/v1 PodDisruptionBudget 403 | poddisruptionbudget.policy/calico-kube-controllers created 404 | ``` 405 | 406 | ```bash 407 | vi ~/ckad/06-04-beta-ingress.yml 408 | ``` 409 | 410 | ```yaml 411 | apiVersion: networking.k8s.io/v1beta1 412 | kind: Ingress 413 | metadata: 414 | name: my-ingress #👈👈👈 Change: `my-ingress` 415 | annotations: 416 | nginx.ingress.kubernetes.io/rewrite-target: / 417 | spec: 418 | rules: 419 | - http: 420 | paths: 421 | - path: / #👈👈👈 Change 422 | pathType: Prefix 423 | backend: 424 | service: 425 | name: my-service #👈👈👈 Change: `my-service` 426 | port: 427 | number: 8080 #👈👈👈 Change: --port=8080 428 | ``` 429 | 430 |

431 |
432 | 433 |
Solution 434 |

435 | 436 | Search for `kubectl convert` 437 | 438 | kubernetes.io bookmark: [Migrate to non-deprecated APIs](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#migrate-to-non-deprecated-apis) 439 | 440 | ```bash 441 | kubectl-convert -f ~/ckad/06-04-beta-ingress.yml --output-version networking.k8s.io/v1 442 | ``` 443 | 444 | Output: 445 | 446 | ```console 447 | apiVersion: networking.k8s.io/v1 448 | kind: Ingress 449 | metadata: 450 | annotations: 451 | nginx.ingress.kubernetes.io/rewrite-target: / 452 | creationTimestamp: null 453 | name: my-ingress 454 | spec: 455 | rules: 456 | - http: 457 | paths: 458 | - backend: {} 459 | path: / 460 | pathType: Prefix 461 | status: 462 | loadBalancer: {} 463 | ``` 464 | 465 |

466 |
467 |
468 | 469 | 470 | #### 05-07. Set Environment Variable Question 471 | * Run the code in the preparation section. 472 | * Once the deployment is running alter the environmental variable `TIER=web` to `TIER=app` 473 | 474 | 475 |
Prerequisites 476 |

477 | 478 | ```bash 479 | cat << EOF | kubectl apply -f - 480 | apiVersion: v1 481 | kind: Namespace 482 | metadata: 483 | creationTimestamp: null 484 | name: set-env-namespace 485 | --- 486 | apiVersion: apps/v1 487 | kind: Deployment 488 | metadata: 489 | name: nginx-deployment 490 | namespace: set-env-namespace 491 | labels: 492 | app: nginx 493 | spec: 494 | replicas: 3 495 | selector: 496 | matchLabels: 497 | app: nginx 498 | template: 499 | metadata: 500 | labels: 501 | app: nginx 502 | spec: 503 | containers: 504 | - name: nginx 505 | image: nginx:1.14.2 506 | ports: 507 | - containerPort: 80 508 | env: 509 | - name: TIER 510 | value: web 511 | EOF 512 | ``` 513 | 514 | ```bash 515 | kubectl config set-context --current --namespace=set-env-namespace 516 | kubectl get all 517 | ``` 518 | 519 |

520 |
521 | 522 |
Solution 523 |

524 | 525 | ```bash 526 | # Describe the Deployment 527 | kubectl describe deployment.apps nginx-deployment | grep -i env -A 1 528 | ``` 529 | 530 | ```bash 531 | # Set the env using kubectl set env 532 | kubectl set env deployment.apps nginx-deployment TIER=app 533 | ``` 534 | 535 | ```bash 536 | # Describe the Deployment 537 | kubectl describe deployment.apps nginx-deployment | grep -i env -A 1 538 | ``` 539 | 540 |

541 |
542 |
543 | 544 | #### Clean Up 545 | 546 |
Clean Up 547 |

548 | 549 | ```bash 550 | yes | rm -R ~/ckad/ 551 | kubectl delete ns json-namespace --force 552 | kubectl delete ns log-namespace --force 553 | kubectl delete ns revision-namespace --force 554 | ``` 555 | 556 |

557 |
558 | 559 | _End of Section_ 560 | -------------------------------------------------------------------------------- /06-ckad-miscellaneous.md: -------------------------------------------------------------------------------- 1 | ## Sample CKAD API Q&A 2 | 3 | #### 06-01. What is the current active namespace? 4 | 5 |
Solution - kubectl config get-contexts 6 |

7 | 8 | ```bash 9 | # List all namespaces, but which is currently active? 10 | clear 11 | kubectl get namespace 12 | ``` 13 | 14 | ```console 15 | NAME STATUS AGE 16 | cert-manager Active 15d 17 | default Active 15d 18 | knative-serving Active 15d 19 | kourier-system Active 15d 20 | kube-node-lease Active 15d 21 | kube-public Active 15d 22 | kube-system Active 15d 23 | ns-chaos Active 15d 24 | ns-cookies Active 13d 25 | ns-demo Active 15d 26 | ns-fluentbit Active 7d20h 27 | ns-goldilocks Active 15d 28 | ns-loki Active 15d 29 | ns-vpa Active 15d 30 | projectcontour Active 15d 31 | ``` 32 | 33 | kubernetes.io bookmark: [Kubectl context and configuration](https://kubernetes.io/docs/reference/kubectl/cheatsheet/#kubectl-context-and-configuration) 34 | 35 | ```bash 36 | # Get the current active namespace 37 | kubectl config get-contexts 38 | ``` 39 | 40 | ```bash 41 | CURRENT NAME CLUSTER AUTHINFO NAMESPACE 42 | * do-sgp1-digital-ocean-cluster do-sgp1-digital-ocean-cluster do-sgp1-digital-ocean-cluster-admin ns-cookies 👈👈👈 # ns-cookies is the active namespace 43 | ``` 44 | 45 |

46 |
47 |
48 | 49 | #### 06-02. List all the Kubernetes resources that can be found inside a namespace. By name only. 50 | 51 |
Solution - kubectl api-resources --namespaced=true 52 |

53 | 54 | kubernetes.io bookmark: [Not All Objects are in a Namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/#not-all-objects-are-in-a-namespace) 55 | 56 | ```bash 57 | clear 58 | kubectl api-resources --namespaced=true | more 59 | ``` 60 | 61 | Output: 62 | 63 | ```console 64 | NAME SHORTNAMES APIVERSION NAMESPACED KIND 65 | bindings v1 true Binding 66 | configmaps cm v1 true ConfigMap 67 | endpoints ep v1 true Endpoints 68 | ... 69 | 70 | # Do not need the additional supplied columns. 71 | 72 | ``` 73 | 74 |

75 |
76 | 77 |
kubectl api-resources --namespaced=true -o name 78 |

79 | 80 | ##### Solution 81 | 82 | ```bash 83 | clear 84 | kubectl api-resources --namespaced=true -o name | more 85 | ``` 86 | 87 | Output: 88 | 89 | ```console 90 | bindings 91 | configmaps 92 | endpoints 93 | events 94 | ... 95 | ``` 96 | 97 |

98 |
99 |
100 | 101 | #### 06-03. Give the command to list out all the available API groups on your cluster. Then list out the API's in the `named` group. 102 | 103 |
Prerequisites 104 |

105 | 106 | ```bash 107 | clear 108 | # Use the kubectl proxy to provide credentials to connect to the API server 109 | # kubectl proxy starts a local proxy service on port 8001 110 | # kubectl proxy uses credentials from kubeconfig file 111 | 112 | kubectl proxy & 113 | ``` 114 | 115 |

116 |
117 | 118 |
Solution - All API groups 119 |

120 | 121 | ```bash 122 | clear 123 | # List all available API groups from the API server 124 | 125 | # /api is called the core API's #👈👈👈 126 | # /apis is called the named API's - going forward new features will be made available under this API #👈👈👈 127 | 128 | curl http://localhost:8001 | more 129 | ``` 130 | 131 | Output: 132 | 133 | ```console 134 | { 135 | "paths": [ 136 | "/.well-known/openid-configuration", 137 | "/api", 138 | "/api/v1", 139 | "/apis", 140 | "/apis/", 141 | "/apis/admissionregistration.k8s.io", 142 | "/apis/admissionregistration.k8s.io/v1", 143 | "/apis/admissionregistration.k8s.io/v1beta1", 144 | "/apis/apiextensions.k8s.io", 145 | "/apis/apiextensions.k8s.io/v1", 146 | "/apis/apiextensions.k8s.io/v1beta1", 147 | "/apis/apiregistration.k8s.io", 148 | "/apis/apiregistration.k8s.io/v1", 149 | "/apis/apiregistration.k8s.io/v1beta1", 150 | "/apis/apps", 151 | "/apis/apps/v1", 152 | "/apis/authentication.k8s.io", 153 | "/apis/authentication.k8s.io/v1", 154 | "/apis/authentication.k8s.io/v1beta1", 155 | "/apis/authorization.k8s.io", 156 | "/apis/authorization.k8s.io/v1", 157 | "/apis/authorization.k8s.io/v1beta1", 158 | "/apis/autoscaling", 159 | "/apis/autoscaling/v1", 160 | "/apis/autoscaling/v2beta1", 161 | "/apis/autoscaling/v2beta2", 162 | "/apis/batch", 163 | "/apis/batch/v1", 164 | "/apis/batch/v1beta1", 165 | "/apis/certificates.k8s.io", 166 | "/apis/certificates.k8s.io/v1", 167 | "/apis/certificates.k8s.io/v1beta1", 168 | "/apis/coordination.k8s.io", 169 | "/apis/coordination.k8s.io/v1", 170 | "/apis/coordination.k8s.io/v1beta1", 171 | "/apis/discovery.k8s.io", 172 | "/apis/discovery.k8s.io/v1", 173 | "/apis/discovery.k8s.io/v1beta1", 174 | "/apis/events.k8s.io", 175 | "/apis/events.k8s.io/v1", 176 | "/apis/events.k8s.io/v1beta1", 177 | "/apis/extensions", 178 | ... 179 | ``` 180 | 181 |

182 |
183 | 184 |
Solution - named API's 185 |

186 | 187 | ```bash 188 | clear 189 | # List all supported resource groups under the `named` (apis) group 190 | 191 | curl http://localhost:8001/apis | grep "name" | more 192 | ``` 193 | 194 | Output: 195 | 196 | ```console 197 | ... 198 | "name": "apiregistration.k8s.io", 199 | "name": "apps", 200 | "name": "events.k8s.io", 201 | "name": "authentication.k8s.io", 202 | "name": "authorization.k8s.io", 203 | "name": "autoscaling", 204 | "name": "batch", 205 | "name": "certificates.k8s.io", 206 | "name": "networking.k8s.io", 207 | "name": "extensions", 208 | "name": "policy", 209 | "name": "rbac.authorization.k8s.io", 210 | "name": "storage.k8s.io", 211 | "name": "admissionregistration.k8s.io", 212 | "name": "apiextensions.k8s.io", 213 | "name": "scheduling.k8s.io", 214 | "name": "coordination.k8s.io", 215 | "name": "node.k8s.io", 216 | "name": "discovery.k8s.io", 217 | "name": "flowcontrol.apiserver.k8s.io", 218 | "name": "crd.projectcalico.org", 219 | "name": "projectcontour.io", 220 | "name": "metrics.k8s.io", 221 | ... 222 | ``` 223 | 224 |

225 |
226 |
227 | 228 | #### 06-04. Strategic Merge Patch Question 229 | * Patch the deployment from the previous question: `my-revision-deployment` to have a `revisionHistoryLimit` size of `20` using a **Strategic Merge Patch** 230 | 231 |
Overview 232 |

233 | 234 | ##### Overview 235 | 236 | kubernetes.io bookmark: [Use a strategic merge patch to update a Deployment](https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/#use-a-strategic-merge-patch-to-update-a-deployment) 237 | 238 | 239 | ```bash 240 | kubectl config set-context --current --namespace=revision-namespace 241 | clear 242 | kubectl explain deployment.spec 243 | ``` 244 | 245 | Output: 246 | 247 | ```console 248 | KIND: Deployment 249 | VERSION: apps/v1 250 | 251 | RESOURCE: spec 👈👈👈 First element: =.spec 252 | 253 | DESCRIPTION: 254 | Specification of the desired behavior of the Deployment. 255 | 256 | DeploymentSpec is the specification of the desired behavior of the 257 | Deployment. 258 | 259 | FIELDS: 260 | minReadySeconds 261 | Minimum number of seconds for which a newly created pod should be ready 262 | without any of its container crashing, for it to be considered available. 263 | Defaults to 0 (pod will be considered available as soon as it is ready) 264 | 265 | paused 266 | Indicates that the deployment is paused. 267 | 268 | progressDeadlineSeconds 269 | The maximum time in seconds for a deployment to make progress before it is 270 | considered to be failed. The deployment controller will continue to process 271 | failed deployments and a condition with a ProgressDeadlineExceeded reason 272 | will be surfaced in the deployment status. Note that progress will not be 273 | estimated during the time a deployment is paused. Defaults to 600s. 274 | 275 | replicas 276 | Number of desired pods. This is a pointer to distinguish between explicit 277 | zero and not specified. Defaults to 1. 278 | 279 | revisionHistoryLimit 👈👈👈 Second element: =.spec.revisionHistoryLimit 280 | The number of old ReplicaSets to retain to allow rollback. This is a 281 | pointer to distinguish between explicit zero and not specified. Defaults to 282 | 10. 283 | 284 | selector -required- 285 | Label selector for pods. Existing ReplicaSets whose pods are selected by 286 | this will be the ones affected by this deployment. It must match the pod 287 | template's labels. 288 | 289 | strategy 290 | The deployment strategy to use to replace existing pods with new ones. 291 | 292 | template -required- 293 | Template describes the pods that will be created. 294 | ``` 295 | 296 |

297 | 298 | 299 |
Solution 300 |

301 | 302 | ```bash 303 | # What is the current setting 304 | kubectl get deployment my-revision-deployment -o jsonpath={.spec.revisionHistoryLimit} 305 | ``` 306 | 307 | ```bash 308 | # Create a file to hold the patch 309 | vi ~/ckad/patch-file.yaml 310 | ``` 311 | 312 | ```bash 313 | spec: # 👈👈👈 First element: =.spec 314 | revisionHistoryLimit: 20 # 👈👈👈 Second element: =.spec.revisionHistoryLimit 315 | ``` 316 | 317 | ```bash 318 | kubectl patch deployment my-revision-deployment --patch "$(cat ~/ckad/patch-file.yaml)" 319 | ``` 320 | 321 | ```bash 322 | # Verify your work 323 | kubectl get deployment my-revision-deployment -o jsonpath={.spec.revisionHistoryLimit} 324 | ``` 325 | 326 | 327 |

328 |
329 |
330 | 331 | _End of Section_ 332 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample CKAD exercises and solutions 2 | 3 | - A set of sample questions and solutions to assist in preparing for the [CKAD](https://www.cncf.io/certification/ckad/) exam 4 | - Use the [CKAD tips and tricks](https://github.com/jamesbuckett/ckad-tips) repository for various tips and tricks in the CKAD exam 5 | - Assumed practice environment is [Docker Desktop](https://www.docker.com/products/docker-desktop) with a [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install) backend 6 | - The current version of Kubernetes in the CKAD exam can be found [here](https://docs.linuxfoundation.org/tc-docs/certification/faq-cka-ckad-cks#what-application-version-is-running-in-the-exam-environment) 7 | 8 | ![kubernetes-ckad-color-300x294](https://user-images.githubusercontent.com/18049790/135700768-0f5735b3-4681-4abd-9075-ece42f4ef134.png) 9 |
10 | 11 | ## Disclaimer 12 | 13 | - Opinions expressed here are solely my own and do not express the views or opinions of JPMorgan Chase. 14 | - Any third-party trademarks are the intellectual property of their respective owners and any mention herein is for referential purposes only. 15 |
16 | 17 | ## Questions by Domain 18 | 19 | - [Example CKAD Application Design and Build Q&A](https://github.com/jamesbuckett/ckad-questions/blob/main/01-ckad-design-build.md) 20 | - [Example CKAD Application Environment, Configuration and Security Q&A](https://github.com/jamesbuckett/ckad-questions/blob/main/02-ckad-env-configuration-security.md) 21 | - [Example CKAD Application Deployment Q&A](https://github.com/jamesbuckett/ckad-questions/blob/main/03-ckad-deployment.md) 22 | - [Example CKAD Services and Networking Q&A](https://github.com/jamesbuckett/ckad-questions/blob/main/04-ckad-services-networking.md) 23 | - [Example CKAD Observability and Maintenance Q&A](https://github.com/jamesbuckett/ckad-questions/blob/main/05-ckad-observability-maintenance.md) 24 | - [Example CKAD Miscellaneous Q&A](https://github.com/jamesbuckett/ckad-questions/blob/main/06-ckad-miscellaneous.md) 25 |
26 | 27 | ## Docker Desktop Setup 28 | 29 |
Linux access to Docker desktop 30 |

31 | 32 | - Windows Subsystem for Linux requires you to install a Linux distribution from the Microsoft Store. 33 | - Tick the 'Use the WSL 2 based engine' under the 'General' panel 34 | - To mimic the CKAD exam please execute all the commands in this repo from this Linux distribution that you installed. 35 | - The CKAD exam terminal is Ubuntu based. 36 | - Start the distribution directly by searching for Ubuntu and starting the application 37 | 38 |

39 |
40 | 41 |
Prerequisite software for Docker desktop 42 |

43 | 44 | Please install these software components, required to answer questions in later sections: 45 | 46 |

Metrics Server - Kubernetes top command 47 |

48 | 49 | Metrics Server installs into Kubernetes 50 | 51 | By default the metrics server required for the `kubectl top` command is not present on Docker Desktop. 52 | 53 | Please install the [metrics server](https://github.com/kubernetes-sigs/metrics-server) with the following command: 54 | 55 | ```bash 56 | kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml 57 | ``` 58 | 59 | ```bash 60 | kubectl patch deployment metrics-server -n kube-system --type 'json' -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]' 61 | ``` 62 | 63 |

64 |
65 | 66 |
Contour Ingress - Kubernetes Ingress Controller 67 |

68 | 69 | Contour installs into Kubernetes 70 | 71 | ![contour](https://user-images.githubusercontent.com/18049790/136644054-a6dc7100-cfd5-499c-9c34-01ec4fffbb01.png) 72 | 73 | By default the Contour Ingress required for the Ingress Networking question is not present on Docker Desktop. 74 | 75 | Please install the [contour ingress](https://projectcontour.io/) with the following command: 76 | 77 | ```bash 78 | kubectl apply -f https://projectcontour.io/quickstart/contour.yaml 79 | ``` 80 | 81 |

82 |
83 | 84 |
Calico - Kubernetes Container Network Interface (CNI) 85 |

86 | 87 | Calico installs into Kubernetes 88 | 89 | ![calico](https://user-images.githubusercontent.com/18049790/136644040-3b4cbfe8-3eb6-4c46-b238-adb51c2bb09c.png) 90 | 91 | Calico is required for the non native Kubernetes resources lookup question. 92 | 93 | ```bash 94 | curl https://docs.projectcalico.org/manifests/calico.yaml | kubectl apply -f - 95 | ``` 96 | 97 | To work with Calico Network Policies use the following command: 98 | ```bash 99 | kubectl get networkpolicy.p 100 | ``` 101 | 102 |

103 |
104 | 105 |
Helm - Kubernetes Package Manager 106 |

107 | 108 | Helm installs into WSL Linux 109 | 110 | Please install the [Helm](https://helm.sh/docs/) with the following command: 111 | 112 | ```bash 113 | curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 114 | chmod 700 get_helm.sh 115 | ./get_helm.sh 116 | ``` 117 | 118 |

119 |
120 | 121 |
kubectl convert - Deal with deprecated API versions 122 |

123 | 124 | kubectl convert installs into WSL Linux 125 | 126 | Please install the [kubectl convert](https://kubernetes.io/docs/reference/using-api/deprecation-guide/#migrate-to-non-deprecated-apis) with the following command: 127 | 128 | ```bash 129 | curl -LO https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert 130 | curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl-convert.sha256" 131 | echo "$( 137 |

138 | 139 |
Octant - Kubernetes UI 140 |

141 | 142 | Octant installs into Windows 11 143 | 144 | ![octant](https://user-images.githubusercontent.com/18049790/136644006-b0009cea-690c-4303-88a0-e06738fd28de.png) 145 | 146 | - Consider installing Octant to visualize Kubernetes Resource Types 147 | - This will help you understand the interaction and relationships between Kubernetes Resource types 148 | - Installation instructions for Octant can be found [here](https://octant.dev/) 149 | 150 |

151 |
152 | 153 |

154 |
155 |
156 | 157 | ## Logistics 158 | 159 | - You can follow along and cut and paste the example code snippets into your own cluster 160 | - A single node cluster is sufficient for all examples 161 | - All code snippets have been created and tested on Docker Desktop 162 | - To clean up just execute commands in the "Clean Up" section at the end of each section 163 | 164 | _End of Section_ --------------------------------------------------------------------------------