wide-journalist-72152
02/08/2024, 7:07 PMpants package src/docker/Dockerfile. The full output including the error looks like this:
11:44:08.76 [INFO] Initializing scheduler...
11:44:11.16 [INFO] Scheduler initialized.
11:44:13.90 [INFO] Completed: Building src.python/api-binary-srcs.pex
11:44:14.71 [INFO] Completed: Building docker image hello-api:latest
11:44:14.71 [ERROR] 1 Exception encountered:
Engine traceback:
in `package` goal
ProcessExecutionFailure: Process 'Building docker image hello-api:latest' failed with exit code 1.
stdout:
stderr:
#0 building with "default" instance using docker driver
#1 [internal] load build definition from Dockerfile
#1 transferring dockerfile: 390B done
#1 DONE 0.0s
#2 [internal] load metadata for <http://docker.io/library/python:3.11-slim-bookworm|docker.io/library/python:3.11-slim-bookworm>
#2 DONE 0.0s
#3 [internal] load .dockerignore
#3 transferring context: 2B done
#3 DONE 0.0s
#4 [internal] load build context
#4 transferring context: 1.19MB done
#4 DONE 0.0s
#5 [srcs 1/3] FROM <http://docker.io/library/python:3.11-slim-bookworm|docker.io/library/python:3.11-slim-bookworm>
#5 CACHED
#6 [srcs 2/3] COPY src.python/api-binary-srcs.pex /
#6 DONE 0.0s
#7 [srcs 3/3] RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs
#7 0.287 /usr/local/bin/python3.11: can't open file '//api-binary-srcs.pex': [Errno 2] No such file or directory
#7 ERROR: process "/bin/sh -c PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs" did not complete successfully: exit code: 2
------
> [srcs 3/3] RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs:
0.287 /usr/local/bin/python3.11: can't open file '//api-binary-srcs.pex': [Errno 2] No such file or directory
------
Dockerfile:7
--------------------
5 | FROM python:3.11-slim-bookworm as srcs
6 | COPY src.python/api-binary-srcs.pex /
7 | >>> RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs
8 |
9 | ENTRYPOINT ["/app/pex"]
--------------------
ERROR: failed to solve: process "/bin/sh -c PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs" did not complete successfully: exit code: 2
The "can't open file" error could be related to the fact that api-binary-srcs.pex
is a directory in /dist/src.python rather than a file. The /dist dir looks like the attached screenshot.
Initially I was confused because I thought the error was occurring after the 3rd party dependencies layer build had succeeded which happens by running the same PEX_TOOLS command as the failing one. But looking closely at the full output I can see that's not the case: for some reason the srcs are getting built first even though they come later in the Dockerfile. Perhaps because of the multiple FROM directives?
In any case, I'm probably making a relatively simple Docker error and doubt this is a problem with Pants. I appreciate the input of @happy-kitchen-89482, @wide-midnight-78598 and @better-van-82973 in another thread and hope the public repo makes it very easy for anyone to point out my mistake.
My goals:
• to have a docker image with layers for the infrequently changing 3rd party dependencies and the frequently changing 1st party source to improve image build performance.
• I would like not to compile the pex into .pyc since the small performance benefit in my case doesn't seem worth the cost of not having a deterministic, reproducible build. But if it's easy to set the project up so it's trivial to switch between compiled and uncompiled pex, that's great and I'll gladly document it.
• The container is to run on AWS Fargate with a load balancer handling scaling at the container level so I'd like the container to run plain uvicorn without gunicorn. I think this would be similar for folks deploying FastAPI on Kubernetes.wide-midnight-78598
02/08/2024, 7:11 PM# 3rd party deps
pex_binary(
name="api-binary-deps",
environment="docker_linux",
layout="packed",
execution_mode="venv",
include_sources=False,
include_tools=True,
)
# 1st party srcs
pex_binary(
name="api-binary-srcs",
entry_point="api.main",
environment="docker_linux",
layout="packed",
execution_mode="venv",
include_requirements=False,
include_tools=True,
)wide-journalist-72152
02/08/2024, 7:16 PMwide-midnight-78598
02/08/2024, 7:17 PMwide-midnight-78598
02/08/2024, 7:17 PMwide-midnight-78598
02/08/2024, 7:18 PMwide-midnight-78598
02/08/2024, 7:19 PMwide-midnight-78598
02/08/2024, 7:19 PMbetter-van-82973
02/08/2024, 7:20 PMwide-midnight-78598
02/08/2024, 7:20 PMwide-midnight-78598
02/08/2024, 7:21 PMwide-midnight-78598
02/08/2024, 7:24 PMFROM statements - first two are cached, third is the one we run where everything was pulled together
# Following instructions here for reduced startup time and smaller footprint
# <https://pex.readthedocs.io/en/latest/recipes.html?ref=blog.pantsbuild.org#pex-app-in-a-container>
# There are still some more optimizations that could be done, see below:
# <https://blog.pantsbuild.org/optimizing-python-docker-deploys-using-pants/>
FROM python:3.11-slim as deps
COPY backend.admin/adminapi-pex.pex /api.pex
RUN PEX_TOOLS=1 /usr/local/bin/python3.11 /api.pex venv --scope=deps --compile /bin/app
FROM python:3.11-slim as srcs
COPY backend.admin/adminapi-pex.pex /api.pex
RUN PEX_TOOLS=1 /usr/local/bin/python3.11 /api.pex venv --scope=srcs --compile /bin/app
FROM python:3.11-slim
COPY --from=deps /bin/app /bin/app
COPY --from=srcs /bin/app /bin/app
# Using the same entrypoint as the Azure App Service variant
# Look into the number of cores
EXPOSE 8000
ENTRYPOINT ["/bin/app/bin/gunicorn", "admin.main:app", "--bind=0.0.0.0", "--timeout", "600", "--forwarded-allow-ips=*", "-k", "uvicorn.workers.UvicornWorker", "-w", "1"]
BUILD
python_sources(
name="libadminapi",
sources=["**/*.py", "!*_test.py"],
dependencies=[
"//:reqs#aiohttp",
"//:reqs#psycopg2-binary",
"//:reqs#python-multipart",
],
)
pex_binary(
name="adminapi-pex",
dependencies=[
":libadminapi",
"//:reqs#uvicorn",
"//:reqs#gunicorn",
],
include_tools=True,
platforms=[
"linux-x86_64-cp-311-cp311",
"macosx-13.3-arm64-cp-311-cp311",
"macosx-13.3-x86_64-cp-311-cp311",
],
)
docker_image(
name="dockerized_admin",
image_tags=["latest"],
registries=["redacted.azurecr.io"],
repository="adminapi",
skip_push=True,
)wide-midnight-78598
02/08/2024, 7:25 PMwide-midnight-78598
02/08/2024, 7:26 PMskip_push because I'm pushing via some other mechanism that isn't worth discussing.wide-midnight-78598
02/08/2024, 7:27 PMFROM python:3.11-slim
COPY --from=deps /bin/app /bin/app
COPY --from=srcs /bin/app /bin/app
Then I use gunicorn and uvicorn to run on Azure's crappy infrawide-midnight-78598
02/08/2024, 7:28 PMscie files, and custom interpreters and whatever - but I think this is a good 80/20 above
Also, I could have sworn we have something like that in our docs somewhere, rather than needing to jump between websites
This gets asked a decent amountwide-midnight-78598
02/08/2024, 7:36 PMRUN PEX_TOOLS=1 /usr/local/bin/python3.11 /api-binary-deps.pex venv --scope=deps
FROM python:3.11-slim-bookworm as srcs
COPY src.python/api-binary-srcs.pex /
RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs
Any reason why the deps has a slash prefix, and the srcs doesnt? Intentional?wide-midnight-78598
02/08/2024, 7:37 PMwide-journalist-72152
02/08/2024, 7:38 PMas deps and as srcs. Are those like keywords that somehow filter the build context to include only 3rd party in the first case and only 1st party in the second? I can't find documentation on this aside from the sample code here. (But hey, I'll try it like a blind man being nudged away from the curb!)
I was borrowing from @better-van-82973’s example there where he didn't seem to need that explicit recombination: https://pantsbuild.slack.com/archives/C046T6T9U/p1707342010071579?thread_ts=1690550728.554309&cid=C046T6T9Uwide-journalist-72152
02/08/2024, 7:38 PMwide-midnight-78598
02/08/2024, 7:39 PMbetter-van-82973
02/08/2024, 7:40 PM/ instead of to a dedicated path:
-COPY src.python/api-binary-deps.pex /
-RUN PEX_TOOLS=1 /usr/local/bin/python3.11 /api-binary-deps.pex venv --scope=deps
+
+COPY src.python/api-binary-deps.pex /api-binary-deps.pex
+RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-deps.pex venv --scope=deps --compile /app
FROM python:3.11-slim-bookworm as srcs
-COPY src.python/api-binary-srcs.pex /
-RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs
+COPY src.python/api-binary-srcs.pex /api-binary-srcs.pex
+RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs --compile /app
(You can ignore the --compile restored at the end, that was just for me to verify that that worked too)wide-midnight-78598
02/08/2024, 7:41 PMPresumably my lack of understanding of theandas deps.as srcs
Are those like keywords that somehow filter the build context to
include only 3rd party in the first case and only 1st party in the
second?Yeah, this is getting a bit cargo cult-y Docker's multi-stage, named layers https://docs.docker.com/build/building/multi-stage/#name-your-build-stages And then pex's use of
--scope to split out deps and application code, for better layers
https://pex.readthedocs.io/en/latest/recipes.html#pex-app-in-a-containerwide-midnight-78598
02/08/2024, 7:42 PMI managed to get your Dockerfile to work with a couple minor changes - I
think what is actually breaking this is copying your PEX files toI was wondering about this too - I hadn't had a chance to compile, but I noticed that it wasn't pointed anywhere in particular.instead of to a dedicated path:/
better-van-82973
02/08/2024, 7:46 PMwide-journalist-72152
02/08/2024, 7:59 PMI think what is actually breaking this is copying your PEX files toAh thanks for that! It takes care of the error mentioned above. But it doesn't fix the docker build for me for some reason, just moves me to the next error:instead of to a dedicated path:/
> [srcs 3/3] RUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs:
0.993 [--non-hermetic-scripts]
0.993 [--rm {pex,all}]
0.993 [--emit-warnings]
0.993 [--pex-root PEX_ROOT]
0.993 [--disable-cache]
0.993 [--cache-dir CACHE_DIR]
0.993 [--tmpdir TMPDIR]
0.993 [--rcfile RC_FILE]
0.993 PATH
0.993 PEX_TOOLS=1 ./api-binary-srcs.pex venv: error: the following arguments are required: PATHwide-midnight-78598
02/08/2024, 8:00 PMRUN PEX_TOOLS=1 /usr/local/bin/python3.11 api-binary-srcs.pex venv --scope=srcs /app ?
@wide-journalist-72152 Did you add the /app at the end?wide-journalist-72152
02/08/2024, 8:07 PMFile "/app/lib/python3.11/site-packages/api/main.py", line 1, in <module>
from fastapi import FastAPI
ModuleNotFoundError: No module named 'fastapi'wide-midnight-78598
02/08/2024, 8:08 PMbetter-van-82973
02/08/2024, 8:08 PMpex_binary(
name="api-binary-deps",
# You need an entry_point or something here so Pants knows what dependencies you need
environment="docker_linux",
layout="packed",
execution_mode="venv",
include_sources=False,
include_tools=True,
)wide-midnight-78598
02/08/2024, 8:09 PMpex_binary(
name="adminapi-pex",
dependencies=[
":libadminapi",
"//:reqs#uvicorn",
"//:reqs#gunicorn",
],
But i guess it's different with the dual split pexes?better-van-82973
02/08/2024, 8:10 PMpex_binary(
name="binary-deps",
entry_point="main.py",
dependencies=[
":lib",
"api/src/py/api/scripts:lib",
],
layout="packed",
environment="linux_docker",
execution_mode="venv",
include_sources=False,
include_tools=True,
)wide-journalist-72152
02/08/2024, 8:17 PM":lib",
"api/src/py/api/scripts:lib",better-van-82973
02/08/2024, 8:17 PMpython_sources targets which are used for dependency inference - the dependencies that get packaged into the deps PEX are the dependencies of source files in those directories.
:lib refers to sources in the same directory as the BUILD filewide-journalist-72152
02/08/2024, 9:03 PMpython_sources target I want the api-binary-deps to use for dependency inference is the same directory as the BUILD file, so I gave the name "lib" to the python_sources in that BUILD file and added that as a dependency to the api-binary-deps pex_binary target like so:
python_sources(
name="lib"
)
# 3rd party deps
pex_binary(
name="api-binary-deps",
environment="docker_linux",
dependencies=[
":lib",
],
layout="packed",
execution_mode="venv",
include_sources=False,
include_tools=True,
)
# 1st party srcs
pex_binary(
name="api-binary-srcs",
entry_point="api.main",
environment="docker_linux",
layout="packed",
execution_mode="venv",
include_requirements=False,
include_tools=True,
)
That didn't seem to do anything: still ModuleNotFoundError on the first import. I shouldn't need to add dependencies to the python_sources in this case since Pants can infer that from the imports, right? What am I missing?wide-journalist-72152
02/08/2024, 9:06 PMbetter-van-82973
02/08/2024, 10:07 PM:lib target refers to Python files in the same directory as the BUILD file - and that’s only an empty __init__.py file: https://github.com/davidbeers/PantsFastApiExample/tree/main/src/python
You’ll have to change that dependency target to point at your app code - will leave that as an exercise for the reader 🙂wide-journalist-72152
02/09/2024, 5:02 PMwide-midnight-78598
02/09/2024, 5:06 PMwide-midnight-78598
02/09/2024, 5:06 PMwide-midnight-78598
02/09/2024, 5:07 PMwide-midnight-78598
02/09/2024, 5:07 PMwide-journalist-72152
02/09/2024, 5:25 PMwide-midnight-78598
02/09/2024, 6:26 PM./bin/gunicorn api.main:app --bind=0.0.0.0 -k uvicorn.workers.UvicornWorker -w 1 after extracting the pexes, and it works fine.
I dont have docker on this machine, so can't test that partbetter-van-82973
02/09/2024, 6:31 PMwide-journalist-72152
02/09/2024, 6:49 PMdocker run -p 80:8000 hello-api it works!wide-midnight-78598
02/09/2024, 6:50 PMwide-journalist-72152
02/09/2024, 6:51 PMwide-midnight-78598
02/09/2024, 6:51 PMwide-journalist-72152
02/09/2024, 6:55 PMwide-midnight-78598
02/09/2024, 6:59 PMwide-midnight-78598
02/09/2024, 7:00 PM