feat: adopt mitodl/ol-python-base:3.11, BuildKit cache mounts, granian#3655
Conversation
OpenAPI ChangesShow/hide ## Changes for v0.yaml:Unexpected changes? Ensure your branch is up-to-date with |
There was a problem hiding this comment.
Pull request overview
This PR updates the container build/runtime setup for MITx Online by switching to the shared mitodl/ol-python-base:3.11 image, adding BuildKit cache mounts for faster rebuilds, and changing the default runtime command from uWSGI to granian.
Changes:
- Adopt
mitodl/ol-python-base:3.11as the base stage and install only app-specific apt extras. - Add BuildKit cache mounts for both
apt-getanduv syncto speed up iterative builds. - Update the
django-servertarget to rungranian --interface wsgi main.wsgi:applicationon port 8013.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| Dockerfile | Switches to shared base image, adds BuildKit cache mounts, and changes the runtime CMD to granian. |
| apt.txt | Trims/annotates the apt package list to app-specific extras intended for the new base image approach. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # App-specific apt extras; common-core packages are in mitodl/ol-python-base:3.11. | ||
| # Operator tooling (htop, ngrep, screen, etc.) is also kept here for parity | ||
| # with the production image; trim this list if the production image diverges. | ||
| RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ | ||
| --mount=type=cache,target=/var/lib/apt,sharing=locked \ | ||
| apt-get update && \ | ||
| apt-get install -y --no-install-recommends \ | ||
| dnsutils \ | ||
| htop \ | ||
| iputils-ping \ | ||
| less \ | ||
| libcairo2-dev \ | ||
| lsof \ | ||
| nano \ | ||
| ngrep \ | ||
| procps \ | ||
| screen \ | ||
| wget |
| COPY pyproject.toml uv.lock /src/ | ||
| COPY mitol_*.gz /src/ | ||
| RUN chown -R mitodl:mitodl /src | ||
|
|
||
| USER mitodl | ||
| WORKDIR /src |
| EXPOSE 8013 | ||
| ENV PORT=8013 | ||
| CMD ["uwsgi", "uwsgi.ini"] | ||
| CMD ["granian", "--interface", "wsgi", "--host", "0.0.0.0", "--port", "8013", "--workers", "2", "main.wsgi:application"] |
| # BuildKit cache mount keeps the uv download cache across builds. | ||
| RUN --mount=type=cache,target=/opt/uv-cache,uid=1000,gid=1000 \ | ||
| uv sync --frozen --no-install-project --group prod |
5a889b8 to
8f6b3ff
Compare
blarghmatey
left a comment
There was a problem hiding this comment.
Addressing the bot review comments:
/var/mediaownership (comment on line 78): no change needed. The base image (mitodl/ol-python-base:3.11) already runsmkdir -p /src /opt/venv /var/media && chown -R mitodl:mitodl /src /opt/venv /var/media, so the directory is created with the correct ownership before our layers run.apt.txttwo sources of truth: fixed —apt.txthas been deleted; the Dockerfile is now the single source of truth for app-specific packages.ENV PORTignored by granian CMD: fixed — CMD now uses shell formexec granian ... --port ${PORT:-8013} ...so the port can be overridden at runtime.uv sync --group prodinstalls uwsgi/uwsgitop: fixed — removed theproddependency group frompyproject.toml(and dropped--group prodfrom the Dockerfileuv synccall).uv.lockupdated.COPY certs/*.crtfails when no.crtfiles exist: fixed — changed toCOPY --chmod=644 certs/ /usr/local/share/ca-certificates/(copies the whole directory;update-ca-certificatesno-ops if there are no.crtfiles).
8f6b3ff to
4aaefd2
Compare
rhysyngsun
left a comment
There was a problem hiding this comment.
The nginx configuration needs to be updated to route to granian instead of uwsgi.
https://github.com/mitodl/mitxonline/blob/main/config/nginx.conf.erb
|
Is that template still used at all? My recollection is that the .erb config is a Heroku-only artifact that we should just delete. |
|
It is, that's the template used locally for nginx too, I normalized this project to use a single nginx configuration for heroku and localdev so we didn't have drift between the two. |
|
Here's my nginx logs showing that file getting compiled and the resulting 502 when it tries to connect to the upstream via uwsgi: |
|
Fixed in 4e828ba. Replaced |
|
I'm still getting a 502 error because the |
- FROM mitodl/ol-python-base:3.11 replaces the duplicated substrate stages. - App-specific extras (libcairo2-dev, dnsutils, wget, operator tooling) installed with BuildKit --mount=type=cache for apt. - BuildKit --mount=type=cache on uv sync (target=/opt/uv-cache uid=1000). - CMD changed from uwsgi / uwsgi.ini to granian --interface wsgi main.wsgi:application on port 8013. uwsgi.ini is retained for reference but no longer used. - apt.txt trimmed to extras only with comments documenting what moved to the shared base. - Image size (django-server target): 4.09 GB → 2.16 GB (-1.93 GB).
granian speaks HTTP, not the uWSGI binary protocol. Replace the uwsgi_pass block with proxy_pass and standard HTTP proxy headers. Rename NGINX_UWSGI_PASS -> NGINX_UPSTREAM in docker-compose.yml to match the new variable name in the template. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
uwsgi is no longer installed in the image; run-django-dev.sh still called uwsgi.ini which would fail on container start. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
nginx.conf.erb was already switched to proxy_pass http://localhost:8013 but the Procfile still ran uwsgi (which uses a unix socket, not TCP). Without this change the Heroku deployment would fail — nginx routing to port 8013 with nothing listening there. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The deps stage inherited default-groups = "all" from pyproject.toml, causing dev tools (pytest, ruff, ipdb, etc.) to be installed into the production image. Fix: add --no-dev to the production uv sync; add a development stage extending django-server that runs uv sync without --no-dev to layer in the dev group for local docker-compose workflows. Also: use COPY --chown instead of COPY + RUN chown to avoid an extra layer; update docker-compose to target the new development stage. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ffff114 to
b5a96c9
Compare
What are the relevant tickets?
N/A
Description (What does it do?)
Adopts the new shared Python base image, adds BuildKit cache mounts, and migrates from uWSGI to granian (WSGI interface).
Changes:
FROM mitodl/ol-python-base:3.11replaces the duplicated substrate stages. The shared base is defined in https://github.com/mitodl/ol-infrastructure/tree/main/dockerfiles/ol-python-base.--mount=type=cachefor apt.--mount=type=cache,target=/opt/uv-cache,uid=1000,gid=1000onuv syncfor fastuv.lock-bump rebuilds.uwsgi/uwsgi.initogranian --interface wsgi main.wsgi:applicationon port 8013.uwsgi.iniis retained for reference but no longer used by thedjango-server/productiontargets.deployment.yamlis updated in feat: add ol-python-base shared image and migrate local-dev to granian ol-infrastructure#4750.apt.txttrimmed to extras only with comments documenting what moved to the shared base.Image size (django-server target): 4.09 GB → 2.16 GB (−1.93 GB).
How can this be tested?
After mitodl/ol-infrastructure#4750 merges and the Concourse pipeline publishes the base image:
DOCKER_BUILDKIT=1 docker build --target django-server -t mitodl/mitxonline-app:test . docker run --rm mitodl/mitxonline-app:test granian --versionIn the local-dev Tilt environment:
tilt upmitxonline, confirm the webapp comes up healthy under granian and the readiness probe at/health/readinesspasses.Additional Context
granian was already present as a dependency (
granian>=2.5.4,<3in pyproject.toml) but the Dockerfile and deployment were still running uWSGI. This PR completes the migration. Merge order: ol-infrastructure PR first → Concourse pipeline registered → this PR can be merged.