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


В данном посте поговорим про docker-compose, который по моему мнению является панацеей в вопросе организации и оптимизации рабочего процесса разработчика.


Описывать все я буду почти на пальцах, поэтому если вы до этого ни разу не слышали про docker (что странно), ни разу с ним не работали и хотите в нем разобраться, то прошу под кат.


Предисловие


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


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


Те, кто считают иначе — это ваше полное право, но только не нужно навязывать свою точку зрения.
Спасибо!


Docker


Технология, позволяющая легко (во всех смыслах) разворачивать необходимое рабочее окружение.


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


Docker-compose


Пакетный менеджер (по аналогии с composer и npm, только у docker — контейнеры), позволяющий описывать необходимую структуру в одном файле (конфиге).
Подробнее также можно узнать из ссылок в конце статьи.


Docker-hub


Репозиторий контейнеров (по аналогии с packagist и npm).
Важное замечание: внимательно читайте описание к образу, 70-80% тупых вопросов там описано, не тратьте время на гугление.


Установка


Переписывать документацию docker я не стану, поэтому просто кину ссылки:



Установка обычного программного обеспечения (ПО), проблем возникнут не должно.
Если все же возникнут, то дальше можете не читать, вероятно вы случайно наткнулись на эту статью и разработку в целом...


Если вы устанавливали docker под Windows, то работать нужно через специальную консоль Docker Quickstart Terminal. После установки, на рабочем столе должен появиться соответствующий ярлык.

Структура проекта


Для начала определимся со структурой проектов:


  • project 1
  • project 2
  • project N
    • src
    • container 1
    • container 2
    • container N
    • docker-compose.yml

Каждый проект в обязательном порядке имеет docker-compose.yml и директорию src.
Также для каждого контейнера должна быть своя директория (совпадающая с названием контейнера), где будет храниться вся информация, необходимая для работы контейнера (конфиги, данные, ...).


CMD / Terminal


Для работы с docker и compose мы будем использовать всего несколько команд:


  • docker ps – просмотр всех контейнеров (подробнее),
  • docker-compose up --build – сборка проекта. Параметр build используется для того, чтобы заставить compose пересоздавать контейнеры. (подробнее).

Описание прочих команд, можно найти на официальном сайте.
Перейдем непосредственно к делу.


apache


https://hub.docker.com/_/httpd/


Начнем, пожалуй, с самого популярного сервера — Apache.
Создадим директорию проекта:


  • project
    • src
    • docker-compose.yml

Конфиг будет выглядеть таким образом:


docker-compose.yml
version: '3'

services:
  apache:
    image: httpd:2.4
    ports:
      - 80:80
    volumes:
      - ./src:/usr/local/apache2/htdocs

Что здесь происходит:


  • image: httpd:2.4 — указываем какой образ нам нужно и его версию (список доступных версий и модификаций можно посмотреть в соответствующем docker-hub).
  • ports: 80:80 — пробрасываем порты между docker и нашей машиной, т.е. все запросы которые будут идти на 80 порт нашей машины, будут транслироваться на 80 порт docker.
  • volumes: ./src:/usr/local/apache2/htdocs — линкуем директорию на нашей машине, с рабочей директорий apache, т.е. все файлы находящиеся в директории src, будут доступны для apache, как будто они лежат в директории htdocs (обратное тоже работает, все что создано в docker "копируется" на локальную машину).

Создадим файл src/index.html в рабочей директории с содержимым:


Hi, I'am Apache

Запускаем наш проект:


docker-compose up --build

Переходим в браузер по адресу ПК и наблюдаем приветствие нашего сервера.
Чтобы уничтожить контейнеры нашего проекта, достаточно в консоле выполнить Ctrl+C.


Если докер работает через VirtualBox, то нужно заходить по IP виртуалки. При любом раскладе, если вы работаете через Docker Quickstart Terminal, то адрес выведется при запуске консоли.

Работа в фоновом режиме


Если вам необходимо запустить docker и далее работать в консоле, то можно запустить проект в фоном режиме:


docker-compose up --build -d

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


docker ps

