diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d174557 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,155 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Uploads folder +uploads/ + +# Tests folder +tests/ + +# GitHub Actions +.github/ + +# Visual Studio Code +.vscode/ + +# Renovate +# https://github.com/marketplace/renovate +renovate.json + +# Other files not needed in the Docker image +.git/ +README.md +pyproject.toml +poetry.lock +LICENSE +docker-compose.yml +Dockerfile +.gitignore diff --git a/Dockerfile b/Dockerfile index 766642c..2a85c02 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,52 +1,71 @@ -FROM python:3.10-slim +# We have two stages, one for making the virtual environment and +# installing the dependencies and another for running the uvicorn server. +# We use an virtual environment so we seperate the dependencies from the +# system-level dependencies. +FROM python:3.10-slim AS build-image -# We don't want apt-get to interact with us and we want the default answers to be used for all questions. -ARG DEBIAN_FRONTEND=noninteractive +# Create virtual environment in /opt/venv, we will use this in the other +# stage. +RUN python -m venv /opt/venv + +# Make sure we use the virtualenv. +ENV PATH="/opt/venv/bin:$PATH" + +# Copy requirements.txt to the container, it was generated with +# 'poetry export -f requirements.txt --without-hashes' +# Note that if we pipe the output of the commmand to requirements.txt in +# Windows, it will be UTF-16 LE encoded. +COPY requirements.txt . + +# Install the requirements. +RUN pip install -r requirements.txt + +# This is the stage where we will run the uvicorn server. +FROM python:3.10-slim AS run-image + +# Copy the virtual environment to the run-image. +COPY --from=build-image /opt/venv /opt/venv + +# Install ffmpeg, it is needed for finding the video resolution and +# making the video thumbnail. +RUN apt-get update +RUN apt-get install -y --no-install-recommends ffmpeg + +# Create user so we don't run as root and make the needed directories +# Logs are stored in /var/log/discord-embed and uploaded files, +# thumbnails, and HTML are stored in /Uploads. +RUN useradd --create-home botuser && \ +install --verbose --directory --mode=0755 --owner=botuser --group=botuser /var/log/discord-embed/ /Uploads + +# Persist the uploaded files and files we have created. +VOLUME ["/Uploads"] + +# Change to the user we created so we don't run as root. +USER botuser + +# Change directory to where we will run the bot. +WORKDIR /home/botuser/discord-embed + +# Add main.py and settings.py to the container. +ADD --chown=botuser:botuser . /home/botuser/discord-embed/ + +# Uvicorn runs on port 5000, we can't use any ports below 1024 due to +# not being root. +EXPOSE 5000 + +# Make sure we use the virtualenv. +ENV PATH="/opt/venv/bin:$PATH" # Don't generate byte code (.pyc-files). # These are only needed if we run the python-files several times. # Docker doesn't keep the data between runs so this adds nothing. ENV PYTHONDONTWRITEBYTECODE 1 -# Force the stdout and stderr streams to be unbuffered. -# Will allow log messages to be immediately dumped instead of being buffered. -# This is useful when the bot crashes before writing messages stuck in the buffer. +# Force the stdout and stderr streams to be unbuffered. This means that +# the output will be printed immediately instead of being buffered. If +# the Python application crashes, the output could be lost. ENV PYTHONUNBUFFERED 1 -# Update packages and install needed packages to build our requirements. -RUN apt-get update && apt-get install -y --no-install-recommends curl ffmpeg python-is-python3 - -# Create user so we don't run as root. -RUN useradd --create-home botuser - -# Make log directory -RUN \ -mkdir -p /var/log/discord-embed/ && chown -R botuser:botuser /var/log/discord-embed/ && \ -mkdir /Uploads && chown -R botuser:botuser /Uploads - -VOLUME ["/Uploads"] - -# Change to the user we created. -USER botuser - -# Install poetry. -RUN curl -sSL https://install.python-poetry.org | python - - -# Add poetry to our path. -ENV PATH="/home/botuser/.local/bin/:${PATH}" - -# Copy files from our repository to the container. -ADD --chown=botuser:botuser pyproject.toml poetry.lock README.md LICENSE /home/botuser/discord-embed/ - -# Change directory to where we will run the bot. -WORKDIR /home/botuser/discord-embed - -# Install the requirements. -RUN poetry install --no-interaction --no-ansi --no-dev - -# Add main.py and settings.py to the container. -ADD --chown=botuser:botuser discord_embed /home/botuser/discord-embed/discord_embed/ - -EXPOSE 5000 - -CMD ["poetry", "run", "uvicorn", "discord_embed.main:app", "--host", "0.0.0.0", "--port", "5000"] +# Run the server on all interfaces and on port 5000. +# You should run a reverse proxy like nginx infront of this. +CMD ["uvicorn", "discord_embed.main:app", "--host", "0.0.0.0", "--port", "5000"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1f61fd4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,29 @@ +anyio==3.5.0; python_version >= "3.7" and python_full_version >= "3.6.2" +asgiref==3.5.0; python_version >= "3.7" +certifi==2021.10.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" +charset-normalizer==2.0.12; python_full_version >= "3.6.0" and python_version >= "3" +click==8.1.2; python_version >= "3.7" +colorama==0.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" and platform_system == "Windows" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.5.0" and platform_system == "Windows" +discord-webhook==0.15.0 +fastapi==0.75.2; python_full_version >= "3.6.1" +ffmpeg-python==0.2.0 +future==0.18.2; python_version >= "2.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" +h11==0.13.0; python_version >= "3.7" +httptools==0.4.0; python_version >= "3.7" and python_full_version >= "3.5.0" +idna==3.3; python_full_version >= "3.6.2" and python_version >= "3.7" +jinja2==3.1.1; python_version >= "3.7" +markupsafe==2.1.1; python_version >= "3.7" +pydantic==1.9.0; python_full_version >= "3.6.1" +python-dotenv==0.20.0; python_version >= "3.5" +python-multipart==0.0.5 +pyyaml==6.0; python_version >= "3.7" +requests==2.27.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" +six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" +sniffio==1.2.0; python_version >= "3.7" and python_full_version >= "3.6.2" +starlette==0.17.1; python_version >= "3.6" and python_full_version >= "3.6.1" +typing-extensions==4.2.0; python_version >= "3.7" and python_full_version >= "3.6.1" +urllib3==1.26.9; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" +uvicorn==0.17.6; python_version >= "3.7" +uvloop==0.16.0; sys_platform != "win32" and sys_platform != "cygwin" and platform_python_implementation != "PyPy" and python_version >= "3.7" +watchgod==0.8.2; python_version >= "3.7" +websockets==10.3; python_version >= "3.7" \ No newline at end of file