Речь идет об именованных томах (без «контейнера тома данных» и «от томов») и docker-compose.yml.
Цель здесь состоит в том, чтобы использовать docker-compose для управления двумя сервисами appserver и server-postgresql в двух отдельных контейнерах и использовать функцию «volume:» docker-compose.yml, чтобы сделать данные из сервиса server-postgresql постоянными. .
Dockerfile для server-postgresql выглядит так:
FROM ubuntu:14.04
MAINTAINER xxx
RUN apt-get update && apt-get install -y [pgsql-needed things here]
USER postgres
RUN /etc/init.d/postgresql start && \
psql --command "CREATE USER myUser PASSWORD 'myPassword';" && \
createdb -O diya diya
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.3/main/pg_hba.conf
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
CMD ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]
И docker-compose.yml выглядит так:
version: '2'
services:
appserver:
build: appserver
depends_on:
- server-postgresql
links:
- "server-postgresql:serverPostgreSQL"
ports:
- "1234"
- "1235"
restart: on-failure:10
server-postgresql:
build: serverPostgreSQL
ports:
- "5432"
volumes:
- db-data:/volume_data
restart: on-failure:10
volumes:
db-data:
driver: local
Затем я начинаю все с docker-compose up -d
, я ввожу свой контейнер server-postgresql с docker-compose exec server-postgresql bash
, быстрый ls
действительно показывает /volume_data
, я затем cd
и попробуйте touch testFile
и получил "разрешение отклонено. Это нормально, потому что быстрое ls -l
показывает, что volume_data
принадлежит root:root
.
Теперь, как мне кажется, происходит следующее: поскольку у меня есть USER postgres
в Dockerfile, когда я запускаю docker-compose exec
я вхожу в систему как пользователь postgres (а демон postgresql запускается как пользователь postgres). также, поэтому он не сможет писать на /volume_data
).
Это подтверждается , потому что , когда я запускаю это вместо: docker-compose exec --user root server-postgresql bash
и повторить попытку на cd /volume_data
и touch testFile
, это делает работу (это не ошибка разрешения между хостом и контейнером, а Иногда, когда контейнер монтирует папку хоста, это типичная ошибка разрешения unix, потому что /volume_data
монтируется как 'root: root', пока пользователь 'postgres' пытается писать).
Таким образом, в docker-compose.yml должен быть способ монтировать именованные тома от имени конкретного пользователя, например:
version: '2'
services:
appserver:
[...]
server-postgresql:
[...]
volumes:
- db-data:/volume_data:myUser:myGroup
[...]
volumes:
db-data:
driver: local
Единственный _dirty_ обходной путь, который я могу придумать, - это удалить директиву USER posgres
из файла Docker и изменить ENTRYPOINT так, чтобы он указывал на настраиваемый "init_script.sh" (wihch будет запускаться как 'root', поскольку я удален USER postgres
), этот сценарий изменит права доступа /volume_data
чтобы postgres мог писать на нем, затем su postgres
и запустил демон postgresql (на переднем плане). Но на самом деле это очень грязно, потому что он связывает Dockerfile и docker-compose.yml нестандартным способом (ENTRYPOINT во время выполнения будет полагаться на тот факт, что подключенный том доступен через docker-compose.yml).
Я не думаю, что это поддерживается механизмом докеров, поэтому мы не сможем поддерживать его в Compose, пока он не будет добавлен в API. Однако я не считаю необходимым добавлять эту функцию. Вы всегда можете передать файлы нужному пользователю:
version: '2'
services:
web:
image: alpine:3.3
volumes: ['random:/path']
volumes:
random:
$ docker-compose run web sh
/ # touch /path/foo
/ # ls -l /path
total 0
-rw-r--r-- 1 root root 0 Apr 5 16:11 foo
/ # chown postgres:postgres /path/foo
/ # ls -l /path
total 0
-rw-r--r-- 1 postgres postgres 0 Apr 5 16:11 foo
/ #
$ docker-compose run web sh
/ # ls -l /path
total 0
-rw-r--r-- 1 postgres postgres 0 Apr 5 16:11 foo
Проблема, с которой вы столкнулись, связана с инициализацией именованного тома. По общему признанию, это не то, что обрабатывается Compose (потому что это несколько выходит за рамки), но вы можете легко использовать docker cli для инициализации именованного тома перед запуском docker-compose up
.
Я действительно не был уверен, была ли это проблема Docker или Compose, извините, если я ошибся.
Это план в Docker API, следует ли мне сообщать о проблеме?
Я понимаю возможность ручного входа в контейнер и chown
передачи тома пользователю postgres. Но дело в том, что в моем случае я использую Compose, поэтому я могу сразу создавать экземпляры новых контейнеров для новых клиентов ( docker-compose -p client_name up
), а Compose создаст настраиваемую сеть client_name_default
, она создаст контейнеры с именами client_name _appserver_1
и client_name _server-postgresql_1
и, что еще важнее, будет создан том client_name_db-data
. Все это мне не нужно делать вручную, поэтому его можно запустить с помощью сценария, обрабатывающего регистрацию клиентов.
С помощью описанного вами решения (_вручную_ «ведение журнала» в контейнере с sh
и chown
запись метки тома) у меня не может быть простой процедуры для добавления новых клиентов, и это должно заботиться вручную.
Вот почему я считаю, что эту функцию следует реализовать. В Docker API мы можем указать разрешения ro
или rw
(только для чтения или чтения-записи) при монтировании тома, я думаю, мы можем указать user:group
.
Как вы думаете, имеет ли смысл моя просьба?
На самом деле я прихожу сюда с новостями, кажется, то, что я пытаюсь достичь, выполнимо, но я не знаю, является ли это функцией или ошибкой. Вот почему я изменился:
В моем Dockerfile _перед переходом на пользователя postgres_ я добавил следующее:
# ...
RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data
USER postgres
# ...
Это создает каталог / volume_data и изменяет его разрешения, чтобы пользователь postgres мог писать в нем.
Это часть Dockerfile .
Теперь я ничего не изменил в docker-compose.yml : поэтому docker-compose по-прежнему создает именованный том directory_name_db-data
и монтирует его в /volume_data
а разрешения сохраняются! .
Это означает, что теперь мой именованный том смонтирован в _pre-существующем_ каталоге /volume_data
с сохраненными разрешениями, поэтому postgres может писать в него.
Итак, если это предполагаемое поведение или нарушение безопасности? (Впрочем, в данном случае мне это пригодится!)
Я считаю, что это было добавлено в Docker 1.10.x, чтобы именованные тома инициализировались из первого контейнера, который их использовал. Я думаю, это ожидаемое поведение.
Я также делаю именованные тома, права собственности которых устанавливаются в Dockerfile, а в compose я добавляю user: postgres
чтобы даже PID 1 принадлежал пользователю без полномочий root.
Docker-compose
для volumes
имеет driver_opts
для предоставления параметров.
Было бы очень приятно увидеть там такие варианты, как chmod
, chown
даже для драйвера local
.
И особенно я хочу, чтобы он также был применен для локально созданных каталогов хоста, если его нет при запуске.
Связанный (в некоторой степени) https://github.com/moby/moby/pull/28499
Кто-то уже открыл вопрос по проекту Moby?
Ответ от @dnephin не работает. Проблема в том, что мы запускаем контейнер как стандартный пользователь, команды chown
или chmod
не работают, том принадлежит пользователю root, стандартный пользователь не может изменить разрешения.
@jcberthon предлагаемый метод состоит в том, чтобы запустить контейнер с root в качестве начального пользователя, а затем использовать команду USER
ПОСЛЕ chown / chmod, так что это в основном "удаление привилегий".
Это нормально, если вы контролируете образ Docker, но если вы используете существующие образы, это не вариант.
@ dragon788 и @micheljung , я решил свою проблему.
На самом деле реальная проблема заключалась в том, что в моем Dockerfile я объявил VOLUME
а затем изменил права собственности и права доступа к файлам в этом томе. Эти изменения потеряны. Просто переместив объявление VOLUME
в конец файла Docker (или удалив его, поскольку это необязательно), моя проблема решена. Разрешения правильные.
Итак, ошибка была:
FROM blabla
RUN do stuff
VOLUME /vol
RUN useradd foo && chown -R foo /vol
USER foo
CMD ["blabla.sh"]
chown
в приведенном выше примере файла Dockerfile теряются во время сборки, потому что мы объявляем перед ним VOLUME
. При запуске контейнера dockerd копирует в названный том содержимое /vol
перед объявлением VOLUME
(то есть с правами root). Следовательно, запущенные процессы не могут изменять или изменять разрешения, поэтому даже принудительное использование chown в сценарии blabla.sh не может работать.
Изменив файл на:
FROM blabla
RUN do stuff
RUN useradd foo && chown -R foo /vol
USER foo
VOLUME /vol
CMD ["blabla.sh"]
проблема решена.
@jcberthon, не могли бы вы поделиться, как вы связываете свой том / том с хост-системой в вашем docker-compose.yml?
Я работаю с Docker в Fedora (значит, с включенным SELinux), и ни один из вышеупомянутых методов у меня не сработал. В идеале я хочу запускать приложения в своих контейнерах в контексте пользователя (без root), но проблема с объемом является препятствием для этого.
Единственный обходной путь, который работает для меня, - это исключить моего пользователя приложения и запустить / владеть всем как пользователь root.
Привет, @renanwilliam и @ egee-irl
Я использовал вышеупомянутое решение на нескольких ОС, в т.ч. Fedora 26 и CentOS 7 оба с принудительным SELinux, Ubuntu 16.04, 17.10 и Raspbian 9 все три с активированным AppArmor (со смесью платформ amd64 и armhf).
Итак, как я уже сказал, я переместил объявление VOLUME ...
в конец моего файла Docker, но вы можете удалить его полностью, это не нужно. Что я также обычно делаю, так это исправляю идентификатор пользователя при создании пользователя в Dockerfile (например, useradd -u 8002 -o foo
). Затем я могу просто повторно использовать этот UID на хосте, чтобы предоставить соответствующие разрешения для папки.
Итак, следующим шагом будет создание «подвески» каталога /vol
на хосте, допустим, это /opt/mycontainer1/vol
, так что это
$ sudo mkdir -p /opt/mycontainer1/vol
$ sudo chown -R 8002 /opt/mycontainer1/vol
$ sudo chmod 0750 /opt/mycontainer1/vol
Затем при запуске контейнера от имени пользователя foo он сможет писать в каталог /opt/mycontainer1/vol
. Что-то вроде:
$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol mycontainer1-img
На хостах на основе SELinux вы можете добавить параметр :z
:Z
для тома, чтобы Docker соответствующим образом пометил папку. Разница между z
и Z
заключается в том, что строчная буква z
помечает том, так что потенциально всем контейнерам этого хоста может быть разрешен доступ SELinux к этому каталогу (но, очевидно, только если вы привяжете его к другому контейнеру), тогда как верхний регистр Z
пометит его так, чтобы только этот конкретный контейнер мог получить доступ к каталогу. Итак, в Fedora с SELinux вы можете попробовать:
$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol:Z mycontainer1-img
Обновление : вы можете проверить мое репо здесь https://github.com/jcberthon/unifi-docker Я использую этот метод и объясняю, как настроить хост и запустить ваш контейнер. Надеюсь, это поможет в дальнейшем решении ваших проблем.
Кстати, прошу прощения @renanwilliam за долгую задержку с
Короче для нетерпеливых:
RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data
Предварительно создав каталог тома, и chown
решит эту проблему, потому что том сохранит разрешения существующего каталога.
Это плохой обходной путь, поскольку он неочевиден (выполнение команды chown в файле Docker с последующим наследованием этого владения во время монтирования). Открытие управления владельцем и группой в интерфейсе командной строки docker-compose и docker было бы наименее неожиданным для команд стиля unix.
@villasv
Небольшой совет: объедините 2 RUN ...
в один, это позволяет избежать создания дополнительных слоев и является наилучшей практикой. Итак, ваши 2 строки должны быть
RUN mkdir /volume_data && chown postgres:postgres /volume_data
Но будьте осторожны (как я упоминал в комментарии выше), что вам нужно выполнить указанную выше команду RUN ...
перед объявлением объема (или фактически не объявлением объема) с помощью VOLUME ...
. Если вы выполните (как я) смену владельца после объявления тома, эти изменения не будут записаны и потеряны.
@colbygk это действительно было бы удобно, но Linux работает не так. Docker использует пространство имен монтирования Linux для создания различных иерархий с одним каталогом ( /
и подпапки), но, AFAIK, нет сопоставлений пользователей / групп или разрешений, переопределяющих в настоящее время в пространстве имен монтирования Linux. Эти «монтирования» внутри контейнера (включая тома с подключением к подключению) находятся в файловой системе на хосте (если, конечно, вы не используете какие-либо другие подключаемые модули томов Docker), и эта файловая система соблюдает уровень Linux VFS, который делает все проверка прав доступа к файлам. На хосте может быть даже какой-то MAC (например, SELinux, AppArmor и т. Д.), Который может мешать контейнеру обращаться к файлам внутри контейнера. На самом деле, если вы выполните chroot
, вы можете столкнуться с аналогичными проблемами, поскольку вы можете привязать папки монтирования в chroot, у вас также есть проблема, что процессы, запущенные в среде chroot, могут иметь неправильный эффективный UID / GID для доступа к файлам в привязка-крепление.
К контейнеру применяются простые правила Linux (а также, собственно, и Unix). Хитрость заключается в том, чтобы увидеть и понять возможности и ограничения пространств имен Linux сегодня, и тогда становится яснее, как решать проблемы, подобные этой. Я решил это полностью с помощью классических команд Unix.
@jcberthon Спасибо за вдумчивый ответ:
Я бы сказал, что это должна быть проблема, которая переносится на уровень плагина, как вы предлагаете, и, следовательно, может стать частью общего плагина обработчика тома, который поставляется с Docker. Мне кажется очень uncloud / container, как мне заставить внешний ресурс (внешний по отношению к конкретному контейнеру) придерживаться по существу статических отношений, которые определены в образе, из которого происходит контейнер.
Есть и другие примеры такого точного сопоставления uid / gid, которое встречается в других подобных областях "unix":
Пожалуйста, поправьте меня, если я ошибаюсь; https://github.com/zfsonlinux/zfs/issues/4177, по- видимому, был создан «лидером LXC / LXD» как проблема, связанная с ZFS в Linux, которая неправильно предоставляет информацию UID / GID, чтобы можно было сопоставить их с контейнер почти таким же _exact_ способом, который мы здесь обсуждаем. При внимательном рассмотрении https://github.com/zfsonlinux/zfs/issues/4177 кажется, что тип тома zfs на самом деле уже может поддерживать это сопоставление uid / gid между пространствами имен, но не предоставляет элементы управления для этого.
большинство людей, использующих Docker, предназначено для разработки / CI, мы используем «общие» образы, такие как php / nginx (runner) или gradle / python (builder), поэтому хорошее решение будет:
Поскольку мы можем легко определить разрешение на запись для тома (X: X: OPTION_READ_WRITE), как насчет добавления таким же образом владельца?
SOURCE:TARGET:RW:OWNER
У меня такая же проблема. Вероятно, это лучшая практика, и мой метод _не_:
version: '3.5'
services:
something:
image: someimage
user: '1000'
expose:
- 8080
volumes:
- dev:/app
volumes:
dev:
Это вызывает EACCES: permission denied, access '/app'
.
Как нам это сделать? Т.е. определить новый том и иметь к нему доступ с пользователем без полномочий root?
Привет @Redsandro
Было бы лучше, если бы вы установили в образе someimage
Docker UID равным 1000 для /app
. Или, если вы не можете контролировать это, вы должны использовать для user: ...
в вашем файле создания UID или GID, которые были предназначены автором изображения.
Конечно, если автор образа использовал UID 0, а вы не хотите запускать службу как root (и она может работать как непривилегированный пользователь), тогда поднимите вопрос перед автором образа Docker.
@Redsandro Проверьте обходные пути из: Docker и --userns-remap, как управлять разрешениями на том для обмена данными между хостом и контейнером?
так как это не то, что нужно докеру для управления, вы можете создать другой контейнер для предоставления ваших томов прямо перед запуском связанных контейнеров, например, используя https://hub.docker.com/r/hasnat/volumes-provisioner
version: '2'
services:
volumes-provisioner:
image: hasnat/volumes-provisioner
environment:
PROVISION_DIRECTORIES: "65534:65534:0755:/var/data/prometheus/data"
volumes:
- "/var/data:/var/data"
prometheus:
image: prom/prometheus:v2.3.2
ports:
- "9090:9090"
depends_on:
- volumes-provisioner
volumes:
- "/var/data/prometheus/data:/prometheus/data"
Я не понимаю, почему Docker не исправляет это, я согласен, что мы можем взломать его для целей разработки, но в продакшене - никак!
IMHO podman может работать как (непривилегированный) пользователь (см. Также здесь ) и, вероятно, решит эту проблему. Кто-то также работает над решением для компоновки, и Podman API намеренно совместим с Docker в большинстве частей.
[podman] может здесь помочь некоторым людям, потому что он в значительной степени совместим с Docker.
Полностью согласен, к сожалению подман не работает на Mac
Полностью согласен, к сожалению подман не работает на Mac
Ну ИМХО ни Docker, ни podman нативно работать не будут. Но установка Docker на OS / X очень хорошо скрывает содержимое виртуальных машин.
Но я согласен с тем, что настройка виртуальных машин вручную для получения надлежащей системы разработки может быть болезненной.
Хотя здесь это немного не по теме.
Я больше не пользуюсь OS / X, но только что увидел, что
Я предполагаю, что в ближайшем будущем может развиться какая-то подобная экосистема, потому что уже есть возможность получить доступ к программному обеспечению podman или даже к podman-compose .
Это особенно становится проблемой, когда пользователи без прав sudo в общем кластере случайно создают некоторые папки, принадлежащие root, через docker-compose, а затем они даже не могут сами удалить эти папки.
Я тоже столкнулся с этой проблемой. Мы пытаемся использовать докер в докере, как указано в сообщении jpetazzo .
Мы хотим запустить этот контейнер из файла docker-compose и иметь возможность монтировать сокет докера с хост-машины на машину-контейнер в группе docker. Это сделано для того, чтобы мы могли использовать другого пользователя, кроме root, для запуска докера из контейнера.
В настоящее время, поскольку я не могу указать право собственности и разрешение на файлы монтирования привязки, это невозможно.
Положи сюда мои два цента:
Для "родного" решения для docker-compose я сделал это, поскольку у большинства людей alpine уже есть в их библиотеке изображений:
volumes:
media:
services:
mediainit:
image: alpine
entrypoint: /bin/sh -c "chown -v nobody:nogroup /mnt/media && chmod -v 777 /mnt/media"
container_name: mediainit
restart: "no"
volumes:
- "media:/mnt/media"
Не самый безопасный из методов, конечно, но только контейнеры, которым предоставлены привилегии для контейнера, будут видеть его, так что это не такая уж большая проблема, но вы можете легко сделать его chown user: user или сделать какую-нибудь причуду setacl, если ваше ядро поддерживает Это.
РЕДАКТИРОВАТЬ: Кажется, вы должны сначала перерезать папку перед chmod, иначе она не «прилипнет», по крайней мере, в моем тестировании.
объем владения не находится под контролем docker-compose, поэтому это обсуждение должно происходить в репозитории проекта moby.
Некоторые примечания для людей, которые ищут здесь обходной путь:
Том Docker, когда он впервые используется контейнером, получает его исходное содержимое и разрешения, унаследованные от контейнера. Это означает, что вы можете настроить свое изображение следующим образом:
#Dockerfile
FROM alpine
RUN addgroup -S nicolas && adduser -S nicolas -G nicolas
RUN mkdir /foo && chown nicolas:nicolas /foo
# empty, but owned by `nicolas`. Could also have some initial content
VOLUME /foo
USER nicolas
Такой образ при запуске без явного тома (который будет создан специально со случайным идентификатором) или с именованным томом, который еще не существует, будет «распространять» разрешения на том:
➜ docker run --rm -it -v some_new_volume:/foo myimage
/ $ ls -al /foo
total 8
drwxr-xr-x 2 nicolas nicolas 4096 Oct 18 08:30 .
drwxr-xr-x 1 root root 4096 Oct 18 08:30 ..
То же самое применимо при использовании томов, объявленных в файле создания:
#docker-compose.yml
version: "3"
services:
web:
image: myimage
command: ls -al /foo
volumes:
- db-data:/foo
volumes:
db-data:
➜ docker-compose up
Creating volume "toto_db-data" with default driver
Creating toto_web_1 ... done
Attaching to toto_web_1
web_1 | total 8
web_1 | drwxr-xr-x 2 nicolas nicolas 4096 Oct 18 08:30 .
web_1 | drwxr-xr-x 1 root root 4096 Oct 18 08:37 ..
toto_web_1 exited with code 0
Это не сработает, если вы повторно подключите том, который уже использовался другим контейнером. Изменение корпоративного права собственности или управление им во время создания должно быть реализовано движком или драйверами тома с некоторыми конкретными параметрами. В противном случае вам придется полагаться на некоторые трюки chown
как предложено ^.
Надеюсь это поможет.
Я закрываю эту проблему, поскольку compose не управляет созданием тома, кроме открытого API движка.
Самый полезный комментарий
На самом деле я прихожу сюда с новостями, кажется, то, что я пытаюсь достичь, выполнимо, но я не знаю, является ли это функцией или ошибкой. Вот почему я изменился:
В моем Dockerfile _перед переходом на пользователя postgres_ я добавил следующее:
Это создает каталог / volume_data и изменяет его разрешения, чтобы пользователь postgres мог писать в нем.
Это часть Dockerfile .
Теперь я ничего не изменил в docker-compose.yml : поэтому docker-compose по-прежнему создает именованный том
directory_name_db-data
и монтирует его в/volume_data
а разрешения сохраняются! .Это означает, что теперь мой именованный том смонтирован в _pre-существующем_ каталоге
/volume_data
с сохраненными разрешениями, поэтому postgres может писать в него.Итак, если это предполагаемое поведение или нарушение безопасности? (Впрочем, в данном случае мне это пригодится!)