feat: pre bundled dockergen w/ nginx-proxy template image

This commit is contained in:
Nicolas Duchon 2024-12-18 20:52:50 +01:00
parent c60eff5d16
commit a67aff92e9
7 changed files with 131 additions and 31 deletions

View file

@ -23,7 +23,7 @@ jobs:
name: Build and publish image
strategy:
matrix:
base: [alpine, debian]
flavor: [alpine, debian, dockergen]
runs-on: ubuntu-latest
steps:
- name: Checkout
@ -37,7 +37,7 @@ jobs:
- name: Retrieve docker-gen version
id: docker-gen_version
run: sed -n -e 's;^FROM nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.base }} >> "$GITHUB_OUTPUT"
run: sed -n -e 's;^FROM docker.io/nginxproxy/docker-gen:\([0-9.]*\).*;VERSION=\1;p' Dockerfile.${{ matrix.flavor }} >> "$GITHUB_OUTPUT"
- name: Get Docker tags
id: docker_meta
@ -48,12 +48,15 @@ jobs:
nginxproxy/nginx-proxy
jwilder/nginx-proxy
tags: |
type=semver,pattern={{version}},enable=${{ matrix.base == 'debian' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.base == 'debian' }}
type=semver,suffix=-alpine,pattern={{version}},enable=${{ matrix.base == 'alpine' }}
type=semver,suffix=-alpine,pattern={{major}}.{{minor}},enable=${{ matrix.base == 'alpine' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && matrix.base == 'debian' }}
type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' && matrix.base == 'alpine' }}
type=semver,pattern={{version}},enable=${{ matrix.flavor == 'debian' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ matrix.flavor == 'debian' }}
type=semver,suffix=-alpine,pattern={{version}},enable=${{ matrix.flavor == 'alpine' }}
type=semver,suffix=-alpine,pattern={{major}}.{{minor}},enable=${{ matrix.flavor == 'alpine' }}
type=semver,suffix=-dockergen,pattern={{version}},enable=${{ matrix.flavor == 'dockergen' }}
type=semver,suffix=-dockergen,pattern={{major}}.{{minor}},enable=${{ matrix.flavor == 'dockergen' }}
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' && matrix.flavor == 'debian' }}
type=raw,value=alpine,enable=${{ github.ref == 'refs/heads/main' && matrix.flavor == 'alpine' }}
type=raw,value=dockergen,enable=${{ github.ref == 'refs/heads/main' && matrix.flavor == 'dockergen' }}
labels: |
org.opencontainers.image.authors=Nicolas Duchon <nicolas.duchon@gmail.com> (@buchdag), Jason Wilder
org.opencontainers.image.version=${{ steps.nginx-proxy_version.outputs.VERSION }}
@ -84,7 +87,7 @@ jobs:
uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile.${{ matrix.base }}
file: Dockerfile.${{ matrix.flavor }}
build-args: |
NGINX_PROXY_VERSION=${{ steps.nginx-proxy_version.outputs.VERSION }}
DOCKER_GEN_VERSION=${{ steps.docker-gen_version.outputs.VERSION }}

View file

@ -20,7 +20,7 @@ jobs:
strategy:
matrix:
base_docker_image: [alpine, debian]
flavor: [alpine, debian, dockergen]
steps:
- uses: actions/checkout@v4
@ -43,8 +43,10 @@ jobs:
run: make build-webserver
- name: Build Docker nginx proxy test image
run: make build-nginx-proxy-test-${{ matrix.base_docker_image }}
run: make build-nginx-proxy-test-${{ matrix.flavor }}
- name: Run tests
run: pytest
working-directory: test
env:
COMPOSE_PROFILES: ${{ matrix.flavor == 'dockergen' && 'separateContainers' || 'singleContainer' }}

18
Dockerfile.dockergen Normal file
View file

