Driva Rclone-monteringar tillförlitligt i Docker
För att en FUSE-montering från en container ska fungera både på värden och i andra containrar måste monteringspropagering, AppArmor och återställning efter fel samverka.
En Rclone-montering körs i en Docker-container men ska även vara tillgänglig på värden och i andra containrar. För detta måste monteringshändelser passera flera namnrymder. Ett enskilt Compose-alternativ räcker inte.
I ett praktiskt test med Ubuntu 25.10, kernel 6.17 och Docker 29.6 uppstod tre oberoende fel: Docker nedgraderade omärkligt rshared, AppArmor blockerade fusermount3, och en konsumerande container höll efter en omstart fast vid den gamla monteringen. Det konkreta användningsfallet var en molnlagring för Paperless-ngx; samma mekanismer gäller även för andra FUSE-verktyg som sshfs.
1. Värdkällan måste själv vara shared
För att en montering från containern ska nå värden behöver bind-monteringen propageringen rshared:
volumes:
- type: bind
source: /srv/storage/media
target: /data
bind:
propagation: rshared
rshared fungerar bara om bind-källan på värden själv är en monteringspunkt med shared-propagering. En vanlig katalog uppfyller inte detta krav. Docker rapporterar ändå inget fel utan använder i tysthet en svagare propagering. Det syns i /proc/self/mountinfo i containern:
1938 2077 8:2 /srv/storage/media /data rw,relatime master:1 - ext4 /dev/sda2 rw
master:1 betyder slave-propagering: monteringarna kommer in från värden, men aldrig ut. Rätt värde vore shared:N. Lösningen är att bind-montera källan till sig själv och markera den som shared:
mount --bind /srv/storage/media /srv/storage/media
mount --make-shared /srv/storage/media
För att detta ska överleva en omstart bör det läggas i en systemd-enhet med Before=docker.service. Kontroll: findmnt -no PROPAGATION /srv/storage/media måste returnera shared.
2. AppArmor kontrollerar fusermount3 även i containern
Med korrekt propagering uppstod nästa problem. Monteringen till den delade sökvägen misslyckades fortfarande:
NOTICE: mount helper error: fusermount3: mount failed: Permission denied
CRITICAL: Fatal error: failed to mount FUSE fs: fusermount: exit status 1
De vanliga extra containerbehörigheterna ändrade ingenting: varken CAP_SYS_ADMIN och /dev/fuse eller unconfined eller till och med --privileged. En tmpfs-montering fungerade på samma mål, och FUSE fungerade på andra sökvägar. Först kernelns audit-logg visade den verkliga orsaken:
audit: type=1400 apparmor="DENIED" operation="mount" class="mount"
info="failed mntpnt match" error=-13 profile="fusermount3"
name="/data/documents/originals/" fstype="fuse.rclone"
Ubuntu levererar en AppArmor-profil för binärfilen fusermount3, som endast tillåter FUSE-monteringar till en positivlista med monteringspunktsmönster. Denna profil gäller även för fusermount3 i containern. Avgörande är sökvägen så som containern ser den:
mount fstype=@{fuse_types} ... -> @{HOME}/**/,
mount fstype=@{fuse_types} ... -> /mnt/{,**/},
mount fstype=@{fuse_types} ... -> /media/**/,
mount fstype=@{fuse_types} ... -> /tmp/**/,
/data finns inte i listan, /srv inte heller. Att containern körs unconfined hjälper inte: profilen är knuten till den körbara filen, inte till containern.
Utvägen bygger på att endast fusermount3 omfattas av profilen, medan en vanlig mount --bind inte gör det: montera FUSE på en tillåten sökväg och publicera den därifrån via bind-montering till den delade sökvägen.
rclone mount remote:pfad /mnt/inner/dokumente --allow-other --vfs-cache-mode full &
# vänta tills monteringen svarar, sedan:
mount --bind /mnt/inner/dokumente /data/dokumente
Bind-montering är ett vanligt mount(2)-anrop och propageras, som alla andra, via den delade sökvägen till värden. Detta kunde verifieras ända till en andra container som kunde läsa filerna som uid 1000. --allow-other är obligatoriskt så snart en annan användare än den monterande får åtkomst till filerna; i Rclone-containern måste därför user_allow_other finnas i /etc/fuse.conf (vilket redan är fallet i den officiella imagen).
3. Konsumenter behöver rslave
Det tredje problemet gäller andra sidan. Om Rclone-processen kraschar och monteringen byggs upp på nytt ser värden den omedelbart. En container som har monterat in sökvägen på vanligt sätt via bind ser den däremot inte:
ls: cannot access '/usr/src/app/media': Transport endpoint is not connected
Docker använder som standard rprivate för bind-monteringar: en montering som uppstår på värden efter att containern har startat når aldrig dess monteringsnamnrymd. Containern förblir hängande vid den frånkopplade FUSE-montering tills den återskapas. Lösningen kräver en rad:
volumes:
- type: bind
source: /srv/storage/media
target: /usr/src/app/media
bind:
propagation: rslave
Med rslave vidarebefordrar värden nya monteringshändelser till containern. I testet såg konsumenten efter en hårt avslutad och nyuppbyggd montering åter alla filer utan egen omstart. Omstartsräknaren förblev noll.
Återställning utan manuella ingrepp
De tre byggstenarna ger tillsammans ett robust helhetsmönster som klarar sig utan en watchdog-daemon:
- Monteringscontainern kontrollerar sina monteringar i en slinga. Om någon inte längre svarar avslutas den med en felkod.
restart: unless-stoppedlåter Docker starta om containern.- Vid start rensar containern först bort överblivna monteringar från föregående körning: en överbliven bind-montering på målsökvägen skulle annars blockera ny publicering, och från värden kan en oprivilegierad användare inte ta bort den. I containern går det, och umount propageras utåt:
while grep -q " /data/dokumente " /proc/self/mountinfo; do
umount -l /data/dokumente 2>/dev/null || break
done
- Montera och publicera sedan normalt; konsumenter med
rslavetar automatiskt över den nya monteringen.
I testet tog hela kedjan 160 sekunder: Rclone-processen avslutades, felet upptäcktes, containern startades om, den överblivna monteringen togs bort och den nya monteringen publicerades igen. Den konsumerande containern fortsatte att köra under tiden och märkte bara ett kort avbrott.
Den som kör Rclone direkt på värden via systemd undviker de första två problemen och behöver bara rslave på de konsumerande containrarna. Den extra containern är främst värd det om värden ska hållas fri från Rclone-installationer eller om flera monteringar ska hanteras enhetligt. Då måste alla tre nivåer konfigureras medvetet.
Kommentarer
Kommentarerna hämtas från GitHub / Giscus.