Source : https://jtreminio.com/blog/running-docker-containers-as-current-host-user
Le problème
Vous travaillez sur un projet qui nécessite Node NPM, PHP Composer ou un outil similaire qui télécharge ou compile des dépendances ou des ressources externes pour vous.
Vous ne voulez pas installer le langage (PHP, Node) localement pour exécuter cet outil, vous choisissez donc d'exécuter un conteneur Docker. Voici à quoi ressemblerait l'exécution d'un conteneur avec le support de PHP Composer :
docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
jtreminio/php:7.2 composer require psr/log
Cela démarre un conteneur temporaire utilisant mon image PHP 7.2 qui a Composer pré-installé et exécute composer require psr/log.
Cela génère ce qui suit :
$ ls -la
total 20K
drwxrwxr-x. 3 local_user local_user 4.0K Aug 4 19:34 ./
drwxr-xr-x. 7 local_user local_user 4.0K Aug 4 19:31 ../
drwxr-xr-x. 4 root root 4.0K Aug 4 19:34 vendor/
-rw-r--r--. 1 root root 53 Aug 4 19:34 composer.json
-rw-r--r--. 1 root root 2.1K Aug 4 19:34 composer.lock
Bien que cela fonctionne comme prévu, le problème est que les répertoires et les fichiers générés par Composer appartiennent à l'utilisateur root.
Comme je ne suis pas root, toute tentative de faire autre chose que de lire les fichiers est bloquée :
$ echo '' > composer.json
bash: composer.json: Permission denied
Vous devez utiliser sudo pour tout modifier ou supprimer. Cela devient vite pénible lorsque votre projet utilise Composer, Webpack, Yarn, etc. parce que votre système devient encombré de fichiers et de répertoires appartenant à root.
La raison
Sous Linux, Docker fonctionne comme un démon. Les instructions d'installation officielles recommandent d'installer en tant que root et d'ajouter sélectivement des utilisateurs au groupe docker afin qu'ils puissent exécuter toutes les commandes Docker.
$ ps -fe | grep dockerd 255:root 2356 1 0 Aug03 ? 00:04:06 /usr/bin/dockerd
Lorsque vous créez un nouveau conteneur, il n'est pas créé en tant qu'utilisateur actuel, mais en tant que root, sous lequel le démon est exécuté.
Nous pouvons vérifier que le conteneur fonctionne sous root avec l'ID utilisateur/groupe 0:0 :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
local_user/php:7.2 whoami
root
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
local_user/php:7.2 \
bash -c "echo \$(id -u \${USER}):\$(id -g \${USER})"
0:0
Exécuter le conteneur en tant qu'utilisateur local
Lorsque vous exécutez des conteneurs Docker, vous pouvez spécifier un ID d'utilisateur et un ID de groupe. C'est assez facile à faire :
# Paramètre "-w" pour spécifier le workdir
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u $(id -u ${USER}):$(id -g ${USER}) \
jtreminio/php:7.2 composer require psr/log
Cela génère ce qui suit :
$ ls -la
total 20K
drwxrwxr-x. 3 local_user local_user 4.0K Aug 4 20:09 ./
drwxr-xr-x. 7 local_user local_user 4.0K Aug 4 19:31 ../
drwxr-xr-x. 4 local_user local_user 4.0K Aug 4 20:09 vendor/
-rw-r--r--. 1 local_user local_user 53 Aug 4 20:09 composer.json
-rw-r--r--. 1 local_user local_user 2.1K Aug 4 20:09 composer.lock
Dans mon système, mon utilisateur local_user a l'ID utilisateur 1000 et l'ID groupe 1000, donc la nouvelle ligne -u $(id -u ${USER}):$(id -g ${USER}) est interprétée comme -u 1000:1000.
Note : habituellement, l'identifiant des utilisateurs créés sur les systèmes Linux commence désormais à partir de 1000. Sur les systèmes Fedora/CentOS, il était auparavant de 500. Ceci est spécifié dans le fichier /etc/login.defs. Ainsi, un utilisateur avec l'uid 1000 est le premier utilisateur normal (utilisateur non root) créé sur le système.
Cela fait exactement ce que nous voulons, mais il y a bien sûr un problème : l'utilisateur du conteneur n'est plus root. Sur Composer et NPM, cela signifie simplement que les répertoires de cache internes ne peuvent pas être écrits puisqu'ils appartiennent à l'utilisateur root, mais ce n'est pas vraiment un problème puisque nous détruisons les conteneurs dès qu'ils ont fini d'exécuter ce que nous leur avons demandé (le conteneur Composer ci-dessus est supprimé dès que composer require psr/log a fini de s'exécuter).
Et si vous voulez exécuter une application Web avec PHP-FPM ? Elle doit être capable de créer son fichier PID dans /var/run/php-fpm.pid, si vous utilisez des sessions basées sur des fichiers, elle doit écrire dans /var/lib/php/sessions. Toutes les choses qui nécessitent root ou un utilisateur prédéfini ne fonctionneront plus parce que le conteneur s'exécute en utilisant votre ID d'utilisateur/groupe :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u $(id -u ${USER}):$(id -g ${USER}) \
jtreminio/php:7.2 whoami
whoami: cannot find name for user ID 1000
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u $(id -u ${USER}):$(id -g ${USER}) \
jtreminio/php:7.2 \
bash -c "echo \$(id -u \${USER}):\$(id -g \${USER})"
1000:1000
Si vous essayez de faire quelque chose qui nécessite des autorisations élevées ou un utilisateur spécifique, cela sera refusé :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u $(id -u ${USER}):$(id -g ${USER}) \
jtreminio/php:7.2 ls -la /var/lib/php/sessions
total 8
drwxr-xr-x. 1 www-data www-data 4096 Jul 9 12:35 .
drwxr-xr-x. 1 root root 4096 Jul 26 09:08 ..
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u $(id -u ${USER}):$(id -g ${USER}) \
jtreminio/php:7.2 touch /var/lib/php/sessions/foo
touch: cannot touch '/var/lib/php/sessions/foo': Permission denied
Cela signifie que si vous exécutez le démon PHP-FPM en tant qu'utilisateur local, vous rencontrerez rapidement des problèmes de permissions. Dans ce cas, c'est parce que /var/lib/php/sessions appartient à www-data:www-data qui ne partage probablement pas les identifiants de votre utilisateur local :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
jtreminio/php:7.2 \
bash -c "echo \$(id -u www-data):\$(id -g www-data)"
33:33
Exécuter le conteneur en tant qu'utilisateur interne non root
Jusqu'à présent, nous avons constaté que
rootfonctionne très bien à l'intérieur du conteneur mais il est ennuyeux de travailler avec sur le système hôte ;- votre
utilisateur localfonctionne bien sur votre système hôte, mais rencontrera rapidement des problèmes de permissions à l'intérieur du conteneur.
Et si nous essayions d'exécuter les conteneurs en tant qu'utilisateur non-root qui a les permissions requises pour écrire dans les répertoires du conteneur ?
Essayez avec l'utilisateur interne www-data du conteneur :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u www-data \
jtreminio/php:7.2 \
bash -c "touch /var/lib/php/sessions/foo && echo \$?"
0
Comme prévu, cela a bien fonctionné, mais si vous essayez d'exécuter Composer :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u www-data \
jtreminio/php:7.2 composer require psr/log
[ErrorException]
file_put_contents(./composer.json): failed to open stream: Permission denied
Le problème est que l'utilisateur du conteneur interne www-data avec l'ID utilisateur/groupe 33:33, n'a pas les droits d'écriture sur le répertoire courant de mon hôte :
$ ls -la
total 8.0K
drwxrwxr-x. 2 local_user local_user 4.0K Aug 4 20:34 ./
drwxr-xr-x. 7 local_user local_user 4.0K Aug 4 19:31 ../
Nous pourrions donc ajouter un autre grief à notre liste :
- un utilisateur interne
non-rootavec les permissions requises fonctionne très bienà l'intérieur du conteneurmais échoue complètement sur lesystème hôte avec les volumes.
Pourquoi cela se produit-il ?
L'hôte et les conteneurs ne partagent pas les utilisateurs et les groupes.
Sur mon système local, il n'y a pas d'utilisateur www-data :
$ groups www-data
groups: ‘www-data’: no such user
De même, si j'essaie d'utiliser mon nom d'utilisateur direct pour exécuter le conteneur, je vois des erreurs :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u local_user \
jtreminio/php:7.2 whoami
docker: Error response from daemon: linux spec user: unable to find user local_user: no matching entries in passwd file.
Le conteneur possède son propre système séparé, avec sa propre liste d'utilisateurs dans /etc/passwd et sa propre liste de groupes dans /etc/group. Il n'a aucune idée des utilisateurs ou des groupes qui existent sur le système hôte. De même, l'hôte ne connaît pas les utilisateurs du conteneur. Par convention, root possède les identifiants 0:0, ce qui correspond à n'importe quel système Linux, mais toute autre valeur dépend de chaque distribution et de chaque auteur d'image.
Essayer de partager /etc/passwd
Nous savons maintenant que :
- le système hôte ne connaît pas le
/etc/passwddu conteneur ; - le conteneur ne connaît pas le
/etc/passwddu système hôte.
Que se passe-t-il si nous lions (bind mount) le /etc/passwd de l'hôte à celui du conteneur ?
$ export UID=$(id -u) $ export GID=$(id -g) $ docker run -it \ --user $UID:$GID \ --workdir="/home/$USER" \ --volume="/etc/group:/etc/group:ro" \ --volume="/etc/passwd:/etc/passwd:ro" \ --volume="/etc/shadow:/etc/shadow:ro" \ <IMAGE_NAME> /bin/bash
Pas vraiment ce que l'on pourrait croire, en fait. Mon système hôte n'a toujours pas d'utilisateur correspondant à 33:33, donc même si je lie le /etc/passwd au conteneur, les tentatives d'écriture dans les répertoires appartenant à www-data ne fonctionneront toujours pas.
Ceci est dû au fait que les permissions Linux ne sont pas basées sur le nom, mais sur l'ID.
Par exemple, sur mon système local :
$ ls -lan
total 8.0K
drwxrwxr-x. 2 1000 1000 4.0K Aug 4 20:34 ./
drwxr-xr-x. 7 1000 1000 4.0K Aug 4 19:31 ../
et dans le conteneur :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
-u $(id -u ${USER}):$(id -g ${USER}) \
jtreminio/php:7.2 ls -lan /var/lib/php/sessions
total 8
drwxr-xr-x. 1 33 33 4096 Jul 9 12:35 .
drwxr-xr-x. 1 0 0 4096 Jul 26 09:08 ..
Dans le conteneur www-data:www-data est simplement un alias de 33:33. Le conteneur ne se soucie pas du nom de l'utilisateur et du groupe utilisés, il veut simplement que les ID correspondent.
Utiliser les espaces de noms d'utilisateurs (user namespaces)
Il existe un concept dans Docker Engine appelé User Namespaces.
Le concept se résume à la mise en correspondance des identifiants internes des utilisateurs et des groupes de conteneurs pour refléter différentes valeurs.
Par exemple, vous pouvez demander à Docker d'utiliser votre ID d'utilisateur/groupe actuel comme " plancher " (floor) pour les ID de conteneur. Dans mon exemple, mon compte local_user avec 1000:1000 correspondrait directement à 0:0 dans un conteneur.
En d'autres termes, nous disons à Docker de considérer notre utilisateur actuel sur l'hôte comme root dans les conteneurs ! L'ID utilisateur 1000 du système local correspond directement à l'ID utilisateur 0 du conteneur.
Les espaces de noms d'utilisateurs nous permettraient d'exécuter tous les conteneurs en tant que root en interne, ce qui éliminerait complètement tout problème de permission, et tous les fichiers et répertoires générés sur les volumes partagés appartiendraient à l'utilisateur/groupe de l'hôte, de sorte que nous n'aurions plus besoin de sudo pour les modifier ou les supprimer.
C'est exactement ce dont nous avons besoin, non ? Il y a un hic (car bien sûr il y en a un)... en fait, plusieurs hics !
Vous exécutez le conteneur en tant que root
Normalement, il s'agit d'un problème de sécurité. D'innombrables articles et guides recommandent fortement de ne pas s'exécuter en tant que root dans les conteneurs.
Dans ce cas, ce n'est pas le problème, car la racine interne du conteneur correspond à notre utilisateur actuel, non root, sur l'hôte.
Le problème vient en fait du fait que le super-utilisateur peut faire tout et n'importe quoi dans le conteneur, ce qui ouvre la porte à des problèmes de permissions imprévus lorsque vous déployez en production, où vous n'utiliseriez pas d'espaces de noms d'utilisateurs.
En production, la plupart des conteneurs seront exécutés en tant que non-root, avec des autorisations spécifiquement adaptées à cet ID d'utilisateur/groupe. Toute tentative de faire quoi que ce soit que l'utilisateur n'est pas autorisé à faire entraînera à juste titre des erreurs de refus d'autorisation. Il s'agit d'une caractéristique de sécurité que vous souhaitez, et l'exécution en tant que root l'ignore complètement.
Vous pouvez vous exécuter en tant que root sur votre système local et tout fonctionne parfaitement bien. Vous êtes content, les fichiers sont générés comme prévu, toutes les actions sont autorisées et vous êtes un développeur heureux. Une fois que vous avez déployé en production, vous pouvez soudainement réaliser que vous développiez sur un système très différent de celui de la production. Quelque chose qui fonctionnait parfaitement sur votre système de développement peut facilement se briser sur le système de production en raison de permissions insuffisantes pour l'utilisateur qui exécute le service de conteneur.
Votre ID utilisateur/groupe est le plancher, il monte !
Votre ID d'utilisateur/groupe correspond directement à 0:0 dans le conteneur, mais il y a plus de comptes que le seul root et aucun d'entre eux ne correspond à votre utilisateur hôte.
Cela signifie que si 0:0 correspond au 1000:1000 de mon système hôte, l'utilisateur www-data du conteneur avec les ID 33:33 correspondrait au 1033:1033 de mon système hôte, qui n'existe pas.
Tout ce qui est fait en tant que non-root dans le conteneur se heurtera aux mêmes problèmes que nous avons vus précédemment : ce qui peut être considéré comme des autorisations suffisantes dans le conteneur ne fonctionnera probablement pas de la même manière sur votre hôte.
Les répertoires de mon hôte sont toujours détenus par 1000:1000 et un utilisateur avec 1033:1033 se verra refuser l'accès.
Tous les conteneurs de votre système sont affectés
Si les deux raisons précédentes ne suffisent pas, le fait que la prise en charge des espaces de noms d'utilisateur ne soit pas activée par défaut dans Docker, que son activation nécessite la modification d'un fichier de configuration JSON et le redémarrage du démon Docker, et que tous vos conteneurs existants et futurs mettront en œuvre les espaces de noms, que vous le souhaitiez ou non, devrait suffire à vous faire vous demander si le jeu en vaut la chandelle.
La solution
Le seul moyen sûr de résoudre tous les problèmes précédents est de remplacer complètement les identifiants internes des utilisateurs et des groupes par des valeurs connues et valables.
Dans le conteneur, www-data correspond à 33:33. Pourquoi ne pas le changer pour qu'il soit associé à mon système hôte local_user avec 1000:1000 ?
En d'autres termes, faites en sorte que l'ID utilisateur/groupe www-data du conteneur soit 1000:1000.
Vous ne pouvez pas modifier les ID utilisateur/groupe d'un compte existant. Rappelez-vous que les noms d'utilisateur et de groupe sont simplement des alias pour les ID. Vous pouvez renommer www-data en ce que vous voulez, mais les ID ne changeront pas.
La seule chose que vous pouvez faire est de supprimer et de recréer complètement l'utilisateur/le groupe.
Pour cela, nous devons créer un Dockerfile. Nous devons recréer l'utilisateur avant l'exécution de tout script de point d'entrée (entrypoint) potentiel. Des problèmes inconnus peuvent se produire et se produiront si vous tentez de supprimer un utilisateur/groupe alors qu'il est utilisé par un autre processus.
Tout d'abord, supprimez l'utilisateur et le groupe :
FROM jtreminio/php:7.2
RUN userdel -f www-data &&\
if getent group www-data ; then groupdel www-data; fi
Nous testons si le groupe existe réellement avant d'essayer de le supprimer, afin d'éviter les erreurs possibles lorsque le groupe n'existe pas.
Ensuite, nous pouvons générer l'utilisateur et le groupe avec nos ID :
FROM jtreminio/php:7.2
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN userdel -f www-data &&\
if getent group www-data ; then groupdel www-data; fi &&\
groupadd -g ${GROUP_ID} www-data &&\
useradd -l -u ${USER_ID} -g www-data www-data &&\
install -d -m 0755 -o www-data -g www-data /home/www-data
Après avoir supprimé l'utilisateur et le groupe, nous les recréons avec les valeurs définies.
1000 est mon ID d'utilisateur et de groupe, et maintenant le www-data du conteneur a le même.
N'oubliez pas d'utiliser le flag -l avec useradd ! Il y a un problème très amusant où une valeur élevée de l'UID va générer d'énormes fichiers journaux et geler votre système !
Nous prenons également le temps de générer un répertoire personnel pour notre nouvel utilisateur. Cela permet à votre conteneur d'effectuer plus facilement des actions SSH en utilisant les clés SSH de votre hôte, à condition de lier un volume.
Même si nous avons fini de recréer l'utilisateur et le groupe, il y a un problème : tous les fichiers et répertoires qui appartenaient à l'utilisateur www-data ne pointeront pas automatiquement vers les nouvelles valeurs d'identification. Puisque les noms ne sont que des étiquettes, /var/lib/php/sessions appartient toujours à 33:33, et ne correspond pas à notre nouveau 1000:1000 !
C'est la partie où vous devez en savoir juste assez sur le conteneur que vous voulez exécuter pour pouvoir identifier précisément les fichiers et répertoires dont vous devez mettre à jour le propriétaire.
Heureusement, le principe des conteneurs est de leur faire faire une seule chose, ce qui limite le nombre d'endroits que nous devons mettre à jour. Dans cette image, il n'y en a pas beaucoup du tout :
FROM jtreminio/php:7.2
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN userdel -f www-data &&\
if getent group www-data ; then groupdel www-data; fi &&\
groupadd -g ${GROUP_ID} www-data &&\
useradd -l -u ${USER_ID} -g www-data www-data &&\
install -d -m 0755 -o www-data -g www-data /home/www-data &&\
chown --changes --silent --no-dereference --recursive \
--from=33:33 ${USER_ID}:${GROUP_ID} \
/home/www-data \
/.composer \
/var/run/php-fpm \
/var/lib/php/sessions
Vous pouvez déterminer ce dont vous avez besoin en regardant le Dockerfile utilisé pour générer l'image. Pour l'image jtreminio/php:7.2, nous pouvons voir que les répertoires /.composer, /var/run/php-fpm et /var/lib/php/sessions doivent être mis à jour.
Enfin, il n'y a pas de mal à être explicite sur l'utilisateur que l'on souhaite voir exécuter le conteneur :
FROM jtreminio/php:7.2
ARG USER_ID=1000
ARG GROUP_ID=1000
RUN userdel -f www-data &&\
if getent group www-data ; then groupdel www-data; fi &&\
groupadd -g ${GROUP_ID} www-data &&\
useradd -l -u ${USER_ID} -g www-data www-data &&\
install -d -m 0755 -o www-data -g www-data /home/www-data &&\
chown --changes --silent --no-dereference --recursive \
--from=33:33 ${USER_ID}:${GROUP_ID} \
/home/www-data \
/.composer \
/var/run/php-fpm \
/var/lib/php/sessions
USER www-data
Si vous exécutez le conteneur maintenant, vous deviendrez automatiquement l'utilisateur www-data qui a maintenant l'ID utilisateur/groupe 1000:1000. Tous les fichiers générés par ce conteneur appartiendront à l'utilisateur de mon système hôte.
Mais que faire avec les identifiants codés en dur ? Vos collègues de travail peuvent avoir des ID différents sur leurs machines.
Rendez le paramétrage dynamique
Vous pouvez passer des arguments lors de la construction d'une image en utilisant --build-arg.
Mettez d'abord à jour le Dockerfile :
FROM jtreminio/php:7.2
ARG USER_ID
ARG GROUP_ID
RUN if [ ${USER_ID:-0} -ne 0 ] && [ ${GROUP_ID:-0} -ne 0 ]; then \
userdel -f www-data &&\
if getent group www-data ; then groupdel www-data; fi &&\
groupadd -g ${GROUP_ID} www-data &&\
useradd -l -u ${USER_ID} -g www-data www-data &&\
install -d -m 0755 -o www-data -g www-data /home/www-data &&\
chown --changes --silent --no-dereference --recursive \
--from=33:33 ${USER_ID}:${GROUP_ID} \
/home/www-data \
/.composer \
/var/run/php-fpm \
/var/lib/php/sessions \
;fi
USER www-data
Nous rendons USER_ID et GROUP_ID facultatifs. Si les deux ne sont pas définis, nous ignorons complètement le processus de suppression/création d'utilisateur. Ce Dockerfile peut maintenant être utilisé à des fins de développement et de production.
Pour construire l'image ci-dessus, vous devez procéder comme suit :
$ docker image build \
--build-arg USER_ID=$(id -u ${USER}) \
--build-arg GROUP_ID=$(id -g ${USER}) \
-t php_test \
.
Sending build context to Docker daemon 68.1kB
Step 1/5 : FROM jtreminio/php:7.2
---> 88efaa8cad72
Step 2/5 : ARG USER_ID
---> Running in 51d424a6d618
Removing intermediate container 51d424a6d618
---> 3feace11551d
Step 3/5 : ARG GROUP_ID
---> Running in aaaceff5ee4e
Removing intermediate container aaaceff5ee4e
---> 9f110b0f92c2
Step 4/5 : RUN if [ ${USER_ID:-0} -ne 0 ] && [ ${GROUP_ID:-0} -ne 0 ]; then userdel -f www-data &&
if getent group www-data ; then groupdel www-data; fi && groupadd -g ${GROUP_ID} www-data &&
useradd -l -u ${USER_ID} -g www-data www-data && install -d -m 0755 -o www-data -g www-data /home/www-data &&
chown --changes --silent --no-dereference --recursive --from=33:33 ${USER_ID}:${GROUP_ID}
/home/www-data /.composer /var/run/php-fpm /var/lib/php/sessions ;fi
---> Running in b516a3ab4600
changed ownership of '/.composer/keys.tags.pub' from 33:33 to 1000:1000
changed ownership of '/.composer/keys.dev.pub' from 33:33 to 1000:1000
changed ownership of '/.composer' from 33:33 to 1000:1000
changed ownership of '/var/run/php-fpm' from 33:33 to 1000:1000
changed ownership of '/var/lib/php/sessions' from 33:33 to 1000:1000
Removing intermediate container b516a3ab4600
---> c126c9f07252
Step 5/5 : USER www-data
---> Running in 76831280f239
Removing intermediate container 76831280f239
---> 5c237c8e46cd
Successfully built 5c237c8e46cd
Successfully tagged php_test:latest
Essayez d'exécuter Composer avec la nouvelle image :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
php_test:latest composer require psr/log
et vérifiez le résultat :
$ ls -lan
total 24K
drwxrwxr-x. 3 1000 1000 4.0K Aug 4 22:50 ./
drwxr-xr-x. 7 1000 1000 4.0K Aug 4 19:31 ../
drwxr-xr-x. 4 1000 1000 4.0K Aug 4 22:50 vendor/
-rw-rw-r--. 1 1000 1000 545 Aug 4 22:48 Dockerfile
-rw-r--r--. 1 1000 1000 53 Aug 4 22:50 composer.json
-rw-r--r--. 1 1000 1000 2.1K Aug 4 22:50 composer.lock
Essayez de créer un fichier dans un répertoire protégé :
$ docker container run --rm \
-v ${PWD}:/var/www \
-w /var/www \
php_test:latest \
bash -c "touch /var/lib/php/sessions/foo && echo \$?"
0
Utilisation de Docker Compose
Vous pouvez prendre la commande de construction d'image Docker et l'enregistrer dans un fichier bash pour une exécution facile.
Cependant, la plupart des gens préfèrent utiliser Docker Compose pour le développement local.
Voici à quoi pourrait ressembler votre docker-compose.yml :
version: '3.2'
services:
php:
build:
context: .
dockerfile: Dockerfile
args:
USER_ID: $(id -u ${USER})
GROUP_ID: $(id -g ${USER})
volumes:
- ${HOME}/.composer:/.composer
- ${PWD}:/var/www
ports:
- 9000:9000
Malheureusement, Docker Compose ne peut pas analyser les commandes, donc $(id -u ${USER}) ne fonctionnerait pas dans le fichier YAML. Heureusement, nous pouvons utiliser le fichier .env pour passer des valeurs :
# docker-compose.yml
version: '3.2'
services:
php:
build:
context: .
dockerfile: Dockerfile
args:
USER_ID: ${USER_ID:-0}
GROUP_ID: ${GROUP_ID:-0}
volumes:
- ${HOME}/.composer:/.composer
- ${PWD}:/var/www
ports:
- 9000:9000
; .env
USER_ID=1000
GROUP_ID=1000
Avec un seul docker-compose up -d --build, nous sommes prêts à fonctionner.
Nous avons maintenant un conteneur PHP-FPM qui fonctionne avec mon ID d'utilisateur/groupe système 1000:1000 et qui attend des connexions de Nginx ou Apache.
Quels conteneurs en ont besoin ?
Tous les conteneurs n'ont pas besoin de ces étapes. Pour les images les plus simples, comme le Nginx ou l'Apache officiel, qui ne génèrent pas de fichiers que vous souhaiteriez modifier pendant le développement, vous pouvez ignorer cette étape et laisser le conteneur fonctionner en tant qu'utilisateur interne normal.
Pour les conteneurs qui récupèrent uniquement les dépendances sur le disque local, comme PHP Composer ou Node NPM, vous pouvez vous en sortir en passant directement votre ID d'utilisateur/groupe. Le conteneur se plaindra qu'un utilisateur n'existe pas pour ces valeurs, mais il fonctionnera correctement. Les répertoires de cache peuvent nécessiter un peu de travail.
Les conteneurs avec des démons qui tournent longtemps et qui génèrent une grande quantité de fichiers, comme l'analyse de SCSS en CSS, vous seront très utiles pour mettre en œuvre ce concept.