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

Docker: Add initial Docker deployment setup #75

Merged
merged 1 commit into from
Mar 15, 2024
Merged
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
71 changes: 71 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Build

on:
pull_request:
paths-ignore:
- 'README.md'
- 'LICENSE'
- '.github/**'
- '!.github/workflows/build.yml'
push:
branches:
- main
tags: '[0-9]+.[0-9]+.[0-9]+'
paths-ignore:
- 'README.md'
- 'LICENSE'
- '.github/**'
- '!.github/workflows/build.yml'
release:
types:
- created

jobs:
docker:
name: Build and Push Docker Image
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'ls1intum/Pyris' }}
runs-on: ubuntu-latest
steps:
- name: Compute Tag
uses: actions/github-script@v6
id: compute-tag
with:
result-encoding: string
script: |
if (context.eventName === "pull_request") {
return "pr-" + context.issue.number;
}
if (context.eventName === "release") {
return "latest";
}
if (context.eventName === "push") {
if (context.ref.startsWith("refs/tags/")) {
return context.ref.slice(10);
}
if (context.ref === "refs/heads/main") {
return "latest";
}
}
return "FALSE";
- uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# Build and Push to GitHub Container Registry
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
if: ${{ steps.compute-tag.outputs.result != 'FALSE' }}
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push to GitHub Container Registry
uses: docker/build-push-action@v4
if: ${{ steps.compute-tag.outputs.result != 'FALSE' }}
with:
platforms: amd64, arm64
file: ./Dockerfile
context: .
tags: ghcr.io/ls1intum/pyris:${{ steps.compute-tag.outputs.result }}
push: true
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
#######################
# Custom rules #
#######################
application.local.yml
llm_config.local.yml


########################
# Auto-generated rules #
########################
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Dockerfile to build a container image for a Python 3.12 FastAPI application
FROM python:3.12-slim

# Set the working directory in the container
WORKDIR /app

# Copy the dependencies file to the working directory
COPY requirements.txt .

# Install any dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy the content of the local src directory to the working directory
COPY app/ ./app

# Specify the command to run on container start
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
35 changes: 35 additions & 0 deletions docker/nginx.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ----------------------------------------------------------------------------------------------------------------------
# Nginx base service
# ----------------------------------------------------------------------------------------------------------------------

services:
nginx:
container_name: pyris-nginx
image: nginx:1.23
pull_policy: always
volumes:
- ./nginx/timeouts.conf:/etc/nginx/conf.d/timeouts.conf:ro
- ./nginx/pyris-nginx.conf:/etc/nginx/conf.d/pyris-nginx.conf:ro
- ./nginx/pyris-server.conf:/etc/nginx/includes/pyris-server.conf:ro
- ./nginx/dhparam.pem:/etc/nginx/dhparam.pem:ro
- ./nginx/nginx_502.html:/usr/share/nginx/html/502.html:ro
- ./nginx/70-pyris-setup.sh:/docker-entrypoint.d/70-pyris-setup.sh
- ./nginx/certs/pyris-nginx+4.pem:/certs/fullchain.pem:ro
- ./nginx/certs/pyris-nginx+4-key.pem:/certs/priv_key.pem:ro
ports:
- "80:80"
- "443:443"
# expose the port to make it reachable docker internally even if the external port mapping changes
expose:
- "80"
- "443"
healthcheck:
test: service nginx status || exit 1
start_period: 60s
networks:
- pyris

