acoustic-library-86413
09/03/2023, 9:47 AMA loose layout PEX is similar to a packed PEX, except that neither the Pex bootstrap code nor the dependency code are packed into zip files, but are instead present as collections of loose files in the directory tree providing different caching and syncing tradeoffs.
I'm trying to mimic hot-reloading for local development by voluming in parts of my source code in Docker to the PEX directory on the container and restart it, instead of rebuilding the entire PEX and then restarting the container. I'm able to volume in the source code and have it present after a restart, but it doesn't seem like Gunicorn is picking up on the changes nonetheless. Is this a gunicorn-specific limitation or a PEX-limitation?
Note that I understand that achieving this wouldn't give me the fine-grained caching and dependency slicing that Pants normally offers, but it's a tradeoff I'm willing to make for near-instant reloads when debugging a stubborn issue locally.acoustic-library-86413
09/03/2023, 10:57 AM--reload
option. Now all that's missing is the ability to run pants package
in multiple terminals concurrently without taking a massive performance hit. Hopefully that will be supported in the future. Perhaps someone who understands the underlying Pants-engine can tell me if this: https://github.com/pantsbuild/pants/issues/7654 issue would solve that problem.refined-addition-53644
09/03/2023, 11:03 AMpants package ::
and it should build all packagable items concurrently while ensuring any dependency on common targets is built first.acoustic-library-86413
09/03/2023, 11:06 AMcustom_build(
ref="my-service",
command="pants package projects/my-service/Dockerfile",
deps=["projects/my-service"],
live_update=[
sync(
"projects/my-service/my_service/",
"/bin/gunicorn/my_service/"
),
restart_container()
]
)
To build and deploy your service in a swarm / k8s environment. Multiple `custom_build`'s would trigger multiple calls to Pants.enough-analyst-54434
09/03/2023, 1:57 PMPEX_INHERIT_PATH=prefer
+ `PYTHONPATH=/volume/mount/of/src`: https://pex.readthedocs.io/en/v2.1.145/api/vars.html#PEX_INHERIT_PATHenough-analyst-54434
09/03/2023, 2:02 PMpex_binary
target with this false: https://www.pantsbuild.org/docs/reference-pex_binary#codeinclude_sourcescode + PEX_EXTRA_SYS_PATH=/volume/mount/of/src
enough-analyst-54434
09/03/2023, 2:03 PMenough-analyst-54434
09/03/2023, 2:06 PMacoustic-library-86413
09/03/2023, 6:14 PMFile "<frozen importlib._bootstrap>", line 1004, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'controller_items'
acoustic-library-86413
09/03/2023, 6:17 PMpex_binary(
name="gunicorn",
script="gunicorn",
dependencies=[
"//:root#gunicorn",
"//:root#uvicorn",
"//:root#psycopg2-binary",
"projects/controller-items/controller_items",
],
output_path="controller-items",
include_sources=False,
)
docker_image(
name="controller-items",
)
The pex_binary
has an entrypoint of gunicorn
in order to be able to point to the module in question.
The relevant compose section looks like this:
controller_items:
image: library/controller-items
command: |
/bin/gunicorn controller_items.asgi:app
--bind 0.0.0.0:${CONTROLLER_ITEMS_PORT}
--reload
--worker-class uvicorn.workers.UvicornWorker
environment:
DB_NAME: ${DB_NAME}
DB_PORT: ${DB_PORT}
DB_USERNAME: ${DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD}
DB_HOST: ${DB_HOST}
ports:
- 8000:${CONTROLLER_ITEMS_PORT}
networks:
- internal
volumes:
- ./projects/controller-items/controller_items:/srv/controller_items
And the Dockerfile for completeness:
FROM python:3.10.12
ENV PYTHONPATH=/srv
COPY controller-items /bin/gunicorn
enough-analyst-54434
09/03/2023, 6:21 PMacoustic-library-86413
09/03/2023, 6:22 PMhappy-kitchen-89482
09/04/2023, 12:24 AMrestartable=True
on a file then pants run path/to/file.py
will auto-reload when you change files (see https://github.com/pantsbuild/example-django/blob/main/helloworld/service/frontend/BUILD for a gunicorn example). To run this in a container though you'd have to volume the repo itself into the container and run pants in it. But possibly worth it while debugging?acoustic-library-86413
09/04/2023, 6:33 AMloose
layout was that it required no changes to the underlying Dockerfile. I have a single template.yml compose-file that is populated with environment variables for dev / staging / prod to ensure the setup is as close to identical as possible. Before migrating to pants I had a small script that parsed the template and converted the docker command to run uvicorn
instead of gunicorn
inside the template, and set up a bind volume to the location on the host. This allowed me to switch back and forth seamlessly without editing the Dockerfile for debugging. I'd love to achieve something similar here as packaging and restarting the container often takes ~15+ seconds.lemon-eye-70471
10/23/2023, 5:27 PMacoustic-library-86413
10/23/2023, 8:46 PMloose
hack that does rebuild on code changes. The main problem is that pants
is not great at running multiple invocations concurrently. However it is excellent at multiprocessing in a single invocation. Therefore with my tilt
use case I need something like the following script:
import os
import subprocess
import time
if not os.path.isfile("build.tmp"):
open("build.tmp", "w").close()
print("Building images...")
# Perform multi-project build here
subprocess.call("pants package ::")
os.remove("build.tmp")
while True:
if os.path.isfile("build.tmp"):
print("Build in progress, waiting...")
time.sleep(1)
else:
break
In order to build all services on startup without taking a major performance hit. I can then use the live_update
instructions for hot reloading / restarting the server where necessary.
It's worth noting that since my original attempt at this https://docs.docker.com/compose/file-watch/ has been introduced, and it would be really interesting to try this out as a tilt-alternative for live reloading.
I also feel like I got off easy by being able to attach a debugger to the process running in Docker using debugpy
, as I was mostly using this functionality for debugging purposes anyway.lemon-eye-70471
10/24/2023, 10:47 AMpants package ::
to rebuild the pex binary in loose format, with the pex binary volume-mounted to the container?acoustic-library-86413
10/24/2023, 11:05 AMlemon-eye-70471
10/24/2023, 11:09 AMlemon-eye-70471
10/24/2023, 11:17 AMacoustic-library-86413
10/24/2023, 11:24 AMhappy-kitchen-89482
10/24/2023, 11:31 AMhappy-kitchen-89482
10/24/2023, 11:31 AMacoustic-library-86413
10/24/2023, 11:57 AMlemon-eye-70471
10/24/2023, 12:59 PMpants package ::
on each change code change in order to build the pex binary that is then synced to the docker container, is this a correct assumption? If so I'm not sure how to trigger the package goal on code change.lemon-eye-70471
10/24/2023, 1:58 PMpex_binary(
name="manage",
entry_point="manage.py:main",
dependencies=[
":src",
],
include_sources=False,
layout='loose',
)
2. Run docker container with two volumes, one mounting the packaged pex folder, and a volume of the host source code mounted to the source directory of the mounted pex folder:
---
version: '3'
services:
web:
image: myapp:latest
ports:
- 8000:8000
volumes:
- ../../dist/myapp/manage.pex:/code/app
- ./my_django_project:/code/app/my_django_project
env_file: .env
command: "python /code/app runserver 0:8000"
I'll put together an example repo sometime soon. Thanks for your help! Also if you see any problems with this setup I'm all ears.acoustic-library-86413
10/25/2023, 10:55 AMCompose Watch
was to help you avoid the volumes, since this allows you to run the same compose-file in production without creating volumes on your swarm host or runner.able-advantage-61346
11/22/2024, 3:28 PMlemon-eye-70471
11/22/2024, 3:38 PM