What's changed: Initial version
4.2CI/CD and containers
Covers the CI/CD pipeline that delivers code safely (automating build -> test -> deploy), the unit tests that guard changes (Python unittest/pytest), and the Dockerfile (FROM/RUN/COPY/CMD/EXPOSE) plus the image-and-container relationship that makes an app portable—framed as reading/diagnosing "what this pipeline/Dockerfile guarantees, and where it is wrong."
What DevNet asks is not the definition of "what CI/CD is" but whether you can read what the pipeline or Dockerfile in front of you guarantees and where it is flawed. A pipeline that deploys without passing tests mass-produces incidents, a Dockerfile that ignores caching needlessly slows builds, and an image missing CMD will not start. This section covers CI/CD stages, Python unit tests, and each Dockerfile instruction from the viewpoint of reading and diagnosing a presented artifact.
4.2.1The CI/CD pipeline
- CI (continuous integration) integrates developers' changes into a shared branch frequently, running an automated build and test each time to catch breakage early. CD (continuous delivery/deployment) advances an artifact that passed tests all the way to automatic release/production. In short, it makes
build -> test -> deployrepeatable without human hands. - The heart of a pipeline is a gate: do not advance if tests fail. It is correct design that if the
teststage is red,deploydoes not run; a pipeline that skips tests and pushes to production is an automated incident. The earlier and smaller the failure is caught, the cheaper the fix.
4.2.2Python unit tests
- Unit tests automatically verify the behavior of the smallest units—functions and methods—by their expected output for an input. In Python you use the standard
unittest(assertions likeassertEqual) orpytest(assertstatements and concise syntax). CI mostly runs these unit tests to guard that a change has not broken existing behavior. - A good test checks one behavior in one case and, on failure, makes it clear what broke and why. If
assertEqual(add(2, 3), 5)fails, a regression inaddis obvious at a glance. Passing tests is CI's pass condition, and the practical crux is that code without tests gets no protection from CI.
4.2.3Dockerfile, image, and container
- A Dockerfile is text describing how to build an image. Key instructions:
FROM(base image),RUN(run a command at build time, e.g., install dependencies),COPY(bring files into the image),EXPOSE(declare the listening port),CMD(the default command run when the container starts).docker build -t myapp .builds an image (an immutable template), anddocker run myappstarts a container (a running instance) from that image. - A build caches layers per instruction. Putting the
COPYof frequently-changing app code later and the rarely-changing dependency install (COPY requirements.txt->RUN pip install) earlier avoids reinstalling dependencies on every code change and speeds builds. Reversing the order invalidates the cache every time and slows it. Also mind the timing difference betweenRUN(build time) andCMD(start time).
Most-tested: CI = auto build+test per integration, CD = auto release/deploy after tests pass; if test is red, do not deploy (the gate); Dockerfile: FROM/RUN/COPY/EXPOSE/CMD; build -> image (immutable template) -> run -> container (running instance); RUN = build time, CMD = start time; put rarely-changing dependencies earlier and the frequently-changing code COPY later to use the cache. Questions come as reading/diagnosing a presented pipeline or Dockerfile.
Review the following Dockerfile: after FROM python:3.12-slim it has COPY . /app (copy the whole project), then RUN pip install -r /app/requirements.txt, and finally CMD ["python", "/app/main.py"]. It works, but a complaint arrives that every build is slow. The problem is placing COPY . /app before the dependency install. Editing even one line of source changes the COPY . layer, invalidating the cache, so the subsequent pip install starts over every time. The fix is to first COPY requirements.txt /app/ alone, run RUN pip install -r /app/requirements.txt, and only then COPY . /app for the app body—this way the pip install layer stays cached unless dependencies change, and builds after code edits become dramatically faster. Next, another team reports "it shipped to production even though tests were failing." Looking at the pipeline, the deploy job was configured to run in parallel, independent of the test job's outcome. Since the essence of CI/CD is that tests are the gate, fix it so deploy depends on test success and halts on red (do not let it be overridden by hand). The lesson: for both CI/CD and Dockerfiles, the question is not merely "does it run" but reading from the artifact what it guarantees and where it hurts speed or safety. It is not a vocabulary question about what FROM or CI means; the real skill is diagnosing and fixing this artifact's defects.
| Dockerfile instruction | Role | When it runs | Example |
|---|---|---|---|
| `FROM` | Specify the base image | Build time | `FROM python:3.12-slim` |
| `COPY` | Bring files into the image | Build time | `COPY requirements.txt /app/` |
| `RUN` | Run a command during build (install deps, etc.) | Build time | `RUN pip install -r requirements.txt` |
| `EXPOSE` | Declare the listening port | Metadata | `EXPOSE 8080` |
| `CMD` | Default command at container start | Start time | `CMD ["python", "main.py"]` |
Trap: "In a Dockerfile, instruction order is free as long as the result is the same, so placing COPY . /app before or after RUN pip install makes no difference" is wrong—order governs layer-cache effectiveness, and placing the frequently-changing code COPY before the dependency install invalidates the cache every time and slows the build. Also wrong: "deploy may run even when CI is not green"—tests are the gate, and correct design halts deploy when test fails.
4.2.4Section summary
- CI/CD automates
build -> test -> deployand makes tests the gate (do notdeploywhentestis red) - Python unit tests (
unittest/pytest) guard the smallest units by expected output and are CI's pass condition - Dockerfile (
FROM/RUN/COPY/EXPOSE/CMD) ->buildmakes an image ->runmakes a container; put rarely-changing deps earlier and the codeCOPYlater to use the cache
Sign in to track progress — Log in.
Quick check
(just a quick review)Q1. A Dockerfile does `COPY . /app` for the whole project right after `FROM python:3.12-slim`, then `RUN pip install -r /app/requirements.txt`. Even a one-line source change makes every build slow. Which improvement is most appropriate?
Q2. In a CI/CD pipeline, a commit with failing unit tests was deployed to production. Investigation shows the deploy job runs in parallel, independent of the test job's outcome. Which fix is most appropriate?
Q3. A developer runs `docker build -t myapp .` to produce an artifact, then runs `docker run myapp`. Which description of "what build produces" and "what run does" is most appropriate?
Keep track of your progress
The full study guide is free to read. Sign up free to practice with the question bank, track what you have read, review your mistakes, and highlight passages.

