mirror of
https://github.com/frappe/frappe_docker.git
synced 2024-12-25 11:20:02 +00:00
480 lines
12 KiB
Markdown
480 lines
12 KiB
Markdown
|
### Prerequisites
|
||
|
|
||
|
IMPORTANT: All commands are executed on live server with public IP and DNS Configured.
|
||
|
|
||
|
#### Setup docker swarm
|
||
|
|
||
|
Set hostname
|
||
|
|
||
|
```shell
|
||
|
export USE_HOSTNAME=dog.example.com
|
||
|
|
||
|
echo $USE_HOSTNAME > /etc/hostname
|
||
|
hostname -F /etc/hostname
|
||
|
```
|
||
|
|
||
|
Update packages using tools provided by installed linux distribution.
|
||
|
|
||
|
Example on Ubuntu. Use distro specific commands.
|
||
|
|
||
|
```shell
|
||
|
apt-get update -y && apt-get upgrade
|
||
|
```
|
||
|
|
||
|
Install Docker using official convenience script
|
||
|
|
||
|
```shell
|
||
|
# Download Docker
|
||
|
curl -fsSL get.docker.com -o get-docker.sh
|
||
|
# Install Docker using the stable channel (instead of the default "edge")
|
||
|
CHANNEL=stable sh get-docker.sh
|
||
|
# Remove Docker install script
|
||
|
rm get-docker.sh
|
||
|
```
|
||
|
|
||
|
Setup Swarm Mode
|
||
|
|
||
|
```shell
|
||
|
docker swarm init --advertise-addr 111.111.111.111
|
||
|
```
|
||
|
|
||
|
Note: Select the public IP of the server instead of 111.111.111.111
|
||
|
|
||
|
Add worker nodes. Execute following command from worker node.
|
||
|
|
||
|
```shell
|
||
|
docker swarm join --token SWMTKN-1-5tl7ya98erd9qtasdfml4lqbosbhfqv3asdf4p13-dzw6ugasdfk0arn0 111.111.111.111:2377
|
||
|
```
|
||
|
|
||
|
Note: Replace appropriate token and Public IP of manager in the command.
|
||
|
|
||
|
#### Install Traefik on manager node
|
||
|
|
||
|
Set environment variables
|
||
|
|
||
|
- `EMAIL=user@domain.com`: Letsencrypt Email
|
||
|
- `DOMAIN`: Domain for traefik dashboard, e.g. traefik.example.com
|
||
|
- `HASHED_PASSWORD=$(openssl passwd -apr1 $PASSWORD)` where `PASSWORD` is secret string
|
||
|
|
||
|
deploy the following yaml.
|
||
|
|
||
|
```shell
|
||
|
docker stack deploy -c traefik.yaml traefik
|
||
|
```
|
||
|
|
||
|
```yaml
|
||
|
version: "3.3"
|
||
|
|
||
|
services:
|
||
|
traefik:
|
||
|
image: traefik:v2.2
|
||
|
ports:
|
||
|
- target: 80
|
||
|
published: 80
|
||
|
mode: host
|
||
|
- target: 443
|
||
|
published: 443
|
||
|
mode: host
|
||
|
command:
|
||
|
- --api
|
||
|
- --log.level=INFO
|
||
|
- --accesslog=true
|
||
|
- --metrics.prometheus=true
|
||
|
- --providers.docker=true
|
||
|
- --providers.docker.endpoint=unix:///var/run/docker.sock
|
||
|
- --providers.docker.swarmMode=true
|
||
|
- --providers.docker.exposedbydefault=false
|
||
|
- --providers.docker.network=traefik-public
|
||
|
- --entrypoints.http.address=:80
|
||
|
- --entrypoints.https.address=:443
|
||
|
- --certificatesResolvers.certbot=true
|
||
|
- --certificatesResolvers.certbot.acme.httpChallenge=true
|
||
|
- --certificatesResolvers.certbot.acme.httpChallenge.entrypoint=http
|
||
|
- --certificatesResolvers.certbot.acme.email=${EMAIL?Variable EMAIL not set}
|
||
|
- --certificatesResolvers.certbot.acme.storage=/certs/acme-v2.json
|
||
|
volumes:
|
||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||
|
- /data/traefik/certs:/certs
|
||
|
networks:
|
||
|
- traefik-public
|
||
|
deploy:
|
||
|
mode: replicated
|
||
|
replicas: 1
|
||
|
placement:
|
||
|
constraints:
|
||
|
- node.role == manager
|
||
|
update_config:
|
||
|
parallelism: 1
|
||
|
delay: 10s
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
labels:
|
||
|
# v2.2
|
||
|
- "traefik.docker.network=traefik-public"
|
||
|
- "traefik.enable=true"
|
||
|
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
|
||
|
# Http
|
||
|
- "traefik.http.routers.traefik.rule=Host(`${DOMAIN?Variable DOMAIN not set}`)"
|
||
|
- "traefik.http.routers.traefik.entrypoints=http,https"
|
||
|
# Enable Let's encrypt auto certificat creation
|
||
|
- "traefik.http.routers.traefik.tls.certresolver=certbot"
|
||
|
# Enable authentification
|
||
|
- "traefik.http.routers.traefik.middlewares=traefik-auth"
|
||
|
- "traefik.http.middlewares.traefik-auth.basicauth.users=admin:${HASHED_PASSWORD?Variable HASHED_PASSWORD not set}"
|
||
|
# Redirect All hosts to HTTPS
|
||
|
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
|
||
|
- "traefik.http.routers.http-catchall.entrypoints=http"
|
||
|
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
|
||
|
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
||
|
- "traefik.http.routers.traefik.service=api@internal"
|
||
|
- "traefik.http.routers.traefik.tls"
|
||
|
|
||
|
networks:
|
||
|
traefik-public:
|
||
|
name: traefik-public
|
||
|
attachable: true
|
||
|
driver: overlay
|
||
|
```
|
||
|
|
||
|
#### Install Portainer
|
||
|
|
||
|
Set environment variable `DOMAIN` as domain where portainer is located e.g. `DOMAIN=portainer.example.com`
|
||
|
|
||
|
deploy the following yaml.
|
||
|
|
||
|
```shell
|
||
|
docker stack deploy -c portainer.yaml portainer
|
||
|
```
|
||
|
|
||
|
```yaml
|
||
|
version: "3.3"
|
||
|
|
||
|
services:
|
||
|
agent:
|
||
|
image: portainer/agent:1.5.1
|
||
|
environment:
|
||
|
AGENT_CLUSTER_ADDR: tasks.agent
|
||
|
volumes:
|
||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||
|
- /var/lib/docker/volumes:/var/lib/docker/volumes
|
||
|
networks:
|
||
|
- agent-network
|
||
|
deploy:
|
||
|
mode: global
|
||
|
placement:
|
||
|
constraints:
|
||
|
- node.platform.os == linux
|
||
|
|
||
|
portainer:
|
||
|
image: portainer/portainer:1.23.2
|
||
|
command: -H tcp://tasks.agent:9001 --tlsskipverify
|
||
|
volumes:
|
||
|
- portainer-data:/data
|
||
|
networks:
|
||
|
- agent-network
|
||
|
- traefik-public
|
||
|
deploy:
|
||
|
placement:
|
||
|
constraints:
|
||
|
- node.role == manager
|
||
|
- node.labels.portainer.portainer-data == true
|
||
|
labels:
|
||
|
- "traefik.docker.network=traefik-public"
|
||
|
- "traefik.enable=true"
|
||
|
- "traefik.http.services.portainer.loadbalancer.server.port=9000"
|
||
|
# Http
|
||
|
- "traefik.http.routers.portainer.rule=Host(`${DOMAIN?Variable DOMAIN not set}`)"
|
||
|
- "traefik.http.routers.portainer.entrypoints=http,https"
|
||
|
# Enable Let's encrypt auto certificate creation
|
||
|
- "traefik.http.routers.portainer.tls.certresolver=certbot"
|
||
|
networks:
|
||
|
agent-network:
|
||
|
attachable: true
|
||
|
traefik-public:
|
||
|
external: true
|
||
|
|
||
|
volumes:
|
||
|
portainer-data:
|
||
|
```
|
||
|
|
||
|
Use Portainer for rest of the guide
|
||
|
|
||
|
### Create Config
|
||
|
|
||
|
Configs > Add Config > `frappe-mariadb-config`
|
||
|
|
||
|
```
|
||
|
[mysqld]
|
||
|
character-set-client-handshake = FALSE
|
||
|
character-set-server = utf8mb4
|
||
|
collation-server = utf8mb4_unicode_ci
|
||
|
|
||
|
[mysql]
|
||
|
default-character-set = utf8mb4
|
||
|
```
|
||
|
|
||
|
### Create Secret
|
||
|
|
||
|
Secret > Add Secret > `frappe-mariadb-root-password`
|
||
|
|
||
|
```
|
||
|
longsecretpassword
|
||
|
```
|
||
|
|
||
|
Note down this password.
|
||
|
It is only available in mariadb containers at location `/run/secrets/frappe-mariadb-root-password` later
|
||
|
|
||
|
### Deploy MariaDB Replication
|
||
|
|
||
|
Stacks > Add Stacks > `frappe-mariadb`
|
||
|
|
||
|
```yaml
|
||
|
version: "3.7"
|
||
|
|
||
|
services:
|
||
|
mariadb-master:
|
||
|
image: 'bitnami/mariadb:10.3'
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
configs:
|
||
|
- source: frappe-mariadb-config
|
||
|
target: /opt/bitnami/mariadb/conf/bitnami/my_custom.cnf
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
secrets:
|
||
|
- frappe-mariadb-root-password
|
||
|
volumes:
|
||
|
- 'mariadb_master_data:/bitnami/mariadb'
|
||
|
environment:
|
||
|
- MARIADB_REPLICATION_MODE=master
|
||
|
- MARIADB_REPLICATION_USER=repl_user
|
||
|
- MARIADB_REPLICATION_PASSWORD_FILE=/run/secrets/frappe-mariadb-root-password
|
||
|
- MARIADB_ROOT_PASSWORD_FILE=/run/secrets/frappe-mariadb-root-password
|
||
|
|
||
|
mariadb-slave:
|
||
|
image: 'bitnami/mariadb:10.3'
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
configs:
|
||
|
- source: frappe-mariadb-config
|
||
|
target: /opt/bitnami/mariadb/conf/bitnami/my_custom.cnf
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
secrets:
|
||
|
- frappe-mariadb-root-password
|
||
|
volumes:
|
||
|
- 'mariadb_slave_data:/bitnami/mariadb'
|
||
|
environment:
|
||
|
- MARIADB_REPLICATION_MODE=slave
|
||
|
- MARIADB_REPLICATION_USER=repl_user
|
||
|
- MARIADB_REPLICATION_PASSWORD_FILE=/run/secrets/frappe-mariadb-root-password
|
||
|
- MARIADB_MASTER_HOST=mariadb-master
|
||
|
- MARIADB_MASTER_PORT_NUMBER=3306
|
||
|
- MARIADB_MASTER_ROOT_PASSWORD_FILE=/run/secrets/frappe-mariadb-root-password
|
||
|
|
||
|
volumes:
|
||
|
mariadb_master_data:
|
||
|
mariadb_slave_data:
|
||
|
|
||
|
configs:
|
||
|
frappe-mariadb-config:
|
||
|
external: true
|
||
|
|
||
|
secrets:
|
||
|
frappe-mariadb-root-password:
|
||
|
external: true
|
||
|
|
||
|
networks:
|
||
|
frappe-network:
|
||
|
name: frappe-network
|
||
|
attachable: true
|
||
|
```
|
||
|
|
||
|
### Deploy Frappe/ERPNext
|
||
|
|
||
|
Stacks > Add Stacks > `frappe-bench-v12`
|
||
|
|
||
|
```yaml
|
||
|
version: "3"
|
||
|
|
||
|
services:
|
||
|
redis-cache:
|
||
|
image: redis:latest
|
||
|
volumes:
|
||
|
- redis-cache-vol:/data
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
redis-queue:
|
||
|
image: redis:latest
|
||
|
volumes:
|
||
|
- redis-queue-vol:/data
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
redis-socketio:
|
||
|
image: redis:latest
|
||
|
volumes:
|
||
|
- redis-socketio-vol:/data
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
erpnext-nginx:
|
||
|
image: frappe/erpnext-nginx:v12.7.1
|
||
|
environment:
|
||
|
- FRAPPE_PY=erpnext-python
|
||
|
- FRAPPE_PY_PORT=8000
|
||
|
- FRAPPE_SOCKETIO=frappe-socketio
|
||
|
- SOCKETIO_PORT=9000
|
||
|
volumes:
|
||
|
- sites-vol:/var/www/html/sites:rw
|
||
|
- assets-vol:/assets:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
- traefik-public
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
labels:
|
||
|
- "traefik.docker.network=traefik-public"
|
||
|
- "traefik.enable=true"
|
||
|
- "traefik.http.services.frappe-bench-v12.loadbalancer.server.port=80"
|
||
|
# Http
|
||
|
- "traefik.http.routers.frappe-bench-v12.rule=Host(${SITES?Variable SITES not set})"
|
||
|
- "traefik.http.routers.frappe-bench-v12.entrypoints=http,https"
|
||
|
# Enable Let's encrypt auto certificate creation
|
||
|
- "traefik.http.routers.frappe-bench-v12.tls.certresolver=certbot"
|
||
|
|
||
|
erpnext-python:
|
||
|
image: frappe/erpnext-worker:v12.7.1
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
environment:
|
||
|
- MARIADB_HOST=${MARIADB_HOST?Variable MARIADB_HOST not set}
|
||
|
- REDIS_CACHE=redis-cache:6379
|
||
|
- REDIS_QUEUE=redis-queue:6379
|
||
|
- REDIS_SOCKETIO=redis-socketio:6379
|
||
|
- SOCKETIO_PORT=9000
|
||
|
- AUTO_MIGRATE=1
|
||
|
volumes:
|
||
|
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||
|
- assets-vol:/home/frappe/frappe-bench/sites/assets:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
frappe-socketio:
|
||
|
image: frappe/frappe-socketio:v12.5.1
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
volumes:
|
||
|
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
frappe-worker-default:
|
||
|
image: frappe/erpnext-worker:v12.7.1
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
command: worker
|
||
|
volumes:
|
||
|
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
frappe-worker-short:
|
||
|
image: frappe/erpnext-worker:v12.7.1
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
command: worker
|
||
|
environment:
|
||
|
- WORKER_TYPE=short
|
||
|
volumes:
|
||
|
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
frappe-worker-long:
|
||
|
image: frappe/erpnext-worker:v12.7.1
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
command: worker
|
||
|
environment:
|
||
|
- WORKER_TYPE=long
|
||
|
volumes:
|
||
|
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
frappe-schedule:
|
||
|
image: frappe/erpnext-worker:v12.7.1
|
||
|
deploy:
|
||
|
restart_policy:
|
||
|
condition: on-failure
|
||
|
command: schedule
|
||
|
volumes:
|
||
|
- sites-vol:/home/frappe/frappe-bench/sites:rw
|
||
|
networks:
|
||
|
- frappe-network
|
||
|
|
||
|
volumes:
|
||
|
redis-cache-vol:
|
||
|
redis-queue-vol:
|
||
|
redis-socketio-vol:
|
||
|
assets-vol:
|
||
|
sites-vol:
|
||
|
|
||
|
networks:
|
||
|
traefik-public:
|
||
|
external: true
|
||
|
frappe-network:
|
||
|
external: true
|
||
|
```
|
||
|
|
||
|
Use environment variables:
|
||
|
|
||
|
- `MARIADB_HOST=frappe-mariadb_mariadb-master`
|
||
|
- `SITES` variable is list of sites in back tick and separated by comma
|
||
|
```
|
||
|
SITES=`site1.example.com`,`site2.example.com`
|
||
|
```
|
||
|
|
||
|
### Create new site job
|
||
|
|
||
|
1. Containers > Add Container > `add-site1-example-com`
|
||
|
2. Select Image frappe/erpnext-worker:v12
|
||
|
3. Set command as `new`
|
||
|
4. Select network `frappe-network`
|
||
|
5. Select Volume `frappe-bench-v12_sites_vol` and mount in container `/home/frappe/frappe-bench/sites`
|
||
|
6. Env variables:
|
||
|
- MYSQL_ROOT_PASSWORD=longsecretpassword
|
||
|
- SITE_NAME=site1.example.com
|
||
|
7. Start container
|
||
|
|
||
|
### Migrate Sites job
|
||
|
|
||
|
1. Containers > Add Container > `migrate-sites`
|
||
|
2. Select Image frappe/erpnext-worker:v12
|
||
|
3. Set command as `migrate`
|
||
|
4. Select network `frappe-network`
|
||
|
5. Select Volume `frappe-bench-v12_sites_vol` and mount in container `/home/frappe/frappe-bench/sites`
|
||
|
6. Env variables:
|
||
|
- MAINTENANCE_MODE=1
|
||
|
7. Start container
|
||
|
|