В моем случае получился такой вывод:


CONTAINER ID        IMAGE       ...
988e27da7bdf        httpd:2.4   ...

Теперь останавливаем и удаляем наш контейнер:


docker stop 988e27da7bdf
docker rm 988e27da7bdf

Либо можно поступить грубо и сразу удалить:


docker rm -f 988e27da7bdf

nginx


https://hub.docker.com/_/nginx/


Конфиг nginx строиться по той же самой схеме, что и apache: образ, порты, рабочая директория. Выглядит файл таким образом:


docker-compose.yml
version: '3'

services:
  nginx:
    image: nginx:1.13
    ports:
      - 80:80
    volumes:
      - ./src:/usr/share/nginx/html

Создаем файл src/index.html в рабочей директории с содержимым:


Hi, I'am Nginx

Заходим в браузер, и видим приветствие очередного сервера.


php + apache


https://hub.docker.com/_/php/


Если говорить про связку PHP и Apache, то для нее уже есть готовый образ, поэтому про линковку контейнеров будем говорить далее. А сейчас просто конфиг:


docker-compose.yml
version: '3'

services:
  web:
    image: php:7.2-apache
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html

Создаем файл src/index.php в рабочей директории с содержимым:


<?php

phpinfo();

Проверяем его работу в браузере.


php + nginx


https://hub.docker.com/_/nginx/
https://hub.docker.com/_/php/


В данной связке php будет в формате fpm. Схематично это выглядит таким образом:



Соответственно нам нужно будет переписать конфиг сервера.
Для этого нужно будет слинковать помимо рабочей директории, еще и файл конфигурации сервера:


docker-compose.yml
version: '3'

services:
  nginx:
    image: nginx:1.13
    ports:
      - 80:80
    volumes:
      - ./src:/usr/share/nginx/html
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - php

  php:
    image: php:7.2-fpm
    volumes:
      - ./src:/usr/share/nginx/html

Что изменилось:


  • volumes: ./nginx/nginx.conf:/etc/nginx/nginx.conf — линкуем файл конфига nginx;
  • depends_on: php — указываем зависимость nginx от php, т.е. по сути, это гарантия того что контейнер php запуститься раньше nginx.

Если мы не укажем depends_on, то можем словить подобную ошибку:


nginx_1  | 2018/01/05 08:56:42 [emerg] 1#1: host not found in upstream "php" in /etc/nginx/nginx.conf:23
nginx_1  | nginx: [emerg] host not found in upstream "php" in /etc/nginx/nginx.conf:23

Создаем файл /nginx/nginx.conf в директории нашего проекта. Сам конфиг выглядит таким образом:


nginx.conf
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        root   /usr/share/nginx/html;
        listen       80;
        server_name  localhost;

        location / {
            index  index.html index.htm;
        }

        location ~ \.php$ {
            fastcgi_pass   php:9000;
            fastcgi_index  index.php;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_path_info;
        }
    }
}

Все стандартное, главное, чтобы директива root совпадала с docker-compose.yml.
Глядя на конфиг, прошу обратить внимание на директиву fastcgi_pass, а именно на значение php:9000.
Обычно, когда настраивается локальный сервер указывается localhost:9000, НО т.к. php у нас находится в другом контейнере, то обращаться мы должны именно к нему (docker сам "подставит" нужный IP контейнера, по сути вся магия скрыта в простом дописывании файла hosts).
Чтобы это стало возможным, в наш docker-compose нужно добавить директиву links (хотя по факту не надо, подробнее).


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


  • project
    • src
      • index.php
    • nginx
      • nginx.conf
    • docker-compose.yml

Запускаем, проверяем, радуемся!


php + apache + nginx


https://hub.docker.com/_/nginx/
https://hub.docker.com/_/php/
https://hub.docker.com/_/httpd/


Наверное, самая популярная связка для веб-проектов. Схематично это выглядит так:



Пара замечаний:


  • php используется как php-fpm, потому что быстрее и моднее;
  • apache используется, потому что популярен и htaccess.

Для того чтобы все настроить нам нужно будет также слинковать конфиг apache, и таким образом docker-compose будет выглядеть так:


docker-compose.yml
version: '3'

services:
  apache:
    image: httpd:2.4
    volumes:
      - ./src:/var/www/html
      - ./httpd/httpd.conf:/usr/local/apache2/conf/httpd.conf
    depends_on:
      - php

  nginx:
    image: nginx:1.13
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - apache

  php:
    image: php:7.2-fpm
    volumes:
      - ./src:/var/www/html

Так как на просторах интернета я не нашел нормальный конфиг (оке, я просто не искал), а под рукой как раз docker, то решено было вытаскивать его из стандартного контейнера.
Уложилось все в 3 команды:


docker run -d httpd:2.4
docker ps
docker cp [ID контейнера]:/usr/local/apache2/conf/httpd.conf ./httpd.conf

После выполнения данных команд, в текущей директории появится файл httpd.conf, который мы и будем использовать в качестве основы.
По сути, это простое копирование из запущенного контейнера.
Создаем файл /httpd/httpd.conf в рабочей директории, который после правок выглядит так:


httpd.conf
ServerRoot "/usr/local/apache2"

Listen 80

LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
# additional
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

ServerAdmin you@example.com

<Directory />
    AllowOverride none
    Require all denied
</Directory>

DocumentRoot "/var/www/html"
<Directory "/var/www/html">
    Options Indexes FollowSymLinks Includes ExecCGI
    AllowOverride All
    Require all granted
</Directory>

<IfModule unixd_module>
    User daemon
    Group daemon
</IfModule>

<IfModule dir_module>
    DirectoryIndex index.php index.pl index.cgi index.asp index.shtml index.html index.htm                    default.php default.pl default.cgi default.asp default.shtml default.html default.htm                    home.php home.pl home.cgi home.asp home.shtml home.html home.htm
</IfModule>

<Files ".ht*">
    Require all denied
</Files>

<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
        LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>
</IfModule>

<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>

<Directory "/usr/local/apache2/cgi-bin">
    AllowOverride All
    Options None
    Require all granted
</Directory>

<IfModule headers_module>
    RequestHeader unset Proxy early
</IfModule>

<IfModule mime_module>
    TypesConfig conf/mime.types
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
    AddType text/html .shtml
    AddHandler cgi-script .cgi .pl .asp
    AddOutputFilter INCLUDES .shtml
</IfModule>

#
# Настройка FPM
#
<IfModule proxy_module>
    <FilesMatch "\.php$">
        SetHandler  "proxy:fcgi://php:9000"
    </FilesMatch>
</IfModule> 

Создаем файл /nginx/nginx.conf в рабочей директории со следующим содержимым:


nginx.conf
worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;

        location ~ \.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js)$ {
            root /var/www/html;
        }

        location ~ /\.ht {
            deny  all;
        }

        location / {
            proxy_pass http://apache;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_connect_timeout 120;
            proxy_send_timeout 120;
            proxy_read_timeout 180;
        }
    }
}

В строке proxy_pass http://apache мы опять указываем не IP адрес, а название контейнера (вспоминаем про магию с hosts).


Для тестинга, нам нужно будет проверить, работает ли PHP и работает ли Apache.
Сформируем такую структуру проекта:


  • nginx
    • nginx.conf
  • httpd
    • httpd.conf
  • src
    • protected
      • .htaccess
      • index.html
    • index.php
  • docker-compose.yml

Содержимое .htaccess:


Deny from all

Содержимое index.php:


<?php

phpinfo();

Содержимое index.html:


Apache not working :-(

Если все настроено корректно, то картина должна быть следующей:


  • /index.php — откроется информация о php
  • /protected/index.html — откроется 403 ошибка apache
  • /protected/.htaccess — откроется 403 ошибка nginx (визуально они отличаются)

mariadb + phpmyadmin


https://hub.docker.com/_/mariadb/
https://hub.docker.com/r/phpmyadmin/phpmyadmin/


Поговорим про базы данных.
Конфиг для подключения выглядит следующим образом:


docker-compose.yml
version: '3'

services:
  mariadb:
    image: mariadb:10.3
    restart: always
    volumes:
      - ./mariadb:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: qwerty

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    links: 
      - mariadb:db
    ports:
      - 8765:80
    environment:
      MYSQL_ROOT_PASSWORD: qwerty
    depends_on:
      - mariadb

Для mariadb и phpmyadmin мы указали директиву environment, которая содержит специфические для конкретного контейнера переменные (подробности можно посмотреть в репозиториях самих контейнеров).
В данном случае, это пароль для пользователя root.


Для phpmyadmin мы вручную указали директиву links:


links: 
      - mariadb:db

Сделать это необходимо для того, чтобы phpmyadmin знал с какой базой соединятся.
Если бы контейнер mariadb назывался db, то указывать эту директорию было бы не нужно.


Для mariadb мы слинковали директорию с данными:


volumes:
    - ./mariadb:/var/lib/mysql

Сделано это для того, чтобы данные хранились в директории нашего проекта, а не внутри контейнера.


Если вы работаете не на linux машине, то у вас возникнут проблемы с размещением данных базы на локальной машине.
Эти непреодолимое обстоятельство, к сожалению, на данный момент не преодолено.
У кого есть решение просьба поделиться.
Однако по умолчанию (даже после уничтожения контейнера), данные базы сохраняются и вы можете пересоздавать контейнер сколько угодно раз — данные сохранятся в недрах локальной машины.

php + apache + nginx + mariadb + phpmyadmin


https://hub.docker.com/_/nginx/
https://hub.docker.com/_/php/
https://hub.docker.com/_/httpd/
https://hub.docker.com/_/mariadb/
https://hub.docker.com/r/phpmyadmin/phpmyadmin/


Ну, а теперь совмещаем наши конфиги, и получаем неплохое веб-окружение:


docker-compose.yml
version: '3'

services:
  apache:
    image: httpd:2.4
    volumes:
      - ./src:/var/www/html
      - ./httpd/httpd.conf:/usr/local/apache2/conf/httpd.conf
    depends_on:
      - php

  nginx:
    image: nginx:1.13
    ports:
      - 80:80
    volumes:
      - ./src:/var/www/html
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - apache

  php:
    build:
      ./php
    volumes:
      - ./src:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
    depends_on:
      - mariadb

  mariadb:
    image: mariadb:10.3
    volumes:
      - ./mariadb:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: qwerty

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    links: 
      - mariadb:db
    ports:
      - 8765:80
    environment:
      MYSQL_ROOT_PASSWORD: qwerty
    depends_on:
      - mariadb

Для php мы добавили директиву build (подробнее), в которой указали директорию php, где хранится Dockerfile со следующим содержанием:


FROM php:7.2-apache
RUN apt-get update
RUN docker-php-ext-install pdo pdo_mysql mysqli

В данном файле мы обновляем репозитории и устанавливаем php модули (подробнее про docker-php-ext-install).
Также мы слинковали конфиг php, чтобы можно было его настроить нужным нам образом.
Содержимое php.ini можно взять, например, здесь.


Запускаем, проверяем, радуемся!
Если все сделано правильно, то index.php отработает без ошибок, а в директории project/mysql появятся служебные файлы базы.


Docker production


По данному вопросу к сожалению я ничего сказать не могу, зато может сказать официальная документация.
Если у вас есть опыт использования docker на боевых проектах, то просьба поделиться своим опытом в комментариях: стоит ли, какие трудности и подводные камни у вас возникли и др. полезную информацию для молодых и неопытных.


Заключение


Вот собственно и все, чем я хотел поделиться.
Как видите необязательно знать принцип работы docker, чтобы успешно с ним работать.
Да, конечно, для тонкой настройки или сложных задач, необходимо уже углубляться в дебри docker, но в средне-статистическом случае — это не нужно.
Если у вас есть что добавить, или вы заметили какой-то косяк, или вы с чем-то не согласны, то прошу в комментарии, обсудим ;-)


Полезные ссылки (a.k.a список используемой литературы)


