Зачем нужен Docker и практика работы с ним
16 февраля 2015
Docker — это инструмент, предоставляющий удобный интерфейс для работы с контейнерами. Процессу, запущенному под Docker, кажется, что он работает в минимальном окружении, где помимо него есть только его дети. Хотя при этом процесс работает в той же операционной системе, что и остальные, нормальные, процессы, он просто их не видит, ровно как не видит файлов и всего остального за пределами своей «песочницы». Можно думать о Docker, как о прокачанном chroot или аналоге FreeBSD Jails.
Зачем нужен Docker, если есть тот же Vagrant? Главным образом, просто потому что виртуализация в Docker дешевле. Используя Vagrant, вы эмулируете работу целой операционной системы, тогда как Docker позволяет изолировать просто один процесс. Соответственно, используя одно и то же железо, с помощью Docker вы можете создать больше виртуальных окружений, чем при помощи Vagrant. Более того, контейнеры запускаются (и останавливаются тоже) практически моментально, так как в них не происходит загрузка отдельной ОС. К тому же, Docker использует «слоеную» файловую систему AuFS, благодаря которой контейнеры могут совместно использовать одинаковые части файловой системы, доступные только на чтение. Если образ файловой системы занимает 5 Гб и вы запускаете 100 виртуальных окружений, то Vagrant потребуется 500 Гб места, а Docker — на порядок меньше.
Ну понятно, то есть, Docker во всем лучше Vagrant? На самом деле — нет. Docker не позволит вам запустить Windows, если вы сидите под Ubuntu. Слои AuFS накапливаются, из-за чего приходится время от времени их «схлопывать», а также прибегать к другим трюкам. Отмечаются проблемы в безопасности и общая сложность Docker, из-за которых даже начались работы над альтернативным проектом Rocket. Наконец, в Docker есть целый ряд косяков, которые приходится либо обходить какими-то хаками, либо находить образы, в которых эти хаки уже расставили за тебя.
В общем и целом, если вас всем устраивает Vagrant, скорее всего, вам нужен Vagrant, а не Docker.
Прежде, чем мы перейдем к основам работы с Docker, нужно отметить еще один момент. Есть как минимум два способа запуска приложений в Docker. Первый — просто запустить с тонной флагов, указывающих, куда писать логи, где брать конфиг, какой порт слушать и так далее (поскольку это просто запуск процесса, а не sudo service ololo start
), скрестить пальчики и надеяться, что все будет хорошо, даже при условии, что в окружении нет корректного initd, syslog, crond, и так далее. Второй — использовать специальный образ, например, baseimage, в котором решены эти и многие другие проблемы, тупо зайти в окружение по ssh, настроить все по-человечески и запускать приложение в полном соответствии с ожиданиями его разработчиков.
Я лично всегда был сторонником второго способа. Хотя находятся люди, утверждающие, что второй способ, видите ли, не идиоматичный, и единственным правильным способом является первый.
Дополнение: С момента написания этого поста появился Docker Desktop, позволяющий пользоваться Docker под Windows и MacOS.
Установка Docker. В Ubuntu 14.04 поставить Docker можно, воспользовавшись пакетом docker.io, но Docker вы в результате получите доисторический. Предпочтительнее воспользоваться следующей последовательностью действий:
--recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
sudo sh -c "echo deb https://get.docker.com/ubuntu docker main \
> /etc/apt/sources.list.d/docker.list"
sudo apt-get update
sudo apt-get install lxc-docker
Аналогами боксов из мира Vagrant в мире Docker являются образы (image). Множество готовых образов с предустановленными MongoDB, WordPress и много чем еще можно найти в официальном реестре. Мы с вами, как уже отмечалось, воспользуемся baseimage:
Запускаем контейнер:
Прописываем свой ключик в ~/.ssh/authorized_keys. Теперь важный момент. Если остановить контейнер, все сделанные в нем изменения откатятся. Поэтому нужно сохранить состояние контейнера в новый образ. В соседнем терминале смотрим список запущенных контейнеров:
Из списка нас сейчас интересует container id. Сохраняем образ и останавливаем контейнер:
sudo docker stop 2fd
Теперь запускаем новый образ (-d
означает «демонизировать»):
По умолчанию контейнеры как бы находятся за NAT — они могут свободно ходить в сеть, но из сети нельзя так просто попасть в контейнеры. При этом с точки зрения хост-системы контейнеры находятся в сети 172.17.42.0/16. Узнать IP конкретного контейнера можно командой:
Насколько я могу судить, на данный момент нет простого способа присвоить контейнеру заранее известный IP адрес. Однако можно забиндить порты контейнера на интерфейсы-порты хост-системы, а также заставить контейнеры видеть друг друга при помощи файлов /etc/hosts. Подробности описаны здесь.
Давайте прибьем контейнер, затем запустим его снова, пробросив порт 22 контейнера на 127.0.0.1:222, и зайдем внутрь контейнера по ssh:
sudo docker run --dns 192.168.0.1 -p 127.0.0.1:222:22 \
-d -i -t baseimage-ssh /sbin/my_init
ssh -p 222 root@localhost
Адрес DNS-сервера, используемого хост-системой, можно узнать, сказав:
Итак, мы в системе! Теперь можно настраивать все и вся привычными нам средствами. Давайте, например, поднимем Nginx:
apt-get install nginx
service nginx start
curl localhost
Должны увидеть:
<html>
<head>
<title>Welcome to nginx!</title>
...
Поскольку мы используем образ baseimage, а не совсем честную Ubuntu, для того, чтобы Nginx запускался при старте контейнера, нужно дополнительно отредактировать файл /etc/my_init.d/01_services.sh:
service nginx start
А затем сказать:
Теперь открываем терминал в хост-системе и сохраняем образ:
sudo docker commit 25f baseimage-nginx
sudo docker stop 25f
Запускаем получившийся образ и проверяем:
--name docker-nginx --dns 192.168.0.1 \
-p 127.0.0.1:222:22 -p 127.0.0.1:8080:80 \
-d -i -t baseimage-nginx /sbin/my_init
curl localhost:8080
Заметьте, что здесь мы присвоили контейнеру имя, а то сколько уже можно пользоваться этими хэшами? Остановить контейнер теперь можно так:
Если вдруг вы ошиблись во время настройки контейнера, то можете либо откатиться к последней рабочей версии, либо починить текущую как-то так:
--skip-startup-files -- bash -l
Наконец, образы можно экспортировать:
… и импортировать:
Кстати, экспортированные образы неплохо gzip’уются.
За кадром остался еще целый ряд интересных вопросов, например, ограничение ресурсов, используемых контейнером, монтирование части файловой системы хоста в госте, взаимодействие контейнеров друг с другом или написание Dockerfile. Но во всем этом вы и без меня разберетесь при помощи официальной документации.
А для каких целей вы используете Docker и используете ли его вообще?
Дополнение: Также вас могут заинтересовать посты Быстрое введение в Kubernetes и Тестирование проектов на Go с dockertest.
Метки: Linux, Виртуализация.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.