mkuf / prind

print in docker - Deploy a containerized Klipper Stack for your 3D Printer
GNU General Public License v3.0
324 stars 82 forks source link

Multiple Printers #28

Closed truong0vanchien closed 2 years ago

truong0vanchien commented 2 years ago

Hi Mkuf, I am using Fluidd of your repo. Since It is run by docker so I am going to use two Fluidd docker images connecting two my printers. I managed to run those at 2 different ports (port 80 and port 4040) and different names. Does your repo support it? Since I have no luck on this, if I run one printer, it worked fine but the second docker always got "gateway timeout" when accessing, this is my docker ps:

CONTAINER ID   IMAGE                    COMMAND                  CREATED              STATUS              PORTS                  NAMES
70d6ea3415aa   mkuf/moonraker:nightly   "/opt/venv/bin/pytho…"   39 seconds ago       Up 29 seconds       7125/tcp               moonraker
7a94c3d27ea3   mkuf/klipper:nightly     "/opt/venv/bin/pytho…"   41 seconds ago       Up 32 seconds                              klipper
96924e337fe9   cadriel/fluidd:latest    "/docker-entrypoint.…"   About a minute ago   Up About a minute   80/tcp                 fluidd
bd613ee549d5   traefik:v2.5             "/entrypoint.sh --ac…"   About a minute ago   Up About a minute   0.0.0.0:80->80/tcp     traefik
2803c1001972   mkuf/ustreamer:nightly   "/opt/ustreamer/ustr…"   About a minute ago   Up About a minute   8080/tcp               webcam
0f484b960720   mkuf/moonraker:nightly   "/opt/venv/bin/pytho…"   6 minutes ago        Up 5 minutes        7125/tcp               moonraker2
5c2b161da6a4   mkuf/klipper:nightly     "/opt/venv/bin/pytho…"   6 minutes ago        Up 5 minutes                               klipper2
8965501a7d74   cadriel/fluidd:latest    "/docker-entrypoint.…"   6 minutes ago        Up 5 minutes        80/tcp                 fluidd2
ec11cc5c878b   mkuf/ustreamer:nightly   "/opt/ustreamer/ustr…"   6 minutes ago        Up 6 minutes        8080/tcp               webcam2
24cea8ee0cdd   traefik:v2.5             "/entrypoint.sh --ac…"   6 minutes ago        Up 5 minutes        0.0.0.0:4040->80/tcp   traefik2

Could you give me some advice? Thanks in advance.

mkuf commented 2 years ago

Hi,

there is not much for me to see here. What did you do to start the extra containers? How are they configured?

Have a look at https://github.com/mkuf/prind/issues/22, there I described two ways to achieve this.
Thinking about this again, running the stack multiple times might not work, as container labels will overlap between stacks and traefik will pick up containers from outside the current stack. This might cause the behaviour, you're experiencing. Without knowing how you got to this point, I can't give you any advice on how to proceed.

You're probably better off, writing your own compose file that houses multiple klipper and moonraker services, that are not proxied by traefik. You can still use traefik for proxying webcam services and the frontend of your choice.

-Markus

truong0vanchien commented 2 years ago

Hi Mkuf, I used the second method you mentioned in #22

Alternatively, you could start the whole stack multiple times. Clone prind into a different subdirectory and reconfigure the portbindings for traefik in your docker-compose.override.yaml. You can then access all Services via a different Port (in this case 81). Keep using different Ports for each printer that you'd like to run.

services: traefik: ports:

  • 81:80 This comes with the Downside that supporting Services such as traefik or the web frontends run multiple times. This requires more resources but is probably the easiest way if you do not want to deal with writing compose files yourself.

As I observed, the problem should be from overlapping Klipper and moonraker twice in the same port. Anyway, thanks for supporting. I will try another method to deal with multiple printers. Regards.

mkuf commented 2 years ago

You could just go ahead and use the templating that is already in place for the klipper service to create new instances of klipper.

There is no templating for moonraker in place, but you could easily adapt that and also create multiple moonraker instances from a common template. I'd omit the traefik labels on those moonraker services and add a portbinding for every printer you'd like to run.

Keep in mind, that for every printer you'd like to run, you will also need a unique set of docker-volumes. The config directory might be shared between all services, but every service needs its own config file.

Let me know how this works for you. I'd be happy to add multi printer support for everybody to use. I'm afraid that, usability wise, this is not as straight forward. With multiple instances of the same service, we are reaching the limits of what docker compose can achieve, without being to complicated for novice users.

-Markus

mkuf commented 2 years ago

Thinking about this further, multiple printers as a extension of the current functionality is not possible without breaking things or worsen the user experience.

If anyone can come up with a solution that integrates well with the current docker file without adding too much around the vanilla docker compose configs (e.g. extensive scripting to build templates on the fly), I'm happy to discuss this in a PR.

For the Time being, anyone interested in using the Container Images in their own compose file, this might be a starting point.

