Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docker configuration for exercising Datasette behind Apache mod_proxy #1521

Closed
simonw opened this issue Nov 19, 2021 · 10 comments
Closed

Docker configuration for exercising Datasette behind Apache mod_proxy #1521

simonw opened this issue Nov 19, 2021 · 10 comments
Labels
docker The official Docker image, plus other things related to running Datasette on Docker ops tests

Comments

@simonw
Copy link
Owner

simonw commented Nov 19, 2021

Having a live demo running on Cloud Run that proxies through Apache and uses base_url would be incredibly useful for replicating and debugging this kind of thing. I wonder how hard it is to run Apache and mod_proxy in the same Docker container as Datasette?

Originally posted by @simonw in #1519 (comment)

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

This pattern looks like it can help: https://ahmet.im/blog/cloud-run-multiple-processes-easy-way/ - see example in https://github.com/ahmetb/multi-process-container-lazy-solution

I got that demo working locally like this:

cd /tmp
git clone https://github.com/ahmetb/multi-process-container-lazy-solution
cd multi-process-container-lazy-solution
docker build -t multi-process-container-lazy-solution .
docker run -p 5000:8080 --rm multi-process-container-lazy-solution

I want to use apache2 rather than nginx though. I found a few relevant examples of Apache in Alpine:

@simonw simonw added docker The official Docker image, plus other things related to running Datasette on Docker ops tests labels Nov 19, 2021
@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

I'll get this working on my laptop first, but then I want to get it up and running on Cloud Run - maybe with a GitHub Actions workflow in this repo that re-deploys it on manual execution.

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

From this example: https://github.com/tigelane/dockerfiles/blob/06cff2ac8cdc920ebd64f50965115eaa3d0afb84/Alpine-Apache2/Dockerfile#L25-L31 it looks like running apk add apache2 installs a config file at /etc/apache2/httpd.conf - so one approach is to then modify that file.

# APACHE - Alpine
#################
RUN apk --update add apache2 php5-apache2 && \
    #apk add openrc --no-cache && \
    rm -rf /var/cache/apk/* && \
    sed -i 's/#ServerName www.example.com:80/ServerName localhost/' /etc/apache2/httpd.conf && \
    mkdir -p /run/apache2/

# Upload our files from folder "dist".
COPY dist /var/www/localhost/htdocs

# Manually set up the apache environment variables
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
ENV APACHE_PID_FILE /var/run/apache2.pid

# Execute apache2 on run
########################
EXPOSE 80
ENTRYPOINT ["httpd"]
CMD ["-D", "FOREGROUND"]

I think I'll create my own separate copy and modify that.

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

Made myself this Dockerfile to let me explore a bit:

FROM python:3-alpine

RUN apk add --no-cache \
	apache2

CMD ["sh"]

Then:

% docker run alpine-apache2-sh
% docker run -it alpine-apache2-sh
/ # ls /etc/apache2/httpd.conf
/etc/apache2/httpd.conf
/ # cat /etc/apache2/httpd.conf
#
# This is the main Apache HTTP server configuration file.  It contains the
# configuration directives that give the server its instructions.
...

Copying that into a GIST like so:

docker run -it --entrypoint sh alpine-apache2-sh -c "cat /etc/apache2/httpd.conf" | pbcopy

Gist here: https://gist.github.com/simonw/5ea0db6049192cb9f761fbd6beb3a84a

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

Stripping comments using this StackOverflow recipe: https://unix.stackexchange.com/a/157619

docker run -it --entrypoint sh alpine-apache2-sh \
  -c "cat /etc/apache2/httpd.conf" | sed '/^[[:blank:]]*#/d;s/#.*//'

Result is here: https://gist.github.com/simonw/0a05090df5fcff8e8b3334621fa17976

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

There's a promising looking minimal Apache 2 proxy config here: https://stackoverflow.com/questions/26474476/minimal-configuration-for-apache-reverse-proxy-in-docker-container

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

https://github.com/krallin/tini says:

NOTE: If you are using Docker 1.13 or greater, Tini is included in Docker itself. This includes all versions of Docker CE. To enable Tini, just pass the --init flag to docker run.

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

