image

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

Сразу оговорюсь, эта статья так сказать «от новичка новичку», поэтому постараюсь подробно рассказать обо всех сложностях и вопросах, которые у меня возникли в процессе настройки окружения в Docker.

Добро пожаловать под кат!

Итак, все что я опишу ниже, я буду делать на ноутбуке известной «фруктовой компании», но так как ранее я делал тоже самое на VDS под управлением Centos 7, то я буду делать небольшие лирические отступления с описание, как я это делал на VDS.

Начнем мы естественно в регистрации на docker hub, который будет выступать в качестве системы контроля версий, но только для наших контейнеров. Docker hub при бесплатном использовании позволяет иметь только 1 приватный репозиторий, поэтому мы будем каждый отдельный образ помечать соответствующими тегами — nginx, php7-fpm. Я не буду описывать создание репозитория, думаю, ни у кого с этим проблем не возникнет.

Теперь мы можем установить сам docker на нашу рабочую станцию — в моем случае это описание находится здесь.

При установке Docker на Mac у нас сразу устанавливается docker toolbox, в котором есть необходимый нам инструмент docker-compose. Мы будем использовать его для объединения наших контейнеров о общее окружение.

Установка docker-compose на Centos 7
К сожалению, на VDS мне пришлось устанавливать его отдельно через pip.
yum -y install python-pip
pip install docker-compose

Далее логинимся в нашем Докере:

docker login

Теперь нам доступны наши приватные репозитории (правда там пока пусто), там правда сейчас пусто, но мы это скоро исправим:)

Для своего проекта я сделал следующую структуру файлов:

+-- contaners                    # Директория с кастомными образами для docker
¦   +-- fpm
¦   ¦   +-- Dockerfile
¦   ¦   L-- conf
¦   ¦       L-- fpm.conf        # Необходимые настройки fpm, у меня файл пустой:)
¦   L-- nginx
¦       +-- Dockerfile
¦       L-- conf
¦           L-- nginx.conf
+-- database                     # Директоря для хранения баз данных
+-- docker-compose.yml
+-- logs                         # Директоря для хранения логов
L-- php-code                     # Директоря с php кодом
    +-- html
    L-- index.php

Зачем нужна php-code/html
При отсутствии директории html образ контейнера nginx не запуститься, где-то я находил, что проблема решается указанием WORKDIR в Dockerfile, но эта директория все-равно понадобится.

Для проекта я буду использовать кастомные образы для nginx и fpm, поэтому их я вынес в отдельные директории. Кастомные образы описываются при помощи Dockerfile. Вот мои:

./contaners/fpm/Dockerfile
FROM php:fpm
MAINTAINER nickname <my-email@domain>

RUN apt-get update && apt-get install -y         libmcrypt-dev         && apt-get install -y libpq-dev         && docker-php-ext-install -j$(nproc) mcrypt         && pecl install mongodb
        && docker-php-ext-enable mongodb
RUN docker-php-ext-install mbstring
RUN docker-php-ext-install exif
RUN docker-php-ext-install opcache

RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql     && docker-php-ext-install pgsql pdo_pgsql
COPY conf/ /usr/local/etc/php-fpm.d/
CMD ["php-fpm"]


./contaners/nginx/Dockerfile

FROM nginx:latest
MAINTAINER nickname <my-email@domain>
COPY ./conf /etc/nginx/conf.d/


