feat: add development Docker stack

This commit is contained in:
Julian Lobbes 2023-03-14 16:28:52 +01:00
parent 6844c50497
commit 2d845afd56
10 changed files with 236 additions and 8 deletions

View file

@ -7,7 +7,7 @@ This is a template for building a webapp using the following tech stack:
- [Vue Router](https://router.vuejs.org/guide/) - frontend router for Vue
- [TailwindCSS](https://tailwindcss.com/docs/configuration) - CSS framework
- [Axios](https://axios-http.com/docs/intro) - library for making API requests from the frontend
- [Parcel](/https://parceljs.org/docs/) - bundler used for frontend module resolution and asset optimization
- [Parcel](https://parceljs.org/docs/) - bundler used for frontend module resolution and asset optimization
- [npm](https://docs.npmjs.com/cli/v7/configuring-npm/package-json) - node package manager for JavaScript packages
- **Backend**:
- [FastAPI](https://fastapi.tiangolo.com/) - Python-framework for backend APIs
@ -35,6 +35,11 @@ docker-compose -f production.docker-compose.yml up -d --force-recreate --build -
This starts two containers, called `backend` and `frontend`, and exposes the deployed web application on port `8000`.
You can open a browser and navigate to [http:localhost:8000/](http:localhost:8000/) to try it out.
To stop the containers, run the following as root:
```sh
docker-compose -f production.docker-compose.yml down
```
### Production Frontend Container
The `frontend` production Docker image is built according to `production.frontend.Dockerfile`.
@ -74,7 +79,25 @@ This prevents repetition when writing your endpoint code and allows you to acces
## Development Configuration
> **TODO:** implement
The development configuration can be deployed using the `development.docker-compose.yml` file, by running (as root):
```sh
docker-compose -f development.docker-compose.yml up -d --build --force-recreate --remove-orphans
```
This starts three containers, called `backend`, `parcel` and `frontend`, and exposes the deployed web application on port `8000`.
You can open a browser and navigate to [http:localhost:8000/](http:localhost:8000/) to try it out.
To stop the containers, run the following as root:
```sh
docker-compose -f development.docker-compose.yml down
```
The development containers work very similarly to the production containers, with the exception that hot reloading of configuration
and sourcecode files is enabled.
This means that when you modify the frontend or backend sourcecode while the development container is running, the changes are detected
and applied automatically, restarting the containers is usually not required.
# Customization
@ -132,5 +155,6 @@ Depending on the configuration you wish to customize, please refer to each compo
# TODOs / Roadmap
1. Add a development Docker configuration
1. Add sqlite/postgres containers
2. Minify CSS in prod
3. Add testing tools

38
development.Caddyfile Normal file
View file

@ -0,0 +1,38 @@
:3000 {
encode zstd gzip
@staticfiles {
method GET
path /static/*
}
handle @staticfiles {
file_server {
root /app/public/
}
}
handle_path /api/* {
reverse_proxy * backend:3001
}
@robots {
method GET
path /robots.txt
path /sitemap.xml
}
handle @robots {
file_server {
root /app/robots/
}
}
handle * {
reverse_proxy * parcel:1234
}
log {
output stderr
format console
}
}

View file

@ -0,0 +1,25 @@
# syntax=docker/dockerfile:1
FROM python:3
# Create non-root user
ARG CUSTOM_UID
ARG CUSTOM_GID
ENV CUSTOM_USERNAME=backend
ENV CUSTOM_GROUPNAME=backend
RUN groupadd --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
useradd --uid ${CUSTOM_UID:-1000} --gid ${CUSTOM_GID:-1000} --create-home --shell /bin/bash ${CUSTOM_USERNAME} && \
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app
# Copy source files
WORKDIR /app
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} requirements.txt /app/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} backend /app/backend/
# Install dependencies
RUN pip install -r requirements.txt
# Run ASGI server
EXPOSE 3001/tcp
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
ENTRYPOINT ["uvicorn", "backend.main:app", "--root-path", "/api", "--host", "0.0.0.0", "--port", "3001", "--access-log", "--use-colors", "--log-level", "debug", "--reload"]

View file

@ -0,0 +1,54 @@
---
version: "3"
services:
frontend:
container_name: frontend
restart: unless-stopped
build:
context: .
dockerfile: ./development.frontend.Dockerfile
args:
CUSTOM_UID: 1000
CUSTOM_GID: 1000
environment:
TZ: Europe/Berlin
ports:
- "8000:3000"
volumes:
- ./public/:/app/public/:ro
- ./robots/:/app/robots/:ro
- ./Caddyfile:/app/Caddyfile:ro
parcel:
container_name: parcel
restart: unless-stopped
build:
context: .
dockerfile: ./development.parcel.Dockerfile
args:
CUSTOM_UID: 1000
CUSTOM_GID: 1000
volumes:
- ./frontend/:/app/frontend/:ro
- ./postcss.config.js:/app/postcss.config.js:ro
- ./.postcssrc:/app/.postcssrc:ro
- ./tailwind.config.js:/app/tailwind.config.js:ro
expose:
- "1234"
backend:
container_name: backend
restart: unless-stopped
build:
context: .
dockerfile: ./development.backend.Dockerfile
args:
CUSTOM_UID: 1000
CUSTOM_GID: 1000
expose:
- "3001"
volumes:
- ./backend/:/app/backend/:ro
- ./requirements.txt:/app/requirements.txt:ro
...

View file

@ -0,0 +1,33 @@
# syntax=docker/dockerfile:1
FROM debian:latest
# Install Caddy
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y curl && \
apt install -y debian-keyring debian-archive-keyring apt-transport-https && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list && \
apt update && apt install -y caddy && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
ARG CUSTOM_UID
ARG CUSTOM_GID
ENV CUSTOM_USERNAME=webserver
ENV CUSTOM_GROUPNAME=webserver
RUN groupadd --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
useradd --uid ${CUSTOM_UID:-1000} --gid ${CUSTOM_GID:-1000} --create-home --shell /bin/bash ${CUSTOM_USERNAME} && \
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app
# Copy source files
WORKDIR /app
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} development.Caddyfile /app/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} public /app/public/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} robots /app/robots/
# Install dependencies
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
# Run Caddy in development mode
ENTRYPOINT ["caddy", "run", "--config", "/app/development.Caddyfile", "--adapter", "caddyfile", "--watch"]

View file

@ -0,0 +1,34 @@
# syntax=docker/dockerfile:1
FROM debian:latest
# Install packages
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && apt install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_19.x | bash - && apt-get install -y nodejs && \
apt install -y debian-keyring debian-archive-keyring apt-transport-https && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list && \
apt update && apt install -y caddy && \
rm -rf /var/lib/apt/lists/*
# Create non-root user
ARG CUSTOM_UID
ARG CUSTOM_GID
ENV CUSTOM_USERNAME=webserver
ENV CUSTOM_GROUPNAME=webserver
RUN groupadd --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
useradd --uid ${CUSTOM_UID:-1000} --gid ${CUSTOM_GID:-1000} --create-home --shell /bin/bash ${CUSTOM_USERNAME} && \
mkdir /app && chown ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000} /app
# Copy source files
WORKDIR /app
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} package.json package-lock.json .postcssrc postcss.config.js tailwind.config.js /app/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} frontend /app/frontend/
# Install dependencies
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
RUN npm install
# Run the parcel file watcher
ENTRYPOINT ["npx", "parcel", "frontend/src/html/index.html"]

View file

@ -1,6 +1,26 @@
<script>
import axios from 'axios';
export default {
data () {
return {
hello: null
}
},
mounted () {
axios
.get('/api/hello/')
.then(response => (this.hello = response.data))
.catch(error => console.log(error));
}
}
</script>
<template>
<div class="p-8">
<h1 class="font-accent text-5xl">Hello World!</h1>
<p>If you're seeing this text, Vue.js is working correctly!</p>
<h1 class="font-accent text-5xl">Hello!</h1>
<p>If you're seeing this text, Vue.js is working correctly.</p>
<p>The backend API responded with: {{ hello }}</p>
</div>
</template>

View file

@ -22,4 +22,4 @@ RUN pip install -r requirements.txt
# Run ASGI server
EXPOSE 3001/tcp
USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
ENTRYPOINT ["uvicorn", "backend.main:app", "--root-path", "/api", "--host", "0.0.0.0", "--port", "3001"]
ENTRYPOINT ["uvicorn", "backend.main:app", "--root-path", "/api", "--host", "0.0.0.0", "--port", "3001", "--access-log"]

View file

@ -23,7 +23,7 @@ RUN groupadd --gid ${CUSTOM_GID:-1000} ${CUSTOM_GROUPNAME} && \
# Copy source files
WORKDIR /app
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} package.json package-lock.json .postcssrc postcss.config.js tailwind.config.js Caddyfile /app/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} package.json package-lock.json .postcssrc postcss.config.js tailwind.config.js production.Caddyfile /app/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} frontend /app/frontend/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} public /app/public/
COPY --chown=${CUSTOM_USERNAME}:${CUSTOM_GROUPNAME} robots /app/robots/
@ -33,4 +33,4 @@ USER ${CUSTOM_UID:-1000}:${CUSTOM_GID:-1000}
RUN npm install
RUN npx parcel build frontend/src/html/index.html
ENTRYPOINT ["caddy", "run", "--config", "/app/Caddyfile"]
ENTRYPOINT ["caddy", "run", "--config", "/app/production.Caddyfile"]