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

Prod deployment Merge to Main #33

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
205f74a
temporal annotation models and endpoints
BryonLewis Feb 6, 2024
e690218
migrations for temporal endpoints
BryonLewis Feb 7, 2024
6333167
Merge branch 'main' into temporal-annotations
BryonLewis Feb 7, 2024
db68837
updating migrations
BryonLewis Feb 7, 2024
e5e4a63
basics of rendering temporal annotations
BryonLewis Feb 7, 2024
3a81aea
linting
BryonLewis Feb 7, 2024
e077453
temporal creation/editing/deletion
BryonLewis Feb 16, 2024
7bf39fb
supporting multiple users with sequence and pulse annotations
BryonLewis Feb 16, 2024
963f3b9
client cleanup of minor issues
BryonLewis Feb 16, 2024
dd9abcd
adding prod-deployment infrastructure
BryonLewis Feb 16, 2024
f489ce1
adding environment variables
BryonLewis Feb 19, 2024
ebeaa0e
reconfigure items
BryonLewis Feb 19, 2024
d7bbe8b
updating prod config
BryonLewis Feb 19, 2024
0f66d76
updating prod config
BryonLewis Feb 19, 2024
a3d3308
allowed hosts
BryonLewis Feb 19, 2024
8cb91ea
adding fake email url
BryonLewis Feb 19, 2024
f2265ca
new configuration
BryonLewis Feb 19, 2024
3dba307
new configuration
BryonLewis Feb 19, 2024
57e6ca3
allowed hosts'
BryonLewis Feb 19, 2024
ac11a79
prod config
BryonLewis Feb 19, 2024
52f100e
add network to celery
BryonLewis Feb 19, 2024
b9fe319
prod
BryonLewis Feb 19, 2024
d37b41e
base configuration
BryonLewis Feb 19, 2024
b305465
adding traefik
BryonLewis Feb 19, 2024
6f01c8a
update traefik
BryonLewis Feb 19, 2024
c0344f4
port fix
BryonLewis Feb 19, 2024
ff04240
traefik config
BryonLewis Feb 19, 2024
fba95a0
traefik config
BryonLewis Feb 19, 2024
4fc3236
remove traefik
BryonLewis Feb 19, 2024
f1bfd2d
prod update
BryonLewis Feb 20, 2024
3ef3cdb
Merge branch 'main' into prod-deployment
BryonLewis Feb 20, 2024
e0d2d65
updating hostname
BryonLewis Feb 20, 2024
f75d9ae
updating hostname
BryonLewis Feb 20, 2024
3cbeba1
traefik routing
BryonLewis Feb 20, 2024
4037929
update nginx
BryonLewis Feb 20, 2024
46cf8bf
updating traefik
BryonLewis Feb 20, 2024
05853fb
reverting to nginx proxy
BryonLewis Feb 20, 2024
beced4d
issuer cert
BryonLewis Feb 20, 2024
2904eb1
trusted origins
BryonLewis Feb 20, 2024
99e08b6
add https mixin to configuration
BryonLewis Feb 20, 2024
a40fab8
update configuration
BryonLewis Feb 20, 2024
bd62f60
remove https
BryonLewis Feb 20, 2024
26f116c
refactoring prod .env files
BryonLewis Feb 20, 2024
e5d2344
refactoring environment files
BryonLewis Feb 21, 2024
f31c841
update docker
BryonLewis Feb 21, 2024
096ce32
linting fix
BryonLewis Feb 21, 2024
ae16c2e
add server hostname to environment
BryonLewis Feb 21, 2024
c6078f4
deployment docs update
BryonLewis Feb 21, 2024
a87023c
deployment docs update
BryonLewis Feb 21, 2024
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
82 changes: 82 additions & 0 deletions DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# bats-ai

## Deployment with Docker (recommended quickstart)

This was a bit rushed so the deployment utilizes a single
docker file `docker-compose.prod.yml` in the root of the directory

I wanted some simple instructions below to configure the deployment

Be sure to use the proper hostname (batdetectai.kitware.com) in
all locations that require it.

## Docker Compose Differences

I created a `client` service which has it's own Dockerfile and
builds the vue client app.
The `client` service also uses a reverse proxy to route
`/api`, `/admin` fields to the django server.
The client will need to be built with a different Client ID
for accessing the server.

### Initial Setup for Deployment

