Рубріки: Теория

Как сделать Composer Install внутри Docker-контейнера

Сергей Почекутов

Задача этого материала — максимально предметно показать, как подготовить образ Docker с PHP Composer. Также мы будем делать multi-stage-билды. Конфигурации могут быть разными, поэтому посмотрим на общую концепцию и несколько примеров, а для закрепления создадим на практике приложение на Laravel, работающее в трех служебных контейнерах. Это практический how-to — здесь минимум теории, но максимум пошаговых инструкций, поэтому открываем свои системные терминалы и погнали следом за мной.

Содержание:
1. Что такое PHP Composer
2. Установка Composer
3. Docker multi-stage билд
4. Репозитории в composer.json
5. Composer user
6. Пример проекта на Laravel
Заключение

1. Что такое PHP Composer

Composer — инструмент для установки библиотек при разработке проектов на PHP. Его аналоги в других языках — npm для Node.js, pip для Python и другие. Все они используются для того, чтобы разворачивать проекты вместе с зависимостями.

Необходимые для установки зависимости и их версии указываются в специальном файле. В случае с Composer это composer.json.

2. Установка Composer

Чтобы установить Composer на Linux, запустите терминал и выполните команду:

curl -s https://getcomposer.org/installer | php

Затем проверьте тип файла:

file composer.phar

Убедитесь, что Composer работает:

php composer.phar

В выводе отобразится версия, а также рекомендации по использованию и справка.

Перенесите Composer в /usr/local/bin:

sudo mv composer.phar /usr/local/bin/composer
sudo chmod +x /usr/local/bin/composer

Теперь создайте тестовый проект и переключитесь в папку с ним:

mkdir ~/Scripts/PHP/ComposerTest
cd ~/Scripts/PHP/ComposerTest/

Можно начинать добавление зависимостей. Допустим, в этом проекте используется микрофреймворк Slim. Чтобы разворачивать его, нужно добавить зависимость в composer.json:

{
"require": {
"slim/slim": "2.*"
}
}

Запустите установку Composer:

composer install

Проверьте каталог vendor. Это дефолтная директория для Composer:

tree vendor/
    Output:
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
...

Внутри — Composer и микрофреймворк Slim, который вы указали в списке зависимостей проекта.

3. Docker multi-stage билд

Следующая задача — сборка Docker-образа, в котором будут PHP и Composer. Используйте для этого multi-stage builds, которые появились в Dicker 17.05. Подробнее о них вы можете узнать из документации.

Схема развертывания будет такой:

  1. Запускаете контейнер с Composer.
  2. Запускаете контейнер с нужной версией PHP.
  3. Копируете из контейнера с Composer phar-файл.
  4. Создаете новый контейнер, из которого собираете образ.

Внутри 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

В первой строке вы задаете имя, на которое затем ссылаетесь в третьей строке при копировании.

Соберите образ:

docker build -t php-composer:1.0 .

Чтобы убедиться, что все работает, проверьте версию:

docker run -ti php-composer:1.0 --version

4. Репозитории в composer.json

В качестве практики установите библиотеку, используя собранный образ. Для примера возьмем geoip2.

Обновите файл composer.json. Добавьте в него ссылку на репозиторий, из которого можно скачать библиотеку geoip2.

{
    "require": {
        "geoip2/geoip2": "~2.0"
    }, 
    "repositories": [{
        "type": "vcs",
        "url": "git@github.com:antimattr/GoogleBundle.git"}
    ]
}

Затем обновите Dockerfile. Добавьте в него Workdir и укажите каталог, в котором Composer найдет файл composer.json:

FROM composer:latest AS composer
FROM php:7.2.3
COPY --from=composer /usr/bin/composer /usr/bin/composer
WORKDIR /app

Соберите вторую версию образа:

docker build -t php-composer:2.0 .

Проверьте версию Composer, чтобы убедиться, что сборка прошла успешно:

docker run -ti --volume $(pwd)/:/app php-composer:2.0 composer --version

Удалите файл composer.lock, который остался от установки фреймворка Slim:

rm composer.lock

Запустите контейнер, передав команду composer install:

docker run -ti --volume $(pwd)/:/app php-composer:2.0 composer install

Сборка должна быть успешной за исключением того, что нет 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 .

Запустите composer install:

docker run -ti --volume $(pwd)/:/app php-composer:3.0 composer install

Теперь ошибок быть не должно.

5. Composer user

Осталось решить проблему с пользователем. Vendor и composer.lock создаются от root. Если не указано другое, то в самом контейнере используется тоже root. Это легко проверить:

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 .
Output:
Step 5/6 : RUN whoami
---> Running in 8707eba18a3b
root
...
Successfully tagged php-composer:3.1

Если вы хотите, чтобы vendor оставался за тем пользователем, которого вы создали сами, то можно использовать два пути. Первый — с помощью инструкции USER добавить нужного пользователя во время сборки контейнера. Второй — запускать контейнер от пользователя, используя --user.

Dockerfile USER

Обновите Docker-файл:

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 build -t php-composer:3.2 .

Удалите каталог vendor и файл composer.lock:

sudo rm -rf vendor/ && sudo rm composer.lock

Соберите проект, используя версию 3.2:

docker run -ti --volume $(pwd)/:/app php-composer:3.2 composer install

Проверьте файлы:

ls -l
Output:
-rw-r--r-- 1 sample sample  142 Dec 13 15:21 composer.json
-rw-r--r-- 1 sample sample 8286 Dec 13 15:21 composer.lock
-rw-r--r-- 1 sample sample  222 Dec 13 15:18 Dockerfile
drwxr-xr-x 6 sample sample 4096 Dec 13 15:21 vendor

