Se trata de volúmenes con nombre (por lo que no hay "contenedor de volumen de datos", no "volúmenes de") y docker-compose.yml.
El objetivo aquí es usar docker-compose para administrar dos servicios 'appserver' y 'server-postgresql' en dos contenedores separados y usar la función "volúmenes:" docker-compose.yml para hacer que los datos del servicio 'server-postgresql' sean persistentes .
El Dockerfile para 'server-postgresql' se ve así:
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"]
Y el docker-compose.yml se ve así:
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
Luego comienzo todo con docker-compose up -d
, entro en mi contenedor server-postgresql con docker-compose exec server-postgresql bash
, un rápido ls
revela /volume_data
, luego cd
en él e intente touch testFile
y obtuvo "permiso denegado. Lo cual es normal porque un rápido ls -l
muestra que volume_data
es propiedad de root:root
.
Ahora, lo que creo que está sucediendo es que, dado que tengo USER postgres
en el Dockerfile, cuando ejecuto docker-compose exec
estoy conectado como usuario 'postgres' (y el demonio postgresql se ejecuta como usuario 'postgres' también, por lo que no podrá escribir en /volume_data
).
Esto se confirma porque cuando corro en su lugar: docker-compose exec --user root server-postgresql bash
y reintento a cd /volume_data
y touch testFile
, que hace el trabajo (no es un error de permiso entre el anfitrión y el contenedor, como A veces es el caso cuando el contenedor monta una carpeta de host, este es un error de permiso típico de Unix porque /volume_data
está montado como 'root: root' mientras el usuario 'postgres' está intentando escribir).
Por lo tanto, debería haber una forma en docker-compose.yml para montar volúmenes con nombre como usuario específico, algo como:
version: '2'
services:
appserver:
[...]
server-postgresql:
[...]
volumes:
- db-data:/volume_data:myUser:myGroup
[...]
volumes:
db-data:
driver: local
La única solución _dirty_ que se me ocurre es eliminar la directiva USER posgres
del Dockerfile y cambiar ENTRYPOINT para que apunte a un "init_script.sh" personalizado (que se ejecutaría como 'root' ya que eliminado USER postgres
), este script cambiaría los permisos de /volume_data
para que 'postgres' pueda escribir en él, luego su postgres
y ejecutar el demonio postgresql (en primer plano). Pero esto en realidad es muy sucio, porque vincula Dockerfile y docker-compose.yml de una manera no estándar (el tiempo de ejecución ENTRYPOINT se basaría en el hecho de que un volumen montado está disponible mediante docker-compose.yml).
No creo que esto sea compatible con el motor de la ventana acoplable, por lo que no hay forma de que podamos admitirlo en Compose hasta que se agregue a la API. Sin embargo, no creo que sea necesario agregar esta función. Siempre puede transferir los archivos al usuario correcto:
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
El problema al que se enfrenta es acerca de la inicialización de un volumen con nombre. Es cierto que Compose no maneja esto (porque está algo fuera de alcance), pero puede usar fácilmente la ventana acoplable cli para inicializar un volumen con nombre antes de ejecutar docker-compose up
.
De hecho, no estaba seguro de si se trataba de un problema de Docker o de Redacción, lo siento si lo archivé mal.
¿Es este un plan en la API de Docker? ¿Debería presentar un problema allí?
Entiendo la posibilidad de iniciar sesión manualmente en el contenedor y chown
-ingresar el volumen al usuario 'postgres'. Pero la cuestión es que, en mi caso, estoy usando Compose para poder instanciar inmediatamente nuevos contenedores para nuevos clientes ( docker-compose -p client_name up
) y Compose creará una red personalizada client_name_default
, creará los contenedores con los nombres client_name _appserver_1
y client_name _server-postgresql_1
y _más importante_, creará el volumen client_name_db-data
. Todo lo cual no tengo que hacer manualmente, por lo que puede ser ejecutado por el script que maneja el registro del cliente.
Con la solución que describió (_manually_ "registrando" en el contenedor con sh
y chown
-ing la etiqueta de volumen), no puedo tener un procedimiento simple para agregar nuevos clientes, y esto debe ser atendido a mano.
Por eso creo que debería implementarse esta función. En la API de Docker, podemos especificar los permisos ro
o rw
(para solo lectura o lectura-escritura) al montar un volumen, creo que deberíamos poder especificar user:group
.
¿Qué piensas? ¿Tiene sentido mi solicitud?
En realidad, vengo aquí con noticias, parece que lo que estoy tratando de lograr es factible, pero no sé si esto es una característica o un error. He aquí por qué cambié:
En mi Dockerfile , _antes de cambiar al usuario 'postgres'_ agregué estos:
# ...
RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data
USER postgres
# ...
Lo que hace es crear un directorio / volume_data y cambiar sus permisos para que el usuario 'postgres' pueda escribir en él.
Esta es la parte de Dockerfile .
Ahora no he cambiado nada en docker-compose.yml : entonces docker-compose aún crea el Volumen con nombre directory_name_db-data
y lo monta en /volume_data
y los permisos persisten! .
Lo que significa que ahora, tengo mi Volumen con nombre montado en el directorio _pre-existente_ /volume_data
con los permisos preservados, para que 'postgres' pueda escribir en él.
Entonces, ¿este es el comportamiento previsto o una violación de la seguridad? (¡Sin embargo, me sirve en este caso!)
Creo que esto se agregó en Docker 1.10.x para que los volúmenes con nombre se inicialicen desde el primer contenedor que los usó. Creo que es el comportamiento esperado.
También estoy haciendo volúmenes con nombre con la propiedad establecida en el Dockerfile y en la composición estoy agregando user: postgres
para que incluso el PID 1 sea propiedad de un usuario no root.
Docker-compose
para volumes
tiene driver_opts
para proporcionar opciones.
Será muy bueno ver opciones como chmod
, chown
incluso para el controlador local
.
Y especialmente, quiero que también se aplique a los directorios de host creados localmente si no está presente al inicio.
Relacionado (hasta cierto punto) https://github.com/moby/moby/pull/28499
¿Alguien ya abrió un problema en el proyecto Moby?
La respuesta de @dnephin no funciona. El problema es porque estamos ejecutando el contenedor como un usuario estándar, los comandos chown
o chmod
fallan, el volumen es propiedad de root, un usuario estándar no puede cambiar los permisos.
@jcberthon el método sugerido es ejecutar el contenedor con root como usuario inicial y luego usar el comando USER
DESPUÉS del chown / chmod para que básicamente "pierda privilegios".
Eso está bien si tiene el control de la imagen de Docker, pero si está utilizando imágenes existentes, esa no es realmente una opción.
@ dragon788 y @micheljung , resolví mi problema.
En realidad, el problema real fue que en mi Dockerfile, declaré VOLUME
y luego modifiqué la propiedad y los permisos de los archivos en ese volumen. Esos cambios se pierden. Simplemente moviendo la declaración VOLUME
al final del Dockerfile (o eliminándolo, ya que es opcional), mi problema está resuelto. Los permisos son correctos.
Entonces el error fue:
FROM blabla
RUN do stuff
VOLUME /vol
RUN useradd foo && chown -R foo /vol
USER foo
CMD ["blabla.sh"]
Los chown
en el Dockerfile de ejemplo anterior se pierden durante la compilación porque declaramos VOLUME
antes. Al ejecutar el contenedor, dockerd copia dentro del volumen nombrado el contenido de /vol
antes de la declaración VOLUME
(es decir, con los permisos de root). Por lo tanto, los procesos en ejecución no pueden modificar o cambiar los permisos, por lo que incluso forzar chown en el script blabla.sh no puede funcionar.
Cambiando el archivo a:
FROM blabla
RUN do stuff
RUN useradd foo && chown -R foo /vol
USER foo
VOLUME /vol
CMD ["blabla.sh"]
el problema esta resuelto.
@jcberthon, ¿ podría compartir cómo vincula su volumen / vol con el sistema host en su docker-compose.yml?
Estoy trabajando con Docker en Fedora (por lo tanto, SELinux habilitado) y ninguno de los métodos mencionados anteriormente funcionó para mí. Idealmente, quiero ejecutar aplicaciones en mis Contenedores bajo el contexto de un usuario (sin root) pero este problema de Volumen es un obstáculo para eso.
La única solución que me funciona es eliminar el usuario de mi aplicación y ejecutar / poseer todo como usuario root.
Hola @renanwilliam y @ egee-irl
He estado usando la solución mencionada anteriormente en varios sistemas operativos incluidos. Fedora 26 y CentOS 7 ambos con SELinux forzado, Ubuntu 16.04, 17.10 y Raspbian 9 los tres con AppArmor activado (con una mezcla de plataformas amd64 y armhf).
Entonces, como dije, ahora moví la declaración VOLUME ...
al final de mi Dockerfile, pero puede eliminarlo por completo, no es necesario. Lo que también suelo hacer es corregir el ID de usuario al crear el usuario en el Dockerfile (por ejemplo, useradd -u 8002 -o foo
). Luego, simplemente puedo reutilizar ese UID en el host para otorgar los permisos adecuados a la carpeta.
El siguiente paso es crear el "colgante" del directorio /vol
en el host, digamos que es /opt/mycontainer1/vol
, entonces eso es
$ sudo mkdir -p /opt/mycontainer1/vol
$ sudo chown -R 8002 /opt/mycontainer1/vol
$ sudo chmod 0750 /opt/mycontainer1/vol
Luego, cuando ejecute el contenedor como usuario foo, podrá escribir en el directorio /opt/mycontainer1/vol
. Algo como:
$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol mycontainer1-img
En hosts basados en SELinux, es posible que desee agregar la opción :z
:Z
para el volumen para que Docker etiquete la carpeta de manera adecuada. La diferencia entre z
y Z
es que la minúscula z
marcará el volumen para que potencialmente todos los contenedores de este host puedan ser permitidos por SELinux para acceder a ese directorio (pero obviamente solo si lo vincula a montarlo en otro contenedor), mientras que las mayúsculas Z
lo etiquetarán para que solo ese contenedor específico pueda acceder al directorio. Entonces, en Fedora con SELinux, es posible que desee probar:
$ sudo -u docker-adm docker run --name mycontainer1 -v /opt/mycontainer1/vol:/vol:Z mycontainer1-img
Actualización : puede consultar mi repositorio aquí https://github.com/jcberthon/unifi-docker Estoy usando este método y explicando cómo configurar el host y ejecutar su contenedor. Espero que esto pueda ayudar a resolver aún más sus problemas.
Por cierto, me disculpo @renanwilliam por la larga demora en responderte. No tengo mucho tiempo libre este fin de año ...
Entonces, cuento largo para los impacientes:
RUN mkdir /volume_data
RUN chown postgres:postgres /volume_data
Crear el directorio del volumen de antemano y chown
resuelve, porque el volumen conservará los permisos del directorio preexistente.
Este es un trabajo deficiente, ya que no es obvio (hacer un chown en un Dockerfile y luego heredar esa propiedad durante el montaje). Exponer el control del propietario y del grupo en la CLI de docker-compose y docker sería el camino de menor sorpresa para los comandos de estilo Unix.
@villasv
Un pequeño consejo: fusiona los 2 RUN ...
en uno, esto evita crear capas adicionales y es una buena práctica. Entonces tus 2 líneas deberían ser
RUN mkdir /volume_data && chown postgres:postgres /volume_data
Pero tenga cuidado (como mencioné en un comentario anterior) que debe hacer el comando RUN ...
anterior antes de declarar el volumen (o en realidad no declarar el volumen) usando VOLUME ...
. Si hace (como hice yo) el cambio de propiedad después de declarar el volumen, esos cambios no se registran ni se pierden.
@colbygk sería realmente útil, pero no es así como funciona Linux. Docker usa el espacio de nombres de montaje de Linux para crear diferentes jerarquías de un solo directorio ( /
y subcarpetas), pero AFAIK no hay asignaciones de usuarios / grupos o permisos que anulen actualmente en el espacio de nombres de montaje de Linux. Esos "montajes" dentro de un contenedor (y que incluyen volúmenes de montaje de enlace) están en un sistema de archivos en el host (a menos que use otros complementos de volumen de Docker, por supuesto), y este sistema de archivos respeta la capa VFS de Linux que hace todo la comprobación de permisos de archivo. Incluso podría haber en el host algún MAC (por ejemplo, SELinux, AppArmor, etc.) que podría interferir con un contenedor que accede a los archivos dentro del contenedor. En realidad, si hace chroot
, puede encontrar problemas similares, ya que puede vincular carpetas de montaje dentro del chroot, también tiene el problema de que los procesos que se ejecutan dentro del entorno chroot pueden tener el UID / GID efectivo incorrecto para acceder a los archivos en el bind-mount.
Se aplican reglas simples de Linux (y en realidad también de Unix) para el contenedor. El truco consiste en ver y comprender las posibilidades y los límites de los espacios de nombres de Linux en la actualidad, y luego se está volviendo más claro cómo resolver problemas como este. Lo resolví completamente usando comandos clásicos de Unix.
@jcberthon Gracias por su
Yo diría que este debería ser un problema que se inserta en la capa de complemento como sugiere y, por lo tanto, podría convertirse en parte del complemento de controlador de volumen genérico que se envía con Docker. Me parece muy poco nublado / contenedor forzar a un recurso externo (externo a un contenedor en particular) a adherirse a relaciones esencialmente estáticas que se definen en la imagen de la que se deriva un contenedor.
Hay otros ejemplos de este tipo exacto de mapeo uid / gid que ocurre en otras áreas similares de "unix":
Por favor, corríjame si estoy equivocado; https://github.com/zfsonlinux/zfs/issues/4177 parece haber sido originado por el "líder de LXC / LXD" como un problema relacionado con ZFS en Linux que no proporciona correctamente información UID / GID para permitir el mapeo de esos en un contenedor casi de la manera _exacta_ que estamos discutiendo aquí. Mirando de cerca https://github.com/zfsonlinux/zfs/issues/4177 , parece que el tipo de volumen zfs en realidad ya podría admitir este mapeo uid / gid entre espacios de nombres, pero no expone los controles para hacerlo.
la mayoría de las personas que usan Docker es para dev / CI, usamos imágenes "genéricas" como php / nginx (corredor) o gradle / python (constructor), por lo que una buena solución:
Dado que podemos decidir fácilmente el permiso de escritura de un volumen (X: X: OPTION_READ_WRITE), ¿qué hay de agregar de la misma manera, el propietario?
SOURCE:TARGET:RW:OWNER
Estoy teniendo el mismo problema. Probablemente haya una mejor práctica, y mi método es _no_:
version: '3.5'
services:
something:
image: someimage
user: '1000'
expose:
- 8080
volumes:
- dev:/app
volumes:
dev:
Esto causa EACCES: permission denied, access '/app'
.
¿Cómo deberíamos hacer esto? ¿Es decir, definir un nuevo volumen y poder acceder a él con un usuario que no sea root?
Hola @Redsandro
Sería mejor que establezca en la imagen de Docker someimage
el UID en 1000 por /app
. O si no puede controlar eso, debe usar para user: ...
en su archivo de redacción el UID o GID que pretendía el autor de la imagen.
Por supuesto, si el autor de la imagen usó el UID 0 y no desea ejecutar el servicio como root (y puede ejecutarse como un usuario sin privilegios), entonces plantee un problema al autor de la imagen de Docker.
@Redsandro Verifique las soluciones alternativas de: Docker y --userns-remap, ¿cómo administrar los permisos de volumen para compartir datos entre el host y el contenedor?
dado que esto no es algo que Docker debe administrar, puede crear otro contenedor para aprovisionar sus volúmenes justo antes de que comiencen los contenedores relacionados, por ejemplo, usando 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"
No entiendo por qué Docker no está arreglando este, estoy de acuerdo en que podemos piratearlo para propósitos de desarrollo, pero en producción, ¡de ninguna manera!
En mi humilde opinión, privilegios) (ver también aquí ) y probablemente resolverá esto. Alguien también trabaja en una solución de redacción y la API de Podman es intencionalmente compatible con Docker en la mayoría de las partes.
[podman] podría ayudar a algunas personas aquí, sin embargo, porque es compatible con Docker en gran parte.
Totalmente de acuerdo, desafortunadamente podman no funciona en Mac
Totalmente de acuerdo, desafortunadamente podman no funciona en Mac
Bueno, en mi humilde opinión ni Docker ni podman nunca funcionarán de forma nativa. Pero las instalaciones de Docker en OS / X ocultan muy bien las cosas de la máquina virtual.
Pero estoy de acuerdo en que configurar las máquinas virtuales manualmente para tener un sistema de desarrollo adecuado puede ser doloroso.
Sin embargo, aquí se está saliendo un poco del tema.
Ya no soy un usuario de OS / X, pero acabo de ver que hay un dmg de podman _experimental_ .
Supongo que algún ecosistema similar podría desarrollarse en un futuro más cercano porque ya es posible acceder a podman programmatic o incluso a podman-compose .
Esto se convierte especialmente en un problema cuando los usuarios sin derechos de sudo en un clúster compartido crean accidentalmente algunas carpetas propiedad de root a través de docker-compose y luego ni siquiera pueden eliminar esas carpetas ellos mismos.
También me encontré con este problema. Estamos intentando usar la ventana acoplable en la ventana acoplable como se publicación de jpetazzo .
Queremos iniciar este contenedor desde el archivo docker-compose y poder montar el socket de la ventana acoplable desde la máquina host en la máquina del contenedor en el grupo de la ventana acoplable. Esto es para que podamos usar otro usuario que no sea root para ejecutar la ventana acoplable desde el interior del contenedor.
Actualmente, dado que no puedo especificar la propiedad y el permiso de los archivos de montaje de enlace, esto no se puede lograr.
Pon mis dos centavos aquí:
Para una solución "nativa" de docker-compose, hice esto, ya que la mayoría de las personas ya tendrán alpine en su biblioteca de imágenes:
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"
No es el método más seguro, por supuesto, pero solo los contenedores a los que se les otorgan privilegios al contenedor lo verán, por lo que no es un gran problema, pero puede convertirlo fácilmente en usuario: usuario o hacer algo de setacl si su kernel lo admite. eso.
EDITAR: Parece que primero debes usar la carpeta antes de chmod o no se 'pega', al menos en mis pruebas.
la propiedad del volumen no está bajo el control de docker-compose, por lo que esta discusión debe tener lugar en el repositorio del proyecto moby.
Algunas notas para las personas que buscan aquí una solución alternativa:
El volumen de Docker, cuando un contenedor lo usa por primera vez, obtiene su contenido inicial y el permiso heredado del contenedor. Lo que significa que puedes configurar tu imagen así:
#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
Una imagen de este tipo, cuando se ejecuta sin un volumen explícito (que se creará a propósito con un ID aleatorio) o con un volumen con nombre que aún no existe, "propagará" los permisos al volumen:
➜ 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 ..
Lo mismo se aplica cuando se utilizan volúmenes declarados en un archivo de redacción:
#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
Esto no funcionará si vuelve a adjuntar un volumen que ya ha sido utilizado por otro contenedor. Cambiar la propiedad del volumen, o controlarlo en el momento de la creación, debe ser implementado por el motor o por controladores de volumen con algunas opciones específicas. De lo contrario, tendrá que confiar en algunos trucos de chown
como se sugiere ^.
Espero que esto ayude.
Estoy cerrando este problema ya que componer no tiene control sobre la creación de volumen, pero la API del motor expuesta.
Comentario más útil
En realidad, vengo aquí con noticias, parece que lo que estoy tratando de lograr es factible, pero no sé si esto es una característica o un error. He aquí por qué cambié:
En mi Dockerfile , _antes de cambiar al usuario 'postgres'_ agregué estos:
Lo que hace es crear un directorio / volume_data y cambiar sus permisos para que el usuario 'postgres' pueda escribir en él.
Esta es la parte de Dockerfile .
Ahora no he cambiado nada en docker-compose.yml : entonces docker-compose aún crea el Volumen con nombre
directory_name_db-data
y lo monta en/volume_data
y los permisos persisten! .Lo que significa que ahora, tengo mi Volumen con nombre montado en el directorio _pre-existente_
/volume_data
con los permisos preservados, para que 'postgres' pueda escribir en él.Entonces, ¿este es el comportamiento previsto o una violación de la seguridad? (¡Sin embargo, me sirve en este caso!)