├── en └── README.md ├── SUMMARY.md ├── README.md ├── ru └── README.md ├── docker2.md ├── docker4.md ├── docker_mac.md ├── docker3.md ├── docker1.md ├── docker5.md └── docker6.md /en/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [Je suis Docker](docker1.md) 4 | * [Полуавтоматическая винтовка с самонаведением на ногу](docker2.md) 5 | * [Monkey patch](docker3.md) 6 | * [Херак-херак, и в production!](docker4.md) 7 | * [Data Volume container](docker5.md) 8 | * [Portable database](docker6.md) 9 | * [Заметки для Mac OS X](docker_mac.md) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Мифы и рецепты Docker. 2 | 3 | Когда пятый знакомый попросил меня рассказать про Docker‬, я понял, что об этом надо написать. Потом еще написал. Ну я подумал, если я убежал так далеко, может быть просто продолжить писать? Так я и сделал. 4 | В итоге у меня получилась серия заметок, а Никита Гусаков (@nkt) подсказал как объединить их в книгу. 5 | 6 | Cпасибо Сергею Яковлеву (@sergeyklay), Славе Шуранову (@shuranov) и моей жене Кате за коррекцию. 7 | 8 | Если вы открыли этот файл прямо на github, рекомендую перейти на [Gitbook](https://www.gitbook.com/book/grikdotnet/docker-myths-and-receipts/details) - здесь удобнее читать. 9 | 10 | 11 | Григорий Кочанов. -------------------------------------------------------------------------------- /ru/README.md: -------------------------------------------------------------------------------- 1 | # Мифы и рецепты Docker. 2 | 3 | Когда пятый знакомый попросил меня рассказать про Docker‬, я понял, что об этом надо написать. Потом еще написал. Ну я подумал, если я убежал так далеко, может быть просто продолжить писать? Так я и сделал. 4 | В итоге у меня получилась серия заметок, а Никита Гусаков (@nkt) подсказал как объединить их в книгу. 5 | 6 | Cпасибо Сергею Яковлеву (@sergeyklay), Славе Шуранову (@shuranov) и моей жене Кате за коррекцию. 7 | 8 | Если вы открыли этот файл прямо на github, рекомендую перейти на [Gitbook](https://www.gitbook.com/book/grikdotnet/docker-myths-and-receipts/details) - здесь удобнее читать. 9 | 10 | 11 | Григорий Кочанов. -------------------------------------------------------------------------------- /docker2.md: -------------------------------------------------------------------------------- 1 | Полуавтоматическая винтовка с самонаведением на ногу 2 | ======== 3 | 4 | **Как не надо использовать Docker.** 5 | 6 | Чтобы понимать эту статью надо знать базовые команды Dockerfile для создания изображений и принципы объектно-ориентированного дизайна. 7 | 8 | Открываю документацию любого официального образа сервисного ПО - например, [Nginx](https://hub.docker.com/_/nginx/) и нахожу раздел "How to use this image". 9 | Нам предлагают создать свой образ на базе официального, скопировав в него наши файлы, настроить мапинг порта в мир, и подмонтировать свою папку с конфигами. 10 | ``` 11 | FROM ... 12 | COPY . /usr/src/myapp 13 | WORKDIR /usr/src/myapp 14 | ``` 15 | 16 | Да, нам предлагают унаследовать Model от View в одном звездном классе и заплатить за хранение на Docker Hub образов наших проприетарных приложений. Business, as usual. 17 | Это же нам предлагают сделать в образах [PHP](https://hub.docker.com/_/php/), [Pyhon](https://github.com/docker-library/docs/blob/master/python/README.md), Ruby, и так далее. Для Python и Ruby даже сделаны версии с ONBUILD-триггерами на построение изображений-наследников, которыми приложение из папки будет скопировано в новый образ, "which should be all you need" - как 640 килобайт. 18 | 19 | К счастью, решение известно так же давно как проблема. Банда четырех, создатель Java Джеймс Гослинг и Фаулер последние 20 лет твердят: используйте композицию вместо наследования. 20 | Поэтому я положу рядом контейнеры с разными сервисами, создам адаптер, data transfer object, и свяжу их через конфиг. 21 | 22 | Наследование я использую для расширения существующего функционала в рамках инкапсуляции с учетом принципа подстановки Лисков - например, для подключения к php нужных мне расширений и системных библиотек, которые эти расширения используют. 23 | -------------------------------------------------------------------------------- /docker4.md: -------------------------------------------------------------------------------- 1 | Херак-херак, и в production! 2 | ======== 3 | 4 | В [предыдущей серии](./docker3.md) я рассказал как легким движением руки запустить сервисы php-fpm и nginx в докере на локальной машине. Это можно сделать удаленно. 5 | 6 | Точно так же создаю контейнеры PHP 7 и Nginx, сразу включа маппинг порта. 7 | ``` 8 | $ docker run -d --name=php7 php:7-fpm 9 | 3d1b737edfcc3f1102fa54c91f9120da4b86d8cbba3092b6f80156c0e31b4d8f 10 | $ docker run -d -p 8080:80 --name=nginx nginx 11 | 80be81b27e012fd061ff4b682f0b7b8803500bc38a4b9f787f91661603b2d4b7 12 | ``` 13 | 14 | Когда служба и клиент работают на разных машинах, у службы нет доступа к файловой системе клиентского приложения. Файлы конфигов можно копировать в контейнер. Что надо править в конифгах - смотрите в прошлой статье. 15 | 16 | ``` 17 | $ docker cp "php7:/usr/src/php/php.ini-development" php.ini 18 | $ vi php.ini 19 | $ docker cp php.ini "php7:/usr/local/etc/php/" 20 | $ docker exec php7 pkill -o -USR2 php-fpm 21 | 22 | $ echo "test2.php 23 | $ docker exec php7 mkdir /scripts 24 | $ docker cp test2.php "php7:/scripts/" 25 | 26 | $ docker cp "nginx:/etc/nginx" nginx 27 | $ vi nginx/nginx.conf 28 | $ vi nginx/conf.d/default.conf 29 | $ docker cp nginx "nginx:/etc/" 30 | $ docker exec nginx service nginx reload 31 | Reloading nginx: nginx. 32 | ``` 33 | 34 | Теперь можно проверить что php видит мой конфиг. 35 | ``` 36 | ~$ docker exec php7 php -i |grep php.ini 37 | Configuration File (php.ini) Path => /usr/local/etc/php 38 | Loaded Configuration File => /usr/local/etc/php/php.ini 39 | ``` 40 | Запускаю: 41 | ``` 42 | airgri:articles gri$ docker-machine ip dev 43 | 192.168.99.104 44 | airgri:monkeypatch gri$ curl 192.168.99.104:8080/test2.php 45 | Hello cruel world! 7.0.0RC1 46 | ``` 47 | 48 | Настраивать логи в такой конфигурации бессмысленно. -------------------------------------------------------------------------------- /docker_mac.md: -------------------------------------------------------------------------------- 1 | Docker myths and receipts. Mac OS X 2 | ======== 3 | 4 | * VMWare Fusion and Parallels Desktop are better then VirtualBox in performance and stability. 5 | 6 | * At the moment of writing the Kinematic app sometimes can't do the work. If you have general experience in unix and want to use Virtual Box, I recommended installing a [test build](https://www.virtualbox.org/wiki/Testbuilds) and update it periodically rather than using a VirtualBox installed with the Docker Toolbox. Maybe it will get fixed later. 7 | 8 | * Tired to type `$ docker full_command` every time? 9 | Set up the [bash-completion with docker extension](http://stackoverflow.com/a/26132452) and enjoy. 10 | 11 | * Docker-machine is a nice tool. Use it to create a Docker virtual machine and open a terminal. 12 | 13 | ``` 14 | airgri:~ gri$ docker-machine create --driver virtualbox dev 15 | No default boot2docker iso found locally, downloading the latest release... 16 | Downloading https://github.com/boot2docker/boot2docker/releases/download/v1.8.1/boot2docker.iso to /Users/gri/.docker/machine/cache/boot2docker.iso... 17 | Creating VirtualBox VM... 18 | Creating SSH key... 19 | Starting VirtualBox VM... 20 | Starting VM... 21 | To see how to connect Docker to this machine, run: docker-machine env dev 22 | 23 | airgri:~ gri$ docker-machine ssh dev 24 | ## . 25 | ## ## ## == 26 | ## ## ## ## ## === 27 | /"""""""""""""""""\___/ === 28 | ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~ 29 | \______ o __/ 30 | \ \ __/ 31 | \____\_______/ 32 | _ _ ____ _ _ 33 | | |__ ___ ___ | |_|___ \ __| | ___ ___| | _____ _ __ 34 | | '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__| 35 | | |_) | (_) | (_) | |_ / __/ (_| | (_) | (__| < __/ | 36 | |_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_| 37 | Boot2Docker version 1.8.1, build master : 7f12e95 - Thu Aug 13 03:24:56 UTC 2015 38 | Docker version 1.8.1, build d12ea79 39 | docker@dev:~$ 40 | ``` 41 | 42 | * If you see an error 43 | 44 | > `Error creating machine: Get https://api.github.com/repos/boot2docker/boot2docker/releases: dial tcp: lookup api.github.com: no DNS servers` 45 | 46 | add a resolving server to your /etc/resolv.conf, like "`nameserver 8.8.8.8`" 47 | 48 | 49 | Of course, OSX will overwrite it on next reboot, but you don't need it too often. 50 | 51 | * docker-machine (as well as boot2docker) mounts the /Users folder from a host Mac OS to the /Users folder of a virtual machine. Not to a docker container. 52 | 53 | ``` 54 | docker@dev:~$ mount |grep Users 55 | none on /Users type vboxsf (rw,nodev,relatime) 56 | ``` 57 | -------------------------------------------------------------------------------- /docker3.md: -------------------------------------------------------------------------------- 1 | Monkey patch 2 | ======== 3 | 4 | ### Настройка локально 5 | 6 | В этой статье я предполагаю, что служба docker запущена на той же машине, на которой выполняются команды, и у процесса есть доступ на чтение к текущей папке. Еще я подразумеваю, что вы умеете настраивать связку PHP-FPM и Nginx. 7 | 8 | Беру образы Nginx и PHP 7. 9 | ``` 10 | ~$ docker pull nginx 11 | ... 12 | ~$ docker pull php:7-fpm 13 | Status: Downloaded newer image for php:7-fpm 14 | ``` 15 | 16 | Теперь у меня есть два чужих класса, которые надо связать вместе через внедрение зависимостей. Самый простой способ добавлять зависимости в чужой код, конечно же, [monkeypatching](https://ru.wikipedia.org/wiki/Monkey_patch)! 17 | Сначала создаю контейнеры. Помню о [второй сложности программирования](http://martinfowler.com/bliki/TwoHardThings.html) - даю контейнерам вразумительные имена, они будут нужны, чтобы контейнеры могли взаимодействовать между собой. 18 | ``` 19 | ~$ docker create --name=php7 php:7-fpm 20 | 3d1b737edfcc3f1102fa54c91f9120da4b86d8cbba3092b6f80156c0e31b4d8f 21 | ~$ docker create --name=nginx nginx 22 | 80be81b27e012fd061ff4b682f0b7b8803500bc38a4b9f787f91661603b2d4b7 23 | ``` 24 | 25 | ### PHP 26 | 27 | Начну с PHP - его настроить сложнее. Где лежат конфиги для PHP - можно увидеть в его [Dockerfile](https://github.com/docker-library/php/blob/f5e091ac3815dce80ca496298e0cb94638844b10/7.0/fpm/Dockerfile): 28 | 29 | ```Dockerfile 30 | ENV PHP_INI_DIR /usr/local/etc/php 31 | --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \ 32 | WORKDIR /var/www/html 33 | COPY php-fpm.conf /usr/local/etc/ 34 | 35 | ``` 36 | 37 | Копирую себе из контенера содержимое каталога с файлами конфигурации php 38 | ``` 39 | ~$ mkdir monkeypatch 40 | ~$ cd monkeypatch/ 41 | $ docker cp php7:/usr/local/etc localetc 42 | $ ls localetc/ 43 | pear.conf php php-fpm.conf php-fpm.conf.default php-fpm.d 44 | $ ls localetc/php 45 | conf.d 46 | ``` 47 | Мейнтейнеры положили в образ php-fpm.conf, но не положили дефолтный php.ini. Придется взять его из исходников php. 48 | 49 | $ docker cp "$PHP7:/usr/src/php/php.ini-development" localetc/php/php.ini 50 | 51 | Правлю конфиги, как обычно. В какой папке PHP ищет расширения? 52 | Узнать можно, запустив php, например, во временном контейнере. 53 | ``` 54 | $ docker run --rm php:7-fpm php -i |grep extension_dir 55 | extension_dir => /usr/local/lib/php/extensions/no-debug-non-zts-20141001 => /usr/local/lib/php/extensions/no-debug-non-zts-20141001 56 | $ docker run --rm php:7-fpm ls /usr/local/lib/php/extensions/no-debug-non-zts-20141001 57 | opcache.a 58 | opcache.so 59 | ``` 60 | В расширениях только opcache, можно подключить его. 61 | ``` 62 | $ echo extension_dir = "/usr/local/lib/php/extensions/no-debug-non-zts-20141001" >> localetc/php/php.ini 63 | $ echo zend_extension = opcache.so >> localetc/php/php.ini 64 | ``` 65 | Пересоздаю контейнер php и монтирую в него папку с конифгами. Путь к монтируемой папке должен быть от корня - служба не знает, из какой папки вызывается клиент docker. 66 | ``` 67 | $ docker rm php7 68 | php7 69 | $ docker run -v "$(pwd)/localetc:/usr/local/etc" --name=php7 php:7-fpm php -i |grep Configuration 70 | Configuration File (php.ini) Path => /usr/local/etc/php 71 | Loaded Configuration File => /usr/local/etc/php/php.ini 72 | ``` 73 | Теперь можно пересоздать контейнер php7 с тестовым приложением на php. Создатели образа не позаботились о том, чтобы php-fpm работал как демон, так что надо самим запускать его фоном, не освобождая стандартные каналы ввода-вывода. 74 | ``` 75 | $ docker rm php7 76 | $ mkdir scripts 77 | $ echo " scripts/test.php 78 | $ docker run -v "$(pwd)/localetc:/usr/local/etc" \ 79 | -v "$(pwd)/scripts:/scripts" \ 80 | --name=php7 php:7-fpm & 81 | [29-Aug-2015 15:19:25] NOTICE: fpm is running, pid 1 82 | [29-Aug-2015 15:19:25] NOTICE: ready to handle connections 83 | ``` 84 | Пока что для удобства отладки я оставляю вывод из контейнера php-fpm в свою консоль. 85 | 86 | ### NGINX 87 | 88 | С Nginx всё просто и стандартно. Копирую на диск папку конфигов: 89 | ``` 90 | $ docker run --name=nginx nginx 91 | $ docker cp nginx:/etc/nginx . 92 | $ docker rm nginx 93 | ``` 94 | В папке `nginx/` надо отредактировать nginx.conf, fastcgi_params по вкусу, и создать конфигурационный файл для своего сайта в `nginx/conf.d/`. 95 | Основное для связи nginx с php - это указать в имени хоста имя контейнера с php, а директивы root и SCRIPT_FILENAME должны указывать на путь, который php поймёт в своём контейнере php7. 96 | ```Nginx 97 | location ~ \.php$ { 98 | fastcgi_pass php7:9000; 99 | fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 100 | ``` 101 | 102 | Монтирую конфиги в контейнер nginx и запускаю с маппингом 80-го порта контейнера на локальный 8080. 103 | 104 | ``` 105 | $ docker run -v "$(pwd)/nginx:/etc/nginx" -p 8080:80 --name=nginx nginx & 106 | $ curl 127.0.0.1:8080/test.php 107 | 172.17.0.65 - 29/Aug/2015:15:50:29 +0000 "GET /test.php" 200 108 | Hello world! 7.0.0RC1 109 | ``` 110 | Rock'n'Roll! 111 | 112 | В версии 1.7 в команде `docker run` надо указывать параметр --link чтобы в контейнере резолвилось имя другого контейнера. В версии 1.8.1 все работает и без этого параметра. 113 | 114 | ### Логи 115 | 116 | Мейнтейнеры образа php решили писать все логи fpm в /proc/self/fd/2, он же STDERR - как error_log, так и access.log. Однако, лог запросов у меня будет писать nginx, а в работе php меня интересуют только ошибки, поэтому предлагаю отредактировать localetc/php-fpm.conf и написать что-то привычное: 117 | 118 | error_log = /var/log/php/php-fpm.error.log 119 | ;access.log = /proc/self/fd/2 120 | 121 | В Nginx обошлись без самодеятельности, так что включаю access log в конфиге сайта nginx/conf.d/site.ru.conf 122 | 123 | access_log /var/log/nginx/host.access.log main; 124 | 125 | Теперь можно создать папку для логов c правом записи для демона docker и подмонтировать ее в контейнеры. 126 | В эту же папку можно писать и вывод контейнеров, при этом контейнеры можно детачить: 127 | ``` 128 | $ mkdir log 129 | $ sudo chgrp docker log/ 130 | $ sudo chmod g+rwx log/ 131 | $ docker stop nginx php7 132 | $ docker rm nginx php7 133 | $ docker run -d --name=php7 \ 134 | -v "$(pwd)/localetc:/usr/local/etc" \ 135 | -v "$(pwd)/scripts:/scripts" \ 136 | -v "$(pwd)/log:/var/log/php" \ 137 | php:7-fpm >>log/docker.php.log 2>&1 138 | $ docker run -d --name=nginx \ 139 | -v "$(pwd)/nginx:/etc/nginx" \ 140 | -v "$(pwd)/log:/var/log/nginx" \ 141 | -p 8080:80 \ 142 | nginx >>log/docker.nginx.log 2>&1 143 | $ curl 127.0.0.1:8080/test.php 144 | Hello world! 7.0.0RC1 145 | ``` 146 | 147 | Когда надо поменять конфигурацию - можно дать команду перезагрузки php и nginx. 148 | ``` 149 | $ docker exec php7 pkill -o -USR2 php-fpm 150 | $ docker exec nginx service nginx reload 151 | Reloading nginx: nginx. 152 | ``` 153 | Когда php 7 будет включен в дистрибутив Debian в образе php:7 появится init-скрипт. При желании, можно добавить его самостоятельно из дистрибутива по выбору. 154 | -------------------------------------------------------------------------------- /docker1.md: -------------------------------------------------------------------------------- 1 | Je suis Docker. 2 | ======== 3 | 4 | Вокруг постоянно говорят про Docker. Я знаю что вы отвечаете: "Это что-то про контейнеры, виртуализацию, облака", "У нас все и так работает", "Это все баловство", "Он не запустится на нашем старом ядре линукса", "Точно так же можно подготовить образ для облака и запустить его", "Можно просто настроить LXC, chroot или AppArmor". Вы знаете, что он вам не нужен. Очередная модная штука. В конце концов, просто лень разбираться. 5 | Но любопытно! Тогда, читайте. 6 | 7 | Если вы не слышали о контейнерах в Линуксе, вот список страниц, которые надо прочитать, чтобы понимать о чем речь: 8 | - https://en.wikipedia.org/wiki/LXC 9 | - https://en.wikipedia.org/wiki/UnionFS 10 | - http://habrahabr.ru/post/253877/ 11 | - https://www.docker.com/whatisdocker 12 | 13 | Поставьте Docker, он небольшой. Для Windows и Mac можно просто поставить Toolbox: https://www.docker.com/toolbox. 14 | Создавать виртуальную машину и настраивать лучше из командной строки, а не через графическую обертку. 15 | Заметки для пользователей MAC OS [здесь](./docker_mac.md). 16 | Прочитайте несколько уроков из мануала. Здесь я пишу о том, чего в документации нет. 17 | 18 | **Docker - это не виртуализация.** 19 | 20 | Вот какой у меня линукс: 21 | ``` 22 | Welcome to Ubuntu 15.04 (GNU/Linux 3.19.0-15-generic x86_64) 23 | 24 | Last login: Tue Aug 18 00:43:50 2015 from 192.168.48.1 25 | gri@ubuntu:~$ uname -a 26 | Linux ubuntu 3.19.0-15-generic #15-Ubuntu SMP Thu Apr 16 23:32:37 UTC 2015 x86_64 x86_64 x86_64 GNU/ Linux 27 | gri@ubuntu:~$ free -h 28 | total used free shared buffers cached 29 | Mem: 976M 866M 109M 11M 110M 514M 30 | -/+ buffers/cache: 241M 735M 31 | Swap: 1.0G 1.0M 1.0G 32 | ``` 33 | Запускаю CentOS 34 | ``` 35 | gri@ubuntu:~$ docker run -ti centos 36 | [root@301fc721eeb9 /]# uname -a 37 | Linux 301fc721eeb9 3.19.0-15-generic #15-Ubuntu SMP Thu Apr 16 23:32:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 38 | [root@301fc721eeb9 /]# cat /etc/redhat-release 39 | CentOS Linux release 7.1.1503 (Core) 40 | [root@301fc721eeb9 /]# free -h 41 | total used free shared buff/cache available 42 | Mem: 976M 85M 100M 12M 790M 677M 43 | Swap: 1.0G 1.0M 1.0G 44 | 45 | ``` 46 | 47 | Docker - это не chroot, их функционал частично совпадает. Это не система безопасности вроде AppArmor. Docker использует те же контейнеры, что и LXC, но интересен он не контейнерами. Docker - это ничего из того, что я думал о нем до того, как прочитал документацию. 48 | 49 | То же ядро, память, файловая система, а дистрибутивы, библиотеки и пользователи - разные. 50 | 51 | **Docker - это инструмент объекто-ориентированного проектирования.** 52 | 53 | Регулярно возникает вопрос, является ли конфигурация nginx частью веб-приложения. Системные администраторы спорят с разработчиками. 54 | Но недавно в мире появились devops и захотели вместо последовательно-процедурного вызова команд из bash думать привычным OOP. 55 | Docker дает инкапсуляцию, наследование и полиморфизм компонентам системы, таким как база данных и данные. 56 | Это значит, что можно провести декомпозицию всей информационной системы, выделить приложение, web-сервер, базу данных, системные библиотеки, рабочие данные в независимые компоненты, внедрять зависимости из конфигов, и заставить все это работать одной группой, одинаково на разных компьютерах. 57 | 58 | Такой подход можно использовать, чтобы снизить потери рабочего времени дорогих front-end разработчиков на настройку базы данных и Nginx. 59 | Чтобы уйти от vendor lock-in. Не обломаться когда openssl на сервере не поддерживает cipher, используемый в API госучреждения. 60 | Чтобы приложение работало независимо от версии PHP или Python на сервере заказчика. 61 | Создавать open source не только в виде кода, но и настройкой пакетов из нескольких приложений, написанных на разных языках, работающих на разных слоях OSI. 62 | 63 | **Начало** 64 | 65 | Итак, я открыл https://docs.docker.com/mac/started/, поставил Docker, выполнил несколько упражнений, и почувствовал, что меня держат за дурачка-двоечника, которого боятся перегрузить информацией. 66 | Первый вопрос: куда это чертов докер поставился, где лежат его файлы, в каком формате, как оно все устроено? 67 | Ответы здесь: http://blog.thoward37.me/articles/where-are-docker-images-stored/ 68 | 69 | Если вкратце, для работы с файловой системой Docker может использовать один из нескольких драйверов, обычно это [AUFS](https://en.wikipedia.org/wiki/Aufs), и все файлы контейнеров лежат в /var/lib/docker/aufs/diff/. 70 | В /var/lib/docker/containers/ служебная информация, а не сами файлы контейнеров. 71 | 72 | Образы - это как классы в коде. Контейнеры - как объекты, созданные из классов. Основное отличие - контейнер можно закомитить и сделать из него образ. 73 | Образы состоят из так называемых слоев, слои - это папки, которые лежат в /var/lib/docker/aufs/diff/. Обычно образы приложений наследуют какие-то готовые официальные системные образы. Когда Docker скачивает образ, ему нужны только те слои, которых у него нет. 74 | 75 | Например, скачаю я официальный образ nginx: https://hub.docker.com/r/library/nginx/tags/ 76 | ``` 77 | docker@dev:~$ docker pull nginx 78 | latest: Pulling from nginx 79 | aface2a79f55: Pull complete 80 | 72b67c8ad0ca: Downloading [=============> ] 883.6 kB/3.386 MB 81 | 9108e25be489: Download complete 82 | 902b87aaaec9: Already exists 83 | 9a61b6b1315e: Already exists 84 | ``` 85 | Пишут, что образ nginx 1.9.4 размером 52 мб, а по факту, у меня скачается всего 3 мб. Это потому, что nginx собран на образе `debian:jessie`, который у меня "Already exists". 86 | Есть много образов на базе Ubuntu. Конечно, стоит собирать свою систему из образов с одним предком. 87 | 88 | **Docker не исполняет контейнеры, а управляет ими** 89 | 90 | Контейнеры исполняются механизмом ядра под названием Cgroups(https://en.wikipedia.org/wiki/Cgroups). 91 | Служба `docker` запускает контейнер по команде, полученной, от клиентского приложения (например, `docker`) и останавливает его когда в контейнере освобождается поток стандартного ввода-вывода. Поэтому в конфигурации Nginx для Docker [пишут](https://hub.docker.com/_/nginx/): 92 | 93 | > Be sure to include daemon off; in your custom configuration to ensure that Nginx stays in the foreground so that Docker can track the process properly (otherwise your container will stop immediately after starting)! 94 | 95 | Когда работа контейнера заканчивается, он не удаляется, если это не указать специально. Каждый запуск контейнера командой `$ docker run image_name` без параметров `--name` или `--rm` создает новый контейнер с уникальным идентификатором, который остается в системе до удаления. Так что Docker - система, склонная к замусориванию. 96 | Имена контейнеров в системе уникальны. Рекомендую присваивать имя каждому создаваемому постоянному контейнеру, а контейнеры, в которых не нужно сохранять данные, рекомендую создавать с параметром --rm. 97 | Контейнеры создаются командами `docker run` и `docker create`. Посмотреть список всех существующих в системе контейнеров можно командой `docker ps -a`. 98 | 99 | **Docker - это клиент-серверная системная служба** 100 | 101 | Соответственно, Docker может и зависнуть. Если вы дали команду скачать образ, единственный способ прервать процесс скачивания - перезапустить службу. Авторы уже давно обсуждают что с этим делать, но воз и ныне там. 102 | 103 | Например, в версии 1.8.1 есть воспроизводимая ошибка: 104 | ``` 105 | docker@dev:~$ docker pull debian 106 | Using default tag: latest 107 | latest: Pulling from library/debian 108 | 2c49f83e0b13: Downloading [===================> ] 19.89 MB/51.37 MB 109 | ``` 110 | Нажимаю Ctrl-C, затем сразу запускаю скачивание повторно. 111 | ``` 112 | docker@dev:~$ docker pull debian 113 | Using default tag: latest 114 | ``` 115 | Картина Репина "Приплыли". То есть, зависли. Надо перезапустить демон. 116 | ``` 117 | docker@dev:~$ sudo /etc/init.d/docker restart 118 | Need TLS certs for dev,127.0.0.1,10.0.2.15,192.168.99.104 119 | ------------------- 120 | docker@dev:~$ sudo /etc/init.d/docker status 121 | Docker daemon is running 122 | docker@dev:~$ docker pull debian 123 | Using default tag: latest 124 | latest: Pulling from library/debian 125 | ... 126 | Status: Downloaded newer image for debian:latest 127 | ``` 128 | Бывает, что демон docker не хочет умирать самостоятельно и не освобождает порт, а init-скрипт пограничные случаи еще не отрабатывает. 129 | Так что не забывайте проверять `sudo /etc/init.d/docker status`, `sudo netstat -ntpl`, доставайте бубен и танцуйте. 130 | 131 | Еще надо помнить, что порядок операторов для команды docker имеет значение. Если написать `docker create nginx --name=nginx`, --name=nginx будет считаться командой, которую надо выполнить в контейнере, а не именем контейнера. 132 | 133 | Теперь вам будет проще разбираться с официальной документацией. 134 | 135 | -------------------------------------------------------------------------------- /docker5.md: -------------------------------------------------------------------------------- 1 | Data Volume container. 2 | ======== 3 | 4 | В docker есть любопытный инструмент, который называется [Data Volume сontainer](https://docs.docker.com/userguide/dockervolumes/#creating-and-mounting-a-data-volume-container). 5 | У меня он ассоциациируется с паттерном data transfer object. 6 | 7 | **Data Volume** 8 | 9 | Том (volume) - это внешний для контейнера каталог или файл, монтированный в контейнер. 10 | Есть три типа data volume: 11 | * Пользовательский каталог или файл, который монтируется с указанием полного пути в host-системе, пример: `docker run -v "$(pwd)/log/:/log/" nginx`; 12 | * Том под управлением docker с заданным именем; 13 | * Автоматически создаваемый том со служебным именем. 14 | 15 | Второй тип получается, если имя каталога указать без пути. Пример: `docker run -v test:/test/ nginx`. В этом случае в /var/lib/docker/volumes/ будет создан каталог с указанным названием, или будет подключен существующий каталог. 16 | В Dockerfile директиве [VOLUME](https://docs.docker.com/reference/builder/#volume) можно указать имя каталога, который будет создан в /var/lib/docker/volumes/. Пример: `VOLUME mysql_dump_v1.1:/docker-entrypoint-initdb.d/`. При создании первого контейнеров из такого образа будет создан каталог /var/lib/docker/volumes/test, а при создании второго и следующих контейнеров, этот каталог будет для них общим. Такая вот любопытная недокументированная возможность. 17 | 18 | Если не указывать имя каталога, который вы монтируете - будет создан новый каталог в /var/lib/docker/volumes/ с названием из 64 цифр в 16-ричной нотации, это третий тип. Пример команды: `docker create -v /shared_folder nginx`. 19 | 20 | Относительно удаления монтируемых томов действуют следующие правила: 21 | * При удалении контейнера командой rm, как в `docker rm drunk_ritchie`, volume-каталоги не удаляются; 22 | * Если контейнер создан с параметром --rm, например, `docker run --rm -v /var/lib/mysql mysql`, созданные volume-каталоги будут удалены при завершении работы контейнера; 23 | * Если команде удаления указать параметр -v, например, `docker rm -v drunk_ritchie`, будут удалены автоматически созданные контейнеры, в том числе с заданным именем; 24 | * Если подключить том в несколько контейнеров, а затем удалять их с параметром -v, каталог будет удален из /var/lib/docker/volumes/ при удалении последнего контейнера, к которому подключен этот том. Если один из контейнеров, в которые был монтирован том, удален без -v, каталог тома останется, даже если следующий контейнер будет удален с параметром -v; 25 | * Пользовательские каталоги и файлы docker не удаляет. 26 | 27 | Разобраться к чему относятся тома с автоматическими названиями в /var/lib/docker/volumes/ сложно, и при удалении контейнеров они остаются как мусор. В документации пишут, что разработчики [работают над проблемой](https://github.com/docker/docker/issues/14214). 28 | В текущей реализации (1.8.2) я не вижу смысла использовать тома с автоматически генерируемыми именами. 29 | Самый удобный вариант - монтировать в контейнеры собственные, пользовательские каталоги. С файлами в них удобно работать, и удалить эти каталоги легко, когда станут не нужны. 30 | 31 | Вот что еще нужно знать про тома в docker: 32 | * В каталог /var/lib/docker/volumes/ доступ есть только для рута, так что просмотреть содержимое автоматически созданных томов можно только через `sudo`; 33 | * Данные из томов не экспортируются при выполнении `docker export`, `docker save` и не сохранятся в образ командой `docker commit`; 34 | * Если при монтировании указать несуществующий каталог или если к нему нет доступа - docker не выдаст никакой ошибки; 35 | * При подключении в контейнер внешнего каталога у него остаются uid/gid host-системы; 36 | * В Dockerfile можно указать тома, которые будут созданы для контейнера, но только каталоги, файл можно подмонтировать только параметром команды run или create. 37 | 38 | Перефразируя фильм "Адвокат дьявола", авторы docker дают нам тома в контейнерах, дарят этот экстраординарный подарок, а потом для своего ролика космических трюков устанавливает противоположные правила игры. 39 | Расшаривай каталог между контейнерами - но не удаляй. Монтируй свою папку - но не распространяй в образе. Записывай папки в образ и распространяй - но не расшаривай между контейнерами! 40 | 41 | Несмотря на проблемы, использовать в контейнерах общие каталоги для хранения файлов приложения и базы данных очень удобно, а проблему можно подпереть костылем в виде пары shell-команд в Dockerfile. 42 | 43 | **UID/GID и Data Volume** 44 | 45 | В контейнерах обычно есть свои пользователи и группы со своими uid/gid, а значения uid/gid у монтируемых каталогов присвоены в host-системе с ее пользователями. 46 | У группы docker, которая создатся в host-системе, gid равен 999. Однако, в официальных образах php и python пользователя с uid 999 нет вообще, а рабочие процессы исполняются под www-data 33:33. 47 | Кроме того, процессы в контейнерах с uid 0 могут спокойно поменять владельца подключаемого каталога, выставив выставив значение, отсутствующее в host-сиситеме. 48 | С большой вероятностью возникнут проблемы с правами доступа к подключаемым каталогам. 49 | 50 | Надеюсь, когда-нибудь для образов docker примут соглашение, по которому во всех образах будет стандартная группа docker с одним gid. Пока что придется создать специальную группу для томов и добавить в нее своего пользователя. 51 | 52 | ```console 53 | $ sudo groupadd -g 56789 docker_volumes 54 | $ sudo usermod -a -G docker_volumes gri 55 | ``` 56 | 57 | **Подготовка образа с приложением** 58 | 59 | В документации предлагают создавать volume container из образа операционной системы или базы данных. 60 | Если такой контейнер экспортировать, в файл пойдет вся операционная система. Наследование от образа mysql добавит к размеру архива порядка 66 мегабайт при сжатии xz. Чтобы этого избежать, я создаю контейнер от [Busybox](https://hub.docker.com/r/library/busybox/) - он позволяет выполнять простые команды и открывать терминал в контейнере, при этом добавляет в архив менее мегабайта. 61 | 62 | Я создам образ с пустым volume и файлами приложения. При создании контейнера будет проверяться пустой ли каталог, подключенный как volume. 63 | Если пустой - в него копируются файлы приложения. Если каталог не пустой - не копируются. 64 | При создании контейнера из образа я монтирую в него в качестве тома свой каталог, и в него копируются файлы приложения. 65 | Я могу редактировать файлы приложения и пересоздавать контейнер, подключая к нему свои файлы. 66 | При желании я могу вернуться к начальному состоянию из образа, создав новый контейнер и подключив пустой каталог. 67 | 68 | Копирую скрипты приложения в каталоге `source` и готовлю образ со скриптами: 69 | ```console 70 | $ mkdir data_volume 71 | $ chgrp docker data_volume 72 | $ cd data_volume/ 73 | $ cp ~/app source 74 | $ vi Dockerfile 75 | ``` 76 | 77 | Вот содержимое Dockerfile, из которого я создам образ со своим приложением: 78 | ```Dockerfile 79 | FROM busybox 80 | VOLUME /scripts 81 | RUN adduser -D -u 56789 docker_volumes 82 | COPY source /source/ 83 | USER docker_volumes 84 | CMD test "$(ls -A "/scripts/" 2>/dev/null)" || cp /source/* /scripts/ 85 | ``` 86 | При запуске контейнера будет выполнятся команда из директивы CMD. Если каталог `application/` пустой, в него будут скопированы скрипты приложения. 87 | Если при запуске контейнера указать команду, директива CMD игнорируется, поэтому я не использую директиву ENTRYPOINT - так удобней дебажить, открыв консоль в контейнер. 88 | 89 | Создаю образ и контейнер: 90 | ``` 91 | $ docker build -t grikdotnet/application . 92 | $ cd .. 93 | $ mkdir application 94 | $ sudo chgrp docker_volumes application 95 | $ docker run --name application -v "$(pwd)/application:/scripts" grikdotnet/application 96 | ``` 97 | Теперь у меня есть контейнер с data volume `/scripts/`, в который монтирован мой каталог `./application/`, и в нем появилось мое приложение. 98 | Этот data volume я могу подмонтировать в контейнер php. 99 | 100 | 101 | Этот образ удобно экспортировать 102 | ``` 103 | ~$ docker save grikdotnet/application | xz > application.image.tar.xz 104 | $ ls -lh application.dc.tar.xz 105 | -rw-rw-r-- 1 gri gri 858K Sep 8 14:53 application.dc.tar.xz 106 | ``` 107 | и импортировать 108 | ``` 109 | $ docker rm application 110 | $ docker rmi grikdotnet/application 111 | $ rm -rf application/* 112 | $ docker load --input application.image.tar.xz 113 | $ docker run --name application -v "$(pwd)/application:/scripts" grikdotnet/application 114 | $ ls -Al application 115 | total 4 116 | -rw-r--r-- 1 56789 docker_volumes 28 Sep 9 09:22 test.php 117 | ``` 118 | Да, пользователя с gid 56789 в host-системе нет. 119 | 120 | **UID/GID процессов среды исполнения** 121 | 122 | Конечно, PHP/Python/etc тоже может исполняться под пользователем docker_volumes. 123 | 124 | В случае PHP для этого надо расширить официальный образ, используя Dockerfile, подобный такому: 125 | ```Dockerfile 126 | FROM php:5.6-fpm 127 | RUN adduser --group --system --uid 56789 --disabled-password docker_volumes 128 | RUN apt-get update && apt-get install -y \ 129 | libmcrypt-dev \ 130 | && docker-php-ext-install mcrypt pdo_mysql 131 | CMD ["php-fpm"] 132 | ``` 133 | и в подключаемом php-fpm.conf указать 134 | 135 | user = docker_volumes 136 | group = docker_volumes 137 | 138 | Подключение приложения к контейнеру с PHP выполняется параметром `--volumes-from`. 139 | ``` 140 | $ docker run -d --name=php56 \ 141 | --volumes-from application \ 142 | my_php_image >>log/docker.php.log 2>&1 143 | ``` 144 | 145 | Пример пошаговых инструкций по запуску php можно найти в [третьей части](./docker3.md). 146 | -------------------------------------------------------------------------------- /docker6.md: -------------------------------------------------------------------------------- 1 | Portable database. 2 | ======== 3 | 4 | В этой заметке я опишу свое исследование работы с mysql в docker - стремление сделать так, чтобы команде разработчиков было удобно. 5 | Основные алгоритмы хорошо описаны в документации к образу [MySQL](https://hub.docker.com/r/mysql/mysql-server/), который предоставляет Oracle. 6 | С PostgreSQL можно работать точно так же. 7 | 8 | **Вводная** 9 | 10 | Обычно я не хочу раздавать полный дамп рабочей базы разработчикам. Причины могут быть разные - большой размер рабочей базы или критические данные. Для разработчиков часто создается урезанная версия небольшого размера со структурой и частью данных. 11 | Эту базу я хочу раздавать в виде образа и запускать работающий сервер БД одной командой. 12 | На рабочем сервере я хочу хранить файлы базы в удобном каталоге и работать с базой из командной строки через сокет. 13 | 14 | **UID/GID в разных образах** 15 | 16 | В так называемом "официальном" образе mysql (и mariadb) у пользователя mysql логичное для docker значение 999:999 в /etc/passwd. В другом официальном образе mysql-server, который предоставляет Oracle, у пользователя mysql стандартный для red-hat uid 27 - и это удобно для доступа к файлам базы утилитами из host-системы. В то же время, в Ubuntu нет стандартного значения uid/gid для mysql. 17 | Выбирайте себе официальный образ по вкусу. Я для примера взял mysql/mysql-server. 18 | 19 | **Пароль для MySQL** 20 | 21 | Официальные образы предлагают, что для инициализации хранилища пароли для рута указаны в командной строке как переменная окружения контейнера. 22 | Это почти так же небезопасно, как запускать базу без пароля - команда `docker inspect mysql` выведет все переменные в секции "Env". Кроме того, переменный окружения досутпны во всех прилинкованных контейнерах. 23 | Однако, иначе контейнер просто завершает работу с ошибкой. 24 | Я решу проблему так: инициализирую хранилище из временного контейнера, которому при запуске укажу пароль для root, а для постоянной работы сервис запускается без указания пароля. 25 | 26 | **Запускаю локально** 27 | 28 | Скачиваю образ MySQL, запускаю во временном контейнере, беру из него конфиг и пишу свой дополнительный файл конфига: 29 | ```console 30 | $ docker run --rm mysql/mysql-server cat /etc/my.cnf 31 | $ vi extra.cnf 32 | sql-mode = "STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER" 33 | innodb_file_per_table 34 | innodb_flush_log_at_trx_commit = 2 35 | 36 | [client] 37 | user=root 38 | password=my-secret-pw 39 | ``` 40 | 41 | Чтобы файлы базы данных и лог mysql писались в мою папку, подключаю их как data volume, и инициализирую базу данных. Файл лога при монтировании должен сущестовать, иначе Docker создаст вместо него каталог. 42 | 43 | ```console 44 | $ touch log/mysqld.log 45 | $ chmod a+w log/mysqld.log 46 | $ mkdir mysql_data 47 | $ docker run -d --name mysql \ 48 | -v "$(pwd)/mysql_data:/var/lib/mysql" \ 49 | -e MYSQL_ROOT_PASSWORD=my-secret-pw \ 50 | mysql/mysql-server 51 | 01effb0bb481d06dc1642a4f18950224049900971d972beea2a6ab311bd60ceb 52 | $ docker inspect mysql 53 | "Env": [ 54 | "MYSQL_ROOT_PASSWORD=my-secret-pw", 55 | 56 | # Жду несколько секунд, чтобы завершилась инициализация хранилища 57 | 58 | $ docker stop mysql 59 | $ docker rm mysql 60 | $ docker run -d --name mysql \ 61 | -v "$(pwd)/mysql_data:/var/lib/mysql" \ 62 | -v "$(pwd)/log/mysqld.log:/var/log/mysqld.log" \ 63 | -v "$(pwd)/extra.cnf:/etc/extra.cnf" \ 64 | mysql/mysql-server --defaults-extra-file=/etc/extra.cnf 65 | ``` 66 | 67 | Создаю базу из файла дампа. 68 | ``` 69 | $ cat dump.sql | docker exec -i mysql mysql --defaults-extra-file=/etc/extra.cnf -B 70 | ``` 71 | Этот дамп должен быть создан командой `mysqldump --databases db_name` чтобы в нем были инструкции `create database` и `use databsase`. 72 | Иначе сначала создайте базу данных из консоли, и добавьте ее название в виде параметра. 73 | 74 | Из контейнера коносльный клиент mysql вызывается так: 75 | ``` 76 | $ docker exec -ti mysql mysql --defaults-extra-file=/etc/extra.cnf 77 | Welcome to the MySQL monitor. Commands end with ; or \g. 78 | ``` 79 | 80 | Можно подключиться к серверу в контейнере через сокет в монтированном каталоге: 81 | ``` 82 | $ mysql --defaults-file=./extra.cnf -S ./mysql_data/mysql.sock 83 | Welcome to the MySQL monitor. Commands end with ; or \g. 84 | ... 85 | mysql> \q 86 | Bye 87 | 88 | $ mysqldump --defaults-file=./extra.cnf -S ./mysql_data/mysql.sock test > dump.sql 89 | ``` 90 | 91 | Теперь у меня есть работающий сервер, в котором мои данные, конфиг и логи лежат в моих каталогах, отдельно от сторонней, неконтролируемой, но изолированной в контейнере [среды исполнения](https://ru.wikipedia.org/wiki/Среда_выполнения) самой СУБД. 92 | 93 | **Образ с дампом базы данных** 94 | 95 | Рабочие файлы СУБД занимают намного больше места, чем текстовый дамп, они не переносимы между версиями и деривативами, такими как MariaDB и Percona. Базу данных удобно передавать в виде сжатого текстового дампа. 96 | 97 | Я хочу создать и раздавать компактный образ с дампом базы, без полновесной операционной системы и самой СУБД. 98 | В контейнер СУБД, в каталог /var/lib/mysql я хочу монтировать свой локальный каталог. 99 | При первом запуске СУБД надо автоматически инициализировать базу данных дампом из контейнера приложения. При последующих запусках, когда база уже существует, надо ее не трогать. 100 | По всей видимости, мне нужен дополнительный слой абстракции, который позволит вызвать команду mysql извне контейнера mysql чтобы передать ему на вход дамп из другого контейнера. 101 | 102 | Здесь я упираюсь в ограничения возможностей docker. Утилита compose, которая создана для объединения контейнеров, не предоставляет возможности автоматически вызвать команду инициализации контейнера, не встраивая ее в образ. Можно встроить скрипт инициализации в собственный образ, унаследованный от официального, однако, в образе MySQL такой скрипт [уже есть](https://github.com/docker-library/mysql/blob/master/5.6/docker-entrypoint.sh). 103 | 104 | Надеюсь, что когда-нибудь разработчики docker compose реализуют возможность указывать команды, которые надо запускать в контейнере при инициализации. Пока что есть два пути автоматизировать установку базы из дампа: 105 | * через docker-compose создавать специальный контейнер для инициализации базы с клиентским модулем mysql на базе [Alpine Linux](https://hub.docker.com/_/alpine/). К счастью, сам контейнер для запуска команды инициализации передавать не нужно, достаточно одного Dockerfile с командами; 106 | * недокументированная возможность - при инициализации базы скрипт entrypoint.sh [обрабатывает](https://github.com/docker-library/mysql/blob/master/5.6/docker-entrypoint.sh#L79) файлы из /docker-entrypoint-initdb.d/ 107 | 108 | Последний вариант, своеобразный callback handler, поддерживается всеми официальными образами, включая [MariaDB](https://github.com/docker-library/mariadb/blob/master/docker-entrypoint.sh#L79), [Percona](https://github.com/docker-library/percona/blob/master/docker-entrypoint.sh#L79), а так же образом Oracle MySQL и [PostgreSQL](https://github.com/docker-library/postgres/blob/master/9.4/docker-entrypoint.sh#L76). Этот способ самый удобный, буду его использовать. 109 | 110 | **Data volume image** 111 | 112 | Дамп базы даннных удобно хранить и передавать в одном образе с приложением. Доработаю образ приложения, который я создал в прошлой статье: 113 | 114 | ```console 115 | $ cd data_volume/ 116 | $ cp ~/dump.sql . 117 | $ cat << EOF > Dockerfile 118 | FROM busybox 119 | RUN mkdir /docker-entrypoint-initdb.d/ 120 | COPY dump.sql /docker-entrypoint-initdb.d/dump.sql 121 | VOLUME mysql_dump_v1.0:/docker-entrypoint-initdb.d/ 122 | VOLUME /scripts 123 | RUN adduser -D -u 56789 docker_volumes 124 | COPY source /source/ 125 | USER docker_volumes 126 | CMD test "$(ls -A "/scripts/" 2>/dev/null)" || cp /source/* /scripts/ 127 | EOF 128 | $ docker build -t grikdotnet/application . 129 | $ docker save grikdotnet/application | xz > application.image.tar.xz 130 | ``` 131 | 132 | Если в Dockerfile сначала указать команды записи файлов в каталог, а затем объявить этот каталог как volume, при создании контейнера из образа эти файлы будут скопированы в volume, и смогут быть доступны в других контейнерах. Чтобы файл дампа не стал мусором после удаления контейнера, я задал осмысленное имя для каталога в host-системе. Забыть добавить ключ -v в команду `docker rm`, к сожалению, слишком легко. 133 | 134 | Теперь я могу импортировать приложение в виде образа, создать из него контейнер, запустить MySQL, и база данных будет автоматически загружена из дампа. 135 | 136 | ``` 137 | $ docker run --name application -v "$(pwd)/application:/scripts" grikdotnet/application 138 | $ sudo ls /var/lib/docker/volumes/ 139 | mysql_dump_v1.0 140 | $ docker run -d --name mysql -v "$(pwd)/mysql_data:/var/lib/mysql" --volumes-from application -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql/mysql-server 141 | $ docker exec -ti mysql mysql 142 | ... 143 | mysql> show databases; 144 | +--------------------+ 145 | | Database | 146 | +--------------------+ 147 | | information_schema | 148 | | mysql | 149 | | performance_schema | 150 | | my_database | 151 | +--------------------+ 152 | ``` 153 | Так как образ я создаю для удобства разработчиков, а порт из контейнера в мир не транслируется, пароль для root в mysql я отключаю. 154 | 155 | Чтобы заменить MySQL версии Oracle на MariaDB, достаточно удалить контейнер mysql, файлы базы данных, и в строке запуска заменить название образа: 156 | ```Console 157 | $ docker rm -fv mysql 158 | $ sudo rm -rf mysql_data/* 159 | $ docker run -d --name mysql -v "$(pwd)/mysql_data:/var/lib/mysql" --volumes-from application MYSQL_ALLOW_EMPTY_PASSWORD=yes mariadb 160 | ``` 161 | Можно и не удалять, а запустить несколько контейнеров СУБД, отработать репликацию, или сравнить планы исполнения запросов в разных сборках. 162 | 163 | **Подключение PHP** 164 | 165 | Подключить MySQL в связке с php достаточно просто. 166 | 167 | Сначала добавляю расширение pdo_mysql [как рекомендуют авторы образа php](https://github.com/docker-library/docs/blob/master/php/README.md): 168 | 169 | ```dockerfile 170 | FROM php:5.6-fpm 171 | RUN docker-php-ext-install pdo_mysql 172 | CMD ["php-fpm"] 173 | ``` 174 | Создаю образ, контейнер и запускаю php-fpm: 175 | ```console 176 | $ docker build -t grikdotnet/php-pdo_mysql . 177 | $ docker run -d --name=php7 \ 178 | --volumes-from application 179 | -v "$(pwd)/localetc:/usr/local/etc" \ 180 | -v "$(pwd)/log:/var/log/php" \ 181 | grikdotnet/php-pdo_mysql 182 | ``` 183 | 184 | --------------------------------------------------------------------------------