Patchwork docker: add Docker files for running an Apache mod_wsgi server

login
register
mail settings
Submitter Gregory Szorc
Date Nov. 12, 2014, 4:37 a.m.
Message ID <09160f191fc370d779fe.1415767064@3.1.168.192.in-addr.arpa>
Download mbox | patch
Permalink /patch/6685/
State Superseded
Commit fd5247a88e6369031ec9ea159b0be228ba5bad43
Headers show

Comments

Gregory Szorc - Nov. 12, 2014, 4:37 a.m.
# HG changeset patch
# User Gregory Szorc <gregory.szorc@gmail.com>
# Date 1415766730 28800
#      Tue Nov 11 20:32:10 2014 -0800
# Node ID 09160f191fc370d779fea0c627dfe01240cefc83
# Parent  18cc87e4375afaeb5986ef9e941854cefa893759
docker: add Docker files for running an Apache mod_wsgi server

I frequently find myself wanting to run hgweb in a production-like
environment, with a real HTTP server and multiple WSGI workers.

This patch introduces a Docker environment for running Mercurial
under Apache + mod_wsgi. With just a few command executions, it is
possible to spin up a Docker container running hgweb.

The Docker environment is designed with customization in mind. With no
options, we clone Mercurial from upstream and run @ with a multi-repo
hgweb setup that allows pushes. An empty repository is created. You can
push your favorite repository to it and test things.

It is possible to customize the hgweb WSGI and config files if you wish
to stray from the defaults.

For Mercurial developers, it is possible to mount your local Mercurial
checkout directory in the container. This means that you can make local
changes and fire up an Apache WSGI environment within seconds to test
those changes. (This is the primary use case I developed this patch
for.)

You can also employ data volume magic to have persistent repositories in
the container.

All of this is documented in the README.rst.

Is the container and environment perfect? No. But you have to start
somewhere. I'm already using this environment to examine memory and
performance behavior of Mercurial when concurrently serving multiple
clones. I find this environment extremely useful and I feel Mercurial
developers will enjoy this new tool in their toolbelt.
Brendan Cully - Nov. 12, 2014, 5:58 p.m.
I like it.

Can I ask why you need to apt-get install mercurial, when you appear
to be either using the user-supplied source tree or cloning from
selenic?

It might be nice to arrange to have the apache logs written into the
host, perhaps with another VOLUME argument.

I also worry vaguely that the default pushable repo is a bit of a
security risk -- it might make sense to leave those settings out so
that you get hg serve defaults (I assume that per-repo settings still
work to override this).