Got it working! Here's a Dockerfile which runs completely stand-alone (thanks to using the echo $' trick to write out the config files it needs) and successfully serves Datasette behind Apache and mod_proxy:

FROM python:3-alpine

RUN apk add --no-cache \
	apache2 \
	apache2-proxy \
	bash

RUN pip install datasette

ENV TINI_VERSION v0.18.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini
RUN chmod +x /tini

# Append this to the end of the default httpd.conf file
RUN echo $'ServerName localhost\n\
\n\
<Proxy *>\n\
  Order deny,allow\n\
  Allow from all\n\
</Proxy>\n\
\n\
ProxyPass        / http://localhost:9000/\n\
ProxyPassReverse / http://localhost:9000/\n\
Header add X-Proxied-By "Apache2"' >> /etc/apache2/httpd.conf

WORKDIR /app

RUN echo $'#!/usr/bin/env bash\n\
set -e\n\
\n\
httpd -D FOREGROUND &\n\
datasette -p 9000 &\n\
\n\
wait -n' > /app/start.sh

RUN chmod +x /app/start.sh

EXPOSE 80
ENTRYPOINT ["/tini", "--", "/app/start.sh"]

Run it like this:

docker build -t datasette-apache2-proxy .   
docker run -p 5000:80 --rm datasette-apache2-proxy

Then run this to confirm:

~ % curl -i 'http://localhost:5000/-/versions.json'
HTTP/1.1 200 OK
Date: Fri, 19 Nov 2021 19:54:05 GMT
Server: uvicorn
content-type: application/json; charset=utf-8
X-Proxied-By: Apache2
Transfer-Encoding: chunked

{"python": {"version": "3.10.0", "full": "3.10.0 (default, Nov 13 2021, 03:23:03) [GCC 10.3.1 20210424]"}, "datasette": {"version": "0.59.2"}, "asgi": "3.0", "uvicorn": "0.15.0", "sqlite": {"version": "3.35.5", "fts_versions": ["FTS5", "FTS4", "FTS3"], "extensions": {"json1": null}, "compile_options": ["COMPILER=gcc-10.3.1 20210424", "ENABLE_COLUMN_METADATA", "ENABLE_DBSTAT_VTAB", "ENABLE_FTS3", "ENABLE_FTS3_PARENTHESIS", "ENABLE_FTS4", "ENABLE_FTS5", "ENABLE_GEOPOLY", "ENABLE_JSON1", "ENABLE_MATH_FUNCTIONS", "ENABLE_RTREE", "ENABLE_UNLOCK_NOTIFY", "MAX_VARIABLE_NUMBER=250000", "SECURE_DELETE", "THREADSAFE=1", "USE_URI"]}}

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

And this is the version that proxies to a base_url of /foo/bar/:

FROM python:3-alpine

RUN apk add --no-cache \
	apache2 \
	apache2-proxy \
	bash

RUN pip install datasette

ENV TINI_VERSION v0.18.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini
RUN chmod +x /tini

# Append this to the end of the default httpd.conf file
RUN echo $'ServerName localhost\n\
\n\
<Proxy *>\n\
  Order deny,allow\n\
  Allow from all\n\
</Proxy>\n\
\n\
ProxyPass        /foo/bar/ http://localhost:9000/\n\
Header add X-Proxied-By "Apache2"' >> /etc/apache2/httpd.conf

RUN echo $'<a href="/foo/bar/">Datasette</a>' > /var/www/localhost/htdocs/index.html

WORKDIR /app

ADD https://latest.datasette.io/fixtures.db /app/fixtures.db

RUN echo $'#!/usr/bin/env bash\n\
set -e\n\
\n\
httpd -D FOREGROUND &\n\
datasette fixtures.db --setting base_url "/foo/bar/" -p 9000 &\n\
\n\
wait -n' > /app/start.sh

RUN chmod +x /app/start.sh

EXPOSE 80
ENTRYPOINT ["/tini", "--", "/app/start.sh"]

@simonw
Copy link
Owner Author

simonw commented Nov 19, 2021

This configuration works great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docker The official Docker image, plus other things related to running Datasette on Docker ops tests
Projects
None yet
Development

No branches or pull requests

1 participant