Frontend + Backend Integrated Deployment (K8s only) (#45)
* support running backend + frontend together on k8s * split nginx container into separate frontend service, which uses nignx-base image and the static frontend files * add nginx-based frontend image to docker-compose build (for building only, docker-based combined deployment not yet supported) * backend: - fix paths for email templates - chart: support '--set backend_only=1' and '--set frontend_only=1' to only force deploy one or the other - run backend from root /api in uvicorn
This commit is contained in:
parent
87bef6d967
commit
05c1129fb8
@ -8,5 +8,5 @@ RUN pip install -r requirements.txt
|
||||
|
||||
ADD . /app
|
||||
|
||||
CMD uvicorn main:app --host 0.0.0.0 --reload --access-log --log-level debug
|
||||
CMD uvicorn main:app --host 0.0.0.0 --root-path /api --reload --access-log --log-level debug
|
||||
|
||||
|
@ -37,7 +37,7 @@ class EmailSender:
|
||||
message = f"""
|
||||
Please verify your registration for Browsertrix Cloud for {receiver_email}
|
||||
|
||||
You can verify by clicking here: {self.host}/app/verify/{token}
|
||||
You can verify by clicking here: {self.host}/verify?token={token}
|
||||
|
||||
The verification token is: {token}"""
|
||||
|
||||
@ -49,7 +49,7 @@ The verification token is: {token}"""
|
||||
message = f"""
|
||||
You are invited by {sender} to join their archive, {archive_name} on Browsertrix Cloud!
|
||||
|
||||
You can join by clicking here: {self.host}/app/join/{token}
|
||||
You can join by clicking here: {self.host}/join/{token}?email={receiver_email}
|
||||
|
||||
The invite token is: {token}"""
|
||||
|
||||
|
@ -21,3 +21,4 @@
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
frontend/
|
||||
|
@ -63,8 +63,10 @@ http {
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:8000/;
|
||||
proxy_set_header Host $host;
|
||||
#proxy_pass http://localhost:8000/;
|
||||
#proxy_set_header Host $host;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,35 +2,28 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.name }}
|
||||
name: {{ .Values.name }}-backend
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Values.name }}
|
||||
role: backend
|
||||
replicas: {{ .Values.api_num_replicas }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Values.name }}
|
||||
role: backend
|
||||
|
||||
annotations:
|
||||
# force helm to update the deployment each time
|
||||
{{- if not .Values.frontend_only }}
|
||||
"helm.update": {{ randAlphaNum 5 | quote }}
|
||||
{{- end }}
|
||||
|
||||
spec:
|
||||
volumes:
|
||||
- name: nginx-config
|
||||
configMap:
|
||||
name: nginx-config
|
||||
items:
|
||||
- key: nginx.conf
|
||||
path: nginx.conf
|
||||
|
||||
- name: nginx-resolver
|
||||
emptyDir: {}
|
||||
|
||||
initContainers:
|
||||
{{- if .Values.minio_local }}
|
||||
- name: init-bucket
|
||||
@ -47,42 +40,8 @@ spec:
|
||||
args: ['-c', 'mc mb --ignore-existing local/{{ .Values.minio_local_bucket_name }}' ]
|
||||
{{- end }}
|
||||
|
||||
- name: init-nginx
|
||||
image: {{ .Values.nginx_image }}
|
||||
command: ["/bin/sh"]
|
||||
args: ["-c", "echo resolver $(awk 'BEGIN{ORS=\" \"} $1==\"nameserver\" {print $2}' /etc/resolv.conf) valid=30s \";\" > /etc/nginx/resolvers/resolvers.conf"]
|
||||
volumeMounts:
|
||||
- name: nginx-resolver
|
||||
mountPath: /etc/nginx/resolvers/
|
||||
|
||||
|
||||
containers:
|
||||
- name: nginx
|
||||
image: {{ .Values.nginx_image }}
|
||||
imagePullPolicy: {{ .Values.nginx_pull_policy }}
|
||||
volumeMounts:
|
||||
- name: nginx-config
|
||||
mountPath: /etc/nginx/nginx.conf
|
||||
subPath: nginx.conf
|
||||
readOnly: true
|
||||
|
||||
- name: nginx-resolver
|
||||
mountPath: /etc/nginx/resolvers/
|
||||
readOnly: true
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: {{ .Values.nginx_limit_cpu }}
|
||||
|
||||
requests:
|
||||
cpu: {{ .Values.nginx_requests_cpu }}
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
|
||||
|
||||
- name: api
|
||||
image: {{ .Values.api_image }}
|
||||
imagePullPolicy: {{ .Values.api_pull_policy }}
|
||||
@ -101,6 +60,13 @@ spec:
|
||||
cpu: {{ .Values.api_requests_cpu }}
|
||||
memory: {{ .Values.api_requests_memory }}
|
||||
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8000
|
||||
failureThreshold: 30
|
||||
periodSeconds: 5
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
@ -112,9 +78,10 @@ kind: Service
|
||||
|
||||
metadata:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
name: {{ .Values.name }}
|
||||
name: {{ .Values.name }}-backend
|
||||
labels:
|
||||
app: {{ .Values.name }}
|
||||
role: backend
|
||||
|
||||
{{- if .Values.service }}
|
||||
{{- if .Values.service.annotations }}
|
||||
@ -128,6 +95,7 @@ metadata:
|
||||
spec:
|
||||
selector:
|
||||
app: {{ .Values.name }}
|
||||
role: backend
|
||||
|
||||
{{- if .Values.service }}
|
||||
{{- if .Values.service.type }}
|
||||
@ -137,7 +105,5 @@ spec:
|
||||
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
port: 8000
|
||||
name: api
|
||||
#externalIPs:
|
||||
# - 127.0.0.1
|
@ -45,3 +45,6 @@ metadata:
|
||||
|
||||
data:
|
||||
{{ (.Files.Glob "*.conf").AsConfig | indent 2 }}
|
||||
|
||||
#{{ (.Files.Glob "frontend/*.*").AsConfig | indent 2 }}
|
||||
|
||||
|
99
chart/templates/frontend.yaml
Normal file
99
chart/templates/frontend.yaml
Normal file
@ -0,0 +1,99 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Values.name }}-frontend
|
||||
namespace: {{ .Release.Namespace }}
|
||||
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: {{ .Values.name }}
|
||||
role: frontend
|
||||
replicas: 1
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: {{ .Values.name }}
|
||||
role: frontend
|
||||
|
||||
annotations:
|
||||
# force helm to update the deployment each time
|
||||
{{- if not .Values.backend_only }}
|
||||
"helm.update": {{ randAlphaNum 5 | quote }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
spec:
|
||||
volumes:
|
||||
- name: nginx-resolver
|
||||
emptyDir: {}
|
||||
|
||||
initContainers:
|
||||
- name: init-nginx
|
||||
image: {{ .Values.nginx_image }}
|
||||
command: ["/bin/sh"]
|
||||
args: ["-c", "echo resolver $(awk 'BEGIN{ORS=\" \"} $1==\"nameserver\" {print $2}' /etc/resolv.conf) valid=30s \";\" > /etc/nginx/resolvers/resolvers.conf"]
|
||||
volumeMounts:
|
||||
- name: nginx-resolver
|
||||
mountPath: /etc/nginx/resolvers/
|
||||
|
||||
|
||||
containers:
|
||||
- name: nginx
|
||||
image: {{ .Values.nginx_image }}
|
||||
imagePullPolicy: {{ .Values.nginx_pull_policy }}
|
||||
volumeMounts:
|
||||
- name: nginx-resolver
|
||||
mountPath: /etc/nginx/resolvers/
|
||||
readOnly: true
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: {{ .Values.nginx_limit_cpu }}
|
||||
|
||||
requests:
|
||||
cpu: {{ .Values.nginx_requests_cpu }}
|
||||
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 80
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
|
||||
metadata:
|
||||
namespace: {{ .Release.Namespace }}
|
||||
name: {{ .Values.name }}-frontend
|
||||
labels:
|
||||
app: {{ .Values.name }}
|
||||
role: frontend
|
||||
|
||||
{{- if .Values.service }}
|
||||
{{- if .Values.service.annotations }}
|
||||
annotations:
|
||||
{{- range $key, $val := .Values.service.annotations }}
|
||||
{{ $key }}: {{ $val | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
spec:
|
||||
selector:
|
||||
app: {{ .Values.name }}
|
||||
role: frontend
|
||||
|
||||
{{- if .Values.service }}
|
||||
{{- if .Values.service.type }}
|
||||
type: {{ .Values.service.type | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
name: frontend
|
||||
|
@ -40,11 +40,19 @@ spec:
|
||||
number: 9000
|
||||
{{- end }}
|
||||
|
||||
- path: /api/(.*)
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: browsertrix-cloud-backend
|
||||
port:
|
||||
number: 8000
|
||||
|
||||
- path: /(.*)
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: browsertrix-cloud
|
||||
name: browsertrix-cloud-frontend
|
||||
port:
|
||||
number: 80
|
||||
|
||||
|
@ -17,6 +17,12 @@ services:
|
||||
- minio
|
||||
- mongo
|
||||
|
||||
frontend:
|
||||
build: ./frontend
|
||||
image: registry.digitalocean.com/btrix/webrecorder/browsertrix-frontend
|
||||
ports:
|
||||
- 8010:80
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
command: redis-server --appendonly yes
|
||||
|
6
frontend/Dockerfile
Normal file
6
frontend/Dockerfile
Normal file
@ -0,0 +1,6 @@
|
||||
FROM nginx
|
||||
|
||||
COPY ./index.html /usr/share/nginx/html
|
||||
COPY ./dist/main.js /usr/share/nginx/html
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
|
78
frontend/nginx.conf
Normal file
78
frontend/nginx.conf
Normal file
@ -0,0 +1,78 @@
|
||||
worker_processes 1;
|
||||
error_log stderr;
|
||||
pid /var/run/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
access_log /dev/stdout;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
include ./resolvers/resolvers.conf;
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
proxy_buffering off;
|
||||
proxy_buffers 16 64k;
|
||||
proxy_buffer_size 64k;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
error_page 500 501 502 503 504 /50x.html;
|
||||
|
||||
merge_slashes off;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
# fallback to index for any page
|
||||
error_page 404 /index.html;
|
||||
|
||||
location ~* /watch/([^/]+)/([^/]+)/ws {
|
||||
set $archive $1;
|
||||
set $crawl $2;
|
||||
#auth_request /authcheck;
|
||||
|
||||
proxy_pass http://$2.crawlers.svc.cluster.local:9037/ws;
|
||||
proxy_set_header Host "localhost";
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $http_connection;
|
||||
}
|
||||
|
||||
location ~* /watch/([^/]+)/([^/]+)/ {
|
||||
set $archive $1;
|
||||
set $crawl $2;
|
||||
#auth_request /authcheck;
|
||||
|
||||
proxy_pass http://$2.crawlers.svc.cluster.local:9037/;
|
||||
proxy_set_header Host "localhost";
|
||||
}
|
||||
|
||||
location = /authcheck {
|
||||
internal;
|
||||
proxy_pass http://localhost:8000/archives/$archive/crawls/$crawl;
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
}
|
||||
|
||||
location /healthz {
|
||||
return 200;
|
||||
}
|
||||
|
||||
location / {
|
||||
#proxy_pass http://localhost:8000/;
|
||||
#proxy_set_header Host $host;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ const shoelaceAssetsSrcPath = path.resolve(
|
||||
__dirname,
|
||||
"node_modules/@shoelace-style/shoelace/dist/assets"
|
||||
);
|
||||
const shoelaceAssetsPublicPath = "/shoelace/assets";
|
||||
const shoelaceAssetsPublicPath = "shoelace/assets";
|
||||
|
||||
module.exports = {
|
||||
entry: "./src/index.ts",
|
||||
@ -63,7 +63,7 @@ module.exports = {
|
||||
static: [
|
||||
{
|
||||
directory: shoelaceAssetsSrcPath,
|
||||
publicPath: shoelaceAssetsPublicPath,
|
||||
publicPath: "/" + shoelaceAssetsPublicPath,
|
||||
},
|
||||
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user