{"id":214,"date":"2019-07-21T20:24:14","date_gmt":"2019-07-22T03:24:14","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=214"},"modified":"2019-08-18T20:13:28","modified_gmt":"2019-08-19T03:13:28","slug":"ckad-series-part-3-configuration","status":"publish","type":"post","link":"https:\/\/nillsf.com\/index.php\/2019\/07\/21\/ckad-series-part-3-configuration\/","title":{"rendered":"CKAD series part 3: Configuration"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">This is part 3 in a multi-part series on my CKAD learning experience. For the other parts in the series, please check out the following links:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/07\/09\/ckad-series-part-1-intro-exam-topics-my-study-plan\/\">Part 1: intro, exam topics and my study plan<\/a><\/li><li><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/07\/11\/ckad-series-part-2-core-concepts\/\">Part 2: core concepts<\/a><\/li><li><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/07\/28\/ckad-series-part-4-multi-container-pods\/\">Part 4: multi-container pods<\/a><\/li><li><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/08\/01\/ckad-part-5-observability\/\">Part 5: Observability<\/a> <\/li><li><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/08\/05\/ckad-series-part-6-pod-design\/\">Part 6: Pod Design<\/a><\/li><li><a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/08\/18\/ckad-series-part-7-services-and-networking\/\">Part 7: Networking<\/a> <\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">As I mentioned in the intro to this blog series, my study plan was to cover the topics in the&nbsp;<a href=\"https:\/\/github.com\/cncf\/curriculum\">official curriculum for the CKAD<\/a>&nbsp;one by one \u2013 and share my learnings with you. In this post we&#8217;ll walk through configuration and these 5 topics:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Understand ConfigMaps<\/li><li>Understand SecurityContexts<\/li><li>Define an application resource requirements<\/li><li>Create and consume secrets<\/li><li>Understand ServiceAccount<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s cover each of those five topics:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Configmaps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"http:\/\/ https:\/\/kubernetes.io\/docs\/tasks\/configure-pod-container\/configure-pod-configmap\/\">ConfigMaps<\/a> are a mechanism within Kubernetes are a mechanism to pass configuration data to the containers in your pods. That data could be command-line arguments, environment variables, ports\u2026 By using ConfigMaps, you keep configuration data outside of your pod definitions. Within ConfigMaps, you define key-value pairs of configuration data. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can create\nConfigMaps in a couple of ways:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>From a literal value (in kubectl)<\/li><li>From a configuration file<\/li><li>From a yaml file<\/li><li>Using kustomization.yaml (not covered in this post)<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">From a literal<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll start of by creating our configmap, adding that to our pod, and then look at the value.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create configmap literal --from-literal=MYNAME=Nills<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: config-literal\nspec:\n  containers:\n    - name: basic\n      image: nginx\n      <strong>env:\n        - name: MYNAMEFROMLITERAL\n          valueFrom:\n            configMapKeyRef:\n              name: literal\n              key: MYNAME<\/strong><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can then exec into our container to get the environmental variable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f simplepodconfigliteral.yaml\nkubectl exec -it config-literal \/bin\/bash\necho $MYNAMEFROMLITERAL #FROM WITHIN THE CONTAINER<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">From a configuration file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now do the same, but with a configuration file rather than a literal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo 'MYNAME=John Smith' > name.config\n\nkubectl create configmap fromfile --from-file=name.config<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: config-file\nspec:\n  containers:\n    - name: basic\n      image: nginx\n      env:\n        - name: MYNAMEFROMFILE\n          valueFrom:\n            configMapKeyRef:\n              name: fromfile\n              key: name.config<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f simplepodconfigfile.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then exec into the container to get the environmental variable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl exec -it config-file \/bin\/bash\necho $MYNAMEFROMFILE<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The output here is<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MYNAME=JohnSmith<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Which is actually the content of the file, not the variable itself. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Meaning we couldn&#8217;t directly set the environmental variable, but we get the content of the file. If you want to get the actual values, you should import the configmap with the flag<code> --from-env-file<\/code>. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create configmap fromenvfile --from-env-file=name.config<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: env-file\nspec:\n  containers:\n    - name: basic\n      image: nginx\n      env:\n        - name: MYNAMEFROMFILE\n          valueFrom:\n            configMapKeyRef:\n              name: fromenvfile\n              key: MYNAME<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f simplepodenvfile.yaml\nkubectl exec -it env-file \/bin\/bash\necho MYNAMEFROMFILE<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You then see the actual value, not the whole content of the file.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To see the difference between the <code>--from-file<\/code> flag, and the <code>--from-env-file<\/code> flag, you can <code>describe <\/code>(and compare) both configmaps (if you created both with me) by doing the following describes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl describe configmap\/fromfile configmap\/fromenvfile<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Name:         fromfile\nNamespace:    default\nLabels:       <none>\nAnnotations:  <none>\n\n<strong>Data\n====\nname.config:\n----\nMYNAME=JohnSmith <\/strong>\nEvents:  <none>\n\n\nName:         fromenvfile\nNamespace:    default\nLabels:       <none>\nAnnotations:  <none>\n\n<strong>Data\n====\nMYNAME:\n----\nJohnSmith <\/strong>\nEvents:  <none>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can see the real impact of <code>--from-file<\/code> and <code>--from-env-file<\/code> if you have multiple values to set. To test this out, just add another value to <code>name.config<\/code>, and recreate the configmaps:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo 'SECONDNAME=Test' >> name.config<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl delete configmap\/fromfile configmap\/fromenvfile\nkubectl create configmap fromfile --from-file=name.config\nkubectl create configmap fromenvfile --from-env-file=name.config\nkubectl describe configmap\/fromfile configmap\/fromenvfile<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>Name:         fromfile\nNamespace:    default\nLabels:       <none>\nAnnotations:  <none>\n\n<strong>Data\n====\nname.config:\n----\nMYNAME=JohnSmith\nSECONDNAME=Test<\/strong>\nEvents:  <none>\n\n\nName:         fromenvfile\nNamespace:    default\nLabels:       <none>\nAnnotations:  <none>\n\n<strong>Data\n====\nMYNAME:\n----\nJohnSmith\nSECONDNAME:\n----\nTest<\/strong>\nEvents:  <none>\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Using <code>--from-file <\/code>actually loads the file, and using <code>--from-env-file<\/code> loads the actual values in the file. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">From a YAML file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You can also create configmaps directly as YAML files. An example below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kind: ConfigMap \napiVersion: v1 \nmetadata:\n  name: config-from-yaml\ndata:\n  myname: this-is-yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You can create this via:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f configmap.yaml<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can reference this configmap in the following way:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: config-yaml\nspec:\n  containers:\n    - name: basic\n      image: nginx\n      env:\n        - name: MYNAMEFROMYAML\n          valueFrom:\n            configMapKeyRef:\n              name: config-from-yaml\n              key: myname<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f simplepodyaml.yaml\n\nkubectl exec -it config-yaml \/bin\/bash\necho $MYNAMEFROMYAML<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The output should be<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>this-is-yaml<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Summarizing configmaps<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Configmaps are a way to store non-secret data in the kubernetes apiserver. There are multiple ways to create them. When using a file, you should be careful and distinguish between <code>--from-file <\/code>and <code>--from-env-file<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Understand SecurityContexts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A <a href=\"https:\/\/kubernetes.io\/docs\/tasks\/configure-pod-container\/security-context\/\">securitycontext <\/a>describes the privileges and access control that a pod will use. Some settings here include (from the kubernetes docs):<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Discretionary Access Control: Permission to access an object, like a file, is based on user ID (UID) and group ID (GID).<\/li><li>Security Enhanced Linux (SELinux): Objects are assigned security labels.<\/li><li>Running as privileged or unprivileged.<\/li><li>Linux Capabilities: Give a process some privileges, but not all the privileges of the root user.<\/li><li>AppArmor: Use program profiles to restrict the capabilities of individual programs.<\/li><li>Seccomp: Filter a process\u2019s system calls.<\/li><li>AllowPrivilegeEscalation: Controls whether a process can gain more privileges than its parent process. This bool directly controls whether the no_new_privs flag gets set on the container process. AllowPrivilegeEscalation is true always when the container is: 1) run as Privileged OR 2) has CAP_SYS_ADMIN.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">This is actually a very important topic to dive\ninto. As we&#8217;re prepping for the exam, I don\u2019t want to dive too deep into the\ndetails of all the settings and the topics. Let&#8217;s have a look at some of the\nsettings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll start of with creating a simple pod, without a security context. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: simple-security\nspec:\n  containers:\n    - name: basic\n      image: nginx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s create this with the command <code>kubectl create -f simple-security.yaml<\/code> &#8211; and then exec into our container with <code>kubectl exec -it simple-security sh<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once in our container, let&#8217;s have a look at our current processes and user. You&#8217;ll see everything is running as root, and you&#8217;re logged as <code>root<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/ # ps aux\nPID   USER     TIME  COMMAND\n    1 root      0:00 sleep 3600\n   59 root      0:00 sh\n   64 root      0:00 ps aux\n\/ # id\nuid=0(root) gid=0(root) groups=10(wheel)\n\/ # exit<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now change our pod definition, to include a user and group id:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: user-security\nspec:\n  securityContext:\n    runAsUser: 1000\n    runAsGroup: 3000\n    fsGroup: 2000\n  containers:\n    - name: basic\n      image: busybox\n      command:\n        - sleep\n        - \"3600\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now do the same: create the container, exec into it, and look at out processes and user. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f user-security.yaml\npod\/user-security created\nkubectl exec -it user-security sh\n\/ $ ps aux\nPID   USER     TIME  COMMAND\n    1 1000      0:00 sleep 3600\n    6 1000      0:00 sh\n   11 1000      0:00 ps aux\n\/ $ id\nuid=1000 gid=3000 groups=2000<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s have a look at what we did here, we defined <strong>at the pod level <\/strong>a securitycontext. This means all containers in our pod will run as a certain user. Let&#8217;s have a look at what happens when we combine both, a pod securitycontext and one specifically on our container:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: container-security\nspec:\n  securityContext:\n    runAsUser: 1000\n    runAsGroup: 3000\n    fsGroup: 2000\n  containers:\n    - name: basic\n      image: busybox\n      command:\n        - sleep\n        - \"3600\"\n      securityContext:\n        runAsUser: 666<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f container-security.yaml\npod\/container-security created\nkubectl exec -it container-security sh\n\/ $ ps aux\nPID   USER     TIME  COMMAND\n    1 666       0:00 sleep 3600\n    6 666       0:00 sh\n   11 666       0:00 ps aux\n\/ $ id\nuid=666 gid=3000 groups=2000<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We see an outcome as expected. We use the <code>groupid<\/code> and <code>groups<\/code> of the pod definition, but the more finegrained container policy is applied. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Define an application resource requirements<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/kubernetes.io\/docs\/concepts\/configuration\/manage-compute-resources-container\/ \">Application resource requirements<\/a> &#8211; and constraints &#8211; are critical within a kubernetes application definition. It provides information to the kubernetes system on how many resources should be &#8216;reserved&#8217; for each container in the pods in a deployment &#8211; and it can set maxima as well. Additionally, it also allows you to set quota at namespace level; so a certain namespace cannot exceed a certain amount of resources. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There\nare four definitions for resource requirements:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>CPU requests<\/strong>: Expressed as a fraction of a core that is reserved for the container. This can be expressed as a decimal number (0.1) or as a &#8216;milli&#8217; value (100m). 0.1 and 100m are equivalent. <\/li><li><strong>Memory requests<\/strong>: Expresses as a number of bytes of memory to be reserved for the container. You can express this either as a actual number of bytes, as a E, P, T, G, M, K value or as a Ei, Pi, Ti, Gi, Mi, Ki value. Wondering about the difference between M and Mi? M is 10-based, M i is 2 based, meaning M is 1,000,000 and Mi is 1,048,576.<\/li><li><strong>CPU limits<\/strong>: The fraction of a core that can actually be scheduled. The milli-value here is multiplied by 100, and this value is the amount of ms that can be run per 100ms. (100m, would mean this container can be scheduled on the cpu for 10ms per 100ms)<\/li><li><strong>Memory limits<\/strong>: The maximum amount of memory that a container is allowed to use. If this value is exceeded, your container might be terminated. <\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The\nkubernetes scheduler will work with the requests and limits to schedule pods\nacross your cluster. The sum of the requests for containers in the pods running\non a node will never exceed the total available resources on that node. This\nmeans that on a 2 core machine, a maximum of 4 pods with a request of 500m will\nbe scheduled. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Please take into account that requests are reserved, and are not linked to the actual usage on your cluster. With incorrect requests, you could run a very underutilized cluster. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">CPU settings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s start playing around a little with CPU requirements first:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: cpu-demo\nspec:\n  containers:\n  - name: cpu-demo-ctr\n    image: vish\/stress\n    resources:\n      limits:\n        cpu: \"1\"\n      requests:\n        cpu: \"0.5\"\n    args:\n    - -cpus\n    - \"2\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">What you see in the yaml above, is that we create a stress container. We limit it to 1CPU, with a reservation of 0.5CPU, while we&#8217;re running stress with 2CPUs. Let&#8217;s look at how much CPU is being used:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f cpu-requests.yaml\npod\/cpu-demo created\nkubectl top pods\nNAME       CPU(cores)   MEMORY(bytes)\ncpu-demo   990m         1Mi<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">One thing I want you to notice here: when exceeding the limits, the pod kept running (i.e. wasn&#8217;t killed), and was just limited. This is different with memory usage, let&#8217;s have a look:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Memory settings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s start with creating a pod that will try to get 150MB of memory, while being allowed 200MB. This should work, without any glitches:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: memory-low\nspec:\n  containers:\n  - name: memory-demo-ctr\n    image: polinux\/stress\n    resources:\n      limits:\n        memory: \"200Mi\"\n      requests:\n        memory: \"100Mi\"\n    command: [\"stress\"]\n    args: [\"--vm\", \"1\", \"--vm-bytes\", \"150M\", \"--vm-hang\", \"1\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can create this and then watch the memory utilization:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f memory-low.yaml\nkubectl top pods\nNAME         CPU(cores)   MEMORY(bytes)\nmemory-low   40m          151Mi<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And if we check a <code>kubectl get pods<\/code>, we&#8217;ll see zero restarts (as expected):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>NAME         READY   STATUS    RESTARTS   AGE\nmemory-low   1\/1     Running   0          3m56s<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can now edit the pod definition, and put a memory limit of 125Mi in place. This will create issues, and will cause kubernetes to kill and restart our pod.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: memory-high\nspec:\n  containers:\n  - name: memory-demo-ctr\n    image: polinux\/stress\n    resources:\n      limits:\n        memory: \"125Mi\"\n      requests:\n        memory: \"100Mi\"\n    command: [\"stress\"]\n    args: [\"--vm\", \"1\", \"--vm-bytes\", \"150M\", \"--vm-hang\", \"1\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can create this pod, and we can quickly see that I&#8217;ll get killed and restarted:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f memory-high.yaml\nkubectl get pods --watch\n\nNAME          READY   STATUS      RESTARTS   AGE\nmemory-high   0\/1     OOMKilled   0          6s\nmemory-low    1\/1     Running     0          6m\nmemory-high   0\/1     OOMKilled   1          7s\nmemory-high   0\/1     CrashLoopBackOff   1          8s\nmemory-high   0\/1     OOMKilled          2          21s<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, the container is killed due to exceeding it&#8217;s memory requirements. Notice how this is different from our earlier CPU scenario?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create and consume secrets<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If\nyou&#8217;ve followed along with our Configmaps example earlier, you&#8217;ll notice a lot\nof similarities to secrets. In essence, secrets are configmaps that are base64\nobfuscated. There&#8217;s a couple of ways to create secrets, with the clue here\nbeing who does the base64 encoding:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Using kubectl, referencing a file. Kubernetes will do the base64 encoding<\/li><li>Using kubectl, using literal values. Kubernetes will do the base64 encoding<\/li><li>Using a YAML file, you need to do the base64 encoding.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Afterwards you can consume secrets in your pods, we&#8217;ll check that after we created a few. Let&#8217;s have a look at all three mechanisms:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using kubectl, referencing a file:<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s quickly create a file with the value of a secret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo 'love' >secretingredient.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can now create a secret referencing that file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create secret generic recipe --from-file=.\/secretingredient.txt <\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can then look at the secret, which will look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get secret recipe -o yaml\napiVersion: v1\ndata:\n  secretingredient.txt: bG92ZQo=\nkind: Secret\nmetadata:\n  creationTimestamp: \"2019-07-17T04:17:49Z\"\n  name: recipe\n  namespace: default\n  resourceVersion: \"3404565\"\n  selfLink: \/api\/v1\/namespaces\/default\/secrets\/recipe\n  uid: d63c536a-a849-11e9-aedb-0aee3919cf84\ntype: Opaque<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, the text itself is obfuscated. We can decode this using base64:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"bG92ZQo=\" | base64 -d -\nlove<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using kubectl, using literal values<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">We can do the same using a literal-value, using the following kubectl:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create secret generic recipe --from-literal=mysecret=ilovekubernetes<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can then do the same as before, get the secret and decode the value:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl get secret literalsecret -o yaml\napiVersion: v1\ndata:\n  mysecret: aWxvdmVrdWJlcm5ldGVz\nkind: Secret\nmetadata:\n  creationTimestamp: \"2019-07-17T04:22:12Z\"\n  name: literalsecret\n  namespace: default\n  resourceVersion: \"3404979\"\n  selfLink: \/api\/v1\/namespaces\/default\/secrets\/literalsecret\n  uid: 731c8496-a84a-11e9-aedb-0aee3919cf84\ntype: Opaque\n\necho \"aWxvdmVrdWJlcm5ldGVz\" | base64 -d -\nIlovekubernetes<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Using a YAML file<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Last way to create a secret is to create it via a yaml file. This yaml file expects your secret to be base64 encoded already, so let&#8217;s first base64 encode a string and create a secret:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"heavymetal\" | base64 -\naGVhdnltZXRhbAo=<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can then create this secret, and do the same as before, trying to decode it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Secret\nmetadata:\n  name: music\ntype: Opaque\ndata:\n  myfavorite: aGVhdnltZXRhbAo=<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f secret.yaml\nsecret\/music created\nkubectl get secret music -o yaml\napiVersion: v1\ndata:\n  myfavorite: aGVhdnltZXRhbAo=\nkind: Secret\nmetadata:\n  creationTimestamp: \"2019-07-17T04:26:20Z\"\n  name: music\n  namespace: default\n  resourceVersion: \"3405365\"\n  selfLink: \/api\/v1\/namespaces\/default\/secrets\/music\n  uid: 070d7b14-a84b-11e9-aedb-0aee3919cf84\ntype: Opaque\n\necho \"aGVhdnltZXRhbAo=\" | base64 -d -\nheavymetal<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now have a look on how we can use these secrets in our pods:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Using secrets<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s have a look at how we can use the three secrets we created:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Using a file: recipe<\/li><li>Using a literal: literalsecret<\/li><li>Using a YAML file: music<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll consume all three secrets in a single pod. We\u2019ll consume the file secret twice, once as an environmental variable and once as a volume.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: secretsconsumed\nspec:\n  containers:\n    - name: basic\n      image: busybox\n      command:\n        - sleep\n        - \"3600\"\n      env:\n        - name: FROMFILE\n          valueFrom:\n            secretKeyRef:\n              name: recipe\n              key: secretingredient.txt\n        - name: FROMLITERAL\n          valueFrom:\n            secretKeyRef:\n              name: literalsecret\n              key: mysecret\n        - name: FROMYAML\n          valueFrom:\n            secretKeyRef:\n              name: music\n              key: myfavorite\n      volumeMounts:\n        - name: secret-volume\n          mountPath: \"\/tmp\/supersecret\"\n          readOnly: true\n  volumes:\n    - name: secret-volume\n      secret:\n        secretName: recipe<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can then create our pod, exec into it, and have a look at the environment variables that are set and read our file. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f secretsconsumed.yaml\nkubectl exec -it secretsconsumed sh\n\/ # echo $FROMFILE\nlove\n\/ # echo $FROMLITERAL\nilovekubernetes\n\/ # echo $FROMYAML\nheavymetal\n\/ # cat \/tmp\/supersecret\/secretingredient.txt\nlove<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Understand ServiceAccounts<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A <a href=\"https:\/\/kubernetes.io\/docs\/reference\/access-authn-authz\/service-accounts-admin\/ \">service account<\/a> provides an identity for processes that run in a Pod. This allows processes in a pod to communicate to the api-server. Each pod by default gets assigned the <strong>default<\/strong>&nbsp; service account.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s go ahead and test this out (instructions partly from this <a href=\"https:\/\/stackoverflow.com\/questions\/30690186\/how-do-i-access-the-kubernetes-api-from-within-a-pod-container\">stackoverflow <\/a>post):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: simple-pod\nspec:\n  containers:\n    - name: basic\n      image: radial\/busyboxplus:curl\n      command:\n        - sleep\n        - \"3600\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;ll create this pod and connect to the kubernetes API:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f simplepod.yaml\nkubectl exec -it simple-pod sh\nKUBEHOST=\"aksworksho-akschallenge-d19ddd-c565b193.hcp.westus2.azmk8s.io\" #change this to your cluster\nKUBE_TOKEN=$(cat \/var\/run\/secrets\/kubernetes.io\/serviceaccount\/token)\ncurl -sSk -H \"Authorization: Bearer $KUBE_TOKEN\" \\\n      https:\/\/$KUBEHOST\/api\/v1\/namespaces\/default\/pods\/$HOSTNAME<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The response to my curl (as you will see as well if your cluster is RBAC enabled) is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"kind\": \"Status\",\n  \"apiVersion\": \"v1\",\n  \"metadata\": {\n\n  },\n  \"status\": \"Failure\",\n  <strong>\"message\": \"pods \\\"simple-pod\\\" is forbidden: User \\\"system:serviceaccount:default:default\\\" cannot get resource \\\"pods\\\" in API group \\\"\\\" in the namespace \\\"default\\\"\",\n  \"reason\": \"Forbidden\",<\/strong>\n  \"details\": {\n    \"name\": \"simple-pod\",\n    \"kind\": \"pods\"\n  },\n  \"code\": 403<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s try to solve this by <a href=\"https:\/\/kubernetes.io\/docs\/reference\/access-authn-authz\/rbac\/\">giving our default service account access to the API-server<\/a>. As I am lazy, I&#8217;ll show you something new, how you can create multiple objects from a single yaml file, by using the<code> --- <\/code>in between the objects.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: rbac.authorization.k8s.io\/v1\nkind: Role\nmetadata:\n  namespace: default\n  name: pod-reader\nrules:\n- apiGroups: [\"\"] # \"\" indicates the core API group\n  resources: [\"pods\"]\n  verbs: [\"get\", \"watch\", \"list\"]\n---\napiVersion: rbac.authorization.k8s.io\/v1\nkind: RoleBinding\nmetadata:\n  name: read-pods\n  namespace: default\nsubjects:\n- kind: ServiceAccount\n  name: default \n  apiGroup: \"\"\nroleRef:\n  kind: Role \n  name: pod-reader  \n  apiGroup: \"\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">What we&#8217;ve done here, is create a Role called <code>pod-reader<\/code>, that allows its subjects to get, watch and list pods in the default namespace. Next we create a RoleBinding, that links out default ServiceAccount to that role.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s create this, and see what is looks like in our pod. Btw. Notice how we didn&#8217;t kill our pod, we only made api-level changes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create -f solution.yaml\nkubectl exec -it simple-pod sh\nKUBEHOST=\"aksworksho-akschallenge-d19ddd-c565b193.hcp.westus2.azmk8s.io\" #change this to your cluster\nKUBE_TOKEN=$(cat \/var\/run\/secrets\/kubernetes.io\/serviceaccount\/token)\ncurl -sSk -H \"Authorization: Bearer $KUBE_TOKEN\" \\\n      https:\/\/$KUBEHOST\/api\/v1\/namespaces\/default\/pods\/$HOSTNAME<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And our response is the full json definition of our pod. <strong><em>#SUCCESS<\/em><\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We can also create our own service accounts, and then give them permissions to pods. Let&#8217;s try the same, with a new service account, the same role, a new rolebinding and a new pod.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: rbac.authorization.k8s.io\/v1\nkind: RoleBinding\nmetadata:\n  name: thanos-read-pods\n  namespace: default\nsubjects:\n- kind: ServiceAccount\n  name: thanos \n  apiGroup: \"\"\nroleRef:\n  kind: Role \n  name: pod-reader  \n  apiGroup: \"\"\n---\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: thanos\n  namespace: default\n---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: thanos-pod\nspec:\n  serviceAccountName: thanos\n  containers:\n    - name: basic\n      image: radial\/busyboxplus:curl\n      command:\n        - sleep\n        - \"3600\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, we&#8217;ll exec into our new pod, and try our curl again:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl exec -it thanos-pod sh\nKUBEHOST=\"aksworksho-akschallenge-d19ddd-c565b193.hcp.westus2.azmk8s.io\" #change this to your cluster\nKUBE_TOKEN=$(cat \/var\/run\/secrets\/kubernetes.io\/serviceaccount\/token)\ncurl -sSk -H \"Authorization: Bearer $KUBE_TOKEN\" \\\n      https:\/\/$KUBEHOST\/api\/v1\/namespaces\/default\/pods\/$HOSTNAME<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And again we get a full JSON definition of our pod. <em><strong>#SUCCESS<\/strong><\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary of configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In part 3 of my CKAD series we dove into configuration. We touched on configmaps and secrets (both pretty similar), securitycontext, resource constraints and service accounts. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Up to part 4?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part 3 in a multi-part series on my CKAD learning experience. For the other parts in the series, please check out the following links: Part 1: intro, exam topics and my study plan Part 2: core concepts Part 4: multi-container pods Part 5: Observability Part 6: Pod Design Part 7: Networking As I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[20],"tags":[19,17,18],"class_list":["post-214","post","type-post","status-publish","format-standard","hentry","category-ckad","tag-certification","tag-ckad","tag-kubernetes"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/214","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=214"}],"version-history":[{"count":8,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/214\/revisions"}],"predecessor-version":[{"id":294,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/214\/revisions\/294"}],"wp:attachment":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=214"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=214"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=214"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}