diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index bf847b82..fea90597 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -58,7 +58,7 @@ jobs: port: ${{ secrets.DEMO_SERVER_PORT }} script_stop: true script: | - cd ~/docker/mealie-next + cd ~/docker/mealie docker-compose pull docker-compose down -v docker-compose up -d diff --git a/.github/workflows/partial-backend.yml b/.github/workflows/partial-backend.yml index c2866e33..ae6b4543 100644 --- a/.github/workflows/partial-backend.yml +++ b/.github/workflows/partial-backend.yml @@ -70,7 +70,7 @@ jobs: run: | poetry run black . --check - - name: Lint (Flake8) + - name: Lint (Ruff) run: | make backend-lint diff --git a/.github/workflows/partial-builder.yml b/.github/workflows/partial-builder.yml index 0e363a9e..e97d6be0 100644 --- a/.github/workflows/partial-builder.yml +++ b/.github/workflows/partial-builder.yml @@ -40,11 +40,10 @@ jobs: password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build Frontend Image - working-directory: "frontend" run: | docker build --push --no-cache \ --tag hkotel/mealie:frontend-${{ inputs.tag }} \ - --platform linux/amd64,linux/arm64 . + --platform linux/amd64,linux/arm64 --file=./docker/frontend.Dockerfile . build-backend: runs-on: ubuntu-latest @@ -77,4 +76,37 @@ jobs: docker build --push --no-cache \ --tag hkotel/mealie:api-${{ inputs.tag }} \ --build-arg COMMIT=$(git rev-parse HEAD) \ - --platform linux/amd64,linux/arm64 . + --platform linux/amd64,linux/arm64 --file=./docker/api.Dockerfile . + + build-omni: + runs-on: ubuntu-latest + name: Build Backend + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Set up QEMU + id: qemu + uses: docker/setup-qemu-action@v1 + with: + image: tonistiigi/binfmt:latest + platforms: all + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + with: + install: true + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build Omni-Image + run: | + docker build --push --no-cache \ + --tag hkotel/mealie:omni-${{ inputs.tag }} \ + --build-arg COMMIT=$(git rev-parse HEAD) \ + --platform linux/amd64,linux/arm64 --file=./docker/omni.Dockerfile . diff --git a/.github/workflows/partial-trivy-backend-container-scanning.yml b/.github/workflows/partial-trivy-backend-container-scanning.yml index b36aaaf7..e0fb3e84 100644 --- a/.github/workflows/partial-trivy-backend-container-scanning.yml +++ b/.github/workflows/partial-trivy-backend-container-scanning.yml @@ -15,7 +15,7 @@ jobs: - name: Build Dockerfile run: | - docker build -t mealie . + docker build -t mealie --file=./docker/api.Dockerfile . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master diff --git a/.github/workflows/partial-trivy-frontend-container-scanning.yml b/.github/workflows/partial-trivy-frontend-container-scanning.yml index 159f0a16..43d8db9a 100644 --- a/.github/workflows/partial-trivy-frontend-container-scanning.yml +++ b/.github/workflows/partial-trivy-frontend-container-scanning.yml @@ -15,7 +15,7 @@ jobs: - name: Build Dockerfile run: | - docker build -t mealie ./frontend/ + docker build -t mealie --file=./docker/frontend.Dockerfile . - name: Run Trivy vulnerability scanner uses: aquasecurity/trivy-action@master diff --git a/.vscode/settings.json b/.vscode/settings.json index a91a9b7b..2f9a9f96 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -46,7 +46,6 @@ "package.json": "package-lock.json, yarn.lock, .eslintrc.js, tsconfig.json, .prettierrc, .editorconfig", "pyproject.toml": "poetry.lock, alembic.ini, .pylintrc", "netlify.toml": "runtime.txt", - "docker-compose.yml": "Dockerfile, .dockerignore, docker-compose.dev.yml, docker-compose.yml", "README.md": "LICENSE, SECURITY.md" } } diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index 8bcac2f8..00000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,48 +0,0 @@ -# WARNING: currently not functional, see #756, #1072 -# Use root/example as user/password credentials -version: "3.4" -services: - # Vue Frontend - mealie-frontend: - container_name: mealie-frontend - image: mealie-frontend:dev - build: - context: ./frontend - dockerfile: Dockerfile.frontend - restart: always - ports: - - 9920:8080 - environment: - - GLOBAL_MIDDLEWARE=null - - BASE_URL="" - - ALLOW_SIGNUP=true - volumes: - - ./frontend/:/app - - /app/node_modules - - # Fast API - mealie-api: - container_name: mealie-api - image: mealie-api:dev - build: - context: ./ - target: development - dockerfile: Dockerfile - restart: always - ports: - - 9921:9000 - environment: - TZ: America/Anchorage # Specify Correct Timezone for Date/Time to line up correctly. - volumes: - - ./dev/data:/app/dev/data - - ./mealie:/app/mealie - - # Mkdocs - mealie-docs: - container_name: mealie-docs - image: squidfunk/mkdocs-material - restart: always - ports: - - 9922:8000 - volumes: - - ./docs:/docs diff --git a/Dockerfile b/docker/api.Dockerfile similarity index 80% rename from Dockerfile rename to docker/api.Dockerfile index eaa30e69..956b5317 100644 --- a/Dockerfile +++ b/docker/api.Dockerfile @@ -51,33 +51,6 @@ COPY ./poetry.lock ./pyproject.toml ./ # install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally RUN poetry install -E pgsql --only main -############################################### -# Development Image -############################################### -FROM python-base as development -ENV PRODUCTION=false -ENV TESTING=false - -# copying poetry and venv into image -COPY --from=builder-base $POETRY_HOME $POETRY_HOME -COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH - -# copy backend -COPY ./mealie $MEALIE_HOME/mealie -COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/ - -# Alembic -COPY ./alembic $MEALIE_HOME/alembic -COPY ./alembic.ini $MEALIE_HOME/ - -# venv already has runtime deps installed we get a quicker install -WORKDIR $MEALIE_HOME -RUN . $VENV_PATH/bin/activate && poetry install --with main,dev -WORKDIR / - -RUN chmod +x $MEALIE_HOME/mealie/run.sh -ENTRYPOINT $MEALIE_HOME/mealie/run.sh "reload" - ############################################### # CRFPP Image ############################################### @@ -135,5 +108,6 @@ EXPOSE ${APP_PORT} HEALTHCHECK CMD python $MEALIE_HOME/mealie/scripts/healthcheck.py || exit 1 +COPY ./docker/api.entry.sh $MEALIE_HOME/mealie/run.sh RUN chmod +x $MEALIE_HOME/mealie/run.sh ENTRYPOINT $MEALIE_HOME/mealie/run.sh diff --git a/mealie/run.sh b/docker/api.entry.sh old mode 100755 new mode 100644 similarity index 60% rename from mealie/run.sh rename to docker/api.entry.sh index d4d2944e..f4a5a70f --- a/mealie/run.sh +++ b/docker/api.entry.sh @@ -22,7 +22,7 @@ change_user() { echo "Switching to dedicated user" exec gosu $PUID "$BASH_SOURCE" "$@" - elif [ "$(id -u)" = $PUID ]; then + elif [ "$(id -u)" = $PUID ]; then echo " User uid: $PUID User gid: $PGID @@ -41,28 +41,19 @@ init() { poetry run python /app/mealie/db/init_db.py } -if [ "$ARG1" == "reload" ]; then - echo "Hot Reload!" +echo "Production" - init +change_user - # Start API - python /app/mealie/app.py +init + +GUNICORN_PORT=${API_PORT:-9000} + +# Start API + +if [ "$WEB_GUNICORN" == 'true' ]; then + echo "Starting Gunicorn" + gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload else - echo "Production" - - change_user - - init - - GUNICORN_PORT=${API_PORT:-9000} - - # Start API - - if [ "$WEB_GUNICORN" == 'true' ]; then - echo "Starting Gunicorn" - gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload - else - uvicorn mealie.app:app --host 0.0.0.0 --port $GUNICORN_PORT - fi + uvicorn mealie.app:app --host 0.0.0.0 --port $GUNICORN_PORT fi diff --git a/docker-compose.yml b/docker/docker-compose.yml similarity index 94% rename from docker-compose.yml rename to docker/docker-compose.yml index a5da2ed1..b9a159bf 100644 --- a/docker-compose.yml +++ b/docker/docker-compose.yml @@ -8,8 +8,8 @@ services: limits: memory: 500M build: - context: ./frontend - dockerfile: Dockerfile + context: ../ + dockerfile: ./docker/frontend.Dockerfile restart: always volumes: - mealie-data:/app/data/ @@ -43,9 +43,9 @@ services: limits: memory: 1000M build: - context: ./ + context: ../ target: production - dockerfile: Dockerfile + dockerfile: ./docker/api.Dockerfile restart: always volumes: - mealie-data:/app/data/ diff --git a/frontend/Caddyfile b/docker/frontend.Caddyfile similarity index 100% rename from frontend/Caddyfile rename to docker/frontend.Caddyfile diff --git a/frontend/Dockerfile b/docker/frontend.Dockerfile similarity index 80% rename from frontend/Dockerfile rename to docker/frontend.Dockerfile index c94279e2..b397d5af 100644 --- a/frontend/Dockerfile +++ b/docker/frontend.Dockerfile @@ -2,16 +2,16 @@ FROM node:16 as builder WORKDIR /app -COPY . . +COPY ./frontend . RUN yarn install \ --prefer-offline \ --frozen-lockfile \ --non-interactive \ - --production=false \ + --production=false \ # https://github.com/docker/build-push-action/issues/471 --network-timeout 1000000 - + RUN yarn build RUN rm -rf node_modules && \ @@ -29,7 +29,8 @@ WORKDIR /app # copying caddy into image COPY --from=builder /app . -COPY ./Caddyfile /app/ +COPY ./docker/frontend.Caddyfile /app/Caddyfile +COPY ./docker/frontend.entry.sh /app/run.sh ENV HOST 0.0.0.0 EXPOSE 3000 diff --git a/frontend/run.sh b/docker/frontend.entry.sh similarity index 87% rename from frontend/run.sh rename to docker/frontend.entry.sh index 73469fdc..d15c3f62 100644 --- a/frontend/run.sh +++ b/docker/frontend.entry.sh @@ -4,4 +4,4 @@ caddy start --config /app/Caddyfile # Start Node Application -yarn start -p 3001 \ No newline at end of file +yarn start -p 3001 diff --git a/docker/omni.Dockerfile b/docker/omni.Dockerfile new file mode 100644 index 00000000..1c80b657 --- /dev/null +++ b/docker/omni.Dockerfile @@ -0,0 +1,156 @@ +FROM node:16 as builder + +WORKDIR /app + +COPY ./frontend . + +RUN yarn install \ + --prefer-offline \ + --frozen-lockfile \ + --non-interactive \ + --production=false \ + # https://github.com/docker/build-push-action/issues/471 + --network-timeout 1000000 + +RUN yarn build + +RUN rm -rf node_modules && \ + NODE_ENV=production yarn install \ + --prefer-offline \ + --pure-lockfile \ + --non-interactive \ + --production=true + +############################################### +# Base Image - Python +############################################### +FROM python:3.10-slim as python-base + +ENV MEALIE_HOME="/app" + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=off \ + PIP_DISABLE_PIP_VERSION_CHECK=on \ + PIP_DEFAULT_TIMEOUT=100 \ + POETRY_HOME="/opt/poetry" \ + POETRY_VIRTUALENVS_IN_PROJECT=true \ + POETRY_NO_INTERACTION=1 \ + PYSETUP_PATH="/opt/pysetup" \ + VENV_PATH="/opt/pysetup/.venv" + +# prepend poetry and venv to path +ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH" + +# create user account +RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \ + && usermod -G users abc \ + && mkdir $MEALIE_HOME + +############################################### +# Builder Image +############################################### +FROM python-base as builder-base +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + curl \ + build-essential \ + libpq-dev \ + libwebp-dev \ + tesseract-ocr-all \ + # LDAP Dependencies + libsasl2-dev libldap2-dev libssl-dev \ + gnupg gnupg2 gnupg1 \ + && pip install -U --no-cache-dir pip + +# install poetry - respects $POETRY_VERSION & $POETRY_HOME +ENV POETRY_VERSION=1.3.1 +RUN curl -sSL https://install.python-poetry.org | python3 - + +# copy project requirement files here to ensure they will be cached. +WORKDIR $PYSETUP_PATH +COPY ./poetry.lock ./pyproject.toml ./ + +# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally +RUN poetry install -E pgsql --only main + +############################################### +# CRFPP Image +############################################### +FROM hkotel/crfpp as crfpp + +RUN echo "crfpp-container" + +############################################### +# Production Image +############################################### +FROM python-base as production +ENV PRODUCTION=true +ENV TESTING=false + +ARG COMMIT +ENV GIT_COMMIT_HASH=$COMMIT + +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + gosu \ + tesseract-ocr-all \ + curl \ + gnupg \ + && apt-get autoremove \ + && rm -rf /var/lib/apt/lists/* + +RUN apt-get install -y curl \ + && curl -sL https://deb.nodesource.com/setup_16.x | bash - \ + && apt-get install -y nodejs \ + && curl -L https://www.npmjs.com/install.sh | sh + +# Add Yarn +RUN npm install -g yarn + +# copying poetry and venv into image +COPY --from=builder-base $POETRY_HOME $POETRY_HOME +COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH + +ENV LD_LIBRARY_PATH=/usr/local/lib +COPY --from=crfpp /usr/local/lib/ /usr/local/lib +COPY --from=crfpp /usr/local/bin/crf_learn /usr/local/bin/crf_learn +COPY --from=crfpp /usr/local/bin/crf_test /usr/local/bin/crf_test + +# copy backend +COPY ./mealie $MEALIE_HOME/mealie +COPY ./poetry.lock ./pyproject.toml $MEALIE_HOME/ +COPY ./gunicorn_conf.py $MEALIE_HOME + +# Alembic +COPY ./alembic $MEALIE_HOME/alembic +COPY ./alembic.ini $MEALIE_HOME/ + +# venv already has runtime deps installed we get a quicker install +WORKDIR $MEALIE_HOME +RUN . $VENV_PATH/bin/activate && poetry install -E pgsql --only main +WORKDIR / + +# Grab CRF++ Model Release +RUN python $MEALIE_HOME/mealie/scripts/install_model.py + +VOLUME [ "$MEALIE_HOME/data/" ] +ENV APP_PORT=9000 + +EXPOSE ${APP_PORT} + +HEALTHCHECK CMD python $MEALIE_HOME/mealie/scripts/healthcheck.py || exit 1 + +# ---------------------------------- +# Copy Frontend + +# copying caddy into image +COPY --from=builder /app $MEALIE_HOME/frontend/ + +ENV HOST 0.0.0.0 + +EXPOSE ${APP_PORT} +COPY ./docker/omni.entry.sh $MEALIE_HOME/run.sh + +RUN chmod +x $MEALIE_HOME/run.sh +ENTRYPOINT $MEALIE_HOME/run.sh diff --git a/docker/omni.docker-compose.yml b/docker/omni.docker-compose.yml new file mode 100644 index 00000000..90ffba19 --- /dev/null +++ b/docker/omni.docker-compose.yml @@ -0,0 +1,46 @@ +version: "3.4" +services: + omni-mealie: + container_name: mealie + image: mealie-omni:dev + build: + context: ../ + target: production + dockerfile: ./docker/omni.Dockerfile + restart: always + volumes: + - mealie-data:/app/data/ + ports: + - 9091:3000 + environment: + ALLOW_SIGNUP: "false" + + DB_ENGINE: sqlite # Optional: 'sqlite', 'postgres' + # ===================================== + # Postgres Config + POSTGRES_USER: mealie + POSTGRES_PASSWORD: mealie + POSTGRES_SERVER: postgres + POSTGRES_PORT: 5432 + POSTGRES_DB: mealie + + # ===================================== + # Web Concurrency + WEB_GUNICORN: true + WORKERS_PER_CORE: 0.5 + MAX_WORKERS: 1 + WEB_CONCURRENCY: 1 + + # ===================================== + # Email Configuration + # SMTP_HOST= + # SMTP_PORT=587 + # SMTP_FROM_NAME=Mealie + # SMTP_AUTH_STRATEGY=TLS # Options: 'TLS', 'SSL', 'NONE' + # SMTP_FROM_EMAIL= + # SMTP_USER= + # SMTP_PASSWORD= + +volumes: + mealie-data: + driver: local diff --git a/docker/omni.entrypoint.sh b/docker/omni.entrypoint.sh new file mode 100644 index 00000000..730cdbc9 --- /dev/null +++ b/docker/omni.entrypoint.sh @@ -0,0 +1,59 @@ +# Start Backend API +#!/bin/bash + +# Strict Mode +# set -e +# IFS=$'\n\t' + +# Get PUID/PGID +PUID=${PUID:-911} +PGID=${PGID:-911} + +add_user() { + groupmod -o -g "$PGID" abc + usermod -o -u "$PUID" abc +} + +change_user() { + # If container is started as root then create a new user and switch to it + if [ "$(id -u)" = "0" ]; then + add_user + chown -R $PUID:$PGID /app + + echo "Switching to dedicated user" + exec gosu $PUID "$BASH_SOURCE" "$@" + elif [ "$(id -u)" = $PUID ]; then + echo " + User uid: $PUID + User gid: $PGID + " + fi +} + +init() { + # $MEALIE_HOME directory + cd /app + + # Activate our virtual environment here + . /opt/pysetup/.venv/bin/activate + + # Initialize Database Prerun + poetry run python /app/mealie/db/init_db.py +} + +# change_user +init +GUNICORN_PORT=${API_PORT:-9000} + +# Start API + +if [ "$WEB_GUNICORN" == 'true' ]; then + echo "Starting Gunicorn" + gunicorn mealie.app:app -b 0.0.0.0:$GUNICORN_PORT -k uvicorn.workers.UvicornWorker -c /app/gunicorn_conf.py --preload & +else + uvicorn mealie.app:app --host 0.0.0.0 --port $GUNICORN_PORT & +fi + +# ------------------------------ +# Start Frontend Nuxt Server +cd /app/frontend && yarn start -p 3000 diff --git a/docs/docs/documentation/getting-started/api-usage.md b/docs/docs/documentation/getting-started/api-usage.md index dc1b24f8..734fe256 100644 --- a/docs/docs/documentation/getting-started/api-usage.md +++ b/docs/docs/documentation/getting-started/api-usage.md @@ -2,8 +2,7 @@ ## Getting a Token -Mealie supports long-live api tokens in the user frontend. See [user settings page](../users-groups/user-settings.md) - +Mealie supports long-live api tokens in the user frontend. These can be created on the `/user/profile/api-tokens` page. ## Key Components diff --git a/docs/docs/documentation/getting-started/installation/single-container.md b/docs/docs/documentation/getting-started/installation/single-container.md new file mode 100644 index 00000000..2b0ede20 --- /dev/null +++ b/docs/docs/documentation/getting-started/installation/single-container.md @@ -0,0 +1,35 @@ +# Using the Omni Image + +Since [#1948](https://github.com/hay-kot/mealie/pull/1948) we've started publishing an experimental image that merges both the frontend and backend services into a single container image. This image is currently in an experimental state, and should be used with caution. Continued support for this image will be based on user feedback and demand, if you're using this image please let us know how it's working for you. + + +- [Feedback Discussion](https://github.com/hay-kot/mealie/discussions/1949) + +**For Environmental Variable Configuration See:** + +Note that frontend and backend configurations are both applied to the same container. + +- [Frontend Configuration](./frontend-config.md) +- [Backend Configuration](./backend-config.md) + +```yaml +--- +version: "3.7" +services: + mealie-omni: + image: hkotel/mealie:omni-nightly + container_name: mealie + volumes: + - mealie-data:/app/data/ + environment: + - ALLOW_SIGNUP=true + - PUID=1000 + - PGID=1000 + - TZ=America/Anchorage + - BASE_URL=https://mealie.yourdomain.com + restart: always + +volumes: + mealie-data: + driver: local +``` diff --git a/docs/docs/documentation/getting-started/introduction.md b/docs/docs/documentation/getting-started/introduction.md index ae19e473..55cd8be9 100644 --- a/docs/docs/documentation/getting-started/introduction.md +++ b/docs/docs/documentation/getting-started/introduction.md @@ -53,11 +53,6 @@ As to why we need a database? * [FastAPI](https://fastapi.tiangolo.com/) * [Docker](https://www.docker.com/) - -## Road Map - -[See Roadmap](../../roadmap.md) - ## Contributing diff --git a/docs/docs/overrides/api.html b/docs/docs/overrides/api.html index f971e469..0ad2bc79 100644 --- a/docs/docs/overrides/api.html +++ b/docs/docs/overrides/api.html @@ -14,7 +14,7 @@
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index e2d68354..eb89182c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -70,6 +70,7 @@ nav: - Installation Checklist: "documentation/getting-started/installation/installation-checklist.md" - SQLite (Recommended): "documentation/getting-started/installation/sqlite.md" - PostgreSQL: "documentation/getting-started/installation/postgres.md" + - Single Container (Experimental): "documentation/getting-started/installation/single-container.md" - Frontend Configuration: "documentation/getting-started/installation/frontend-config.md" - Backend Configuration: "documentation/getting-started/installation/backend-config.md" - Usage: diff --git a/makefile b/makefile index 7146390f..a81c9f02 100644 --- a/makefile +++ b/makefile @@ -122,12 +122,11 @@ frontend-lint: ## ๐Ÿงบ Run yarn lint # ----------------------------------------------------------------------------- # Docker makefile -docker-dev: ## ๐Ÿณ Build and Start Docker Development Stack - docker-compose -f docker-compose.dev.yml -p dev-mealie down && \ - docker-compose -f docker-compose.dev.yml -p dev-mealie up --build +docker/omni: ## ๐Ÿณ Build and start the omni style container + cd docker && docker-compose -f omni.docker-compose.yml -p mealie-omni up --build -docker-prod: ## ๐Ÿณ Build and Start Docker Production Stack - docker-compose -f docker-compose.yml -p mealie up --build +docker/prod: ## ๐Ÿณ Build and Start Docker Production Stack + cd docker && docker-compose -f docker-compose.yml -p mealie up --build generate: poetry run python dev/code-generation/main.py