mirror of
https://github.com/frappe/frappe_docker.git
synced 2024-11-08 14:21:05 +00:00
refactor: build only one frappe/erpnext image (#1032)
* ci: skip frappe builds * refactor: build only one frappe/erpnext image * fix: lint nginx entrypoint script * docs: update and organize docs * docs: fix lint errors * fix(custom): pass base64 encoded apps json * ci: update dependabot * docs: update contributing * docs: remove info about multi image setup * fix: initiate empty common_site_config.json default config has host keys set to localhost causes connection errors * docs: add details for pwd volumes * fix: symlink assets instead of copy * fix: nginx private files * ci: skip docker compose v2 install for ubuntu-latest * fix: organize layers * feat: allow remove git remote for custom image * docs: allow remove git remote for custom image * fix: remove duplicate --apps_path
This commit is contained in:
parent
f8e43a3114
commit
e6088af885
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@ -11,22 +11,12 @@ updates:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: images/nginx
|
||||
directory: images/production
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: images/worker
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: docker
|
||||
directory: images/socketio
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
- package-ecosystem: npm
|
||||
directory: images/socketio
|
||||
directory: images/custom
|
||||
schedule:
|
||||
interval: daily
|
||||
|
||||
|
9
.github/scripts/update_pwd.py
vendored
9
.github/scripts/update_pwd.py
vendored
@ -13,12 +13,9 @@ def get_versions():
|
||||
def update_pwd(frappe_version: str, erpnext_version: str):
|
||||
with open("pwd.yml", "r+") as f:
|
||||
content = f.read()
|
||||
for image, version in (
|
||||
("frappe/frappe-socketio", frappe_version),
|
||||
("frappe/erpnext-worker", erpnext_version),
|
||||
("frappe/erpnext-nginx", erpnext_version),
|
||||
):
|
||||
content = re.sub(rf"{image}:.*", f"{image}:{version}", content)
|
||||
content = re.sub(
|
||||
rf"frappe/erpnext:.*", f"frappe/erpnext:{erpnext_version}", content
|
||||
)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
f.write(content)
|
||||
|
3
.github/workflows/docker-build-push.yml
vendored
3
.github/workflows/docker-build-push.yml
vendored
@ -54,9 +54,6 @@ jobs:
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install Docker Compose v2
|
||||
uses: ndeloof/install-compose-action@4a33bc31f327b8231c4f343f6fba704fedc0fa23
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m venv venv
|
||||
|
@ -61,16 +61,10 @@ Run pytest:
|
||||
pytest
|
||||
```
|
||||
|
||||
> We also have `requirements-dev.txt` file that contains development requirements for backend image (you can find it in `images/worker/` directory).
|
||||
|
||||
# Documentation
|
||||
|
||||
Place relevant markdown files in the `docs` directory and index them in README.md located at the root of repo.
|
||||
|
||||
# Wiki
|
||||
|
||||
Add alternatives that can be used optionally along with frappe_docker. Add articles to list on home page as well.
|
||||
|
||||
# Frappe and ERPNext updates
|
||||
|
||||
Each Frappe/ERPNext release triggers new stable images builds as well as bump to helm chart.
|
||||
|
43
README.md
43
README.md
@ -21,40 +21,39 @@ cd frappe_docker
|
||||
|
||||
Wait for 5 minutes for ERPNext site to be created or check `create-site` container logs before opening browser on port 8080. (username: `Administrator`, password: `admin`)
|
||||
|
||||
# Development
|
||||
# Documentation
|
||||
|
||||
We have baseline for developing in VSCode devcontainer with [frappe/bench](https://github.com/frappe/bench). [Start development](development).
|
||||
### [Production](#production)
|
||||
|
||||
# Production
|
||||
- [List of containers](docs/list-of-containers.md)
|
||||
- [Single Compose Setup](docs/single-compose-setup.md)
|
||||
- [Environment Variables](docs/environment-variables.md)
|
||||
- [Single Server Example](docs/single-server-example.md)
|
||||
- [Setup Options](docs/setup-options.md)
|
||||
- [Site Operations](docs/site-operations.md)
|
||||
- [Backup and Push Cron Job](docs/backup-and-push-cronjob.md)
|
||||
- [Port Based Multi Tenancy](docs/port-based-multi-tenancy.md)
|
||||
- [Migrate from multi-image setup](docs/migrate-from-multi-image-setup.md)
|
||||
|
||||
We provide simple and intuitive production setup with prebuilt Frappe and ERPNext images and compose files. To learn more about those, [read the docs](docs/images-and-compose-files.md).
|
||||
### [Custom Images](#custom-images)
|
||||
|
||||
Also, there's docs to help with deployment:
|
||||
- [Custom Apps](docs/custom-apps.md)
|
||||
- [Build Version 10 Images](docs/build-version-10-images.md)
|
||||
|
||||
- Examples:
|
||||
- [Single Server](docs/single-server-example.md)
|
||||
- [Setup options](docs/setup-options.md)
|
||||
- [Kubernetes (frappe/helm)](https://helm.erpnext.com)
|
||||
- [Site operations](docs/site-operations.md).
|
||||
- Other
|
||||
- [backup and push cron jobs](docs/backup-and-push-cronjob.md)
|
||||
- [bench console and vscode debugger](docs/bench-console-and-vscode-debugger.md)
|
||||
- [build version 10](docs/build-version-10-images.md)
|
||||
- [connect to localhost services from containers for local app development](docs/connect-to-localhost-services-from-containers-for-local-app-development.md)
|
||||
- [patch code from images](docs/patch-code-from-images.md)
|
||||
- [port based multi tenancy](docs/port-based-multi-tenancy.md)
|
||||
- [Troubleshoot](docs/troubleshoot.md)
|
||||
### [Development](#development)
|
||||
|
||||
# Custom app
|
||||
- [Development using containers](docs/development.md)
|
||||
- [Bench Console and VSCode Debugger](docs/bench-console-and-vscode-debugger.md)
|
||||
- [Connect to localhost services](docs/connect-to-localhost-services-from-containers-for-local-app-development.md)
|
||||
|
||||
Learn how to containerize your custom Frappe app(s) in [this guide](custom_app/README.md).
|
||||
### [Troubleshoot](docs/troubleshoot.md)
|
||||
|
||||
# Contributing
|
||||
|
||||
If you want to contribute to this repo refer to [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
This repository is only for Docker related stuff. You also might want to contribute to:
|
||||
This repository is only for container related stuff. You also might want to contribute to:
|
||||
|
||||
- [Frappe framework](https://github.com/frappe/frappe#contributing),
|
||||
- [ERPNext](https://github.com/frappe/erpnext#contributing),
|
||||
- or [Frappe Bench](https://github.com/frappe/bench).
|
||||
- [Frappe Bench](https://github.com/frappe/bench).
|
||||
|
28
compose.yaml
28
compose.yaml
@ -5,15 +5,24 @@ x-depends-on-configurator: &depends_on_configurator
|
||||
|
||||
x-backend-defaults: &backend_defaults
|
||||
<<: *depends_on_configurator
|
||||
image: frappe/frappe-worker:${FRAPPE_VERSION:?No Frappe version set}
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?No ERPNext version set}
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets:ro
|
||||
|
||||
services:
|
||||
configurator:
|
||||
<<: *backend_defaults
|
||||
command: configure.py
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- >
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -g redis_socketio "redis://$$REDIS_SOCKETIO";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: ${DB_HOST}
|
||||
DB_PORT: ${DB_PORT}
|
||||
@ -27,7 +36,9 @@ services:
|
||||
<<: *backend_defaults
|
||||
|
||||
frontend:
|
||||
image: frappe/frappe-nginx:${FRAPPE_VERSION}
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION}
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
SOCKETIO: websocket:9000
|
||||
@ -38,15 +49,17 @@ services:
|
||||
PROXY_READ_TIMOUT: ${PROXY_READ_TIMOUT:-120}
|
||||
CLIENT_MAX_BODY_SIZE: ${CLIENT_MAX_BODY_SIZE:-50m}
|
||||
volumes:
|
||||
- sites:/usr/share/nginx/html/sites
|
||||
- assets:/usr/share/nginx/html/assets
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
depends_on:
|
||||
- backend
|
||||
- websocket
|
||||
|
||||
websocket:
|
||||
<<: *depends_on_configurator
|
||||
image: frappe/frappe-socketio:${FRAPPE_VERSION}
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION}
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
|
||||
@ -69,4 +82,3 @@ services:
|
||||
# ERPNext requires local assets access (Frappe does not)
|
||||
volumes:
|
||||
sites:
|
||||
assets:
|
||||
|
@ -1,46 +0,0 @@
|
||||
This is basic configuration for building images and testing custom apps that use Frappe.
|
||||
|
||||
You can see that there's four files in this folder:
|
||||
|
||||
- `backend.Dockerfile`,
|
||||
- `frontend.Dockerfile`,
|
||||
- `docker-bake.hcl`,
|
||||
- `compose.override.yaml`.
|
||||
|
||||
Python code will be built in `backend.Dockerfile`. JS and CSS (and other fancy frontend stuff) files will be built in `frontend.Dockerfile`.
|
||||
|
||||
`docker-bake.hcl` is reference file for [Buildx Bake](https://github.com/docker/buildx/blob/master/docs/reference/buildx_bake.md). It helps to build images without having to remember all build arguments.
|
||||
|
||||
`compose.override.yaml` is [Compose](https://docs.docker.com/compose/compose-file/) override that replaces images from [main compose file](https://github.com/frappe/frappe_docker/blob/main/compose.yaml) so it would use your own images.
|
||||
|
||||
To get started, install Docker and [Buildx](https://github.com/docker/buildx#installing). Then copy all content of this folder (except this README) to your app's root directory. Also copy `compose.yaml` in the root of this repository.
|
||||
|
||||
Before the next step—to build images—replace "custom_app" with your app's name in `docker-bake.hcl`. After that, let's try to build:
|
||||
|
||||
```bash
|
||||
FRAPPE_VERSION=... ERPNEXT_VERSION=... docker buildx bake
|
||||
```
|
||||
|
||||
> 💡 We assume that majority of our users use ERPNext, that's why images in this tutorial are based on ERPNext images. If don't want ERPNext, change base image in Dockerfile and remove ERPNEXT_VERSION from bake file. To know more about steps used to build frontend image read comments in `frontend.Dockerfile`.
|
||||
|
||||
If something goes wrong feel free to leave an issue.
|
||||
|
||||
To test if site works, setup `.env` file (check [example](<(https://github.com/frappe/frappe_docker/blob/main/example.env)>)) and run:
|
||||
|
||||
```bash
|
||||
docker-compose -f compose.yaml -f overrides/compose.noproxy.yaml -f overrides/compose.mariadb.yaml -f overrides/compose.redis.yaml -f custom_app/compose.override.yaml up -d
|
||||
docker-compose exec backend \
|
||||
bench new-site 127.0.0.1 \
|
||||
--mariadb-root-password 123 \
|
||||
--admin-password admin \
|
||||
--install-app <Name of your app>
|
||||
docker-compose restart backend
|
||||
```
|
||||
|
||||
Cool! You just containerized your app!
|
||||
|
||||
## Installing multiple apps
|
||||
|
||||
Backend builds contain `install-app` script that places app where it should be. Each call to script installs given app. Usage: `install-app [APP_NAME]`.
|
||||
|
||||
If you want to install an app from git, clone it locally, COPY in Dockerfile.
|
@ -1,14 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
ARG ERPNEXT_VERSION
|
||||
FROM frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
USER root
|
||||
|
||||
ARG APP_NAME
|
||||
COPY . ../apps/${APP_NAME}
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
install-app ${APP_NAME}
|
||||
|
||||
USER frappe
|
@ -1,21 +0,0 @@
|
||||
services:
|
||||
configurator:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
backend:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
frontend:
|
||||
image: custom_app/nginx:${VERSION}
|
||||
|
||||
queue-short:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
queue-default:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
queue-long:
|
||||
image: custom_app/worker:${VERSION}
|
||||
|
||||
scheduler:
|
||||
image: custom_app/worker:${VERSION}
|
@ -1,27 +0,0 @@
|
||||
APP_NAME="custom_app"
|
||||
|
||||
variable "FRAPPE_VERSION" {}
|
||||
variable "ERPNEXT_VERSION" {}
|
||||
|
||||
group "default" {
|
||||
targets = ["backend", "frontend"]
|
||||
}
|
||||
|
||||
target "backend" {
|
||||
dockerfile = "backend.Dockerfile"
|
||||
tags = ["custom_app/worker:latest"]
|
||||
args = {
|
||||
"ERPNEXT_VERSION" = ERPNEXT_VERSION
|
||||
"APP_NAME" = APP_NAME
|
||||
}
|
||||
}
|
||||
|
||||
target "frontend" {
|
||||
dockerfile = "frontend.Dockerfile"
|
||||
tags = ["custom_app/nginx:latest"]
|
||||
args = {
|
||||
"FRAPPE_VERSION" = FRAPPE_VERSION
|
||||
"ERPNEXT_VERSION" = ERPNEXT_VERSION
|
||||
"APP_NAME" = APP_NAME
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
ARG FRAPPE_VERSION=version-14
|
||||
# Prepare builder image
|
||||
FROM frappe/bench:latest as assets
|
||||
|
||||
ARG FRAPPE_VERSION=version-14
|
||||
ARG ERPNEXT_VERSION=version-14
|
||||
ARG APP_NAME
|
||||
|
||||
# Setup frappe-bench using FRAPPE_VERSION
|
||||
RUN bench init --version=${FRAPPE_VERSION} --skip-redis-config-generation --verbose --skip-assets /home/frappe/frappe-bench
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
# Comment following if ERPNext is not required
|
||||
RUN bench get-app --branch=${ERPNEXT_VERSION} --skip-assets --resolve-deps erpnext
|
||||
|
||||
# Copy custom app(s)
|
||||
COPY --chown=frappe:frappe . apps/${APP_NAME}
|
||||
|
||||
# Setup dependencies
|
||||
RUN bench setup requirements
|
||||
|
||||
# Build static assets, copy files instead of symlink
|
||||
RUN bench build --production --verbose --hard-link
|
||||
|
||||
|
||||
# Use frappe-nginx image with nginx template and env vars
|
||||
FROM frappe/frappe-nginx:${FRAPPE_VERSION}
|
||||
|
||||
# Remove existing assets
|
||||
USER root
|
||||
RUN rm -fr /usr/share/nginx/html/assets
|
||||
|
||||
# Copy built assets
|
||||
COPY --from=assets /home/frappe/frappe-bench/sites/assets /usr/share/nginx/html/assets
|
||||
|
||||
# Use non-root user
|
||||
USER 1000
|
@ -44,16 +44,8 @@ target "bench-test" {
|
||||
# Main images
|
||||
# Base for all other targets
|
||||
|
||||
group "frappe" {
|
||||
targets = ["frappe-worker", "frappe-nginx", "frappe-socketio"]
|
||||
}
|
||||
|
||||
group "erpnext" {
|
||||
targets = ["erpnext-worker", "erpnext-nginx"]
|
||||
}
|
||||
|
||||
group "default" {
|
||||
targets = ["frappe", "erpnext"]
|
||||
targets = ["erpnext"]
|
||||
}
|
||||
|
||||
function "tag" {
|
||||
@ -68,46 +60,20 @@ function "tag" {
|
||||
|
||||
target "default-args" {
|
||||
args = {
|
||||
FRAPPE_REPO = "${FRAPPE_REPO}"
|
||||
ERPNEXT_REPO = "${ERPNEXT_REPO}"
|
||||
FRAPPE_PATH = "${FRAPPE_REPO}"
|
||||
ERPNEXT_PATH = "${ERPNEXT_REPO}"
|
||||
BENCH_REPO = "${BENCH_REPO}"
|
||||
FRAPPE_VERSION = "${FRAPPE_VERSION}"
|
||||
ERPNEXT_VERSION = "${ERPNEXT_VERSION}"
|
||||
FRAPPE_BRANCH = "${FRAPPE_VERSION}"
|
||||
ERPNEXT_BRANCH = "${ERPNEXT_VERSION}"
|
||||
PYTHON_VERSION = can(regex("v13", "${ERPNEXT_VERSION}")) ? "3.9.9" : "3.10.5"
|
||||
NODE_VERSION = can(regex("v13", "${FRAPPE_VERSION}")) ? "14.19.3" : "16.18.0"
|
||||
}
|
||||
}
|
||||
|
||||
target "frappe-worker" {
|
||||
target "erpnext" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/worker"
|
||||
target = "frappe"
|
||||
tags = tag("frappe-worker", "${FRAPPE_VERSION}")
|
||||
}
|
||||
|
||||
target "erpnext-worker" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/worker"
|
||||
context = "."
|
||||
dockerfile = "images/production/Containerfile"
|
||||
target = "erpnext"
|
||||
tags = tag("erpnext-worker", "${ERPNEXT_VERSION}")
|
||||
}
|
||||
|
||||
target "frappe-nginx" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/nginx"
|
||||
target = "frappe"
|
||||
tags = tag("frappe-nginx", "${FRAPPE_VERSION}")
|
||||
}
|
||||
|
||||
target "erpnext-nginx" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/nginx"
|
||||
target = "erpnext"
|
||||
tags = tag("erpnext-nginx", "${ERPNEXT_VERSION}")
|
||||
}
|
||||
|
||||
target "frappe-socketio" {
|
||||
inherits = ["default-args"]
|
||||
context = "images/socketio"
|
||||
tags = tag("frappe-socketio", "${FRAPPE_VERSION}")
|
||||
tags = tag("erpnext", "${ERPNEXT_VERSION}")
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ Create backup service or stack.
|
||||
version: "3.7"
|
||||
services:
|
||||
backup:
|
||||
image: frappe/erpnext-worker:v14
|
||||
image: frappe/erpnext:v14
|
||||
entrypoint: ["bash", "-c"]
|
||||
command: |
|
||||
for SITE in $(/home/frappe/frappe-bench/env/bin/python -c "import frappe;print(' '.join(frappe.utils.get_sites()))")
|
||||
|
101
docs/custom-apps.md
Normal file
101
docs/custom-apps.md
Normal file
@ -0,0 +1,101 @@
|
||||
### Clone frappe_docker and switch directory
|
||||
|
||||
```shell
|
||||
git clone https://github.com/frappe/frappe_docker
|
||||
cd frappe_docker
|
||||
```
|
||||
|
||||
### Load custom apps through json
|
||||
|
||||
`apps.json` needs to be passed in as build arg environment variable.
|
||||
|
||||
```shell
|
||||
export APPS_JSON='[
|
||||
{
|
||||
"url": "https://github.com/frappe/payments",
|
||||
"branch": "develop"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/frappe/erpnext",
|
||||
"branch": "version-14"
|
||||
},
|
||||
{
|
||||
"url": "https://user:password@git.example.com/project/repository.git",
|
||||
"branch": "main"
|
||||
}
|
||||
]'
|
||||
export APPS_JSON_BASE64=$(echo ${APPS_JSON} | base64 --wrap=0)
|
||||
```
|
||||
|
||||
You can also generate base64 string from json file:
|
||||
|
||||
```shell
|
||||
export APPS_JSON_BASE64=$(base64 --wrap=0 /path/to/apps.json)
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- `url` needs to be http(s) git url with token/auth in case of private repo.
|
||||
- add dependencies manually in `apps.json` e.g. add `payments` if you are installing `erpnext`
|
||||
- use fork repo or branch for ERPNext in case you need to use your fork or test a PR.
|
||||
|
||||
### Build Image
|
||||
|
||||
```shell
|
||||
buildah build \
|
||||
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
|
||||
--build-arg=FRAPPE_BRANCH=version-14 \
|
||||
--build-arg=PYTHON_VERSION=3.10.5 \
|
||||
--build-arg=NODE_VERSION=16.18.0 \
|
||||
--build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \
|
||||
--tag=ghcr.io/user/repo/custom:1.0.0 \
|
||||
--file=images/custom/Containerfile .
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
- Use `docker` instead of `buildah` as per your setup.
|
||||
- `FRAPPE_PATH` and `FRAPPE_BRANCH` build args are optional and can be overridden in case of fork/branch or test a PR.
|
||||
- Make sure `APPS_JSON_BASE64` variable has correct base64 encoded JSON string. It is consumed as build arg, base64 encoding ensures it to be friendly with environment variables. Use `jq empty apps.json` to validate `apps.json` file.
|
||||
- Make sure the `--tag` is valid image name that will be pushed to registry.
|
||||
- Change `--build-arg` as per version of Python, NodeJS, Frappe Framework repo and branch
|
||||
- Set `--build-arg=REMOVE_GIT_REMOTE=true` to remove git upstream remotes from all apps. Use this in case they have secrets or private tokens and you don't wish to ship them in final image.
|
||||
|
||||
### Push image to use in yaml files
|
||||
|
||||
Login to `docker` or `buildah`
|
||||
|
||||
```shell
|
||||
buildah login
|
||||
```
|
||||
|
||||
Push image
|
||||
|
||||
```shell
|
||||
buildah push ghcr.io/user/repo/custom:1.0.0
|
||||
```
|
||||
|
||||
### Use Kaniko
|
||||
|
||||
Following executor args are required. Example runs locally in docker container.
|
||||
You can run it part of CI/CD or part of your cluster.
|
||||
|
||||
```shell
|
||||
podman run --rm -it \
|
||||
-v "$HOME"/.docker/config.json:/kaniko/.docker/config.json \
|
||||
gcr.io/kaniko-project/executor:latest \
|
||||
--dockerfile=images/custom/Containerfile \
|
||||
--context=git://github.com/frappe/frappe_docker \
|
||||
--build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \
|
||||
--build-arg=FRAPPE_BRANCH=version-14 \
|
||||
--build-arg=PYTHON_VERSION=3.10.5 \
|
||||
--build-arg=NODE_VERSION=16.18.0 \
|
||||
--build-arg=APPS_JSON=$APPS_JSON \
|
||||
--destination=ghcr.io/user/repo/custom:1.0.0
|
||||
```
|
||||
|
||||
More about [kaniko](https://github.com/GoogleContainerTools/kaniko)
|
||||
|
||||
### Use Images
|
||||
|
||||
Make sure image name is correct to be pushed to registry. After the images are pushed, you can pull them to servers to be deployed. If the registry is private, additional auth is needed.
|
@ -14,9 +14,9 @@ It is recommended you allocate at least 4GB of RAM to docker:
|
||||
- [Instructions for macOS](https://docs.docker.com/desktop/settings/mac/#advanced)
|
||||
|
||||
Here is a screenshot showing the relevant setting in the Help Manual
|
||||
![image](/docs/images/Docker%20Manual%20Screenshot%20-%20Resources%20section.png)
|
||||
![image](images/Docker%20Manual%20Screenshot%20-%20Resources%20section.png)
|
||||
Here is a screenshot showing the settings in Docker Desktop on Mac
|
||||
![images](/docs/images/Docker%20Desktop%20Screenshot%20-%20Resources%20section.png)
|
||||
![images](images/Docker%20Desktop%20Screenshot%20-%20Resources%20section.png)
|
||||
|
||||
## Bootstrap Containers for development
|
||||
|
60
docs/environment-variables.md
Normal file
60
docs/environment-variables.md
Normal file
@ -0,0 +1,60 @@
|
||||
## Environment Variables
|
||||
|
||||
All of the commands are directly passed to container as per type of service. Only environment variables used in image are for `nginx-entrypoint.sh` command. They are as follows:
|
||||
|
||||
- `BACKEND`: Set to `{host}:{port}`, defaults to `0.0.0.0:8000`
|
||||
- `SOCKETIO`: Set to `{host}:{port}`, defaults to `0.0.0.0:9000`
|
||||
- `UPSTREAM_REAL_IP_ADDRESS`: Set Nginx config for [ngx_http_realip_module#set_real_ip_from](http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from), defaults to `127.0.0.1`
|
||||
- `UPSTREAM_REAL_IP_HEADER`: Set Nginx config for [ngx_http_realip_module#real_ip_header](http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_header), defaults to `X-Forwarded-For`
|
||||
- `UPSTREAM_REAL_IP_RECURSIVE`: Set Nginx config for [ngx_http_realip_module#real_ip_recursive](http://nginx.org/en/docs/http/ngx_http_realip_module.html#real_ip_recursive) Set defaults to `off`
|
||||
- `FRAPPE_SITE_NAME_HEADER`: Set proxy header `X-Frappe-Site-Name` and serve site named in the header, defaults to `$host`, i.e. find site name from host header. More details [below](#frappe_site_name_header)
|
||||
- `PROXY_READ_TIMEOUT`: Upstream gunicorn service timeout, defaults to `120`
|
||||
- `CLIENT_MAX_BODY_SIZE`: Max body size for uploads, defaults to `50m`
|
||||
|
||||
To bypass `nginx-entrypoint.sh`, mount desired `/etc/nginx/conf.d/default.conf` and run `nginx -g 'daemon off;'` as container command.
|
||||
|
||||
## Configuration
|
||||
|
||||
We use environment variables to configure our setup. docker-compose uses variables from `.env` file. To get started, copy `example.env` to `.env`.
|
||||
|
||||
### `FRAPPE_VERSION`
|
||||
|
||||
Frappe framework release. You can find all releases [here](https://github.com/frappe/frappe/releases).
|
||||
|
||||
### `DB_PASSWORD`
|
||||
|
||||
Password for MariaDB (or Postgres) database.
|
||||
|
||||
### `DB_HOST`
|
||||
|
||||
Hostname for MariaDB (or Postgres) database. Set only if external service for database is used.
|
||||
|
||||
### `DB_PORT`
|
||||
|
||||
Port for MariaDB (3306) or Postgres (5432) database. Set only if external service for database is used.
|
||||
|
||||
### `REDIS_CACHE`
|
||||
|
||||
Hostname for redis server to store cache. Set only if external service for redis is used.
|
||||
|
||||
### `REDIS_QUEUE`
|
||||
|
||||
Hostname for redis server to store queue data. Set only if external service for redis is used.
|
||||
|
||||
### `REDIS_SOCKETIO`
|
||||
|
||||
Hostname for redis server to store socketio data. Set only if external service for redis is used.
|
||||
|
||||
### `ERPNEXT_VERSION`
|
||||
|
||||
ERPNext [release](https://github.com/frappe/frappe/releases). This variable is required if you use ERPNext override.
|
||||
|
||||
### `LETSENCRYPT_EMAIL`
|
||||
|
||||
Email that used to register https certificate. This one is required only if you use HTTPS override.
|
||||
|
||||
### `FRAPPE_SITE_NAME_HEADER`
|
||||
|
||||
This environment variable is not required. Default value is `$$host` which resolves site by host. For example, if your host is `example.com`, site's name should be `example.com`, or if host is `127.0.0.1` (local debugging), it should be `127.0.0.1` This variable allows to override described behavior. Let's say you create site named `mysite` and do want to access it by `127.0.0.1` host. Than you would set this variable to `mysite`.
|
||||
|
||||
There is other variables not mentioned here. They're somewhat internal and you don't have to worry about them except you want to change main compose file.
|
@ -1,101 +0,0 @@
|
||||
# Images
|
||||
|
||||
There's 4 images that you can find in `/images` directory:
|
||||
|
||||
- `bench`. It is used for development. [Learn more how to start development](../development/README.md).
|
||||
- `nginx`. This image contains JS and CSS assets. Container using this image also routes incoming requests using [nginx](https://www.nginx.com).
|
||||
- `socketio`. Container using this image processes realtime websocket requests using [Socket.IO](https://socket.io).
|
||||
- `worker`. Multi-purpose Python backend. Runs [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/) with [gunicorn](https://gunicorn.org), queues (via `bench worker`), or schedule (via `bench schedule`).
|
||||
|
||||
> `nginx`, `socketio` and `worker` images — everything we need to be able to run all processes that Frappe framework requires (take a look at [Bench Procfile reference](https://frappeframework.com/docs/v13/user/en/bench/resources/bench-procfile)). We follow [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#decouple-applications) and split these processes to different containers.
|
||||
|
||||
> ERPNext images don't have their own Dockerfiles. We use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) to reuse as much things as possible and make our builds more efficient.
|
||||
|
||||
# Compose files
|
||||
|
||||
After building the images we have to run the containers. The best and simplest way to do this is to use [compose files](https://docs.docker.com/compose/compose-file/).
|
||||
|
||||
We have one main compose file, `compose.yaml`. Services described, networking, volumes are also handled there.
|
||||
|
||||
## Services
|
||||
|
||||
All services are described in `compose.yaml`
|
||||
|
||||
- `configurator`. Updates `common_site_config.json` so Frappe knows how to access db and redis. It is executed on every `docker-compose up` (and exited immediately). Other services start after this container exits successfully.
|
||||
- `backend`. [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/).
|
||||
- `db`. Optional service that runs [MariaDB](https://mariadb.com) if you also use `overrides/compose.mariadb.yaml` or [Postgres](https://www.postgresql.org) if you also use `overrides/compose.postgres.yaml`.
|
||||
- `redis`. Optional service that runs [Redis](https://redis.io) server with cache, [Socket.IO](https://socket.io) and queues data.
|
||||
- `frontend`. [nginx](https://www.nginx.com) server that serves JS/CSS assets and routes incoming requests.
|
||||
- `proxy`. [Traefik](https://traefik.io/traefik/) proxy. It is here for complicated setups or HTTPS override (with `overrides/compose.https.yaml`).
|
||||
- `websocket`. Node server that runs [Socket.IO](https://socket.io).
|
||||
- `queue-short`, `queue-default`, `queue-long`. Python servers that run job queues using [rq](https://python-rq.org).
|
||||
- `scheduler`. Python server that runs tasks on schedule using [schedule](https://schedule.readthedocs.io/en/stable/).
|
||||
|
||||
## Overrides
|
||||
|
||||
We have several [overrides](https://docs.docker.com/compose/extends/):
|
||||
|
||||
- `overrides/compose.proxy.yaml`. Adds traefik proxy to setup.
|
||||
- `overrides/compose.noproxy.yaml`. Publishes `frontend` ports directly without any proxy.
|
||||
- `overrides/compose.erpnext.yaml`. Replaces all Frappe images with ERPNext ones. ERPNext images are built on top of Frappe ones, so it is safe to replace them.
|
||||
- `overrides/compose.https.yaml`. Automatically sets up Let's Encrypt certificate and redirects all requests to directed to http, to https.
|
||||
- `overrides/compose.mariadb.yaml`. Adds `db` service and sets its image to MariaDB.
|
||||
- `overrides/compose.postgres.yaml`. Adds `db` service and sets its image to Postgres. Note that ERPNext currently doesn't support Postgres.
|
||||
- `overrides/compose.redis.yaml`. Adds `redis` service and sets its image to `redis`.
|
||||
|
||||
It is quite simple to run overrides. All we need to do is to specify compose files that should be used by docker-compose. For example, we want ERPNext:
|
||||
|
||||
```bash
|
||||
# Point to main compose file (compose.yaml) and add one more.
|
||||
docker-compose -f compose.yaml -f overrides/compose.erpnext.yaml config
|
||||
```
|
||||
|
||||
⚠ Make sure to use docker-compose v2 (run `docker-compose -v` to check). If you want to use v1 make sure the correct `$`-signs as they get duplicated by the `config` command!
|
||||
|
||||
That's it! Of course, we also have to setup `.env` before all of that, but that's not the point.
|
||||
|
||||
## Configuration
|
||||
|
||||
We use environment variables to configure our setup. docker-compose uses variables from `.env` file. To get started, copy `example.env` to `.env`.
|
||||
|
||||
### `FRAPPE_VERSION`
|
||||
|
||||
Frappe framework release. You can find all releases [here](https://github.com/frappe/frappe/releases).
|
||||
|
||||
### `DB_PASSWORD`
|
||||
|
||||
Password for MariaDB (or Postgres) database.
|
||||
|
||||
### `DB_HOST`
|
||||
|
||||
Hostname for MariaDB (or Postgres) database. Set only if external service for database is used.
|
||||
|
||||
### `DB_PORT`
|
||||
|
||||
Port for MariaDB (3306) or Postgres (5432) database. Set only if external service for database is used.
|
||||
|
||||
### `REDIS_CACHE`
|
||||
|
||||
Hostname for redis server to store cache. Set only if external service for redis is used.
|
||||
|
||||
### `REDIS_QUEUE`
|
||||
|
||||
Hostname for redis server to store queue data. Set only if external service for redis is used.
|
||||
|
||||
### `REDIS_SOCKETIO`
|
||||
|
||||
Hostname for redis server to store socketio data. Set only if external service for redis is used.
|
||||
|
||||
### `ERPNEXT_VERSION`
|
||||
|
||||
ERPNext [release](https://github.com/frappe/frappe/releases). This variable is required if you use ERPNext override.
|
||||
|
||||
### `LETSENCRYPT_EMAIL`
|
||||
|
||||
Email that used to register https certificate. This one is required only if you use HTTPS override.
|
||||
|
||||
### `FRAPPE_SITE_NAME_HEADER`
|
||||
|
||||
This environment variable is not required. Default value is `$$host` which resolves site by host. For example, if your host is `example.com`, site's name should be `example.com`, or if host is `127.0.0.1` (local debugging), it should be `127.0.0.1` This variable allows to override described behavior. Let's say you create site named `mysite` and do want to access it by `127.0.0.1` host. Than you would set this variable to `mysite`.
|
||||
|
||||
There is other variables not mentioned here. They're somewhat internal and you don't have to worry about them except you want to change main compose file.
|
58
docs/list-of-containers.md
Normal file
58
docs/list-of-containers.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Images
|
||||
|
||||
There's 4 images that you can find in `/images` directory:
|
||||
|
||||
- `bench`. It is used for development. [Learn more how to start development](../development/README.md).
|
||||
- `production`.
|
||||
- Multi-purpose Python backend. Runs [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/) with [gunicorn](https://gunicorn.org), queues (via `bench worker`), or schedule (via `bench schedule`).
|
||||
- Contains JS and CSS assets and routes incoming requests using [nginx](https://www.nginx.com).
|
||||
- Processes realtime websocket requests using [Socket.IO](https://socket.io).
|
||||
- `custom`. It is used to build bench using `apps.json` file set with `--apps_path` during bench initialization. `apps.json` is a json array. e.g. `[{"url":"{{repo_url}}","branch":"{{repo_branch}}"}]`
|
||||
|
||||
Image has everything we need to be able to run all processes that Frappe framework requires (take a look at [Bench Procfile reference](https://frappeframework.com/docs/v14/user/en/bench/resources/bench-procfile)). We follow [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#decouple-applications) and split these processes to different containers.
|
||||
|
||||
> We use [multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) and [Docker Buildx](https://docs.docker.com/engine/reference/commandline/buildx/) to reuse as much things as possible and make our builds more efficient.
|
||||
|
||||
# Compose files
|
||||
|
||||
After building the images we have to run the containers. The best and simplest way to do this is to use [compose files](https://docs.docker.com/compose/compose-file/).
|
||||
|
||||
We have one main compose file, `compose.yaml`. Services described, networking, volumes are also handled there.
|
||||
|
||||
## Services
|
||||
|
||||
All services are described in `compose.yaml`
|
||||
|
||||
- `configurator`. Updates `common_site_config.json` so Frappe knows how to access db and redis. It is executed on every `docker-compose up` (and exited immediately). Other services start after this container exits successfully.
|
||||
- `backend`. [Werkzeug server](https://werkzeug.palletsprojects.com/en/2.0.x/).
|
||||
- `db`. Optional service that runs [MariaDB](https://mariadb.com) if you also use `overrides/compose.mariadb.yaml` or [Postgres](https://www.postgresql.org) if you also use `overrides/compose.postgres.yaml`.
|
||||
- `redis`. Optional service that runs [Redis](https://redis.io) server with cache, [Socket.IO](https://socket.io) and queues data.
|
||||
- `frontend`. [nginx](https://www.nginx.com) server that serves JS/CSS assets and routes incoming requests.
|
||||
- `proxy`. [Traefik](https://traefik.io/traefik/) proxy. It is here for complicated setups or HTTPS override (with `overrides/compose.https.yaml`).
|
||||
- `websocket`. Node server that runs [Socket.IO](https://socket.io).
|
||||
- `queue-short`, `queue-default`, `queue-long`. Python servers that run job queues using [rq](https://python-rq.org).
|
||||
- `scheduler`. Python server that runs tasks on schedule using [schedule](https://schedule.readthedocs.io/en/stable/).
|
||||
|
||||
## Overrides
|
||||
|
||||
We have several [overrides](https://docs.docker.com/compose/extends/):
|
||||
|
||||
- `overrides/compose.proxy.yaml`. Adds traefik proxy to setup.
|
||||
- `overrides/compose.noproxy.yaml`. Publishes `frontend` ports directly without any proxy.
|
||||
- `overrides/compose.https.yaml`. Automatically sets up Let's Encrypt certificate and redirects all requests to directed to http, to https.
|
||||
- `overrides/compose.mariadb.yaml`. Adds `db` service and sets its image to MariaDB.
|
||||
- `overrides/compose.postgres.yaml`. Adds `db` service and sets its image to Postgres. Note that ERPNext currently doesn't support Postgres.
|
||||
- `overrides/compose.redis.yaml`. Adds `redis` service and sets its image to `redis`.
|
||||
|
||||
It is quite simple to run overrides. All we need to do is to specify compose files that should be used by docker-compose. For example, we want ERPNext:
|
||||
|
||||
```bash
|
||||
# Point to main compose file (compose.yaml) and add one more.
|
||||
docker-compose -f compose.yaml -f overrides/compose.redis.yaml config
|
||||
```
|
||||
|
||||
⚠ Make sure to use docker-compose v2 (run `docker-compose -v` to check). If you want to use v1 make sure the correct `$`-signs as they get duplicated by the `config` command!
|
||||
|
||||
That's it! Of course, we also have to setup `.env` before all of that, but that's not the point.
|
||||
|
||||
Check [environment variables](environment-variables.md) for more.
|
115
docs/migrate-from-multi-image-setup.md
Normal file
115
docs/migrate-from-multi-image-setup.md
Normal file
@ -0,0 +1,115 @@
|
||||
## Migrate from multi-image setup
|
||||
|
||||
All the containers now use same image. Use `frappe/erpnext` instead of `frappe/frappe-worker`, `frappe/frappe-nginx` , `frappe/frappe-socketio` , `frappe/erpnext-worker` and `frappe/erpnext-nginx`.
|
||||
|
||||
Now you need to specify command and environment variables for following containers:
|
||||
|
||||
### Frontend
|
||||
|
||||
For `frontend` service to act as static assets frontend and reverse proxy, you need to pass `nginx-entrypoint.sh` as container `command` and `BACKEND` and `SOCKETIO` environment variables pointing `{host}:{port}` for gunicorn and websocket services. Check [environment variables](environment-variables.md)
|
||||
|
||||
Now you only need to mount the `sites` volume at location `/home/frappe/frappe-bench/sites`. No need for `assets` volume and asset population script or steps.
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
frontend:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
# ... removed for brevity
|
||||
```
|
||||
|
||||
### Websocket
|
||||
|
||||
For `websocket` service to act as socketio backend, you need to pass `["node", "/home/frappe/frappe-bench/apps/frappe/socketio.js"]` as container `command`
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
websocket:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
# ... removed for brevity
|
||||
```
|
||||
|
||||
### Configurator
|
||||
|
||||
For `configurator` service to act as run once configuration job, you need to pass `["bash", "-c"]` as container `entrypoint` and bash script inline to yaml. There is no `configure.py` in the container now.
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
configurator:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
restart: "no"
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- >
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -g redis_socketio "redis://$$REDIS_SOCKETIO";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
REDIS_SOCKETIO: redis-socketio:6379
|
||||
SOCKETIO_PORT: "9000"
|
||||
# ... removed for brevity
|
||||
```
|
||||
|
||||
### Site Creation
|
||||
|
||||
For `create-site` service to act as run once site creation job, you need to pass `["bash", "-c"]` as container `entrypoint` and bash script inline to yaml. Make sure to use `--no-mariadb-socket` as upstream bench is installed in container.
|
||||
|
||||
The `WORKDIR` has changed to `/home/frappe/frappe-bench` like `bench` setup we are used to. So the path to find `common_site_config.json` has changed to `sites/common_site_config.json`.
|
||||
|
||||
Example change:
|
||||
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
create-site:
|
||||
image: frappe/erpnext:${ERPNEXT_VERSION:?ERPNext version not set}
|
||||
restart: "no"
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- >
|
||||
wait-for-it -t 120 db:3306;
|
||||
wait-for-it -t 120 redis-cache:6379;
|
||||
wait-for-it -t 120 redis-queue:6379;
|
||||
wait-for-it -t 120 redis-socketio:6379;
|
||||
export start=`date +%s`;
|
||||
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
do
|
||||
echo "Waiting for sites/common_site_config.json to be created";
|
||||
sleep 5;
|
||||
if (( `date +%s`-start > 120 )); then
|
||||
echo "could not find sites/common_site_config.json with required keys";
|
||||
exit 1
|
||||
fi
|
||||
done;
|
||||
echo "sites/common_site_config.json found";
|
||||
bench new-site frontend --no-mariadb-socket --admin-password=admin --db-root-password=admin --install-app payments --install-app erpnext --set-default;
|
||||
|
||||
# ... removed for brevity
|
||||
```
|
@ -1,34 +0,0 @@
|
||||
Example: https://discuss.erpnext.com/t/sms-two-factor-authentication-otp-msg-change/47835
|
||||
|
||||
Above example needs following Dockerfile based patch
|
||||
|
||||
```Dockerfile
|
||||
FROM frappe/erpnext-worker:v12.17.0
|
||||
|
||||
...
|
||||
USER root
|
||||
RUN sed -i -e "s/Your verification code is/আপনার লগইন কোড/g" /home/frappe/frappe-bench/apps/frappe/frappe/twofactor.py
|
||||
USER frappe
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
Example for `nginx` image,
|
||||
|
||||
```Dockerfile
|
||||
FROM frappe/erpnext-nginx:v13.27.0
|
||||
|
||||
# Hack to use Frappe/ERPNext offline.
|
||||
RUN sed -i 's/navigator.onLine/navigator.onLine||true/' \
|
||||
/usr/share/nginx/html/assets/js/desk.min.js \
|
||||
/usr/share/nginx/html/assets/js/dialog.min.js \
|
||||
/usr/share/nginx/html/assets/js/frappe-web.min.js
|
||||
```
|
||||
|
||||
Alternatively copy the modified source code file directly over `/home/frappe/frappe-bench/apps/frappe/frappe/twofactor.py`
|
||||
|
||||
```Dockerfile
|
||||
...
|
||||
COPY twofactor.py /home/frappe/frappe-bench/apps/frappe/frappe/twofactor.py
|
||||
...
|
||||
```
|
@ -6,43 +6,64 @@ Remove the traefik service from docker-compose.yml
|
||||
|
||||
### Step 2
|
||||
|
||||
Create nginx config file `/opt/nginx/conf/serve-8001.conf`:
|
||||
Add service for each port that needs to be exposed.
|
||||
|
||||
```
|
||||
server {
|
||||
listen 8001;
|
||||
server_name $http_host;
|
||||
e.g. `port-site-1`, `port-site-2`, `port-site-3`.
|
||||
|
||||
location / {
|
||||
|
||||
rewrite ^(.+)/$ $1 permanent;
|
||||
rewrite ^(.+)/index\.html$ $1 permanent;
|
||||
rewrite ^(.+)\.html$ $1 permanent;
|
||||
|
||||
proxy_set_header X-Frappe-Site-Name mysite.localhost;
|
||||
proxy_set_header Host mysite.localhost;
|
||||
proxy_pass http://frontend;
|
||||
}
|
||||
}
|
||||
```yaml
|
||||
# ... removed for brevity
|
||||
services:
|
||||
# ... removed for brevity
|
||||
port-site-1:
|
||||
image: frappe/erpnext:v14.11.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: site1.local
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
ports:
|
||||
- "8080:8080"
|
||||
port-site-2:
|
||||
image: frappe/erpnext:v14.11.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: site2.local
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
ports:
|
||||
- "8081:8080"
|
||||
port-site-3:
|
||||
image: frappe/erpnext:v14.11.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: site3.local
|
||||
SOCKETIO: websocket:9000
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
ports:
|
||||
- "8082:8080"
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Replace the port with any port of choice e.g. `listen 4200;`
|
||||
- Change `mysite.localhost` to site name
|
||||
- Repeat the server blocks for multiple ports and site names to get the effect of port based multi tenancy
|
||||
- For old images use `proxy_pass http://erpnext-nginx` instead of `proxy_pass http://frontend`
|
||||
|
||||
### Step 3
|
||||
|
||||
Run the docker container
|
||||
|
||||
```shell
|
||||
docker run --network=<project-name>_default \
|
||||
-p 8001:8001 \
|
||||
--volume=/opt/nginx/conf/serve-8001.conf:/etc/nginx/conf.d/default.conf -d nginx
|
||||
```
|
||||
|
||||
Note: Change the volumes, network and ports as needed
|
||||
|
||||
With the above example configured site will be accessible on `http://localhost:8001`
|
||||
- Above setup will expose `site1.local`, `site2.local`, `site3.local` on port `8080`, `8081`, `8082` respectively.
|
||||
- Change `site1.local` to site name to serve from bench.
|
||||
- Change the `BACKEND` and `SOCKETIO` environment variables as per your service names.
|
||||
- Make sure `sites:` volume is available as part of yaml.
|
||||
|
@ -60,7 +60,6 @@ environment variables or the `configurator` will fail.
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.proxy.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
# Start containers
|
||||
@ -86,7 +85,6 @@ docker compose --project-name <project-name> -f ~/gitops/docker-compose.yml up -
|
||||
```sh
|
||||
# Generate YAML
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
-f overrides/compose.mariadb.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f overrides/compose.https.yaml \
|
||||
@ -110,7 +108,6 @@ nano .env
|
||||
|
||||
# Pull new images
|
||||
docker compose -f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
# ... your other overrides
|
||||
config > ~/gitops/docker-compose.yml
|
||||
|
||||
|
40
docs/single-compose-setup.md
Normal file
40
docs/single-compose-setup.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Single Compose Setup
|
||||
|
||||
This setup is a very simple single compose file that does everything to start required services and a frappe-bench. It is used to start play with docker instance with a site. The file is located in the root of repo and named `pwd.yml`.
|
||||
|
||||
## Services
|
||||
|
||||
### frappe-bench components
|
||||
|
||||
- backend, serves gunicorn backend
|
||||
- frontend, serves static assets through nginx frontend reverse proxies websocket and gunicorn.
|
||||
- queue-default, default rq worker.
|
||||
- queue-long, long rq worker.
|
||||
- queue-short, short rq worker.
|
||||
- schedule, event scheduler.
|
||||
- websocket, socketio websocket for realtime communication.
|
||||
|
||||
### Run once configuration
|
||||
|
||||
- configurator, configures `common_site_config.json` to set db and redis hosts.
|
||||
- create-site, creates one site to serve as default site for the frappe-bench.
|
||||
|
||||
### Service dependencies
|
||||
|
||||
- db, mariadb, container with frappe specific configuration.
|
||||
- redis-cache, redis for cache data.
|
||||
- redis-queue, redis for rq data.
|
||||
- redis-socketio, redis for socketio pubsub.
|
||||
|
||||
## Volumes
|
||||
|
||||
- sites: Volume for bench data. Common config, all sites, all site configs and site files will be stored here.
|
||||
- logs: Volume for bench logs. all process logs are dumped here. No need to mount it. Each container will create a temporary volume for logs if not specified.
|
||||
|
||||
## Adaptation
|
||||
|
||||
If you understand containers use the `pwd.yml` as a reference to build more complex setup like, single server example, Docker Swarm stack, Kubernetes Helm chart, etc.
|
||||
|
||||
This serves only site called `frontend` through the nginx. `FRAPPE_SITE_NAME_HEADER` is set to `frontend` and a default site called `frontend` is created.
|
||||
|
||||
Change the `$$host` will allow container to accept any host header and serve that site. To escape `$` in compose yaml use it like `$$`. To unset default site remove `currentsite.txt` file from `sites` directory.
|
@ -87,13 +87,13 @@ Deploy the traefik container with letsencrypt SSL
|
||||
```shell
|
||||
docker compose --project-name traefik \
|
||||
--env-file ~/gitops/traefik.env \
|
||||
-f docs/compose/compose.traefik.yaml \
|
||||
-f docs/compose/compose.traefik-ssl.yaml up -d
|
||||
-f overrides/compose.traefik.yaml \
|
||||
-f overrides/compose.traefik-ssl.yaml up -d
|
||||
```
|
||||
|
||||
This will make the traefik dashboard available on `traefik.example.com` and all certificates will reside in `/data/traefik/certificates` on host filesystem.
|
||||
|
||||
For LAN setup deploy the traefik container without overriding `docs/compose/compose.traefik-ssl.yaml`.
|
||||
For LAN setup deploy the traefik container without overriding `overrides/compose.traefik-ssl.yaml`.
|
||||
|
||||
### Install MariaDB
|
||||
|
||||
@ -120,7 +120,7 @@ Note: Change the password from `changeit` to more secure one.
|
||||
Deploy the mariadb container
|
||||
|
||||
```shell
|
||||
docker compose --project-name mariadb --env-file ~/gitops/mariadb.env -f docs/compose/compose.mariadb-shared.yaml up -d
|
||||
docker compose --project-name mariadb --env-file ~/gitops/mariadb.env -f overrides/compose.mariadb-shared.yaml up -d
|
||||
```
|
||||
|
||||
This will make `mariadb-database` service available under `mariadb-network`. Data will reside in `/data/mariadb`.
|
||||
@ -155,10 +155,9 @@ Create a yaml file called `erpnext-one.yaml` in `~/gitops` directory:
|
||||
docker compose --project-name erpnext-one \
|
||||
--env-file ~/gitops/erpnext-one.env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f docs/compose/compose.multi-bench.yaml \
|
||||
-f docs/compose/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-one.yaml
|
||||
-f overrides/compose.multi-bench.yaml \
|
||||
-f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-one.yaml
|
||||
```
|
||||
|
||||
For LAN setup do not override `compose.multi-bench-ssl.yaml`.
|
||||
@ -176,7 +175,7 @@ Create sites `one.example.com` and `two.example.com`:
|
||||
```shell
|
||||
# one.example.com
|
||||
docker compose --project-name erpnext-one exec backend \
|
||||
bench new-site one.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site one.example.com --no-mariadb-socket --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
```
|
||||
|
||||
You can stop here and have a single bench single site setup complete. Continue to add one more site to the current bench.
|
||||
@ -184,7 +183,7 @@ You can stop here and have a single bench single site setup complete. Continue t
|
||||
```shell
|
||||
# two.example.com
|
||||
docker compose --project-name erpnext-one exec backend \
|
||||
bench new-site two.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site two.example.com --no-mariadb-socket --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
```
|
||||
|
||||
#### Create second bench
|
||||
@ -217,10 +216,9 @@ Create a yaml file called `erpnext-two.yaml` in `~/gitops` directory:
|
||||
docker compose --project-name erpnext-two \
|
||||
--env-file ~/gitops/erpnext-two.env \
|
||||
-f compose.yaml \
|
||||
-f overrides/compose.erpnext.yaml \
|
||||
-f overrides/compose.redis.yaml \
|
||||
-f docs/compose/compose.multi-bench.yaml \
|
||||
-f docs/compose/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-two.yaml
|
||||
-f overrides/compose.multi-bench.yaml \
|
||||
-f overrides/compose.multi-bench-ssl.yaml config > ~/gitops/erpnext-two.yaml
|
||||
```
|
||||
|
||||
Use the above command after any changes are made to `erpnext-two.env` file to regenerate `~/gitops/erpnext-two.yaml`. e.g. after changing version to migrate the bench.
|
||||
@ -236,10 +234,10 @@ Create sites `three.example.com` and `four.example.com`:
|
||||
```shell
|
||||
# three.example.com
|
||||
docker compose --project-name erpnext-two exec backend \
|
||||
bench new-site three.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site three.example.com --no-mariadb-socket --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
# four.example.com
|
||||
docker compose --project-name erpnext-two exec backend \
|
||||
bench new-site four.example.com --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
bench new-site four.example.com --no-mariadb-socket --mariadb-root-password changeit --install-app erpnext --admin-password changeit
|
||||
```
|
||||
|
||||
#### Create custom domain to existing site
|
||||
@ -271,8 +269,8 @@ Generate yaml to reverse proxy:
|
||||
```shell
|
||||
docker compose --project-name custom-one-example \
|
||||
--env-file ~/gitops/custom-one-example.env \
|
||||
-f docs/compose/compose.custom-domain.yaml \
|
||||
-f docs/compose/compose.custom-domain-ssl.yaml config > ~/gitops/custom-one-example.yaml
|
||||
-f overrides/compose.custom-domain.yaml \
|
||||
-f overrides/compose.custom-domain-ssl.yaml config > ~/gitops/custom-one-example.yaml
|
||||
```
|
||||
|
||||
For LAN setup do not override `compose.custom-domain-ssl.yaml`.
|
||||
|
@ -9,7 +9,7 @@ Note:
|
||||
- Wait for the `db` service to start and `configurator` to exit before trying to create a new site. Usually this takes up to 10 seconds.
|
||||
|
||||
```sh
|
||||
docker-compose exec backend bench new-site <site-name> --mariadb-root-password <db-password> --admin-password <admin-password>
|
||||
docker-compose exec backend bench new-site <site-name> --no-mariadb-socket --mariadb-root-password <db-password> --admin-password <admin-password>
|
||||
```
|
||||
|
||||
If you need to install some app, specify `--install-app`. To see all options, just run `bench new-site --help`.
|
||||
@ -24,7 +24,7 @@ docker-compose exec backend bench set-config -g root_password <root-password>
|
||||
Also command is slightly different:
|
||||
|
||||
```sh
|
||||
docker-compose exec backend bench new-site <site-name> --db-type postgres --admin-password <admin-password>
|
||||
docker-compose exec backend bench new-site <site-name> --no-mariadb-socket --db-type postgres --admin-password <admin-password>
|
||||
```
|
||||
|
||||
## Push backup to S3 storage
|
||||
|
@ -1,5 +1,4 @@
|
||||
1. [Fixing MariaDB issues after rebuilding the container](#fixing-mariadb-issues-after-rebuilding-the-container)
|
||||
1. [Letsencrypt companion not working](#letsencrypt-companion-not-working)
|
||||
1. [docker-compose does not recognize variables from `.env` file](#docker-compose-does-not-recognize-variables-from-env-file)
|
||||
1. [Windows Based Installation](#windows-based-installation)
|
||||
|
||||
@ -46,12 +45,11 @@ Note: For MariaDB 10.4 and above use `mysql.global_priv` instead of `mysql.user`
|
||||
|
||||
### docker-compose does not recognize variables from `.env` file
|
||||
|
||||
If you are using old version of `docker-compose` the .env file needs to be located in directory from where the docker-compose command is executed. There may also be difference in official `docker-compose` and the one packaged by distro.
|
||||
If you are using old version of `docker-compose` the .env file needs to be located in directory from where the docker-compose command is executed. There may also be difference in official `docker-compose` and the one packaged by distro. Use `--env-file=.env` if available to explicitly specify the path to file.
|
||||
|
||||
### Windows Based Installation
|
||||
|
||||
- Set environment variable `COMPOSE_CONVERT_WINDOWS_PATHS` e.g. `set COMPOSE_CONVERT_WINDOWS_PATHS=1`
|
||||
- Make the `frappe-mariadb.cnf` read-only for mariadb container to pick it up.
|
||||
- While using docker machine, port-forward the port 80 of VM to port 80 of host machine
|
||||
- Name all the sites ending with `.localhost`. and access it via browser locally. e.g. `http://site1.localhost`
|
||||
- related issue comment https://github.com/frappe/frappe_docker/issues/448#issuecomment-851723912
|
||||
|
145
images/custom/Containerfile
Normal file
145
images/custom/Containerfile
Normal file
@ -0,0 +1,145 @@
|
||||
ARG PYTHON_VERSION=3.10.5
|
||||
FROM python:${PYTHON_VERSION}-slim-bullseye AS base
|
||||
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6-1
|
||||
ARG NODE_VERSION=16.18.0
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
RUN useradd -ms /bin/bash frappe \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
curl \
|
||||
git \
|
||||
vim \
|
||||
nginx \
|
||||
gettext-base \
|
||||
# MariaDB
|
||||
mariadb-client \
|
||||
# Postgres
|
||||
libpq-dev \
|
||||
postgresql-client \
|
||||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# NodeJS
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >>/home/frappe/.bashrc \
|
||||
# Install wkhtmltopdf with patched qt
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& apt-get install -y ./$downloaded_file \
|
||||
&& rm $downloaded_file \
|
||||
# Clean up
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -fr /etc/nginx/sites-enabled/default \
|
||||
&& pip3 install frappe-bench \
|
||||
# Fixes for non-root nginx and logs to stdout
|
||||
&& sed -i '/user www-data/d' /etc/nginx/nginx.conf \
|
||||
&& ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log \
|
||||
&& touch /run/nginx.pid \
|
||||
&& chown -R frappe:frappe /etc/nginx/conf.d \
|
||||
&& chown -R frappe:frappe /etc/nginx/nginx.conf \
|
||||
&& chown -R frappe:frappe /var/log/nginx \
|
||||
&& chown -R frappe:frappe /var/lib/nginx \
|
||||
&& chown -R frappe:frappe /run/nginx.pid
|
||||
|
||||
COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
|
||||
COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
|
||||
COPY resources/push_backup.py /usr/local/bin/push-backup
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
# For frappe framework
|
||||
wget \
|
||||
# For psycopg2
|
||||
libpq-dev \
|
||||
# Other
|
||||
libffi-dev \
|
||||
liblcms2-dev \
|
||||
libldap2-dev \
|
||||
libmariadb-dev \
|
||||
libsasl2-dev \
|
||||
libtiff5-dev \
|
||||
libwebp-dev \
|
||||
redis-tools \
|
||||
rlwrap \
|
||||
tk8.6-dev \
|
||||
cron \
|
||||
# For pandas
|
||||
gcc \
|
||||
build-essential \
|
||||
libbz2-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# apps.json includes
|
||||
ARG APPS_JSON_BASE64
|
||||
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
|
||||
mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
|
||||
fi
|
||||
|
||||
USER frappe
|
||||
|
||||
ARG REMOVE_GIT_REMOTE
|
||||
ARG FRAPPE_BRANCH=version-14
|
||||
ARG FRAPPE_PATH=https://github.com/frappe/frappe
|
||||
RUN export APP_INSTALL_ARGS="" && \
|
||||
if [ -n "${APPS_JSON_BASE64}" ]; then \
|
||||
export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
|
||||
fi && \
|
||||
bench init ${APP_INSTALL_ARGS}\
|
||||
--frappe-branch=${FRAPPE_BRANCH} \
|
||||
--frappe-path=${FRAPPE_PATH} \
|
||||
--no-procfile \
|
||||
--no-backups \
|
||||
--skip-redis-config-generation \
|
||||
--verbose \
|
||||
/home/frappe/frappe-bench && \
|
||||
cd /home/frappe/frappe-bench && \
|
||||
echo "$(jq 'del(.db_host, .redis_cache, .redis_queue, .redis_socketio)' sites/common_site_config.json)" \
|
||||
> sites/common_site_config.json && \
|
||||
if [ -n "${REMOVE_GIT_REMOTE}" ]; then \
|
||||
find apps -name .git -type d -prune | xargs -i git --git-dir {} remote rm upstream; \
|
||||
fi
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
FROM base as backend
|
||||
|
||||
USER frappe
|
||||
|
||||
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
VOLUME [ \
|
||||
"/home/frappe/frappe-bench/sites", \
|
||||
"/home/frappe/frappe-bench/sites/assets", \
|
||||
"/home/frappe/frappe-bench/logs" \
|
||||
]
|
||||
|
||||
CMD [ \
|
||||
"/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--chdir=/home/frappe/frappe-bench/sites", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
@ -1,62 +0,0 @@
|
||||
FROM frappe/bench:latest as assets_builder
|
||||
|
||||
ARG FRAPPE_VERSION
|
||||
ARG FRAPPE_REPO=https://github.com/frappe/frappe
|
||||
ARG PYTHON_VERSION
|
||||
ARG NODE_VERSION
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
RUN PYENV_VERSION=${PYTHON_VERSION} bench init --version=${FRAPPE_VERSION} --frappe-path=${FRAPPE_REPO} --skip-redis-config-generation --verbose --skip-assets /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
|
||||
FROM assets_builder as frappe_assets
|
||||
|
||||
RUN bench setup requirements \
|
||||
&& if [ -z "${FRAPPE_VERSION##*v14*}" ] || [ "$FRAPPE_VERSION" = "develop" ]; then \
|
||||
export BUILD_OPTS="--production";\
|
||||
fi \
|
||||
&& FRAPPE_ENV=production bench build --verbose --hard-link ${BUILD_OPTS}
|
||||
|
||||
|
||||
FROM assets_builder as erpnext_assets
|
||||
|
||||
ARG ERPNEXT_VERSION
|
||||
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
|
||||
RUN bench get-app --branch=${ERPNEXT_VERSION} --skip-assets --resolve-deps erpnext ${ERPNEXT_REPO}\
|
||||
&& if [ -z "${ERPNEXT_VERSION##*v14*}" ] || [ "$ERPNEXT_VERSION" = "develop" ]; then \
|
||||
export BUILD_OPTS="--production"; \
|
||||
fi \
|
||||
&& FRAPPE_ENV=production bench build --verbose --hard-link ${BUILD_OPTS}
|
||||
|
||||
|
||||
FROM alpine/git as bench
|
||||
|
||||
# Error pages
|
||||
ARG BENCH_REPO=https://github.com/frappe/bench
|
||||
RUN git clone --depth 1 ${BENCH_REPO} /tmp/bench \
|
||||
&& mkdir /out \
|
||||
&& mv /tmp/bench/bench/config/templates/502.html /out \
|
||||
&& touch /out/.build
|
||||
|
||||
FROM nginxinc/nginx-unprivileged:1.23.3-alpine as frappe
|
||||
|
||||
# Set default ENV variables for backwards compatibility
|
||||
ENV PROXY_READ_TIMOUT=120
|
||||
ENV CLIENT_MAX_BODY_SIZE=50m
|
||||
|
||||
# https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/stable/alpine/20-envsubst-on-templates.sh
|
||||
COPY nginx-template.conf /etc/nginx/templates/default.conf.template
|
||||
# https://github.com/nginxinc/docker-nginx-unprivileged/blob/main/stable/alpine/docker-entrypoint.sh
|
||||
COPY entrypoint.sh /docker-entrypoint.d/frappe-entrypoint.sh
|
||||
|
||||
COPY --from=bench /out /usr/share/nginx/html/
|
||||
COPY --from=frappe_assets /home/frappe/frappe-bench/sites/assets /usr/share/nginx/html/assets
|
||||
|
||||
USER 1000
|
||||
|
||||
|
||||
FROM frappe as erpnext
|
||||
|
||||
COPY --from=erpnext_assets /home/frappe/frappe-bench/sites/assets /usr/share/nginx/html/assets
|
@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Update timestamp for ".build" file to enable caching assets:
|
||||
# https://github.com/frappe/frappe/blob/52d8e6d952130eea64a9990b9fd5b1f6877be1b7/frappe/utils/__init__.py#L799-L805
|
||||
if [ -d /usr/share/nginx/html/sites ]; then
|
||||
touch /usr/share/nginx/html/sites/.build -r /usr/share/nginx/html/.build
|
||||
fi
|
132
images/production/Containerfile
Normal file
132
images/production/Containerfile
Normal file
@ -0,0 +1,132 @@
|
||||
ARG PYTHON_VERSION=3.10.5
|
||||
FROM python:${PYTHON_VERSION}-slim-bullseye AS base
|
||||
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6-1
|
||||
ARG NODE_VERSION=16.18.0
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
|
||||
RUN useradd -ms /bin/bash frappe \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
curl \
|
||||
git \
|
||||
vim \
|
||||
nginx \
|
||||
gettext-base \
|
||||
# MariaDB
|
||||
mariadb-client \
|
||||
# Postgres
|
||||
libpq-dev \
|
||||
postgresql-client \
|
||||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# NodeJS
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >>/home/frappe/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >>/home/frappe/.bashrc \
|
||||
# Install wkhtmltopdf with patched qt
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& apt-get install -y ./$downloaded_file \
|
||||
&& rm $downloaded_file \
|
||||
# Clean up
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm -fr /etc/nginx/sites-enabled/default \
|
||||
&& pip3 install frappe-bench \
|
||||
# Fixes for non-root nginx and logs to stdout
|
||||
&& sed -i '/user www-data/d' /etc/nginx/nginx.conf \
|
||||
&& ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log \
|
||||
&& touch /run/nginx.pid \
|
||||
&& chown -R frappe:frappe /etc/nginx/conf.d \
|
||||
&& chown -R frappe:frappe /etc/nginx/nginx.conf \
|
||||
&& chown -R frappe:frappe /var/log/nginx \
|
||||
&& chown -R frappe:frappe /var/lib/nginx \
|
||||
&& chown -R frappe:frappe /run/nginx.pid
|
||||
|
||||
COPY resources/nginx-template.conf /templates/nginx/frappe.conf.template
|
||||
COPY resources/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh
|
||||
COPY resources/push_backup.py /usr/local/bin/push-backup
|
||||
|
||||
FROM base AS builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
# For frappe framework
|
||||
wget \
|
||||
# For psycopg2
|
||||
libpq-dev \
|
||||
# Other
|
||||
libffi-dev \
|
||||
liblcms2-dev \
|
||||
libldap2-dev \
|
||||
libmariadb-dev \
|
||||
libsasl2-dev \
|
||||
libtiff5-dev \
|
||||
libwebp-dev \
|
||||
redis-tools \
|
||||
rlwrap \
|
||||
tk8.6-dev \
|
||||
cron \
|
||||
# For pandas
|
||||
gcc \
|
||||
build-essential \
|
||||
libbz2-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
USER frappe
|
||||
|
||||
ARG FRAPPE_BRANCH=version-14
|
||||
ARG FRAPPE_PATH=https://github.com/frappe/frappe
|
||||
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
|
||||
ARG ERPNEXT_BRANCH=version-14
|
||||
RUN bench init \
|
||||
--frappe-branch=${FRAPPE_BRANCH} \
|
||||
--frappe-path=${FRAPPE_PATH} \
|
||||
--no-procfile \
|
||||
--no-backups \
|
||||
--skip-redis-config-generation \
|
||||
--verbose \
|
||||
/home/frappe/frappe-bench && \
|
||||
cd /home/frappe/frappe-bench && \
|
||||
bench get-app --branch=${ERPNEXT_BRANCH} --resolve-deps erpnext ${ERPNEXT_REPO} && \
|
||||
echo "$(jq 'del(.db_host, .redis_cache, .redis_queue, .redis_socketio)' sites/common_site_config.json)" \
|
||||
> sites/common_site_config.json
|
||||
|
||||
FROM base as erpnext
|
||||
|
||||
USER frappe
|
||||
|
||||
COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
VOLUME [ \
|
||||
"/home/frappe/frappe-bench/sites", \
|
||||
"/home/frappe/frappe-bench/sites/assets", \
|
||||
"/home/frappe/frappe-bench/logs" \
|
||||
]
|
||||
|
||||
CMD [ \
|
||||
"/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--chdir=/home/frappe/frappe-bench/sites", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
@ -1,34 +0,0 @@
|
||||
FROM alpine/git as builder
|
||||
|
||||
ARG FRAPPE_VERSION
|
||||
ARG FRAPPE_REPO=https://github.com/frappe/frappe
|
||||
RUN apk add -U jq
|
||||
RUN git clone --depth 1 -b ${FRAPPE_VERSION} ${FRAPPE_REPO} /opt/frappe
|
||||
RUN jq --argjson dependencies "$(jq '.dependencies | INDEX( "express", "redis", "socket.io", "superagent" ) as $keep | \
|
||||
del( \
|
||||
. | objects | \
|
||||
.[ \
|
||||
keys_unsorted[] | \
|
||||
select( $keep[ . ] | not ) \
|
||||
] \
|
||||
)' /opt/frappe/package.json)" '.dependencies = $dependencies | del(.scripts.prepare)' /opt/frappe/package.json > /opt/frappe/dependencies.json && \
|
||||
mv /opt/frappe/dependencies.json /opt/frappe/package.json
|
||||
|
||||
# NodeJS LTS
|
||||
FROM node:18-alpine
|
||||
|
||||
RUN addgroup -S frappe \
|
||||
&& adduser -S frappe -G frappe
|
||||
USER frappe
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
RUN mkdir -p sites apps/frappe
|
||||
|
||||
COPY --chown=frappe:frappe --from=builder /opt/frappe/package.json /opt/frappe/socketio.js /opt/frappe/node_utils.js apps/frappe/
|
||||
|
||||
RUN cd apps/frappe \
|
||||
&& npm install --omit=dev
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench/sites
|
||||
|
||||
CMD [ "node", "/home/frappe/frappe-bench/apps/frappe/socketio.js" ]
|
@ -1,146 +0,0 @@
|
||||
# syntax=docker/dockerfile:1.3
|
||||
|
||||
ARG PYTHON_VERSION
|
||||
FROM python:${PYTHON_VERSION}-slim-bullseye as base
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# Postgres
|
||||
libpq-dev \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN useradd -ms /bin/bash frappe
|
||||
USER frappe
|
||||
RUN mkdir -p /home/frappe/frappe-bench/apps /home/frappe/frappe-bench/logs /home/frappe/frappe-bench/sites /home/frappe/frappe-bench/config
|
||||
WORKDIR /home/frappe/frappe-bench
|
||||
|
||||
USER root
|
||||
RUN pip install --no-cache-dir -U pip wheel \
|
||||
&& python -m venv env \
|
||||
&& env/bin/pip install --no-cache-dir -U pip wheel
|
||||
|
||||
COPY install-app.sh /usr/local/bin/install-app
|
||||
|
||||
|
||||
FROM base as build_deps
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# Install git here because it is not required in production
|
||||
git \
|
||||
# gcc and g++ are required for building different packages across different versions
|
||||
# of Frappe and ERPNext and also on different platforms (for example, linux/arm64).
|
||||
# It is safe to install build deps even if they are not required
|
||||
# because they won't be included in final images.
|
||||
gcc \
|
||||
g++ \
|
||||
# Make is required to build wheels of ERPNext deps in develop branch for linux/arm64
|
||||
make \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
FROM build_deps as frappe_builder
|
||||
|
||||
ARG FRAPPE_VERSION
|
||||
ARG FRAPPE_REPO=https://github.com/frappe/frappe
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
git clone --depth 1 -b ${FRAPPE_VERSION} ${FRAPPE_REPO} apps/frappe \
|
||||
&& install-app frappe \
|
||||
&& env/bin/pip install -U gevent \
|
||||
# Link Frappe's node_modules/ to make Website Theme work
|
||||
&& mkdir -p /home/frappe/frappe-bench/sites/assets/frappe/node_modules \
|
||||
&& ln -s /home/frappe/frappe-bench/sites/assets/frappe/node_modules /home/frappe/frappe-bench/apps/frappe/node_modules
|
||||
|
||||
|
||||
FROM frappe_builder as erpnext_builder
|
||||
|
||||
ARG PAYMENTS_VERSION=develop
|
||||
ARG PAYMENTS_REPO=https://github.com/frappe/payments
|
||||
ARG ERPNEXT_VERSION
|
||||
ARG ERPNEXT_REPO=https://github.com/frappe/erpnext
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
if [ -z "${ERPNEXT_VERSION##*v14*}" ] || [ "$ERPNEXT_VERSION" = "develop" ]; then \
|
||||
git clone --depth 1 -b ${PAYMENTS_VERSION} ${PAYMENTS_REPO} apps/payments && install-app payments; \
|
||||
fi \
|
||||
&& git clone --depth 1 -b ${ERPNEXT_VERSION} ${ERPNEXT_REPO} apps/erpnext \
|
||||
&& install-app erpnext
|
||||
|
||||
FROM base as configured_base
|
||||
|
||||
ARG WKHTMLTOPDF_VERSION=0.12.6-1
|
||||
ARG NODE_VERSION
|
||||
ENV NVM_DIR=/home/frappe/.nvm
|
||||
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}
|
||||
RUN apt-get update \
|
||||
# Setup Node lists
|
||||
&& apt-get install --no-install-recommends -y curl git \
|
||||
# NodeJS with NVM
|
||||
&& mkdir -p ${NVM_DIR} \
|
||||
&& curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash \
|
||||
&& . ${NVM_DIR}/nvm.sh \
|
||||
&& nvm install ${NODE_VERSION} \
|
||||
&& nvm use v${NODE_VERSION} \
|
||||
&& npm install -g yarn \
|
||||
&& nvm alias default v${NODE_VERSION} \
|
||||
&& rm -rf ${NVM_DIR}/.cache \
|
||||
&& echo 'export NVM_DIR="/home/frappe/.nvm"' >>~/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm' >> ~/.bashrc \
|
||||
&& echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion' >> ~/.bashrc \
|
||||
# Install wkhtmltopdf with patched qt
|
||||
&& if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
|
||||
&& if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
|
||||
&& downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
|
||||
&& curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
|
||||
&& apt-get install -y ./$downloaded_file \
|
||||
&& rm $downloaded_file \
|
||||
# Cleanup
|
||||
&& apt-get purge -y --auto-remove curl \
|
||||
&& apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
# MariaDB
|
||||
mariadb-client \
|
||||
# Postgres
|
||||
postgresql-client \
|
||||
# For healthcheck
|
||||
wait-for-it \
|
||||
jq \
|
||||
# Clean up
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY pretend-bench.sh /usr/local/bin/bench
|
||||
COPY push_backup.py /usr/local/bin/push-backup
|
||||
COPY configure.py patched_bench_helper.py /usr/local/bin/
|
||||
COPY gevent_patch.py /opt/patches/
|
||||
|
||||
WORKDIR /home/frappe/frappe-bench/sites
|
||||
|
||||
CMD [ "/home/frappe/frappe-bench/env/bin/gunicorn", \
|
||||
"--bind=0.0.0.0:8000", \
|
||||
"--threads=4", \
|
||||
"--workers=2", \
|
||||
"--worker-class=gthread", \
|
||||
"--worker-tmp-dir=/dev/shm", \
|
||||
"--timeout=120", \
|
||||
"--preload", \
|
||||
"frappe.app:application" \
|
||||
]
|
||||
|
||||
|
||||
FROM configured_base as frappe
|
||||
|
||||
COPY --from=frappe_builder /home/frappe/frappe-bench/apps/frappe /home/frappe/frappe-bench/apps/frappe
|
||||
COPY --from=frappe_builder /home/frappe/frappe-bench/env /home/frappe/frappe-bench/env
|
||||
COPY --from=frappe_builder /home/frappe/frappe-bench/sites/apps.txt /home/frappe/frappe-bench/sites/
|
||||
|
||||
USER frappe
|
||||
|
||||
|
||||
# Split frappe and erpnext to reduce image size (because of frappe-bench/env/ directory)
|
||||
FROM configured_base as erpnext
|
||||
|
||||
COPY --from=erpnext_builder --chown=frappe:frappe /home/frappe/frappe-bench/apps /home/frappe/frappe-bench/apps
|
||||
COPY --from=erpnext_builder --chown=frappe:frappe /home/frappe/frappe-bench/env /home/frappe/frappe-bench/env
|
||||
COPY --from=erpnext_builder --chown=frappe:frappe /home/frappe/frappe-bench/sites/apps.txt /home/frappe/frappe-bench/sites/
|
||||
|
||||
|
||||
USER frappe
|
@ -1,56 +0,0 @@
|
||||
#!/usr/local/bin/python
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any, TypeVar
|
||||
|
||||
|
||||
def update_config(**values: Any):
|
||||
fname = "common_site_config.json"
|
||||
if not os.path.exists(fname):
|
||||
with open(fname, "a") as f:
|
||||
json.dump({}, f)
|
||||
|
||||
with open(fname, "r+") as f:
|
||||
config: dict[str, Any] = json.load(f)
|
||||
config.update(values)
|
||||
f.seek(0)
|
||||
f.truncate()
|
||||
json.dump(config, f)
|
||||
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
|
||||
def env(name: str, type_: type[_T] = str) -> _T:
|
||||
value = os.getenv(name)
|
||||
if not value:
|
||||
raise RuntimeError(f'Required environment variable "{name}" not set')
|
||||
try:
|
||||
value = type_(value)
|
||||
except Exception:
|
||||
raise RuntimeError(
|
||||
f'Cannot convert environment variable "{name}" to type "{type_}"'
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def generate_redis_url(url: str):
|
||||
return f"redis://{url}"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
update_config(
|
||||
db_host=env("DB_HOST"),
|
||||
db_port=env("DB_PORT", int),
|
||||
redis_cache=generate_redis_url(env("REDIS_CACHE")),
|
||||
redis_queue=generate_redis_url(env("REDIS_QUEUE")),
|
||||
redis_socketio=generate_redis_url(env("REDIS_SOCKETIO")),
|
||||
socketio_port=env("SOCKETIO_PORT", int),
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
@ -1,3 +0,0 @@
|
||||
import gevent.monkey
|
||||
|
||||
gevent.monkey.patch_all()
|
@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
set -x
|
||||
|
||||
APP=$1
|
||||
|
||||
cd /home/frappe/frappe-bench
|
||||
|
||||
env/bin/pip install -e "apps/$APP"
|
||||
|
||||
echo "$APP" >>sites/apps.txt
|
@ -1,48 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import click
|
||||
import click.exceptions
|
||||
import frappe.app
|
||||
import frappe.database.db_manager
|
||||
import frappe.utils.bench_helper
|
||||
|
||||
|
||||
def patch_database_creator():
|
||||
"""
|
||||
We need to interrupt Frappe site database creation to monkeypatch
|
||||
functions that resolve host for user that owns site database.
|
||||
In frappe_docker this was implemented in "new" command:
|
||||
https://github.com/frappe/frappe_docker/blob/c808ad1767feaf793a2d14541ac0f4d9cbab45b3/build/frappe-worker/commands/new.py#L87
|
||||
"""
|
||||
|
||||
frappe.database.db_manager.DbManager.get_current_host = lambda self: "%"
|
||||
|
||||
|
||||
def patch_click_usage_error():
|
||||
bits: tuple[str, ...] = (
|
||||
click.style(
|
||||
"Only Frappe framework bench commands are available in container setup.",
|
||||
fg="yellow",
|
||||
bold=True,
|
||||
),
|
||||
"https://frappeframework.com/docs/v13/user/en/bench/frappe-commands",
|
||||
)
|
||||
notice = "\n".join(bits)
|
||||
|
||||
def format_message(self: click.exceptions.UsageError):
|
||||
if "No such command" in self.message:
|
||||
return f"{notice}\n\n{self.message}"
|
||||
return self.message
|
||||
|
||||
click.exceptions.UsageError.format_message = format_message
|
||||
|
||||
|
||||
def main() -> int:
|
||||
patch_database_creator()
|
||||
patch_click_usage_error()
|
||||
frappe.utils.bench_helper.main()
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# shellcheck disable=SC2068
|
||||
~/frappe-bench/env/bin/python /usr/local/bin/patched_bench_helper.py frappe $@
|
@ -1,27 +0,0 @@
|
||||
x-erpnext-backend-image: &erpnext_backend_image
|
||||
image: frappe/erpnext-worker:${ERPNEXT_VERSION:?No ERPNext version set}
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets:ro
|
||||
|
||||
services:
|
||||
configurator:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
backend:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
frontend:
|
||||
image: frappe/erpnext-nginx:${ERPNEXT_VERSION}
|
||||
|
||||
queue-short:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
queue-default:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
queue-long:
|
||||
<<: *erpnext_backend_image
|
||||
|
||||
scheduler:
|
||||
<<: *erpnext_backend_image
|
@ -1,16 +1,30 @@
|
||||
services:
|
||||
configurator:
|
||||
environment:
|
||||
REDIS_CACHE: redis:6379/0
|
||||
REDIS_QUEUE: redis:6379/1
|
||||
REDIS_SOCKETIO: redis:6379/2
|
||||
REDIS_CACHE: redis-cache:6379
|
||||
REDIS_QUEUE: redis-queue:6379
|
||||
REDIS_SOCKETIO: redis-socketio:6379
|
||||
depends_on:
|
||||
- redis
|
||||
- redis-cache
|
||||
- redis-queue
|
||||
- redis-socketio
|
||||
|
||||
redis:
|
||||
redis-cache:
|
||||
image: redis:6.2-alpine
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
- redis-cache-data:/data
|
||||
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
volumes:
|
||||
- redis-queue-data:/data
|
||||
|
||||
redis-socketio:
|
||||
image: redis:6.2-alpine
|
||||
volumes:
|
||||
- redis-socketio-data:/data
|
||||
|
||||
volumes:
|
||||
redis-data:
|
||||
redis-cache-data:
|
||||
redis-queue-data:
|
||||
redis-socketio-data:
|
||||
|
74
pwd.yml
74
pwd.yml
@ -2,18 +2,30 @@ version: "3"
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
configurator:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: none
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
command:
|
||||
- configure.py
|
||||
- >
|
||||
bench set-config -g db_host $$DB_HOST;
|
||||
bench set-config -gp db_port $$DB_PORT;
|
||||
bench set-config -g redis_cache "redis://$$REDIS_CACHE";
|
||||
bench set-config -g redis_queue "redis://$$REDIS_QUEUE";
|
||||
bench set-config -g redis_socketio "redis://$$REDIS_SOCKETIO";
|
||||
bench set-config -gp socketio_port $$SOCKETIO_PORT;
|
||||
environment:
|
||||
DB_HOST: db
|
||||
DB_PORT: "3306"
|
||||
@ -23,15 +35,16 @@ services:
|
||||
SOCKETIO_PORT: "9000"
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
create-site:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
condition: none
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
entrypoint:
|
||||
- bash
|
||||
- -c
|
||||
@ -42,19 +55,19 @@ services:
|
||||
wait-for-it -t 120 redis-queue:6379;
|
||||
wait-for-it -t 120 redis-socketio:6379;
|
||||
export start=`date +%s`;
|
||||
until [[ -n `grep -hs ^ common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
until [[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".db_host // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_cache // empty"` ]] && \
|
||||
[[ -n `grep -hs ^ sites/common_site_config.json | jq -r ".redis_queue // empty"` ]];
|
||||
do
|
||||
echo "Waiting for common_site_config.json to be created";
|
||||
echo "Waiting for sites/common_site_config.json to be created";
|
||||
sleep 5;
|
||||
if (( `date +%s`-start > 120 )); then
|
||||
echo "could not find common_site_config.json with required keys";
|
||||
echo "could not find sites/common_site_config.json with required keys";
|
||||
exit 1
|
||||
fi
|
||||
done;
|
||||
echo "common_site_config.json found";
|
||||
bench new-site frontend --admin-password=admin --db-root-password=admin --install-app payments --install-app erpnext --set-default;
|
||||
echo "sites/common_site_config.json found";
|
||||
bench new-site frontend --no-mariadb-socket --admin-password=admin --db-root-password=admin --install-app payments --install-app erpnext --set-default;
|
||||
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
@ -76,10 +89,12 @@ services:
|
||||
- db-data:/var/lib/mysql
|
||||
|
||||
frontend:
|
||||
image: frappe/erpnext-nginx:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- nginx-entrypoint.sh
|
||||
environment:
|
||||
BACKEND: backend:8000
|
||||
FRAPPE_SITE_NAME_HEADER: frontend
|
||||
@ -90,13 +105,13 @@ services:
|
||||
PROXY_READ_TIMOUT: 120
|
||||
CLIENT_MAX_BODY_SIZE: 50m
|
||||
volumes:
|
||||
- sites:/usr/share/nginx/html/sites
|
||||
- assets:/usr/share/nginx/html/assets
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
ports:
|
||||
- "8080:8080"
|
||||
|
||||
queue-default:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@ -107,10 +122,10 @@ services:
|
||||
- default
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
queue-long:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@ -121,10 +136,10 @@ services:
|
||||
- long
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
queue-short:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@ -135,7 +150,7 @@ services:
|
||||
- short
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
redis-queue:
|
||||
image: redis:6.2-alpine
|
||||
@ -162,7 +177,7 @@ services:
|
||||
- redis-socketio-data:/data
|
||||
|
||||
scheduler:
|
||||
image: frappe/erpnext-worker:v14.12.1
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
@ -171,21 +186,24 @@ services:
|
||||
- schedule
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
websocket:
|
||||
image: frappe/frappe-socketio:v14.22.0
|
||||
image: frappe/erpnext:v14.12.1
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
command:
|
||||
- node
|
||||
- /home/frappe/frappe-bench/apps/frappe/socketio.js
|
||||
volumes:
|
||||
- sites:/home/frappe/frappe-bench/sites
|
||||
- assets:/home/frappe/frappe-bench/sites/assets
|
||||
- logs:/home/frappe/frappe-bench/logs
|
||||
|
||||
volumes:
|
||||
assets:
|
||||
db-data:
|
||||
redis-queue-data:
|
||||
redis-cache-data:
|
||||
redis-socketio-data:
|
||||
sites:
|
||||
logs:
|
||||
|
@ -1,3 +0,0 @@
|
||||
frappe @ git+https://github.com/frappe/frappe.git
|
||||
boto3-stubs[s3]
|
||||
black==22.12.0
|
52
resources/nginx-entrypoint.sh
Executable file
52
resources/nginx-entrypoint.sh
Executable file
@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set variables that do not exist
|
||||
if [[ -z "$BACKEND" ]]; then
|
||||
echo "BACKEND defaulting to 0.0.0.0:8000"
|
||||
export BACKEND=0.0.0.0:8000
|
||||
fi
|
||||
if [[ -z "$SOCKETIO" ]]; then
|
||||
echo "SOCKETIO defaulting to 0.0.0.0:9000"
|
||||
export SOCKETIO=0.0.0.0:9000
|
||||
fi
|
||||
if [[ -z "$UPSTREAM_REAL_IP_ADDRESS" ]]; then
|
||||
echo "UPSTREAM_REAL_IP_ADDRESS defaulting to 127.0.0.1"
|
||||
export UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
|
||||
fi
|
||||
if [[ -z "$UPSTREAM_REAL_IP_HEADER" ]]; then
|
||||
echo "UPSTREAM_REAL_IP_HEADER defaulting to X-Forwarded-For"
|
||||
export UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
|
||||
fi
|
||||
if [[ -z "$UPSTREAM_REAL_IP_RECURSIVE" ]]; then
|
||||
echo "UPSTREAM_REAL_IP_RECURSIVE defaulting to off"
|
||||
export UPSTREAM_REAL_IP_RECURSIVE=off
|
||||
fi
|
||||
if [[ -z "$FRAPPE_SITE_NAME_HEADER" ]]; then
|
||||
# shellcheck disable=SC2016
|
||||
echo 'FRAPPE_SITE_NAME_HEADER defaulting to $host'
|
||||
# shellcheck disable=SC2016
|
||||
export FRAPPE_SITE_NAME_HEADER='$host'
|
||||
fi
|
||||
|
||||
if [[ -z "$PROXY_READ_TIMEOUT" ]]; then
|
||||
echo "PROXY_READ_TIMEOUT defaulting to 120"
|
||||
export PROXY_READ_TIMEOUT=120
|
||||
fi
|
||||
|
||||
if [[ -z "$CLIENT_MAX_BODY_SIZE" ]]; then
|
||||
echo "CLIENT_MAX_BODY_SIZE defaulting to 50m"
|
||||
export CLIENT_MAX_BODY_SIZE=50m
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
envsubst '${BACKEND}
|
||||
${SOCKETIO}
|
||||
${UPSTREAM_REAL_IP_ADDRESS}
|
||||
${UPSTREAM_REAL_IP_HEADER}
|
||||
${UPSTREAM_REAL_IP_RECURSIVE}
|
||||
${FRAPPE_SITE_NAME_HEADER}
|
||||
${PROXY_READ_TIMEOUT}
|
||||
${CLIENT_MAX_BODY_SIZE}' \
|
||||
</templates/nginx/frappe.conf.template >/etc/nginx/conf.d/frappe.conf
|
||||
|
||||
nginx -g 'daemon off;'
|
@ -14,8 +14,8 @@ map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
server_name $http_host;
|
||||
root /usr/share/nginx/html;
|
||||
server_name ${FRAPPE_SITE_NAME_HEADER};
|
||||
root /home/frappe/frappe-bench/sites;
|
||||
|
||||
proxy_buffer_size 128k;
|
||||
proxy_buffers 4 256k;
|
||||
@ -37,7 +37,7 @@ server {
|
||||
|
||||
location ~ ^/protected/(.*) {
|
||||
internal;
|
||||
try_files /sites/$http_host/$1 =404;
|
||||
try_files /${FRAPPE_SITE_NAME_HEADER}/$1 =404;
|
||||
}
|
||||
|
||||
location /socket.io {
|
||||
@ -47,23 +47,23 @@ server {
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header X-Frappe-Site-Name ${FRAPPE_SITE_NAME_HEADER};
|
||||
proxy_set_header Origin $scheme://$http_host;
|
||||
proxy_set_header Origin $scheme://${FRAPPE_SITE_NAME_HEADER};
|
||||
proxy_set_header Host $host;
|
||||
|
||||
proxy_pass http://socketio-server;
|
||||
}
|
||||
|
||||
location / {
|
||||
rewrite ^(.+)/$ $proxy_x_forwarded_proto://$http_host$1 permanent;
|
||||
rewrite ^(.+)/index\.html$ $proxy_x_forwarded_proto://$http_host$1 permanent;
|
||||
rewrite ^(.+)\.html$ $proxy_x_forwarded_proto://$http_host$1 permanent;
|
||||
rewrite ^(.+)/$ $proxy_x_forwarded_proto://${FRAPPE_SITE_NAME_HEADER}$1 permanent;
|
||||
rewrite ^(.+)/index\.html$ $proxy_x_forwarded_proto://${FRAPPE_SITE_NAME_HEADER}$1 permanent;
|
||||
rewrite ^(.+)\.html$ $proxy_x_forwarded_proto://${FRAPPE_SITE_NAME_HEADER}$1 permanent;
|
||||
|
||||
location ~ ^/files/.*.(htm|html|svg|xml) {
|
||||
add_header Content-disposition "attachment";
|
||||
try_files /sites/${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
try_files /${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
}
|
||||
|
||||
try_files /sites/${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
try_files /${FRAPPE_SITE_NAME_HEADER}/public/$uri @webserver;
|
||||
}
|
||||
|
||||
location @webserver {
|
||||
@ -72,18 +72,12 @@ server {
|
||||
proxy_set_header X-Frappe-Site-Name ${FRAPPE_SITE_NAME_HEADER};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Use-X-Accel-Redirect True;
|
||||
proxy_read_timeout ${PROXY_READ_TIMOUT};
|
||||
proxy_read_timeout ${PROXY_READ_TIMEOUT};
|
||||
proxy_redirect off;
|
||||
|
||||
proxy_pass http://backend-server;
|
||||
}
|
||||
|
||||
# error pages
|
||||
error_page 502 /502.html;
|
||||
location /502.html {
|
||||
internal;
|
||||
}
|
||||
|
||||
# optimizations
|
||||
sendfile on;
|
||||
keepalive_timeout 15;
|
@ -109,6 +109,7 @@ def parse_args(args: list[str]) -> Arguments:
|
||||
|
||||
|
||||
def main(args: list[str]) -> int:
|
||||
os.chdir("sites")
|
||||
push_backup(parse_args(args))
|
||||
return 0
|
||||
|
@ -1,24 +0,0 @@
|
||||
services:
|
||||
configurator:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
backend:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
frontend:
|
||||
image: localhost:5000/frappe/erpnext-nginx:${ERPNEXT_VERSION}
|
||||
|
||||
websocket:
|
||||
image: localhost:5000/frappe/frappe-socketio:${FRAPPE_VERSION}
|
||||
|
||||
queue-short:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
queue-default:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
queue-long:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
||||
|
||||
scheduler:
|
||||
image: localhost:5000/frappe/erpnext-worker:${ERPNEXT_VERSION}
|
@ -1,24 +1,24 @@
|
||||
services:
|
||||
configurator:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
backend:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
frontend:
|
||||
image: localhost:5000/frappe/frappe-nginx:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
websocket:
|
||||
image: localhost:5000/frappe/frappe-socketio:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
queue-short:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
queue-default:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
queue-long:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
||||
scheduler:
|
||||
image: localhost:5000/frappe/frappe-worker:${FRAPPE_VERSION}
|
||||
image: localhost:5000/frappe/erpnext:${ERPNEXT_VERSION}
|
||||
|
@ -55,11 +55,12 @@ def frappe_site(compose: Compose):
|
||||
site_name = "tests"
|
||||
compose.bench(
|
||||
"new-site",
|
||||
site_name,
|
||||
"--no-mariadb-socket",
|
||||
"--mariadb-root-password",
|
||||
"123",
|
||||
"--admin-password",
|
||||
"admin",
|
||||
site_name,
|
||||
)
|
||||
compose("restart", "backend")
|
||||
yield site_name
|
||||
@ -68,11 +69,7 @@ def frappe_site(compose: Compose):
|
||||
@pytest.fixture(scope="class")
|
||||
def erpnext_setup(compose: Compose):
|
||||
compose.stop()
|
||||
|
||||
args = ["-f", "overrides/compose.erpnext.yaml"]
|
||||
if CI:
|
||||
args += ("-f", "tests/compose.ci-erpnext.yaml")
|
||||
compose(*args, "up", "-d", "--quiet-pull")
|
||||
compose("up", "-d", "--quiet-pull")
|
||||
|
||||
yield
|
||||
compose.stop()
|
||||
@ -83,13 +80,14 @@ def erpnext_site(compose: Compose):
|
||||
site_name = "test_erpnext_site"
|
||||
args = [
|
||||
"new-site",
|
||||
site_name,
|
||||
"--no-mariadb-socket",
|
||||
"--mariadb-root-password",
|
||||
"123",
|
||||
"--admin-password",
|
||||
"admin",
|
||||
"--install-app",
|
||||
"erpnext",
|
||||
site_name,
|
||||
]
|
||||
erpnext_version = os.environ.get("ERPNEXT_VERSION")
|
||||
if erpnext_version in [
|
||||
|
@ -89,7 +89,13 @@ def test_frappe_connections_in_backends(
|
||||
):
|
||||
filename = "_ping_frappe_connections.py"
|
||||
compose("cp", f"tests/{filename}", f"{service}:/tmp/")
|
||||
compose.exec(service, python_path, f"/tmp/{filename}")
|
||||
compose.exec(
|
||||
"-w",
|
||||
"/home/frappe/frappe-bench/sites",
|
||||
service,
|
||||
python_path,
|
||||
f"/tmp/{filename}",
|
||||
)
|
||||
|
||||
|
||||
def test_push_backup(
|
||||
|
Loading…
Reference in New Issue
Block a user