Документация официального сайта
Overview of Docker Compose (официальный сайт)
Полное практическое руководство по Docker: с нуля до кластера на AWS
Поняв Docker
Полная автоматизация «development» среды с помощью docker-compose


P.S.


Если честно, я не понимаю откуда столько негатива.
Судя по комментариям, основные претензии к формулировкам и терминологии, и это с учетом того, что в предисловии я написал что умышленно упрощаю многие моменты.
Цель статьи — показать, как просто работать с docker-compose, как вместо того, чтобы разворачивать 100500 окружений и ПО для каждого своего проекта, можно использовать docker-compose и быть довольным.
Тут нет речи про prodUction (одного абзаца хватит), про deploy, про миграцию между dev и prod средой.
Нет, статья не об этом.


P.P.S.


Большое спасибо krocos Caravus Vershnik Fesor за дельные комментарии.

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


  1. Caravus
    05.01.2018 16:15

    Docker prodaction

    По данному вопросу к сожалению я ничего сказать не могу

    (орфография и пунктуация — авторская)
    Весь смысл использовать докер — переносимость окружения. Если вы не знаете как это всё запустить на проде — ценность статьи стремится к нулю.
    Я не просто так это пишу, это серьёзная проблема. Как выкатить это всё с дева на тест/прод, как дать понять программе что она теперь на проде (и не надо выводить отладочную информацию), как монтировать статику из php контейнера в apache/nginx контейнеры минуя хост (какой хост на проде-то?), как это всё масштабировать и т.д. и т.п.
    Если мы не знаем как это всё будет запущено на проде — как тестировать? Как убедиться что на проде наш код не превратится в тыкву?


    1. romy4
      05.01.2018 17:21

      собрать контейнер с export ENV=production?


      1. Caravus
        05.01.2018 17:33
        +1

        Контейнер должен быть один и тот же. Как вариант вашего предложения — запускать с нужными переменными. То есть на деве «ENV=DEV» на проде «ENV=PROD» и т.д.


      1. ADSapozhnikov
        05.01.2018 18:05
        +1

        Собирая разные контейнеры для Дев/Прод вы не сможете гарантировать, что в собранном через 5 минут Прод контейнере все установилось так же как и на Дев.
        Пример: зависимости которые вытягиваются через npm(майтейнер что-то обновил/удалил/недоступен репозиторий). И вуаля — у вас не консистентный прод контейнер.


        1. romy4
          05.01.2018 18:25

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


          1. ADSapozhnikov
            05.01.2018 19:05

            у вас все прибито гвоздями и нет внешних зависимостей, либо есть но все со своих зеркал?
            Несомненно, контейнер проходит все стадии QA. Но опять же, уберем npm — почему-то мне встречалось когда один и тот же пакет/билд фейлился каким-то образом при установке, чисто исходя из состояния звезд. rpm — может криво заехать из вашего нексуса и т.д.


        1. andreishe
          06.01.2018 00:09

          Так и настраивая окружение для прода иначе, чем для дева вы рискуете, что оно будет работать иначе (это вообще ожидаемое поведение) или работать не будет вообще.


    1. rapita
      06.01.2018 13:35

      Статья не про Deploy используя Docker. Она больше о том, что попробовать разные языки/утилиты через контейнеры — очень просто.


    1. Vershnik
      06.01.2018 13:35

      Для прода нужно чтобы не было статических ссылок на другие контейнеры в конфиг файлах nginx.conf и т.д.


      Значение хоста и порта в инструкциях типа SetHandler "proxy:fcgi://php:9000" должны приходить из ENV: SetHandler "${PHP_UPSTREAM}"


      Конфиги и все файлы из src должны "запекаться" в image при помощи ADD/COPY инструкций Dockerfile.


      Процесс надо разделить на 2 этапа. Сначала билдим все images, потом в docker-compose.yml ссылаемся на эти собранные image.


      docker build -t my-php:dev-SNAPSHOT ./php
      image: my-php:dev-SNAPSHOT

      Программа должна работать без "volumes" в docker-compose (Их можно опционально добавлять при девепоменте, чтобы изменения в файлах хоста автоматически переносились в запущенный контейнер и не надо было билдить и запускать новый image каждый раз когда надо увидеть внесенные в код изменнения)


      Далее надо использовать Rancher или Kubernetes чтобы это все дело оркестрировать на проде.


      1. rpsv Автор
        06.01.2018 13:47

        Программа должна работать без «volumes» в docker-compose (Их можно опционально добавлять при девепоменте, чтобы изменения в файлах хоста автоматически переносились в запущенный контейнер и не надо было билдить и запускать новый image каждый раз когда надо увидеть внесенные в код изменнения)

        Но тогда получатся разные конфиги для prod и для dev, по моему это вообще не огонь, или альтернативного решения нет?

        Конфиги и все файлы из src должны «запекаться» в image при помощи ADD/COPY инструкций Dockerfile.

        А почему нельзя так оставить, обязательно в образ пихать?
        По факту тоже самое получается, или в чем тонкость?
        Docker отслеживает изменения docker-compose.yml или только изменения Dockerfile?


        1. Fesor
          07.01.2018 23:12

          А почему нельзя так оставить, обязательно в образ пихать?

          потому что намного удобнее дистрибьютить исходники вместе с инфраструктурой а не порознь.


          По факту тоже самое получается, или в чем тонкость?

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


          Docker отслеживает изменения docker-compose.yml или только изменения Dockerfile?

          докер ничего не отслеживает. Если вы поменяли Dockerfile вы должны попросить его собрать новый образ. Если образ вдруг ничем не отличается (включая файлы которые вы копируете) он просто возьмет все слои из кэша.


          Что до изменений в docker-compose — вы меняете не compose файлы а конфигурацию контейнера. Так что при запуске сервиса докер сравнит параметры контейнера и пересоздаст его если нужно (новый образ, другие параметры, новые переменные).


      1. Caravus
        08.01.2018 15:40

        Это интересный план, но делать нужно всё с точностью наоборот :)

        Для прода нужно чтобы не было статических ссылок на другие контейнеры в конфиг файлах nginx.conf и т.д.

        Значение хоста и порта в инструкциях типа SetHandler «proxy:fcgi://php:9000» должны приходить из ENV: SetHandler "${PHP_UPSTREAM}"

        В чём проблема с такими ссылками? И докер-композ и кубернетес спокойно работают с именами контейнеров. Запихивая их через ENV вы добавляете ещё одно место где может накосячить человек (человеческий фактор). Лишнее нарушение изоляции. В ENV вообще ничего ценного быть не должно.
        Конфиги и все файлы из src должны «запекаться» в image при помощи ADD/COPY инструкций Dockerfile.

        Конфиги как раз таки должы пробрасываться снаружи. Делается это для того чтобы эти конфиги не появились в ваших репозиториях (гит, докер). Для разного окружения — разные конфиги, потому засунуть их все в докерфайл — создать место с неопределённостью в работе (какой конфиг сейчас подгружен, какие значения?). Простой способ (не рекомендую) через ENV, чуть сложнее — через секреты. Можно записывать в райнтайм, опять же, зависит от того для кого.

        Программа должна работать без «volumes» в docker-compose

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

        Далее надо использовать Rancher или Kubernetes чтобы это все дело оркестрировать на проде.

        Насколько я понял — ранчер сейчас сам переходит на кубернетес, так что скоро это будет одно и тоже, просто у ранчера будет морда поудобней.


  1. disc
    05.01.2018 23:34
    +1

    Простите, но Docker-compose никак не пакетный менеджер. Он служит для управления мульти-контейнерными приложениями.


    1. krocos
      06.01.2018 14:47

      «Локальный docker stack» как бы. Что бы поднять посмотреть, как примерно это чудо будет работать в кластерном режиме.


      1. Fesor
        07.01.2018 23:13

        Это инструмент для оркестрации контейнерами.


        1. krocos
          08.01.2018 12:37

          Ну как бы да, но это не то же самое, что управление кластером. Вот (не совсем в курсе как у кубернейтс) в режиме роя, докер имеет менеджер ноды, которые раздают таски шедулеру для изменения стейта роя. Вот это больше похоже на оркестрацию. Нежели compose, который скорее инструмент, что бы не не делать много «docker run ...» и «docker volume/network create ...» ручками.

          Всё нет времени разобраться с кубернейтс. Скажите, как вам этот инструмент? Намного сложней сварма при реальной эксплуатации? Хочется простоты и полного понимания того, что происходит с кластером. Поэтому изначально был выбран сварм. Но вижу много хороших отзывов о кубернейтс и всё не могу для себя решить, стоит ли присмотреться.


          1. Caravus
            08.01.2018 15:45

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


  1. krocos
    06.01.2018 13:35

    Надо было наверно еще рассказать как из локальных сборок (используя docker-compose) потом всё перенести на прод, на swarm например. А еще лучше, как это сделать используя, например, Jenkins Blue Ocean (ибо тоже создан для облегчения понимания), как тестить сборки и получать вывод в дженкинсе же. Тогда наверно смысла было бы больше. Хотя, это наверно на книжку материала.


  1. lomnev
    06.01.2018 13:35

    Возможно когда-нибудь пользователи Docker начнут массово приходить к пониманию, что контейнеры должны быть stateless. Без этого условия использование Docker в некоторых типах проектов имеет шансы стать антипаттерном.


    1. krocos
      06.01.2018 14:41

      А еще, сервисы должны быть готовы, что другие сервисы могут быть еще не готовы или внезапно упали в произвольном количестве. Об этом вроде Фаулер писал.


  1. Nothing666
    06.01.2018 13:35

    Чисто для ознакомления решил добавить на проект jenkins и sonarqube технологии, поднял проект на docker-compose добавил контейнеров для jenkins, sonarqube, database, API(backend-services), UI(контейнер NGINXа что б юзать как прокси, т. к. хотел оставить все на одном порте(повторяюсь делал для ознакомления и просто експерементировал(secure-low знаю))
    Получилась интересная вещь:
    project/standartUrl — редиректил на проект.
    project/jenkinsUrl — на Jenkins.
    project/sonarqube — на Sonarqube.
    Потратил почти неделю на проблему: Nginx редиректил на проект и нормально его загружал, но при редиректе на SonarQube и Jenkins(далее tools) упрямо не хотел загружать css и js файлы. Перепробовал все (сначало проблема была в сабдомене для tools). Решив проблему

    Решение
    Казалось норм решение прокинуть в docker-compose volume с конфигурациями где указан sonar.web.context=/specifyUrl для Sonarqube
    prefix=/specifyUrl для Jenkins
    Но нет.
    Пришлось перебилдить images тулзов с указанием вшупмнтх сетингов.


  1. denis_skripnik
    06.01.2018 13:35
    +1

    Здравствуйте. Жаль, что у вас в статье все примеры docker-compose.yml картинками. я незрячий — их не вижу.
    Хотел научиться работать с Docker, но не разобрался, как делать без особого труда виртуальные домены.


  1. krocos
    06.01.2018 15:03

    Вот еще один мало обсуждаемый аспект (как мне показалось). Как же контейнерам подключать данные? Ведь информация же должна быть не убиваемой. Ну и вот одно из решений — подключение NFS в контейнеры. Этот вариант кажется хорош со всех сторон. Но вот какого-то простого how to нет. На docker-compose в основном примеры. А как же это сделать на docker swarm примеров мало. Может даже не так уж и мало, но авторы статей вываливают кучу опций монтирования, которые малопонятны для простых обывателей.

    Тема интересная и новым людям моск иногда взрывает. Ибо репликаций одного контейнера может быть много, а может это и глобальный контейнер. И как это провернуть если система работает на 20 физических машинах например? В общем, неплохо было бы статью про то как сделать это сначала на compose, а потом безболезненно перенести на swarm.


    1. Fesor
      07.01.2018 23:18

      Как же контейнерам подключать данные?

      докер предоставляет вам абстракцию — волумы. Это что-то типа mount хостовой файловой системы. Причем как и в случае с mount, замаунтить можно хоть NFS хоть еще что.


      Но вот какого-то простого how to нет.

      1. маунтить волумы через NFS не сказать что распространенная задача. Как правило все же данные либо хранятся в пределах хоста, либо напрямую контейнер к ним доступа не имеет (s3 какой). Ну и еще вариант с репликацией по сети (базы данных).


      2. для волумов существуют плагины, коих великое множество под самые разные файловые системы. В случае с NFS это к примеру вот такой вот плагин.

      А как же это сделать на docker swarm примеров мало.

      потому что все (ну как все, те кто хотят удобненько и быстро) используют кубернетис. Он это умеет.


      Ибо репликаций одного контейнера может быть много

      потому контейнеры по возможности должны быть stateless. Такие контейнеры по определению проще скейлить.


  1. Fesor
    07.01.2018 23:27

    Технология, позволяющая легко (во всех смыслах) разворачивать необходимое рабочее окружение.

    GIT для инфраструктуры — самое простое определение.


    Пакетный менеджер (по аналогии с composer и npm, только у docker — контейнеры),

    docker-compose, как предполагает его название (compose — композиция), это инструмент для оркестрацией контейнерами. Задача docker-compose удобно описывать сервисы (контейнера) и их конфигурацию а так же запускать это дело. За скачку образов отвечает сам докер.


    Для начала определимся со структурой проектов:

    судя по приведенному листенингу вы не определились.


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

    типичная путаница в терминах, не "контейнеры" а "образы". Лучше бы потратили один-два абзаца что бы хотя бы самому для себя разобрать в чем разница.


    Схематично это выглядит таким образом:

    nginx отправляет запрос на php-fpm, а по вашей диаграмме они типа независимо обрабатывают запросы.


    Как видите необязательно знать принцип работы docker, чтобы успешно с ним работать.

    а потом толпы лемингов атакуют чаты с глупыми вопросами, которые устраняются вечером чтения документации/чтения статей.


  1. rpsv Автор
    08.01.2018 10:53

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

    Вам лень поделиться? Или вы не знаете?

    судя по приведенному листенингу вы не определились.

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

    типичная путаница в терминах, не «контейнеры» а «образы». Лучше бы потратили один-два абзаца что бы хотя бы самому для себя разобрать в чем разница.

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

    Повторюсь, что статья в большей степени для начинающих, чтобы не особо вдаваясь в термины docker научиться с ним работать (с docker-compose). Чтобы узнать что такое докер в конце статьи ссылки приложены.

    nginx отправляет запрос на php-fpm, а по вашей диаграмме они типа независимо обрабатывают запросы.

    Это блок-схема, а не диаграмма активности или коммуникации.

    а потом толпы лемингов атакуют чаты с глупыми вопросами, которые устраняются вечером чтения документации/чтения статей.

    А следующий абзац лень прочитать? «Да, конечно, для тонкой настройки или сложных задач, необходимо уже углубляться в дебри docker, но в средне-статистическом случае — это не нужно.»


    1. Fesor
      08.01.2018 12:37

      Вам лень поделиться? Или вы не знаете?

      Лень если честно. В прочем делиться мне не жалко — https://github.com/fesor/project-skeleton.


      Это не путаница

      то есть вы сознательно вводите людей в заблуждение, ну ок.


      Повторюсь, что статья в большей степени для начинающих

      Начинающие должны сразу понять что такое образы, что такое слои и т.д. Лучше бы опустили в статье "как настроить apache" или php-fpm и указали бы ссылку на отдельную статью.


      научиться с ним работать (с docker-compose)

      но они же не научатся, в этом и проблема.


      но в средне-статистическом случае — это не нужно

      То что docker-compose не является менеджером пакетов знать нужно. То что есть образы и контейнеры из них разворачиваются знать нужно. То что можно зайти в контейнер, сделать commit и получить новый образ, знать нужно (что бы проще было объяснять как работает Dockerfile, как организовать деплоймент и т.д. и людям было бы проще когда они столкнутся с проблемами).


      1. Fantyk
        09.01.2018 03:46

        спасибо за ссылку на project-skeleton, а есть примеры скриптов/репозитория деплоя проекта?