Full lesson
Explore the full explanation, examples, and visuals at your own pace.
app.py, then the log after a routine restart
After a routine restart, this Python app crashed because REDIS_PORT held a URL, not a number. It had run fine for days, and the manifest never set REDIS_PORT. So the code fell back on whatever the environment provided, and int() couldn't parse tcp://10.96.12.4:6379.
import os
# optional, defaults to 6379
port = int(os.environ.get("REDIS_PORT", "6379"))
# --- log after rollout restart ---
ValueError: invalid literal for int() with base 10:
'tcp://10.96.12.4:6379'The cause: kubelet adds Service variables
The cause is kubelet, the node agent that starts your container. At start, it takes the variables from your Pod spec and merges them with variables generated for every Service that already exists in the same namespace. So the Service named redis quietly adds its own variables to the container's environment.
env inside a newly started app container
Kubernetes builds these names from the Service name: uppercase it, and turn dashes into underscores. Here, redis becomes REDIS. You get REDIS_SERVICE_HOST and REDIS_SERVICE_PORT first. Then come the Docker-links-style variables, and REDIS_PORT is a full URL, tcp colon slash slash 10.96.12.4, port 6379. That's the name your app reads.
REDIS_SERVICE_HOST=10.96.12.4
REDIS_SERVICE_PORT=6379
REDIS_PORT=tcp://10.96.12.4:6379
REDIS_PORT_6379_TCP=tcp://10.96.12.4:6379
REDIS_PORT_6379_TCP_PROTO=tcp
REDIS_PORT_6379_TCP_PORT=6379
REDIS_PORT_6379_TCP_ADDR=10.96.12.4Why it worked for days
Follow the timeline. The kubelet starts the old Pod before the Service exists, so it gets no REDIS variables. Then the Service redis is created, and the kubelet sees it, but the old Pod's environment stays unchanged. Days later, a rollout restart starts a new Pod, and only now does REDIS_PORT get injected. The crash follows a harmless restart.
Three rules of injected variables
Three rules explain all of this. The names come from Services, so another team's Service called redis decides your variable names. They're set only at container start, so running Pods never update. And explicit env entries in your spec win over injected ones.
- Named after Services, not your manifest
- Set only when a container starts
- Explicit spec values take precedence
Who is at risk: optional variables with defaults
Follow the decision on screen. If REDIS_PORT is in your spec, your value takes precedence, and the app runs. If it's missing, the injected URL fills the slot, so the fallback default never runs, and int() hits that URL and crashes. Optional variables with defaults are the vulnerable case.
deployment.yaml: two fixes
Here are two fixes in the Deployment's Pod template. First, enableServiceLinks set to false stops Kubernetes from injecting those Docker-links-style variables, so REDIS_PORT never appears. Second, the app reads a prefixed name, MYAPP_REDIS_PORT, set explicitly to 6379. No Service can collide with that. Either change prevents the crash.
spec:
template:
spec:
enableServiceLinks: false
containers:
- name: app
image: shop-app:1.4
env:
- name: MYAPP_REDIS_PORT
value: "6379"Fixes and their trade-offs
Each fix has a cost. Disabling service links is clean, but legacy code might read those injected variables, so check first. Prefixing avoids collisions, though it needs an app change. An explicit REDIS_PORT works too, but every Pod has to carry it.
- Disable service links: check nothing depends on them
- Prefix variables: MYAPP_REDIS_PORT avoids collisions
- Set REDIS_PORT explicitly: takes precedence
The usual path: find Services by DNS name
The usual path is DNS. The app Pod asks cluster DNS to resolve redis, or redis.shop from another namespace, and DNS returns the Service address, 10.96.12.4. The Service then forwards to the Redis Pod on port 6379. The lookup happens at query time, so creation order doesn't matter. That's why many teams turn service links off.
At container start, kubelet stamps existing Services into your env.
At container start, kubelet stamps existing Services into your env. So prefix your app variables or disable service links, and discover Services through DNS.
