Как настроить права в Docker?
Решил поразбираться в Docker и сделать себе сборку для laravel (LEMP).
- docker — файлы настроек и Dockerfile для mysql,php,nginx
- hosts — настройки вир.хостов (nginx)
- logs — логи nginx
- mysql — файлы БД
- www ‒ директории проектов
Столкнулся с проблемой, когда логи nginx, которые попадают в директорию ./logs — создаются под root. Соответственно, если требуется их удалить — необходимо лезть в консоль для: sudo rm *.log
version: '3' services: #Php-fpm Service app: build: context: ./docker/php/ dockerfile: php-fpm.docker container_name: app restart: unless-stopped tty: true environment: SERVICE_NAME: app SERVICE_TAGS: dev working_dir: /var/www volumes: - ./www:/var/www - ./docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini links: - db networks: - laravel-network #Nginx Service webserver: build: context: ./docker/nginx/ dockerfile: nginx.docker container_name: webserver restart: unless-stopped tty: true ports: - "80:80" - "443:443" volumes: - ./www:/var/www - ./hosts/:/etc/nginx/conf.d - ./logs:/var/log/nginx links: - app - db networks: - laravel-network #MariaDB Service db: build: context: ./docker/mysql/ dockerfile: mariadb.docker container_name: db restart: unless-stopped tty: true ports: - "3306:3306" environment: MYSQL_DATABASE: laravel MYSQL_ROOT_PASSWORD: secret SERVICE_TAGS: dev SERVICE_NAME: mysql volumes: - ./mysql:/var/lib/mysql/ - ./docker/mysql/my.cnf:/etc/mysql/my.cnf networks: - laravel-network #PHPMyadmin Service pma: image: phpmyadmin:latest container_name: pma restart: unless-stopped tty: true ports: - 81:80 environment: PMA_HOST: db MYSQL_USERNAME: root MYSQL_ROOT_PASSWORD: secret links: - db networks: - laravel-network #Docker Networks networks: laravel-network: driver: bridge
Изначально была аналогичная проблема с MariaDB — все файлы создавались под root, пофиксил — запустив контейнер не от root пользователя.
FROM mariadb:latest # Add user for laravel application RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www USER www
FROM nginx # Add user for laravel application RUN groupadd -g 1000 www RUN useradd -u 1000 -ms /bin/bash -g www www USER www
webserver | 2020/10/28 07:09:25 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2 webserver | nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2 webserver | 2020/10/28 07:09:25 [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied) webserver | nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (13: Permission denied)
Если попробовать выдать 777 на /var/cache/nginx/ (RUN chmod -R 777 /var/cache/nginx/), то появляется другая ошибка:
webserver | 2020/10/28 07:12:54 [warn] 1#1: the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2 webserver | nginx: [warn] the "user" directive makes sense only if the master process runs with super-user privileges, ignored in /etc/nginx/nginx.conf:2 webserver | 2020/10/28 07:12:54 [emerg] 1#1: bind() to 0.0.0.0:80 failed (13: Permission denied) webserver | nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)
Могу предположить — проблема связана с правами, но я не могу разобраться, как заставить nginx стартовать от имени созданного пользователя (www) или иным способом обойти проблему, то есть выдать на создаваемые логи полные права (или права www пользователя).
С докером познакомился «буквально вчера», поэтому может не вижу очевидного решения проблемы. Не подскажите, как пофиксить данную проблему или может посоветуете вариант получше?
Setting correct permission for Docker PHP-FPM on mounted folder
Now, if you have followed my guide on setting up Docker with PHP-FPM then you’ll most likely face this issue where your files and directories permission will have to set to 777 in order for docker to write files to your mounted folder.
In order to resolve this, you’ll need to reset your 777 mistakes using the command given in my reset files/directories permission article.
Once you’ve done that, you’ll be back to your square one where your application can’t write to your mounted folder.
Now, in your mounted folder assuming its in /root/www you’ll need to look for the user that exec your php script in your php-fpm docker. By default its www-data (dahhh). So let’s find out what this user id is on the parent machine by firing the following docker command
docker exec phpfpm id www-data
where phpfpm is the docker name of your PHP-FPM container. If you are not using PHP-FPM on a separate container, you can easily just replace phpfpm to your LEMP/LAMP docker container name.
and the above will show you something like this
[email protected]:~# docker exec phpfpm id www-data uid=82(www-data) gid=82(www-data) groups=82(www-data),82(www-data)
the above means that on the parent machine, the user id for www-data is 82. Now, go ahead and change the user permission on your mounted folder to 82 with the following command
where /root/www is the example mounted folder used in this article.
Now, with the correct user permission, your application should be able to write correctly without the need to set your directories permissions to 777 which is pretty insecure.
getId() — блог о программировании
В этой статье я расскажу, как я решил проблему с правами на файлы, которая проявляется при разработке веб-приложений с использованием Docker и контейнеров php-fpm, nginx, node.
- на вашей машине установлена ОС из семейства Linux, например, Ubuntu
- вы логинитесь и работаете не root-пользователем, например, пользователь ubuntu с образы php-fpm, nginx, node из официального репозитория Docker Hub
- исходный код вы редактируете не в докере, а на хост-машине, монтируя директорию с ним в докер-контейнеры
Решение
Суть решения в том, что мы меняем пользователя, под которым работает nginx, php-fpm, node на пользователя с тем же id, что и локальный пользователь, который редактирует файлы.
Вынесем id пользователя в build arguments для возможности указывать разных пользователей в разных окружениях. Команда Dockerfile `RUN` не поддерживает ENV-переменные, поэтому используем именно build arguments. Для development подойдет локальный пользователь, для stage и CI нужен тот пользователь, от имени которого запускаются build-скрипты в CI, например `gitlab-runner`, а на production лучше оставить www-data (id=33) и сделать выполнение консольных команд от его имени.
Привожу команды Dockerfile для каждого образа, которые создают нового пользователя внутри образа и прописывают его в конфиги приложений.
# PHP-FPM
ARG WEB_USER_ID=33
ARG WEB_USER_NAME=www-data
RUN echo «Building for web user: name=$»
RUN useradd -m -u $ $ || echo «User exists»
RUN sed -i — «s/user = www-data/user = $/g» /usr/local/etc/php-fpm.d/www.conf
USER $
#NGINX
ARG WEB_USER_ID=33
ARG WEB_USER_NAME=www-data
RUN echo «Building for web user: name=$»
RUN useradd -m -u $ $ || echo «User exists»
RUN sed -i — «s/user nginx;/user $;/» /etc/nginx/nginx.conf
#NODE
ARG WEB_USER_ID=33
ARG WEB_USER_NAME=www-data
RUN echo «Building for web user: name=$»
RUN useradd -m -u $ $ || echo «User exists»
USER $
Аргументы имеют значения по умолчанию, которые соответствуют production окружению. Мы создаем пользователя в системе, потому что конфигурация nginx и php-fpm требуют указания имени пользователя, а не uid. С помощью `sed` меняем конфигурацию приложения, указывая, что сервис должен работать от имени указанного пользователя. Последней командой мы указываем, что контейнер, созданный на основе этого образа, должен работать от имени указанного пользователя.
Сервисы php и nginx удобно поднимать с помощью `docker-compose`. Для указания пользователя в build arguments на разных окружениях можно использовать docker-compose.override.yml:
app:
build:
context: ./app
args:
WEB_USER_ID: 1000
WEB_USER_NAME: roman
nginx:
build:
context: ./nginx
args:
WEB_USER_ID: 1000
WEB_USER_NAME: roman
Контейнер с node, который используется с целью сборки через npm, не останется поднятым через docker-compose, поэтому отдельно соберем для него образ с аргументами ‘—build-arg’ и будем его запускать:
docker build -t project_node ./node —build-arg WEB_USER_ID=»$WEB_USER_ID» —build-arg WEB_USER_NAME=»$WEB_USER_NAME»
docker run —rm \
-v `pwd`:/var/www/project \
-w /var/www/project/app \
-e «HOME=/var/node» \
project_node bash
Здесь используются переменные bash-скрипта `$WEB_USER_ID` и `$WEB_USER_NAME`, определить которые можно несколькими способами. В разделе ниже о скрипте первой настройки есть пример получения значений этих переменных из ранее поднятого контейнера.
После сборки образов с указанием id и именем пользователя не требуется указывать дополнительно еще раз пользователя при использовании команд `docker-compose exec` и `docker run`, если только не нужно выполнить команду от имени root-пользователя.
Права на доступ к записи в некоторые каталоги
Некоторые программы внутри контейнера могут пытаться писать в директории, доступа к которым у них нет у нового пользователя. Это директории не внутри домашней директории нового пользователя.
Если эта директория не монтируется с хост-системы, то права на запись можно дать отдельной командой в Dockerfile:
Если же директория монтируется, то права на запись в нее будет только у root-пользователя. В этом случае изменять права нужно отдельной командой сразу после старта контейнера. Например, добавить в скрипт первой настройки:
Скрипт первой настройки
При первом развертывании проекта на машине после сборки образов и поднятии сервиса я предлагаю выполнять init-скрипт:
echo «Permissions»
WEB_USER_ID=»$(docker-compose exec -T app id -u | tr -d ‘[:space:]’)»
WEB_USER_NAME=»$(docker-compose exec -T app id -u -n | tr -d ‘[:space:]’)»
if [ -z «$» ] || [ -z «$» ]; then
echo «Cannot get user id or name. Exit.»
exit 1
fi;
echo «Using WEB_USER_ID=$WEB_USER_ID, WEB_USER_NAME=$WEB_USER_NAME»
docker-compose exec -T —user root app chown -R «$WEB_USER_ID» ./
Скрипт получает из образа id пользователя, под которым работает сервис, затем устанавливает для всех файлов проекта владельца по этому uid.
При регулярном развертывании на production следует учесть, что при вышеуказанном подходе владельцем всех файлов проекта должен быть тот же пользователь, что указан в аргументах сборки, например, www-data (33). Здесь многое зависит о того, как организован deploy. Например, либо сразу запускать команды git от имени того же пользователя, либо потом менять владельца полученных новых файлов.
Заключение
Получаем, что web-сервер и команды из консоли выполняются от имени хост-пользователя, а значит, ни у какой из этих частей не возникнут проблемы с правами.