Ниже рассмотрим сначала сам Composer (от PHP далёк, и с Composer дела раньше не имел, хотя сам PHP потрогать довелось), потом — пример сборки Docker контейнера и его использование под разными пользователями.
Composer предназначен для установки общих библиотек при создании PHP-проекта. Как ближайший аналог из «мира» NodeJS — npm, который выполняет установку различных зависимостей.
Кроме Composer для PHP имеется PEAR, но он сейчас практически не используется (хотя Composer поддерживает установку пакетов из PEAR).
Как и для npm — у Composer имеется файл, в котором перечисляются необходимые для установки зависимости и их версии, и репозитории.
Для Composer дефолтный репозиторий — https://packagist.org, кроме него в роли репозитория можно использовать любой VCS типа Github.
Установка Composer
Сначала — установим Composer локально.
На Linux выполняем:
curl -s https://getcomposer.org/installer | php
All settings correct for using Composer
Downloading...
Composer (version 1.6.3) successfully installed to: /home/setevoy/Work/composer.phar
Use it: php composer.phar
Проверяем тип файла:
file composer.phar
composer.phar: a /usr/bin/env php script executable (binary data)
Проверяем его работу:
php composer.phar
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Composer version 1.6.3 2018-01-31 16:28:17
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
...
Переносим в /usr/loca/bin:
sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer
Создадим тестовый проект:
mkdir ~/Scripts/PHP/ComposerTest
cd ~/Scripts/PHP/ComposerTest/
Например у нас есть зависимость от PHP фрейморка Slim — в каталоге PHP проекта создадим файл composer.json, в котором описываем зависимость:
{
"require": {
"slim/slim": "2.*"
}
}
Для npm дефолтным каталог является ~/node_modules:
npm root
/home/setevoy/node_modules
А для composer — vendor в каталоге, из которого вызвается composer.
Запускаем установку:
composer install
Loading composer repositories with package information
slim/slim suggests installing ext-mcrypt (Required for HTTP cookie encryption)
slim/slim suggests installing phpseclib/mcrypt_compat (Polyfil for mcrypt extension)
Writing lock file
Generating autoload files
Проверяем каталог vendor:
tree vendor/
vendor/
├── autoload.php
├── composer
│ ├── autoload_classmap.php
│ ├── autoload_namespaces.php
│ ├── autoload_psr4.php
│ ├── autoload_real.php
│ ├── autoload_static.php
│ ├── ClassLoader.php
│ ├── installed.json
│ └── LICENSE
└── slim
└── slim
├── composer.json
├── CONTRIBUTING.md
├── index.php
├── LICENSE
├── phpunit.xml.dist
├── README.markdown
├── Slim
│ ├── Environment.php
│ ├── Exception
...
В случае, если установку надо выполнить от другого пользователя, что бы установленные файлы имели другой UID/GID — используем, например, sudo:
sudo useradd phpcomposeruser
rm -rf vendor/
ls -l
total 12
-rw-r--r-- 1 setevoy setevoy 51 Mar 17 11:59 composer.json
-rw-r--r-- 1 setevoy setevoy 2192 Mar 17 12:01 composer.lock
sudo -u phpcomposeruser composer install
Cannot create cache directory /home/phpcomposeruser/.composer/cache/repo/https---packagist.org/, or directory is not writable. Proceeding without cache
Cannot create cache directory /home/phpcomposeruser/.composer/cache/files/, or directory is not writable. Proceeding without cache
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
slim/slim suggests installing ext-mcrypt (Required for HTTP cookie encryption)
slim/slim suggests installing phpseclib/mcrypt_compat (Polyfil for mcrypt extension)
Generating autoload files
Проверяем:
ls -l vendor/slim/
total 4
drwxr-xr-x 4 phpcomposeruser phpcomposeruser 4096 Mar 17 12:08 slim
Docker multi-stage билд
Следующей задачей будет собрать Docker образ, который будет включать в себя PHP и Composer.
Тут можно использовать multi-stage билды, которые появились в Docker в версии 17.05.
Идея заключается в том, что мы запускаем контейнер с Composer, затем — контейнер с желаемой версией PHP, копируем из контейнера с Composer его исполняемый phar-файл, и создаём новый контейнер, из которого собираем свой образ.
Dockerfile будет выглядеть так:
FROM composer:latest AS composer
FROM php:7.2.3
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN composer --version && php --v
В первой строке, в инструкции FROM, используем оператор AS, что бы задать имя запускаемому во время билда контейнеру, а затем — ссылаемся на это имя в COPY, что бы из него скопировать необходимый файл, в данном случае — /usr/bin/composer.
Failed to clone https://github.com/composer/ca-bundle.git, git was not found, check that it is installed and in your PATH env.
sh: 1: git: not found
Отлично! Всё работает. Вот только git-а нет 🙂
Т.к. просто скопировать исполняемый файл git — не вариант из-за зависимостей от системных библиотек, добавим его установку.
Обновляем Dockerfile:
FROM composer:latest AS composer
FROM php:7.2.3
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN apt update && apt install -y git
WORKDIR /app
Собираем образ:
docker build -t php-composer:3.0 .
...
Successfully tagged php-composer:3.0
И запускаем composer install:
docker run -ti --volume $(pwd)/:/app php-composer:3.0 composer install
Do not run Composer as root/super user! See https://getcomposer.org/root for details
Loading composer repositories with package information
Reading composer.json of antimattr/google-bundle (v2.0.0)
Failed to download composer/ca-bundle from dist: The zip extension and unzip command are both missing, skipping.
A php.ini file does not exist. You will have to create one.
Now trying to download from source
- Installing composer/ca-bundle (1.1.0): Cloning 943b2c4fca from cache
Failed to download maxmind/web-service-common from dist: The zip extension and unzip command are both missing, skipping.
A php.ini file does not exist. You will have to create one.
Now trying to download from source
- Installing maxmind/web-service-common (v0.5.0): Cloning 61a9836fa3 from cache
Failed to download maxmind-db/reader from dist: The zip extension and unzip command are both missing, skipping.
A php.ini file does not exist. You will have to create one.
Now trying to download from source
- Installing maxmind-db/reader (v1.3.0): Cloning e042b4f8a2 from cache
Failed to download geoip2/geoip2 from dist: The zip extension and unzip command are both missing, skipping.
A php.ini file does not exist. You will have to create one.
Now trying to download from source
- Installing geoip2/geoip2 (v2.8.0): Cloning 63b0d87d47 from cache
maxmind-db/reader suggests installing ext-bcmath (bcmath or gmp is required for decoding larger integers with the pure PHP decoder)
maxmind-db/reader suggests installing ext-gmp (bcmath or gmp is required for decoding larger integers with the pure PHP decoder)
maxmind-db/reader suggests installing ext-maxminddb (A C-based database decoder that provides significantly faster lookups)
Writing lock file
Generating autoload files
ОК, теперь всё работает.
Composer user
Последняя проблема, которую осталось решить — это пользователь:
ls -l
total 24
-rw-r--r-- 1 setevoy setevoy 142 Mar 17 14:18 composer.json
-rw-r--r-- 1 root root 8286 Mar 17 14:18 composer.lock
-rw-r--r-- 1 setevoy setevoy 155 Mar 17 14:14 Dockerfile
drwxr-xr-x 6 root root 4096 Mar 17 14:18 vendor
vendor и composer.lock создаются от рута, т.к. в самом контейнере используется пользователь root, если не указано другое.
Проверим — добавляем RUN whoami в Dockerfile:
FROM composer:latest AS composer
FROM php:7.2.3
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN apt update && apt install -y git
RUN whoami
WORKDIR /app
Собираем:
docker build -t php-composer:3.1 .
...
Step 5/6 : RUN whoami
---> Running in 8707eba18a3b
root
...
Successfully tagged php-composer:3.1
А мы хотим, что бы каталог vendor на хосте оставался за пользователем phpcomposeruser, которого мы создали в начале.
Тут есть два варианта.
Dockerfile USER
Первый — добавить пользователя phpcomposeruser во время сборки контейнера, и использовать инструкцию USER.
Обновляем Dockerfile:
FROM composer:latest AS composer
FROM php:7.2.3
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN apt update && apt install -y git
RUN adduser phpcomposeruser
USER phpcomposeruser
RUN whoami
RUN id
WORKDIR /app
docker run -ti --volume $(pwd)/:/app php-composer:3.2 composer install
Проверяем файлы:
ls -l
total 24
-rw-r--r-- 1 setevoy setevoy 142 Mar 17 14:37 composer.json
-rw-r--r-- 1 setevoy setevoy 8286 Mar 17 14:37 composer.lock
-rw-r--r-- 1 setevoy setevoy 222 Mar 17 14:34 Dockerfile
drwxr-xr-x 6 setevoy setevoy 4096 Mar 17 14:37 vendor
Упс! Откуда взялся setevoy, если мы указывали phpcomposeruser?
Вернёмся к логу сборки образа:
…
Step 8/9 : RUN id
—> Running in 288eab068311
uid=1000(phpcomposeruser) gid=1000(phpcomposeruser) groups=1000(phpcomposeruser)
…
А теперь проверим — кому принадлежит UID 1000 на хосте, с которого мы билдим:
getent passwd 1000
setevoy:x:1000:1000::/home/setevoy:/bin/bash
Отлично… Пользователь с UID 1000 в образе — это phpcomposeruser (первый созданный в системе пользователь, после рута), а на хосте — это пользователь setevoy.
Соответственно, на хосте, с которого запускаем билд, у пользователя phpcomposeruser == UID 1001:
Другой вариант решить проблему — запускать контейнер от определённого пользователя, используя --user.
Убираем USER из Dockefile, возвращаем его к виду:
FROM composer:latest AS composer
FROM php:7.2.3
COPY --from=composer /usr/bin/composer /usr/bin/composer
RUN apt update && apt install -y git
WORKDIR /app
Собираем версию 3.3:
docker build -t php-composer:3.3 .
Удаляем каталог и файл:
!685
sudo rm -rf vendor/ && sudo rm composer.lock
И пробуем собрать проект, передав --user phpcomposeruser:
docker run --user phpcomposeruser -ti --volume $(pwd)/:/app php-composer:3.3 composer install
docker: Error response from daemon: linux spec user: unable to find user phpcomposeruser: no matching entries in passwd file.
Снова «упс»? 🙂
Логично — мы ведь не создавали пользователя phpcomposeruser в образе php-composer:3.3:
docker run -ti php-composer:3.3 cut -d: -f1 /etc/passwd
root
daemon
bin
sys
sync
games
man
lp
mail
news
uucp
proxy
www-data
backup
list
irc
gnats
nobody
_apt
Поэтому как вариант решения — мы можем передать в контейнер UID и GID вместо имени пользователя — используем id -u (print only the effective user ID) и -g (print only the effective group ID).
Меняем владельца composer.json на phpcomposeruser, который будет владельцем проекта: