Что было. Образ собран правильно: приложение запускается не от root, а от пользователя, созданного в Dockerfile с UID 5000. Каталог с хоста подключён через bind-mount для логов и данных. При старте cannot create log.txt: Permission denied. На хосте каталог принадлежит пользователю с UID 1000.
Почему. Ядро одно на хост и контейнер, и права на файлы проверяются по числовым UID/GID. Имя пользователя внутри контейнера ничего не значит: файл на хосте принадлежит 1000, процесс в контейнере работает от 5000, для ядра это чужой пользователь без права записи. Никакого «перевода» имён между хостом и контейнером по умолчанию нет.
Четыре рабочих способа.
- Запустить процесс с UID владельца каталога.
docker run --user 1000:1000 …или в composeuser: "1000:1000". Самый простой путь для своих сервисов. Недостаток: внутри контейнера у такого UID может не быть записи в/etc/passwd, некоторые программы на это жалуются. - Сменить владельца каталога на хосте под UID контейнера.
chown -R 5000:5000 /srv/app/data. Хорошо, когда образ чужой и UID фиксирован (у популярных образов он указан в документации, например 999 для баз данных, 1000 для многих приложений). Файлы на хосте будут «принадлежать» несуществующему пользователю, это нормально. - Именованные тома вместо bind-mount.
volumes: - appdata:/data: Docker при первом запуске копирует права из образа, и приложение пишет без вопросов. Данные лежат в/var/lib/docker/volumes, бэкапить их черезdocker run --rm -v appdata:/data -v $PWD:/backup alpine tar czf /backup/data.tgz /data. - Параметризовать UID при сборке. В Dockerfile
ARG UID=1000иuseradd -u $UID app, при сборке--build-arg UID=$(id -u). Удобно для сред разработки, где у каждого свой UID.
Что не делать. chmod -R 777 на каталог хоста снимает симптом и делает данные доступными любому процессу на машине; на сервере это недопустимо. Запуск контейнера от root «чтобы работало» возвращает все риски, ради ухода от которых создавали непривилегированного пользователя.
Диагностика за минуту.
docker run --rm -v /srv/app/data:/data образ id # какой UID у процесса
ls -ln /srv/app/data # числовые владельцы на хосте
Если числа не совпадают, применить один из способов выше. Для файлов, которые контейнер уже создал от root (частая история после первого запуска без --user), исправить владельца на хосте один раз.
Про SELinux. На системах с включённым SELinux нужен ещё суффикс :z или :Z у подключаемого каталога, иначе отказ будет даже при совпадающих UID.
Bind-mount описаны в документации Docker.