Docker управление контейнерами: базовые возможности. Понимая Docker Преимущества Docker перед LXC, OpenVZ и другими решениями виртуализации уровня ОС

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

Из этого руководства вы узнаете о связи контейнеров и образов Docker, а также о том, как устанавливать, запускать, останавливать и удалять контейнеры.

Обзор

Образ Docker можно представить в качестве некоторого шаблона, который используется для создания контейнеров. Образы обычно начинаются с корневой файловой системы, к которой затем сверху слоями добавляются различные изменения и соответствующие им параметры запуска. В отличие от типичных дистрибутивов Linux, образ Docker обычно содержит только части, которые необходимы для запуска приложения. У образов нет статусов, и они не изменяются. Правильнее сказать, что они являются исходной точкой, основой для контейнеров Docker.

Образы «оживают» в тот момент, когда вы вводите команду docker run - она сразу же создает контейнер в результате добавления поверх образа новый уровень для чтения и записи. Эта комбинация уровней только для чтения (поверх которых добавляется уровень для чтения и записи) также известна как UnionFS - файловая система, производящая каскадно-объединённое монтирование файловых систем. Когда в существующий файл запущенного контейнера вносится какое-либо изменение, файл копируется из области только для чтения на уровень для записи и чтения, где и применяются эти изменения. И теперь изначальный файл скрыт версией с уровнем для записи и чтения, но он не удален. Подобные изменения в уровне для записи и чтения существуют только внутри данного отдельного контейнера. Когда контейнер удаляется, все изменения также теряются (если они не были сохранены).

Работа с контейнерами

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

Шаг 1: создание двух контейнеров

Написанная ниже команда docker run создает новый контейнер, который в качестве основания будет использовать образ Ubuntu. Ключ -t предоставит терминал, а -i - возможность взаимодействовать с ним. Для того, чтобы оказаться внутри контейнера, можно использовать стандартную команду bash. То есть вы можете ввести:

$ docker run -ti ubuntu

$ docker run -i -t ubuntu:14.04 /bin/bash

(во втором случае вы запустите команду /bin/bash внутри контейнера и автоматически окажетесь внутри контейнера)

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

Root@11cc47339ee1:/#

Теперь, используя команду echo, внесите изменения в директорию /tmp, а затем проверьте, что изменения были записаны при помощи команды cat:

Echo "Example1" > /tmp/Example1.txt cat /tmp/Example1.txt

На экране вы должны увидеть:

Теперь выйдите из контейнера:

Как только данная команда была выполнена, и вы вышли из командной строки, контейнер Docker перестал работать. Увидеть это вы можете, если используете команду docker ps:

Среди запущенных контейнеров вы не увидите тот, который использовался выше:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

Однако вы можете добавить ключ -a для того, чтобы увидеть все контейнеры - как работающие, так и остановленные - и тогда вам высветится контейнер, в котором вы работали ранее:

$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 11cc47339ee1 ubuntu "/bin/bash" 9 minutes ago Exited (127) 10 seconds ago small_sinoussi