1. Run `docker compose run --rm django ./manage.py migrate`
2. Run `docker compose run --rm django ./manage.py createsuperuser`
and follow the prompts to create your own user
3. Run `docker compose run --rm django ./manage.py makeclient \
--username [email protected] \
--uri https://batdetectai.kitware.com/`
4. Run `docker compose run --rm django ./manage.py loaddata species` to load species
data into the database
5. Run `docker compose run --rm django ./manage.py collectstatic`
to collect the static files
6. Run `docker compose -f docker-compose.prod.yml up` to start the server
add `-d` for a silent version to run in the background
7. Copy over the ./dev/.env.prod.docker-compose.template
to `./dev/.env.prod.docker-compose.template` and change the default passwords
8. Change the ID in the `./client/env.production` to a custom ID - this will
probably require a `docker compose build` to build the app afterwards
9. After creating the basic application log into the django admin `batdetectai.kitware.com/admin`
and change the ApplicationId to the ID in the `./client.env.production`
10. Test logging in/out and uploading data to the server.

### system.d service

Service that will automatically start and launch the server
Create this at `/etc/systemd/system` using sudo

```systemd
[Unit]
Description=batai-server
Requires=docker.service
After=docker.service

[Service]
ExecStartPre=/bin/sleep 10
Environment=PATH=/usr/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin
Restart=always
User=bryon
Group=docker
TimeoutStartSec=300
RestartSec=20
WorkingDirectory=/home/bryon/batai
# Shutdown container (if running) when unit is started
ExecStartPre=docker compose down
# Start container when unit is started
ExecStart=docker compose -f docker-compose.prod.yml up
# Stop container when unit is stopped
ExecStop=docker compose down

[Install]
WantedBy=multi-user.target
```

After run `sudo systemctl enable batai.service`
Then to start you can use `sudo systemctl start batai.service`
Stopping: `sudo systemctl stop batai.service`

### User Management

There is no email server connected up so users need to be
individually approved and their email verified by an admin
32 changes: 28 additions & 4 deletions bats_ai/settings.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import os
from pathlib import Path

from composed_configuration import (
Expand All @@ -10,12 +11,9 @@
ProductionBaseConfiguration,
TestingBaseConfiguration,
)
from composed_configuration._configuration import _BaseConfiguration
from configurations import values

CORS_ALLOWED_ORIGINS = [
'http://localhost:3000',
]


class BatsAiMixin(ConfigMixin):
WSGI_APPLICATION = 'bats_ai.wsgi.application'
Expand Down Expand Up @@ -80,6 +78,32 @@ class TestingConfiguration(BatsAiMixin, TestingBaseConfiguration):
pass


class KitwareConfiguration(BatsAiMixin, _BaseConfiguration):
SECRET_KEY = values.SecretValue()
baseHost = 'batdetectai.kitware.com'
if 'SERVERHOSTNAME' in os.environ:
baseHost = os.environ['SERVERHOSTNAME']
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
DEFAULT_FILE_STORAGE = 'minio_storage.storage.MinioMediaStorage'
MINIO_STORAGE_ENDPOINT = values.Value(
'minio:9000',
)
MINIO_STORAGE_USE_HTTPS = values.BooleanValue(False)
MINIO_STORAGE_ACCESS_KEY = values.SecretValue()
MINIO_STORAGE_SECRET_KEY = values.SecretValue()
MINIO_STORAGE_MEDIA_BUCKET_NAME = values.Value(
environ_name='STORAGE_BUCKET_NAME',
environ_required=True,
)
MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True
MINIO_STORAGE_AUTO_CREATE_MEDIA_POLICY = 'READ_WRITE'
MINIO_STORAGE_MEDIA_USE_PRESIGNED = True
MINIO_STORAGE_MEDIA_URL = 'http://127.0.0.1:9000/django-storage'
ALLOWED_HOSTS = [baseHost]
CSRF_TRUSTED_ORIGINS = [f'https://{baseHost}', f'https://{baseHost}']
CORS_ORIGIN_WHITELIST = [f'https://{baseHost}', f'https://{baseHost}']


class ProductionConfiguration(BatsAiMixin, ProductionBaseConfiguration):
pass

