Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: "Check out repository code"
uses: "actions/checkout@v2"
uses: "actions/checkout@v4"
- name: "Set up Python"
uses: "actions/setup-python@v2"
uses: "actions/setup-python@v5"
id: "python-setup"
with:
python-version: "3.9"
python-version: "3.12"
- name: "Install yamllint"
run: "pip install yamllint"
- name: "Run yamllint"
Expand Down
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# nautobot-docker-compose

Network to Code has an existing published Nautobot Docker Image on Docker Hub. See [here](https://hub.docker.com/repository/docker/networktocode/nautobot). This project uses Docker Compose. The Docker compose file in this project pulls that Nautobot Docker image using the latest stable Nautobot release along with several other Docker images required for Nautobot to function. See the diagram below. This project is for those looking for a multi-container single-node install for Nautobot often coupled with backup & HA capabilities from their hypervisor manager.
Network to Code publishes Nautobot container images on GitHub Container Registry. See [ghcr.io/nautobot/nautobot](https://github.com/nautobot/nautobot/pkgs/container/nautobot). This project uses Docker Compose to build a local Nautobot image from those upstream artifacts and run it alongside the supporting services Nautobot requires. See the diagram below. This project is for those looking for a multi-container single-node install for Nautobot often coupled with backup & HA capabilities from their hypervisor manager.

![Container Stack](docs/img/container_stack.png)

By default, this project deploys the Nautobot application, a single worker container, Redis containers, and PostgreSQL. It does not deploy NGINX, SSL, or any Nautobot plugins. However, the project is extensible to allow users to tailor it to their specific requirements. For example, if you need to deploy [SSL](docs/create_ssl_cert.md) or [plugins](docs/plugins.md), see the docs linked. The web server used on the application is [pyuwsgi](https://uwsgi-docs.readthedocs.io/en/latest/).
By default, this project deploys the Nautobot application, a Celery worker, a Celery beat scheduler, Redis, and PostgreSQL. It does not deploy NGINX, SSL, or any Nautobot plugins. However, the project is extensible to allow users to tailor it to their specific requirements. For example, if you need to deploy [SSL](docs/create_ssl_cert.md) or [plugins](docs/plugins.md), see the docs linked. The web server used on the application is [pyuwsgi](https://uwsgi-docs.readthedocs.io/en/latest/).

## Docker Compose

Expand Down Expand Up @@ -215,11 +215,12 @@ Example Output:

```bash
❯ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
143f10daa229 networktocode/nautobot:latest "nautobot-server rqw…" 2 minutes ago Up 2 minutes (healthy) nautobot-docker-compose_celery_worker_1
bb29124d7acb networktocode/nautobot:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes (healthy) 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:8443->8443/tcp, :::8443->8443/tcp nautobot-docker-compose_nautobot_1
ad57ac1749b3 redis:alpine "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 6379/tcp nautobot-docker-compose_redis_1
5ab83264e6fe postgres:10 "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 5432/tcp nautobot-docker-compose_postgres_1
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
143f10daa229 yourrepo/nautobot-docker-compose:local "nautobot-server cel…" 2 minutes ago Up 2 minutes (healthy) nautobot-docker-compose-celery-worker-1
bb29124d7acb yourrepo/nautobot-docker-compose:local "nautobot-server cel…" 2 minutes ago Up 2 minutes nautobot-docker-compose-celery-beat-1
ad57ac1749b3 yourrepo/nautobot-docker-compose:local "/docker-entrypoint.…" 2 minutes ago Up 2 minutes (healthy) 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp nautobot-docker-compose-nautobot-1
5ab83264e6fe redis:6-alpine "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 6379/tcp nautobot-docker-compose-redis-1
1c2d3e4f5a6b postgres:13-alpine "docker-entrypoint.s…" 2 minutes ago Up 2 minutes (healthy) 5432/tcp nautobot-docker-compose-db-1
```

2. Execute Create Super User Command and follow the prompts
Expand Down
13 changes: 0 additions & 13 deletions config/nautobot_config.py.ldap
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,6 @@ TESTING = len(sys.argv) > 1 and sys.argv[1] == "test"
# Redis
#

# The django-redis cache is used to establish concurrent locks using Redis.
#
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": parse_redis_connection(redis_database=0),
"TIMEOUT": 300,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
}
}

# Redis Cacheops
CACHEOPS_REDIS = parse_redis_connection(redis_database=1)

Expand Down
2 changes: 1 addition & 1 deletion docs/create_ssl_cert.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ By default the Docker image comes with a self signed certificate that is valid f

```yaml
nautobot:
image: "networktocode/nautobot:latest"
image: "yourrepo/nautobot-docker-compose:local"
env_file:
- "local.env"
ports:
Expand Down
Binary file modified docs/img/container_stack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
163 changes: 163 additions & 0 deletions docs/img/generate_container_stack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
"""Generate the container stack diagram used in the README."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure this process is desirable.


from pathlib import Path

from PIL import Image, ImageDraw, ImageFilter, ImageFont


WIDTH = 248
HEIGHT = 440
OUTPUT = Path(__file__).with_name("container_stack.png")

TITLE_FONT = "/System/Library/Fonts/Supplemental/Trebuchet MS Bold.ttf"
BOLD_FONT = "/System/Library/Fonts/Supplemental/Arial Bold.ttf"
BODY_FONT = "/System/Library/Fonts/Supplemental/Arial.ttf"


def vertical_gradient(size, top, bottom):
"""Create a vertical RGBA gradient."""
width, height = size
image = Image.new("RGBA", size, 0)
pixels = image.load()
for y in range(height):
blend = y / max(height - 1, 1)
color = tuple(int(top[i] * (1 - blend) + bottom[i] * blend) for i in range(4))
for x in range(width):
pixels[x, y] = color
return image


def rounded_panel(size, radius, fill_top, fill_bottom, stroke=None, stroke_width=0):
"""Create a rounded rectangle panel with a vertical gradient fill."""
panel = Image.new("RGBA", size, (0, 0, 0, 0))
gradient = vertical_gradient(size, fill_top, fill_bottom)
mask = Image.new("L", size, 0)
mask_draw = ImageDraw.Draw(mask)
mask_draw.rounded_rectangle((0, 0, size[0] - 1, size[1] - 1), radius=radius, fill=255)
panel.paste(gradient, (0, 0), mask)
if stroke and stroke_width:
panel_draw = ImageDraw.Draw(panel)
for index in range(stroke_width):
panel_draw.rounded_rectangle(
(index, index, size[0] - 1 - index, size[1] - 1 - index),
radius=radius,
outline=stroke,
)
return panel


def add_shadow(base, box, radius=10, offset=(0, 6), color=(24, 45, 74, 70)):
"""Add a soft shadow behind a rounded rectangle."""
shadow = Image.new("RGBA", base.size, (0, 0, 0, 0))
shadow_draw = ImageDraw.Draw(shadow)
x0, y0, x1, y1 = box
x_offset, y_offset = offset
shadow_draw.rounded_rectangle(
(x0 + x_offset, y0 + y_offset, x1 + x_offset, y1 + y_offset),
radius=radius,
fill=color,
)
base.alpha_composite(shadow.filter(ImageFilter.GaussianBlur(8)))


def build_diagram():
"""Create the Nautobot container stack image."""
image = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
image.alpha_composite(vertical_gradient((WIDTH, HEIGHT), (241, 247, 252, 255), (220, 234, 245, 255)))

overlay = Image.new("RGBA", (WIDTH, HEIGHT), (0, 0, 0, 0))
overlay_draw = ImageDraw.Draw(overlay)
for y in range(42, HEIGHT, 28):
overlay_draw.line((18, y, WIDTH - 18, y), fill=(255, 255, 255, 34), width=1)
for x in range(28, WIDTH, 32):
overlay_draw.line((x, 42, x, HEIGHT - 22), fill=(255, 255, 255, 18), width=1)
image.alpha_composite(overlay)

title_font = ImageFont.truetype(TITLE_FONT, 18)
label_font = ImageFont.truetype(BOLD_FONT, 11)
box_font = ImageFont.truetype(BOLD_FONT, 15)
box_font_small = ImageFont.truetype(BOLD_FONT, 13)
mini_font = ImageFont.truetype(BODY_FONT, 9)

draw = ImageDraw.Draw(image)
draw.text((WIDTH // 2, 18), "Container Stack", anchor="mm", font=title_font, fill=(38, 68, 94, 255))
draw.text((WIDTH // 2, 34), "Nautobot 3", anchor="mm", font=mini_font, fill=(83, 115, 140, 255))

vm_box = (23, 49, 225, 410)
add_shadow(image, vm_box, radius=16, offset=(0, 8), color=(28, 52, 79, 58))
vm_panel = rounded_panel(
(vm_box[2] - vm_box[0], vm_box[3] - vm_box[1]),
16,
(247, 251, 254, 255),
(233, 241, 247, 255),
stroke=(107, 128, 145, 255),
stroke_width=3,
)
image.alpha_composite(vm_panel, vm_box[:2])

draw = ImageDraw.Draw(image)
draw.rounded_rectangle((39, 61, 209, 84), radius=10, fill=(226, 235, 242, 255))
draw.text((124, 72), "VIRTUAL MACHINE", anchor="mm", font=label_font, fill=(81, 102, 116, 255))

containers = [
("Nautobot App", "Web UI + API", (89, 208, 176, 255), (62, 170, 140, 255), (20, 58, 66, 255), box_font),
("Celery Worker", "Async jobs", (247, 197, 104, 255), (228, 167, 62, 255), (78, 50, 0, 255), box_font),
("Celery Beat", "Scheduled tasks", (248, 221, 116, 255), (226, 187, 53, 255), (92, 71, 0, 255), box_font),
("Redis", "Broker + cache", (248, 120, 110, 255), (219, 87, 80, 255), (255, 248, 248, 255), box_font),
(
"PostgreSQL / MySQL",
"Database",
(131, 170, 245, 255),
(84, 118, 212, 255),
(248, 251, 255, 255),
box_font_small,
),
]

box_left = 46
box_right = 202
box_width = box_right - box_left
box_height = 42
start_y = 98
gap = 12

for index, (title, subtitle, top_color, bottom_color, text_color, font) in enumerate(containers):
top = start_y + index * (box_height + gap)
bottom = top + box_height
add_shadow(image, (box_left, top, box_right, bottom), radius=11, offset=(0, 4), color=(18, 36, 58, 48))
panel = rounded_panel(
(box_width, box_height),
11,
top_color,
bottom_color,
stroke=(255, 255, 255, 70),
stroke_width=1,
)
image.alpha_composite(panel, (box_left, top))

draw = ImageDraw.Draw(image)
draw.text((124, top + 15), title, anchor="mm", font=font, fill=text_color)
subtitle_color = text_color if index < 3 else (255, 243, 243, 230) if index == 3 else (240, 245, 255, 230)
draw.text((124, top + 29), subtitle, anchor="mm", font=mini_font, fill=subtitle_color)
if index < len(containers) - 1:
arrow_top = bottom + 3
arrow_bottom = bottom + gap - 3
draw.rounded_rectangle((122, arrow_top, 126, arrow_bottom), radius=2, fill=(122, 142, 158, 220))
draw.polygon([(119, arrow_bottom - 1), (129, arrow_bottom - 1), (124, arrow_bottom + 6)], fill=(122, 142, 158, 220))

whale = Image.new("RGBA", (90, 40), (0, 0, 0, 0))
whale_draw = ImageDraw.Draw(whale)
whale_draw.rounded_rectangle((10, 12, 66, 32), radius=10, fill=(79, 116, 205, 255))
whale_draw.polygon([(66, 17), (88, 22), (66, 29)], fill=(79, 116, 205, 255))
for x in (28, 38, 48):
whale_draw.rectangle((x, 4, x + 7, 12), fill=(102, 139, 224, 255))
whale_draw.ellipse((52, 19, 56, 23), fill=(255, 255, 255, 255))
image.alpha_composite(whale.filter(ImageFilter.GaussianBlur(0.2)), (79, 360))

draw = ImageDraw.Draw(image)
draw.rounded_rectangle((57, 425, 191, 431), radius=3, fill=(193, 207, 218, 150))
return image


if __name__ == "__main__":
build_diagram().save(OUTPUT)
3 changes: 1 addition & 2 deletions environments/Dockerfile-LDAP
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@ CMD ["nautobot-server", "runserver", "0.0.0.0:8080", "--insecure"]

RUN apt-get update && \
apt-get upgrade -y && \
apt-get install -y libldap2-dev libsasl2-dev libssl-dev && \
apt-get autoremove -y && \
apt-get clean all && \
rm -rf /var/lib/apt/lists/*

RUN apt-get install -y libldap2-dev libsasl2-dev libssl-dev

COPY ./pyproject.toml ./poetry.lock /source/
COPY ../plugins /source/plugins
# COPY ./packages /source/packages
Expand Down
8 changes: 5 additions & 3 deletions environments/docker-compose.ldap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ services:
- "-c" # this is to evaluate the $NAUTOBOT_LOG_LEVEL from the env
- "nautobot-server celery worker -l $$NAUTOBOT_LOG_LEVEL --events" ## $$ because of docker-compose
depends_on:
- "nautobot"
nautobot:
condition: "service_healthy"
healthcheck:
interval: "30s"
timeout: "10s"
Expand All @@ -49,13 +50,14 @@ services:
- "-c" # this is to evaluate the $NAUTOBOT_LOG_LEVEL from the env
- "nautobot-server celery beat -l $$NAUTOBOT_LOG_LEVEL" ## $$ because of docker-compose
depends_on:
- "nautobot"
nautobot:
condition: "service_healthy"
healthcheck:
disable: true
<<: *nautobot-base
# ---------------------------------
redis:
image: "redis:alpine"
image: "redis:6-alpine"
env_file:
- "local.env"
- "creds.env"
Expand Down
Loading
Loading