networks:
pyris:
driver: "bridge"
name: pyris
2 changes: 2 additions & 0 deletions docker/nginx/70-pyris-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# disable default.conf
mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.disabled || true
28 changes: 28 additions & 0 deletions docker/nginx/certs/pyris-nginx+4-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQCuCJSHStSQd02f
j+IlQFes7pVUcYv2r0qm5qicGwPcKQf1/nmsy6k4WhE9HQV9VO9LQ4doSNp9NuYX
P/JQqdYLZYYQvxHS+fR7ofIPjirsrbQYAkG5F6imM8H7MkkueG3HGqaKD54PBmC4
BgBJDFWiF8jSNSYNKOE2L5SaYG/g3LLIkWBlhBQHgrprkio4pv5Y44+nf+hGWkSj
bkRo2+PmIsNmQrpDB2o0O7uoyswa71HE967n9K17SWZ7Hi4kP6BGUWn65P5JB10a
6kz0y8183Uzz99bx8hzxLPg6VNiJZQ+dH4M1Jn6kysKiyV4x24JsM9s6t+Vhln9E
KX5ktosdAgMBAAECggEBAJs3ddkwqWLrtOSR/H2C5G+NHsyAtPdgIfG3mTwZcBjk
03/X5gdyYUusMOHTx3ifzwjOgq9FAvFYjGDCHMlKoGfrtWWsNCZ53k6CApVTE/+h
cRVUte9yJW2Ojf0PPWvf5vEEWPKbuTnnU03ttEVyZdG66tZoprZn9m1QhHYnesEO
PMPvYMd3Oyko8MD/Rr1A/KS/rmc0yfUvgLsqF6PLxq3NKxyVD/8Tp4u9aXbPMnd2
vugVxjjvt5ubscF1Owi8EjqjVkXlw94JzLcy70XfBzsS2EvUtX/hmHgBEsViXUOQ
KGVyeFTvuReq0RvLQi1LA8vs2q6UC0ZYX75wGDfWWnUCgYEAyP6FY6xdP+N83qEM
TzAf2a33bBCcD5zbrfsvYwHwdzcAz9HBdf3TN1ZcbgfIzIWvuo+hFdjZd32E2+b7
tSGpcs21iZ3dn1aWxngNs/h94h6cNak/02iCbOsmMX9rHfKZd1ODnQyA8q0s9PQY
uWWWMUfqPse7mSYbgU0aYOVFraMCgYEA3ak3N2mTgTVsUqhNyZCJlmtafp0tsT6b
/7GKSqkl741wokM6un3wx1eo6Q95mngxOlY2xxq9OChnNSEa9ZQnzdUDtQ0YE4QD
09awTIMHNCeSqpV2n3Yv2fT3C5Ya5/WEtYGpVAtqgxwWPij8+VMOa8MVzy+/v6Hg
N1Tpww+Y8D8CgYEAhbEGeK4FuKFQRaVJ0sJn7RrSIIdLxvbHCIqzkl+P2zwyxgj3
bcxP2dcP1ABJiADESouO0kFTJS/QV5TkiC7DzyEVR1xCNeIamBjyxGrdELLbpLXX
Rn+VgW1IElR2o4zil4RtXuEaRFD8PlK+v1La/ByhqvCfz9aRJQhsK1dVaZECgYEA
jRYR0TFf89P/OLVrnapkCNwX45ND7Bc/0AY/UbpMLSfH02AbV2yl/xvqpT12Vz29
h7Ysc5qvabk9x/FkaX99vmOhUnIdKv7SONnjqS+VPDsb/XvY3zKozoA/Zp6KTa5W
Y/k9wALsLruH5NTOABw/h5PKo+9uixkLz+w6Ri/9Vp0CgYEAqfkZJe7vCOIwtIwj
Mq5knkJgR+Vq30i4jRoFU0yxIcWA1hODVBnK39+mtA++/3+r5DY5fGRTc9mMyXU/
y2N2nfSnvPMAUaRmisB7NhmvinEgymlrX+WE+7S9/+nOQADxzWSc6Hxg/ub6mTYV
k2/hv9uG1gbm2+OBP/EBOr48jz0=
-----END PRIVATE KEY-----
25 changes: 25 additions & 0 deletions docker/nginx/certs/pyris-nginx+4.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIERjCCAq6gAwIBAgIQSQ2vfdquHAQcrzbEKx46mzANBgkqhkiG9w0BAQsFADBf
MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGjAYBgNVBAsMEXJvb3RA
MWY0ZmQzNzYzNmMyMSEwHwYDVQQDDBhta2NlcnQgcm9vdEAxZjRmZDM3NjM2YzIw
HhcNMjIxMjA1MDk0NTEzWhcNMjUwMzA1MDk0NTEzWjBFMScwJQYDVQQKEx5ta2Nl
cnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxGjAYBgNVBAsMEXJvb3RAMWY0ZmQz
NzYzNmMyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArgiUh0rUkHdN
n4/iJUBXrO6VVHGL9q9KpuaonBsD3CkH9f55rMupOFoRPR0FfVTvS0OHaEjafTbm
Fz/yUKnWC2WGEL8R0vn0e6HyD44q7K20GAJBuReopjPB+zJJLnhtxxqmig+eDwZg
uAYASQxVohfI0jUmDSjhNi+UmmBv4NyyyJFgZYQUB4K6a5IqOKb+WOOPp3/oRlpE
o25EaNvj5iLDZkK6QwdqNDu7qMrMGu9RxPeu5/Ste0lmex4uJD+gRlFp+uT+SQdd
GupM9MvNfN1M8/fW8fIc8Sz4OlTYiWUPnR+DNSZ+pMrCosleMduCbDPbOrflYZZ/
RCl+ZLaLHQIDAQABo4GXMIGUMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDATAfBgNVHSMEGDAWgBSpuKALkiwfLnQmm7+JNG2bxGAIgzBMBgNVHREE
RTBDgg1hcnRlbWlzLW5naW54gg9hcnRlbWlzLmV4YW1wbGWCCWxvY2FsaG9zdIcE
fwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAYEApG8ZADQe
SsH/nqH9WpR3ZkYg0rm8pw+YquBNUdDFG2/4IQtaaxrgsvNPrEEMXfCO4vvnC0cH
6Tgay8LzFZxU9D1F06VZ9S1C7KNnYSsjgwhW7wxem1JXgauoutA8D0uHLr/2bVnz
rTShQT7gRp9SRunqDylaSkgpXlfZQRlEANrYT8Jh6LIHRjkxLh/etw7VdFA6Tywh
iQGBE/EbQcGpmqHBoMytblku0D8H+pcFHZ03AZq0FTMbByM9GekQ8HJV88epqvqJ
7pWyQPX9lr7yC6n121dPoA0ylP8D7jIBCmlFeF+QWCiRAgdeb1w+JONHMgI97IR+
9HBm6gGE+Da/TRq82w02tUN/F7NHdzqwKGx/GKLrEsdNlfP6D9iiVtfBGBoAUm+C
2t3jbQEgqYHA+mzadS75RGJsRnVdY24IHvNjEnESW6KCaSfQyMmp3trRH6JeOttU
2JeqRPjmOzNvzIcB76w1/hB2ljhimyfoxB8Gbrts+GFPRZE+AXg1mvCn
-----END CERTIFICATE-----
13 changes: 13 additions & 0 deletions docker/nginx/dhparam.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-----BEGIN DH PARAMETERS-----
MIICCAKCAgEAiT9FabLCTYkbfSNvenLC4q5kWMNIjCUTcYWZpt8xOlmrJQqgui9h
lKXP3hDe0J50oqYUkDQ+YS8i+GCVLzAJqXixqynLqrz/v5IWgloQMJJKlPBEl9M6
/Kh40+VyasVz7toja4qbyN12Kz1S8qLOlmxCcPmOpxGwIUG2yYSuZH9JQ724Gnji
4puFw3CXDm6aBZWBb7cpwEvHSVW5C9R+Acph7ahCby9kWpLrLsNHL73+jJiJSGcx
hig+Yie2XTTlUBHVcxlHCZu8pFXA40hLuagejmXGuVfaaoezMyU1OpfrJpsJSE2s
OxFEt01nCaEguNn7L1dr46fHWux651/UCRHR8MB0J6KEOuKDhgQ8bq+WSGlowaJM
NGhGxAlFH98D/gbOrVcRxJDpmaSFVVwO4piDT/pBDvzaS6Ll8dnoKLv8TNa2r7dG
gedlnJ2gIhU3lLLjqIwe+fmrfhlr3ybwuIiSx/efEaw65vDnOkOHeKKXtbxUAMS6
07bLIKLEw4QRwMmrLhzu2sZnFipAppXjsQ8tRa/QO4eoaEM97FKq6qONVwAA2if6
l3amSySYVDvMYpaOwQYawKTole1Kon06h8JlIr+A5W3vmraMfQZZY72HAkxuOYH0
wchOEYKU+jlmutbEdz747Ngleb5kp55CtL/PlEawEpqXWWXYBqo8mmMCAQI=
-----END DH PARAMETERS-----
27 changes: 27 additions & 0 deletions docker/nginx/nginx_502.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- ansible-managed -->
<!DOCTYPE html>
<html lang="en">
<title>Pyris Maintenance</title>
<meta http-equiv="refresh" content="5"/>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">
<style>
html, body { padding: 0; margin: 0; width: 100%; height: 100%; }
* {box-sizing: border-box;}
body { text-align: center; padding: 0; background: #353d47; color: #fff; font-family: Open Sans; }
h1 { font-size: 50px; font-weight: 100; text-align: center;}
body { font-family: Open Sans; font-weight: 100; font-size: 20px; color: #fff; text-align: center; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -ms-flex-align: center; align-items: center;}
article { display: block; width: 700px; padding: 50px; margin: 0 auto; }
a { color: #fff; font-weight: bold;}
a:hover { text-decoration: none; }
svg { width: 75px; margin-top: 1em; }
</style>

<article>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 202.24 202.24"><defs><style>.cls-1{fill:#fff;}</style></defs><title>Asset 3</title><g id="Layer_2" data-name="Layer 2"><g id="Capa_1" data-name="Capa 1"><path class="cls-1" d="M101.12,0A101.12,101.12,0,1,0,202.24,101.12,101.12,101.12,0,0,0,101.12,0ZM159,148.76H43.28a11.57,11.57,0,0,1-10-17.34L91.09,31.16a11.57,11.57,0,0,1,20.06,0L169,131.43a11.57,11.57,0,0,1-10,17.34Z"/><path class="cls-1" d="M101.12,36.93h0L43.27,137.21H159L101.13,36.94Zm0,88.7a7.71,7.71,0,1,1,7.71-7.71A7.71,7.71,0,0,1,101.12,125.63Zm7.71-50.13a7.56,7.56,0,0,1-.11,1.3l-3.8,22.49a3.86,3.86,0,0,1-7.61,0l-3.8-22.49a8,8,0,0,1-.11-1.3,7.71,7.71,0,1,1,15.43,0Z"/></g></g></svg>
<h1>We&rsquo;ll be back soon!</h1>
<div>
<p>We&rsquo;re performing some maintenance at the moment. Sorry for the inconvenience.</p>
<p>&mdash; Your Pyris Administrators</p>
</div>
</article>
</html>
41 changes: 41 additions & 0 deletions docker/nginx/pyris-nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Load balancing
upstream pyris {
server pyris-app:8000;
}

# Remove nginx version from HTTP response
server_tokens off;

# Rate limit for the login REST call, at most one requests per two seconds
limit_req_zone $binary_remote_addr zone=loginlimit:10m rate=30r/m;

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;

return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name _;

ssl_certificate /certs/fullchain.pem;
ssl_certificate_key /certs/priv_key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# TODO: dynamic dh param generation not needed here? Otherwise have to generate them somehow if not available at container entrypoint
ssl_dhparam /etc/nginx/dhparam.pem;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
# ssl_early_data on;

include includes/pyris-server.conf;
}
32 changes: 32 additions & 0 deletions docker/nginx/pyris-server.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
resolver 127.0.0.11;
resolver_timeout 5s;
client_max_body_size 10m;
client_body_buffer_size 1m;

location / {
proxy_pass http://pyris;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
# proxy_set_header Early-Data $ssl_early_data;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_send_timeout 900s;
proxy_read_timeout 900s;
proxy_max_temp_file_size 0;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
fastcgi_send_timeout 900s;
fastcgi_read_timeout 900s;
client_max_body_size 128M;
}

error_page 502 /502.html;
location /502.html {
root /usr/share/nginx/html;
internal;
}
4 changes: 4 additions & 0 deletions docker/nginx/timeouts.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
proxy_send_timeout 900s;
proxy_read_timeout 900s;
fastcgi_send_timeout 900s;
fastcgi_read_timeout 900s;
21 changes: 21 additions & 0 deletions docker/pyris-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ----------------------------------------------------------------------------------------------------------------------
# Setup for a Pyris development server.
# ----------------------------------------------------------------------------------------------------------------------

services:
pyris-app:
extends:
file: ./pyris.yml
service: pyris-app
pull_policy: never
restart: "no"
volumes:
- ../application.local.yml:/config/application.yml:ro
- ../llm_config.local.yml:/config/llm_config.yml:ro
networks:
- pyris

networks:
pyris:
driver: "bridge"
name: pyris
42 changes: 42 additions & 0 deletions docker/pyris-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# ----------------------------------------------------------------------------------------------------------------------
# Setup for a Pyris production server.
# ----------------------------------------------------------------------------------------------------------------------
# It is designed to take in a lot of environment variables to take in all the configuration of the deployment.
# ----------------------------------------------------------------------------------------------------------------------

services:
pyris-app:
extends:
file: ./pyris.yml
service: pyris-app
image: ghcr.io/ls1intum/pyris:${PYRIS_DOCKER_TAG:-latest}
depends_on:
nginx:
condition: service_started
pull_policy: always
restart: unless-stopped
volumes:
- ${PYRIS_APPLICATION_YML_FILE}:/config/application.yml:ro
- ${PYRIS_LLM_CONFIG_YML_FILE}:/config/llm_config.yml:ro
networks:
- pyris

nginx:
extends:
file: ./nginx.yml
service: nginx
restart: always
volumes:
- type: bind
source: ${NGINX_PROXY_SSL_CERTIFICATE_PATH:-./nginx/certs/pyris-nginx+4.pem}
target: "/certs/fullchain.pem"
- type: bind
source: ${NGINX_PROXY_SSL_CERTIFICATE_KEY_PATH:-./nginx/certs/pyris-nginx+4-key.pem}
target: "/certs/priv_key.pem"
networks:
- pyris

networks:
pyris:
driver: "bridge"
name: pyris
Loading
Loading