Когда создается контейнер, у него появляется ID и автоматически сгенерированное название. В данном случае 11cc47339ee1 - это идентификационный номер (ID) контейнера, а small_sinoussi - сгенерированное имя. Команда ps -a показывает эти данные, а также образ, из которого контейнер был создан (в данном случае ubuntu), когда контейнер был создан (9 минут назад), и какая команда была в нем запущена ("/bin/bash”). Также вы можете посмотреть статус контейнера (из него вышли 10 секунд назад). В том случае, если бы контейнер до сих пор работал, вы бы увидели статус "Up" и время, которое он уже работает.

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

$ docker run -ti ubuntu

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

Root@6e4341887b69:/# cat /tmp/Example1

Вывод будет:

Cat: /tmp/Example1: No such file or directory

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

Root@6e4341887b69:/# exit $ docker ps -a

Вывод будет:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6e4341887b69 ubuntu "/bin/bash" About a minute ago Exited (1) 6 seconds ago kickass_borg 11cc47339ee1 ubuntu "/bin/bash" 15 minutes ago Exited (127) 6 minutes ago small_sinoussi

Шаг 2: перезапуск первого контейнера

Для того, чтобы заново запустить уже созданный контейнер, необходимо команду start использовать с двумя ключами -ai. В конце вам необходимо написать идентификационный номер контейнера, с которым вы хотите работать, либо его название. В итоге ваша команда будет выглядеть следующим образом:

Docker start -ai 11cc47339ee1

Теперь вы снова находитесь в оболочке bash внутри контейнера и можете убедиться в том, что файл, который вы создавали в начале статьи, все еще находится здесь:

Cat /tmp/Example1.txt

Вы увидите на экране:

Теперь вы можете выйти из контейнера:

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

Шаг 3: удаление обоих контейнеров

Завершающим шагом будет удаление двух контейнеров, которые вы создали, следуя данному руководству. Для этого необходимо использовать команду docker rm. Однако она действует только на остановленные контейнеры. После команды необходимо указать идентификационный номер либо название одного или нескольких контейнеров. К примеру, чтобы удалять контейнеры, созданные ранее, необходимо ввести команду:

Docker rm 6e4341887b69 small_sinoussi

На экране высветится:

6e4341887b69 small_sinoussi

Теперь оба контейнера были удалены.

Заключение

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

Справка по командам управления образами и контейнерами Docker.

Термины

Образ - это статический билд на основе определенной OS.

Контейнер - это запущенный инстанс образа.

Права на запуск docker

Чтобы запускать Docker контейнеры под своим пользователем (без sudo), нужно добавиться в соответствующую группу:

Sudo usermod -aG docker YOU_USER

Сервис Docker

Управление сервисом Docker"а:

Sudo service docker start|stop|restart|status sudo restart docker # алиас

Образы

Список доступных образов:

Docker images

Скачать образ (или весь репозиторий) из официального регистра (хранилища образов):

Docker pull ubuntu:14.04

Посмотреть информацию об образе:

Docker inspect ubuntu

Удалить образ:

Docker commit CONTAINER_ID IMAGE_NAME

Контейнеры

Внимание!

После запуска Docker контейнера сервисы/демоны (как-то SSH, Supervisor и прочие) не будут запускаться автоматически! Я потратил несколько часов на отладку ошибки: "ssh_exchange_identification: read: Connection reset by peer ", при попытке подключиться к контейнеру по SSH. А оказалось, что всего-то не запускался демон sshd. Вы должны будете вручную запускать нужные демоны или супервизор после старта контейнера:

Docker exec CONTAINER_ID bash -c "service ssh start"

Список всех контейнеров (запущенных и остановленных):

Docker ps -a

Удалить контейнер(ы):

Docker rm CONTAINER_ID CONTAINER_ID

Удалить все контейнеры:

Docker rm $(docker ps -aq)

Создать и запустить Docker контейнер c Ubuntu 14.04 в интерактивном режиме (открыть shell этого контейнера):

Docker run -it ubuntu bash docker run [опции] образ [команда] -i Интерактивный режим, держим STDIN открытым -t Allocate/creates a pseudo-TTY that attaches stdin and stdout --name Имя контейнера вместо ID -w Указать рабочую директорию (--workdir) -e Установить переменную окружения в контейнере -u Пользователь:группа под которым должен быть запущен контейнер -v Смонтировать в контейнер файл или каталог хост-системы -p Пробросить порт(ы) контейнера - <порт хост-системы>:<порт контейнера> (--publish=) --entrypoint Заменить дефолтную команду из ENTRYPOINT файла Dockerfile

Примечание

Чтобы отсоединить TTY без остановки контейнера нажмите Ctr + P + Ctrl + Q .

Создать и запустить Docker контейнер в режиме демона с пробросом SSH порта:

Docker run -itd -p 127.0.0.1:221:22 ubuntu

Создать и запустить контейнер с последующим удалением этого контейнера после остановки (полезно для отладки):

Docker run -i -t --rm ubuntu bash

Запустить остановленный контейнер интерактивно:

Docker start -i CONTAINER_ID

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

Docker attach CONTAINER_ID

Команды Docker

Usage: docker COMMAND docker daemon [ --help | ... ] docker [ --help | -v | --version ] A self-sufficient runtime for containers. Options: --config=~/.docker Location of client config files -D, --debug=false Enable debug mode --disable-legacy-registry=false Do not contact legacy registries -H, --host= Daemon socket(s) to connect to -h, --help=false Print usage -l, --log-level=info Set the logging level --tls=false Use TLS; implied by --tlsverify --tlscacert=~/.docker/ca.pem Trust certs signed only by this CA --tlscert=~/.docker/cert.pem Path to TLS certificate file --tlskey=~/.docker/key.pem Path to TLS key file --tlsverify=false Use TLS and verify the remote -v, --version=false Print version information and quit Commands: attach Attach to a running container build Build an image from a Dockerfile commit Create a new image from a container"s changes cp Copy files/folders between a container and the local filesystem create Create a new container diff Inspect changes on a container"s filesystem events Get real time events from the server exec Run a command in a running container export Export a container"s filesystem as a tar archive history Show the history of an image images List images import Import the contents from a tarball to create a filesystem image info Display system-wide information inspect Return low-level information on a container or image kill Kill a running container load Load an image from a tar archive or STDIN login Register or log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container network Manage Docker networks pause Pause all processes within a container port List port mappings or a specific mapping for the CONTAINER ps List containers pull Pull an image or a repository from a registry push Push an image or a repository to a registry rename Rename a container restart Restart a container rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save an image(s) to a tar archive search Search the Docker Hub for images start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop a running container tag Tag an image into a repository top Display the running processes of a container unpause Unpause all processes within a container volume Manage Docker volumes wait Block until a container stops, then print its exit code Run "docker COMMAND --help" for more information on a command.

Docker представляет собой систему управления контейнерами. Он позволяет «упаковать» приложение или веб-сайт со всем его окружением и зависимостями в контейнер, которым в дальнейшем можно легко и просто управлять: переносить на другой сервер, масштабировать, обновлять.

Docker был написан на языке программирования Go и выпущен в 2013 году. Изначально он работал только с Linux-системами, однако на данный момент его можно использовать также в Windows и macOS. Несмотря на то, что проект является относительно новым, он уже широко используется многими специалистами и продолжает завоевывать популярность.

Важной частью экосистемы Docker является Docker Hub - открытый репозиторий образов контейнеров. В нём можно найти десятки готовых приложений от официальных разработчиков. Среди них - nginx, MySQL, Apache, Gitlab, Redmine, Elasticsearch, Jenkins и другие.

Например, для запуска WordPress с помощью Docker достаточно выполнить следующие команды:

Docker run --name wp-mysql -e MYSQL_ROOT_PASSWORD=wpmsqlpsswd -d mysql:5.7 <вывод пропущен> docker run --name my-wordpress --link wp-mysql:mysql -d -p 80:80 wordpress

После этого откройте в браузере страницу http://12.34.56.78 (здесь укажите реальный IP-адрес вашего VDS) и приступите к настройке CMS!

Теперь расскажем о том, что представляет из себя Docker. Три основных термина в экосистеме Docker:

  • образ (image) - шаблон, который используется для создания контейнеров. Представляет собой слепок файловой системы, в котором расположен код приложения и его окружение;
  • реестр (registry) - репозиторий образов. Docker Hub, о котором шла речь выше, - это публичный репозиторий, где хранится огромное количество образов;
  • контейнер (container) - запущенное приложение, т.е. совокупность процессов и образа.

Преимущества Docker

Как уже было замечено выше, Docker - это в первую очередь удобство: выполнить многие действия - к примеру, создать готовое для запуска приложение или установить дополнительное ПО - становится намного проще и быстрее. Используя Docker, вы сможете оптимизировать свою работу и экономить время.

Возьмем ситуацию, когда вам нужно установить Redmine, Github, Jenkins или что-либо другое. В обычной ситуации вы бы самостоятельно устанавливали пакеты, настраивали окружение, а также отдельно устанавливали бы веб-сервер. С Docker все намного проще - вам просто необходимо запустить готовый контейнер из репозитория hub.docker.com . В базе Docker содержится более 100 тысяч готовых к установке приложений, среди которых вы точно найдете нужное.

Образы контейнеров Docker содержат в себе все необходимые приложению библиотеки, поэтому конфликтов с другим ПО не будет.

Стоит также отдельно отметить, что, используя Docker, вы сможете легко перенести приложение в другую среду: достаточно загрузить образ в репозиторий и скачать его на сервере. И вы можете не беспокоиться, что запущенное приложение использует все ваши ресурсы - Docker позволяет настроить количество ресурсов, выделенное под каждое приложение.

Docker в VDS Timeweb

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

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


Соответственно, работа с Docker может производиться также либо в консоли, либо в соответствующем интерфейсе.

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

В том, что Docker - это действительно must have инструмент для разработчика и администратора сколько-нибудь крупного проекта. Но даже если это не так, Docker все равно нужно знать: уже в самом ближайшем будущем он будет везде, начиная от десктопного Linux-дистрибутива и заканчивая пулом серверов на AWS. А самое приятное, что разобраться с Docker довольно легко, если, конечно, правильно понимать принцип его работы.

Apt-get в мире виртуальных окружений

Docker базируется на технологиях namespaces и cgroups (первая обеспечивает изоляцию, вторая - группировку процессов и ограничение ресурсов), поэтому в плане виртуализации он мало чем отличается от привычных нам LXC/OpenVZ, и рассказывать тут особо не о чем. Та же нативная скорость работы, те же методы изоляции, основанные на механизмах ядра Linux. Однако уровнем выше начинается совсем другая история. Изюминка Docker в том, что он позволяет развернуть полноценное виртуальное окружение и запустить в нем приложение так же просто, как, например, перезапустить веб-сервер.

Абстрагируемся от деталей конкретных дистрибутивов и представим, что у нас есть чистый CentOS и мы хотим запустить в нем определенную команду в полностью виртуальном окружении без доступа к основной системе. Придется скачивать образы дистрибутивов, разворачивать их в систему и настраивать виртуальное окружение? Вовсе нет, все, что нужно сделать, - это запустить две команды:

$ sudo yum install docker-io $ sudo docker run -t ubuntu:latest /usr/bin/top

И это все. Мы только что запустили утилиту top внутри контейнера с окружением на базе последней доступной на данный момент версии Ubuntu с выводом информации в текущий терминал. И все это с помощью одной простой команды (установка не в счет). Неплохо, не правда ли? В общем-то, мы можем даже «зайти» в этот контейнер и делать все то, что обычно делают со свежеустановленной системой:

$ sudo docker run -t -i ubuntu:latest /bin/bash # apt-get update # apt-get install nginx #

Как видишь, с сетью тоже все ОK, поэтому мы можем обновить систему, установить и настроить любой софт. Немного похоже на магию, но на самом деле все очень просто. Docker - это своего рода apt-get в мире контейнеров, только вместо пакетов здесь образы файловой системы, а вместо официальных Debian/Ubuntu-репозиториев - облачное хранилище, называемое Docker Hub.

Когда мы выполнили «docker run...», система сделала следующее:

  1. Утилита docker связалась с демоном dockerd на нашей локальной машине, передала от нас привет и попросила запустить последнюю версию Ubuntu (об этом говорит тег latest в команде) в изолированном контейнере.
  2. Демон dockerd сверился со своей записной книжкой, сходил в каталог /var/lib/docker и выяснил, что образа файловой системы с последней Ubuntu на нашей машине нет, поэтому он решил обратиться к Docker Hub с целью выяснить, а есть ли такой образ там.
  3. Пообщавшись с Docker Hub, он убедился, что образ все-таки существует, и попросил отправить его нам.
  4. Получив нужный образ, dockerd смонтировал его файловую систему, сделал в нее chroot и запустил указанную в последнем аргументе команду, ограничив ее «область видимости» с помощью namespaces (по сути, отрезал ей доступ к основной ФС, процессам хост-системы, IPC и прочему, заперев в песочнице), но перекинул в нее файлы устройства текущего терминала (флаг -t), чтобы наш top смог отрисовать свой псевдографический интерфейс.

Изюминка такой модели в том, что Docker Hub открыт для всех и любой может подготовить собственный образ (об этом позже) и опубликовать его с целью установки на другую машину и/или другим человеком. На момент написания статьи в Docker Hub было опубликовано более 45 тысяч образов на все случаи жизни, начиная от образов «голых» дистрибутивов и заканчивая образами с преднастроенными серверными и десктопными приложениями, работающими в минималистичном Linux-окружении.

Что, если мы хотим запустить Firefox внутри виртуального окружения? Нет ничего проще, открываем Docker Hub в браузере, нажимаем Browse & Search и вбиваем firefox. На экран вывалится список результатов. Смотрим, kennethkl/firefox вроде вполне подходит. Клацаем по нему и видим инфу, как все это дело запустить. Автор говорит нам выполнить такую команду:

$ sudo docker run -d --name firefox -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix kennethkl/firefox

Пробуем. Да, действительно, после недолгого скачивания образа получаем на экране стандартный Firefox. На этом же примере, кстати, можно ознакомиться с еще четырьмя полезными опциями команды docker run:

  • -d - «демонизирует» контейнер, то есть просто отключает Docker от STDOUT виртуального окружения и позволяет ему работать в фоне;
  • --name - имя контейнера, которое он получит вместо идентификатора;
  • -e - позволяет «пробросить» в виртуалку переменную окружения;
  • -v - пробрасывает указанный файл или каталог (формат /файл/на/хост/системе:/файл/в/виртуалке или просто /файл/на/хост/системе, если пути совпадают).

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

Есть и более простой способ поиска образов Docker, с помощью команды docker search:

$ sudo docker search nginx

INFO

Любой пользователь Docker может запустить свой личный приватный Hub. Он носит название «реестр» и доступен в виде уже готового образа. Все, что нужно сделать, - это просто запустить его: docker run -p 5555:5555 registry.

Демон Docker доступен не только с помощью клиента, но и с использованием RESTful API, причем как локально, так и с удаленной машины. Стандартные порты Docker - tcp/2375 e tcp/2376.

Образ Docker не обязательно запускать сразу после скачивания, можно сначала скачать его на локальную машину с помощью команды docker pull, а лишь затем запустить: docker pull ubuntu.

Слоеный пирог

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

Если ты уже успел поиграть с образом Ubuntu из первых двух примеров, то наверняка заметил, что каждый новый запуск контейнера происходит «с нуля», а все изменения, сделанные в прошлом сеансе, теряются. Это вовсе не баг, это одна из ключевых особенностей архитектуры Docker, которая делает его еще более интересным и привлекательным решением.

Дело в том, что в подавляющем большинстве случаев «образ Docker» - это вовсе не монолитный образ файловой системы, а своего рода слоеный пирог, состоящий из нескольких образов файловых систем, на основе которых формируется контейнер. При этом отдельно взятые образы ФС вовсе не отвечают за те или иные части структуры каталога (как, например, в случае с разбиением диска под Linux на разделы /home, /var, /boot), а наслаиваются друг на друга с помощью механизма AUFS ядра Linux (также есть поддержка той же функциональности через использование btrfs, device mapper и overlay).

Чтобы разобраться с тем, как это работает, вернемся к нашей контейнерной Ubuntu. Запускаем контейнер и устанавливаем nginx, как показано во втором примере в начале статьи, но не завершаем его. Вместо этого запускаем еще один терминал и смотрим список запущенных контейнеров:

$ sudo docker ps

Эта команда покажет все запущенные контейнеры вместе с их ID, используемым образом, запущенной командой, временем работы и прочим. Нас интересует значение в столбце CONTEINER ID. Копируем его и запускаем следующую команду:

$ sudo docker commit ID-контейнера ubuntu-nginx

После того как она отработает, можно выйти из контейнера, таким образом завершив его работу. А далее просто запускаем контейнер ubuntu-nginx и видим, что nginx никуда не пропал и находится на своем месте:

$ sudo docker run -i -t ubuntu-nginx /bin/bash # which nginx /usr/sbin/nginx

Что же мы сделали? Мы создали еще один слой, то есть дополнительный образ ФС, и сгенерировали новый Docker-образ на основе уже существующего Docker-образа Ubuntu с включением нашего образа ФС, который содержит nginx. Звучит немного путано, правда? На самом деле все довольно просто.

Мы уже выяснили, что каждый Docker-образ состоит из нескольких образов ФС. Когда мы запускаем контейнер, эти образы монтируются и собираются в одну структуру каталога с помощью AUFS. Например, первый образ может содержать только базовую установку Ubuntu, второй добавляет к ней набор стандартных демонов, третий - утилиты администрирования и так далее. Docker монтирует все слои в режиме «только чтение», но, чтобы мы имели возможность изменять содержимое образа, сверху подключается еще один изначально пустой слой в режиме «чтение/запись».


По умолчанию после завершения контейнера (которое происходит после завершения последнего работающего в нем процесса) последний слой стирается и все наши изменения пропадают. Однако, используя команду docker commit, мы можем «зафиксировать» изменения, создав новый Docker-образ на основе уже существующих образов ФС плюс образа ФС с нашими изменениями. Так внесенные нами изменения сохранятся. По желанию мы можем запустить контейнер ubuntu-nginx, внести в него изменения и точно так же сохранить в новый Docker-образ с помощью commit, добавив еще один слой. Чтобы посмотреть список всех получившихся в итоге (и полученных из Docker Hub) образов, можно использовать команду docker images, а для просмотра истории формирования слоев - команду docker history:

$ sudo docker history ubuntu-nginx

Такой подход к формированию образов дает большую гибкость в управлении контейнерами, экономит уйму времени и позволяет с легкостью переносить уже сконфигурированные Docker-образы между машинами (образ можно выложить на Docker Hub и затем развернуть на другой машине). Менее очевидный плюс - экономия дискового пространства. Если мы развернем на машине целый зоопарк контейнеров, каждый из которых будет изначально основан на одном базовом образе (той же Ubuntu, например) - они все будут ссылаться на этот базовый образ и не дублировать его содержимое.


Docker вне Linux

Единственный способ запустить Docker в OS X или Windows - это установить его в виртуальную машину. Не обязательно делать это вручную, можно воспользоваться уже готовым решением, например boot2docker. Это набор скриптов, которые позволяют быстро развернуть виртуальную машину с Linux и Docker внутри VirtualBox и запустить ее с автоматическим открытием доступа по SSH. Инструкцию по его использованию и сам инсталлятор можно найти на официальном сайте Docker .

Настройка сети

Для того чтобы контейнеры могли общаться между собой и с внешним миром, Docker автоматически поднимает виртуальный сетевой мост и настраивает правила маскарадинга (NAT) для внешнего сетевого интерфейса. Это значит, что извне достучаться до контейнеров не получится. Однако мы можем настроить проброс портов, чтобы запрос к определенным портам внешнего сетевого интерфейса машины автоматически перенаправлялся на указанные порты контейнера. Например, в компании Mirantis главный узел Fuel (это такой GUI для деплоя и настройки OpenStack) запускается в Docker и использует функцию проброса портов, чтобы открыть доступ к контейнеру ful/nginx (порт 8000):

$ sudo docker run -d -p 8000:8000 fuel/nginx_6.0:latest /usr/local/bin/start.sh

Мы могли бы пробросить порт 8000 на любой другой порт контейнера, просто изменив второе число в опции -p, но в данной конфигурации это не имеет смысла.

Проброс файлов и Dockerfile

В начале статьи мы уже познакомились с флагом -v, позволяющим пробросить в контейнер любой файл или каталог из хост-системы. Это очень удобная функция, ее можно использовать как для хранения каких-либо временных данных, так и для расшаривания файлов между несколькими контейнерами. В Mirantis эта функция используется для проброса файлов конфигурации сервиса Fuel/astute (/etc/astute) внутрь контейнера:

$ sudo docker run -d -v /etc/astute fuel/astute_6.0:latest /usr/local/bin/start.sh

То же самое можно сделать с помощью команды VOLUME в Dockerfile. Сам по себе Dockerfile - это местный эквивалент Makefile, но если последний предназначен для сборки приложений из исходных текстов, то Dockerfile позволяет собирать образы для Docker. Его назначение - упростить создание новых образов без необходимости запускать контейнер, производить в нем какие-то операции и выполнять коммит. Ты можешь просто написать Dockerfile, и Docker сделает все за тебя. Для примера рассмотрим Dockerfile для сборки Fuel/astute:

FROM fuel/centos MAINTAINER Matthew Mosesohn [email protected] RUN rm -rf /etc/yum.repos.d/*;\ echo -e "\nname=Nailgun Local Repo\nbaseurl=http://$(route -n | awk "/^0.0.0.0/ { print $2 }"):_PORT_/os/x86_64/\ngpgcheck=0" > /etc/yum.repos.d/nailgun.repo;\ yum clean all;\ yum --quiet install -y ruby21-nailgun-mcagents sysstat ADD etc /etc ADD start.sh /usr/local/bin/start.sh RUN puppet apply --detailed-exitcodes -d -v /etc/puppet/modules/nailgun/examples/astute-only.pp; [[ $? == 0 || $? == 2 ]] RUN chmod +x /usr/local/bin/start.sh;\ echo -e "\nname=Nailgun Local Repo\nbaseurl=file:/var/www/nailgun/centos/x86_64\ngpgcheck=0" > /etc/yum.repos.d/nailgun.repo; yum clean all VOLUME /etc/astute CMD /usr/local/bin/start.sh

Нетрудно понять, для чего он предназначен. Он создает образ на базе fuel/centos, запускает несколько команд для подготовки образа, добавляет в образ файлы из текущего каталога, применяет манифест Puppet, меняет права доступа на некоторые файлы, пробрасывает в контейнер каталог /etc/asture/ из хост-системы и запускает контейнер с помощью команды /usr/local/bin/start.sh.

Для сборки контейнера достаточно положить Dockerfile и все файлы, которые будут добавлены в него, в какой-нибудь каталог и выполнить следующую команду:

$ sudo docker build fuel/astute_6.0:latest

В данном случае мы выбрали имя fuel/astute_6.0:latest, хотя оно может быть любым.

Нюансы работы с Docker

Docker построен вокруг идеи о том, что в каждом контейнере должен работать только один сервис. Ты расфасовываешь Apache, MySQL, nginx, Varnish и все, что может понадобится для проекта, по разным контейнерам, а затем используешь Docker для сборки всего этого вместе. Такой подход дает большую гибкость, так как позволяет с легкостью менять конфигурацию, тестировать обновления и выполнять миграцию отдельных сервисов на другие машины.

По этой же причине Docker не принято использовать для запуска полноценных Linux-окружений с демоном init, демонами cron и syslog и другими стандартными компонентами дистрибутива. Вместо этого мы просто запускаем нужный нам сервис, и он работает в виртуальном окружении в полном одиночестве:

$ sudo docker run -d -p 80 ubuntu-nginx /usr/sbin/nginx

Но здесь есть небольшая проблема. Docker завершает работу контейнера сразу после того, как будет завершен запущенный в нем процесс (в данном случае nginx), а так как nginx по умолчанию демонизируется, то есть форкает новый процесс и завершает тот, что мы запустили руками, то Docker сразу после этого завершает и контейнер, прибивая форкнутый Docker.

В случае с nginx обойти эту проблему можно, добавив daemon off; первой строкой в его конфиг. Для других демонов потребуются свои настройки, а некоторым можно запретить демонизироваться прямо из командной строки. Например, в sshd для этого предусмотрен флаг -D:

$ sudo docker run -d -p 22 ubuntu-ssh /usr/sbin/sshd -D

В любой момент к контейнеру можно подключиться с помощью команды docker exec с целью просмотреть логи или изменить настройки (здесь и далее ID-контейнера - это либо ID, которое можно увидеть в выводе docker ps, либо имя, заданное при запуске в опции --name):

$ sudo docker exec -ti ID-контейнера /bin/bash

Но и здесь есть одна небольшая загвоздка. Как мы знаем, вся накопленная во время работы виртуального окружения информация потеряется, если мы завершим работу виртуального окружения, а вместе с ней исчезнут логи и изменения, внесенные в настройки. Бесконечно создавать слои мы тоже не можем (хотя бы потому, что их может быть не больше 127), но мы можем пойти немного другим путем и воспользоваться встроенной в Docker системой агрегации логов. Конечно, Docker не умеет собирать логи отдельных приложений, но умеет накапливать вывод STDOUT, то есть любой консольный вывод. Все, что нам остается, - это изменить конфиг nginx так, чтобы логи сыпались в /dev/stdout, а затем просматривать их с помощью команды docker logs:

$ sudo docker logs ID-контейнера

Другой и более правильный вариант - это просто вынести логи (а если нужно, и настройки) на хост-систему с помощью уже описанной опции -v:

$ sudo mkdir /root/logs $ sudo docker run -d -v /root/logs:/var/logs -p 80 ubuntu-nginx /usr/sbin/nginx

При необходимости контейнер можно остановить корректно, завершив работающий в нем сервис с помощью команды docker stop:

$ sudo docker stop ID-контейнера

А если корректно остановить по какой-то причине не выходит, то можно и прибить его с помощью kill:

$ sudo docker kill ID-контейнера

При этом происходит одна важная вещь, о которой забывают многие новички: Docker сохраняет метаинформацию о контейнере. На деле это значит, что если ты запускаешь, например, nginx, указав с помощью аргументов команды docker run его имя, каталоги, которые нужно пробросить в контейнер, порты, переменные окружения и тому подобное, то вся эта информация будет сохранена при завершении контейнера и, чтобы запустить его в следующий раз, тебе уже не придется ее указывать, а достаточно просто выполнить такую команду (вместо ID можно использовать имя):

$ sudo docker start ID-контейнера

Если в сохранении состояния нет необходимости (например, для тестирования или проверки какой-то функциональности), то можно использовать флаг --rm, который заставит Docker полностью уничтожить контейнер после его завершения (с сохранением образа):

$ sudo docker run --rm -i -t busybox /bin/bash

Уничтожить все ранее сохраненные контейнеры можно с помощью такой команды:

# docker rm $(docker ps -a -q)

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

$ sudo docker run --restart=always \ -d -v /root/logs:/var/logs -p 80 \ ubuntu-nginx /usr/sbin/nginx

В любой момент образ можно экспортировать в единый файл и затем импортировать на другой машине. Для этого предусмотрены команды docker save и docker restore. Использовать их очень просто, экспорт выполняется так:

$ sudo docker save -o ubuntu-nginx.img ubuntu-nginx

А импорт так:

$ sudo docker load -i ubuntu-nginx.img

Выводы

Docker - превосходный инструмент. Для непосвященного человека он может показаться игрушкой, которая не годится больше ни для чего, кроме запуска софта в песочнице, однако с его помощью можно решать огромный спектр задач, о чем мы и поговорим в следующей статье.

Преимущества Docker перед LXC, OpenVZ и другими решениями виртуализации уровня ОС

  1. Docker использует переносимый универсальный формат образов. Это означает, что эти образы могут быть без каких-либо проблем перенесены на другую машину и расшарены для использования другими юзерами.
  2. Образ может служить базой для других образов. В Docker считается нормой использовать множество слоев для формирования конечного образа. Ты можешь начать с базового образа Ubuntu, затем добавить Apache 2.4, чтобы создать микросервис Ubuntu + Apache.
  3. При выполнении коммита образ можно версионировать, так же как это делается в GIT.
  4. У Docker большое комьюнити и обширная экосистема, которая включает серьезное количество инструментов масштабирования, группировки, мониторинга, разворачивания и управления контейнерами.

Внутри Docker только Linux , и, экспериментально, FreeBSD. Запускается нативно под Linux и, экспериментально, под FreeBSD. Под MacOSX, Windows - через виртуальную машину.

Докер - это двойная изоляция. Изоляция того, что лежит внутри контейнера Докера от операционной системы и изоляция операционной системы от того, что лежит внутри Докер. Изоляция подразумевает изоляцию всех файлов, портов, приоритетов.

Это почти виртуальная машина. Почти, да не совсем.

Есть такое понятие "ад зависимостей". Любое ПО устанавливаемое на компьютер, тянет за собой зависимости (конфигурационные файлы, статические файлы называемые обычно asset, вспомогательные утилиты/сервисы, библиотеки и пр.). Ряд из этих библиотек/утилит/сервисов несовместим друг с другом. А с учетом того, что каждая из этих библиотек/утилит/сервисов имеет и свои зависимости - ситуация еще хуже.

Например, мы используем Yandex.Cocaine, которая нормально компилируется только на Ubuntu 14.04 (и, вроде, на Debian 7). Но не под CentOS 6, 7, Debian 8, FreeBSD 9, 10, Ubuntu 15, 16 и пр. - скомпилировать его невозможно . Запускаем в этих операционных системах в Докере.

С другой стороны, и одновременно с этим, вам необходимо установить другое, более современное ПО. И одновременно более старое. Причем речь даже не идет об серьезно отличающихся версиях Linux. Например, одно ПО требует не менее Ubuntu 14.10, а другое не более Linux 14.04.

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

Таким образом, мы имеем бинарный файл запускаемый как бы в своей операционной системе.

Вы можете сказать - ба, да это же давно известная виртуальная машина. Но нет, это не так. Это так называемые контейнера. Никакой виртуальной машиной там и не пахнет. За исключением Windows и MacOSX, где работа без виртуальном машины пока экспериментально возможно только, а нормой в этих ОС является использование Докера внутри полноценной виртуальной машины.

Но виртуальные машины с Докером используются только для разработки. Для запуска в production виртуальные машины с Докер не используются.

Докер использует контейнеры операционной системы. LXC в Linux, Jails в FreeBSD. Контейнер - это область операционной системы, изолированная от основной части операционной системы. В контейнере свое дерево каталогов (включая системные /dev, /bin, /sbin и пр.), свои сетевые порты и пр. и пр.

Но при этом не используется полная виртуализация. Что существенно экономит ресурсы. Запустить 100 полноценных виртуальных машин вряд ли получится даже на мощном сервере. А вот запустить 100 контейнеров Docker даже на слабом домашнем компьютере - возможно.

Правда использование не полной виртуализации ограничивает использование операционных систем внутри контейнеров. Как правило, это специально подготовленные версии Linux или FreeBSD . Именно специально подготовленные. Windows - в принципе в контейнере запустить невозможно.

Контейнеры существовали и до Docker. Докер, строго говоря, это всего лишь очень удобный набор инструментов , собранных воедино, для управления контейнерной виртуализацией. Но очень удобный .

Зачем это используется?

Ребята из всяческих Dropbox, Facebook и и пр. гигантах, запускающие по 1 млн. различных программ в своих сервисах, столкнулись, что невозможно везде гарантировать идентичные настройки операционной системы. А это критично.

Вплоть до того, что идеально написанная и оттестированная программа на реальном сервере начинает себя вести непредсказуемо.

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

Более того - изначально разработчик программного обеспечения тестирует программу в контейнере Докер , с определенными настроками. И в этом же (или с такими же настройками) контейнере Докера программа уезжает на сервер.

Это позволяет гарантировать гораздо большую идентичность среды разработки и среды исполнения.

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

Потом плюнули на попытки упорядочить окружение в ОС - и сейчас концепция такова - устанавливать программы на сервера вместе со своими индивидуально настроенными под них операционными системами - то есть внутри контейнеров. 1 контейнер = 1 настройка ОС = 1 программа внутри.

Другими словами:

  • Докер-контейнер нужно использовать для отладки.
  • Тот же Докер-контейнер нужно использовать и на сервере.

Это позволяет не трудиться с настройками "под сервер" локально на машине разработчика. Это позволяет разрабатывать на машине разработчика совершенно разные программы одновременно , которые требует несовместимых настроек операционной системы . Это позволяет давать гораздо больше гарантий, что программа на сервере будет вести себя также как и на машине разработчика. Это позволяет разрабатывать под Windows/MacOSX с удобным "прозрачным" тестированием под Linux.

Докер применим к созданию/настройке только серверного программного обеспечения под Linux (экспериментально под FreeBSD). Не для смартфонов. А если десктопов - то только программное обеспечение без GUI.

Посколько Докер позволил одним махом упростить работу разработчикам и админам и повысить качество результата - сейчас бум на Докер. Придумано огромная гора инструментов для управления развертыванием приложений созданных с Докером. Если раньше чтобы запустить 10 000 программ на 1000 серверах нужно было как минимум 3 высококвалифицированнейших девопса, которые писали кучу описаний как это сделать на Puppet, Salt, Chef, Ansible, да и то не было гарантий, это все тестилось месяцами. То сейчас с Докер даже один квалифицированных девопс может рулить миллионами программ на десятках тысяч серверов. С куда как большей гарантией, что все это заведется нормально.

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

Разработчик отдает весь свой результат в систему CI (обычно через git)
CI на каждый новый коммит делает с помощью Docker образ для тестирования.
Если тесты проходят успешно, то этот же самый Docker образ, отправляется на развертывание в production.
Или, чуть иначе в компилируемых системах, где исходники не нужны в production: в Docker производится развертывание среды для компиляции, а для тестирования разворачивается второй образ с уже откомпилированным добром, который уже отправляется в production.

То есть при правильной огранизации дела разработчик не может/не должен влиять на то, какой будет образ.
А вот в тестовой среде (запускаемом на сервер, недоступном разработчику в больших командах) и в production как раз используется один и тот же образ .

Основная идея - что тестировали, ровно то и запускаем на боевом сервере. Один-в-один, включая те же самые файлы (не такие же, а именно те же самые).

Поделитесь с друзьями или сохраните для себя:

Загрузка...