Day 4 - Kubernetes Without The Tears

TJ GokkenTJ Gokken
9 min read

So far, we’ve deployed apps, filtered pods like a seasoned K8s professional, and organized our cluster with labels and namespaces. Now it’s time to tackle something that comes up in every real-world app: configuration.

Hardcoding stuff inside containers? Yeah, let’s not. We are developers - when do we every hard code stuff?

Under the Hood

All apps need things like database connection strings, feature flags, environment variables and API keys and passwords (please don’t put these in plain YAML - we’ll see how we can store these).

Kubernetes gives us two tools for this and their names are pretty self-explanatory: Secrets for well, secrets as in sensitive data and ConfigMaps for all other non-sensitive config data.

Let’s see how they work in K8s.

ConfigMaps: Externalized Settings

ConfigMaps, as their name suggests, let you inject configuration values into your pods—things like app settings, URLs, or even other config files.

You can create them from literal values or from files:

kubectl create configmap my-config --from-literal=APP_MODE=production
kubectl create configmap my-config --from-file=config.json

While this is great, writing a config file in terminal is pretty hard core and if you like doing that, all the power to you and much respect, but I am a bit lazier. Therefore, I prefer to create or edit the config files in my favorite editor, such as Visual Studio Code.

Once you’re done with the file, just use --from-file to load it in.

💡
Yes, if you make changes to the file, you’ll need to delete and recreate the ConfigMap using --from-file again, or use kubectl apply -f with a YAML-based ConfigMap definition. I have to admit, it’s not the most elegant update process.

If you missed it please read the callout box above. Updating config files is like pulling teeth in K8s. That’s why many folks choose to manage their config declaratively.

Declarative config means defining your resources in YAML files that describe what you want—rather than imperatively telling Kubernetes what to do step by step. You save the file, version control it (Git, FTW), and apply it like so:

kubectl apply -f configmap.yaml

Now you’ve got a repeatable setup, easy rollbacks, and fewer flags to remember (I did tell you that command kubectl apply -f is one of the most used ones now, didn’t I?)

To see it:

kubectl get configmap my-config -o yaml

You then reference it in your pod spec using envFrom, env, or volumeMounts.

Example:

envFrom:
- configMapRef:
    name: my-config

Or specify individual keys:

env:
- name: APP_MODE
  valueFrom:
    configMapKeyRef:
      name: my-config
      key: APP_MODE

If this is a bit overwhelming, do not worry. I think it will become much more clearer when we have our hands on it in the lab section.

Secrets: You show me yours and I’ll show you mine

Secrets work like ConfigMaps, but Kubernetes encodes the values in base64. It’s not encryption—it’s just obscured from casual eyes. You still need to be careful.

Let’s say you want to define a secret using a YAML file. Here’s what it looks like in plain text, and Kubernetes will handle the base64 encoding for you when you use kubectl create secret:

kubectl create secret generic my-secret --from-literal=DB_PASSWORD=supersecret

You can inspect this secret using

kubectl get secret my-secret -o yaml

to see something like:

apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  DB_PASSWORD: c3VwZXJzZWNyZXQ=

That c3VwZXJzZWNyZXQ= is just supersecret in base64. Kubernetes handles this encoding automatically when you pass values with --from-literal. You don’t need to encode it yourself unless you’re writing the YAML manually.

💡
You can decode the secrets with echo c3VwZXJzZWNyZXQ= | base64 --decode if you want to see them.

So, this works great if you have one or two secrets, but what if you have a lot of secrets? Afterall, we all have more than one secret, don’t we?

Then you have options. You can pas multiple values inline;

kubectl create secret generic nginx-secret \
  --from-literal=API_KEY=12345-abcde \
  --from-literal=DB_PASSWORD=supersecret \
  --from-literal=LOG_LEVEL=debug

Or you can create a file called secrets.env with key-value pairs (yeah, it is a pair this time unlike the labels we have seen on Day 3).

API_KEY=12345-abcde
DB_PASSWORD=supersecret
LOG_LEVEL=debug

and then use

kubectl create secret generic nginx-secret --from-env-file=secrets.env

But hold on - at the beginning of this article there was this big fat warning about not using secrets in YAML files. Yet, now we have them in plain text files? Yeah, not very secure, is it? If someone gets access to your repo or local machine, they can see your secrets. That’s why in production environments, secrets are often stored in secure vaults such as Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault (offered by IBM).

You can integrate these vaults with Kubernetes using external secret operators or CSI drivers. We won’t dive into those just yet, but keep that in mind when you start deploying to real clusters.

For our learning purposes, however, both .env file or inline works just fine.

Now that we confused ourselves a lot, let’s stop here and see all this in action before we go any further and enter the twilight zone.

Lab: Config and Secrets

Let’s set up a ConfigMap and a Secret, and wire them into a pod. Remember to be in the k8s-labs folder (or whatever folder you are using to keep our lab files in)

Step 1: Create the ConfigMap

kubectl create configmap nginx-config --from-literal=APP_MODE=staging -n dev-space

Step 2: Create the secret

We’ve got two options here. We either pass the secret(s) inline;

kubectl create secret generic nginx-secret \
  --from-literal=API_KEY=12345-abcde \
  --from-literal=DB_PASSWORD=supersecret \
  --from-literal=LOG_LEVEL=debug
  -n dev-space

Or, we create a secrets.env file in the same directory with this content:

API_KEY=12345-abcde
DB_PASSWORD=supersecret
LOG_LEVEL=debug

and then run the following command:

kubectl create secret generic nginx-secret --from-env-file=secrets.env -n dev-space

FYI, I went with the secrets.env file option.

Step 3: Update Your Deployment YAML

Add this under spec.template.spec.env:

env:
- name: APP_MODE
  valueFrom:
    configMapKeyRef:
      name: nginx-config
      key: APP_MODE
- name: API_KEY
  valueFrom:
    secretKeyRef:
      name: nginx-secret
      key: API_KEY

And save this file as nginx-deployment-day4.yaml. Here is what the full YAML file looks like after the changes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: dev-space
  labels:
    app: nginx
    env: dev
    tier: frontend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
        env: dev
        tier: frontend
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        env:
        - name: APP_MODE
          valueFrom:
            configMapKeyRef:
              name: nginx-config
              key: APP_MODE
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: nginx-secret
              key: API_KEY

Then re-apply the deployment:

kubectl apply -f nginx-deployment-day4.yaml

Step 4: Check the Environment Inside the Pod

Let’s find the pod name:

kubectl get pods -n dev-space

Then exec into it:

kubectl exec -n dev-space -it <pod-name> -- /bin/sh

Don’t be alarmed if all you see is a lonely # prompt—that means you’ve successfully dropped into the container shell. It’s a minimal shell and it’s ready for commands.

Let’s give that pound sign a purpose: check the env variables:

env | grep APP_MODE
env | grep API_KEY

Boom. Config from the outside world.

If you want to explore more:

  • ls / to see the file system

  • cat /etc/os-release to see what distro you’re in

  • exit to get out of the container


We’re halfway through. Keep going—this plane’s got plenty of altitude left.

But hold on, don’t leave just yet. Grab another cup of coffee, tea or better yet some more water and stick around. I’d like to add a bonus section.


Bonus: GUI Options for K8s

Because the mouse gets lonely and sometimes you just want to click stuff.

We’ve been living in the command line for a while, and that’s great for automation and scripting.

But let’s be honest—not everyone dreams in YAML and unless you are working with Kubernetes day in day out, you won’t remember any of these commands. Sure, you will look for them and use the cheat sheet that is included in this series, but when you are new at something, a GUI is invaluable. Sometimes, you just need color in your life or even you just want to click and right click just because you can.

Fortunately, Kubernetes has some great GUI options if you want to see what’s going on without typing kubectl every five minutes. I would like to introduce two of these options with a brief introduction:

Kubernetes Dashboard

This is the official web-based UI. We installed it back in Day 1, remember? (I surely don’t). This dashboard helps us;

  • View and manage pods, deployments, and services

  • Inspect logs, scale workloads, and see events in real time

  • Great for getting a quick visual snapshot

So, let’s launch it:

kubectl proxy

This is the only command we will type - well, maybe one more, but that’s it. If you want, you can put this command in a batch file and double click it. Anyway, once you do this, you will get a message like this:

Starting to serve on 127.0.0.1:8001

So, open your favorite browser and go to that address and… more code. You get a list of all the URLs you can go to. Let’s go to this one:

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login

I know it’s a mouthful but you can bookmark it. Once you are there, you will need to enter a token. Enter:

kubectl -n kubernetes-dashboard create token dashboard-admin

Copy the generated token, paste it in your browser where it says Token, and finally you get something more than a black and white screen. Click around and see how you like it.

Lens

If you are thinking, Kubernetes Dashboard is still using the CLI, you are right. For a more complete user experience and GUI, there is an open source tool called Lens. It is a desktop app and autodetects your clusters and you can view your logs, resource usage and YAML manifests.

You can download it from https://k8slens.dev/

Once you install it, this is the experience you get:

Kind of like Visual Studio Code but for Kubernetes. Oh, by the way, the default theme is dark but I used light, so we get a visual different than the command line.

We’ll keep using CLI throughout this series—but if you want to explore or debug something quickly, these GUI options can be a lifesaver.

What’s Next

If you’ve made it this far, give yourself a high five — you’re not just using Kubernetes, you’re speaking its language.

Tomorrow we’ll look at probes and scaling—because what good is a healthy app if you can’t tell it’s healthy or scale it when it gets popular?

See you tomorrow on Day 5 - we’ve got some probes to poke and apps to scale 😎.

0
Subscribe to my newsletter

Read articles from TJ Gokken directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

TJ Gokken
TJ Gokken

TJ Gokken is an Enterprise AI/ML Integration Engineer with a passion for bridging the gap between technology and practical application. Specializing in .NET frameworks and machine learning, TJ helps software teams operationalize AI to drive innovation and efficiency. With over two decades of experience in programming and technology integration, he is a trusted advisor and thought leader in the AI community