On Tuesday, 11 November 2014 at 20:37, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1415766730 28800
> #      Tue Nov 11 20:32:10 2014 -0800
> # Node ID 09160f191fc370d779fea0c627dfe01240cefc83
> # Parent  18cc87e4375afaeb5986ef9e941854cefa893759
> docker: add Docker files for running an Apache mod_wsgi server
> 
> I frequently find myself wanting to run hgweb in a production-like
> environment, with a real HTTP server and multiple WSGI workers.
> 
> This patch introduces a Docker environment for running Mercurial
> under Apache + mod_wsgi. With just a few command executions, it is
> possible to spin up a Docker container running hgweb.
> 
> The Docker environment is designed with customization in mind. With no
> options, we clone Mercurial from upstream and run @ with a multi-repo
> hgweb setup that allows pushes. An empty repository is created. You can
> push your favorite repository to it and test things.
> 
> It is possible to customize the hgweb WSGI and config files if you wish
> to stray from the defaults.
> 
> For Mercurial developers, it is possible to mount your local Mercurial
> checkout directory in the container. This means that you can make local
> changes and fire up an Apache WSGI environment within seconds to test
> those changes. (This is the primary use case I developed this patch
> for.)
> 
> You can also employ data volume magic to have persistent repositories in
> the container.
> 
> All of this is documented in the README.rst.
> 
> Is the container and environment perfect? No. But you have to start
> somewhere. I'm already using this environment to examine memory and
> performance behavior of Mercurial when concurrently serving multiple
> clones. I find this environment extremely useful and I feel Mercurial
> developers will enjoy this new tool in their toolbelt.
> 
> diff --git a/contrib/docker-apache-server/Dockerfile b/contrib/docker-apache-server/Dockerfile
> new file mode 100644
> --- /dev/null
> +++ b/contrib/docker-apache-server/Dockerfile
> @@ -0,0 +1,24 @@
> +FROM debian:wheezy
> +
> +ENV DEBIAN_FRONTEND noninteractive
> +ENV WSGI_PROCESSES 4
> +ENV WSGI_THREADS 8
> +ENV WSGI_MAX_REQUESTS 100000
> +
> +EXPOSE 80
> +VOLUME ["/var/hg/htdocs", "/var/hg/repos"]
> +
> +RUN apt-get update && apt-get -y install mercurial libapache2-mod-wsgi \
> +    python-dev
> +
> +# Install our own Apache site.
> +RUN a2dissite 000-default
> +ADD vhost.conf /etc/apache2/sites-available/hg
> +RUN a2ensite hg
> +
> +ADD hgwebconfig /defaulthgwebconfig
> +
> +ADD entrypoint.sh /entrypoint.sh
> +ENTRYPOINT ["/entrypoint.sh"]
> +
> +CMD ["/usr/sbin/apache2", "-DFOREGROUND"]
> diff --git a/contrib/docker-apache-server/README.rst b/contrib/docker-apache-server/README.rst
> new file mode 100644
> --- /dev/null
> +++ b/contrib/docker-apache-server/README.rst
> @@ -0,0 +1,134 @@
> +====================
> +Apache Docker Server
> +====================
> +
> +This directory contains code for running a Mercurial hgweb server via
> +mod_wsgi with the Apache HTTP Server inside a Docker container.
> +
> +.. important::
> +
> +   This container is intended for testing purposes only: it is
> +   **not** meant to be suitable for production use.
> +
> +Building Image
> +==============
> +
> +The first step is to build a Docker image containing Apache and mod_wsgi::
> +
> +  $ docker build -t hg-apache .
> +
> +Running the Server
> +==================
> +
> +You can run the server::
> +
> +  $ docker run -rm -it hg-apache
> +
> +You should see some start-up actions (including a Mercurial install)
> +and some output saying Apache has started.
> +
> +The HTTP server is bound to port 80, but it isn't accessible outside
> +the Docker container. That's not very exciting.
> +
> +To make the HTTP server accessible outside the container, you'll need
> +to publish the port on the host machine::
> +
> +  $ docker run -rm -it -p 8000:80 hg-apache
> +
> +Now if you load ``http://localhost:8000/`` (or whatever interface Docker
> +is using), you should see hgweb running!
> +
> +For your convenience, we've created an empty repository available at
> +``/repo``. Feel free to populate it with ``hg push``.
> +
> +Customizing the Server
> +======================
> +
> +By default, the Docker container runs an up-to-date build of the @
> +bookmark from upstream Mercurial. It installs its own hgweb config and
> +set of repositories. It uses some reasonable defaults for mod_wsgi.
> +
> +Customizing the WSGI Dispatcher And Mercurial Config
> +----------------------------------------------------
> +
> +By default, the Docker environment installs a custom ``hgweb.wsgi``
> +file (based on the example in ``contrib/hgweb.wsgi``). The file
> +is installed into ``/var/hg/htdocs//hgweb.wsgi``.
> +
> +A default hgweb configuration file is also installed. The ``hgwebconfig``
> +file from this directory is installed into ``/var/hg/htdocs/config``.
> +
> +You have a few options for customizing these files.
> +
> +The simplest is to hack up ``hgwebconfig`` and ``entrypoint.sh`` in
> +this directory and to rebuild the Docker image. This has the downside
> +that the Mercurial working copy is modified and you may accidentally
> +commit unwanted changes.
> +
> +The next simplest is to copy this directory somewhere, make your changes,
> +then rebuild the image. No working copy changes involved.
> +
> +The preferred solution is to mount a host file into the container and
> +overwrite the built-in defaults.
> +
> +For example, say we create a custom hgweb config file in ``~/hgweb``. We
> +can start the container like so to install our custom config file::
> +
> +  $ docker run --rm -it -v ~/hgweb:/var/hg/htdocs/config -p 8000:80 hg-apache
> +
> +You can do something similar to install a custom WSGI dispatcher::
> +
> +  $ docker run --rm --it -v ~/hgweb.wsgi:/var/hg/htdocs/hgweb.wsgi -p 8000:80 hg-apache
> +
> +Managing Repositories
> +---------------------
> +
> +Repositories are served from ``/var/hg/repos`` by default. This directory
> +is configured as a Docker volume. This means you can mount an existing
> +data volume container in the container so repository data is persisted
> +across container invocations. See
> +https://docs.docker.com/userguide/dockervolumes/ for more.
> +
> +Alternatively, if you just want to perform lightweight repository
> +manipulation, open a shell in the container::
> +
> +  $ docker exec -it <container> /bin/bash
> +
> +Then run ``hg init``, etc to manipulate the repositories in ``/var/hg/repos``.
> +
> +Running Mercurial from Local Source
> +-----------------------------------
> +
> +By default, Mercurial is cloned from upstream and the ``@`` bookmark
> +is installed.
> +
> +Mercurial developers may wish to run Mercurial from locally modified
> +source code rather than the upstream source. To do this, mount your
> +local Mercurial checkout under ``/var/hg/source`` in the container.
> +For example::
> +
> +  $ docker run --rm -it -v ~/src/hg:/var/hg/source -p 8000:80 hg-apache
> +
> +.. warning::
> +
> +   If the architecture of the host machine doesn't match that of the
> +   Docker host (e.g. when running Boot2Docker under OS X), Mercurial's
> +   Python C extensions will fail to run. Be sure to ``make clean`` your
> +   local source tree before mounting it in the container.
> +
> +mod_wsgi Configuration Settings
> +-------------------------------
> +
> +mod_wsgi settings can be controlled with the following environment
> +variables.
> +
> +WSGI_PROCESSES
> +   Number of WSGI processes to run.
> +WSGI_THREADS
> +   Number of threads to run in each WSGI process
> +WSGI_MAX_REQUESTS
> +   Maximum number of requests each WSGI process may serve before it is
> +   reaped.
> +
> +See https://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIDaemonProcess
> +for more on these settings.
> diff --git a/contrib/docker-apache-server/entrypoint.sh b/contrib/docker-apache-server/entrypoint.sh
> new file mode 100755
> --- /dev/null
> +++ b/contrib/docker-apache-server/entrypoint.sh
> @@ -0,0 +1,58 @@
> +#!/bin/sh
> +
> +# This script gets executed on container start. Its job is to set up
> +# the Mercurial environment and invoke the server.
> +
> +# Mercurial can be started in two modes.
> +# If the MERCURIAL_SOURCE environment variable is set and it points to a
> +# Mercurial source directory, we will install Mercurial from that directory.
> +# Otherwise, we download the Mercurial source and install it manually.
> +
> +set -e
> +
> +SOURCE_DIR=/var/hg/source
> +INSTALL_DIR=/var/hg/install
> +REPOS_DIR=/var/hg/repos
> +HTDOCS_DIR=/var/hg/htdocs
> +
> +if [ ! -d ${SOURCE_DIR} ]; then
> +  hg clone http://selenic.com/repo/hg ${SOURCE_DIR}
> +fi
> +
> +cd ${SOURCE_DIR}
> +/usr/bin/python2.7 setup.py install --root=/ --prefix=${INSTALL_DIR} --force
> +cd ..
> +
> +mkdir -p ${HTDOCS_DIR}
> +
> +if [ ! -f ${HTDOCS_DIR}/config ]; then
> +  cp /defaulthgwebconfig ${HTDOCS_DIR}/config
> +fi
> +
> +if [ ! -f ${HTDOCS_DIR}/hgweb.wsgi ]; then
> +  cat >> ${HTDOCS_DIR}/hgweb.wsgi << EOF
> +config = '${HTDOCS_DIR}/config'
> +
> +import sys
> +sys.path.insert(0, '${INSTALL_DIR}/lib/python2.7/site-packages')
> +
> +from mercurial import demandimport
> +demandimport.enable()
> +
> +from mercurial.hgweb import hgweb
> +application = hgweb(config)
> +EOF
> +fi
> +
> +mkdir -p ${REPOS_DIR}
> +
> +if [ ! -d ${REPOS_DIR}/repo ]; then
> +  echo "Creating empty repo"
> +  ${INSTALL_DIR}/bin/hg init ${REPOS_DIR}/repo
> +  chown -R www-data:www-data ${REPOS_DIR}/repo
> +fi
> +
> +. /etc/apache2/envvars
> +
> +echo "Starting Apache HTTP Server"
> +exec "$@"
> diff --git a/contrib/docker-apache-server/hgwebconfig b/contrib/docker-apache-server/hgwebconfig
> new file mode 100644
> --- /dev/null
> +++ b/contrib/docker-apache-server/hgwebconfig
> @@ -0,0 +1,6 @@
> +[paths]
> +/ = /var/hg/repos/**
> +
> +[web]
> +allow_push = *
> +push_ssl = False
> diff --git a/contrib/docker-apache-server/vhost.conf b/contrib/docker-apache-server/vhost.conf
> new file mode 100644
> --- /dev/null
> +++ b/contrib/docker-apache-server/vhost.conf
> @@ -0,0 +1,24 @@
> +# Apache won't be able to resolve its own hostname, so we sneak this
> +# into the global context to silence a confusing-to-user warning on
> +# server start.
> +ServerName hg
> +
> +<VirtualHost *:80>
> +  DocumentRoot /var/hg/htdocs
> +  <Directory />
> +    Options FollowSymLinks
> +    AllowOverride None
> +  </Directory>
> +
> +  SetEnv HGENCODING UTF-8
> +  SetEnv LC_TYPE UTF-8
> +
> +  WSGIDaemonProcess hg processes=${WSGI_PROCESSES} threads=${WSGI_THREADS} maximum-requests=${WSGI_MAX_REQUESTS} user=www-data group=www-data display-name=hg-wsgi
> +  WSGIProcessGroup hg
> +
> +  WSGIScriptAliasMatch ^(.*) /var/hg/htdocs/hgweb.wsgi$1
> +
> +  ErrorLog ${APACHE_LOG_DIR}/error.log
> +  LogLevel warn
> +  CustomLog ${APACHE_LOG_DIR}/access.log combined
> +</VirtualHost>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
>
Gregory Szorc - Nov. 12, 2014, 6:51 p.m.
On 11/12/14 9:58 AM, Brendan Cully wrote:
> I like it.
>
> Can I ask why you need to apt-get install mercurial, when you appear
> to be either using the user-supplied source tree or cloning from
> selenic?

We need a Mercurial to clone from selenic. I suppose we could defer 
installing Mercurial unless we actually need it. Or, we could download a 
tarball and "bootstrap" a Mercurial install. I think these are follow-up 
fodder.

> It might be nice to arrange to have the apache logs written into the
> host, perhaps with another VOLUME argument.

Great idea. I think it should be a follow-up.

> I also worry vaguely that the default pushable repo is a bit of a
> security risk -- it might make sense to leave those settings out so
> that you get hg serve defaults (I assume that per-repo settings still
> work to override this).

As stated in the README, this isn't meant to be used in production. The 
default repo is all about developer convenience so you can spin up a 
container and have somewhere to push changes to without requiring you to 
manually init a repo.

I think a feature to make the creation of the default repo optional or 
better scripting to create repos would be excellent feature additions.

> On Tuesday, 11 November 2014 at 20:37, Gregory Szorc wrote:
>> # HG changeset patch
>> # User Gregory Szorc <gregory.szorc@gmail.com>
>> # Date 1415766730 28800
>> #      Tue Nov 11 20:32:10 2014 -0800
>> # Node ID 09160f191fc370d779fea0c627dfe01240cefc83
>> # Parent  18cc87e4375afaeb5986ef9e941854cefa893759
>> docker: add Docker files for running an Apache mod_wsgi server
>>
>> I frequently find myself wanting to run hgweb in a production-like
>> environment, with a real HTTP server and multiple WSGI workers.
>>
>> This patch introduces a Docker environment for running Mercurial
>> under Apache + mod_wsgi. With just a few command executions, it is
>> possible to spin up a Docker container running hgweb.
>>
>> The Docker environment is designed with customization in mind. With no
>> options, we clone Mercurial from upstream and run @ with a multi-repo
>> hgweb setup that allows pushes. An empty repository is created. You can
>> push your favorite repository to it and test things.
>>
>> It is possible to customize the hgweb WSGI and config files if you wish
>> to stray from the defaults.
>>
>> For Mercurial developers, it is possible to mount your local Mercurial
>> checkout directory in the container. This means that you can make local
>> changes and fire up an Apache WSGI environment within seconds to test
>> those changes. (This is the primary use case I developed this patch
>> for.)
>>
>> You can also employ data volume magic to have persistent repositories in
>> the container.
>>
>> All of this is documented in the README.rst.
>>
>> Is the container and environment perfect? No. But you have to start
>> somewhere. I'm already using this environment to examine memory and
>> performance behavior of Mercurial when concurrently serving multiple
>> clones. I find this environment extremely useful and I feel Mercurial
>> developers will enjoy this new tool in their toolbelt.
>>
>> diff --git a/contrib/docker-apache-server/Dockerfile b/contrib/docker-apache-server/Dockerfile
>> new file mode 100644
>> --- /dev/null
>> +++ b/contrib/docker-apache-server/Dockerfile
>> @@ -0,0 +1,24 @@
>> +FROM debian:wheezy
>> +
>> +ENV DEBIAN_FRONTEND noninteractive
>> +ENV WSGI_PROCESSES 4
>> +ENV WSGI_THREADS 8
>> +ENV WSGI_MAX_REQUESTS 100000
>> +
>> +EXPOSE 80
>> +VOLUME ["/var/hg/htdocs", "/var/hg/repos"]
>> +
>> +RUN apt-get update && apt-get -y install mercurial libapache2-mod-wsgi \
>> +    python-dev
>> +
>> +# Install our own Apache site.
>> +RUN a2dissite 000-default
>> +ADD vhost.conf /etc/apache2/sites-available/hg
>> +RUN a2ensite hg
>> +
>> +ADD hgwebconfig /defaulthgwebconfig
>> +
>> +ADD entrypoint.sh /entrypoint.sh
>> +ENTRYPOINT ["/entrypoint.sh"]
>> +
>> +CMD ["/usr/sbin/apache2", "-DFOREGROUND"]
>> diff --git a/contrib/docker-apache-server/README.rst b/contrib/docker-apache-server/README.rst
>> new file mode 100644
>> --- /dev/null
>> +++ b/contrib/docker-apache-server/README.rst
>> @@ -0,0 +1,134 @@
>> +====================
>> +Apache Docker Server
>> +====================
>> +
>> +This directory contains code for running a Mercurial hgweb server via
>> +mod_wsgi with the Apache HTTP Server inside a Docker container.
>> +
>> +.. important::
>> +
>> +   This container is intended for testing purposes only: it is
>> +   **not** meant to be suitable for production use.
>> +
>> +Building Image
>> +==============
>> +
>> +The first step is to build a Docker image containing Apache and mod_wsgi::
>> +
>> +  $ docker build -t hg-apache .
>> +
>> +Running the Server
>> +==================
>> +
>> +You can run the server::
>> +
>> +  $ docker run -rm -it hg-apache
>> +
>> +You should see some start-up actions (including a Mercurial install)
>> +and some output saying Apache has started.
>> +
>> +The HTTP server is bound to port 80, but it isn't accessible outside
>> +the Docker container. That's not very exciting.
>> +
>> +To make the HTTP server accessible outside the container, you'll need
>> +to publish the port on the host machine::
>> +
>> +  $ docker run -rm -it -p 8000:80 hg-apache
>> +
>> +Now if you load ``http://localhost:8000/`` (or whatever interface Docker
>> +is using), you should see hgweb running!
>> +
>> +For your convenience, we've created an empty repository available at
>> +``/repo``. Feel free to populate it with ``hg push``.
>> +
>> +Customizing the Server
>> +======================
>> +
>> +By default, the Docker container runs an up-to-date build of the @
>> +bookmark from upstream Mercurial. It installs its own hgweb config and
>> +set of repositories. It uses some reasonable defaults for mod_wsgi.
>> +
>> +Customizing the WSGI Dispatcher And Mercurial Config
>> +----------------------------------------------------
>> +
>> +By default, the Docker environment installs a custom ``hgweb.wsgi``
>> +file (based on the example in ``contrib/hgweb.wsgi``). The file
>> +is installed into ``/var/hg/htdocs//hgweb.wsgi``.
>> +
>> +A default hgweb configuration file is also installed. The ``hgwebconfig``
>> +file from this directory is installed into ``/var/hg/htdocs/config``.
>> +
>> +You have a few options for customizing these files.
>> +
>> +The simplest is to hack up ``hgwebconfig`` and ``entrypoint.sh`` in
>> +this directory and to rebuild the Docker image. This has the downside
>> +that the Mercurial working copy is modified and you may accidentally
>> +commit unwanted changes.
>> +
>> +The next simplest is to copy this directory somewhere, make your changes,
>> +then rebuild the image. No working copy changes involved.
>> +
>> +The preferred solution is to mount a host file into the container and
>> +overwrite the built-in defaults.
>> +
>> +For example, say we create a custom hgweb config file in ``~/hgweb``. We
>> +can start the container like so to install our custom config file::
>> +
>> +  $ docker run --rm -it -v ~/hgweb:/var/hg/htdocs/config -p 8000:80 hg-apache
>> +
>> +You can do something similar to install a custom WSGI dispatcher::
>> +
>> +  $ docker run --rm --it -v ~/hgweb.wsgi:/var/hg/htdocs/hgweb.wsgi -p 8000:80 hg-apache
>> +
>> +Managing Repositories
>> +---------------------
>> +
>> +Repositories are served from ``/var/hg/repos`` by default. This directory
>> +is configured as a Docker volume. This means you can mount an existing
>> +data volume container in the container so repository data is persisted
>> +across container invocations. See
>> +https://docs.docker.com/userguide/dockervolumes/ for more.
>> +
>> +Alternatively, if you just want to perform lightweight repository
>> +manipulation, open a shell in the container::
>> +
>> +  $ docker exec -it <container> /bin/bash
>> +
>> +Then run ``hg init``, etc to manipulate the repositories in ``/var/hg/repos``.
>> +
>> +Running Mercurial from Local Source
>> +-----------------------------------
>> +
>> +By default, Mercurial is cloned from upstream and the ``@`` bookmark
>> +is installed.
>> +
>> +Mercurial developers may wish to run Mercurial from locally modified
>> +source code rather than the upstream source. To do this, mount your
>> +local Mercurial checkout under ``/var/hg/source`` in the container.
>> +For example::
>> +
>> +  $ docker run --rm -it -v ~/src/hg:/var/hg/source -p 8000:80 hg-apache
>> +
>> +.. warning::
>> +
>> +   If the architecture of the host machine doesn't match that of the
>> +   Docker host (e.g. when running Boot2Docker under OS X), Mercurial's
>> +   Python C extensions will fail to run. Be sure to ``make clean`` your
>> +   local source tree before mounting it in the container.
>> +
>> +mod_wsgi Configuration Settings
>> +-------------------------------
>> +
>> +mod_wsgi settings can be controlled with the following environment
>> +variables.
>> +
>> +WSGI_PROCESSES
>> +   Number of WSGI processes to run.
>> +WSGI_THREADS
>> +   Number of threads to run in each WSGI process
>> +WSGI_MAX_REQUESTS
>> +   Maximum number of requests each WSGI process may serve before it is
>> +   reaped.
>> +
>> +See https://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIDaemonProcess
>> +for more on these settings.
>> diff --git a/contrib/docker-apache-server/entrypoint.sh b/contrib/docker-apache-server/entrypoint.sh
>> new file mode 100755
>> --- /dev/null
>> +++ b/contrib/docker-apache-server/entrypoint.sh
>> @@ -0,0 +1,58 @@
>> +#!/bin/sh
>> +
>> +# This script gets executed on container start. Its job is to set up
>> +# the Mercurial environment and invoke the server.
>> +
>> +# Mercurial can be started in two modes.
>> +# If the MERCURIAL_SOURCE environment variable is set and it points to a
>> +# Mercurial source directory, we will install Mercurial from that directory.
>> +# Otherwise, we download the Mercurial source and install it manually.
>> +
>> +set -e
>> +
>> +SOURCE_DIR=/var/hg/source
>> +INSTALL_DIR=/var/hg/install
>> +REPOS_DIR=/var/hg/repos
>> +HTDOCS_DIR=/var/hg/htdocs
>> +
>> +if [ ! -d ${SOURCE_DIR} ]; then
>> +  hg clone http://selenic.com/repo/hg ${SOURCE_DIR}
>> +fi
>> +
>> +cd ${SOURCE_DIR}
>> +/usr/bin/python2.7 setup.py install --root=/ --prefix=${INSTALL_DIR} --force
>> +cd ..
>> +
>> +mkdir -p ${HTDOCS_DIR}
>> +
>> +if [ ! -f ${HTDOCS_DIR}/config ]; then
>> +  cp /defaulthgwebconfig ${HTDOCS_DIR}/config
>> +fi
>> +
>> +if [ ! -f ${HTDOCS_DIR}/hgweb.wsgi ]; then
>> +  cat >> ${HTDOCS_DIR}/hgweb.wsgi << EOF
>> +config = '${HTDOCS_DIR}/config'
>> +
>> +import sys
>> +sys.path.insert(0, '${INSTALL_DIR}/lib/python2.7/site-packages')
>> +
>> +from mercurial import demandimport
>> +demandimport.enable()
>> +
>> +from mercurial.hgweb import hgweb
>> +application = hgweb(config)
>> +EOF
>> +fi
>> +
>> +mkdir -p ${REPOS_DIR}
>> +
>> +if [ ! -d ${REPOS_DIR}/repo ]; then
>> +  echo "Creating empty repo"
>> +  ${INSTALL_DIR}/bin/hg init ${REPOS_DIR}/repo
>> +  chown -R www-data:www-data ${REPOS_DIR}/repo
>> +fi
>> +
>> +. /etc/apache2/envvars
>> +
>> +echo "Starting Apache HTTP Server"
>> +exec "$@"
>> diff --git a/contrib/docker-apache-server/hgwebconfig b/contrib/docker-apache-server/hgwebconfig
>> new file mode 100644
>> --- /dev/null
>> +++ b/contrib/docker-apache-server/hgwebconfig
>> @@ -0,0 +1,6 @@
>> +[paths]
>> +/ = /var/hg/repos/**
>> +
>> +[web]
>> +allow_push = *
>> +push_ssl = False
>> diff --git a/contrib/docker-apache-server/vhost.conf b/contrib/docker-apache-server/vhost.conf
>> new file mode 100644
>> --- /dev/null
>> +++ b/contrib/docker-apache-server/vhost.conf
>> @@ -0,0 +1,24 @@
>> +# Apache won't be able to resolve its own hostname, so we sneak this
>> +# into the global context to silence a confusing-to-user warning on
>> +# server start.
>> +ServerName hg
>> +
>> +<VirtualHost *:80>
>> +  DocumentRoot /var/hg/htdocs
>> +  <Directory />
>> +    Options FollowSymLinks
>> +    AllowOverride None
>> +  </Directory>
>> +
>> +  SetEnv HGENCODING UTF-8
>> +  SetEnv LC_TYPE UTF-8
>> +
>> +  WSGIDaemonProcess hg processes=${WSGI_PROCESSES} threads=${WSGI_THREADS} maximum-requests=${WSGI_MAX_REQUESTS} user=www-data group=www-data display-name=hg-wsgi
>> +  WSGIProcessGroup hg
>> +
>> +  WSGIScriptAliasMatch ^(.*) /var/hg/htdocs/hgweb.wsgi$1
>> +
>> +  ErrorLog ${APACHE_LOG_DIR}/error.log
>> +  LogLevel warn
>> +  CustomLog ${APACHE_LOG_DIR}/access.log combined
>> +</VirtualHost>
>> _______________________________________________
>> Mercurial-devel mailing list
>> Mercurial-devel@selenic.com
>> http://selenic.com/mailman/listinfo/mercurial-devel
>>
Mads Kiilerich - Nov. 13, 2014, 3:43 a.m.
TL;DR:

This seems very useful for some very specific use cases. It deserves a 
life somewhere ... but I think it should be something else before it 
would be beneficial to let it live in the Mercurial repo. A life 
elsewhere would perhaps be better.

On 11/12/2014 05:37 AM, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1415766730 28800
> #      Tue Nov 11 20:32:10 2014 -0800
> # Node ID 09160f191fc370d779fea0c627dfe01240cefc83
> # Parent  18cc87e4375afaeb5986ef9e941854cefa893759
> docker: add Docker files for running an Apache mod_wsgi server
>
> I frequently find myself wanting to run hgweb in a production-like
> environment, with a real HTTP server and multiple WSGI workers.

Production-like ... but not production. That seems like a very special 
and narrow use case.

If it only is for testing and with no migration path to production, why 
should anybody than Mercurial developers want to use this?

> This patch introduces a Docker environment for running Mercurial
> under Apache + mod_wsgi. With just a few command executions, it is
> possible to spin up a Docker container running hgweb.

apache+mod_wsgi+multiple workers ... why not just run hg serve? What is 
it you want to test?

If you need this specific combo and can't use hg serve, how relevant 
will this be for people who use a different setup - such as nginx or 
uwsgi? Isn't this too specific to be of general use?

> The Docker environment is designed with customization in mind. With no
> options, we clone Mercurial from upstream and run @ with a multi-repo
> hgweb setup that allows pushes. An empty repository is created. You can
> push your favorite repository to it and test things.
>
> It is possible to customize the hgweb WSGI and config files if you wish
> to stray from the defaults.
>
> For Mercurial developers, it is possible to mount your local Mercurial
> checkout directory in the container. This means that you can make local
> changes and fire up an Apache WSGI environment within seconds to test
> those changes. (This is the primary use case I developed this patch
> for.)

Being a part of Mercurial source tree, but containing functionality for 
downloading source from elsewhere. That sounds very meta ... and not 
like something that belongs in Mercurial.

Clone from upstream sounds like an (odd) addition to a feature that 
would be more well-defined without it. I would suggest leaving it out in 
the first round, perhaps coming back and adding it later. Instead, it 
should use the source tree it is a part of.

For the topic of docker and debian and packaging, shouldn't we start by 
finishing the "packaging plan" for debian packages that bkero worked on 
in Munich? It seems like that would be first starting point and a 
possible building block for solving this use case.

> You can also employ data volume magic to have persistent repositories in
> the container.
>
> All of this is documented in the README.rst.
>
> Is the container and environment perfect? No. But you have to start
> somewhere. I'm already using this environment to examine memory and
> performance behavior of Mercurial when concurrently serving multiple
> clones. I find this environment extremely useful and I feel Mercurial
> developers will enjoy this new tool in their toolbelt.

"An easy way to test hgweb in the current checkout in apache" sounds 
like the primary and most relevant functionality this could give.

I suggest a first mile stone: make a simple command / script for 
launching a locally (system) installed apache+mod_wsgi as the current 
user with the right configuration.

Next milestone: run this with apache+mod_wsgi in docker, mounting the 
current checkout inside docker. Apache, mod_wsgi and python should live 
in docker, Mercurial and all config files, data files and log files 
should live in the real file system. (I would hate it when the input or 
output to my tests disappear.)


Final comment:

The content of this patch seems to implement a fine and quite 
customizable but also quite leaky abstraction layer on top of something 
complex. It is thus questionable how much it abstracts.

I think it would be more valuable to have a simple example for how to 
run hgweb in docker in production.

/Mads
Augie Fackler - Nov. 13, 2014, 1:24 p.m.
On Thu, Nov 13, 2014 at 04:43:29AM +0100, Mads Kiilerich wrote:
> TL;DR:
>
> This seems very useful for some very specific use cases. It deserves a life
> somewhere ... but I think it should be something else before it would be
> beneficial to let it live in the Mercurial repo. A life elsewhere would
> perhaps be better.
>
> On 11/12/2014 05:37 AM, Gregory Szorc wrote:
> ># HG changeset patch
> ># User Gregory Szorc <gregory.szorc@gmail.com>
> ># Date 1415766730 28800
> >#      Tue Nov 11 20:32:10 2014 -0800
> ># Node ID 09160f191fc370d779fea0c627dfe01240cefc83
> ># Parent  18cc87e4375afaeb5986ef9e941854cefa893759
> >docker: add Docker files for running an Apache mod_wsgi server
> >
> >I frequently find myself wanting to run hgweb in a production-like
> >environment, with a real HTTP server and multiple WSGI workers.
>
> Production-like ... but not production. That seems like a very special and
> narrow use case.
>
> If it only is for testing and with no migration path to production, why
> should anybody than Mercurial developers want to use this?

It might also serve as a useful starting point for someone that wants
to automate their hg serving setup with docker, but isn't sure where
to start.

>
> >This patch introduces a Docker environment for running Mercurial
> >under Apache + mod_wsgi. With just a few command executions, it is
> >possible to spin up a Docker container running hgweb.
>
> apache+mod_wsgi+multiple workers ... why not just run hg serve? What is it
> you want to test?

I'm assuming he wants to test under a realistic multi-threaded
environment, rather than the phoney no-concurrency variant that hg
serve gives you.

>
> If you need this specific combo and can't use hg serve, how relevant will
> this be for people who use a different setup - such as nginx or uwsgi? Isn't
> this too specific to be of general use?
>
> >The Docker environment is designed with customization in mind. With no
> >options, we clone Mercurial from upstream and run @ with a multi-repo
> >hgweb setup that allows pushes. An empty repository is created. You can
> >push your favorite repository to it and test things.
> >
> >It is possible to customize the hgweb WSGI and config files if you wish
> >to stray from the defaults.
> >
> >For Mercurial developers, it is possible to mount your local Mercurial
> >checkout directory in the container. This means that you can make local
> >changes and fire up an Apache WSGI environment within seconds to test
> >those changes. (This is the primary use case I developed this patch
> >for.)
>
> Being a part of Mercurial source tree, but containing functionality for
> downloading source from elsewhere. That sounds very meta ... and not like
> something that belongs in Mercurial.
>
> Clone from upstream sounds like an (odd) addition to a feature that would be
> more well-defined without it. I would suggest leaving it out in the first
> round, perhaps coming back and adding it later. Instead, it should use the
> source tree it is a part of.

+1, that seems reasonable.

>
> For the topic of docker and debian and packaging, shouldn't we start by
> finishing the "packaging plan" for debian packages that bkero worked on in
> Munich? It seems like that would be first starting point and a possible
> building block for solving this use case.
>
> >You can also employ data volume magic to have persistent repositories in
> >the container.
> >
> >All of this is documented in the README.rst.
> >
> >Is the container and environment perfect? No. But you have to start
> >somewhere. I'm already using this environment to examine memory and
> >performance behavior of Mercurial when concurrently serving multiple
> >clones. I find this environment extremely useful and I feel Mercurial
> >developers will enjoy this new tool in their toolbelt.
>
> "An easy way to test hgweb in the current checkout in apache" sounds like
> the primary and most relevant functionality this could give.
>
> I suggest a first mile stone: make a simple command / script for launching a
> locally (system) installed apache+mod_wsgi as the current user with the
> right configuration.
>
> Next milestone: run this with apache+mod_wsgi in docker, mounting the
> current checkout inside docker. Apache, mod_wsgi and python should live in
> docker, Mercurial and all config files, data files and log files should live
> in the real file system. (I would hate it when the input or output to my
> tests disappear.)
>
>
> Final comment:
>
> The content of this patch seems to implement a fine and quite customizable
> but also quite leaky abstraction layer on top of something complex. It is
> thus questionable how much it abstracts.
>
> I think it would be more valuable to have a simple example for how to run
> hgweb in docker in production.
>
> /Mads
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel
Mads Kiilerich - Nov. 13, 2014, 2:17 p.m.
On 11/13/2014 02:24 PM, Augie Fackler wrote:
> It might also serve as a useful starting point for someone that wants 
> to automate their hg serving setup with docker, but isn't sure where 
> to start. 

I agree it would be nice to have that. But that do not seem to be what 
this aim at being.

I think the starting point should be something with focus on simplicity 
and where you edit the source / configuration if you want something else.

>>> This patch introduces a Docker environment for running Mercurial
>>> under Apache + mod_wsgi. With just a few command executions, it is
>>> possible to spin up a Docker container running hgweb.
>> apache+mod_wsgi+multiple workers ... why not just run hg serve? What is it
>> you want to test?
> I'm assuming he wants to test under a realistic multi-threaded
> environment, rather than the phoney no-concurrency variant that hg
> serve gives you.

hg serve might be phoney, but AFAIK&CS it is multi-threaded. In what wat 
is it not? I would expect that it would be perfect for reproducing 
concurrency issues.

I see the point in having realistic test too, but a realistic should be 
as close to the production environment as possible - that would be 
something different in each setup, not something we can carry upstream.

/Mads
Augie Fackler - Nov. 13, 2014, 2:18 p.m.
On Thu, Nov 13, 2014 at 9:17 AM, Mads Kiilerich <mads@kiilerich.com> wrote:
>>>> This patch introduces a Docker environment for running Mercurial
>>>> under Apache + mod_wsgi. With just a few command executions, it is
>>>> possible to spin up a Docker container running hgweb.
>>>
>>> apache+mod_wsgi+multiple workers ... why not just run hg serve? What is
>>> it
>>> you want to test?
>>
>> I'm assuming he wants to test under a realistic multi-threaded
>> environment, rather than the phoney no-concurrency variant that hg
>> serve gives you.
>
>
> hg serve might be phoney, but AFAIK&CS it is multi-threaded. In what wat is
> it not? I would expect that it would be perfect for reproducing concurrency
> issues.

I should have been more precise: modwsgi+many workers provides real
_concurrency_ in a way that hg serve cannot - yes, we use threads, but
the GIL means that it's somewhat rare that multiple requests can do
anything useful.

>
> I see the point in having realistic test too, but a realistic should be as
> close to the production environment as possible - that would be something
> different in each setup, not something we can carry upstream.
Mads Kiilerich - Nov. 13, 2014, 2:52 p.m.
On 11/13/2014 03:18 PM, Augie Fackler wrote:
> On Thu, Nov 13, 2014 at 9:17 AM, Mads Kiilerich <mads@kiilerich.com> wrote:
>>>>> This patch introduces a Docker environment for running Mercurial
>>>>> under Apache + mod_wsgi. With just a few command executions, it is
>>>>> possible to spin up a Docker container running hgweb.
>>>> apache+mod_wsgi+multiple workers ... why not just run hg serve? What is
>>>> it
>>>> you want to test?
>>> I'm assuming he wants to test under a realistic multi-threaded
>>> environment, rather than the phoney no-concurrency variant that hg
>>> serve gives you.
>>
>> hg serve might be phoney, but AFAIK&CS it is multi-threaded. In what wat is
>> it not? I would expect that it would be perfect for reproducing concurrency
>> issues.
> I should have been more precise: modwsgi+many workers provides real
> _concurrency_ in a way that hg serve cannot - yes, we use threads, but
> the GIL means that it's somewhat rare that multiple requests can do
> anything useful.

Yes, there is no simple way to make hg serve use forked workers - but it 
seems like it would be simple to add if someone needs it.

But how relevant is it to do testing of concurrency in different forked 
worker processes? These workers will only interact through the file 
system (and by clients ending up on different workers for different http 
requests) and compete over system resources. It will be very rare to 
find tricky issues in such setups and it will scale quite linearly.

I would assume that the relevant and hard test is using worker threads 
where threads manipulate the same data. That is very likely to find errors.

For all realistic setups using threaded workers, it would have to GIL. 
Which will be fine, because it probably will spend most of the time 
waiting for IO or compressing/diffing stuff, hopefully without having 
the GIL. AFAICS, hg serve provide just as much concurrency as any 
'realistic' server setup does.

/Mads
Antoine Pitrou - Nov. 13, 2014, 3:36 p.m.
On Tue, 11 Nov 2014 20:37:44 -0800
Gregory Szorc <gregory.szorc@gmail.com> wrote:
> +
> +  WSGIDaemonProcess hg processes=${WSGI_PROCESSES} threads=${WSGI_THREADS} maximum-requests=${WSGI_MAX_REQUESTS} user=www-data group=www-data display-name=hg-wsgi

*Don't* use maximum-requests! It will corrupt some clones.
See http://bz.selenic.com/show_bug.cgi?id=2595#c30

Regards

Antoine.
Mads Kiilerich - Nov. 13, 2014, 4:45 p.m.
On 11/13/2014 04:36 PM, Antoine Pitrou wrote:
> On Tue, 11 Nov 2014 20:37:44 -0800
> Gregory Szorc <gregory.szorc@gmail.com> wrote:
>> +
>> +  WSGIDaemonProcess hg processes=${WSGI_PROCESSES} threads=${WSGI_THREADS} maximum-requests=${WSGI_MAX_REQUESTS} user=www-data group=www-data display-name=hg-wsgi
> *Don't* use maximum-requests! It will corrupt some clones.
> See http://bz.selenic.com/show_bug.cgi?id=2595#c30

I do not think that bz comment is correct / relevant in this (or that) 
context.

This issue was about incomplete responses and handling of it. 
maximum-requests will shut the application down _between_ requests. I 
see no indication that it should be so stupid that it killed a process 
that still is delivering a response.

hgweb will store all relevant data (if any) to disk at the end of each 
request (or before that). hgweb do not have any relevant data between 
requests and do not have any problem with being killed between requests.

My conclusion: There is no reason to fear maximum-requests with hgweb. 
There is no way it can cause repo corruption or client issues.

/Mads
Antoine Pitrou - Nov. 13, 2014, 4:54 p.m.
On Thu, 13 Nov 2014 17:45:33 +0100
Mads Kiilerich <mads@kiilerich.com> wrote:
> On 11/13/2014 04:36 PM, Antoine Pitrou wrote:
> > On Tue, 11 Nov 2014 20:37:44 -0800
> > Gregory Szorc <gregory.szorc@gmail.com> wrote:
> >> +
> >> +  WSGIDaemonProcess hg processes=${WSGI_PROCESSES} threads=${WSGI_THREADS} maximum-requests=${WSGI_MAX_REQUESTS} user=www-data group=www-data display-name=hg-wsgi
> > *Don't* use maximum-requests! It will corrupt some clones.
> > See http://bz.selenic.com/show_bug.cgi?id=2595#c30
> 
> I do not think that bz comment is correct / relevant in this (or that) 
> context.
> 
> This issue was about incomplete responses and handling of it. 
> maximum-requests will shut the application down _between_ requests. I 
> see no indication that it should be so stupid that it killed a process 
> that still is delivering a response.

Then you should read again.  This problem was witnessed first hand by
several people, and it could be reproduced deterministically as stated
in the issue comments.  hg.python.org has had failed clone issues until
I removed the maximum-requests setting, and the issues haven't
resurfaced after.

Unless mod_wsgi changed its implementations of maximum-requests, that
parameter should definitely not be enabled for hgweb.

Regards

Antoine.
Matt Mackall - Nov. 13, 2014, 7:40 p.m.
On Tue, 2014-11-11 at 20:37 -0800, Gregory Szorc wrote:
> # HG changeset patch
> # User Gregory Szorc <gregory.szorc@gmail.com>
> # Date 1415766730 28800
> #      Tue Nov 11 20:32:10 2014 -0800
> # Node ID 09160f191fc370d779fea0c627dfe01240cefc83
> # Parent  18cc87e4375afaeb5986ef9e941854cefa893759
> docker: add Docker files for running an Apache mod_wsgi server

I do like the general idea of this, but agree that it should be running
the local hg.

Patch

diff --git a/contrib/docker-apache-server/Dockerfile b/contrib/docker-apache-server/Dockerfile
new file mode 100644
--- /dev/null
+++ b/contrib/docker-apache-server/Dockerfile
@@ -0,0 +1,24 @@ 
+FROM debian:wheezy
+
+ENV DEBIAN_FRONTEND noninteractive
+ENV WSGI_PROCESSES 4
+ENV WSGI_THREADS 8
+ENV WSGI_MAX_REQUESTS 100000
+
+EXPOSE 80
+VOLUME ["/var/hg/htdocs", "/var/hg/repos"]
+
+RUN apt-get update && apt-get -y install mercurial libapache2-mod-wsgi \
+    python-dev
+
+# Install our own Apache site.
+RUN a2dissite 000-default
+ADD vhost.conf /etc/apache2/sites-available/hg
+RUN a2ensite hg
+
+ADD hgwebconfig /defaulthgwebconfig
+
+ADD entrypoint.sh /entrypoint.sh
+ENTRYPOINT ["/entrypoint.sh"]
+
+CMD ["/usr/sbin/apache2", "-DFOREGROUND"]
diff --git a/contrib/docker-apache-server/README.rst b/contrib/docker-apache-server/README.rst
new file mode 100644
--- /dev/null
+++ b/contrib/docker-apache-server/README.rst
@@ -0,0 +1,134 @@ 
+====================
+Apache Docker Server
+====================
+
+This directory contains code for running a Mercurial hgweb server via
+mod_wsgi with the Apache HTTP Server inside a Docker container.
+
+.. important::
+
+   This container is intended for testing purposes only: it is
+   **not** meant to be suitable for production use.
+
+Building Image
+==============
+
+The first step is to build a Docker image containing Apache and mod_wsgi::
+
+  $ docker build -t hg-apache .
+
+Running the Server
+==================
+
+You can run the server::
+
+  $ docker run -rm -it hg-apache
+
+You should see some start-up actions (including a Mercurial install)
+and some output saying Apache has started.
+
+The HTTP server is bound to port 80, but it isn't accessible outside
+the Docker container. That's not very exciting.
+
+To make the HTTP server accessible outside the container, you'll need
+to publish the port on the host machine::
+
+  $ docker run -rm -it -p 8000:80 hg-apache
+
+Now if you load ``http://localhost:8000/`` (or whatever interface Docker
+is using), you should see hgweb running!
+
+For your convenience, we've created an empty repository available at
+``/repo``. Feel free to populate it with ``hg push``.
+
+Customizing the Server
+======================
+
+By default, the Docker container runs an up-to-date build of the @
+bookmark from upstream Mercurial. It installs its own hgweb config and
+set of repositories. It uses some reasonable defaults for mod_wsgi.
+
+Customizing the WSGI Dispatcher And Mercurial Config
+----------------------------------------------------
+
+By default, the Docker environment installs a custom ``hgweb.wsgi``
+file (based on the example in ``contrib/hgweb.wsgi``). The file
+is installed into ``/var/hg/htdocs//hgweb.wsgi``.
+
+A default hgweb configuration file is also installed. The ``hgwebconfig``
+file from this directory is installed into ``/var/hg/htdocs/config``.
+
+You have a few options for customizing these files.
+
+The simplest is to hack up ``hgwebconfig`` and ``entrypoint.sh`` in
+this directory and to rebuild the Docker image. This has the downside
+that the Mercurial working copy is modified and you may accidentally
+commit unwanted changes.
+
+The next simplest is to copy this directory somewhere, make your changes,
+then rebuild the image. No working copy changes involved.
+
+The preferred solution is to mount a host file into the container and
+overwrite the built-in defaults.
+
+For example, say we create a custom hgweb config file in ``~/hgweb``. We
+can start the container like so to install our custom config file::
+
+  $ docker run --rm -it -v ~/hgweb:/var/hg/htdocs/config -p 8000:80 hg-apache
+
+You can do something similar to install a custom WSGI dispatcher::
+
+  $ docker run --rm --it -v ~/hgweb.wsgi:/var/hg/htdocs/hgweb.wsgi -p 8000:80 hg-apache
+
+Managing Repositories
+---------------------
+
+Repositories are served from ``/var/hg/repos`` by default. This directory
+is configured as a Docker volume. This means you can mount an existing
+data volume container in the container so repository data is persisted
+across container invocations. See
+https://docs.docker.com/userguide/dockervolumes/ for more.
+
+Alternatively, if you just want to perform lightweight repository
+manipulation, open a shell in the container::
+
+  $ docker exec -it <container> /bin/bash
+
+Then run ``hg init``, etc to manipulate the repositories in ``/var/hg/repos``.
+
+Running Mercurial from Local Source
+-----------------------------------
+
+By default, Mercurial is cloned from upstream and the ``@`` bookmark
+is installed.
+
+Mercurial developers may wish to run Mercurial from locally modified
+source code rather than the upstream source. To do this, mount your
+local Mercurial checkout under ``/var/hg/source`` in the container.
+For example::
+
+  $ docker run --rm -it -v ~/src/hg:/var/hg/source -p 8000:80 hg-apache
+
+.. warning::
+
+   If the architecture of the host machine doesn't match that of the
+   Docker host (e.g. when running Boot2Docker under OS X), Mercurial's
+   Python C extensions will fail to run. Be sure to ``make clean`` your
+   local source tree before mounting it in the container.
+
+mod_wsgi Configuration Settings
+-------------------------------
+
+mod_wsgi settings can be controlled with the following environment
+variables.
+
+WSGI_PROCESSES
+   Number of WSGI processes to run.
+WSGI_THREADS
+   Number of threads to run in each WSGI process
+WSGI_MAX_REQUESTS
+   Maximum number of requests each WSGI process may serve before it is
+   reaped.
+
+See https://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIDaemonProcess
+for more on these settings.
diff --git a/contrib/docker-apache-server/entrypoint.sh b/contrib/docker-apache-server/entrypoint.sh
new file mode 100755
--- /dev/null
+++ b/contrib/docker-apache-server/entrypoint.sh
@@ -0,0 +1,58 @@ 
+#!/bin/sh
+
+# This script gets executed on container start. Its job is to set up
+# the Mercurial environment and invoke the server.
+
+# Mercurial can be started in two modes.
+# If the MERCURIAL_SOURCE environment variable is set and it points to a
+# Mercurial source directory, we will install Mercurial from that directory.
+# Otherwise, we download the Mercurial source and install it manually.
+
+set -e
+
+SOURCE_DIR=/var/hg/source
+INSTALL_DIR=/var/hg/install
+REPOS_DIR=/var/hg/repos
+HTDOCS_DIR=/var/hg/htdocs
+
+if [ ! -d ${SOURCE_DIR} ]; then
+  hg clone http://selenic.com/repo/hg ${SOURCE_DIR}
+fi
+
+cd ${SOURCE_DIR}
+/usr/bin/python2.7 setup.py install --root=/ --prefix=${INSTALL_DIR} --force
+cd ..
+
+mkdir -p ${HTDOCS_DIR}
+
+if [ ! -f ${HTDOCS_DIR}/config ]; then
+  cp /defaulthgwebconfig ${HTDOCS_DIR}/config
+fi
+
+if [ ! -f ${HTDOCS_DIR}/hgweb.wsgi ]; then
+  cat >> ${HTDOCS_DIR}/hgweb.wsgi << EOF
+config = '${HTDOCS_DIR}/config'
+
+import sys
+sys.path.insert(0, '${INSTALL_DIR}/lib/python2.7/site-packages')
+
+from mercurial import demandimport
+demandimport.enable()
+
+from mercurial.hgweb import hgweb
+application = hgweb(config)
+EOF
+fi
+
+mkdir -p ${REPOS_DIR}
+
+if [ ! -d ${REPOS_DIR}/repo ]; then
+  echo "Creating empty repo"
+  ${INSTALL_DIR}/bin/hg init ${REPOS_DIR}/repo
+  chown -R www-data:www-data ${REPOS_DIR}/repo
+fi
+
+. /etc/apache2/envvars
+
+echo "Starting Apache HTTP Server"
+exec "$@"
diff --git a/contrib/docker-apache-server/hgwebconfig b/contrib/docker-apache-server/hgwebconfig
new file mode 100644
--- /dev/null
+++ b/contrib/docker-apache-server/hgwebconfig
@@ -0,0 +1,6 @@ 
+[paths]
+/ = /var/hg/repos/**
+
+[web]
+allow_push = *
+push_ssl = False
diff --git a/contrib/docker-apache-server/vhost.conf b/contrib/docker-apache-server/vhost.conf
new file mode 100644
--- /dev/null
+++ b/contrib/docker-apache-server/vhost.conf
@@ -0,0 +1,24 @@ 
+# Apache won't be able to resolve its own hostname, so we sneak this
+# into the global context to silence a confusing-to-user warning on
+# server start.
+ServerName hg
+
+<VirtualHost *:80>
+  DocumentRoot /var/hg/htdocs
+  <Directory />
+    Options FollowSymLinks
+    AllowOverride None
+  </Directory>
+
+  SetEnv HGENCODING UTF-8
+  SetEnv LC_TYPE UTF-8
+
+  WSGIDaemonProcess hg processes=${WSGI_PROCESSES} threads=${WSGI_THREADS} maximum-requests=${WSGI_MAX_REQUESTS} user=www-data group=www-data display-name=hg-wsgi
+  WSGIProcessGroup hg
+
+  WSGIScriptAliasMatch ^(.*) /var/hg/htdocs/hgweb.wsgi$1
+
+  ErrorLog ${APACHE_LOG_DIR}/error.log
+  LogLevel warn
+  CustomLog ${APACHE_LOG_DIR}/access.log combined
+</VirtualHost>