Is there some magical way to speed up or debug the...
# general
a
Is there some magical way to speed up or debug the cold start-up times of
execution_mode=venv
for PEXes ran inside Docker images? It's taking around 2 minutes to start inside the Docker image, and only a couple of seconds on my local machine. The Docker image is using Debian 10, and the host machine is running Ubuntu 22.04.
1
w
https://www.pantsbuild.org/docs/reference-pex_binaries#codeexecution_modecode I'm guessing you didn't want to use the zipapp variant, to speed up cold starts?
a
Unfortunately the tool I'm calling in my script appears to be relying on externally installed modules of its own. Setting it to zipapp results in the app crashing with an import error.
To be less cryptic. I'm trying to run
prefect
as a PEX binary. Without
execution_mode=venv
it fails with an error
No module named uvicorn
since it's trying to use Uvicorn from my local interpreter instead of from the PEX.
w
Gotcha... Hmm, so fwiw, I tend to unpack my venvs in the build step when I'm using fastapi/uvicorn, etc. Never heard of prefect. Sooo, what that looks like is .... [searching...]
a
I'm interested to see what that looks like! Sounds like it could be a solution here.
Do you have an example of how you configure this with your FastAPI app? Are you invoking PEX directly, or letting Pants do the heavy lifting?
w
Trying to find an example
🥳 1
Last time I used docker was a long time ago in a computer far away
So, a lazy way was something like this:
Copy code
pex_binary(
  name="apigateway",
  entry_point="apigateway.main",
  dependencies=[":libapigateway"],
  output_path="apps/apigateway/apigateway.pex",
  include_tools=True,
)
Copy code
COPY apigateway.pex /apigateway.pex
RUN PEX_TOOLS=1 /apigateway.pex venv /app
WORKDIR /app
What I do today, is to use
scie
to package up python interpreters and my pexes, and then on first-run, I take the slow first-boot hit, BUT, I guess you could also just eagerly run something like this (which is basically similar to what's on that PEX page)
Copy code
pex_binary(
  name="apigateway-pex",
  dependencies=[
    ":libapigateway", 
    "//:reqs#uvicorn",
    "//:reqs#gunicorn",
  ],
  include_tools=True,
  platforms=["linux-x86_64-cp-311-cp311", "macosx-13.3-arm64-cp-311-cp311",]
)

scie_binary( <-- This is my plugin, not in Pants
  name="apigateway",
  dependencies=[":apigateway-pex"],
  platforms=["linux-x86_64", "macos-aarch64"],
  lift="lift.toml",
)
Copy code
[[lift.commands]]
exe = "{scie.bindings.venv}/venv/bin/uvicorn"
args = ["apigateway.main:app", "--port", "7999"]

[[lift.bindings]]
name = "venv"
description = "Installs the APIGateway into a venv and pre-compiles .pyc"
exe = "#{cpython:python}"
args = [
    "{:apigateway-pex}",
    "venv",
    "--bin-path",
    "prepend",
    "--compile",
    "--rm",
    "all",
    "{scie.bindings}/venv",
]
I think between those examples, and what's already on the
pex
documentation about running gunicorn, and running in Docker - you should be able to build up a pre-compiled image, which has the best of both worlds
a
I'm admittedly a bit out of my depth here, but I'll try to get something working based on your examples. The initial cold-start boot here is just detrimental at this point, but I can't seem to avoid it. I'll try the "lazy" recipe first.
w
Yeah, that was specifically to expose some of the tools and apps to
nginx unit
because it needed stuff in a certain location.
a
Hmm. Doing things the "lazy way", running the resulting project in /app fails with "No module named 'prefect'.
w
Is it in your dependencies?
Again, tool I have zero knowledge about
a
Yup 🙂 And it's in the lib64 folder in the PEX
w
I guess I'd need to see build files, and the corresponding docker setup (note: that docker code I had wasn't done via Pants, it was a couple years ago - just running on the pex directly)
a
Yeah I can understand that. I'll try to whip up something reproducible that I can share!
w
👍
a
Also, thanks for the help so far!
👍 1
Seems like it's not actually the execution mode that's having the most impact. I've set up a repository where I attempt to reproduce the problem. When I set layout="loose" I see that time to start the service doubles. I assume it's going to keep rising as I add more sources and dependencies.
https://github.com/Moortiii/prefect-pants The repository is available here.
b
In case you haven’t seen it, https://blog.pantsbuild.org/optimizing-python-docker-deploys-using-pants/ is a pants-specific version of the pex docs above. I’m not sure it’ll help though (I’ve only skimmed the rest of the thread, sorry if it’s irrelevant).
w
I knew I wasn't imagining that post. I could have sworn Josh had a docker pex blog
In reading, @acoustic-library-86413 this is probably exactly what you want
a
Yes, this looks like exactly what I want! I also think it provides a much more elegant solution for my source-code voluming for hot-reloading than my somewhat creative solution using layout="loose". I'll report back with results.
@broad-processor-92400 I've tried to implement this in the branch "multi-stage-build" in my example repository. However I'm not able to make the entrypoint of the final PEX be "prefect" in this case. In addition, when I run the built PEX inside the image, my local imports such as
from example_service.flows import example_flow
are not working either. Can anyone point me in the right direction here?
b
Thanks for the example, very helpful. I’m not at a computer, so can’t play with it. But… once you’ve used the PEXes to write out a venv, I think the entry point may be unnecessary: the venv bin directory likely includes a prefect script that can be invoked directly (ie have the “which script to call” information encoded in the docker file rather than the pex)
(I’ve never used prefect so this is all made up!) As for local imports, not sure… are those files ending up in the src pex / docker image where you’d expect?
a
I see that my /bin/app has a /bin folder where a Prefect binary is located. I've always been sort of bamboozled by what the "script" in both pex_binary and previously my pyproject.toml for poetry was calling under the hood. Maybe it's just calling out to this binary. If I use the Python interpreter present in /bin/app/bin/python3.11 then I am able to import my first-party sources! Will try to spin things up and see if everything works as expected.
@broad-processor-92400 @wide-midnight-78598 Great success! Everything is up and running now. I also cleaned up the example repository so that it can be used for a blog post - hopefully saving someone else a lot of headache some time in the future.
e
@acoustic-library-86413 maybe I'm missing something, but how is your example repo any different from the existing blog post linked above (https://blog.pantsbuild.org/optimizing-python-docker-deploys-using-pants/).
It seems exactly the same; IOW if you had found and read that on your own, the outcome would be what others helped you towards here.
a
TL;DR: The purpose of the article is to show others how to run Prefect in docker-compose, whilst packaging it with Pants and still being able to volume in your tasks / flows and have fast start up times. @enough-analyst-54434 We've been using Pants for a few short months now and to be honest I still find the behind-the-scenes magic of PEXes to be quite confusing and assume others, especially those new to Pants, do the same. The idea was to just put an article on my personal blog out there for anyone who might happen to Google a combination of "pants, prefect, docker-compose" just to show that it is possible and doesn't need to be a bad experience. The original article is both present in the README and would also be linked from the post. Joshuas article uses an entrypoint which is a file in his own application, rather than a script / binary from a third-party dependency. Just from my own team I have seen that people easily become confused by this, because a lot of the results that (at least I) get when looking for examples invoke PEX directly, rather than through Pants. As an example, these are the top two articles when I google how to serve a Gunicorn application with Pants: • https://stackoverflow.com/questions/53179396/how-run-a-flask-app-packaged-as-pex-in-gunicornhttps://pex.readthedocs.io/en/v2.1.66/recipes.html#gunicorn-and-pex I don't know if that makes sense, and I certainly don't want to be stepping on any toes 🙂
e
Ok. Definitely not stepping on toes, I'm just often confused by the things folks write about / the details that trip them up in a blog-worthy way. Have at it. I'm sure some folks will find this useful.