Merge 41006c36a5 into 14779ec4be
This commit is contained in:
commit
afdcd15b24
13 changed files with 228 additions and 46 deletions
|
|
@ -14,10 +14,6 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
|
||||||
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \
|
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \
|
||||||
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
|
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
|
||||||
|
|
||||||
# Install Forego
|
|
||||||
ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego
|
|
||||||
RUN chmod u+x /usr/local/bin/forego
|
|
||||||
|
|
||||||
ENV DOCKER_GEN_VERSION 0.7.3
|
ENV DOCKER_GEN_VERSION 0.7.3
|
||||||
|
|
||||||
RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
|
RUN wget https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
|
||||||
|
|
@ -32,4 +28,3 @@ ENV DOCKER_HOST unix:///tmp/docker.sock
|
||||||
VOLUME ["/etc/nginx/certs"]
|
VOLUME ["/etc/nginx/certs"]
|
||||||
|
|
||||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
CMD ["forego", "start", "-r"]
|
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,6 @@ RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
|
||||||
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \
|
&& sed -i 's/^http {/&\n server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf \
|
||||||
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
|
&& sed -i 's/worker_processes 1/worker_processes auto/' /etc/nginx/nginx.conf
|
||||||
|
|
||||||
# Install Forego
|
|
||||||
ADD https://github.com/jwilder/forego/releases/download/v0.16.1/forego /usr/local/bin/forego
|
|
||||||
RUN chmod u+x /usr/local/bin/forego
|
|
||||||
|
|
||||||
ENV DOCKER_GEN_VERSION 0.7.3
|
ENV DOCKER_GEN_VERSION 0.7.3
|
||||||
|
|
||||||
RUN wget --quiet https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
|
RUN wget --quiet https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-alpine-linux-amd64-$DOCKER_GEN_VERSION.tar.gz \
|
||||||
|
|
@ -29,4 +25,3 @@ ENV DOCKER_HOST unix:///tmp/docker.sock
|
||||||
VOLUME ["/etc/nginx/certs"]
|
VOLUME ["/etc/nginx/certs"]
|
||||||
|
|
||||||
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
ENTRYPOINT ["/app/docker-entrypoint.sh"]
|
||||||
CMD ["forego", "start", "-r"]
|
|
||||||
|
|
|
||||||
2
Procfile
2
Procfile
|
|
@ -1,2 +0,0 @@
|
||||||
dockergen: docker-gen -watch -notify "nginx -s reload" /app/nginx.tmpl /etc/nginx/conf.d/default.conf
|
|
||||||
nginx: nginx
|
|
||||||
|
|
@ -1,7 +1,86 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
###############################################################################
|
||||||
|
#
|
||||||
|
# Signals:
|
||||||
|
# - HUP: reload docker-gen
|
||||||
|
# - USR1: reload nginx
|
||||||
|
#
|
||||||
|
###############################################################################
|
||||||
|
set -u
|
||||||
|
|
||||||
# Warn if the DOCKER_HOST socket does not exist
|
function start_docker_gen {
|
||||||
|
echo "~~~~~ Starting docker-gen ~~~~~"
|
||||||
|
{
|
||||||
|
while true; do
|
||||||
|
docker-gen -watch -notify "kill -USR1 1" /app/nginx.tmpl /etc/nginx/conf.d/default.conf
|
||||||
|
echo "docker-gen exited"
|
||||||
|
echo "~~~~~ Restarting docker-gen ~~~~~~"
|
||||||
|
done
|
||||||
|
} &
|
||||||
|
}
|
||||||
|
|
||||||
|
function start_nginx {
|
||||||
|
echo "~~~~~ Starting nginx ~~~~~"
|
||||||
|
{
|
||||||
|
while true; do
|
||||||
|
nginx
|
||||||
|
echo "nginx exited, checking config..."
|
||||||
|
if nginx -t; then
|
||||||
|
echo "~~~~~ Restarting nginx ~~~~~~"
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
} &
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function reload_nginx {
|
||||||
|
if pgrep nginx >/dev/null; then
|
||||||
|
# nginx is already running
|
||||||
|
echo "~~~~~ Reloading nginx ~~~~~"
|
||||||
|
pkill -HUP nginx
|
||||||
|
else
|
||||||
|
start_nginx
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
function handle_SIGHUP {
|
||||||
|
echo "~~~~~ Signal HUP received ~~~~~"
|
||||||
|
if ! pgrep nginx >/dev/null; then
|
||||||
|
echo "~~~~~ Starting nginx ~~~~~"
|
||||||
|
nginx &
|
||||||
|
fi
|
||||||
|
pkill -HUP docker-gen # forward SIGHUP to docker-gen
|
||||||
|
wait
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_SIGUSR1 {
|
||||||
|
echo "~~~~~ Signal USR1 received ~~~~~"
|
||||||
|
reload_nginx
|
||||||
|
wait
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
# If the user has run provided a command, run it instead
|
||||||
|
if [ $# -ne 0 ]; then
|
||||||
|
exec "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat <<-OEBANNER
|
||||||
|
███╗ ██╗ ██████╗ ██╗███╗ ██╗██╗ ██╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗██╗ ██╗
|
||||||
|
████╗ ██║██╔════╝ ██║████╗ ██║╚██╗██╔╝ ██╔══██╗██╔══██╗██╔═══██╗╚██╗██╔╝╚██╗ ██╔╝
|
||||||
|
██╔██╗ ██║██║ ███╗██║██╔██╗ ██║ ╚███╔╝█████╗██████╔╝██████╔╝██║ ██║ ╚███╔╝ ╚████╔╝
|
||||||
|
██║╚██╗██║██║ ██║██║██║╚██╗██║ ██╔██╗╚════╝██╔═══╝ ██╔══██╗██║ ██║ ██╔██╗ ╚██╔╝
|
||||||
|
██║ ╚████║╚██████╔╝██║██║ ╚████║██╔╝ ██╗ ██║ ██║ ██║╚██████╔╝██╔╝ ██╗ ██║
|
||||||
|
╚═╝ ╚═══╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
|
||||||
|
OEBANNER
|
||||||
|
|
||||||
|
|
||||||
|
# Error if the DOCKER_HOST socket does not exist
|
||||||
if [[ $DOCKER_HOST == unix://* ]]; then
|
if [[ $DOCKER_HOST == unix://* ]]; then
|
||||||
socket_file=${DOCKER_HOST#unix://}
|
socket_file=${DOCKER_HOST#unix://}
|
||||||
if ! [ -S $socket_file ]; then
|
if ! [ -S $socket_file ]; then
|
||||||
|
|
@ -10,13 +89,15 @@ if [[ $DOCKER_HOST == unix://* ]]; then
|
||||||
Typically you should run your jwilder/nginx-proxy with: \`-v /var/run/docker.sock:$socket_file:ro\`
|
Typically you should run your jwilder/nginx-proxy with: \`-v /var/run/docker.sock:$socket_file:ro\`
|
||||||
See the documentation at http://git.io/vZaGJ
|
See the documentation at http://git.io/vZaGJ
|
||||||
EOT
|
EOT
|
||||||
socketMissing=1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If the user has run the default command and the socket doesn't exist, fail
|
|
||||||
if [ "$socketMissing" = 1 -a "$1" = forego -a "$2" = start -a "$3" = '-r' ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$@"
|
trap handle_SIGHUP HUP
|
||||||
|
trap handle_SIGUSR1 USR1
|
||||||
|
trap "exit 0" TERM
|
||||||
|
|
||||||
|
rm /etc/nginx/conf.d/default.conf
|
||||||
|
start_docker_gen
|
||||||
|
wait
|
||||||
|
|
@ -17,7 +17,7 @@ from requests.packages.urllib3.util.connection import HAS_IPV6
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logging.getLogger('backoff').setLevel(logging.INFO)
|
logging.getLogger('backoff').setLevel(logging.INFO)
|
||||||
logging.getLogger('DNS').setLevel(logging.DEBUG)
|
logging.getLogger('DNS').setLevel(logging.INFO)
|
||||||
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN)
|
logging.getLogger('requests.packages.urllib3.connectionpool').setLevel(logging.WARN)
|
||||||
|
|
||||||
CA_ROOT_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt')
|
CA_ROOT_CERTIFICATE = os.path.join(os.path.dirname(__file__), 'certs/ca-root.crt')
|
||||||
|
|
@ -287,7 +287,7 @@ def wait_for_nginxproxy_to_be_ready():
|
||||||
container = containers[0]
|
container = containers[0]
|
||||||
for line in container.logs(stream=True):
|
for line in container.logs(stream=True):
|
||||||
if "Watching docker events" in line:
|
if "Watching docker events" in line:
|
||||||
logging.debug("nginx-proxy ready")
|
logging.info("nginx-proxy ready")
|
||||||
break
|
break
|
||||||
|
|
||||||
def find_docker_compose_file(request):
|
def find_docker_compose_file(request):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# Docker Image running one (or multiple) webservers listening on all given ports from WEB_PORTS environment variable
|
# Docker Image running one (or multiple) webservers listening on all given ports from WEB_PORTS environment variable
|
||||||
|
|
||||||
FROM python:3
|
FROM python:3
|
||||||
|
RUN pip install flask==0.12
|
||||||
COPY ./webserver.py /
|
COPY ./webserver.py /
|
||||||
COPY ./entrypoint.sh /
|
COPY ./entrypoint.sh /
|
||||||
WORKDIR /opt
|
WORKDIR /opt
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,26 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
|
||||||
import os, sys
|
import sys
|
||||||
import http.server
|
from flask import Flask, Response, request
|
||||||
import socketserver
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Handler(http.server.SimpleHTTPRequestHandler):
|
@app.route("/")
|
||||||
def do_GET(self):
|
def root():
|
||||||
|
return Response("I'm %s\n" % os.environ['HOSTNAME'], mimetype="text/plain")
|
||||||
self.send_response(200)
|
|
||||||
self.send_header("Content-Type", "text/plain")
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
if self.path == "/headers":
|
|
||||||
self.wfile.write(self.headers.as_string().encode())
|
@app.route("/headers")
|
||||||
elif self.path == "/port":
|
def headers():
|
||||||
response = "answer from port %s\n" % PORT
|
return Response("".join(["%s: %s\n" % (header, value) for header, value in request.headers.items()]), mimetype="text/plain")
|
||||||
self.wfile.write(response.encode())
|
|
||||||
elif self.path == "/":
|
|
||||||
response = "I'm %s\n" % os.environ['HOSTNAME']
|
@app.route("/port")
|
||||||
self.wfile.write(response.encode())
|
def port():
|
||||||
else:
|
return Response("answer from port %s\n" % PORT, mimetype="text/plain")
|
||||||
self.wfile.write("No route for this path!\n".encode())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
PORT = int(sys.argv[1])
|
PORT = int(sys.argv[1])
|
||||||
socketserver.TCPServer.allow_reuse_address = True
|
app.run(host="0.0.0.0", port=PORT)
|
||||||
httpd = socketserver.TCPServer(('0.0.0.0', PORT), Handler)
|
|
||||||
httpd.serve_forever()
|
|
||||||
|
|
|
||||||
18
test/test_entrypoint/docker-compose.yml
Normal file
18
test/test_entrypoint/docker-compose.yml
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
nginx-proxy:
|
||||||
|
image: jwilder/nginx-proxy:test
|
||||||
|
container_name: nginx-proxy
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
# - ../../docker-entrypoint.sh:/app/docker-entrypoint.sh:ro
|
||||||
|
|
||||||
|
web1:
|
||||||
|
container_name: web1
|
||||||
|
image: web
|
||||||
|
expose:
|
||||||
|
- 81
|
||||||
|
environment:
|
||||||
|
WEB_PORTS: "81"
|
||||||
|
VIRTUAL_HOST: web1.nginx-proxy
|
||||||
60
test/test_entrypoint/test_entrypoint.py
Normal file
60
test/test_entrypoint/test_entrypoint.py
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import re
|
||||||
|
import docker
|
||||||
|
|
||||||
|
|
||||||
|
docker_client = docker.from_env()
|
||||||
|
RE_PGREP_RESPONSE = re.compile("^(?P<pid>\d+)\n", re.MULTILINE)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pid(process_name):
|
||||||
|
pgrep_response = docker_client.containers.get("nginx-proxy").exec_run("pgrep %s" % process_name)
|
||||||
|
pids = []
|
||||||
|
for m in RE_PGREP_RESPONSE.finditer(pgrep_response):
|
||||||
|
pids.append(int(m.group("pid")))
|
||||||
|
assert len(pids) > 0
|
||||||
|
return set(pids)
|
||||||
|
|
||||||
|
|
||||||
|
def kill(process_name):
|
||||||
|
docker_client.containers.get("nginx-proxy").exec_run("pkill %s" % process_name)
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
def assert_reverse_proxy_behavior(nginxproxy):
|
||||||
|
assert "answer from port 81\n" == nginxproxy.get("http://web1.nginx-proxy/port").text
|
||||||
|
docker_client.containers.get("web1").stop()
|
||||||
|
sleep(2)
|
||||||
|
assert nginxproxy.get("http://web1.nginx-proxy/").status_code == 503
|
||||||
|
docker_client.containers.get("web1").start()
|
||||||
|
sleep(2)
|
||||||
|
assert "answer from port 81\n" == nginxproxy.get("http://web1.nginx-proxy/port").text
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
def test_dockergen_is_restarted_when_killed(docker_compose, nginxproxy):
|
||||||
|
assert_reverse_proxy_behavior(nginxproxy)
|
||||||
|
first_pids = get_pid("docker-gen")
|
||||||
|
kill("docker-gen")
|
||||||
|
assert_reverse_proxy_behavior(nginxproxy)
|
||||||
|
second_pids = get_pid("docker-gen")
|
||||||
|
assert first_pids != second_pids
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_is_restarted_when_killed(docker_compose, nginxproxy):
|
||||||
|
assert_reverse_proxy_behavior(nginxproxy)
|
||||||
|
first_pids = get_pid("nginx")
|
||||||
|
kill("nginx")
|
||||||
|
assert_reverse_proxy_behavior(nginxproxy)
|
||||||
|
second_pids = get_pid("nginx")
|
||||||
|
assert len(first_pids.intersection(second_pids)) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_term_signal_exits_nginxproxy(docker_compose):
|
||||||
|
assert "running" == docker_compose.containers.get("nginx-proxy").status
|
||||||
|
print docker_compose.containers.get("nginx-proxy").exec_run("kill -TERM 1")
|
||||||
|
sleep(2)
|
||||||
|
assert "exited" == docker_compose.containers.get("nginx-proxy").status
|
||||||
|
|
@ -63,7 +63,7 @@ def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy):
|
||||||
def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
|
def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
|
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert "X-Real-IP: " in r.text
|
assert "X-Real-IP: ".upper() in r.text.upper()
|
||||||
|
|
||||||
def test_Host_is_passed_on(docker_compose, nginxproxy):
|
def test_Host_is_passed_on(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
|
r = nginxproxy.get("http://web.nginx-proxy.tld/headers")
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ def test_X_Forwarded_Ssl_is_overwritten(docker_compose, nginxproxy):
|
||||||
def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
|
def test_X_Real_IP_is_generated(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
|
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
|
||||||
assert r.status_code == 200
|
assert r.status_code == 200
|
||||||
assert "X-Real-IP: " in r.text
|
assert "X-Real-IP: ".upper() in r.text.upper()
|
||||||
|
|
||||||
def test_Host_is_passed_on(docker_compose, nginxproxy):
|
def test_Host_is_passed_on(docker_compose, nginxproxy):
|
||||||
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
|
r = nginxproxy.get("https://web.nginx-proxy.tld/headers")
|
||||||
|
|
|
||||||
34
test/test_restart.py
Normal file
34
test/test_restart.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import pytest
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def test_dockergen_is_running(docker_compose):
|
||||||
|
assert docker_compose.containers.get("reverseproxy").exec_run("pgrep docker-gen") != ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_is_running(docker_compose):
|
||||||
|
assert docker_compose.containers.get("reverseproxy").exec_run("pgrep nginx") != ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_answers_with_503(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/")
|
||||||
|
assert r.status_code == 503
|
||||||
|
|
||||||
|
|
||||||
|
def test_survive_restart(docker_compose, nginxproxy):
|
||||||
|
docker_compose.containers.get("reverseproxy").restart()
|
||||||
|
time.sleep(2) # give time to eventually fail
|
||||||
|
assert docker_compose.containers.get("reverseproxy").status == "running"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dockergen_is_still_running(docker_compose):
|
||||||
|
assert docker_compose.containers.get("reverseproxy").exec_run("pgrep -c docker-gen") != ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_is_still_running(docker_compose):
|
||||||
|
assert docker_compose.containers.get("reverseproxy").exec_run("pgrep -c nginx") != ''
|
||||||
|
|
||||||
|
|
||||||
|
def test_nginx_still_answers_with_503(docker_compose, nginxproxy):
|
||||||
|
r = nginxproxy.get("http://nginx-proxy/")
|
||||||
|
assert r.status_code == 503
|
||||||
5
test/test_restart.yml
Normal file
5
test/test_restart.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
reverseproxy:
|
||||||
|
container_name: reverseproxy
|
||||||
|
image: jwilder/nginx-proxy:test
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
Loading…
Reference in a new issue