I have updated the Docker image again. I actually had to embed the knowledge of dealing with a wheel directory into the image as there is no way for a user to override environment variables during the build phase except by not using the onbuild image and creating a manual one again, but want to avoid that for various reasons.
With the very latest images what you can now do is.
1. Build the image.
$ time docker build -t myapp .Sending build context to Docker daemon 574 kB
Sending build context to Docker daemon
Step 0 : FROM grahamdumpleton/mod-wsgi-docker:python-2.7-onbuild
# Executing 2 build triggers
Trigger 0, COPY . /app
Step 0 : COPY . /app
Trigger 1, RUN mod_wsgi-docker-build
Step 0 : RUN mod_wsgi-docker-build
---> Running in fa3adb991c09
-----> Installing dependencies with pip
Collecting Django<1.8 (from -r requirements.txt (line 1))
Downloading Django-1.7.8-py2.py3-none-any.whl (7.4MB)
Installing collected packages: Django
Successfully installed Django-1.7.8
-----> Running .whiskey/action_hooks/build
….
61 static files copied to '/app/example/htdocs'.
---> c72e831182e0
Removing intermediate container cc5bdce4213b
Removing intermediate container fa3adb991c09
Step 1 : CMD --working-directory example --url-alias /static example/htdocs --application-type module example.wsgi
---> Running in 0e70fe87ecc8
---> 5da3bdf7d5e1
Removing intermediate container 0e70fe87ecc8
Successfully built 5da3bdf7d5e1
real 0m15.185s
user 0m0.181s
sys 0m0.014s
So this is like what you would normally be doing now.
2. Run the image but don't start the WSGI server, just enter into the shell by setting the entry point. Using a special shell command here so pick up same environment as will exist when deploying. Also mount the current directory source as well so it actually overrides what is in the image. As it is a built image you can use 'pip freeze' to see that packages were installed.
$ docker run -it --rm --entrypoint mod_wsgi-docker-shell -v `pwd`:/app myapp
Django==1.7.8
mod-wsgi==4.4.13
virtualenv==13.0.3
wheel==0.24.0
3. We are now going to create wheels for the packages that we want installed each time. In this case what is in the requirements.txt file. We will store the wheels in the .whiskey/wheelhouse directory. You might have to create the .whiskey directory if it doesn't already exist.
root@095b6ad1ff25:/app# pip wheel --wheel-dir .whiskey/wheelhouse -r requirements.txt
Collecting Django<1.8 (from -r requirements.txt (line 1))
Downloading Django-1.7.8-py2.py3-none-any.whl (7.4MB)
100% |████████████████████████████████| 7.4MB 60kB/s
Saved ./.whiskey/wheelhouse/Django-1.7.8-py2.py3-none-any.whl
Skipping Django, due to already being wheel.
root@095b6ad1ff25:/app# ls -las .whiskey/wheelhouse/
total 7252
0 drwxr-xr-x 1 1000 staff 102 Jun 20 06:12 .
0 drwxr-xr-x 1 1000 staff 136 Jun 20 06:12 ..
7252 -rw-r--r-- 1 1000 staff 7423353 Jun 20 06:12 Django-1.7.8-py2.py3-none-any.whl
root@095b6ad1ff25:/app# exit
Because we mounted the original source directory, this has actually written it back to your host. Once done we can exit.
From the host you can see that files are now present.
$ ls -las .whiskey/wheelhouse/
total 14504
0 drwxr-xr-x 3 graham staff 102 20 Jun 16:12 .
0 drwxr-xr-x 4 graham staff 136 20 Jun 16:12 ..
14504 -rw-r--r-- 1 graham staff 7423353 20 Jun 16:12 Django-1.7.8-py2.py3-none-any.whl
4. Build the image again. This time it the wheelhouse directory will be copied into the image and the wheel files there will be used to do pip installation.
$ time docker build -t myapp .
Sending build context to Docker daemon 7.998 MB
Sending build context to Docker daemon
Step 0 : FROM grahamdumpleton/mod-wsgi-docker:python-2.7-onbuild
# Executing 2 build triggers
Trigger 0, COPY . /app
Step 0 : COPY . /app
Trigger 1, RUN mod_wsgi-docker-build
Step 0 : RUN mod_wsgi-docker-build
---> Running in aed306a4db30
-----> Detected wheelhouse for pip
-----> Installing dependencies with pip
Collecting Django<1.8 (from -r requirements.txt (line 1))
Installing collected packages: Django
Successfully installed Django-1.7.8
-----> Running .whiskey/action_hooks/build
….
61 static files copied to '/app/example/htdocs'.
---> 9f4681e0d152
Removing intermediate container aed306a4db30
Removing intermediate container 1f9033a7887e
Step 1 : CMD --working-directory example --url-alias /static example/htdocs --application-type module example.wsgi
---> Running in d56665f7d8be
---> 6d7ee644df22
Removing intermediate container d56665f7d8be
Successfully built 6d7ee644df22
real 0m9.264s
user 0m0.256s
sys 0m0.040s
For just Django, this time it is 6 seconds quicker. For packages which need code compilation, would be a lot quicker.
So the general idea is that keep doing builds and will use all the wheels. If you need to update wheels, change the requirements.txt file and run the shell again to generate the wheels for new versions and then builds will pick them up and make things quicker.
So the mounting of a volume is only done when you want to generate the wheels.
When wanting to build a final image for production release, you can remove the wheelhouse directory if want to force everything to be rebuilt from scratch. Also make sure wheelhouse directory is ignored in git repository so don't accidentally check it in to repository.
Graham