#do you have tips to create a small python app image ?
1 messages · Page 1 of 1 (latest)
do you have any more tips ?
also does distroless images are a good idea for production ?
Hi! I have a few suggestions. What does your production image need to run? Web framework, cli run...?
@glossy pawn Hi, didn't saw your message sorry 😅
a all in on webserver + admin cli
also i'm targeting ["linux/amd64", "linux/arm64", "linux/arm/v7"] as platform, but some python libs requires building from source 💀
finally I'm using poetry in the container (to speed up deps installation)
async def build_yauks(
client: dagger.Client,
platform: str,
) -> dagger.Container:
"""Build the containerized version of the app."""
backend_path = get_project_path() / "backend"
frontend_dist = await build_frontend_dist(client)
system_deps = [
"git",
"curl",
"gcc",
"musl-dev",
"jpeg-dev",
"zlib-dev",
"libffi-dev",
"cairo-dev",
"pango-dev",
"gdk-pixbuf-dev",
"cargo",
]
commands = [
["apk", "add"] + system_deps,
["pip", "install", "poetry==1.4.2"],
["poetry", "config", "virtualenvs.create", "false", "--local"],
["poetry", "install", "--only", "main"],
["pip", "uninstall", "-y", "poetry"],
["apk", "del"] + system_deps,
["rm", "-rf", "/var/cache/apk/*"],
["rm", "-rf", "/root/.cache/"],
["rm", "-rf", "/usr/local/src/*"],
]
commands_as_str = " && ".join([" ".join(cmd) for cmd in commands])
ct = (
client.container(platform=dagger.Platform(platform))
.from_("python:3.11.3-alpine3.17")
.with_label("maintainer", "Yauks")
.with_label("platform", platform)
.with_workdir("/app")
.with_env_variable("PYTHONUNBUFFERED", "1")
.with_env_variable("PYTHONDONTWRITEBYTECODE", "1")
.with_env_variable("TZ", "UTC")
.with_env_variable("CARGO_NET_GIT_FETCH_WITH_CLI", "true")
.with_directory(".", client.host().directory(str(backend_path), include=["pyproject.toml", "poetry.toml"]))
.with_exec(["sh", "-c", commands_as_str])
.with_directory(
".", client.host().directory(str(backend_path), exclude=[".venv", ".vscode", ".ruff_cache", "__pycache__"])
)
.with_directory("frontend-dist", frontend_dist)
)
return ct
I want to make an example for this use case, in the meantime here's some quick tips:
- If you turn that into a multi-stage build (https://docs.dagger.io/544174/multistage-build), you don't need to worry about cleaning up;
- Set a
VIRTUAL_ENV=/opt/venvenv var to a known place and add it to thePATH=${VIRTUAL_ENV}/bin:${PATH}, this is all you need to activate and use a virtual environment, Poetry will respect it as well (https://pythonspeed.com/articles/activate-virtualenv-dockerfile/, #1083752578032095263 message) - Add a cache mount to pip's cache so you don't install the whole thing on any change to pyproject and the lock file
- Install poetry globally (root) and the project packages with
--user, this way poetry won't need to be removed from/opt/venvlater - Copy
/opt/venvfrom the build container into the run container - Only add your app's files to the run container, since build only needs pyproject and the lock file
- If you have a
.dockerignorethat you want to reuse, you can probably read it, split the lines and send that toexclude=
Thanks for the tips 
Another thing, a friend complained about it need "too much dependancies where I just want to do docker build"
The only way to solve this and keep dagger is to create an image with dagger inside
Whichh sound bad ?
You mean running the python dagger script in a container?
yes, that sound a bad idea
but it's the only way to allow using docker run, I think ?
hmmm
Error relocating /usr/local/bin/python: Py_BytesMain: symbol not found
copying venv is probably not safe as copying a binarie
Not at all, we do it today in our own CI, using the Go SDK to build all the SDKs (https://github.com/dagger/dagger/blob/793f5073eda34be29092c88ca48fa9e699ed946e/internal/mage/sdk/python.go#L195).
But we have plans to make that easy for your use case:
- Extensions (https://github.com/dagger/dagger/issues/4796)
- Project artifacts (https://github.com/dagger/dagger/issues/4881)
Not sure what you mean. Do you need to run dagger with a docker run?
I've done it quite a lot, so it depends on what you're doing. Here's an old Dockerfile of mine:
# syntax=docker/dockerfile:1
FROM registry.gitlab.com/xxx/fastapi:3.8-build as python-build
COPY pyproject.toml poetry.lock ./
RUN --mount=type=cache,target=/root/.cache poetry-install
FROM registry.gitlab.com/xxx/fastapi:3.8
COPY --from=python-build /opt/venv /opt/venv
COPY --chown=app:app app /app/app
COPY --chown=app:app static /app/static
COPY --chown=app:app templates /app/templates
...
I don't need it, but a friend complained about "creating my own cli/custom script in order to build oci images is reinventing the wheel"
Not quite. There's many advantages of using a dagger SDK here instead of a Dockerfile. But it'll become much easier in the future with what we have planned.
also, for now there is no way to do local dev with hmr right ?
I mean, in the container I would like to run a long running process (uvicorn --reload as example)
but from what I understood volumes binds are not yet(?) a feature
so I must have to export the image (either on fs host or registry) and load/fetch it and use docker directly ?
Yep, dagger is more focused on CI like tasks. There's workarounds you can do but I'm not sure it'll be worth it. I know others have wanted it but I don't know if anyone's done it successfully. I remember doing web dev with my team using docker compose, but the difference in macos vs linux with user permissions in the bind mounts pushed us to use a tool that integrated with compose to synchronize a named volume with the host disk (basically a side car container with a daemon doing the sync). I can't remember the name of the tool.
I would need to know a bit more details on the use case to suggest a better solution, maybe @torn seal and @warped abyss can help you there.
I have a nuxt3 app for the frontend with HMR, I just would like to have hmr working when I edit somes files, same for the backend (fastapi behind)
splitting the build (of dev images, npm install as example) and the (dev, npm run dev) run is probably unavoidable
You can watch the files and trigger a pipeline run that rebuilds the image, deploys to your local docker engine and runs the image for you. So there's a lot of automation you can do in dagger.
I already tried with watchfiles (from samuelcolvin), for the backend it would work not so bad
but doing a full rebuild for the frontend is not optimal, it will rebuild the whole frontend app builde instead of patching only what it need
and I also need to access to the http dev servers from the host
so I must use a trick like some ssh port forwarding or network hack
Can you limit what gets built with cache mounts in appropriate places?