Expand Down
7 changes: 3 additions & 4 deletions client/.env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
VUE_APP_API_ROOT=https://CHANGEME/api/v1
VUE_APP_OAUTH_API_ROOT=https://CHANGEME/oauth/
VUE_APP_OAUTH_CLIENT_ID=CHANGEME
VUE_APP_SENTRY_DSN=CHANGEME
VUE_APP_API_ROOT=https://batdetectai.kitware.com/api/v1
VUE_APP_OAUTH_API_ROOT=https://batdetectai.kitware.com/oauth/
VUE_APP_OAUTH_CLIENT_ID=HSJWFZ2cIpWQOvNyCXyStV9hiOd7DfWeBOCzo4pP
3 changes: 3 additions & 0 deletions client/.env.production.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
VUE_APP_API_ROOT=http://localhost/api/v1
VUE_APP_OAUTH_API_ROOT=http://localhost/oauth/
VUE_APP_OAUTH_CLIENT_ID=HSJWFZ2cIpWQOvNyCXyStV9hiOd7DfWeBOCzo4pP
4 changes: 2 additions & 2 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ git grep CHANGEME

## Recommended IDE Setup

- [VSCode](https://code.visualstudio.com/)
- [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
* [VSCode](https://code.visualstudio.com/)
* [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)

## Type Support For `.vue` Imports in TS

Expand Down
2 changes: 2 additions & 0 deletions dev/.env.docker-compose
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
DJANGO_CONFIGURATION=DevelopmentConfiguration
DJANGO_DATABASE_NAME=django
DJANGO_DATABASE_PASSWORD=postgres
DJANGO_DATABASE_URL=postgres://postgres:postgres@postgres:5432/django
DJANGO_CELERY_BROKER_URL=amqp://rabbitmq:5672/
DJANGO_MINIO_STORAGE_ENDPOINT=minio:9000
Expand Down
13 changes: 13 additions & 0 deletions dev/.env.prod.docker-compose.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
DJANGO_CONFIGURATION=KitwareConfiguration
DJANGO_DATABASE_NAME=django
DJANGO_DATABASE_PASSWORD=postgres
DJANGO_DATABASE_URL=postgres://postgres:postgres@postgres:5432/django
DJANGO_CELERY_BROKER_URL=amqp://rabbitmq:5672/
DJANGO_MINIO_STORAGE_ENDPOINT=minio:9000
DJANGO_MINIO_STORAGE_ACCESS_KEY=minioAccessKey
DJANGO_MINIO_STORAGE_SECRET_KEY=minioSecretKey
DJANGO_STORAGE_BUCKET_NAME=django-storage
DJANGO_MINIO_STORAGE_ENDPOINT=minio:9000
SERVERHOSTNAME=batdetectai.kitware.com
DJANGO_SECRET_KEY=changeme
[email protected]
36 changes: 36 additions & 0 deletions dev/client.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Use official Node.js image as the base image for building Vue.js app
FROM node:16 as build-stage

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY client/package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application
COPY client .

# Build the Vue.js application
RUN npm run build

# Use NGINX as the final base image
FROM nginx:alpine

# Remove default NGINX website
RUN rm -rf /usr/share/nginx/html/*

# Copy built Vue.js app to NGINX HTML directory
COPY --from=build-stage /app/dist /usr/share/nginx/html

RUN ls
# Copy custom NGINX configuration
COPY nginx/nginx.conf /etc/nginx/nginx.conf

# Expose port 80
EXPOSE 80

# Start NGINX
CMD ["nginx", "-g", "daemon off;"]
172 changes: 172 additions & 0 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
version: '3.8'

services:
# COMMENTED OUT UNTIL READY TO TEST
traefik:
restart: always
image: traefik:v2.4
container_name: traefik
env_file: ./dev/.env.prod.docker-compose
networks:
- django-nginx
command: >
--providers.docker=true
--providers.docker.exposedByDefault=false
--log.level=${LOG_LEVEL:-WARN}
--providers.docker.exposedByDefault=false
--providers.file.filename=/var/traefik/dynamic.yml
--entrypoints.web.address=:80
--entrypoints.websecure.address=:443
--entrypoints.websecure.http.tls.certresolver=myresolver
--certificatesresolvers.myresolver.acme.email=${ACME_EMAIL:[email protected]}
--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
--certificatesresolvers.myresolver.acme.httpchallenge=true
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
--certificatesresolvers.myresolver.acme.caserver=${ACME_CA_SERVER:-https://acme-v02.api.letsencrypt.org/directory}
labels:
# Traefik HTTPS Redirect
- "traefik.enable=true"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.rule=Host(`${SERVERHOSTNAME:-batdetectai.kitware.com}`)"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https-mddl@docker"
- "traefik.http.middlewares.redirect-to-https-mddl.redirectscheme.scheme=https"
volumes:
- "${SOCK_PATH:-/var/run/docker.sock}:/var/run/docker.sock"
- "./traefik/letsencrypt:/letsencrypt"
- "./traefik/dynamic.yml:/var/traefik/dynamic.yml:ro"
ports:
- "80:80"
- "443:443"

django:
build:
context: .
dockerfile: ./dev/django.Dockerfile
command: 'gunicorn bats_ai.wsgi:application --bind 0.0.0.0:8000'
# ./manage.py runserver 0.0.0.0:8000 --noreload
# entrypoint: ["/bin/bash"]
# command: ""
# Log printing via Rich is enhanced by a TTY
tty: true
env_file: ./dev/.env.prod.docker-compose
networks:
- django-nginx
volumes:
- .:/opt/django-project
environment:
- SERVERHOSTNAME=${SERVERHOSTNAME:-batdetectai.kitware.com}
ports:
- 8000:8000
depends_on:
- postgres
- rabbitmq
- minio
celery:
build:
context: .
dockerfile: ./dev/django.Dockerfile
command: [
"celery",
"--app", "bats_ai.celery",
"worker",
"--loglevel", "INFO",
"--without-heartbeat"
]
# Docker Compose does not set the TTY width, which causes Celery errors
tty: false
env_file: ./dev/.env.prod.docker-compose
networks:
- django-nginx
volumes:
- .:/opt/django-project
depends_on:
- postgres
- rabbitmq
- minio
client:
build:
context: .
dockerfile: ./dev/client.Dockerfile
env_file: ./dev/.env.prod.docker-compose
networks:
- django-nginx
depends_on:
- django
labels:
- "traefik.http.routers.client-rtr.entrypoints=websecure"
- "traefik.http.routers.client-rtr.rule=Host(`${SERVERHOSTNAME:-batdetectai.kitware.com}`)"
- "traefik.enable=true"
- "traefik.http.services.client-svc.loadbalancer.server.port=80"
postgres:
image: postgis/postgis:latest
env_file: ./dev/.env.prod.docker-compose
environment:
- POSTGRES_DB=${DJANGO_DATABASE_NAME:-django}
- POSTGRES_PASSWORD=${DJANGO_MINIO_STORAGE_SECRET_KEY:-postgres}
networks:
- django-nginx
ports:
- ${DOCKER_POSTGRES_PORT-5432}:5432
volumes:
- postgres:/var/lib/postgresql/data

rabbitmq:
env_file: ./dev/.env.prod.docker-compose
image: rabbitmq:management
networks:
- django-nginx
ports:
- ${DOCKER_RABBITMQ_PORT-5672}:5672
- ${DOCKER_RABBITMQ_CONSOLE_PORT-15672}:15672
volumes:
- rabbitmq:/var/lib/rabbitmq/mnesia

minio:
image: minio/minio:latest
# When run with a TTY, minio prints credentials on startup
tty: true
command: ["server", "/data", "--console-address", ":${DOCKER_MINIO_CONSOLE_PORT-9001}"]
env_file: ./dev/.env.prod.docker-compose
environment:
- MINIO_ROOT_USER=${DJANGO_MINIO_STORAGE_ACCESS_KEY:-minioAccessKey}
- MINIO_ROOT_PASSWORD=${DJANGO_DATABASE_PASSWORD:-minioSecretKey}
networks:
- django-nginx
ports:
- ${DOCKER_MINIO_PORT-9000}:9000
- ${DOCKER_MINIO_CONSOLE_PORT-9001}:9001
volumes:
- minio:/data

flower:
env_file: ./dev/.env.prod.docker-compose
build:
context: .
dockerfile: ./dev/django.Dockerfile
command: [
"celery",
"--app", "bats_ai.celery",
"flower"
]
tty: false
volumes:
- .:/opt/django-project
networks:
- django-nginx
ports:
- ${DOCKER_FLOWER_PORT-5555}:5555
depends_on:
- postgres
- rabbitmq
- minio
- celery

volumes:
postgres:
sourcedb:
minio:
rabbitmq:

networks:
django-nginx:
driver: bridge
Loading
Loading