Gunicorn is replaced with nginx-unit
We now serve Netbox with an nginx-unit instance instead of Gunicorn. This allows us to get rid of the extra Nginx container because Unit is also serving the static files. The static files are now collected at container buildtime instead of every startup.
This commit is contained in:
parent
380cb77080
commit
d273391773
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
10
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -65,13 +65,3 @@ If your log is very long, create a Gist instead (and post the link to it): https
|
||||
```text
|
||||
LOG LOG LOG
|
||||
```
|
||||
|
||||
The output of `docker-compose logs nginx`:
|
||||
<!--
|
||||
Only if you have gotten a 5xx http error, else delete this section.
|
||||
If your log is very long, create a Gist instead (and post the link to it): https://gist.github.com
|
||||
-->
|
||||
|
||||
```text
|
||||
LOG LOG LOG
|
||||
```
|
||||
|
2
.github/workflows/push.yml
vendored
2
.github/workflows/push.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
||||
- ./build.sh develop
|
||||
docker_from:
|
||||
- '' # use the default of the build script
|
||||
# - python:3.10-rc-alpine # disable until dependencies work
|
||||
- alpine:edge
|
||||
fail-fast: false
|
||||
runs-on: ubuntu-latest
|
||||
name: Builds new Netbox Docker Images
|
||||
|
48
Dockerfile
48
Dockerfile
@ -12,27 +12,15 @@ RUN apk add --no-cache \
|
||||
libffi-dev \
|
||||
libxslt-dev \
|
||||
openldap-dev \
|
||||
postgresql-dev
|
||||
|
||||
WORKDIR /install
|
||||
|
||||
RUN pip install --prefix="/install" --no-warn-script-location \
|
||||
# gunicorn is used for launching netbox
|
||||
gunicorn \
|
||||
greenlet \
|
||||
eventlet \
|
||||
# napalm is used for gathering information from network devices
|
||||
napalm \
|
||||
# ruamel is used in startup_scripts
|
||||
'ruamel.yaml>=0.15,<0.16' \
|
||||
# django_auth_ldap is required for ldap
|
||||
django_auth_ldap \
|
||||
# django-storages was introduced in 2.7 and is optional
|
||||
django-storages
|
||||
postgresql-dev \
|
||||
py3-pip \
|
||||
python3-dev \
|
||||
&& python3 -m venv /opt/netbox/venv \
|
||||
&& /opt/netbox/venv/bin/python3 -m pip install --upgrade pip setuptools
|
||||
|
||||
ARG NETBOX_PATH
|
||||
COPY ${NETBOX_PATH}/requirements.txt /
|
||||
RUN pip install --prefix="/install" --no-warn-script-location -r /requirements.txt
|
||||
COPY ${NETBOX_PATH}/requirements.txt requirements-container.txt /
|
||||
RUN /opt/netbox/venv/bin/pip install -r /requirements.txt -r /requirements-container.txt
|
||||
|
||||
###
|
||||
# Main stage
|
||||
@ -44,6 +32,7 @@ FROM ${FROM} as main
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
ca-certificates \
|
||||
curl \
|
||||
graphviz \
|
||||
libevent \
|
||||
libffi \
|
||||
@ -51,35 +40,38 @@ RUN apk add --no-cache \
|
||||
libressl \
|
||||
libxslt \
|
||||
postgresql-libs \
|
||||
ttf-ubuntu-font-family
|
||||
python3 \
|
||||
py3-pip \
|
||||
ttf-ubuntu-font-family \
|
||||
unit \
|
||||
unit-python3
|
||||
|
||||
WORKDIR /opt
|
||||
|
||||
COPY --from=builder /install /usr/local
|
||||
COPY --from=builder /opt/netbox/venv /opt/netbox/venv
|
||||
|
||||
ARG NETBOX_PATH
|
||||
COPY ${NETBOX_PATH} /opt/netbox
|
||||
|
||||
COPY docker/configuration.docker.py /opt/netbox/netbox/netbox/configuration.py
|
||||
COPY docker/gunicorn_config.py /etc/netbox/
|
||||
COPY docker/nginx.conf /etc/netbox-nginx/nginx.conf
|
||||
COPY docker/docker-entrypoint.sh /opt/netbox/docker-entrypoint.sh
|
||||
COPY docker/launch-netbox.sh /opt/netbox/launch-netbox.sh
|
||||
COPY startup_scripts/ /opt/netbox/startup_scripts/
|
||||
COPY initializers/ /opt/netbox/initializers/
|
||||
COPY configuration/ /etc/netbox/config/
|
||||
COPY docker/nginx-unit.json /etc/unit/
|
||||
|
||||
WORKDIR /opt/netbox/netbox
|
||||
|
||||
# Must set permissions for '/opt/netbox/netbox/static' directory
|
||||
# to g+w so that `./manage.py collectstatic` can be executed during
|
||||
# container startup.
|
||||
# Must set permissions for '/opt/netbox/netbox/media' directory
|
||||
# to g+w so that pictures can be uploaded to netbox.
|
||||
RUN mkdir static && chmod -R g+w static media
|
||||
RUN mkdir -p static /opt/unit/state/ /opt/unit/tmp/ \
|
||||
&& chmod -R g+w media /opt/unit/ \
|
||||
&& SECRET_KEY="dummy" /opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py collectstatic --no-input
|
||||
|
||||
ENTRYPOINT [ "/opt/netbox/docker-entrypoint.sh" ]
|
||||
|
||||
CMD ["gunicorn", "-c /etc/netbox/gunicorn_config.py", "netbox.wsgi"]
|
||||
CMD [ "/opt/netbox/launch-netbox.sh" ]
|
||||
|
||||
LABEL ORIGINAL_TAG="" \
|
||||
NETBOX_GIT_BRANCH="" \
|
||||
|
@ -56,7 +56,7 @@ cd netbox-docker
|
||||
tee docker-compose.override.yml <<EOF
|
||||
version: '3.4'
|
||||
services:
|
||||
nginx:
|
||||
netbox:
|
||||
ports:
|
||||
- 8000:8080
|
||||
EOF
|
||||
|
14
build.sh
14
build.sh
@ -49,7 +49,7 @@ if [ "${1}x" == "x" ] || [ "${1}" == "--help" ] || [ "${1}" == "-h" ]; then
|
||||
echo " DOCKERFILE The name of Dockerfile to use."
|
||||
echo " Default: Dockerfile"
|
||||
echo " DOCKER_FROM The base image to use."
|
||||
echo " Default: 'python:3.9-alpine'"
|
||||
echo " Default: 'alpine:3.13'"
|
||||
echo " DOCKER_TARGET A specific target to build."
|
||||
echo " It's currently not possible to pass multiple targets."
|
||||
echo " Default: main ldap"
|
||||
@ -106,7 +106,7 @@ else
|
||||
fi
|
||||
|
||||
###
|
||||
# Variables for fetching the source
|
||||
# Variables for fetching the Netbox source
|
||||
###
|
||||
SRC_ORG="${SRC_ORG-netbox-community}"
|
||||
SRC_REPO="${SRC_REPO-netbox}"
|
||||
@ -115,10 +115,10 @@ URL="${URL-https://github.com/${SRC_ORG}/${SRC_REPO}.git}"
|
||||
NETBOX_PATH="${NETBOX_PATH-.netbox}"
|
||||
|
||||
###
|
||||
# Fetching the source
|
||||
# Fetching the Netbox source
|
||||
###
|
||||
if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ] ; then
|
||||
echo "🌐 Checking out '${NETBOX_BRANCH}' of netbox from the url '${URL}' into '${NETBOX_PATH}'"
|
||||
echo "🌐 Checking out '${NETBOX_BRANCH}' of Netbox from the url '${URL}' into '${NETBOX_PATH}'"
|
||||
if [ ! -d "${NETBOX_PATH}" ]; then
|
||||
$DRY git clone -q --depth 10 -b "${NETBOX_BRANCH}" "${URL}" "${NETBOX_PATH}"
|
||||
fi
|
||||
@ -135,7 +135,7 @@ if [ "${2}" != "--push-only" ] && [ -z "${SKIP_GIT}" ] ; then
|
||||
$DRY git checkout -qf FETCH_HEAD
|
||||
$DRY git prune
|
||||
)
|
||||
echo "✅ Checked out netbox"
|
||||
echo "✅ Checked out Netbox"
|
||||
fi
|
||||
|
||||
###
|
||||
@ -157,7 +157,7 @@ fi
|
||||
# Determining the value for DOCKER_FROM
|
||||
###
|
||||
if [ -z "$DOCKER_FROM" ]; then
|
||||
DOCKER_FROM="python:3.9-alpine"
|
||||
DOCKER_FROM="alpine:3.13"
|
||||
fi
|
||||
|
||||
###
|
||||
@ -271,7 +271,7 @@ for DOCKER_TARGET in "${DOCKER_TARGETS[@]}"; do
|
||||
|
||||
if ! printf '%s\n' "${IMAGES_LAYERS_OLD[@]}" | grep -q -P "^${PYTHON_LAST_LAYER}\$"; then
|
||||
SHOULD_BUILD="true"
|
||||
BUILD_REASON="${BUILD_REASON} python"
|
||||
BUILD_REASON="${BUILD_REASON} alpine"
|
||||
fi
|
||||
if [ "${NETBOX_GIT_REF}" != "${NETBOX_GIT_REF_OLD}" ]; then
|
||||
SHOULD_BUILD="true"
|
||||
|
@ -14,19 +14,9 @@ services:
|
||||
- ./configuration:/etc/netbox/config:z,ro
|
||||
- ./reports:/etc/netbox/reports:z,ro
|
||||
- ./scripts:/etc/netbox/scripts:z,ro
|
||||
- netbox-nginx-config:/etc/netbox-nginx:z
|
||||
- netbox-static-files:/opt/netbox/netbox/static:z
|
||||
- netbox-media-files:/opt/netbox/netbox/media:z
|
||||
nginx:
|
||||
command: nginx -c /etc/netbox-nginx/nginx.conf
|
||||
image: nginx:1.19-alpine
|
||||
depends_on:
|
||||
- netbox
|
||||
ports:
|
||||
- 8080
|
||||
volumes:
|
||||
- netbox-static-files:/opt/netbox/netbox/static:ro
|
||||
- netbox-nginx-config:/etc/netbox-nginx/:ro
|
||||
postgres:
|
||||
image: postgres:12-alpine
|
||||
env_file: env/postgres.env
|
||||
@ -45,9 +35,5 @@ services:
|
||||
- redis-server --requirepass $$REDIS_PASSWORD ## $$ because of docker-compose
|
||||
env_file: env/redis-cache.env
|
||||
volumes:
|
||||
netbox-static-files:
|
||||
driver: local
|
||||
netbox-nginx-config:
|
||||
driver: local
|
||||
netbox-media-files:
|
||||
driver: local
|
||||
|
@ -15,30 +15,19 @@ services:
|
||||
- ./configuration:/etc/netbox/config:z,ro
|
||||
- ./reports:/etc/netbox/reports:z,ro
|
||||
- ./scripts:/etc/netbox/scripts:z,ro
|
||||
- netbox-nginx-config:/etc/netbox-nginx:z
|
||||
- netbox-static-files:/opt/netbox/netbox/static:z
|
||||
- netbox-media-files:/opt/netbox/netbox/media:z
|
||||
ports:
|
||||
- "8080"
|
||||
netbox-worker:
|
||||
<<: *netbox
|
||||
depends_on:
|
||||
- redis
|
||||
entrypoint:
|
||||
- python3
|
||||
- /opt/netbox/venv/bin/python
|
||||
- /opt/netbox/netbox/manage.py
|
||||
command:
|
||||
- rqworker
|
||||
|
||||
# nginx
|
||||
nginx:
|
||||
command: nginx -c /etc/netbox-nginx/nginx.conf
|
||||
image: nginx:1.19-alpine
|
||||
depends_on:
|
||||
- netbox
|
||||
ports:
|
||||
- 8080
|
||||
volumes:
|
||||
- netbox-static-files:/opt/netbox/netbox/static:ro
|
||||
- netbox-nginx-config:/etc/netbox-nginx/:ro
|
||||
ports: []
|
||||
|
||||
# postgres
|
||||
postgres:
|
||||
@ -66,10 +55,6 @@ services:
|
||||
env_file: env/redis-cache.env
|
||||
|
||||
volumes:
|
||||
netbox-static-files:
|
||||
driver: local
|
||||
netbox-nginx-config:
|
||||
driver: local
|
||||
netbox-media-files:
|
||||
driver: local
|
||||
netbox-postgres-data:
|
||||
|
@ -9,6 +9,7 @@ from os import scandir
|
||||
import importlib.util
|
||||
import sys
|
||||
|
||||
|
||||
def _filename(f):
|
||||
return f.name
|
||||
|
||||
|
@ -7,6 +7,9 @@ set -e
|
||||
# Allows Netbox to be run as non-root users
|
||||
umask 002
|
||||
|
||||
# Load correct Python3 env
|
||||
source /opt/netbox/venv/bin/activate
|
||||
|
||||
# Try to connect to the DB
|
||||
DB_WAIT_TIMEOUT=${DB_WAIT_TIMEOUT-3}
|
||||
MAX_DB_WAIT_TIME=${MAX_DB_WAIT_TIME-30}
|
||||
@ -60,9 +63,6 @@ else
|
||||
echo "import runpy; runpy.run_path('../startup_scripts')" | ./manage.py shell --interface python
|
||||
fi
|
||||
|
||||
# Copy static files
|
||||
./manage.py collectstatic --no-input
|
||||
|
||||
echo "✅ Initialisation is done."
|
||||
|
||||
# Launch whatever is passed by docker
|
||||
|
@ -1,8 +0,0 @@
|
||||
command = '/usr/bin/gunicorn'
|
||||
pythonpath = '/opt/netbox/netbox'
|
||||
bind = '0.0.0.0:8001'
|
||||
workers = 3
|
||||
errorlog = '-'
|
||||
accesslog = '-'
|
||||
capture_output = False
|
||||
loglevel = 'info'
|
53
docker/launch-netbox.sh
Executable file
53
docker/launch-netbox.sh
Executable file
@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
|
||||
UNIT_CONFIG="${UNIT_CONFIG-/etc/unit/nginx-unit.json}"
|
||||
UNIT_SOCKET="/opt/unit/unit.sock"
|
||||
|
||||
load_configuration() {
|
||||
MAX_WAIT=10
|
||||
WAIT_COUNT=0
|
||||
while [ ! -S $UNIT_SOCKET ]; do
|
||||
if [ $WAIT_COUNT -gte $MAX_WAIT ]; then
|
||||
echo "⚠️ No control socket found; configuration will not be loaded."
|
||||
return 1
|
||||
fi
|
||||
|
||||
WAIT_COUNT=$((WAIT_COUNT + 1))
|
||||
echo "⏳ Waiting for control socket to be created... (${WAIT_COUNT}/${MAX_WAIT})"
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# even when the control socket exists, it does not mean unit has finished initialisation
|
||||
# this curl call will get a reply once unit is fully launched
|
||||
curl --silent --output /dev/null --request GET --unix-socket $UNIT_SOCKET http://localhost/
|
||||
|
||||
echo "⚙️ Applying configuration from $UNIT_CONFIG";
|
||||
|
||||
RESP_CODE=$(curl \
|
||||
--silent \
|
||||
--output /dev/null \
|
||||
--write-out '%{http_code}' \
|
||||
--request PUT \
|
||||
--data-binary "@${UNIT_CONFIG}" \
|
||||
--unix-socket $UNIT_SOCKET \
|
||||
http://localhost/config
|
||||
)
|
||||
if [ "$RESP_CODE" != "200" ]; then
|
||||
echo "⚠️ Could no load Unit configuration"
|
||||
kill "$(cat /opt/unit/unit.pid)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "✅ Unit configuration loaded successfully"
|
||||
}
|
||||
|
||||
load_configuration &
|
||||
|
||||
exec unitd \
|
||||
--no-daemon \
|
||||
--control unix:$UNIT_SOCKET \
|
||||
--pid /opt/unit/unit.pid \
|
||||
--log /dev/stdout \
|
||||
--state /opt/unit/state/ \
|
||||
--tmp /opt/unit/tmp/
|
40
docker/nginx-unit.json
Normal file
40
docker/nginx-unit.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"listeners": {
|
||||
"*:8080": {
|
||||
"pass": "routes"
|
||||
}
|
||||
},
|
||||
|
||||
"routes": [
|
||||
{
|
||||
"match": {
|
||||
"uri": "/static/*"
|
||||
},
|
||||
"action": {
|
||||
"share": "/opt/netbox/netbox"
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"action": {
|
||||
"pass": "applications/netbox"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"applications": {
|
||||
"netbox": {
|
||||
"type": "python 3",
|
||||
"path": "/opt/netbox/netbox/",
|
||||
"module": "netbox.wsgi",
|
||||
"home": "/opt/netbox/venv",
|
||||
"processes": {
|
||||
"max": 4,
|
||||
"spare": 1,
|
||||
"idle_timeout": 120
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"access_log": "/dev/stdout"
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
daemon off;
|
||||
worker_processes 1;
|
||||
|
||||
error_log /dev/stderr info;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
keepalive_timeout 65;
|
||||
gzip on;
|
||||
server_tokens off;
|
||||
client_max_body_size 10M;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
access_log off;
|
||||
|
||||
location /static/ {
|
||||
alias /opt/netbox/netbox/static/;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://netbox:8001;
|
||||
proxy_set_header X-Forwarded-Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8081;
|
||||
access_log off;
|
||||
|
||||
location = /stub_status {
|
||||
stub_status;
|
||||
}
|
||||
}
|
||||
}
|
4
requirements-container.txt
Normal file
4
requirements-container.txt
Normal file
@ -0,0 +1,4 @@
|
||||
napalm==3.2.0
|
||||
ruamel.yaml>=0.15,<0.16
|
||||
django-auth-ldap==2.2.0
|
||||
django-storages==1.10.1
|
Loading…
Reference in New Issue
Block a user