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

Q: Using with Balena which does not support bind mounts #94

Closed basz closed 1 year ago

basz commented 1 year ago

Hi,

first off. Great stuff!

then; excuse me if this is not the place to ask questions. It is probably not. Where should I go for questions?

finally; I'm trying to deploy a multiple printers stack to a pi managed via balena cloud. They use a modified docker deamon which does not support bind mounts. To copy the correct configuration files I use a build context and COPY.

However mounting /dev is also not possible. I have had some success in an earlier stack to use devices and priviledged mode for klipper but was wondering if you have some experience or advise on how to do that properly?

https://github.com/mkuf/prind/blob/main/custom/docker-compose.custom.multiple-printers.yaml#L38

thank you

mkuf commented 1 year ago

Hi there,

it's fine to ask questions here, don't worry. 😄

I'm not familiar with balena cloud, does it support volume mounts at all?
What is the usual way to get runtime configuration to containers? Adding runtime config to containers at build time seems odd if the focus is on hosting containerized applications.

I could imagine adding a entrypoint script that pulls the runtime config at container startup would keep the amount of image builds you'll have to do to a minimum.

Adding /dev to klipper is not strictly necessary.
In earlier versions of prind (prior to v1.7.0), klipper ran in unprivileged mode and without /dev mounted. See https://github.com/mkuf/prind/blob/v1.6.0/custom/docker-compose.custom.multiple-printers.yaml for reference.
This approach relied on the device mapping of docker to pass the MCUs serial port to the container. https://github.com/mkuf/prind/blob/v1.6.0/custom/docker-compose.custom.multiple-printers.yaml#L66-L67

This also assumed that the device file of the printer was known, did not change during the livecycle of the container and is no symlink. Meaning that the MCU was not dis-/reconnected during operation of the host, as this would change the device file from e.g. /dev/ttyUSB0 to /dev/ttyUSB1, furthermore the symlink in /dev/serial/by-id/* promoted by the klipper docs could not be used. This decision was made in the course of https://github.com/mkuf/prind/issues/77.

-Markus

basz commented 1 year ago

balena only supports named volumes. The host OS is supost to be untouchable.

To get this limitation i bake them into the images. a ln -s /dev/null /opt/klipper/config/null takes care of the last hurdle.

But I am happy to report I got this stack working by using the devices: directive and privileged mode.

Fortunately the connected printers will not be unplugged or turned off at any time. So thats not an issue.

thx

What works;

from docker-compose.yml

x-klipper-svc: &klipper-svc
  build: 
      context: ./container-printd
      dockerfile: ./Dockerfile.klipper
  # image: mkuf/klipper:latest
  restart: unless-stopped
  privileged: true
  devices:
    - /dev/ttyUSB0:/dev/ttyUSB0:rwm
    - /dev/ttyUSB1:/dev/ttyUSB1:rwm
  volumes:
    #- /dev:/dev
    # - config:/opt/printer_data/config # copied in build as bind mounts are not supported in balena builds
    - run:/opt/printer_data/run
    - gcode:/opt/printer_data/gcodes
    - log:/opt/printer_data/logs

x-moonraker-svc: &moonraker-svc
  build: 
      context: ./container-printd
      dockerfile: ./Dockerfile.moonraker
  # image: mkuf/moonraker:latest

Dockerfile.moonraker

FROM mkuf/moonraker:latest

COPY config /opt/printer_data/config

USER root

RUN mkdir -p /opt/klipper/config/
RUN mkdir -p /opt/klipper/docs/
RUN ln -s /dev/null /opt/klipper/config/null
RUN ln -s /dev/null /opt/klipper/docs/null
RUN chown -R moonraker:moonraker /opt/klipper

USER moonraker
mkuf commented 1 year ago

Great that it works for you.

One more thing. You should either use privileged or devices.

When using privileged the container already has full access to your hosts devices, so you're able to just set the serial ports in klipper config and are good to go. There is no need to define devices passed to the container at this point.

When using devices without privileged, you gain more separation between the container and the host, as you are only sharing devices that you explicitly configure. This way you'll have to set the serial devices in your compose file and in your klipper config file.

And one last thing, if you're like me and only want a single file for your whole stack even though you're building new images, compose supports inline dockerfiles, an example: https://github.com/mkuf/prind/blob/e5a2e95f912be9e2516a5eaeab6329b00e3bad98/custom/docker-compose.custom.moonraker-timelapse.override.yaml#L37-L47

Are you fine with closing this?

basz commented 1 year ago

great tips! very much obliged

basz commented 1 year ago

An additional question if I may :-)

running two instances of moonraker, i've noticed the database is reset often. That is because the containers are recreated and the database directory is not in a named volume. I am wondering would it be dangerous to run two instances of moonraker from the same database files?

mkuf commented 1 year ago

No idea at all. 😄 It could potentially lead to a situation where one instance locks the DB and block other requests which could result in the locked out moonraker process crashing and interrupting printing operations.

Would you mind sharing your compose file when your setup is done?
I'd love to add it to the custom directory of the project so others may also benefit from it.