Здесь может быть неверно определено имя пользователя. Если это так, вернитесь к логам сборки. Обратите внимание на Step 8/9. Там указан UID пользователя.

UID 1000 — это дефолтный пользователь на хосте. Тот пользователь, которого вы создали, будет иметь UID 1001. Его права на каталог vendor ограничены.

Запуск контейнера от пользователя

Второй способ — запускать контейнер от конкретного пользователя с помощью --user.

Уберите из Docker-файла USER. Верните первоначальный вид:

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 .

Удалите каталог vendor и файл composer.lock:

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.

Это связано с тем, что вы не создали пользователя с таким именем в образе php-composer:3.3. Чтобы устранить эту проблему, можно передать в контейнер UID и GID вместо имени пользователя.

Поменяйте владельца composer.json на phpcomposeruser:

sudo chown phpcomposeruser composer.json

Соберите проект:

docker run --user $(id -u phpcomposeruser):$(id -g phpcomposeruser) -ti --volume $(pwd)/:/app php-composer:3.3 composer install

Проверьте, что все работает:

ls -l
total 24
-rw-r--r-- 1 phpcomposeruser sample  142 Dec 13 14:54 composer.json
-rw-r--r-- 1 phpcomposeruser phpcomposeruser 8286 Dec 13 14:59 composer.lock
-rw-r--r-- 1 sample sample 155 Dec 13 14:44 Dockerfile
drwxr-xr-x 6 phpcomposeruser phpcomposeruser 4096 Dec 13 14:59 vendor

Готово, теперь образ собирается не от root, а от имени нового пользователя.

6. Пример проекта на Laravel

Для закрепления навыков создайте демонстрационное приложение на Laravel, которое будет работать в трех служебных контейнерах.

Пример Dockerfile такого проекта:

FROM php:7.4-fpm

# Arguments defined in docker-compose.yml
ARG user
ARG uid

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
    chown -R $user:$user /home/$user

# Set working directory
WORKDIR /var/www

USER $user

Структура простая:

  • Определите базовый образ, который будет использован. В данном случае это php:7.4-fpm.
  • Установите системные пакеты и расширения PHP.
  • Установите Composer из официального релиза.
  • Создайте нового пользователя и настройте параметры его работы.
  • Задайте рабочую папку по умолчанию.
  • Переключитесь на созданного пользователя.

Последние два параметра гарантируют, что вы будете подключаться как обычный пользователь и находиться в правильной директории.

Настройка nginx и MySQL

Создайте папку для хранения конфигурационных файлов nginx:

mkdir -p docker-compose/nginx

Создайте файл конфигурации nginx docker-compose/nginx/travellist.conf.

Пример его содержимого:

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

Файл указывает nginx на необходимость слушать порт 80 и использовать index.php в качестве страницы по умолчанию.

Чтобы настроить БД, откройте для общего доступа дамп. Он будет импортирован при инициализации контейнера.

Создайте новую папку:

mkdir docker-compose/mysql

Создайте и откройте файл docker-compose/mysql/init_db.sql, вставьте в него следующий код:

DROP TABLE IF EXISTS `cities`;

CREATE TABLE `cities` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `visited` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `cities` (name, visited) VALUES ('Kyiv',1),('Moscow',1),('Minsk',1),('Riga',0),('Helsinki',0),('London',0),('Paris',1),('Oslo',1),('Cancun',0),('Beijing',0);

Создание мультиконтейнерной среды

Используйте Docker Compose, чтобы создать мультиконтейнерную среду. В ней службы могут использовать общие сети и тома хранения данных.

Для настройки мультиконтейнерной среды нужно создать docker-compose.yml. Так выглядит готовый файл для нашего проекта:

version: "3.7"
services:
  app:
    build:
      args:
        user: sample
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travel
    container_name: travel-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travel

  db:
    image: mysql:5.7
    container_name: travel-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travel

  nginx:
    image: nginx:alpine
    container_name: travel-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d/
    networks:
      - travel

networks:
  travel:
    driver: bridge

Это лишь пример того, что должно быть внутри. В файле определены три службы: app, db и nginx. Служба app будет строить образ на базе созданного ранее Dockerfile. Контейнер запустит сервер php-fpm для синтаксической проверки кода. Результаты вернутся в службу nginx. Она работает в отдельном контейнере. Служба mysql определяет контейнер с запущенным сервером MySQL. Все службы соединены в сеть.

Запуск приложения

Соберите образ app:

docker-compose build app

Запустите среду в автономном режиме:

docker-compose up -d

Установите зависимости приложения:

docker-compose exec app composer install

Сгенерируйте уникальный ключ приложения с помощью artisan. Он нужен для шифрования пользовательских сеансов и других критичных данных.

docker-compose exec app php artisan key:generate

Откройте браузер и перейдите на ваш сервер или локальный хост с портом 8000:

http://server_domain_or_IP:8000

Убедитесь, что приложение работает.

Если вы хотите поставить среду Docker Compose на паузу, выполните команду:

docker-compose pause

Состояние всех служб сохранится. Чтобы запустить Compose заново, выполните:

docker-compose unpause

Чтобы закрыть Compose, удалить все контейнеры, сети и тома, выполните:

docker-compose down

Все команды Compose можно посмотреть в документации.

Заключение

Мы рассмотрели лишь общие настройки, которые помогают сделать Composer Install внутри Docker-контейнера. В зависимости от особенностей проекта конфигурация меняется.

Чтобы практиковаться с созданием приложения было проще, посмотрите это дополняющее текст видео:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023