DevOps Day 62: Managing Secrets in Kubernetes¶
Today's task was a deep dive into one of the most critical aspects of running applications in any environment: managing sensitive data. My objective was to use a Kubernetes Secret to store a piece of confidential data (like a password or license key) and then securely make it available to an application running in a Pod.
This was a fantastic lesson in cloud-native security practices. I learned that you should never hardcode secrets in your configuration files or container images. Instead, you use a dedicated object like a Secret to decouple sensitive data from your application code. This document is my very detailed, first-person guide to that entire process, from creating the Secret to consuming it as a file inside a container.
Table of Contents¶
- The Task
- My Step-by-Step Solution
- Why Did I Do This? (The "What & Why" for a K8s Beginner)
- Deep Dive: A Line-by-Line Explanation of My Pod YAML File
- Common Pitfalls for Beginners
- Exploring the Essential
kubectlCommands
The Task¶
My objective was to create a Kubernetes Secret and consume it in a Pod. The specific requirements were:
1. Create a generic Secret named blog from the contents of the file /opt/blog.txt on the jump host.
2. Create a Pod named secret-xfusion.
3. The Pod must run a container named secret-container-xfusion using the ubuntu:latest image.
4. The container must be kept running (using a sleep command).
5. The blog Secret must be mounted as a volume into the container at the path /opt/games.
My Step-by-Step Solution¶
The solution involved an imperative command to create the Secret and a declarative manifest to create the Pod that uses it.
Phase 1: Creating the Secret¶
- I connected to the jump host.
- I used a single, imperative
kubectl create secretcommand to generate the Secret directly from the file. This is the most efficient way to handle this.The command responded withkubectl create secret generic blog --from-file=/opt/blog.txtsecret/blog created, which was my first confirmation of success.
Phase 2: Creating the Pod to Consume the Secret¶
- I created a new file named
secret-pod.yamlusingvi. - Inside the editor, I wrote the following YAML code, which defines the Pod and, most importantly, the
volumesandvolumeMountssections that link the container to the Secret.apiVersion: v1 kind: Pod metadata: name: secret-xfusion spec: containers: - name: secret-container-xfusion image: ubuntu:latest command: ["/bin/sh", "-c", "sleep 3600"] volumeMounts: - name: secret-volume mountPath: "/opt/games" readOnly: true volumes: - name: secret-volume secret: secretName: blog - I saved the file and used
kubectlto create the Pod from my manifest.kubectl apply -f secret-pod.yaml
Phase 3: Verification¶
The final part of the task was to prove that the secret data was correctly mounted inside the running container.
1. First, I checked the Pod's status with kubectl get pods to ensure it was Running.
2. Then, I used kubectl exec to get an interactive shell inside the container.
kubectl exec -it secret-xfusion -- /bin/bash
# Inside the container shell:
ls -l /opt/games
blog.txt. This file was created by Kubernetes from the data in my blog Secret.
4. I then viewed the contents of the file:
cat /opt/games/blog.txt
Why Did I Do This? (The "What & Why" for a K8s Beginner)¶
- The Problem of Secrets: The absolute worst thing I can do is hardcode sensitive data like passwords, API keys, or license numbers directly into my Dockerfile or my Pod's YAML file. This would commit my secrets to my Git repository, making them visible to anyone with access.
- Kubernetes Secret (The Solution): A Secret is a dedicated Kubernetes object designed specifically to hold a small amount of sensitive data.
- How it's stored: The data is stored in the Kubernetes cluster's database (etcd) as base64-encoded strings. This is not encryption, but it prevents someone from accidentally seeing the secret just by looking at a YAML file. Real security is provided by Kubernetes's Role-Based Access Control (RBAC), which controls who can read or create Secret objects.
- Decoupling: The key benefit is that it decouples my application from its secrets. My Pod's definition just says, "I need the secret named blog." It doesn't know or care what the secret's value is. This means I can update the secret in the cluster without having to rebuild my application's image or change its deployment file.
- Consuming Secrets as Volumes (This Task's Method): This is one of two ways a Pod can use a Secret. I told my Pod to create a volume whose source was the blog Secret. Kubernetes then created a temporary, in-memory filesystem and populated it with files. For each key-value pair in the Secret, it created a file where the filename was the key and the file's content was the value. This is often considered more secure than using environment variables, as the secret data is never exposed in the container's environment.
Deep Dive: A Line-by-Line Explanation of My Pod YAML File¶
The YAML for this task demonstrates the powerful link between volumes and volumeMounts.
[Image of a Kubernetes Secret being mounted into a Pod]
apiVersion: v1
kind: Pod
metadata:
name: secret-xfusion
spec:
containers:
- name: secret-container-xfusion
image: ubuntu:latest
command: ["/bin/sh", "-c", "sleep 3600"]
# This 'volumeMounts' block is inside the container's definition.
# It tells this specific container HOW to USE a volume that is declared below.
volumeMounts:
- name: secret-volume # This name MUST match a volume defined in the 'volumes' block.
mountPath: "/opt/games" # This is the path INSIDE this container to mount the volume.
readOnly: true # It's a best practice to mount secrets as read-only.
# This 'volumes' block is at the Pod's 'spec' level. This is where I DECLARE
# all the volumes that will be available to the containers in this Pod.
volumes:
# The '-' indicates an item in a list. This is my volume definition.
- name: secret-volume # I give the volume a name that I can refer to in 'volumeMounts'.
# This 'secret' block tells Kubernetes that the source for this volume
# is a Secret object.
secret:
# This specifies the name of the Secret to use.
secretName: blog
Common Pitfalls for Beginners¶
- Creating the Secret Incorrectly: The kubectl create secret generic command is very powerful. If I had used --from-literal instead of --from-file, the value of the secret would have been the literal string "/opt/blog.txt" instead of the file's content.
- Name Mismatch: A typo in the secretName in the Pod's volumes section, or in the name of the volumeMounts, will cause the Pod to fail to start because it won't be able to find the storage it needs.
- Confusing describe secret and get secret: Running kubectl describe secret blog shows metadata but not the secret's content. To see the base64-encoded content, you would use kubectl get secret blog -o yaml. To see the decoded content, you need to use more advanced commands.
- Forgetting to Verify Inside the Container: The only way to be 100% sure the secret was mounted correctly is to kubectl exec into the container and cat the file.
Exploring the Essential kubectl Commands¶
- kubectl create secret generic [secret-name] --from-file=[file-path]: An imperative command to create a generic secret. This is the fastest way to create a secret from a file's contents. The filename becomes the "key" in the secret's data.
- kubectl apply -f [filename.yaml]: The standard declarative way to create or update resources from a manifest file. I used this for my Pod.
- kubectl get pods: Lists a summary of all Pods.
- kubectl describe secret [secret-name]: Describes a Secret's metadata (name, labels, data keys) but does not show the actual secret values.
- kubectl exec -it [pod-name] -- /bin/bash: My primary verification tool. It gave me an interactive terminal shell inside my running container.
- ls and cat (inside the container): The standard Linux commands I used to list the contents of the mounted directory and view the secret data to confirm the task was successful.