Технология Docker набирает всё большую популярность среди разработчиков и DevOps-специалистов в наши дни, поскольку позволяет без особого труда настроить различные окружения один раз, а затем воспроизводить их на, казалось бы, совсем не совместимом оборудовании. Но начнём мы наш цикл статей о Docker с азов.
В сегодняшней статье мы поговорим о создании контейнеров и, соответственно, об их запуске. В Docker контейнер представляет собой окружение для выполнения какого-либо одного процесса. Это изолированная среда, в которой есть всё необходимое для выполнения нужного процесса, и нет ничего лишнего. Создание контейнера Docker выполняется в момент его запуска, и эти процессы запускаются с помощью команды docker run. Давайте сначала рассмотрим её синтаксис и опции.
Синтаксис и опции docker run
Синтаксис команды docker run похож на синтаксис других команд Linux и выглядит следующим образом:
$ docker run опции образ команда
Утилите обязательно надо передать образ, на основе которого будет создан контейнер. Образ может быть локальным или указывать на образ, который надо загрузить из сети. Мы рассмотрим это в примерах ниже. Опции позволяют настроить контейнер и параметры его запуска более детально. Сама команда позволяет переопределить программу, которая выполняется после запуска контейнера. Например, выполнив /bin/bash, вы можете подключится к самому контейнеру.
Рассмотрим основные опции утилиты, которые мы будем использовать. Опций очень много, поэтому я не могу перечислить их все:
- -d — запускает контейнер в фоновом режиме;
- -t — прикрепляет к контейнеру псевдо-TTY-консоль;
- -i — выводит в терминал STDIN поток контейнера;
- —name — имя контейнера, по которому потом можно будет к нему обращаться;
- —dns — устанавливает DNS-серверы для контейнера;
- —network — тип сети для контейнера, может принимать такие значения: bridge (используется по умолчанию), none, host. Также можно передать идентификатор сети Docker, к которой надо подключится;
- —add-host — добавляет строчку в /etc/hosts;
- —restart — указывает, когда надо перезапускать контейнер. Возможные значения: no, on-failure, always, unless-stopped;
- —rm — удаляет контейнер после завершения его работы;
- -m, —memory — количество оперативной памяти, доступное Docker-контейнеру;
- —memory-swap — объём памяти раздела подкачки, доступный в контейнере;
- —cpus — количество ядер процессора, доступных в контейнере;
- —shm-size — размер файла /dev/shm;
- —device — позволяет монтировать устройства из папки /dev в контейнер;
- —entrypoint — позволяет переопределить скрипт, который выполняется при запуске контейнера, перед запуском основной команды;
- —expose — позволяет пробросить несколько портов из контейнера в хост-систему;
- -P — пробрасывает все порты контейнера в хост-систему;
- -p — переносит все порты контейнера в хост-систему без смены номера порта;
- —link — позволяет настроить связь контейнеров Docker;
- -e — добавляет переменную окружения в контейнер;
- -v, —volume — позволяет монтировать папки хоста в контейнер;
- -w — изменяет рабочую директорию контейнера.
Это основные опции, которые мы будем использовать в этой статье, а теперь давайте рассмотрим на примерах, как создать контейнер Docker в Linux.
Создание контейнера Docker
1. Обычный контейнер
Чтобы создать и запустить контейнер с параметрами, заданными в образе по умолчанию, просто запустите команду без параметров. Давайте воспользуемся контейнером hello-world, который как раз для этого и предназначен:
docker run hello-world
После запуска контейнера Docker будет выведено сообщение с приветствием, и процесс в контейнере завершится.
Для поиска уже готовых образов для контейнеров можно использовать веб-сайт DockerHub. Здесь есть образы для большинства дистрибутивов и системных сервисов, таких, как Nginx, Apache, PHP-FPM, PhpMyAdmin и многих других.
Вверху отображается название контейнера, а чуть ниже — доступные версии. При создании контейнера версия записывается через двоеточие. Например, давайте создадим и запустим контейнер с Ubuntu 18.04. Чтобы к контейнеру было легко получить доступ потом, зададим ему имя с помощью опции —name:
docker run --name Ubuntu1804 ubuntu:18.04
2. Подключение к контейнеру
Образ Ubuntu 18.04, на основе которого мы создали контейнер выше, не содержит команды, которая бы постоянно выполнялась, поэтому если вы попытаетесь подключится к нему с помощью команды docker exec, то получите ошибку: You cannot attach to a stopped container, start it first:
docker attach Ubuntu1804
Так происходит потому, что окружение не может работать без основного процесса, для которого и создан контейнер. Пока работает процесс, будет работать и контейнер. Как только процесс завершён, контейнер завершается, и созданный нами ранее контейнер полностью бесполезен. Вы не сможете к нему подключится, потому что он будет падать сразу же после старта, а к упавшему контейнеру подключится нельзя. Его можно удалить:
docker rm Ubuntu1804
А нам надо создать контейнер с командой, которая будет выполняться. Для этого просто передайте команду после имени образа, например /bin/bash. Чтобы контейнер был доступен интерактивно, создавайте его с опциями -i и -t:
docker run -it --name Ubuntu1804 ubuntu:18.04 /bin/bash
Теперь вы в контейнере и можете выполнять действия прямо в изолированном окружении.
3. Переменные окружения
Очень часто для изменения настроек контейнера используются переменные окружения. Вы задаёте какую-нибудь переменную окружения, а затем её значение используется вашей программой в самом контейнере для выполнения различных действий. Для задания переменных окружения используется опция -e. Запуск контейнера Docker:
docker run -it -e "FOO=bar" --name Ubuntu1804_2 ubuntu:18.04 /bin/bash
echo $FOO
4. Монтирование папок и хранилищ
Когда вам нужно, чтобы контейнер мог работать с исходными кодами программы или с часто изменяемыми данными, рекомендую сделать специальную настройку — будет неудобно каждый раз копировать информацию в контейнер. Куда проще примонтировать папку хоста в контейнер, и все изменения исходников будут сразу же доступны в контейнере. Это очень удобно. Для монтирования папки следует передать её опции -v. Например, примонтируем папку ~/test_docker в контейнер:
docker run -it -v "~/test_docker:/mnt" --name Ubuntu1804_3 ubuntu:18.04 /bin/bash
Хранилища позволяют монтировать в контейнер виртуальный диск, который не удаляется при удалении контейнера. Такое хранилище будет автоматически создано, если передать не существующую папку, а имя для нового хранилища:
docker run -it -v docker_volume:/mnt --rm --name Ubuntu1804_4 ubuntu:18.04 /bin/bash
Напоминаю, что опция —rm удаляет контейнер сразу же после того, как он завершит свою работу. Посмотреть список доступных хранилищ можно командой:
docker volume list
5. Порты контейнера
Если вам нужно получить доступ к какому-либо сервису контейнера по порту, например к веб-интерфейсу, этот порт надо пробросить в хост-систему. Для этого используется опция -p. Давайте установим Nginx и пробросим его порт в хост-систему:
docker run --name Nginx -p 8080:80 -d nginx
6. Связывание контейнеров
Связывание контейнеров позволяет настроить взаимодействие между ними. Связанный контейнер будет доступен по сети по его имени. Соответствующая строчка будет автоматически добавлена в файл /etc/hosts контейнера. Для связывания используется опция —link. Чтобы закрепить знания, полученные выше, давайте создадим контейнер с базой данных MySQL, а затем свяжем его с PhpMyAdmin.
Сначала создаём контейнер MySQL с постоянным хранилищем в /var/lib/mysql. В переменных окружения ему надо передать пароль суперпользователя. Какие переменные окружения ожидает получить контейнер — эту информацию обычно можно найти на странице контейнера на DockerHub. Используйте опцию -d, чтобы контейнер запустился в фоновом режиме:
docker run -v mysql_volume:/var/lib/mysql --name MySQL -e MYSQL_ROOT_PASSWORD=passwd -d mysql:8.0
Затем создаём контейнер с PhpMyAdmin для доступа к этой базе данных и связываем с ним контейнер MySQL под именем db:
docker run --name PhpMyAdmin -d --link MySQL:db -p 8080:80 phpmyadmin/phpmyadmin
Также здесь мы пробрасываем порт 80 в хост-систему, чтобы получить доступ к веб-интерфейсу. Теперь можно проверять в браузере. Всё работает.
7. Сеть для контейнеров
Контейнеры можно не связывать. Если надо объединить три и больше контейнеров между собой, то куда удобнее сразу связать их в одну общую сеть, чем создавать множество подключений для каждого из этих контейнеров. Все объекты в одной сети будут иметь доступ к друг другу по их имени. Сначала необходимо создать сеть:
docker network create -d bridge docker_network
Посмотреть список созданных сетей можно командой:
docker network list
Теперь можно её использовать. Объединим с помощью сети наш MySQL- и PhpMyAdmin-сервера. Для этого надо их остановить и удалить:
docker stop MySQL
docker stop PhpMyAdmin
docker rm MySQL
docker rm PhpMyadmin
Затем создаём:
docker run -v mysql_volume:/var/lib/mysql --network docker_network --name MySQL -e MYSQL_ROOT_PASSWORD=passwd -d mysql:8.0
А для PhpMyAdmin теперь надо передать хост, на котором расположена база данных в переменной окружения PMA_HOST:
docker run --name PhpMyAdmin -d --network docker_network -e PMA_HOST=MySQL -p 8080:80 phpmyadmin/phpmyadmin
Выводы
В этой статье мы рассмотрели, как запустить контейнер Docker и настроить его параметры. Как видите, Docker может быть очень полезным для быстрого разворачивания рабочих окружений. Но редко для этих целей используют саму команду docker run. Обычно используется инструмент docker-compose. Интересно ли вам прочитать об этом инструменте? Напишите в комментариях!