@ -0,0 +1,18 @@
FROM docker.io/nginxproxy/docker-gen:0.14.4
ARG NGINX_PROXY_VERSION
ENV NGINX_PROXY_VERSION=${NGINX_PROXY_VERSION} \
DOCKER_HOST=unix:///tmp/docker.sock \
DHPARAM_SKIP=true \
NGINX_CONTAINER_NAME=nginx-proxy
# Install dependencies
RUN apk add --no-cache --virtual .run-deps bash
RUN mkdir -p '/etc/nginx/conf.d'
COPY app nginx.tmpl LICENSE /app/
WORKDIR /app/
ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["docker-gen", "-notify-sighup", "$NGINX_CONTAINER_NAME", "-watch", "/app/nginx.tmpl", "/etc/nginx/conf.d/default.conf"]

View file

@ -11,10 +11,19 @@ build-nginx-proxy-test-debian:
build-nginx-proxy-test-alpine:
docker build --pull --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.alpine -t nginxproxy/nginx-proxy:test .
build-nginx-proxy-test-dockergen:
docker build --pull --build-arg NGINX_PROXY_VERSION="test" -f Dockerfile.dockergen -t nginxproxy/nginx-proxy:test-dockergen .
test-debian: export COMPOSE_PROFILES = singleContainer
test-debian: build-webserver build-nginx-proxy-test-debian
test/pytest.sh
test-alpine: export COMPOSE_PROFILES = singleContainer
test-alpine: build-webserver build-nginx-proxy-test-alpine
test/pytest.sh
test: test-debian test-alpine
test-dockergen: export COMPOSE_PROFILES = separateContainers
test-dockergen: build-webserver build-nginx-proxy-test-docker-gen
test/pytest.sh
test: test-debian test-alpine test-dockergen

View file

@ -101,7 +101,7 @@ function _setup_dhparam() {
}
# Run the init logic if the default CMD was provided
if [[ $* == 'forego start -r' ]]; then
if [[ $* == "forego start -r" ]] || [[ $* =~ "docker-gen -notify-sighup" ]]; then
_print_version
_check_unix_socket
@ -116,6 +116,11 @@ if [[ $* == 'forego start -r' ]]; then
Warning: The default value of TRUST_DOWNSTREAM_PROXY might change to "false" in a future version of nginx-proxy. If you require TRUST_DOWNSTREAM_PROXY to be enabled, explicitly set it to "true".
EOT
fi
if [[ $3 == "\$NGINX_CONTAINER_NAME" && -n "$NGINX_CONTAINER_NAME" ]]; then
# change the value of $3 to the expanded $NGINX_CONTAINER_NAME variable
set -- "${@:1:2}" "$NGINX_CONTAINER_NAME" "${@:4}"
fi
fi
exec "$@"

View file