./contaners/nginx/conf/nginx.conf
server {
    listen 80;
    index index.php index.html;
    server_name localhost;
    error_log  /etc/logs/nginx/nginx_error.log;
    access_log /etc/logs/nginx/nginx_access.log;
    root /var/www;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass fpm:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

Скрипт из официального образа PHP от Docker Hub позволяет легко установить нужные расширения:
docker-php-ext-install

или

docker-php-ext-enable  # в случае, если расширение уже установлено (как у меня через pecl)

Вся подготовительная работа проведена проведена, теперь надо описать как наши контейнеры будут взаимодействовать. Как я уже писал, делать это мы будем через docker-compose и правила взаимодействия нужно описать в файле docker-compose.yml. Вот мой:

./docker-compose.yml
nginx:
 dockerfile: ./Dockerfile # путь до докер файла указываем относительно директории в build
 build: ./contaners/nginx
 ports:
  - 80:80
 volumes:
  - ./logs:/etc/logs/nginx
 volumes_from:
  - fpm:rw
 environment:
  - NGINX_HOST=localhost
  - NGINX_PORT=80
 command: nginx -g "daemon off;" # Можно было указать в докер-фале, но можно и здесь)
 links:
  - fpm
fpm:
 dockerfile: ./Dockerfile
 build: ./contaners/fpm
 volumes:
  - ./php-code:/var/www:rw

Замечание по поводу указания относительных путей
Если мы указываем относительные пути к файлам и деректориям, то они должны обязательно начинаться с точки (как указание текущей директории), т.е., например, такой путь contaners/nginx/Dockerfile — будет интерпретирован не верно.

Теперь можно запустить наши контейнеры:

docker-compose up -d

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

Все, наши контейнеры работают и линкуются, но кое-что я сделал не так, а именно — мы для запуска контейнера всегда ссылаемся на Dockerfile, это не очень удобно. Сделаем так:

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                NAMES
2d6263b52380        test_nginx          "nginx -g 'daemon off"   8 minutes ago       Up 8 minutes        443/tcp, 0.0.0.0:8080->80/tcp        test_nginx_1
04370a9e1c73        test_fpm            "php-fpm"                8 minutes ago       Up 8 minutes        9000/tcp                             test_fpm_1

docker tag 2d6263b52380 my-login/repo:nginx
docker tag 2d6263b52380 my-login/repo:fpm

docker push my-login/repo:nginx
docker push my-login/repo:php7-fpm

Теперь наши контейнеры управляются через docker hub и Dockerfile'ы нам больше не нужны.
Исправим docker-compose.yml:

./docker-compose.yml
nginx:
 image: my-login/repo:nginx
 ports:
  - 80:80
 volumes:
  - ./logs:/etc/logs/nginx
 volumes_from:
  - fpm:rw
 environment:
  - NGINX_HOST=localhost
  - NGINX_PORT=80
 command: nginx -g "daemon off;" # Можно было указать в докер-фале, но можно и здесь)
 links:
  - fpm
fpm:
 image: my-login/repo:php7-fpm
 volumes:
  - ./php-code:/var/www:rw

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

docker exec -it 04370a9e1c73 bash

И добавить необходимое расширение:

docker-php-ext-install pcntl

Отлично, в контейнер добавили, но мы же хотели использовать docker hub в качестве VCS — значит надо закомитить изменения:

docker commit -m "added pcntl ext" 04370a9e1c73 my-login/repo:php7-fpm

и запушить в репозиторий:

docker push my-login/repo:php7-fpm

Добавим еще контейнеры баз данных (postgresql и mongodb):

./docker-compose.yml
nginx:
 image: my-login/repo:nginx
 ports:
  - 80:80
 volumes:
  - ./logs:/etc/logs/nginx
 volumes_from:
  - fpm:rw
 environment:
  - NGINX_HOST=localhost
  - NGINX_PORT=80
 command: nginx -g "daemon off;"
 links:
  - fpm
fpm:
 image: my-login/repo:php7-fpm
 volumes:
  - ./php-code:/var/www:rw
links:
  - mongo
  - postgres
mongo:
 image: mongo
 ports:
  - 27017:27017 # Проброс портов для внешнего доступа 
 volumes:
 - ./database/mongo:/data/db
postgres:
 image: postgres:latest
 ports:
  - 5432:5432 # Проброс портов для внешнего доступа 
 volumes:
  - ./database/postgres:/data/postgres
 environment:
  POSTGRES_PASSWORD: <myPassword>
  POSTGRES_USER: postgres
  PGDATA : /data/postgres

И теперь выполняем:

docker-compose up -d

Докер добавит нам новые контейнеры к уже запущенным. Но я открыли порты для внешнего доступа, но мы указали пароль только для PostgreSql, нужно тоже самое сделать и для mongodb — как это сделать (и не только) подробно описано здесь.

Add the Initial Admin Use For MongoDB
docker exec -it some-mongo mongo admin
connecting to: admin
> db.createUser({ user: 'jsmith', pwd: 'some-initial-password', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] });
Successfully added user: {
    "user" : "jsmith",
    "roles" : [
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        }
    ]
}


Базы данных из php теперь доступны по хостам postgres и mongo соответственно, т.е. для подключения, например, к mongodb мы должны написать следующее:

$manager = new MongoDB\Driver\Manager("mongodb://mongo:27017");

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

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


  1. arteast
    13.12.2016 16:46
    +1

    > docker exec -it 04370a9e1c73 bash
    > docker-php-ext-install pcntl
    > docker commit -m «added pcntl ext» 04370a9e1c73 my-login/repo:php7-fpm

    Вопрос от новичка — разве так можно? Ведь теперь образ не соответствует тому, что написано в Dockerfile. Что будет, если я завтра решу пересоздать образ из Dockerfile?


    1. jced
      13.12.2016 19:36

      Я в таких случаях правлю докер-файл и перезапускаю контейнер. Но, это на этапе настройки.
      Думаю автор здесь просто показал такую возможность, иначе бы он поправил это до публикации статьи в докер-файле:)


      1. grSereger
        14.12.2016 00:49

        Да, спасибо)

        Вопрос от новичка — разве так можно? Ведь теперь образ не соответствует тому, что написано в Dockerfile. Что будет, если я завтра решу пересоздать образ из Dockerfile?

        Вы вернетесь к первоначальному состоянию.

        А представьте, что те изменения, которые вы накатили — были не нужны? Снова изменте Dockerfile? — если изменений не много это будет легко, а иначе — посложнее.
        Собственно я к тому, что «слоистая архитектура» докера тем и хороша, что при изменениях контейнера достаточно эти изменения закомитить (при необходимости вернуться на одно из предыдущих состояний) и там где еще используется этот контейнер нужно будет просто сделать пулл — в таком случае не нужно полностью собирать контейнер заново. Т.е. некая реализация vcs, а Dockerfile для первоначальной настройки.
        Но как я уже писал — и только погружаюсь эту тему)


  1. mizhgun
    14.12.2016 00:30
    +2

    Установка расширений для PHP в докере делается черезвычайно просто:

    В докере установка расширений для PHP вообще не предусмотрена. То, на что вы ссылаетесь — скрипт из официального образа PHP от Docker Hub, который хоть и является самым популярным, отнюдь не единственный.


    1. grSereger
      14.12.2016 00:34

      Да, вы совершенно правы, спасибо!


  1. Freezy
    14.12.2016 09:01
    +1

    docker tag 2d6263b52380 my-login/repo:nginx
    docker tag 2d6263b52380 my-login/repo:fpm

    Тут небольшая ошибка. для fpm другой id контейнера.

    И в догонку вопрос. Зачем вы указываете NGINX_HOST и NGINX_PORT если в nginx.conf они у вас уже прописаны вручную?
    Как я понимаю, их имеет смысл прописывать если у вас специальный template для nginx.conf. И нужно будет использовать другую команду что бы эти значения заменить. ( command: /bin/bash -c «envsubst < /etc/nginx/conf.d/mysite.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'»
    )


  1. metro2030
    14.12.2016 10:38

    А почему не используете Alpine для основы контейнеров? Он же гораздо легче. У меня крутится NextCloud и nginx именно на нем. Удалось сильно увеличить объём свободного пространства.


    1. hudson
      14.12.2016 11:20
      +1

      А вот можете пояснить что это такое? Я использую знакомые оси в качестве основы — ubuntu, debian. А этот alpine он кто такой? Тоже deb-based?


      1. foxmuldercp
        18.12.2016 00:26

        Мелкий образ линукса для почти всего. образы убунт и прочих центосей — 300+ метров влегкую..


        1. hudson
          18.12.2016 14:09

          Насчёт мелкого как раз понятно — я к тому как его готовить? Я люблю и уважаю CentOS, работая с Docker в принципе не проблема начать работать с deb-based дистрибутивами. Но под alpine не придётся ли переучиваться? Я всё-таки разработчик, а не сисадмин…


          1. foxmuldercp
            19.12.2016 17:16

            Выучить команду "обновить ось" и "поставить пакет такой-то"? По моему больше ничего не надо.
            весь end user/server софт что под гентой, что под слакой, что под фрями давно ничем не отличается — бд, ни веб сервера и почтовые везде одинаковы.


  1. kxl
    14.12.2016 12:37
    -1

    Немного непонятно наличие mongodb при наличии postgresql… Я о том, что Postgresql умеет то, что умеет mongodb — хранить и работать с JSON


    1. jced
      14.12.2016 12:44

      Довольно частый микс! Из того, что встречал чаще всего: реляционка используется для структурированных данных с преимущественным чтением, а монгу частенько используют как свалку логов, она быстра на запись и не требует структуризации данных.


  1. grimskin
    22.12.2016 20:28

    Новичковый вопрос. Столкнулся с тем, что в связке nginx+php-fpm в докере на osx получаю непонятную задержку порядка полутора секунд при каждом запросе. Googling StackOverlow не помог. Собственно, вот сам вопрос на SO — http://stackoverflow.com/questions/40832825/how-to-figure-out-what-slows-down-docker
    Буду признателен, если чем-то сможете помочь.