# EXAMPLE — a component image. Copy, rename, replace. # # Named like the manifest it feeds and the resource it becomes: # # ctrl/Dockerfile.api -> image -api -> image: in k8s/base/api.yaml # # That image string is the ONLY thing connecting the three. Nothing checks it; # a typo shows up as a pod stuck in ImagePullBackOff pulling from the public # index, which reads like a network problem and is not one. # # ── the one that catches everyone ────────────────────────────────────────── # The Tiltfile passes two paths with DIFFERENT bases, in adjacent arguments: # # context='..' the REPO ROOT (the Tiltfile is in ctrl/) # dockerfile='Dockerfile.api' relative to the TILTFILE, so ctrl/Dockerfile.api # # So every COPY below is resolved against the repo root, NOT against this file's # directory. A file sitting right beside this one is still reached as `ctrl/`: # # COPY ctrl/nginx.conf /etc/nginx/conf.d/default.conf # correct # COPY nginx.conf /etc/nginx/conf.d/default.conf # fails — no such file # # Nothing warns you. The build just cannot find a file that is visibly there. FROM python:3.12-slim WORKDIR /app # Dependencies first, in their own layer: they change far less often than the # code, so a source edit does not reinstall them on every rebuild. COPY api/requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt # Repo-root relative — see above. COPY api/ ./api/ # Match this with the containerPort in the manifest and the target of the # Service in front of it. EXPOSE 8000 CMD ["python", "-m", "api"] # ── live_update ──────────────────────────────────────────────────────────── # The sync in the Tiltfile's docker_build must land where this image expects it: # # live_update=[sync('../api', '/app/api')] # # matches `COPY api/ ./api/` with WORKDIR /app. If the two disagree, Tilt syncs # into a path nothing reads and the container keeps serving the built copy — # edits appear to do nothing, with no error anywhere.