Для того чтобы быстро поднять рабочее окружение существует много способов. Один из них — поднять все необходимые сервисы в Docker-контейнерах. Чтобы ускорить создание новых проектов на Yii-framework я написал такую небольшую инструкцию, которую используют разработчики в нашей команде.


На старте у вас должны стоять docker, docker-compose, php и php-composer.
Создаем папку с проектом и в ней папку docker.


mkdir project-dir 
cd project-dir && mkdir docker

В папке docker создаем файл конфигурации нашего контейнера Dockerfile.


# Базовый образ с nginx и php
FROM richarvey/nginx-php-fpm

# Добавляем наше веб приложение
ADD app /var/www/app

# Удаляем конфиги сайтов которые там есть
RUN rm -Rf /etc/nginx/sites-enabled/*

# Добавляем наш конфиг
ADD docker/conf/nginx/site.conf /etc/nginx/sites-available/site.conf
# Включаем его
RUN ln -s /etc/nginx/sites-available/site.conf /etc/nginx/sites-enabled/site.conf

В этой же папке docker создаем docker-compose.yml для поднятия окружения разработки.


# Последняя версия docker-compose
version: '3'

# Создаем общую сеть deafult для всех контейнеров
networks:
  default:
    driver: bridge

# Создаем отдельные контейнеры
services:
  # Контейнер с веб-приложением
  app:
    # Собираем из Dockerfile 
    build: 
      # Корнем указываем корень основного проекта
      context: ../
      dockerfile: ./docker/Dockerfile
    # Показываем наружу 80 порт
    ports: 
      - "80:80"
    # Подключаем к общей сети с другими контейнерами
    networks: 
      - default
    # Запускаем только после db
    depends_on: 
      - db    
    # Линкуем внешнюю папку с исходниками внутрь
    volumes:
      - "../app:/var/www/app"
      # Так же линкуем конфиг для nginx
      - "./conf/nginx:/etc/nginx/sites-available"      
  # Контейнер с базой данных
  db:
    image: mysql:latest
    # Подключаем к общей сети с другими контейнерами
    networks: 
      - default
    # Показываем наружу порт
    ports:
      - "3336:3306"
    # Задаем параметры для инициализации БД
    environment:
      # Пароль к БД
      MYSQL_ROOT_PASSWORD: root
      # Создаваемая по умолчанию бд
      MYSQL_DATABASE: yii-template-db
    # Линкуем внешнюю папку для хранения БД
    volumes:
      - "./database:/var/lib/mysql"

Для nginx создаем папку docker/conf/nginx и файл site.conf в ней. Файлик может изменяться, в зависимости от того, как вы хотите настроить nginx на своем проекте. Его можно менять локально, т.к. он подключается через volume. Но надо не забывать внутри контейнера перезагружать nginx: nginx -s reload


server {
    charset utf-8;
    client_max_body_size 128M;

    listen 80; ## listen for ipv4

    root        /var/www/app/frontend/web/;
    index       index.php;

    access_log  /var/www/app/log/frontend-access.log;
    error_log   /var/www/app/log/frontend-error.log;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    # uncomment to avoid processing of calls to non-existing static files by Yii
    #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
    #    try_files $uri =404;
    #}
    #error_page 404 /404.html;

    # deny accessing php files for the /assets directory
    location ~ ^/assets/.*\.php$ {
        deny all;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php-fpm.sock;
        try_files $uri =404;
    }

    location ~* /\. {
        deny all;
    }
}

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


  • Выполняем команду создания проекта composer create-project --prefer-dist yiisoft/yii2-app-advanced app.
  • Запускаем окружение docker-compose -f docker/docker-compose.yml up -d
  • Инициализируем проект app/init --env=Development --overwrite=All
  • Открываем в редакторе файл app/common/config/main-local.php и заполняем его данными для подключения к БД. В примере у нас пароль root — root, хост БД — db, имя БД — yii-template-db.
  • Подключаемся к контейнеру docker exec -it docker_app_1 bash
  • Выполняем команду миграции БД php /var/www/app/yii migrate
  • Создаем папку для логов mkdir /var/www/app/log
  • И выходим exit
  • Тормозим сервис docker-compose -f docker/docker-compose.yml down
  • Запускаем его заново docker-compose -f docker/docker-compose.yml up -d
  • Открываем localhost в браузере и смотрим на новый сайт.

Upd: Обязательно стоит упомянуть, что всегда есть официальный Docker-образ Yii2.

Комментарии (14)


  1. Fantyk
    03.11.2018 22:56
    -2

    Думаю логично тут оставить ссылку на официальный yii2 docker образ.


    1. antaresm Автор
      04.11.2018 00:20
      +1

      Спасибо, ссылку добавил. Просто цель статьи была показать как создавать свои окружения в docker'e. Кроме того, в официальном докер образе все собрано в один контейнер (приложение и база), что не всегда удобно


  1. radist2s
    04.11.2018 02:00

    Я ни разу не являюсь специалистом по Docker, однако даже мне известно, что размещение более одного контейнера внутри докера, это нарушение самой сути контейнеров. Внутри контейнера supervisord забирает на себя все функции Docker'a. А процессами должен управлять сам Docker, следить за ними, показывать их адекватные метрики. Если очень хочется сделать такой бандл "все в одном", то пожалуйста — создавайте контейнер, внутрь которого пробрасываете сокет Docker'a, и внутри этого контейнера обычным docker-compose или без него запускайте сколько угодно контейнеров, связанных между собой, но только непосредственно через Docker! И когда будет нужно, можно делать так же docker exec на какой-то entrypoint, а от него пробрасывать команды на целевые контейнеры. Даже такой Docker внутри Docker это гораздо лучше, чем один контейнер с несколькими процессами, у которых Docker даже pid не знает, тогда зачем Docker нужен?


    Описанный в статье инструмент можно использовать только для хоумпейджей или для девелопинга. Но кому оно надо, если Docker используются только в более менее крупных проектах, или просто нормальными системщиками. Все прочие используют "сPanel" или максимум sftp. А те, кто хотят войти в мир Docker'а начитаются таких статей, и делают неправильно с самого начала.


    1. antaresm Автор
      04.11.2018 08:13

      1. А где именно тут «все в одном»?
      2. Да, статья именно про окружение для девелопинга


      1. radist2s
        04.11.2018 13:38

        Все в одном здесь вот где:


        FROM richarvey/nginx-php-fp

        Ну даже учитывая, что это окружение для разработки, то я все равно не вижу в этом смысла. Смысл докера в том, чтобы использовать для разработки то же самое окружение для разработки, что и для продакшена. То есть php должен быть скомпилирован точно так же как для дев окружения, как и для прода. Один и тот же, чтобы не было никаких нюансов. Например, для дева запросы по https работают нормально, а на проде нет, потому что для прода php скомпилирован с другой версией OpenSSL. Вот и ломай потом голову, что не так.


        1. antaresm Автор
          04.11.2018 14:07

          Я правильно понимаю, что разделив nginx и php в разные контейнеры мы получим относительно верный вариант?
          И что тогда делать с php-fpm? Его тоже отдельно, чтобы все было по канону?


          1. radist2s
            04.11.2018 15:26

            Правило простое: 1 процесс на 1 контейнер. Если нужно определять свой entrypoint, то внутри обязательно делать:


            #!/usr/bin/env sh
            # ...
            exec daemon-process

            Или если ваш демон не умеет нормально обрабатывать сигналы прерывания(SIGTERM, например), а такое случается, то можно использовать tini. Он и pid передаст куда следует, и за сигналами следить будет, а потом убивать запущенный им процесс. Так как tini добавили в Docker с версии 1.13, можно вообще просто добавить init: true для сервиса в docker-compose.yml, но тогда придется даунгрейднуть формат файла до версии 2.x, или наоборот проапргрейдить до 3.7. (Опцию --init запуска Docker'a в связи с поддержкой Swarm для docker-compose сначала удалили, а потом вернули) Между опцией init: true или модификацией параметров запуска контейнера фактически разницы нет, но в первом варианте вам вообще ничего с имеджем делать не нужно. Для нормальных имеджей ничего дополнительно делать не стоит, там уже все в порядке. Проверить же нормальность можно легко: выполняете docker-compose up и после завершайте по Ctrl + C. Если процесс умирает сразу, а не после задержки в несколько секунд, то внутри все правильно, если нет, попробуйте добавить init: true и процесс будет завершаться сразу, так как им уже будет управлять tini.


            Даже если вам нужно использовать cron, то, по-хорошему, его нужно запускать не на хосте, а в отдельном контейнере, в него пробрасывать сокет Docker'а, и им же что-то делать через docker-compose / docker. Естественно такой имедж нужно будет создавать с установленным внутри docker.


            1. cat_crash
              04.11.2018 16:32
              +1

              И это говорит человек который вначале декларирует:

              Я ни разу не являюсь специалистом по Docker
              . Страшно подумать что мог бы написать нам в ответ специалист Docker


            1. OnYourLips
              04.11.2018 16:33
              +1

              Правило простое: 1 процесс на 1 контейнер.
              Одна задача на контейнер. Не процесс. В документации докера этот пункт best practice недавно обновили и расширили.

              Тот же php-fpm — это набор процессов, но это не нарушает правило.
              Или apache + mod_php, хотя процессов много.


            1. antaresm Автор
              04.11.2018 16:57

              Да, согласен что по канону стоит вынести nginx в отдельный контейнер.
              Но вопрос в том, что стоит ли это делать, если есть гарантии что в продакшене будет точно такая же связка, которая точно не будет менять на протяжении всего проекта?


              1. Zhuravljov
                04.11.2018 18:24

                Дело даже не в том, чтобы именно по канону. Кроме прочего, просто так удобнее. В случае с разделенными php-fpm и nginx вы будете использовать официальные образы с поддержкой и актуальной документацией.


      1. SakhalinDDF
        04.11.2018 16:21

        Ну как минимум у вас в одном контейнере одновременно находится и nginx и php. А докер подход состоит в том, что бы каждый контейнер выполнял всего одну конкретную задачу, тоесть как минимум nginx и php должны разбиты на отдельные контейнеры. Если подразумевается использование каких то второстепенных сервисов — типа кэшируеющего или почтового сервера — то на них так же предлагается заводить свой отдельный сервис. К примеру на одном из поддерживаемых мной проектов вот такой список сервисов на текущий момент

        1. webserver (nginx)
        2. php
        3. events (nodejs)
        4. db
        5. cache (redis)

        При этом я честно говоря не смог с наскока вынести почтовый сервер в отдельный контейнер, поэтому подсадил его рядом с php (каюсь). Но обещаю исправиться =)


  1. iproger
    04.11.2018 03:33

    Сколько не встречаю обзоров, но никак пока не понял как развертывать приложение с существующим проектом из гита? В каждом руководстве create-project.
    Я вообще клонирую мастер и делаю composer install и т.к.


    1. antaresm Автор
      04.11.2018 08:13

      Вместо create-project, просто качаем исходники в папку app