- Docker и docker-compose для начинающих. Докеризуем интернет-магазин
- Докер
- Основы докера
- Докер на практике, docker-compose
- docker и docker-compose. Аналогия для фронтендщиков
- Ставим docker и docker-compose
- Структура проекта
- Тестовый проект default.test
- Конфиг nginx
- Настраиваем php и Dockerfile для него
- Конфиг docker-compose.yml
- Правим файл hosts
- Запускаем проект
- Как работать с docker-compose
- Запускаем второй проект. Мое старое портфолио
- Создаем проект интернет-магазина
- Подключаем mysql
- Устанавливаем PhpMyAdmin
- Зависимости в docker-compose
- Подключаемся к базе через PhpMyAdmin
- Создаем базу данных интернет-магазина
Docker и docker-compose для начинающих. Докеризуем интернет-магазин
Докер везде. Когда-то на него смотрели, как на очередную забаву неугомонных программистов, но докер оказался не таков. Если раньше в любой вакансии писали jquery и zend framework, то сейчас — git и docker. Не удивлюсь, если докер станет таким же стандартом, как и гит в свое время. Или уже стал, это я только из пещеры вылез.
Зачем мне понадобился докер? Ну не знал и не знал, сидел не умничал, сейчас-то что началось?
Разбираться с ним меня сподвигли вы, дорогие читатели. Да ладно? Серьезно. Четверть вопросов по интернет-магазину звучит примерно так: не работает, что делать? Почти всегда причина в том, что нужно настроить окружение. Поставить веб-сервер, завести php и mysql, развернуть базу и прочие рутинные штуки.
Вопрос разворачивания рабочего окружения для веб-проектов волновал меня давно.
Когда я начал заниматься первыми веб-проектами, то ухитрился поднять apache, php и mysql на windows 7. Было это лет 8 назад. История из серии «один раз получилось, но повторить не смогу». Это медаль, которую получаешь раз в жизни.
Вторая попытка осмыслить вопрос рабочего окружения случилась в 2015 году. Я тогда написал адовую статью, как развернуть окружение для веб-разработчика. Это было страшно, но я сам так работал. Схема предполагала винду как основную ОС, в винде ставилась виртуальная машина VirtualBox, в которой поднимался debian. В debian уже nginx, php, mysql и nodejs. Также ssh-сервер на виртуалке и ssh-клиент на винде. Все это волшебным образом соединялось вместе и реально работало! Я не жалею, что творил эту дичь, но вам не советую проделывать такой же путь. Просто не надо. Мир стал намного проще.
Затем я пересел на linux и сразу стало легче. nginx, php и mysql ставились несколькими командами. Раз поставил, нашел типовую конфигурацию для nginx и фигачишь. Создать виртуальный хост для нового проекта — дело двух минут. По сей день так работаю.
Но вопрос читателей меня не оставлял. Ведь все мы разные. Одни только начинают разбираться в веб-разработке. Другие работают на винде и им так удобнее. Третьи занимаются фронтендом и им вообще до фонаря эти php и базы данных. Они хотят потрогать javascript-код, а как его потрогаешь, если магазин просто не заводится?
Часто меня выручали системы вроде denwer и open server. Я сам ими не пользовался, но видел со стороны, как это работает, и поэтому советовал. В том же денвере apache, php и mysql поднимаются довольно просто, на мой взгляд. Судя по отзывам, некоторых читателей это выручало.
Но пора было делать следующий шаг.
Докер
Докер — это такая штука для разработки и разворачивания приложений. В случае веб-проектов докер позволяет развернуть nginx, php, mysql, phpmyadmin и вообще любые приложения. Причем все это добро не нужно ставить руками и засорять систему. Все сервисы запускаются в виртуальных контейнерах и никак не влияют на основную ОС. То есть у вас в системе будут только докер, редактор кода, браузер и командная строка. Звучит круто, но пока бесполезно, да? Но смотрим дальше.
Если приложение завернуто в докер, то оно запускается одной командой. По ходу статьи мы увидим, как это делается. Представьте, чтобы посмотреть, как работает авторизация или админка в интернет-магазине, вам нужно лишь скачать исходники и выполнить команду
Да-да, это все. Забудьте об nginx, php и mysql, о том, что под капотом крутится вебпак и нужно не забывать делать npm install — всю эту работу возьмет на себя докер. Больше не нужно гадать, заведется ли код на вашей версии php или какую базу данных нужно поставить. Все это докер делает под капотом. Нам остается работать над кодом и не думать об окружении.
Конечно, это все звучит здорово, но прежде чем так легко обращаться с докером, нужно разобраться как и что.
Основы докера
Разбираясь в теме, я прочитал десятка три статей по докеру. Но в голове все уложилось только когда я нашел вот эту
Рекомендую статью всем, кто хочет не просто скопипастить примеры из моей статьи, а понять, что такое докер, образы, контейнеры, как монтировать папки и файлы, как запускать команды и еще много полезных вещей. Статья большая, но написано по-человечески, простым языком, с примерами и аналогиями из жизни.
Докер на практике, docker-compose
docker-compose — это система, которая позволяет управлять набором из докер-контейнеров. То есть работают отдельные контейнеры nginx, php, mysql, но все это еще нужно подружить между собой. Любой туториал по докеру приводит в пример, как запустить nginx, но нам этого мало. Как завести сразу все, что нужно? Иными словами, LEMP — linux, nginx, php и mysql.
Здесь случилось то же самое, что и с теорией по основам докера. Я перепробовал много разных вариантов, но то php не заводился, то mysql не подключался. Удалось все сделать только по примеру из этой статьи
Рекомендую читать всем, но особенно тем, кто уже разбирался с docker и docker-compose. Если вы знаете общие принципы или просто хотите завести свой проект, то лучше прочитать эту статью. Автор расписал коротко и по делу. Это не как у меня, статьи только с литром пива читать.
А еще мне понравилась структура проекта из поста. С разрешения автора в своей статье я использую его структуру и конфиги проекта практически без изменений, разве что слегка сократив.
docker и docker-compose. Аналогия для фронтендщиков
Процесс докеризации проекта напоминает мне сборку фронтенда.
В обоих случаях нам нужно решить набор задач. На фронте это собрать весь javascript в один файл и сжать его, препроцессить стили и тоже их сжать, оптимизировать картинки, запустить watcher. А с докером это поднять nginx, php, mysql и развернуть базу.
Для сборки фронта мы используем npm-пакеты, готовые библиотеки для сжатия и склеивания. В докере это готовые образы nginx, php, mysql.
Все это добро на фронте собирается в один конфиг gulpfile.js или webpack.config.js, а в докере — в docker-compose.yml. И все задачи запускаются одной командой, например, gulp build или docker-compose up —build
Вот такая у меня аналогия. С общими делами разобрались, переходим к практике
Ставим docker и docker-compose
Докер отлично работает на линуксе и маке. Говорят, на 10-й винде тоже, но не пробовал. Я не знаю, какая у вас система, поэтому смотрите инструкции здесь.
Выбирайте версию CE — Community Edition, она бесплатная. EE — enterprise, нам ни к чему.
После установки докера перезагрузитесь и проверьте, все ли в порядке
Если что-то не заработает, то наберите такие команды
Структура проекта
- В папке hosts мы будем хранить конфиги nginx
- В images — образы докера, пока только php
- Папка logs нужна для хранения логов nginx
- mysql будет содержать файлы базы данных
- В www будем складывать непосредственно проекты. Каждый проект в отдельной папке. Для начала — default.test, который будем использовать для примера.
- docker-compose.yml — файл конфига, который соберет все в кучу
Тестовый проект default.test
Он будет очень простой. Закинем в папку www/default.test файлик index.php с содержимым
Вот и весь проект. На первом этапе мы всего лишь убедимся, что правильно настроили nginx и php. Давайте же посмотрим, как это сделать
Конфиг nginx
Это файл hosts/default.conf такого содержимого
Если вам приходилось настраивать nginx, то отличие найдете только одно. Раньше в локейшене \.php$ в fastcgi_pass вы писали что-то вроде
Теперь же php:9000. Это докеровская тема, php — это название хоста, по которому доступен php-контейнер, а 9000 — порт, по которому до него можно достучаться.
Такой конфиг более менее универсальный и подходит для локальной разработки несложных проектов. В статье мы рассмотрим 3 проекта и конфиги в них будут различаться только 4 строками.
- index index.php index.html; — это путь к корневому файлу проекта. Можно оставить оба, и php, и html. Если в папке будет index.php, то исполльзуется он, если нет — то index.html
- server_name default.test; — имя хоста, которое мы будем вбивать в браузере
- error_log /var/log/nginx/default.error.log; — куда складывать error логи nginx. Вот и пригодилась папка logs. Для каждого проекта будем заводить отдельные логи, хотя никто не запрещает складывать все в просто error.log и у вас будет один файл на все проекты. Для разработки годится
- access_log /var/log/nginx/default.access.log; — аналогично, только для access логов
- root /var/www/default.test; — папка проекта. Хост и название папки указываем одинаково, с точкой, чтобы не путаться
Настраиваем php и Dockerfile для него
По идее можно не делать отдельный Dockerfile для php, а взять готовый образ. Именно так мы сделаем с nginx и mysql, когда будем настраивать docker-compose.yml. Но проблема в том, что nginx и mysql прекрасно работают из коробки, а с php немного иначе. Он тоже работает, но в официальный образ не включены никакие расширения, которые могут понадобиться при работе. Именно поэтому мы будем собирать php хитрее, через Dockerfile.
Давайте посмотрим на конфиг. Это файл images/php/Dockerfile
Напоминаю, что мы взяли конфиги из этой статьи. Я чуть сократил конфиг, поэтому читайте исходный, найдете там еще что-то интересное. Кратко что здесь происходит.
- FROM php:7.2-fpm — указываем официальный образ. Напоминаю, он пустой, без всяких расширений
- Блок RUN — куча линукс-команд, которые ставят curl, wget и различные расширения вроде mysqli и mcrypt. Без mysqli, например, наш интернет-магазин просто не заработает
- ADD php.ini /usr/local/etc/php/conf.d/40-custom.ini — это возможность использовать кастомный php.ini. Кстати, добавьте его в папку images/php, пусть будет пустой
- WORKDIR /var/www — рабочая директория для php
- CMD [«php-fpm»] — команда запуска контейнера
Да, Dockerfile для php посложнее, чем стандартный nginx-конфиг. nginx мы просто будем копипастить, меняя хост и пути. Здесь же, чтобы самому написать такой файл, нужно представлять, как работает php, где хранится php.ini, что такое рабочая директория и как ставить расширения. Но с другой стороны, базовые настройки меняться не будет, главное, разобраться со страшной портянкой в RUN.
Блок RUN — это набор команд, которые мы нагуглили «как установить php в linux» и вбили в консоли. Разве что расширения ставятся через docker-php-ext-install. Например, в линуксе мы бы поставили mysqli примерно так
А в докере нужно указать его в строке
Если честно, мне пока сложно понять, где и какие команды используются в тех или иных случаях. Поэтому не вижу других способов, чтобы для каждого конкретного случая копать документацию и гуглить. Но повторюсь, для наших проектов большего не понадобится. В Dockerfile мы больше заглядывать не будем.
Пора собирать nginx и php в единое целое — переходим к docker-compose.yml
Конфиг docker-compose.yml
Файл будет такого содержания
Разберем, что здесь написано.
version: ‘2’ — версия docker-compose. Сейчас есть и 3-я, но там какие-то совсем хитрые штуки, нам хватит и 2
services — здесь перечисляем контейнеры, которые будут у нас работать в связке. Пока что nginx и php, позже добавим mysql
image: nginx:latest — используем последнюю версию образа nginx с официального хранилища dockerhub
ports: — «8000:80» — здесь прокидываем порты. nginx в контейнере работает на дефолтном 80, а мы возьмем 8000. Это значит, в браузере будем открывать не default.test, a default.test:8000
volumes: — здесь монтируем файлы и папки. Или прокидываем их из локальной системы в контейнер. Посмотрим первый пример ./hosts:/etc/nginx/conf.d. В папке hosts у нас лежат конфиги nginx, но контейнер-то об этом не знает. Контейнер изолирован от нашей основной системы, а раздел volumes как раз и позволяет «общаться» контейнеру с нашей ОС. Можно считать эту команду копированием содержимого локальной папки ./hosts в «удаленную папку» контейнера /etc/nginx/conf.d. Или еще лучше настройкой ссылки (симлинки). Аналогично ./www:/var/www указывает рабочую директорию для nginx, а ./logs:/var/log/nginx — расположение логов
links: — php указывает, что контейнер nginx имеет зависимость от php
Дальше раздел php. Здесь короче, потому что основное мы указали в Dockerfile
build: ./images/php — папка, где располагается Dockerfile
volumes: — ./www:/var/www — прокидываем рабочую директорию точно так же, как и для nginx
С конфигом docker-compose.yml пока все. Идем дальше
Правим файл hosts
Чтобы наш пробный сайт default.test заработал в браузере, нужно не забыть добавить его в файл hosts. Открываем /etc/hosts с sudo (на unix-системах) и добавляем в него
Точно так же, как и при работе с локальным веб-сервером. Сохраняем файл, закрываем
Запускаем проект
Наконец-то самое интересное, проверим, как все это работает. В консоли из корневой папки проекта запускаем команду
И ждем. Первый запуск будет проходить не быстро. В консоль будет сыпаться до фига всего разного, но можно будет разглядеть, как скачиваются образы ngnix и php, как выполняются команды RUN из php-шного Dockerfile, как запускаются контейнеры.
Если мы все сделали правильно, то увидим в консоли примерно такую картину
done напротив названий контейнеров говорит, что они успешно запущены. Идем в браузер, переходим на http://default.test:8000/ и видим полный расклад phpinfo. Работает!
Мы запустили наш первый проект, завернутый в докер. Не знаю, как вы, а я испытал какой-то священный трепет, когда все это завелось. С ума сойти, я могу просто взять этот проект, перетащить на другой компьютер и запустить его одной командой в консоли. Да, весь проект всего лишь выводит phpinfo, но эта простенькая штука станет базой для разворачивания более сложных проектов, связанных с базой данных да и вообще с чем угодно. Ниже мы научимся подключать в докере mysql и заведем интернет-магазин.
Но сначала немного отвлечемся и посмотрим, какие команды нам пригодятся при работе с docker-compose и вообще с проектами
Как работать с docker-compose
Первой командой, которую мы запустили, был docker-compose up. Команда анализирует конфиг docker-compose.yml, скачивает нужные образы, монтирует файлы и папки и запускает контейнеры. При этом процесс висит в консоли, а чтобы остановить его, нужно нажать ctrl+C, стандартно. А еще в консоли будут выводиться логи docker-compose.
Можно запустить контейнеры с опцией —build, вот так
При этом принудительно пересоберутся все образы. Это может быть полезно, когда мы что-то поменяли в конфигах и пока еще не понимаем, требует это пересборки или нет. Принудительный билд занимает больше времени, но я пока предпочитаю всегда его использовать. Все равно после самого первого сбора запуск проходит очень быстро, а потерпеть лишние 2 секунды не проблема.
Если вы не хотите, чтобы в консоли висел открытый docker-compose, запускайте его как процесс, с опцией -d
Тогда докер запустит все контейнеры, но в фоне. Все будет работать точно так же, но чтобы остановить контейнеры, нужно будет запускать
А логи смотреть, запуская отдельно
Если мы разворачиваем приложение в продакшене, то конечно, запускать его стоит как фоновый процесс. Но в режиме разработки мне больше нравится держать консоль открытой и сразу смотреть логи. И не думать, нужна ли пересборка всех образов или нет. То есть запускать
Еще при разработке бывает полезно заглянуть в логи nginx. Отслеживать их будем через обычный tail -f
Пока это самое базовое, что может нам потребоваться. Для более тонкой работы с отдельными образами и контейнерами читайте вышеупомянутую статью — Docker самый простой и понятный туториал
Запускаем второй проект. Мое старое портфолио
Предлагаю немного передохнуть и закрепить информацию. Каким образом? Мы создадим второй проект. Он тоже php-ный, но чуть сложнее одного файла. Там уже подключаются шрифты, стили и javascript. Технически это не будет отличаться от первого default.test, но фишка в другом. Мы увидим, как просто нам теперь создавать новые проекты, а заодно и посмотрим на мое старое портфолио, которое я создал лет 6-7 назад. Это простой сайт на php, где рассказывается, какой я замечательный человек и разработчик.
Создаем новую папку www/w-portfolio.test. Кладем туда файлы проекта. Расписывать их нет смысла, все найдете в исходниках.
Дальше добавляем nginx конфиг в hosts — w-portfolio.conf с содержимым
То есть мы скопировали конфиг из дефолтного default.conf и поменяли default на w-portfolio. Что может быть проще?
Осталось добавить в файл hosts строку
И перезапустить docker-compose
И открыть в браузере http://w-portfolio.test:8000
Итак, второй сайт разобрали, идем дальше.
Создаем проект интернет-магазина
Все то же самое, что и с проектом w-portfolio.test.
Создаем папку w-shop.test, копируем туда файлы магазина. Если вы читали статьи по магазину или админке, то в курсе, что это за магазин. Можете просто взять свои локальные файлы. А если нет, то взгляните, как этот самый магазин выглядит shop.webdevkin.ru и берите файлы из исходников
Дальше заводим nginx-конфиг w-shop.conf в папке hosts
И прописываем в /etc/hosts строку
Если вы прямо сейчас перезапустите docker-compose, то у вас откроется первая страница http://w-shop.test:8000/ и корзина http://w-shop.test:8000/cart.html. Будет работать даже добавление в корзину. А вот каталог с фильтрами, каталог с пагинацией и отправка заказов не заведутся, потому что там уже включается база. Давайте разбираться, как работать с mysql
Подключаем mysql
Добрались до самого интересного. Идем сразу в конфиг docker-compose.yml и добавим в services новый раздел mysql, вот так
А в раздел php добавим зависимость от нового контейнера
Разбираемся по порядку.
image: mysql — образ mysql из dockerhub-хранилища
ports: — «3307:3306» — справа дефолтный порт, на котором mysql работает в контейнере. Слева — порт, который займет mysql в локальной ОС. Почему я не оставил тоже дефолтный 3306? Потому что у меня уже установлен локальный mysql. У вас, скорее всего, тоже. И когда я попытался запустить докер, то он мне сказал, извини, чувак, порт 3306 уже занят, не шмогла. Проверяем в консоли
Действительно занят. Можно остановить локальный mysql через sudo service mysql stop, а можно просто повесить докеровский mysql на другой порт. Например, на 3307 или любой другой свободный.
Идем дальше.
volumes: — ./mysql:/var/lib/mysql — монтируем папки, ./mysql — это папка в нашем проекте, куда будем складывать файлы базы, а /var/lib/mysql — это папка, где лежит база по дефолту. По идее, можно держать базу и внутри контейнера, но удобнее вытащить в «свою» ОС. Например, так можно будет делать бэкапы и использовать одну и ту же базу в разных проектах
И последнее
environment: MYSQL_ROOT_PASSWORD: root — переменные среды. Здесь указываем только пароль для root-пользователя. В интернет-магазине у нас использовался root/root, пусть и здесь так же
В php мы добавили блок-зависимость от нового контейнера mysql
links: — mysql
Все, база mysql у нас будет подниматься и будет работать. Но есть одно но. Как с ней работать руками? Чтобы прямо войти и посмотреть.
Конечно, можно подключаться к ней в консоли и фигачить create table и прочие хакерские заклинания, но хочется чего-то попроще. Люди давно изобрели PhpMyAdmin, который стоит на каждом хостинге и нам привычен. Давайте и его заведем в докер.
Устанавливаем PhpMyAdmin
В docker-compose.yml добавляем еще один контейнер
- image: phpmyadmin/phpmyadmin — берем официальный образ
- restart: always — вот этой магии, честно, не понял. Всегда перезапускать контейнер, но почему только этот? Как узнаю, расскажу, а пока оставим так
- links: — mysql:mysql — зависимость от mysql
- ports: — 8001:80 — справа дефолтный порт, на котором phpmyadmin запущен в контейнере, слева — любой свободный, я взял 8001 — следующий за 8000, по которому открываем сам сайт
- И переменные среды: хост, пользователь и пароль. В реальном приложении, конечно, не будем заходить под рутом, но для примера годится
environment: PMA_HOST: mysql, MYSQL_USERNAME: root, MYSQL_ROOT_PASSWORD: root
Готово, скоро будем пробовать. Но пока еще раз отвлечемся
Зависимости в docker-compose
Обратите внимание, как мы проставляли зависимости контейнеров друг от друга.
nginx зависит от php, то есть сначала стартует php, а потом nginx. php от mysql, phpmyadmin тоже от mysql и лишь mysql ни от кого не зависит. Она база, она сама по себе работает.
Когда мы запустим docker-compose снова, то увидим в консоли такую картину
Это как раз демонстрирует порядок запуска контейнеров. А останавливаются они в обратном порядке
Сначала те, от которых никто не зависит, в конце — самые «важные».
Но это было отступление, погнали дальше, проверять mysql
Подключаемся к базе через PhpMyAdmin
Запускаем docker-compose up —build. Снова придется подождать, потому что докеру нужно выкачать 2 новых образа. Ждем, дожидаемся успеха и открываем http://w-shop.test:8001. Видим привычную форму входа в PhpMyAdmin. Вбиваем логин/пароль root/root и входим в интерфейс базы. Но здесь нас может ждать сюрприз.
У меня получилось так — я успешно зашел в PhpMyAdmin один раз, поковырялся там, создал базу и вышел. А вот второй раз зайти уже не смог. Вбиваю root/root, а в ответ mysql not connect или что-то такое.
Полез гуглить. Нашел, что в этом случае нужно из под рута выполнить такой запрос
PASSWORD в смысле root — наш пароль рута. Это понятно, а как войти-то в базу, раз PhpMyAdmin не пускает? Нужно лезть руками, в консоли, по хакерски. mysql -uroot -proot и вот это вот. Тонкость в том, что нужно сначала зайти в контейнер mysql, а уже потом в саму базу mysql. Давайте вспомним статью по основам докера, которую я рекомендовал в самом начале.
Чтобы попасть в контейнер, нужно узнать его id. Набиваем docker ps и видим примерно такой список
Id контейнера mysql — 3825c5051ee9. Давайте заглянем в него
Мы зашли в контейнер и запустили в нем команду bash. Ключ -it нужен, чтобы создать интерактивный терминал. Проще говоря, мы попали в консоль контейнера. Здесь уже привычнее. Подключаемся к базе рутом и выполняем нагугленный запрос
После этого все заработало и PhpMyAdmin стал пускать без ограничений.
Создаем базу данных интернет-магазина
По фэншую мы должны были создать базу и накатить sql-дамп через докер. Запустить команду mysql -uroot -proot