Why?
People who don’t know containers land up successfully installing ERPNext with containers and get stuck later. Read for container basics.
Prerequisites
Unix/Linux basics, init system, processes, mount points, bash shell.
podman
or docker
, podman-compose
or docker compose
is installed. Go for rootless podman
if you can. Standard docker
will work. Make sure you’re NOT using snap
to install docker, it’ll not work as expected through snap
.
Install,
Or alternatively install,
This guide explains interacting with containers, it is not a production or development setup guide.
What are containers?
- Not a virtual machines. No dedicated kernel and no VM like isolation on host machine.
- Try
uname -a
in container and outside container. Observe same output. - Start a container and see the process in output of
ps -aux
on host machine.
- Try
- No Dedicated init system like systemd e.g. NO
systemctl enable mariadb
,systemctl restart mariadb
. Management of containers is done by container engine so there is no need of init system in containers. - It is a package (container) with all runtime dependencies included that runs a process on your host machine.
- There is no state maintained in container. Volumes are required for persistence.
Starting containers from ready images
Start a container with the familiar bench.
podman run --rm -it frappe/bench:latest bash
You will enter into a container in interactive mode with pseudo-TTY for container (-it
). The container will be removed when it stops (--rm
). Try bench commands here. You can even do bench init
, you will lose all the data created after container is stopped.
frappe@5a738e029df9:~$ bench init --verbose --skip-redis-config-generation --version version-14 frappe-bench
...
Done in 18.72s.
SUCCESS: Bench frappe-bench initialized
You can see the frappe-bench
directory. It will vanish when container is closed.
Volumes
We need volumes to maintain the data after container is removed. New container can start with the volume resuming from the state in volume.
podman run --name=bench -v "${HOME}"/bench-data:/home/frappe/bench-host --rm -it frappe/bench:latest bash
Now you will see a directory inside container which mounts directory (“${HOME}”/bench-data) from host machine under specified location in container (/home/frappe/bench-host)
You can try to create a bench in that location to keep the data, it’ll fail.
frappe@5a738e029df9:~$ bench init --verbose --skip-redis-config-generation --version version-14 bench-host/frappe-bench
Traceback (most recent call last):
File "/home/frappe/.bench/bench/commands/make.py", line 68, in init
init(
File "/home/frappe/.bench/bench/utils/render.py", line 105, in wrapper_fn
return fn(*args, **kwargs)
File "/home/frappe/.bench/bench/utils/system.py", line 63, in init
bench.setup.dirs()
File "/home/frappe/.bench/bench/utils/render.py", line 126, in wrapper_fn
return fn(*args, **kwargs)
File "/home/frappe/.bench/bench/bench.py", line 337, in dirs
os.makedirs(self.bench.name, exist_ok=True)
File "/home/frappe/.pyenv/versions/3.10.5/lib/python3.10/os.py", line 225, in makedirs
mkdir(name, mode)
PermissionError: [Errno 13] Permission denied: 'bench-host/frappe-bench'
ERROR: There was a problem while creating bench-host/frappe-bench
Do you want to rollback these changes? [y/N]: y
INFO: Rolling back Bench "bench-host/frappe-bench"
you need to set the right permission for directory.
enter the container as root in another terminal and set it.
podman exec --user root -it bench-data bash
root@5a738e029df9:/home/frappe# chown -R 1000:1000 bench-host/
or set it from host machine.
sudo chown -R 1000:1000 "${HOME}"/bench-data
Now try bench init again from previous terminal.
bench init --verbose --skip-redis-config-generation --version version-14 bench-host/frappe-bench
You will have a frappe-bench
at location "${HOME}"/bench-data
on host.
What we just did was we used bind mount to mount a directory from host machine into container. This is fine for development or single vm setup. Better approach will be to use volumes instead of bind mount.
To use volume don’t mention it as a path. e.g.
podman run --name=bench -v bench-data:/home/frappe/bench-host --rm -it frappe/bench:latest bash
Above command will create volume called bench-data
and mount it under specified location in container.
It will work without issuing any additional permission commands.
You can verify with command podman volume ls
.
Volumes can be further extended using drivers, serve them over NFS or cloud provided volumes making them more flexible over bind mounts.
Execute following to know where it is located on host machine.
podman volume inspect bench-data
Now that you have initiated a bench. If you try to start bench from location /home/frappe/bench-host
in container you will face errors due to redis and database not available. We solve this problem in next section.
Exit the bench
container before continuing further.
Environment variables, Networks
Let’s create a common network under which all our containers can connect.
podman network create bench-network
Let’s start 3 redis containers that are require for redis-cache
, redis-queue
and redis-socketio
podman run --name=redis-cache --net=bench-network --rm -d redis:6.2-alpine
podman run --name=redis-queue --net=bench-network --rm -d redis:6.2-alpine
podman run --name=redis-socketio --net=bench-network --rm -d redis:6.2-alpine
We need to start mariadb container with special command params that meet the needs of frappe framework.
We also need to mount mariadb
volume to persist MariaDB state.
podman run \
--name=mariadb \
--net=bench-network \
-e MYSQL_ROOT_PASSWORD=123 \
-v mariadb:/var/lib/mysql \
--rm -d mariadb:10.6 \
--character-set-server=utf8mb4 \
--collation-server=utf8mb4_unicode_ci \
--skip-character-set-client-handshake \
--skip-innodb-read-only-compressed
Params after the image name mariadb:10.6
are passed to mariadb container. -e
sets the required MYSQL_ROOT_PASSWORD
env variable which sets the db root password.
Now start the bench
container again connected to bench-network
and publishing the port 8000 and 9000.
podman run --name=bench --net=bench-network -v bench-data:/home/frappe/bench-host -p 8000:8000 -p 9000:9000 --rm -it frappe/bench:latest bash
Once inside container configure bench to use the started redis and mariadb
frappe@12cf8104d704:~$ cd bench-host/frappe-bench
frappe@12cf8104d704:~/bench-host/frappe-bench$ bench set-config -g redis_cache redis://redis-cache:6379
frappe@12cf8104d704:~/bench-host/frappe-bench$ bench set-config -g redis_queue redis://redis-queue:6379
frappe@12cf8104d704:~/bench-host/frappe-bench$ bench set-config -g redis_socketio redis://redis-socketio:6379
frappe@12cf8104d704:~/bench-host/frappe-bench$ bench set-config -g db_host mariadb
Now you can start the bench.
bench start
You should see services running, check using:
podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
12cf8104d704 docker.io/frappe/bench:latest bash 14 minutes ago Up 14 minutes ago bench
d18a402e2311 docker.io/library/redis:6.2-alpine redis-server 12 minutes ago Up 12 minutes ago redis-cache
2feda9a72654 docker.io/library/redis:6.2-alpine redis-server 12 minutes ago Up 12 minutes ago redis-queue
2f8f637f59e5 docker.io/library/redis:6.2-alpine redis-server 12 minutes ago Up 12 minutes ago redis-socketio
69ab1c7a3c52 docker.io/library/mariadb:10.6 --character-set-s... 4 seconds ago Up 4 seconds ago mariadb
enter into the running bench
container in separate terminal to create new site
podman exec -it bench bash
Now enter the frappe-bench directory and create a site called site1.localhost
frappe@12cf8104d704:~$ cd bench-host/frappe-bench/
frappe@12cf8104d704:~/bench-host/frappe-bench$ bench new-site site1.localhost --no-mariadb-socket --db-root-password=123 --admin-password=admin
you can check http://site1.localhost:8000
in browser now.
Logs and other commands
To check logs run podman logs -f {container-name}
. -f
will follow the logs where you need to CTRL + C
to quit. Example:
podman logs -f bench
List running containers:
podman ps
List all container (including stopped)
podman container ls --all
Stop all containers
podman stop $(podman ps -a -q)
Cleanup everything, delete containers, volumes, networks
podman system prune -f && podman volume prune -f
Check podman --help
for cli help. Check podman or docker docs for in details commands.
Before continuing further you can stop all containers and clean up everything.
Start a set of services
The manual starting of each container and connecting to network is time consuming and prone human errors.
We can use docker compose
or podman-compose
that simplifies the configuration and process of starting multiple containers with shared volumes and network. Check the complete docker-compose reference on docker docs for more details.
Let’s create a compose.yml
that does the above manual steps with one yaml file.
version: "3.7"
services:
mariadb:
image: mariadb:10.6
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --skip-character-set-client-handshake
- --skip-innodb-read-only-compressed
environment:
MYSQL_ROOT_PASSWORD: 123
volumes:
- mariadb-data:/var/lib/mysql
redis-cache:
image: redis:6.2-alpine
redis-queue:
image: redis:6.2-alpine
redis-socketio:
image: redis:6.2-alpine
bench:
image: frappe/bench:latest
command: ["tail", "-f", "/dev/null"]
environment:
- SHELL=/bin/bash
volumes:
- bench-data:/home/frappe/bench-host
ports:
- 8000-8005:8000-8005
- 9000-9005:9000-9005
volumes:
bench-data:
mariadb-data:
now start all service together
podman-compose --project-name=bench up -d
specifying project name with --project-name=bench
will create containers with that prefix or else it will create containers with directory name as prefix.
List the running container
podman ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2897e0156475 docker.io/library/mariadb:10.6 --character-set-s... 30 seconds ago Up 30 seconds ago bench_mariadb_1
ce663bed0a43 docker.io/library/redis:alpine redis-server 22 seconds ago Up 22 seconds ago bench_redis-cache_1
c5399b4f1d45 docker.io/library/redis:alpine redis-server 21 seconds ago Up 21 seconds ago bench_redis-queue_1
cff7336b37c6 docker.io/library/redis:alpine redis-server 20 seconds ago Up 20 seconds ago bench_redis-socketio_1
887ce56c0df3 docker.io/frappe/bench:latest tail -f /dev/null 18 seconds ago Up 18 seconds ago 0.0.0.0:8000-8005->8000-8005/tcp, 0.0.0.0:9000-9005->9000-9005/tcp bench_bench_1
Enter the bench container. It is named bench_bench_1
as per the previous output.
podman exec -it bench_bench_1 bash
once inside container execute the rest of the bench setup commands.
frappe@887ce56c0df3:~$ bench init --verbose --skip-redis-config-generation --version version-14 bench-host/frappe-bench
frappe@887ce56c0df3:~$ cd bench-host/frappe-bench
frappe@887ce56c0df3:~/bench-host/frappe-bench$ bench set-config -g redis_cache redis://redis-cache:6379
frappe@887ce56c0df3:~/bench-host/frappe-bench$ bench set-config -g redis_queue redis://redis-queue:6379
frappe@887ce56c0df3:~/bench-host/frappe-bench$ bench set-config -g redis_socketio redis://redis-socketio:6379
frappe@887ce56c0df3:~/bench-host/frappe-bench$ bench set-config -g db_host mariadb
frappe@887ce56c0df3:~/bench-host/frappe-bench$ bench start
Enter the container from another terminal again to create site.
podman exec -it bench_bench_1 bash
Create site
frappe@887ce56c0df3:~$ cd bench-host/frappe-bench
frappe@887ce56c0df3:~/bench-host/frappe-bench$ bench new-site site1.localhost --no-mariadb-socket --db-root-password=123 --admin-password=admin
Once the site creation is complete you can visit it http://site1.localhost:8000
.
Notice how we can only execute 1 command at a time in container. We can configure that command to start multiple processes, but we can only execute one command.
Next: Container Builds