@ -27,6 +27,9 @@ PYTEST_RUNNING_IN_CONTAINER = os.environ.get('PYTEST_RUNNING_IN_CONTAINER') == "
FORCE_CONTAINER_IPV6 = False # ugly global state to consider containers' IPv6 address instead of IPv4
DOCKER_COMPOSE = os.environ.get('DOCKER_COMPOSE', 'docker compose')
COMPOSE_PROFILES = os.environ.get('COMPOSE_PROFILES', 'singleContainer')
IMAGE_TAG = "test-dockergen" if COMPOSE_PROFILES == "separateContainers" else "test"
docker_client = docker.from_env()
@ -75,11 +78,11 @@ class requests_for_docker(object):
"""
Return list of containers
"""
nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"})
nginx_proxy_containers = docker_client.containers.list(filters={"ancestor": f"nginxproxy/nginx-proxy:{IMAGE_TAG}"})
if len(nginx_proxy_containers) > 1:
pytest.fail("Too many running nginxproxy/nginx-proxy:test containers", pytrace=False)
pytest.fail(f"Too many running nginxproxy/nginx-proxy:{IMAGE_TAG} containers", pytrace=False)
elif len(nginx_proxy_containers) == 0:
pytest.fail("No running nginxproxy/nginx-proxy:test container", pytrace=False)
pytest.fail(f"No running nginxproxy/nginx-proxy:{IMAGE_TAG} container", pytrace=False)
return nginx_proxy_containers
def get_conf(self):
@ -188,7 +191,7 @@ def container_ipv6(container):
return net_info[network_name]["GlobalIPv6Address"]
def nginx_proxy_dns_resolver(domain_name):
def nginx_proxy_single_container_dns_resolver(domain_name):
"""
if "nginx-proxy" if found in host, return the ip address of the docker container
issued from the docker image nginxproxy/nginx-proxy:test.
@ -196,21 +199,44 @@ def nginx_proxy_dns_resolver(domain_name):
:return: IP or None
"""
log = logging.getLogger('DNS')
log.debug(f"nginx_proxy_dns_resolver({domain_name!r})")
log.debug(f"nginx_proxy_single_container_dns_resolver({domain_name!r})")
if 'nginx-proxy' in domain_name:
nginxproxy_containers = docker_client.containers.list(filters={"status": "running", "ancestor": "nginxproxy/nginx-proxy:test"})
if len(nginxproxy_containers) == 0:
log.warn(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}")
log.info(f"no container found from image nginxproxy/nginx-proxy:test while resolving {domain_name!r}")
exited_nginxproxy_containers = docker_client.containers.list(filters={"status": "exited", "ancestor": "nginxproxy/nginx-proxy:test"})
if len(exited_nginxproxy_containers) > 0:
exited_nginxproxy_container_logs = exited_nginxproxy_containers[0].logs()
log.warn(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
log.warning(f"nginxproxy/nginx-proxy:test container might have exited unexpectedly. Container logs: " + "\n" + exited_nginxproxy_container_logs.decode())
return
nginxproxy_container = nginxproxy_containers[0]
ip = container_ip(nginxproxy_container)
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx-proxy container {nginxproxy_container.name}")
return ip
def nginx_proxy_separate_containers_dns_resolver(domain_name):
"""
if "nginx-proxy" if found in host, return the ip address of the docker container
labeled with "com.github.nginx-proxy.nginx-proxy.nginx".
:return: IP or None
"""
log = logging.getLogger('DNS')
log.debug(f"nginx_proxy_separate_containers_dns_resolver({domain_name!r})")
if 'nginx-proxy' in domain_name:
nginx_containers = docker_client.containers.list(filters={"status": "running", "label": "com.github.nginx-proxy.nginx-proxy.nginx"})
if len(nginx_containers) == 0:
log.info(f"no container labeled with com.github.nginx-proxy.nginx-proxy.nginx found while resolving {domain_name!r}")
exited_nginx_containers = docker_client.containers.list(filters={"status": "exited", "label": "com.github.nginx-proxy.nginx-proxy.nginx"})
if len(exited_nginx_containers) > 0:
exited_nginx_container_logs = exited_nginx_containers[0].logs()
log.warning(f"nginx container might have exited unexpectedly. Container logs: " + "\n" + exited_nginx_container_logs.decode())
return
nginx_container = nginx_containers[0]
ip = container_ip(nginx_container)
log.info(f"resolving domain name {domain_name!r} as IP address {ip} of nginx container {nginx_container.name}")
return ip
def docker_container_dns_resolver(domain_name):
"""
if domain name is of the form "XXX.container.docker" or "anything.XXX.container.docker", return the ip address of the docker container
@ -231,7 +257,7 @@ def docker_container_dns_resolver(domain_name):
try:
container = docker_client.containers.get(container_name)
except docker.errors.NotFound:
log.warn(f"container named {container_name!r} not found while resolving {domain_name!r}")
log.warning(f"container named {container_name!r} not found while resolving {domain_name!r}")
return
log.debug(f"container {container.name!r} found ({container.short_id})")
@ -244,7 +270,8 @@ def monkey_patch_urllib_dns_resolver():
"""
Alter the behavior of the urllib DNS resolver so that any domain name
containing substring 'nginx-proxy' will resolve to the IP address
of the container created from image 'nginxproxy/nginx-proxy:test'.
of the container created from image 'nginxproxy/nginx-proxy:test' or
labeled with 'com.github.nginx-proxy.nginx-proxy.nginx'.
"""
prv_getaddrinfo = socket.getaddrinfo
dns_cache = {}
@ -258,7 +285,9 @@ def monkey_patch_urllib_dns_resolver():
pytest.skip("This system does not support IPv6")
# custom DNS resolvers
ip = nginx_proxy_dns_resolver(args[0])
ip = nginx_proxy_single_container_dns_resolver(args[0])
if ip is None:
ip = nginx_proxy_separate_containers_dns_resolver(args[0])
if ip is None:
ip = docker_container_dns_resolver(args[0])
if ip is not None:
@ -319,10 +348,11 @@ def docker_compose_down(compose_file='docker-compose.yml'):
def wait_for_nginxproxy_to_be_ready():
"""
If one (and only one) container started from image nginxproxy/nginx-proxy:test is found,
wait for its log to contain substring "Watching docker events"
If one (and only one) container started from image nginxproxy/nginx-proxy:test
or nginxproxy/nginx-proxy:test-dockergen is found, wait for its log to contain
substring "Watching docker events"
"""
containers = docker_client.containers.list(filters={"ancestor": "nginxproxy/nginx-proxy:test"})
containers = docker_client.containers.list(filters={"ancestor": f"nginxproxy/nginx-proxy:{IMAGE_TAG}"})
if len(containers) != 1:
return
container = containers[0]
@ -371,7 +401,7 @@ def connect_to_network(network):
try:
my_container = docker_client.containers.get(test_container)
except docker.errors.NotFound:
logging.warn(f"container {test_container} not found")
logging.warning(f"container {test_container} not found")
return
# figure out our container networks
@ -399,7 +429,7 @@ def disconnect_from_network(network=None):
try:
my_container = docker_client.containers.get(test_container)
except docker.errors.NotFound:
logging.warn(f"container {test_container} not found")
logging.warning(f"container {test_container} not found")
return
# figure out our container networks
@ -527,7 +557,11 @@ def acme_challenge_path():
def pytest_runtest_logreport(report):
if report.failed:
if isinstance(report.longrepr, ReprExceptionInfo):
test_containers = docker_client.containers.list(all=True, filters={"ancestor": "nginxproxy/nginx-proxy:test"})
nginx_containers = docker_client.containers.list(all=True, filters={"label": "com.github.nginx-proxy.nginx-proxy.nginx"})
for container in nginx_containers:
report.longrepr.addsection('nginx container logs', container.logs())
test_containers = docker_client.containers.list(all=True, filters={"ancestor": f"nginxproxy/nginx-proxy:{IMAGE_TAG}"})
for container in test_containers:
report.longrepr.addsection('nginx-proxy logs', container.logs())
report.longrepr.addsection('nginx-proxy conf', get_nginx_conf_from_container(container))
@ -553,9 +587,9 @@ def pytest_runtest_setup(item):
###############################################################################
try:
docker_client.images.get('nginxproxy/nginx-proxy:test')
docker_client.images.get(f"nginxproxy/nginx-proxy:{IMAGE_TAG}")
except docker.errors.ImageNotFound:
pytest.exit("The docker image 'nginxproxy/nginx-proxy:test' is missing")
pytest.exit(f"The docker image 'nginxproxy/nginx-proxy:{IMAGE_TAG}' is missing")
if Version(docker.__version__) < Version("7.0.0"):
pytest.exit("This test suite is meant to work with the python docker module v7.0.0 or later")

View file

@ -2,8 +2,13 @@ networks:
netA:
netB:
volumes:
nginx_conf:
services:
reverseproxy:
profiles:
- singleContainer
container_name: reverseproxy
networks:
- netA
@ -11,6 +16,30 @@ services:
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
reverseproxynginx:
profiles:
- separateContainers
container_name: reverseproxy
networks:
- netA
image: nginx:alpine
volumes:
- nginx_conf:/etc/nginx/conf.d:ro
labels:
- "com.github.nginx-proxy.nginx-proxy.nginx"
docker-gen:
profiles:
- separateContainers
networks:
- netA
image: nginxproxy/nginx-proxy:test-dockergen
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- nginx_conf:/etc/nginx/conf.d
environment:
NGINX_CONTAINER_NAME: reverseproxy
webA:
networks:
- netA