## Common Templates
x-klipper-svc: &klipper-svc
  image: mkuf/klipper:latest
  restart: unless-stopped
  volumes:
    - ./config:/opt/cfg
    - run:/opt/run
    - gcode:/opt/gcode
    - log:/opt/log

x-moonraker-svc: &moonraker-svc
  image: mkuf/moonraker:latest
  restart: unless-stopped
  volumes:
    - /dev/null:/opt/klipper/config/null
    - /dev/null:/opt/klipper/docs/null
    - ./config:/opt/cfg
    - run:/opt/run
    - gcode:/opt/gcode
    - log:/opt/log

## Service Definitions
services:

  ## Printer1
  ## Access api via port 8001/tcp
  printer1-klipper:
    <<: *klipper-svc
    command: -I run/printer1-klipper.tty -a run/printer1-klipper.sock cfg/printer1.cfg -l log/printer1-klippy.log
    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0
  printer1-moonraker:
    <<: *moonraker-svc
    command: -c cfg/printer1-moonraker.cfg -l log/printer1-moonraker.log
    ports:
      - 8001:7125

  ## Printer2
  ## Access api via port 8002/tcp
  printer2-klipper:
    <<: *klipper-svc
    command: -I run/printer2-klipper.tty -a run/printer2-klipper.sock cfg/printer2.cfg -l log/printer2-klippy.log
    devices:
      - /dev/ttyUSB1:/dev/ttyUSB1
  printer2-moonraker:
    <<: *moonraker-svc
    command: -c cfg/printer2-moonraker.cfg -l log/printer2-moonraker.log
    ports:
      - 8002:7125

  ## Use Mainsail as Frontend
  mainsail:
    image: ghcr.io/mainsail-crew/mainsail:edge
    restart: unless-stopped
    ports:
      - 80:80

volumes:
  run:
  gcode:
  log:
mkuf commented 1 year ago

The template from https://github.com/mkuf/prind/issues/28#issuecomment-1179405557 has been updated to work with the most resent image versions and added to main in https://github.com/mkuf/prind/commit/ff4ca0222645677ef88b765666e7a54d67d53fc1

rizwansarwar commented 1 year ago

@mkuf Thank for the multiple printer compose file. My use case is a bit different where by my printers are not always online at the same time. I tried running the standard prind compose multiple times from different directories but that has colliding name spaces. I was wondering if it is possible to enhance the multiple printer compose file to restart Klipper/Moonraker containers for one printer while other is online.

Right now, I am doing this by running two printers in 2 VM's and running prind on each VM. That is obviously very wasteful way of doing it. But my compose knowledge is terrible and I am wondering if you help or give me some pointers on what to do to achieve this.

mkuf commented 1 year ago

@rizwansarwar this is probably related to https://github.com/mkuf/prind/issues/51
There is nothing on the docker or compose side that can be done to start/stop services depending on the device plugged in.

You can create udev rules on your host that do the work for you.
The following is untested but would be my approach to automate this.

The script arguments vary based on the action of the device, where the first argument is the same value as the label value for org.prind.printer and the second argument is the docker command that should be executed, in this case restart or stop. This assumes that you use the labeling proposed in the custom example.

ACTION=="add",SUBSYSTEM=="tty",ATTRS{idVendor}=="0000",ATTRS{idProduct}=="0000",RUN+="/opt/printerupdown.sh printer1 restart"
ACTION=="remove",SUBSYSTEM=="tty",ENV{ID_VENDOR_ID}=="0000",ENV{ID_MODEL_ID}=="0000",RUN+="/opt/printerupdown.sh printer1 stop"

ACTION=="add",SUBSYSTEM=="tty",ATTRS{idVendor}=="0001",ATTRS{idProduct}=="0000",RUN+="/opt/printerupdown.sh printer2 restart"
ACTION=="remove",SUBSYSTEM=="tty",ENV{ID_VENDOR_ID}=="0001",ENV{ID_MODEL_ID}=="0000",RUN+="/opt/printerupdown.sh printer2 stop"

See https://unix.stackexchange.com/questions/28548/how-to-run-custom-scripts-upon-usb-device-plug-in for further info on how to set your idVendor and idProduct or other attributes of your device.
You'll probably have to do some digging yourself, as this stackexchange thread suggests that the remove action should use ENV instead of ATTRS which I can not verify. https://stackoverflow.com/questions/31055611/how-do-i-use-udev-to-run-a-shell-script-when-a-usb-device-is-removed

The script that gets called is just a wrapper for docker and starts/stops containers by their label.

#!/bin/bash

printer=${1}
action=${2}

docker ${action} $(docker ps -aqf "label=org.prind.printer=${printer}")

-Markus

rizwansarwar commented 1 year ago

@mkuf wow, so quick response. Actually I already use the uDev rules to attach printers to proper TTY so I could extend it to use above.

I think you have given the answer to what I was really after. I was under the impression that I needed to restart the entire stack, but as you pointed out, I could just restart the relevant printer container. Thank you so much.