Overview
- Chapter 8 of the Kubernetes Cookbook: sharing files between containers, feeding config and secrets into Pods, and storage that outlives the Pod (PV, PVC, StorageClass).
- My basics: ConfigMap (decouple config from images, and ConfigMaps aren’t secure). Remember from Pods: anything written inside a container dies with it.
- Back to the hub: Kubernetes Cookbook
Volume Types at a Glance
| Type | Lives as long as | Use it for |
|---|---|---|
emptyDir | the Pod | scratch space, sharing files between containers |
configMap / secret / projected | the Pod (data from API objects) | config files, credentials |
hostPath | the node | node agents only (log shippers). Avoid for apps. |
persistentVolumeClaim | independently of Pods | databases, uploads, anything you can’t lose |
ephemeral (generic) | the Pod, but provisioned by a CSI driver | big scratch disks |
Changed since the book:
Storage is now done by CSI drivers. The in-tree cloud plugins (
awsElasticBlockStore,gcePersistentDisk,azureDisk…) were migrated or removed.gitRepovolumes are deprecated (use an init container that clones instead).
Recipes
Recipe 8.1 — Share Data Between Containers (emptyDir)
- Problem: Two containers in one Pod need to exchange files.
- Solution:
apiVersion: v1
kind: Pod
metadata:
name: sharevol
spec:
containers:
- name: c1
image: busybox:1.36
command: ["sh", "-c", "echo hello > /xchange/data; sleep 3600"]
volumeMounts:
- name: xchange
mountPath: /xchange
- name: c2
image: busybox:1.36
command: ["sh", "-c", "sleep 5; cat /data/data; sleep 3600"]
volumeMounts:
- name: xchange
mountPath: /data
volumes:
- name: xchange
emptyDir: {} # add medium: Memory for a tmpfs- Discussion: This is the basis of the sidecar/adapter patterns. One container writes, another ships or transforms.
- Gotchas: Data is gone when the Pod is deleted or moves node. Set
sizeLimitso a runaway writer can’t fill the node’s disk.
Source: K8s Cookbook, Recipe 8.1
Recipe 8.2 — Pass an API Key with a Secret
- Problem: Give an app a credential without putting it in the image or plain YAML.
- Solution:
kubectl create secret generic pp --from-literal=passphrase='open sesame'
kubectl get secret pp -o jsonpath='{.data.passphrase}' | base64 -d # it's only base64!apiVersion: v1
kind: Pod
metadata:
name: ppconsumer
spec:
containers:
- name: shell
image: busybox:1.36
command: ["sh", "-c", "cat /tmp/access/passphrase; sleep 3600"]
volumeMounts:
- name: passphrase
mountPath: /tmp/access
readOnly: true
env:
- name: PASSPHRASE # alternative: as an env var
valueFrom:
secretKeyRef:
name: pp
key: passphrase
volumes:
- name: passphrase
secret:
secretName: pp
defaultMode: 0400- Discussion: Secret types:
generic(Opaque),docker-registry(image pull credentials) andtls(cert + key, used by Ingress). Max size is 1 MiB. Secrets are namespaced. - Gotchas: Secrets are base64, not encrypted. Anyone with
get secretRBAC, or access to etcd backups, can read them. Do three things:- Turn on encryption at rest (
EncryptionConfigurationwith a KMS v2 provider). Managed clouds often do this for you. - Lock down RBAC on
secrets(listandwatchreveal all of them). - Keep real secrets in a vault and sync them in (see Security).
- Turn on encryption at rest (
- Prefer volume mounts to env vars. Env vars leak into crash dumps and
kubectl describe, and they don’t update when the Secret changes.
Changed since the book:
The flag is now
--encryption-provider-config(no longer “experimental”), with KMS v2 GA. ServiceAccount token Secrets are no longer auto-created (since 1.24). See Security.
Source: K8s Cookbook, Recipe 8.2 · Best Practices book, Ch. 4
Recipe 8.3 — Provide Configuration Data (ConfigMap)
- Problem: Configure an app without rebuilding the image (ConfigMap).
- Solution:
kubectl create configmap siseconfig --from-literal=siseversion=0.9
kubectl create configmap configfile --from-file=example.cfgapiVersion: apps/v1
kind: Deployment
metadata:
name: cmapp
spec:
replicas: 1
selector:
matchLabels:
app: cmapp
template:
metadata:
labels:
app: cmapp
spec:
containers:
- name: sise
image: mhausenblas/simpleservice:0.5.0
envFrom:
- configMapRef:
name: siseconfig # every key becomes an env var
volumeMounts:
- name: cfg
mountPath: /etc/app # every key becomes a file
volumes:
- name: cfg
configMap:
name: configfile- Discussion:
- Env vars are read once at start. A ConfigMap change needs a
kubectl rollout restart. - Volume files refresh automatically (after up to about a minute), so an app that watches its config file can hot-reload.
subPathmounts never refresh.
- Env vars are read once at start. A ConfigMap change needs a
- Gotchas: The ConfigMap must exist before the Pod starts (or mark it
optional: true). Mark stable configimmutable: true. That protects it from accidental edits and saves API server watch load. - Trick: Kustomize’s
configMapGeneratorand Helm’schecksum/configannotation change the Pod template whenever config changes. That triggers a rollout automatically (see Managing Manifests).
Changed since the book:
extensions/v1beta1→apps/v1. Immutable ConfigMaps and Secrets are GA (1.21).
Source: K8s Cookbook, Recipe 8.3 · Best Practices book, Ch. 4
Recipe 8.4 — Use a PersistentVolume (static provisioning)
- Problem: Data must survive Pod restarts.
- Solution: The admin creates a PV (the actual disk). The developer creates a PVC (a request for a disk). Kubernetes binds them, and the Pod mounts the PVC.
apiVersion: v1
kind: PersistentVolume
metadata:
name: hostpathpv
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /tmp/pvdata # fine for minikube/kind, never in prod
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
spec:
storageClassName: manual
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 200Mi# in the Pod template:
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc- Access modes:
| Mode | Meaning |
|---|---|
ReadWriteOnce (RWO) | read-write by one node (several Pods on that node can share it) |
ReadWriteOncePod (RWOP) | read-write by one Pod only (GA 1.29) |
ReadOnlyMany (ROX) | read-only by many nodes |
ReadWriteMany (RWX) | read-write by many nodes. Needs NFS, CephFS, EFS, Azure Files… |
- Gotchas: PVs are cluster-scoped, PVCs are namespaced. A PVC stuck in
Pendingmeans no PV or StorageClass matches its size, mode or class (kubectl describe pvctells you why).
Source: K8s Cookbook, Recipe 8.4
Recipe 8.5 / 8.6 — Dynamic Provisioning with StorageClasses
- Problem: You don’t want an admin hand-making PVs for every claim.
- Solution: Just create the PVC. The default StorageClass provisions a matching PV on demand.
kubectl get storageclass # the one marked (default) is used when none is namedapiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: pd.csi.storage.gke.io # ebs.csi.aws.com, disk.csi.azure.com, rancher.io/local-path…
parameters:
type: pd-ssd
reclaimPolicy: Retain # default is Delete!
volumeBindingMode: WaitForFirstConsumer # create the disk in the zone where the Pod lands
allowVolumeExpansion: true
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
spec:
storageClassName: fast-ssd
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi- Discussion: minikube’s default class (
standard, provisionerk8s.io/minikube-hostpath) and kind’s (standard, local-path) are fine for learning. On cloud you get CSI-backed disks. Grow a volume by editing the PVC’sstorage(ifallowVolumeExpansion). Back up with VolumeSnapshots or Velero. - Gotchas:
reclaimPolicy: Delete(the default for dynamic PVs) destroys the disk when the PVC is deleted. UseRetainfor anything precious.- Use
WaitForFirstConsumer, or you’ll get “volume in zone A, Pod in zone B” (Best Practices). - Block disks are zonal, so a Pod can’t follow its disk to another zone.
Changed since the book:
GKE’s default class now uses the PD CSI driver (
standard-rwo), notkubernetes.io/gce-pd. The blog-post walkthrough the book links to is outdated.
Source: K8s Cookbook, Recipes 8.5 & 8.6 · Best Practices book, Ch. 16
Summary
emptyDirfor scratch and sharing, ConfigMap/Secret for config, PVC for anything that must survive.- ConfigMap and Secret as env vars = restart to pick up changes. As volume = auto-refresh (except
subPath). - Secrets are base64, not encrypted: enable encryption at rest, tighten RBAC, use an external secret store.
- PV (disk) ↔ PVC (request) ↔ StorageClass (on-demand provisioning). Watch
reclaimPolicyandWaitForFirstConsumer.