Docker stop: docker stop | Docker Documentation
Docker: базовый набор команд | Techrocks
Перевод второй части статьи
«Docker simplified: a hands-on guide for absolute beginners».
В предыдущей статье – «Docker: практическое
руководство для совершенных новичков»
– мы рассмотрели, что такое Docker, где он
может нам пригодиться и как его установить.
А теперь давайте, наконец, познакомимся
с основными командами, которые позволят
вам начать использовать Docker.
docker create
Эта команда Docker позволяет нам создать новый контейнер. Вот синтаксис команды:
docker create [options] IMAGE [commands] [arguments]
Обратите внимание: все, что заключено
в квадратные скобки, – опционально. Это
касается всех команд, которые мы разберем
в этом руководстве.
Рассмотрим пару примеров использования
команды.
$ docker create fedora
02576e880a2ccbb4ce5c51032ea3b3bb8316e5b626861fc87d28627c810af03
В примере, указанном выше, команда
docker create создаст новый контейнер с
использованием последнего образа
Fedora.
Перед созданием контейнера команда
проверит, доступен ли на хосте последний
официальный образ Fedora. Если нет, то
прежде чем создать контейнер она скачает
необходимый образ из Docker Hub. Если же
образ Fedora уже есть на хосте, команда
использует его и создаст контейнер.
После успешного создания контейнера
Docker вернет его ID. Например, в указанном
примере
02576e880a2ccbb4ce5c51032ea3b3bb8316e5b626861fc87d28627c810af03 это
ID контейнера, возвращенный Docker.
Каждый контейнер имеет уникальный
ID. С его помощью мы обращаемся к контейнеру
при осуществлении разнообразных
операций, таких как запуск, остановка,
перезапуск и так далее.
Давайте рассмотрим другой пример
использования команды docker create, с опциями
и передаваемыми командами.
$ docker create -t -i ubuntu bash
30986b73dc0022dbba81648d9e35e6e866b4356f026e75660460c3474f1ca005
В этом примере команда docker create создает
контейнер, используя образ Ubuntu. Как уже
говорилось, если на хосте нужного образа
не окажется, он будет скачан из Docker Hub.
Опции -t и -i говорят Docker назначить
контейнеру терминал, чтобы пользователь
мог взаимодействовать с контейнером.
Также Docker получает инструкцию выполнять
команду bash при каждом старте.
docker ps
Команда docker ps дает нам возможность
увидеть все контейнеры, запущенные на
хосте.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
30986b73dc00 ubuntu "bash" 45 minutes ago Up About a minute elated_franklin
Отображаются только те контейнеры,
которые запущены в настоящее время.
Если вы хотите увидеть вообще все
созданные на этом хосте контейнеры
независимо от их текущего статуса, вам
нужно добавить к команде опцию -а.
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
30986b73dc00 ubuntu "bash" About an hour ago Up 29 minutes elated_franklin
02576e880a2c fedora "/bin/bash" About an hour ago Created hungry_sinoussi
Прежде чем идти дальше, давайте разберемся в выводе этой команды.
Вывод команды docker ps
CONTAINER ID: уникальная строка, состоящая
из цифр и букв, привязанная к каждому
контейнеру.
IMAGE: имя образа, который использован
для создания этого контейнера.
COMMAND: любая команда приложения, которая
должна быть выполнена при запуске
контейнера.
CREATED: показывает текущий статус
контейнера, а также сколько времени он
находится в текущем состоянии.
Если контейнер запущен, время будет
отображаться как Up About an hour или Up
3 minutes.
Если контейнер остановлен, будет
написано Exited, в скобках будет указан
код статуса выхода, а затем – время,
прошедшее с момента выхода. Например,
Exited (0) 3 weeks ago или Exited (137) 15 seconds ago, где 0 и
137 это коды выхода.
PORTS: показывает, какие порты определены
для контейнера.
NAMES: помимо CONTAINER ID, у каждого контейнера
есть еще и уникальное имя. Обращаться
к нему можно и по ID, и по имени. При
создании контейнеров Docker автоматически
назначает им дурацкие, хотя и уникальные
имена. Если хотите, чтобы у контенера
было выбранное вами имя, добавьте к
команде docker create или docker run (о ней будем
говорить позже) опцию —name (спереди
двойной дефис).
Надеюсь, теперь вы лучше понимаете
вывод команды docker ps.
docker start
Эта команда запускает любой остановленный
контейнер. Ее синтаксис:
docker start [options] CONTAINER ID/NAME [CONTAINER ID/NAME…]
Для обозначения контейнера можно
брать или несколько первых уникальных
символов из его ID, или его имя. Несколько
примеров:
$ docker start 30986
В примере, указанном выше, Docker запускает
контейнер, чей ID начинается с 30986.
$ docker start elated_franklin
А здесь Docker запускает контейнер с
именем elated_franklin.
docker stop
Эта команда похожа на предыдущую. При
остановке контейнера к нему тоже можно
обращаться или по имени, или по первым
нескольким уникальным символам из ID.
Примеры:
$ docker stop 30986
В указанном выше примере Docker останавливает
контейнер, чей ID начинается с 30986.
$ docker stop elated_franklin
Docker останавливает контейнер по имени
elated_franklin.
docker restart
Эта команда перезапускает любой
контейнер. Синтаксис команды:
docker restart [options] CONTAINER ID/NAME [CONTAINER ID/NAME…]
К контейнеру обращаемся совершенно
аналогично.
$ docker restart 30986
Перезапуск контейнера с ID 30986.
$ docker restart elated_franklin
Перезапуск контейнера с именем
elated_franklin.
docker run
Эта команда сначала создает контейнер,
а потом запускает его. Короче говоря,
это комбинация команд docker create и docker
start. Синтаксис этой команды:
docker run [options] IMAGE [commands] [arguments]
Синтаксис похож на синтаксис команды
docker create. Рассмотрим примеры:
$ docker run ubuntu
30fa018c72682d78cf168626b5e6138bb3b3ae23015c5ec4bbcc2a088e67520
В этом примере Docker создает контейнер,
используя последний образ Ubuntu, и
немедленно запускает его.
Если мы выполним указанную выше
команду, контейнер будет запущен и
моментально остановлен: у нас не будет
никакой возможности с ним взаимодействовать.
Если нам нужно взаимодействовать с
контейнером, мы должны добавить опции
-it, чтобы команда docker run предоставила
нам терминал. С его помощью мы сможем
вводить нужные команды этому контейнеру.
Вот пример такой команды:
$ docker run -it ubuntu
root@e4e633428474:/#
Чтобы выйти из контейнера, нужно
набрать exit в терминале.
docker rm
Эта команда используется для удаления
контейнера. Ее синтаксис:
docker rm [options] CONTAINER ID/NAME [CONTAINER ID/NAME…]
Пример использования:
$ docker rm 30fa elated_franklin
В этом примере одной командой мы
говорим Docker удалить 2 контейнера. Первый
мы обозначаем символами его ID, а второй
– указав его имя.
Внимание: чтобы удалить контейнеры,
их нужно сперва остановить.
docker images
Эта команда выведет все образы Docker,
присутствующие на вашем хосте.
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest 7bb2586065cd 38 hours ago 477MB
httpd latest 5eace252f2f2 38 hours ago 132MB
ubuntu 16.04 9361ce633ff1 2 weeks ago 118MB
ubuntu trusty 390582d83ead 2 weeks ago 188MB
fedora latest d09302f77cfc 2 weeks ago 275MB
ubuntu latest 94e814e2efa8 2 weeks ago 88.9MB
Давайте разберем вывод команды.
REPOSITORY: уникальное имя образа Docker.
TAG: к каждому образу привязан уникальный
тег. Обычно это версия образа, обозначенная
набором цифр или комбинацией букв и
цифр.
IMAGE ID: уникальная для каждого контейнера
строка, состоящая из букв и цифр.
CREATED: показывает время, прошедшее с
момента создания образа.
SIZE: показывает размер образа.
docker rmi
Эта команда позволяет нам удалять
образы из Docker Host. Ее синтаксис:
docker rmi [options] IMAGE NAME/ID [IMAGE NAME/ID…]
Примеры использования:
docker rmi mysql
Эта команда удалит с хоста образ с
именем mysql.
docker rmi httpd fedora
Здесь будут удалены сразу два образа:
httpd и fedora.
docker rmi 94e81
Удаление образа, ID которого начинается
с 94e81.
docker rmi ubuntu:trusty
Эта команда удалит с хоста образ с
именем ubuntu и тегом trusty.
Мы рассмотрели некоторые из основных
команд Docker, но есть и много других.
Заключение
Контейнеризация получила заслуженное
внимание совсем недавно, хотя существовала
уже довольно длительное время. Некоторые
топовые тех-компании, такие как Google,
Amazon Web Services (AWS), Intel, Tesla и Juniper Networks, имеют
собственные версии движков контейнеров.
Они очень широко применяют их в сборке,
запуске, управлении и дистрибуции своих
приложений.
Docker это очень мощный движок
контейнеризации, который может предложить
вам очень многое. Мы рассмотрели его
лишь поверхностно, а в нем есть, что
изучать:
- команды (более мощные, чем рассмотренные
здесь) - образы (создание собственных
образов) - Docker Networking (настройка сетей)
- Docker Services (группирование контейнеров,
использующих один образ) - Docker Stack (группирование сервисов по
приложению) - Docker Compose (инструмент для управления
несколькими контейнерами и их запуска) - Docker Swarm (группирование и управление
оной или большим количеством машин, на
которых запущен docker) - И многое другое…
Если вы заинтересовались Docker и хотите
изучить его подробнее, я бы рекомендовал
вам пройти курсы, указанные ниже. Лично
я нашел их очень познавательными.
Для абсолютных новичков я советую вот
этот курс – он специально разработан
для начинающих.
Если у вас уже есть опыт работы с Docker
и вы хотите расширить свои знания, можете
записаться на этот
курс. Там рассматриваются более
продвинутые темы, имеющие отношение к
Docker.
Docker определенно пригодится вам в будущем, его применение только набирает обороты. Время и деньги, потраченные на изучение Docker, не пропадут даром.
Что такое Docker, и как его использовать? Подробно рассказываем
Разберем по косточкам, ведь Docker – это мощный инструмент, и огромное количество информации по работе с ним вряд ли уместится в брошюрку.
Это ПО с открытым кодом, принцип работы которого проще всего сравнить с транспортными контейнерами. Только подумайте, ведь когда-то транспортные компании сталкивались с похожими проблемами:
- Как перевозить разные (несовместимые) типы товаров вместе (например, продукты питания с химикатами или стекло с кирпичом)?
- Как обрабатывать пакеты разных размеров одним и тем же транспортным средством?
С введением контейнеров стало возможным перевозить вместе кирпичи и стекло, химикаты и еду, а также многое другое. Груз разного размера может быть распределен по стандартизированным контейнерам, которые загружаются/выгружаются одним и тем же транспортным средством.
Но вернемся же к контейнерам. Когда вы разрабатываете приложение, вам нужно предоставить код вместе со всеми его составляющими, такими как библиотеки, сервер, базы данных и т. д. Вы можете оказаться в ситуации, когда приложение работает на вашем компьютере, но отказывается включаться на устройстве другого пользователя.
Эта проблема решается через создание независимости ПО от системы.
Изначально виртуализация была призвана избавить от подобных проблем, но в ней есть существенные недостатки:
- медленная загрузка;
- возможная плата за предоставление дополнительного пространства;
- не все виртуальные машины (VM) поддерживают совместимое использование;
- поддерживающие VM часто требуют сложной настройки;
- образ может быть слишком большим, так как «дополнительная ОС» добавляет гигабайт пространства в проект поверх операционной системы, а в большинстве случаев на сервер ставится несколько VM, которые занимают еще больше места.
Докер же просто разделяет ядро ОС на все контейнеры (Docker container), работающие как отдельные процессы. Это не единственная подобная платформа, но, бесспорно, одна из самых популярных и востребованных.
К его преимуществам относятся:
- Ускоренный процесс разработки. Нет необходимости устанавливать вспомогательные инструменты вроде PostgreSQL, Redis, Elasticsearch: их можно запускать в контейнерах.
- Удобная инкапсуляция приложений.
- Понятный мониторинг.
- Простое масштабирование.
Докер работает не только на его родной ОС, Linux, но также поддерживается Windows и macOS. Единственное отличие от взаимодействия с Linux в том, что на macOS и Windows платформа инкапсулируется в крошечную виртуальную машину. На данный момент Докер для macOS и Windows достиг значительного уровня удобства в использовании.
Кроме того, существует множество дополнительных приложений, таких как Kitematic или Docker Machine, которые помогают устанавливать и использовать Докер на платформах, отличных от Linux.
Здесь можно посмотреть подробную инструкцию по установке. Если вы работаете с Докером на ОС Linux, вам нужно выполнить несколько несложных действий и повторно войти в систему:
sudo usermod -aG docker $(whoami)
1. Контейнер – это исполняемый экземпляр, который инкапсулирует требуемое программное обеспечение. Он состоит из образов. Его можно легко удалить и снова создать за короткий промежуток времени.
2. Образ – базовый элемент каждого контейнера. В зависимости от образа, может потребоваться некоторое время для его создания.
3. Порт – это порт TCP/UDP в своем первоначальном значении. Чтобы все было просто, предположим, что порты могут быть открыты во внешнем мире или подключены к контейнерам (доступны только из этих контейнеров и невидимы для внешнего мира).
4. Том – описывается как общая папка. Тома инициализируются при создании контейнера и предназначены для сохранения данных, независимо от жизненного цикла контейнера.
5. Реестр – это сервер, на котором хранятся образы. Сравним его с GitHub: вы можете вытащить образ из реестра, чтобы развернуть его локально, и так же локально можете вносить в реестр созданные образы.
6. Docker Hub – публичный репозиторий с интерфейсом, предоставляемый Докер Inc. Он хранит множество образов. Ресурс является источником «официальных» образов, сделанных командой Докер или созданных в сотрудничестве с разработчиком ПО. Для официальных образов перечислены их потенциальные уязвимости. Эта информация открыта для любого зарегистрированного пользователя. Доступны как бесплатные, так и платные аккаунты.
Пришло время запустить наш первый контейнер:
docker run ubuntu /bin/echo 'Hello world'
Консольный вывод:
Unable to find image 'ubuntu:latest' locally latest: Pulling from library/ubuntu d54efb8db41d: Pull complete f8b845f45a87: Pull complete e8db7bf7c39f: Pull complete 9654c40e9079: Pull complete 6d9ef359eaaa: Pull complete Digest: sha256:dd7808d8792c9841d0b460122f1acf0a2dd1f56404f8d1e56298048885e45535 Status: Downloaded newer image for ubuntu:latest Hello world
- docker run – это команда запуска контейнера.
- ubuntu – образ, который вы запускаете (например, образ операционной системы Ubuntu). Когда вы его указываете, Докер сначала анализирует элемент в разрезе хоста.
- /bin/echo ‘Hello world’ – команда, которая будет запускаться внутри нового контейнера. Данный контейнер просто выводит «Hello world» и останавливает выполнение.
Теперь попробуем создать интерактивную оболочку внутри контейнера:
docker run -i -t --rm ubuntu /bin/bash
- -t присваивает псевдо-tty или терминал внутри нового контейнера.
- -i позволяет создавать интерактивное соединение, захватывая стандартный вход (STDIN) контейнера.
- —rm требуется для автоматического удаления контейнера при выходе из процесса. По умолчанию контейнеры не удаляются.
Если вы хотите, чтобы контейнер работал после окончания сеанса, вам необходимо его «демонизировать»:
docker run --name daemon -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
- —name daemon назначает имя новому контейнеру. Если вы его не укажете, имя сгенерируется и назначится автоматически.
- -d запускает контейнер в фоновом режиме («демонизирует» его).
Давайте посмотрим, какие контейнеры у нас есть на данный момент:
docker ps -a
Консольный вывод:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 32 seconds ago Up 30 seconds daemon c006f1a02edf ubuntu "/bin/echo 'Hello ..." About a minute ago Exited (0) About a minute ago gifted_nobel
- docker ps – команда для перечисления контейнеров.
- -a показывает все контейнеры (без -a ps покажет только запущенные контейнеры).
ps показывает нам, что у нас есть два контейнера:
- gifted_nobel (имя для этого контейнера генерировалось автоматически) – первый контейнер, который мы создали с набранным «Hello world».
- daemon – третий контейнер, который мы создали и «демонизировали».
Примечание: второй контейнер (с интерактивной оболочкой) отсутствует, потому что мы устанавливаем параметр -rm, в результате чего этот контейнер автоматически удаляется после выполнения.
Давайте проверим журналы и посмотрим, что делает контейнер-демон прямо сейчас:
docker logs -f daemon
Консольный вывод:
... hello world hello world hello world
- docker logs получают журналы контейнера.
- -f следит за выходом журнала.
Теперь давайте остановим контейнер-демон:
docker stop daemon
Проверяем его остановку:
docker ps -a
Консольный вывод:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 5 minutes ago Exited (137) 5 seconds ago daemon c006f1a02edf ubuntu "/bin/echo 'Hello ..." 6 minutes ago Exited (0) 6 minutes ago gifted_nobel
Контейнер остановлен. Давайте запустим его снова:
docker start daemon
Убедимся, что он запущен:
docker ps -a
Консольный вывод:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 5 minutes ago Up 3 seconds daemon c006f1a02edf ubuntu "/bin/echo 'Hello ..." 6 minutes ago Exited (0) 7 minutes ago gifted_nobel
Теперь остановим его и удалим все контейнеры вручную:
docker stop daemon docker rm <your first container name> docker rm daemon
Чтобы удалить все контейнеры, мы можем использовать следующую команду:
docker rm -f $(docker ps -aq)
- docker rm – команда удаления контейнера.
- -f (для rm) должен остановить контейнер, если он работает (принудительное удаление).
- -q (для ps) – это вывод только идентификаторов контейнера.
Начиная с этого примера, вам понадобятся дополнительные файлы, которые вы можете найти в репозитории GitHub. Как вариант, загрузите образцы файлов по ссылке.
Пришло время создать и запустить более важный контейнер, такой как Nginx.
Измените каталог на examples/nginx:
docker run -d --name test-nginx -p 80:80 -v $(pwd):/usr/share/nginx/html:ro nginx:latest
Консольный вывод:
Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 693502eb7dfb: Pull complete 6decb850d2bc: Pull complete c3e19f087ed6: Pull complete Digest: sha256:52a189e49c0c797cfc5cbfe578c68c225d160fb13a42954144b29af3fe4fe335 Status: Downloaded newer image for nginx:latest 436a602273b0ca687c61cc843ab28163c720a1810b09005a36ea06f005b0c971
- -p – отображение портов HOST PORT: CONTAINER PORT.
- -v отвечает за HOST DIRECTORY:CONTAINER DIRECTORY.
Теперь проверьте этот URL-адрес в своем веб-браузере.
Еще мы можем попробовать изменить /example/nginx/index.html (который добавляется в каталог /usr/share/nginx/html внутри контейнера) и обновить страницу.
Получим информацию о контейнере test-nginx:
docker inspect test-nginx
Эта команда отображает системную информацию об установке Докер. Она включает версию ядра, количество контейнеров и образов, открытые порты и т. д.
Чтобы создать образ, сперва вам нужно создать Dockerfile: это текстовый файл с инструкциями и аргументами. Краткое описание инструкций, которые мы собираемся использовать в примере:
- FROM – задать базовый образ
- RUN – выполнить команду в контейнере
- ENV – задать переменную среды
- WORKDIR – установить рабочий каталог
- VOLUME – создать точку монтирования для тома
- CMD – установить исполняемый файл для контейнера
Более подробная информация здесь.
Давайте создадим образ, который получит содержимое сайта и сохранит его в текстовом файле. Нам нужно передать URL-адрес через переменную SITE_URL. Результирующий файл будет помещен в каталог, установленный как том:
FROM ubuntu:latest RUN apt-get update RUN apt-get install --no-install-recommends --no-install-suggests -y curl ENV SITE_URL https://google. com/ WORKDIR /data VOLUME /data CMD sh -c "curl -L $SITE_URL > /data/results"
Dockerfile готов, пришло время создать образ.
Создание образа
Перейдите к examples/curl и выполните следующую команду:
docker build . -t test-curl
Консольный вывод:
Sending build context to Docker daemon 3.584 kB Step 1/7 : FROM ubuntu:latest ---> 0ef2e08ed3fa Step 2/7 : RUN apt-get update ---> Running in 4aa839bb46ec Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB] Get:2 http://archive.ubuntu.com/ubuntu xenial-updates InRelease [102 kB] ... Fetched 24.9 MB in 4s (5208 kB/s) Reading package lists... ---> 35ac5017c794 Removing intermediate container 4aa839bb46ec Step 3/7 : RUN apt-get install --no-install-recommends --no-install-suggests -y curl ---> Running in 3ca9384ecf8d Reading package lists... Building dependency tree. .. Reading state information... The following additional packages will be installed... ---> f3c6d26b95e6 Removing intermediate container 3ca9384ecf8d Step 4/7 : ENV SITE_URL https://google.com/ ---> Running in 21b0022b260f ---> 9a733ee39a46 Removing intermediate container 21b0022b260f Step 5/7 : WORKDIR /data ---> c024301ddfb8 Removing intermediate container 3bc973e5584c Step 6/7 : VOLUME /data ---> Running in a9594a8958fe ---> 6802707a7114 Removing intermediate container a9594a8958fe Step 7/7 : CMD sh -c "curl -L $SITE_URL > /data/results" ---> Running in 37503bc4e386 ---> 5ebb2a65d771 Removing intermediate container 37503bc4e386 Successfully built 5ebb2a65d771
- docker build создает новый образ локально.
- -t устанавливает в образе метку имени.
Теперь у нас есть новый образ, и мы можем его увидеть в списке существующих:
docker images
Консольный вывод:
REPOSITORY TAG IMAGE ID CREATED SIZE test-curl latest 5ebb2a65d771 37 minutes ago 180 MB nginx latest 6b914bbcb89e 7 days ago 182 MB ubuntu latest 0ef2e08ed3fa 8 days ago 130 MB
Мы можем создавать и запускать контейнер из образа. Давайте попробуем сделать это с параметрами по умолчанию:
docker run --rm -v $(pwd)/vol:/data/:rw test-curl
Чтобы просмотреть результаты, сохраненные в файле:
cat ./vol/results
Попробуем с facebook.com:
docker run --rm -e SITE_URL=https://facebook.com/ -v $(pwd)/vol:/data/:rw test-curl
Чтобы просмотреть результаты, сохраненные в файле:
cat ./vol/results
Рекомендации по созданию образов
- Избегайте установки ненужных пакетов – они будут потреблять дополнительное дисковое пространство.
- Используйте кэш.
- Будьте осторожны с объемами. Вы должны помнить, какие данные в томах. Поскольку тома являются постоянными и не «умирают» с контейнерами – следующий контейнер будет использовать данные из тома, которые были созданы предыдущим контейнером.
- Используйте переменные среды (в RUN, EXPOSE, VOLUME). Это сделает ваш Dockerfile более гибким.
Соединение между контейнерами
Docker compose — это единственный правильный способ подключения контейнеров друг к другу.
В этом примере мы подключим контейнеры Python и Redis.
version: '2' services: app: build: context: ./app depends_on: - redis environment: - REDIS_HOST=redis ports: - "5000:5000" redis: image: redis:3.2-alpine volumes: - redis_data:/data volumes: redis_data:
Перейдем к examples/compose и выполним команду:
docker-compose --project-name app-test -f docker-compose.yml up
Консольный вывод
Текущий пример увеличит счетчик просмотров в Redis. Откройте ссылку и убедитесь в этом.
Использование docker-compose – это тема для целого учебника. Чтобы начать работу, вы можете поиграться с некоторыми образами из Docker Hub, а если хотите создать свои собственные – следуйте рекомендациям, перечисленным выше. Единственное, что можно добавить с точки зрения использования docker-compose – всегда давайте явные имена вашим томам. Это простое правило избавит вас от проблемы в будущем.
version: '2' services: ... redis: image: redis:3.2-alpine volumes: - redis_data:/data volumes: redis_data:
В этом случае redis_data будет именем внутри файла docker-compose.yml.
Смотрим выполнение тома:
docker volume ls
Консольный вывод:
DRIVER VOLUME NAME local apptest_redis_data
Без явного имени тома будет UUID. И вот пример:
DRIVER VOLUME NAME local ec1a5ac0a2106963c2129151b27cb032ea5bb7c4bd6fe94d9dd22d3e72b2a41b local f3a664ce353ba24dd43d8f104871594de6024ed847054422bbdd362c5033fc4c local f81a397776458e62022610f38a1bfe50dd388628e2badc3d3a2553bb08a5467f local f84228acbf9c5c06da7be2197db37f2e3da34b7e8277942b10900f77f78c9e64 local f9958475a011982b4dc8d8d8209899474ea4ec2c27f68d1a430c94bcc1eb0227 local ff14e0e20d70aa57e62db0b813db08577703ff1405b2a90ec88f48eb4cdc7c19 local polls_pg_data local polls_public_files local polls_redis_data local projectdev_pg_data local projectdev_redis_data
Докер стал одним из важнейших инструментов современного разработчика. Да, он имеет некоторые ограничения и требования в зависимости от архитектуры вашей системы, но немного усидчивости – и мир контейнеров обязательно будет приручен!
Более 100 полезных сервисов для разработки на все случаи жизни
164 крутых опенсорс проекта для новичков
Путь Python Junior-а в 2017
10 ресурсов для изучения Linux
Самый простой, полный и понятный туториал Docker для новичков.
Добро пожаловать в гайд по изучению Docker, в котором я проиллюстрирую вам совершенно иной подход при разработке ваших приложений с его помощью. Эту статью вы можете считать как быстрый старт, введение в Docker. Когда вы полностью прочитаете эту статью, уверен, вы поймёте, что такое Docker, для чего он нужен, и где используется. Но основным ключом к его освоению — это полное повторение процесса написания кода, как демонстрируется в этой статье. Это гайд по работе с Docker для новичков, потому, дублирование процесса у себя на компьюетере — обязательное условие к его пониманию. Одного чтения недостаточно, важно — повторение процесса и много практики. Так же чтобы наконец-то научиться работать с ним, даже при условии плохого понимая Docker-а, начните его уже применять в своей разработке. Начните, и увидите, как стали продвинутым его пользователем.
Когда я наконец-то понял все тонкости работы с Docker (на полное изучение которого ушло несколько месяцев), и начал правильно применять его при разработке (а он как раз и нужен для разработки, в большей степени), то почувствовал, как будто обрёл какую-то сверхспособоность. Смотря на свой опыт изучения Докера, я понял, что мне есть что рассказать, и чем поделиться. В этой статье я постарался создать максимально понятную для новичков инструкцию, благодаря которой вы сможете полностью изучить Docker за 30 минут. Я долго думал о том, чтобы написать туториал, и наконец-то осилил эту задачу, и, как мне кажется, получилось неплохо 🙂
Эта инструкция так же подходит для тех, кто не имеет никаких знаний, или опыта работы с докером, или аналогичным программным обеспечением. Вы получите все важные знания, необходимые для работы. Статья построена по принципу от простого к сложному. В итоге статьи вы будете чётко понимать, что такое Docker, зачем нужен, как с ним работать, и применять его для разработки: создавать окружение, необходимое для создания вашего приложения.
Эта статья, в больше мере, нацелена на получение практических знаний, и только немного теории, построенной на аналогиях из жизни. Потому, эта статья имеет окрас веселья и лайтовости, в большей мере, чем супер-конкретики и теоретических нюансов.
В этом туториале я показываю всё на примере ОС Windows 10, делая все команды из консоли винды, и демонстрируя процесс установки Docker на Windows 10. Но, все команды будут работать аналогично и на Linux и Mac. Эта статья — это продолжение ряда статей, посвященных настройке рабочего окружения. В прошлой статье мы рассматривали работу с Vagrant, что не менее интересно, чем Docker. И Docker и Vagrant преследуют цель — упростить жизнь разработчикам, но с Докером открывается больше возможностей..
Что вы узнаете из этой статьи
Что такое Docker
О том, как появился Docker:
Docker — это программное обеспечение, которое начинало с того, что зародилось в одной компании, как внутренний проект platform-as-a-service в компании dotCloud.
В процессе развития Докера, он вырос из масштабов внутреннего проекта, стал доступен для широких масс, и затмил своей популярностью своего родителя dotCloud, из-за чего было принято решение создать новую отдельную компанию под названием Docker Incorporated. Направление новосозданной компании было только в разработке Докера, и развитию его экосистемы.
На сайте доккера можно найти статью, в которой рассказывается, что такое Docker. Из их слов — это стандартизированное пакетное программное обеспечение, для разработки и развёртывания проектов.
Но, что это на самом деле значит?
Давайте на секунду забудем про Докер, и вспомним про такую ностальгическую штуку, как GameBoy Color:
Если вы помните, игры для этой приставки поставлялись в виде картриджей:
И я уверен в том, что производители видео игр пользуются успехом из-за своей простоты:
- Когда ты хочешь поиграть, ты просто вставляешь картридж в приставку, и игра сразу же работает.
- Ты можешь поделиться своей игрой с друзьями, передав всего лишь картридж, который они вставят в приставку, и сразу же смогут играть.
Docker следует похожему принципу — позволяет запускать своё ПО настолько просто, что это соизмеримо с вставкой картриджа и нажатием кнопки ON на приставке.
Это основная суть, почему Docker настолько полезен — теперь кто угодно, у кого установлен Docker может запустить приложение, написанное с запуском под Docker, выполнив для этого всего несколько команд.
Раньше, вы, создавая приложения, к примеру на PHP, устанавливали локально PHP, MySql, возможно, NodeJs, при этом устанавливая зависимости в виде нужных расширений и библиотек. И, в случае передачи вашего скрипта какому-то знакомому, ему требовалось настраивать аналогичное окружение, аналогичных версий, иметь аналогичные расширения и конфигурацию, чтобы успешно запустить ваше приложение. Сейчас же, при использовании Докера, такой проблемы не возникнет впринципе. Теперь вам достаточно иметь установленную программу Docker, которая по одной вашей команде установит окружение, описанное для запуска вашего приложения.
Какое программное обеспечение можно запустить с помощью докера? В техническом плане, Docker чем-то похож на виртуальную машину:
Докер — это движок, который запускает виртуальную операционную систему, имеющую чрезвычайно маленький вес (в отличие от Vagrant-а, который создаёт полноценную виртуальную ОС, Докер, имеет особые образы ПО, запускающиеся в виртуальной среде, не создавая полную копию ОС).
Docker позволяет запустить ОС Linux в изолированной среде очень быстро, в течение нескольких минут.
Зачем использовать Docker?
Кошмар при установке ПО, с которым приходится сталкиваться. У вас когда-нибудь было такое, что вы пытаетесь установить ПО на ваш компьютер, а оно отказывается работать? Вы получаете несколько непонятных вам ошибок, из-за которых ничего не запускается. И после нескольких часов гугления, на десятой странице гугла…и на каком-то форуме, до этого неизвестного вам, вы наконец-то находите случайный комментарий, которые помогает пофиксить вашу проблему.
Аналогично, что делает написание PC игр более сложным, чем написание Game Boy игр — это то, что приходится проектировать систему с учётом большого множества существующих PC девайсов и спецификаций. Так как разные компьютеры имеют различные операционные системы, драйвера, процессоры, графические карты, и т.д.
И потому задача разработчика — написать приложение совместимое со всеми популярными системами, что затруднительно и трудоёмко.
Docker спасёт нас. Docker, как и Game Boy приставка, берёт стандартизированные части программного обеспечения и запускает их так, как Game Boy запускал бы игру.
В этом случае вы не должны беспокоиться об операционной системе, на которой пользователь будет запускать ваше приложение. Теперь, когда пользователи будут запускать приложение через Docker — конфигурация будет собрана автоматически, и код будет выполняться всегда.
Как для разработчика, теперь вы не должны волноваться о том, на какой системе будет запущено ваше приложение.
Как пользователь, вам не нужно волноваться о том, что вы скачаете неподходящую версию ПО (нужного для работы программы). В Докере эта программа будет запущена в аналогичных условиях, при которых это приложение было разработано, потому, исключается факт получить какую-то новую, непредвиденную ошибку. Для пользователя все действия сводятся к принципу подключи и играй.
Установка Docker
Docker доступен для любой из операционных систем: Windows, Linux, Max. Для скачивания установочного файла — перейдите по ссылке и выберите подходящую вам версию. Я же, как и писал ранее, выбираю версию docker для Windows 10.
Docker предоставляет 2 сборки:
- Community Edition (полностью бесплатная версия)
- Enterprise Edition (платно). Enterprise Edition содержит в себе дополнительные
свистелки-перделкифункции, которые, на данном этапе, точно не нужны. Функциональность, которую мы будем использовать совершенно не отличается в этих двух сборках.
После установки потребуется перезагрузка системы, и уже можно начинать полноценно работать с Докером.
Для того, чтобы проверить, запущен ли Docker, откроем командную строку (на Windows 10 — Нажмите кнопку windows, и начните писать командная строка)
Где, напишем команду docker
, и в случае успешно работающего докера, получим ответ
Дальше, нужно удостовериться, что вместе с докером, доступен так же, docker-compose
, для этого, выполним команду docker-compose
(вывод обеих команд будет примерно одинакового содержания).
Если вы используете Linux, то, docker-compose нужно будет устанавливать отдельно по инструкции.
Что такое Docker Image?
Docker образ (он же Docker Image), похож на Game Boy картридж — это просто программное обеспечение. Это стандартизированное программное обеспечение, которое запускается на любой приставке Game Boy. Вы можете дать игру вашему другу, и он сможет просто вставить картридж в приставку, и играть.
Как в случае с картриджами, бывают различные игры, так и Docker имеет различные образы ПО: ubuntu, php (который наследуется от оригинального образа Ubuntu), nodejs, и т.д.
Рассмотрим пример скачивания нашего первого образа. Для этого, существует команда:docker pull <IMAGE_NAME>
, где <IMAGE_NAME> — имя скачиваемого образа
Зная эту команду, скачаем образ Ubuntu 18.10
:
docker pull ubuntu:18.10
Эта команда сообщает Докеру о том, что нужно скачать образ Ubuntu 18.10 с Dockerhub.com (не путать с pornhub) — основной репозиторий Docker-образов, на котором вы и можете посмотреть весь их список и подобрать нужный образ для вашей программы.
Это как поездка за новым картриджем в магазин, только намного быстрее :).
Теперь, для того, чтобы посмотреть список всех загруженных образов, нужно выполнить:
docker images
У вас, как и на скрине, должен появиться только что скачанный образ Ubuntu 18.10. И, как обсуждалось выше, по поводу маленького размера образов, чистый образ Ubuntu при установке из Docker-образа, весит всего 74 МБ. Не чудо ли?
Проводя аналогии, команда docker images
выглядит как коллекция картриджей от приставки, которые у вас есть сейчас:
Что такое Docker контейнер?
Теперь представьте, что мы обновили нашу приставку с Game Boy на GameCube. Игры хранятся на диске, который предназначен только для чтения самого образа игры. А прочие файлы (сохранения, кеш и т.д.) сохраняются внутри самой приставки, локально.
Так же, как и игра на диске, исходный Docker Image (образ) — неизменяемый.
Docker контейнер — это экземпляр запущенного образа. Аналогично тому, что вы вставляете диск в приставку, после чего игра начинается.
А сам образ игры никак не модифицируется, все файлы, содержащие изменения хранятся где-то локально на приставке.
Запуск Docker контейнера соответствует тому, что вы играете в свою Gamecube игру. Docker запускает ваш образ в своей среде, аналогично тому, как Gamecube запускает игру с диска, не модифицируя оригинальный образ, а лишь сохраняя изменения и весь прогресс в какой-то песочнице.
Для запуска контейнера существует команда:
docker run <image> <опциональная команды, которая выполнится внутри контейнера>
Давайте запустим наш первый контейнер Ubuntu:
docker run ubuntu:18.10 echo 'hello from ubuntu'
Команда echo 'hello from ubuntu'
была выполнена внутри среды Ubuntu. Другими словами, эта команда была выполнена в контейнере ubuntu:18.10.
Теперь выполним команду для проверки списка запущенных контейнеров:
docker ps
Здесь пустота… это потому что docker ps
показывает только список контейнеров, которые запущены в данный момент (наш же контейнер выполнил одну команду echo 'hello from ubuntu'
и завершил свою работу). А для того, чтобы посмотреть список всех контейнеров без исключения, нужно добавить флаг -a
, выполним:
docker ps -a
Когда процесс внутри контейнера выполнит работу, то Docker-контейнер завершается. Это похоже на режим сохранения энергии в новых игровых консолях — если вы не совершаете действий какое-то время, то система выключается автоматически.
Каждый раз, когда вы будете выполнять команду docker run
, будет создаваться новый контейнер, на каждую из выполненных команд.
Давайте добавим немного интерактивности в наше обучение. Мы можем подключиться к консоли виртуальной ОС (Ubuntu 18.10), и выполнять любое количество команд без завершения работы контейнера, для этого, запустим команду:
docker run -it ubuntu:18.10 /bin/bash
Опция -it
вместе с /bin/bash
даёт доступ к выполнению команд в терминале внутри контейнера Ubuntu. Теперь, внутри этого контейнера можно выполнять любые команды, применимые к Ubuntu. Вы же можете представлять это как мини виртуальную машину, условно, к консоли которой мы подключились по ssh. В результате, теперь мы знаем возможные способы, как войти в контейнер, и как выполнить команду в контейнере Docker-а.
Иногда является очень полезным узнать ID контейнера, с которым мы работаем. И как раз-таки, при выполнении команды docker run -it <IMAGE> /bin/bash
, мы окажемся в терминале, где все команды будут выполняться от имени пользователя root@<containerid>
.
Теперь, все команды буду выполняться внутри операционной системы Ubuntu. Попробуем, например, выполнить команду ls
, и посмотрим, список директорий, внутри этого образа Ubuntu.
Docker контейнер является полностью независимым от системы хоста, из которой он запускался. Как изолированная виртуальная машина. И в ней вы можете производить любые изменения, которые никак не повлияют на основную операционную систему.
Это аналогично тому, как, если бы вы играли в Mario Kart на приставке Gamecube, и неважно, что вы делаете в игре, вы никак не сможете изменить само ядро игры, или изменить информацию, записанную на диске.
Контейнер является полностью независимым и изолированным от основной операционной системы, аналогично виртуальной операционной системе. Вы можете вносить любые изменения внутри виртуалки, и никакие из этих изменений не повлияют на основную операционную систему.
Теперь откройте новое окно терминала (не закрывая и не отключаясь от текущего), и выполните команду docker ps
Только на этот раз вы можете увидеть, что контейнер с Ubuntu 18.10 в текущий момент запущен.
Теперь вернёмся назад к первому окну терминала (который находится внутри контейнера), и выполним:
mkdir /truedir #создаст папку truedir
exit #выйдет из контейнера, и вернётся в основную ОС
Выполнив команду exit
, контейнер будет остановлен (чтобы убедиться, можете проверить командой docker ps
). Теперь, вы так же знаете, как выйти из Docker контейнера.
Теперь, попробуем ещё раз просмотреть список всех контейнеров, и убедимся, что новый контейнер был создан docker ps -a
Так же, для того, чтобы запустить ранее созданный контейнер, можно выполнить команду docker start <CONTAINER_ID>
,
где CONTAINER_ID — id контейнера, который можно посмотреть, выполнив команду docker ps -a
(и увидеть в столбце CONTAINER_ID)
В моём случае, CONTAINER_ID последнего контейнера = 7579c85c8b7e (у вас же, он будет отличаться)
Запустим контейнер командой:
docker start 7579c85c8b7e #ваш CONTAINER_ID
docker ps
docker exec -it 7579c85c8b7e /bin/bash #ваш CONTAINER_ID
И теперь, если внутри контейнера выполнить команду
ls
, то можно увидеть, что ранее созданная папка truedir существует в этом контейнере
Команда exec
позволяет выполнить команду внутри запущенного контейнера. В нашем случае, мы выполнили /bin/bash
, что позволило нам подключиться к терминалу внутри контейнера.
Для выхода, как обычно, выполним exit
.
Теперь остановим и удалим Docker контейнеры (командами docker stop <CONTAINER_ID>
и docker rm <CONTAINER_ID>
соотвественно):
docker ps a # просмотрим список активных контейнеров
docker stop aa1463167766 # остановим активный контейнер
docker rm aa1463167766 # удалим контейнер
docker rm bb597feb7fbe # удалим второй контейнер
В основном, нам не нужно, чтобы в системе плодилось большое количество контейнеров. Потому, команду docker run очень часто запускают с дополнительным флагом —rm, который удаляет запущенный контейнер после работы:
docker run -it --rm ubuntu:18.10 /bin/bash
Что такое DockerFile?
Docker позволяет вам делиться с другими средой, в которой ваш код запускался и помогает в её простом воссоздании на других машинах.
Dockerfile — это обычный конфигурационный файл, описывающий пошаговое создание среды вашего приложения. В этом файле подробно описывается, какие команды будут выполнены, какие образы задействованы, и какие настройки будут применены. А движок Docker-а при запуске уже распарсит этот файл (именуемый как Dockerfile), и создаст из него соответствующий образ (Image), который был описан.
К примеру, если вы разрабатывали приложение на php7.2, и использовали ElasticSearch 9 версии, и сохранили это в Dockerfile-е, то другие пользователи, которые запустят образ используя ваш Dockerfile, получат ту же среду с php7.2 и ElasticSearch 9.
С Dockerfile вы сможете подробно описать инструкцию, по которой будет воссоздано конкретное состояние. И делается это довольно-таки просто и интуитивно понятно.
Представьте, что вы играете в покемонов
Вы пытаетесь пройти первый уровень, но безрезультатно. И я, как ваш друг, хочу с этим помочь. У меня есть 2 таблетки варианта:
- Я дам вам файл сохранений, в котором игра ничинается со второго уровня. Всё что вам нужно — это загрузить файл.
- Я могу написать инструкцию, в которой опишу шаг за шагом процесс прохождения уровня. Это как рецепт, которому нужно будет следовать в точности, как описано. Эта инструкция могла бы выглядеть как-то так:
Инструкция прохождения первого уровня
- Выбрать покемона Squirtle
- Направляйтесь к лесу, к северу от города
- Тренируйтесь, пока покемон не достигнет 10 уровня
- Направляйтесь в Оловянный город
- Подойдите к боссу, и победите его заклинанием Watergun
Что является более полезным? Я склоняюсь, что это второй вариант. Потому что он демонстрирует, как добиться желаемого состояния. Это не просто чёрный ящик, который переносит игру на второй уровень.
С докером вы так же имеете два варианта при создании образа:
- Вы можете запаковать ваш контейнер, создать из него образ (аналогично тому, что вы запишите на диск новую игру с собственными модификациями). Это похоже на способ, когда вы делитесь сохранениями напрямую.
- Или же, можно описать Dockerfile — подробную инструкцию, которая приведёт среду к нужному состоянию.
Я склоняюсь ко второму варианту, потому что он более подробный, гибкий, и редактируемый (вы можете переписать Dockerfile, но не можете перемотать состояние образа в случае прямых изменений).
Пришло время попрактиковаться на реальном примере. Для начала, создадим файл cli.php в корне проекта с содержимым:
<?php
$n = $i = 5;
while ($i--) {
echo str_repeat(' ', $i).str_repeat('* ', $n - $i)."\n";
}
И файл под названием Dockerfile, с содержимым:
FROM php:7.2-cli
COPY cli.php /cli.php
RUN chmod +x /cli.php
CMD php /cli.php
Имена команд в Dockerfile (выделенные красным) — это синтаксис разметки Dockerfile. Эти команды означают:
- FROM — это как буд-то вы выбираете движок для вашей игры (Unity, Unreal, CryEngine). Хоть вы и могли бы начать писать движок с нуля, но больше смысла было бы в использовании готового. Можно было бы использовать, к примеру, ubuntu:18.10, в нашем коде используется образ php:7.2-cli, потому весь код будет запускаться внутри образа с предустановленным php 7.2-cli.
- COPY — Копирует файл с основной системы в контейнер (копируем файл cli.php внутрь контейнера, с одноимённым названием)
- RUN — Выполнение shell-команды из терминала контейнера (в текущем случае, присвоим права на выполнение скрипта /cli.php
- CMD — Выполняет эту команду каждый раз, при новом запуске контейнера
Для просмотра полного списка команд можете перейти по ссылке
При написании Dockerfile, начинать следует с наиболее актуального существующего образа, дополняя его в соответствии с потребностями вашего приложения.
К примеру, мы могли не использовать образ php:7.2-cli, а могли взять ubuntu:18.10, последовательно выполняя команды в RUN одна за одной, устанавливая нужное ПО. Однако, в этом мало смысла, когда уже есть готовые сборки.
Для создания образа из Dockerfile нужно выполнить: docker build <DOCKERFILE_PATH> --tag <IMAGE_NAME>
<DOCKERFILE_PATH> — путь к файлу Dockerfile (.
— текущая директория),
<IMAGE_NAME> — имя, под которым образ будет создан
Выполним:
docker build . --tag pyramid
При том, что имя файла Dockerfile при указывании пути упускается, нужно указывать только директорию, в которой этот файл находится (а
.
означает, что файл находится в той директории, из которой была запущена консоль)
После того, как команда выполнилась, мы можем обращаться к образу по его имени, которое было указано в <IMAGE_NAME>, проверим список образов: docker images
Теперь, запустим контейнер из нашего образа командой docker run pyramid
Круто! Shell скрипт был успешно скопирован, и выполнен благодаря указанному в Dockerfile параметру CMD.
Сначала мы скопировали файл cli.php в Docker образ, который создался с помощью Dockerfile. Для того, чтобы удостовериться в том, что файл действительно был проброшен внутрь контейнера, можно выполнить команду docker run pyramid ls
, которая в списке файлов покажет и cli.php.
Однако, сейчас этот контейнер недостаточно гибкий. Нам бы хотелось, чтобы можно было удобно изменять количество строк, из скольки состоит пирамида.
Для этого, отредактируем файл cli.php, и изменим, чтобы количество аргументов принималось из командной строки. Отредактируем вторую строку на:
$n = $i = $argv[1] ?? 5; //а было $n = $i = 5
// это значит, что мы принимаем аргумент из консоли, а если он не получен, то используем по-умолчанию 5
После чего, пересоберём образ: docker build . --tag pyramid
И запустим контейнер: docker run pyramid php /cli.php 9
, получив вывод ёлки пирамиды в 9 строк
Почему это работает?
Когда контейнер запускается, вы можете переопределить команду записанную в Dockerfile в поле CMD.
Наша оригинальная CMD команда, записанная в Dockerfile php /cli.php
— будет переопределена новой php /cli.php 9
.
Но, было бы неплохо передавать этот аргумент самому контейнеру, вместо переписывания всей команды. Перепишем так, чтобы вместо команды php /cli.php 7
можно было передавать просто аргумент-число.
Для этого, дополним Dockerfile:
FROM php:7.2-cli
COPY cli.php /cli.php
RUN chmod +x /cli.php
ENTRYPOINT ["php", "/cli.php"]
## аргумент, который передаётся в командную строку
CMD ["9"]
Мы немного поменяли формат записи. В таком случае, CMD будет добавлена к тому, что выполнится в ENTRYPOINT.
["php", "/cli.php"]
на самом деле запускается, как php /cli.php
. И, учитывая то, что CMD будет добавлена после выполнения текущей, то итоговая команда будет выглядеть как: php /cli.php 9
— и пользователь сможет переопределить этот аргумент, передавая его в командную строку, во время запуска контейнера.
Теперь, заново пересоберём образ
docker build . --tag pyramid
И запустим контейнер с желаемым аргументом
docker run pyramid 3
Монтирование локальной директории в Docker-контейнер
Монтирование директории в Docker контейнер — это предоставление доступа контейнеру на чтение содержимого вашей папки из основной операционной системы. Помимо чтения из этой папки, так же, контейнер может её изменять, и такая связь является двусторонней: при изменении файлов в основной ОС изменения будут видны в контейнере, и наоборот.
Когда игра читает файлы сохранений, файловая система Game Cube внедряет их в текущий сеанс игры (представим это, даже если это не так). Игра может изменять файл сохранений, и это изменение отразится на файловой системе Game Cube, т.е. возникает двусторонняя связь.
Монтирование директории в контейнер позволяет ему читать и писать данные в эту директорию, изменяя её состояние.
Для того, чтобы смонтировать папку из основной системы в контейнер, можно воспользоваться командойdocker run -v <DIRECTORY>:<CONTAINER_DIRECTORY> ...
,
где DIRECTORY — это путь к папке, которую нужно смонтировать,
CONTAINER_DIRECTORY — путь внутри контейнера.
Только путь к монтируемой папке должен быть прописан полностью:
C:\projects\docker-example
, или на *nix-системах можно воспользоваться конструкцией$(pwd)
Выполним команду:
docker run -it -v C:\projects\docker-example\cli:/mounted ubuntu:18.10 /bin/bash
ls
ls mounted
touch mounted/testfile
При выполнении этой команды, указанная папка смонтируется в папку /mounted, внутри файловой системы контейнера, а команда touch mounted/testfile
создаст новый файл под названием testfile, который вы можете увидеть из основной ОС.
Теперь вы можете увидеть, что после выполнения этой команды в текущей директории появился новый файл testfile. И это говорит о том, что двусторонняя связь работает — при изменении директории на основной ОС всё отразится на смонтированную папку внутри контейнера, а при изменениях изнутри контейнера всё отразится на основную ОС.
Монтирование папки позволяет вам изменять файлы вашей основной системы прямо во время работы внутри Docker контейнера.
Это удобная особенность, которая позволяет нам редактировать код в редакторе на основной ОС, а изменения будут сразу же применяться внутри контейнера.
Что такое Docker Volumes?
Docker Volumes — что-то похоже на карты памяти для Game Cube. Эта карта памяти содержит данные для игры. Эти карты съемные, и могу работать, когда Gamecube приставка выключается. Вы так же можете подключить различные карты памяти, содержащие разные данные, а так же, подключать к разным приставкам.
Вы можете вставить вашу карту внутрь приставки, точно так же, как и Docker Volume может быть прикреплён к любому из контейнеров.
С Docker Volum-ами мы имеем контейнер, который хранит постоянные данные где-то на нашем компьютере (это актуально, потому что после завершения работы контейнер удаляет все пользовательские данные, не входящие в образ). Вы можете прикрепить Volume-данные к любому из запущенных контейнеров.
Вместо того, чтобы каждый раз, при запуске контейнера, писать, какие из папок вы хотите смонтировать, вы просто можете создать один контейнер с общими данными, который потом будете прикреплять.
Лично я, не использую это очень часто на практике, потому что есть много других методов по управлению данными. Однако, это может быть очень полезно для контейнеров, которые должны сохранять какие-то важные данные, или данные, которыми нужно поделиться между несколькими контейнерами.
Порты контейнеров
Docker позволяет нам получить доступ к какому-то из портов контейнера, пробросив его наружу (в основную операционную систему). По умолчанию, мы не можем достучаться к каким-либо из портов контейнера. Однако, в Dockerfile опция EXPOSE
позволяет нам объявить, к какому из портов мы можем обратиться из основной ОС.
Для этого, на по-быстрому, запустим Docker-образ php-apache, который работает на 80 порту.
Для начала, создадим новую папку apache (перейдём в неё cd apache
), в которой создадим файл index.php
, на основе которого мы и поймём, что всё работает.
<?php
echo 'Hello from apache. We have PHP version = ' . phpversion() . PHP_EOL;
А так же, в этой папке создадим файл Dockerfile
:
FROM php:7.2-apache
# Указываем рабочую папку
WORKDIR /var/www/html
# Копируем все файлы проекта в контейнер
COPY . /var/www/html
EXPOSE 80
Пробежимся по командам:
FROM: это вам уже знакомо, это образ с уже установленным php и apache
WORKDIR: создаст папку если она не создана, и перейдёт в неё. Аналогично выполнению команд mkdir /var/www/html && cd /var/www/html
EXPOSE: Apache по-умолчанию запускается на 80 порту, попробуем «прокинуть» его в нашу основную ОС (посмотрим как это работает через несколько секунд)
Для работы с сетью в Docker, нужно проделать 2 шага:
- Прокинуть системный порт (Expose).
- Привязать порт основной ОС к порту контейнера (выполнить соответствие).
Это что-то похоже на подключение вашей PS4 приставки к телевизору по HDMI кабелю. При подключении кабеля, вы явно указываете, какой HDMI-канал будет отображать видео.
В этой аналогии наша основная ОС будет как телевизор, а контейнер — это игровая консоль. Мы должны явно указать, какой порт основной операционной системы будет соответствовать порту контейнера.
EXPOSE в Докерфайле разрешает подключение к 80 порту контейнера — как разрешение HDMI подключения к PS4.
Выполним первый шаг прокидывания порт. Сбилдим контейнер:
docker build . --tag own_php_apache
И после этого, запустим контейнер:
docker run own_php_apache
После чего, попробуем перейти по адресу localhost:80
Но, это не сработало, потому что мы ещё не выполнили 2 шаг по маппингу портов.
Выйдите из контейнера, нажав CTRL+C.
Если у вас проблемы с остановкой контейнера, в новом окне откройте терминал, выполните
docker ps
, найдите ID контейнера, который сейчас запущен, и выполнитеdocker stop {CONTAINER_ID}
(указав ваш ID контейнера)
Теперь, осталось сообщить нашему компьютеру, какой порт контейнера ему нужно слушать, и для этого формат записи будет такой:
docker run -p <HOST_PORT>:<CONTAINER_PORT>
И мы можем указать любое соответствие портов, но сейчас просто укажем, что порт системы 80 будет слушать 80 порт контейнера:
docker run -p 80:80 own_php_apache
Здесь, вы уже наверное заметили, что добавился новый параметр -p 80:80
, который говорит Docker-у: я хочу, чтобы порт 80 из apache был привязан к моему локальному порту 80.
И теперь, если перейти по адресу localhost:80, то должны увидеть успешный ответ:
Оказывается, это даже легче, чем подключение HDMI-кабеля. Сейчас, можем попробовать выполнить запуск на разных портах:
docker run -p 8080:80 own_php_apache
Теперь, немного подчистим за собой: нужно остановить и удалить контейнеры, которые в даный момент мы запустили:
docker ps
docker stop <CONTAINER_ID> ...
docker rm <CONTAINER_ID> ...
Для *nix пользователей есть небольшой хак, который позволит остановить и удалить все контейнеры Docker:
docker stop $(docker ps -a -q) # Остановит все контейнеры docker rm $(docker ps -a -q) # Удалит все остановленные контейнеры
Запомните, что любой, кто будет запускать этот код на своём компьютере, не должен иметь установленный PHP, всё что ему нужно — один только Docker.
Docker образ: прослойка данных и кеширование
Docker умнее, чем вы могли бы подумать :).
Каждый раз, когда вы собираете образ, он кешируется в отдельный слой. Ввиду того, что образы являются неизменяемыми, их ядро никогда не модифицируются, потому применяется система кеширования, которая нужна для увеличения скорости билдинга.
Каждая команда в Dockerfile сохраняется как отельный слой образа.
Рассмотрим это на примере нашего прошлого Dockerfile-а:
FROM php:7.2-apache
# Копирует код ядра
COPY . /var/www/html
WORKDIR /var/www/html
EXPOSE 80
Когда вы пишите свой Dockerfile, вы добавляете слои поверх существующего основного образа (указанного в FROM), и создаёте свой собственный образ (Image).
FROM: говорит Докеру взять за основу этот существующий образ. А все новые команды будут добавлены слоями поверх этого основного образа.
COPY: копирует файлы с основной ОС в образ
WORKDIR: устанавливает текущую папку образа в /var/www/html
Слой Образа Докера это как точка сохранения в игре Super Mario. Если вы хотите изменить какие-то вещи, произошедшие до этой точки сохранения, то вам придётся перезапустить этот уровень полностью. Если вы хотите продолжить прогресс прохождения, вы можете начать с того места, где остановились.
Docker начинает кешировать с «того места, где остановился» во время билдинга Dockerfile. Если в Докерфайле не было никаких изменений с момента последнего билдинга, то образ будет взят полностью из кеша. Если же вы измените какую-то строку в Dockerfile — кеш будет взят только тех слоёв команд, которые находятся выше изменённой команды.
Для иллюстрации этого, добавим новые строки в Dockerfile:
FROM php:7.2-apache
WORKDIR /var/www/html
# Copy the app code
COPY . /var/www/html
RUN apt-get update && apt-get upgrade -y && apt-get install -y curl php7.2-mbstring php7.2-zip php7.2-intl php7.2-xml php7.2-json php7.2-curl
RUN echo "Hello, Docker Tutorial"
EXPOSE 80
После чего, пересоберём образ:
docker build . --tag own_php_apache
Выполнив эту команду, из вывода в консоль можете увидеть, что некоторые слои были взяты из кеша. Это как раз те команды, выше которых в Dockerfile не было добавлено/изменено содержимого.
И можно заметить, что в случае изменения Dockerfile, билдинг занимает больше времени, потому что не используется кеш. Где бы вы не написали команду, все закешированные команды, которые находятся ниже в Dockerfile, будут перебилжены заново. А те, что находятся выше, будут по-прежнему браться из кеша.
Когда вы используете команду COPY, она копирует указанную директорию в контейнер. И, в случае изменения содержимого любого из файлов этой директории, кеш команды COPY будет сброшен. Docker сверяет изменения во время билдинга в каждом из файлов. Если они были изменены, кеш будет сброшен, как и для всех последующих слоёв.
Если честно, то это действительно крутая функция. Docker следит за изменениями в файлах и использует кеш всегда, когда это нужно (когда были произведены изменения в каких-то из файлов). Изменение ваших файлов потенциально может затрагивать будущие команды, из-за чего, и все последующие слои билдятся заново, а не берутся из кеша.
Какие выводы из этого можно сделать:
- Команды, которые вероятнее всего не будут меняться в будущем, нужно помещать как можно выше в Dockerfile.
- Команды копирования данных нужно помещать ниже, потому что файлы при разработке изменяются довольно часто.
- Команды, которые требуют много времени на билдинг, нужно помещать выше.
В заключение, так же хочу сказать, как можно уменьшить размер слоёв Docker образов.
В Dockerfile вы можете иметь несколько команд (RUN) на выполнение:
RUN apt-get update
RUN apt-get install -y wget
RUN apt-get install -y curl
В результате выполнения этой команды, будет создано 3 разных слоя в образе. Вместо этого, все команды стараются объединить в одну строку:
RUN apt-get update && apt-get install -y wget curl
Если команда становится длинной, и нечитаемой, то для переноса на следующую строку делаем так:
RUN apt-get update && apt-get install -y wget curl && \
&& apt-get clean -y \
&& docker-php-ext-install soap mcrypt pdo_mysql zip bcmath
Если же команда становится слишком большой, и неудобной для чтения, то можно создать новый shell скрипт, в который поместить длинную команду, и запускать этот скрипт одной простой командой RUN.
Технически, только команды ADD, COPY, и RUN создают новый слой в Docker образе, остальные команды кешируются по-другому
Что такое Docker-Compose?
Docker-compose это как дирижёр оркестра, где ваш оркестр — это набор контейнеров, которыми нужно управлять. Каждый контейнер имеет отдельную задачу, как и музыкальные инструменты в разных частях песни.
Docker Compose управляет контейнерами, запускает их вместе, в нужной последовательности, необходимой для вашего приложения.
Его можно назвать дирижёром в мире Docker-а.
Docker-compose организовывает совместных запуск контейнеров, как инструменты в групповой игре в определённых участках песни.
Каждый инструмент имеет конкретную задачу в группе оркестра. Труба создаёт основную мелодию песни; фортепиано, гитара дополняют основную мелодию; барабаны задают ритм; саксофоны добавляют больше гармонии и т.д.
Каждый из инструментов имеет свою конкретную работу и задачу, как и наши контейнеры.
Docker-compose написан в формате YAML который по своей сути похож на JSON или XML. Но YAML имеет более удобный формат для его чтения, чем вышеперечисленные. В формате YAML имеют значения пробелы и табуляции, именно пробелами отделяются названия параметров от их значений.
Создадим новый файл docker-compose.yml
, для рассмотрения синтаксиса Docker Compose:
version: '3'
services:
app:
build:
context: .
ports:
- 8080:80
Теперь, построчно разберёмся с заданными параметрами, и что они значат:
version: какая версия docker-compose используется (3 версия — самая последняя на даный момент).
services: контейнеры которые мы хотим запустить.
app: имя сервиса, может быть любым, но желательно, чтобы оно описывало суть этого контейнера.
build: шаги, описывающие процесс билдинга.
context: где находится Dockerfile, из которого будем билдить образ для контейнера.
ports: маппинг портов основной ОС к контейнеру.
Мы можем использовать этот файл для билдинга нашего предыдущего образа apache:
docker-compose build
После выполнения этой команды, Docker спарсит файл docker-compose и создаст описанные сервисы на основе инструкций во вкладке build.
А context говорит о том, из какой директории мы берём Dockerfile для создания образа сервиса (в текущем случае — это означает текущую директорию .
, но могло быть и /php-cli
, /nginx
, и т.д.).
И теперь, запустим эти сервисы, которые создали:
docker-compose up
В результате чего, сервер должен был запуститься, и стать доступным по адресу localhost:8080.
Теперь, отключитесь от консоли, нажав CTRL+C.
Когда контейнер под названием app запускается, docker-compose автоматически связывает указанные порты во вкладке ports
. Вместо того, как мы делали ранее, выполняя -v 8080:80
, docker-compose делает это за нас, получив информацию параметра ports
. Docker-compose избавляет нас боли, связанной с указанием параметров в командной строке напрямую.
С docker-compose.yml мы переносим все параметры, ранее записываемые в командной строке при запуске контейнера в конфигурационный YAML файл.
В этом примере мы записали BUILD и RUN шаги для нашего сервиса в docker-compose.yml. И преимущество такого подхода ещё в том, что теперь для билдинга и для запуска этих сервисов нам нужно запомнить только 2 команды: docker-compose build
и docker-compose up
. При таком подходе не нужно помнить, какие аргументы нужно указывать, какие опции нужно задавать при запуске контейнера.
Теперь давайте сделаем нашу разработку немного легче. Сделаем, чтобы вместо постоянного перестроения образа при каждом изменении файлов, мы можем примонтировать нашу рабочую папку в контейнер.
Для этого, удалите строку COPY . /var/www/html
с Dockerfile — теперь все файлы будут прокинуты из основной ОС. Новый Dockerfile будет иметь вид:
FROM php:7.2-apache
WORKDIR /var/www/html
RUN apt-get update && apt-get install -y wget
EXPOSE 80
Ранее мы рассматривали, как примонтировать папку в контейнер, для этого мы запускали контейнер с аргументом -v <HOST_DIRECTORY>:<CONTAINER_DIRECTORY>
.
С Docker-compose мы можем указать напрямую в docker-compose.yml:
version: '3'
services:
app:
build:
context: .
ports:
- 8080:80
volumes:
- .:/var/www/html
Добавленная строка примонтирует текущую директорию основой операционной системы к директории /var/www/html контейнера.
В отличие от указания путь в консоли, здесь можно указывать относительный путь (
.
), не обязательно указывать полный путь (C:\projects\docker-example\apache
), как было ранее при ручном запуске контейнера.
Теперь, выполните по очереди команды:
docker-compose stop
docker-compose rm
При удалении, вас спросят, действительно ли удалять, напишите y
и нажмите кнопку enter. Эти команды остановят и удалят все контейнеры, описанные в файле docker-compose.yml (то же самое, как мы ранее запускали docker stop <CONTAINER_ID>
и docker rm <CONTAINER_ID>
)
Теперь перебилдим сервисы, потому что мы изменили Dockerfile:
docker-compose build
И заново запустим:
docker-compose up
И опять, по адресу localhost:8080 поднимется наш сервер.
Вместо того, чтобы копировать каждый раз файлы в образ, мы просто примонтировали папку, содержащую исходный код приложения. И теперь, каждый раз не придётся делать ребилд образа, когда файлы изменяются, теперь изменения происходят в лайв режиме, и будут доступны без перестройки образа.
Чтобы в этом убедиться, изменим файл index.php, добавим в него скрипт нами любимой пирамиды:
<?php
$n = $i = $_GET['count'] ?? 4;
echo '<pre>';
while ($i--) {
echo str_repeat(' ', $i).str_repeat('* ', $n - $i)."\n";
}
echo '</pre>';
И теперь, если перейти по адресу localhost:8080?count=10, то увидим, что пирамида выводится:
Монтирование вашей локальной папки как Docker Volume это основной метод как разрабатывать приложения в контейнере.
Так же, как мы ранее выполняли команды внутри контейнера, указывая аргументы -it ... /bin/bash
. Docker-compose так же предоставляет интерфейс по удобному выполнению команд внутри конкретного контейнера. Для этого нужно выполнить команду:
docker-compose exec {CONTAINER_NAME} {COMMAND}
где, вместо {CONTAINER_NAME}
нужно записать имя контейнера, под которым он записан в сервисах;
а вместо {COMMAND}
— желаемую команду.
К примеру, эта команда может выглядеть так:
docker-compose exec php-cli php -v
Но, сделаем это на основе текущего Dockerfile:
docker-compose down # остановим контейнеры
docker-compose up -d # здесь используется опция -d которая сообщает, что контейнер должен висеть в режиме демона
docker-compose exec app apache2 -v
И в результате должны получить
Пока что, в docker-compose.yml описан только один сервис, потому разворачиваем мы только один контейнер. Но реальный файл docker-compose выглядит больше. К примеру, для Laravel он такой:
version: '3'
services:
nginx:
build:
context: ./
dockerfile: docker/nginx.docker
volumes:
- ./:/var/www
ports:
- "8080:80"
depends_on:
- php-fpm
php-fpm:
build:
context: ./
dockerfile: docker/php-fpm.docker
volumes:
- ./:/var/www
depends_on:
- mysql
- redis
environment:
- "DB_PORT=3306"
- "DB_HOST=mysql"
- "REDIS_PORT=6379"
- "REDIS_HOST=redis"
php-cli:
build:
context: ./
dockerfile: docker/php-cli.docker
volumes:
- ./:/var/www
depends_on:
- mysql
- redis
environment:
- "DB_PORT=3306"
- "DB_HOST=mysql"
- "REDIS_PORT=6379"
- "REDIS_HOST=redis"
tty: true
mysql:
image: mysql:5.7
volumes:
- ./storage/docker/mysql:/var/lib/mysql
environment:
- "MYSQL_ROOT_PASSWORD=secret"
- "MYSQL_USER=app"
- "MYSQL_PASSWORD=secret"
- "MYSQL_DATABASE=app"
ports:
- "33061:3306"
redis:
image: redis:3.0
ports:
- "6379:6379"
И при выполнении одной только команды docker-compose up
, поднимутся 5 сервисов. В сравнении с тем, что мы бы вручную выполняли 5 раз команду docker run ...
. Так что, использование docker-compose в этом случае — очевидно. Так что, теперь, ещё один шаг позадчи, теперь вы знаете, что такое docker и docker compose, и для чего нужен каждый из них.
Как писать Микро Сервисы с Docker? Что такое микросервисы?
Docker найболее часто используемый инструмент для написания Микросервисов. Микросервисы — это архитектурный шаблон проектирования который следует философии «разделения ответственности».
Ниже я постараюсь кратко описать, что такое микросервисы:
Одиночный миросервис выполняет одну конкретнкую задачу. Он никак не связан с другими существующими микросервисами. Вместе же, микросервисы образуют приложение.
Микросервис должен выполнять свою задачу в изолированной среде, управлять своей собственной лольной информацией, и быть независимым от общей системы настолько, насколько это возможно. В основном, каждый из микросервисом имеет собственную, отдульную базу данных (если она нужна).
Это позволяет создавать максимально гибкие и легко масштабируемые приложения.
Идея разделения по ответственности предоставляет преимущество в том, что команда разработчиков может работать параллельно, фокусируясь над разными компонентами.
В основном, микросервисы имеют канал коммуникации мужду собой, в виде REST API, который возвращает данные в JSON, или что-то типа того.
Иногда бывает неясно, насколько большой, или маленькой должна быть задача микросервиса, которую он должен решать. Это решение лежит на плечах разработчика, здесь нет чёткого правила.
Использование микросервисов позволяет быстро масшабировать под большой нагрузкой, без масшабирования остальных частей вашего приложения. То есть, такая архитектура позволяет вам масшабировать систему компонентно, там, где это требуется. В случае монолитной, единой системы, вам придётся масшабировать всё приложение.
Ввиду того, что статья и так получилась достаточно большой, то пример реализации микросервисной архитектуры я покажу в следующей статье на эту тему. Я принял решение подготовить более качественный материал на эту тему вместе с примерами кода.
Резюме
Я попытался написать эту статью максимально просто, построив всё объяснение на анлогиях и примерах их жизни. Эта статью можно считать простой инструкцией по работе с Docker. Помимо того, что я описал, как пользоваться Docker-ом, добавив несколько рабочих примеров, которые вы можете попробовать у себя на компьютере, я добавил много дополнительной информации, и некоторые тонкостей работы с Docker-ом. Надеюсь, что эта статья показала вам, что такое Docker, и с чем его едят. В следующей статья я затрону более продвинутые темы и примеры.
Начало работы с Docker. Часть первая. Категория: ОС Linux • Разное
Docker — это система управления процессами приложения в контейнерах. Все процессы выполняются в изолированном пространстве, но в то же время на одном ядре, что позволяет экономить ресурсы основной системы. Docker не реализует собственную систему контейнеров, он использует LXC и выступает в качестве оболочки.
Установка Docker
Дистрибутив Docker, доступный в официальном репозитории Ubuntu, не всегда является последней версией программы. Лучше установить последнюю версию, загрузив ее из официального репозитория Docker.
Сначала обновляем существующий перечень пакетов:
$ sudo apt update
Затем устанавливаем пакеты, которые позволяют apt использовать протокол HTTPS:
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common
Затем добавляем в свою систему ключ GPG официального репозитория Docker:
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Добавляем репозиторий Docker в список источников пакетов apt:
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
Затем обновим базу данных пакетов информацией о пакетах репозитория Docker:
$ sudo apt update
Следует убедиться, что мы устанавливаем Docker из репозитория Docker, а не из репозитория по умолчанию Ubuntu:
$ apt-cache policy docker-ce docker-ce: Установлен: (отсутствует) Кандидат: 5:19.03.8~3-0~ubuntu-bionic Таблица версий: 5:19.03.8~3-0~ubuntu-bionic 500 500 https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages 5:19.03.7~3-0~ubuntu-bionic 500 500 https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages 5:19.03.6~3-0~ubuntu-bionic 500 500 https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages 5:19.03.5~3-0~ubuntu-bionic 500 500 https://download.docker.com/linux/ubuntu bionic/stable amd64 Packages ..........
Далее устанавливаем Docker:
$ sudo apt install docker-ce
Теперь Docker установлен, демон запущен, и процесс будет запускаться при загрузке системы.
$ sudo systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: e Active: active (running) since Wed 2020-03-18 11:46:37 MSK; 1min 59s ago Docs: https://docs.docker.com Main PID: 3656 (dockerd) Tasks: 8 CGroup: /system.slice/docker.service └─3656 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/contain
При установке Docker мы получаем не только сервис docker
, но и утилиту командной строки docker
( клиент Docker).
Использование команды docker без sudo
По умолчанию, запуск команды docker
требует привилегий пользователя root
или пользователя группы docker
, которая автоматически создается при установке Docker. Чтобы не вводить sudo
каждый раз при запуске команды docker
, добавим себя в группу docker
:
$ sudo usermod -aG docker ${USER}
Для применения этих изменений необходимо выполнить команду:
$ su - ${USER}
Использование команды docker
Команда docker
позволяет использовать различные опции и команды с аргументами. Синтаксис выглядит следующим образом:
$ docker [опции] команда [аргументы] # старый синтаксис
$ docker [опции] [группа] команда [аргументы] # новый синтаксис
Пример старого и нового синтаксиса:
$ docker run hello-world # старый синтаксис $ docker container run hello-world # новый синтаксис
Для просмотра всех доступных команд:
$ docker Usage: docker [OPTIONS] COMMAND A self-sufficient runtime for containers Options: --config string Location of client config files (default "/home/evgeniy/.docker") -c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and default context set with "docker context use") -D, --debug Enable debug mode -H, --host list Daemon socket(s) to connect to -l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info") --tls Use TLS; implied by --tlsverify --tlscacert string Trust certs signed only by this CA (default "/home/evgeniy/.docker/ca.pem") --tlscert string Path to TLS certificate file (default "/home/evgeniy/.docker/cert.pem") --tlskey string Path to TLS key file (default "/home/evgeniy/.docker/key.pem") --tlsverify Use TLS and verify the remote -v, --version Print version information and quit Management Commands: builder Manage builds config Manage Docker configs container Manage containers context Manage contexts engine Manage the docker engine image Manage images network Manage networks node Manage Swarm nodes plugin Manage plugins secret Manage Docker secrets service Manage services stack Manage Docker stacks swarm Manage Swarm system Manage Docker trust Manage trust on Docker images volume Manage volumes Commands: attach Attach local standard input, output, and error streams 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 to files or directories 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 Docker objects kill Kill one or more running containers load Load an image from a tar archive or STDIN login Log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container pause Pause all processes within one or more containers 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 one or more containers rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save one or more images to a tar archive (streamed to STDOUT by default) 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 one or more running containers tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE top Display the running processes of a container unpause Unpause all processes within one or more containers update Update configuration of one or more containers version Show the Docker version information wait Block until one or more containers stop, then print their exit codes Run 'docker COMMAND --help' for more information on a command.
Для просмотра опций использования определенной команды:
$ docker search --help Usage: docker search [OPTIONS] TERM Search the Docker Hub for images Options: -f, --filter filter Filter output based on conditions provided --format string Pretty-print search using a Go template --limit int Max number of search results (default 25) --no-trunc Don't truncate output
Работа с образами Docker
Контейнеры Docker создаются и запускаются из образов Docker. По умолчанию Docker получает образы из хаба Docker Hub, представляющего собой реестр образов, который поддерживается компанией Docker.
Скачаем и запустим образ:
$ docker container run hello-world Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 1b930d010525: Pull complete Digest: sha256:f9dfddf63636d84ef479d645ab5885156ae030f611a56f3a7ac7f2fdd86d7e4e Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/
Изначально Docker не смог найти образ hello-world
локально, поэтому загрузил образ из Docker Hub, который является репозиторием по умолчанию. После загрузки образа Docker создал из образа контейнер и запустил приложение в контейнере, которое выдало сообщение «Hello from Docker!».
Образы, доступные в Docker Hub, можно искать с помощью команды команды search
:
$ docker search ubuntu NAME DESCRIPTION STARS OFFICIAL AUTOMATED ubuntu Ubuntu is a Debian-based Linux operating sys… 10634 [OK] dorowu/ubuntu-desktop-lxde-vnc Docker image to provide HTML5 VNC interface … 405 [OK] rastasheep/ubuntu-sshd Dockerized SSH service, built on top of offi… 243 [OK] consol/ubuntu-xfce-vnc Ubuntu container with "headless" VNC session… 212 [OK] ubuntu-upstart Upstart is an event-based replacement for th… 107 [OK] ansible/ubuntu14.04-ansible Ubuntu 14.04 LTS with ansible 98 [OK] ..........
В столбце OFFICIAL
строка OK
показывает, что образ построен и поддерживается компанией, которая занимается разработкой этого проекта. Когда нужный образ выбран, можно загрузить его себе на компьютер с помощью команды pull
:
$ docker pull ubuntu Using default tag: latest latest: Pulling from library/ubuntu 423ae2b273f4: Pull complete de83a2304fa1: Pull complete f9a83bce3af0: Pull complete b6b53be908de: Pull complete Digest: sha256:04d48df82c938587820d7b6006f5071dbbffceb7ca01d2814f81857c631d44df Status: Downloaded newer image for ubuntu:latest docker.io/library/ubuntu:latest
После загрузки образа можно запустить контейнер с загруженным образом с помощью подкоманды run
. Как видно из примера hello-world
, если при выполнении команды run
образ еще не загружен, клиент Docker сначала загрузит образ, а затем запустит контейнер с этим образом.
Для просмотра загруженных на компьютер образов нужно ввести:
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE ubuntu latest 72300a873c2c 3 weeks ago 64.2MB hello-world latest fce289e99eb9 14 months ago 1.84kB
Запуск контейнера Docker
Контейнер hello-world
, запущенный ранее, является примером контейнера, который запускается и завершает работу после вывода тестового сообщения. Контейнеры могут выполнять и более полезные действия, а также могут быть интерактивными. Контейнеры похожи на виртуальные машины, но являются менее требовательными к ресурсам.
В качестве примера запустим контейнер с последней версией Ubuntu. Комбинация опций -i
и -t
обеспечивает интерактивный доступ к командному процессору контейнера:
$ docker run -it ubuntu:latest
Командная строка должна измениться, показывая, что мы работаем в контейнере. Теперь можно запускать любые команды внутри контейнера. Попробуем, например, обновить базу данных пакета внутри контейнера:
# apt update
А после этого установим web-сервер:
# apt install apache2
Чтобы выйти из контейнера, вводим команду
# exit
Команда run
выполняет сразу несколько задач: скачивает образ (если не был скачан ранее), создает новый контейнер, запускает его и выполняет указанную команду (если она задана). Чтобы присвоить контейнеру какое-то осмысленное имя, нужно использовать опцию name
:
$ docker run --name some-name ubuntu:latest /bin/bash
Чтобы запустить контейнер в интерактивном режиме — добавляем опции -i
и -t
:
$ docker run -it --name some-name ubuntu:latest /bin/bash
Когда процесс внутри контейнера выполнит свою работу, то Docker-контейнер завершается. Каждый раз при выполнении команды docker run
, будет создаваться новый контейнер. Чтобы в системе не накапливалось большое количество контейнеров, команду docker run
часто запускают с дополнительной опцией --rm
:
$ docker run -it --rm ubuntu:latest /bin/bash
Управление контейнерами Docker
Через некоторое время после начала использования Docker на машине будет множество активных (запущенных) и неактивных контейнеров. Для просмотра активных контейнеров:
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Для просмотра всех контейнеров:
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 37def446ae84 ubuntu "/bin/bash" 5 minutes ago Exited (127) 25 seconds ago boring_chatterjee 6a278cd2e171 hello-world "/hello" 10 minutes ago Exited (0) 10 minutes ago elegant_euclid
Для запуска остановленного контейнера используем команду start
, затем указываем идентификатор контейнера или его имя:
$ docker container start 37def446ae84
Для подключения к запущенному контейнеру используем команду attach
, затем указываем идентификатор контейнера или его имя:
$ docker container attach boring_chatterjee
Если контейнер был запущен с помощью -i
и -t
, можно отсоединиться от контейнера и оставить его работающим, используя CTRL-p CTRL-q
последовательность клавиш.
Чтобы выполнить отдельную команду внутри контейнера, используем команду exec
, указываем идентификатор или имя контейнера и задаем команду для выполнения:
$ docker container exec boring_chatterjee ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
$ docker container exec -it boring_chatterjee /bin/bash # ls / bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var # exit
Для остановки запущенного контейнера используем команду stop
, затем указываем идентификатор контейнера или его имя:
$ docker container stop boring_chatterjee
Если контейнер больше не нужен, удаляем его командой rm
с указанием либо идентификатора, либо имени контейнера:
$ docker container rm elegant_euclid
Изменить имя контейнера можно с помощью команды rename
:
$ docker container rename boring_chatterjee apache2_ubuntu
$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 37def446ae84 ubuntu "/bin/bash" 5 minutes ago Exited (127) 25 seconds ago apache2_ubuntu
Сохранение изменений в контейнере в образ Docker
После установки Apache2 в контейнере Ubuntu у нас будет работать запущенный из образа контейнер, но он будет отличаться от образа, использованного для его создания. Однако нам может потребоваться такой контейнер Apache2 как основа для будущих образов.
Давайте сохраним состояние контейнера в виде нового образа Docker:
$ docker commit -m "Added web-server Apache2" -a "Evgeniy Tokmakov" 37def446ae84 tokmakov/apache2_ubuntu
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE tokmakov/apache2_ubuntu latest 18073a80c28b 27 seconds ago 191MB ubuntu latest 72300a873c2c 3 weeks ago 64.2MB
Образы также могут строиться с помощью файла Dockerfile
, который позволяет автоматизировать установку программ в новом образе.
Поиск:
CLI • Linux • Ubuntu • Виртуальная машина • Команда • Настройка • Процесс • Установка • Контейнер • Образ • Docker
отвечаем на самые важные вопросы — «Хакер»
Содержание статьи
Мы узнали, что такое Docker, как он работает и какие преимущества может дать. Однако на пути использования этого мощного инструмента у тебя может возникнуть ряд вопросов, и ты столкнешься с трудностями, которые сложно решить без знания архитектурных деталей Docker. В этом FAQ мы попытались собрать ответы на наиболее частые вопросы и решения для самых серьезных проблем Docker.
Как управлять ресурсами, доступными для контейнера (процессор, память)?
В случае с памятью все просто — ты можешь указать максимальный объем памяти, который будет доступен контейнеру:
$ sudo docker run -d -m 256m ubuntu-nginx /usr/sbin/nginx
С процессором все несколько сложнее. Docker полагается на механизм cgroups для ограничения ресурсов процессора, а он оперирует понятием веса, которое может быть от 1 до 1024. Чем выше вес группы процессов (в данном случае контейнера), тем больше шансов у нее получить процессорные ресурсы. Другими словами, если у тебя будет два контейнера с весом 1024 и один с весом 512, то первые два будут иметь в два раза больше шансов получить ресурсы процессора, чем последний. Это нечто вроде приоритета. Значение по умолчанию всегда 1024, для изменения используется опция -c:
$ sudo docker run -d -c 512 ubuntu-ssh /usr/sbin/sshd -D
Также доступна опция —cpuset, с помощью которой контейнер можно привязать к определенным ядрам процессора. Например, если указать —cpuset=»0,1″, то контейнеру будут доступны первые два ядра. Просмотреть статистику использования ресурсов можно с помощью команды docker stats:
$ sudo docker stats Имя/ID-контейнера
Что такое линковка контейнеров и почему она хуже DNS discovery?
Линковка контейнеров — это встроенный в Docker механизм, позволяющий пробрасывать информацию об IP-адресе и открытых портах одного контейнера в другой. На уровне командной строки это делается так:
$ sudo docker run -d --name www --link db:db www /usr/sbin/nginx
Данная команда запускает контейнер www с веб-сервером nginx внутри и пробрасывает внутрь него инфу о контейнере с именем db. При этом происходит две вещи:
- В контейнере www появляется ряд переменных окружения, таких как DB_PORT_8080_TCP_ADDR=172.17.0.82, DB_PORT_8080_TCP_PORT=1234 и DB_PORT_8080_TCP_PROTO=tcp, по три на каждый открытый порт в контейнере db. Эти переменные можно использовать в файлах конфигурации и скриптах, чтобы связать контейнер www с db.
- Файл /etc/hosts контейнера www автоматически обновляется, и в него попадает информация об IP-адресе контейнера db (в данном примере строка 172.17.0.82 db). Это позволяет использовать хостнейм вместо айпишника для обращения к контейнеру db из контейнера www.
Казалось бы, это именно то, что нужно, и без всяких заморочек со SkyDNS. Но данный метод имеет ряд проблем. Первая: обновление записей в /etc/hosts происходит только один раз, а это значит, что, если после перезапуска контейнер db получит другой IP-шник, контейнер www его не увидит (возможно, когда ты читаешь эти строки, проблема уже исправлена). Вторая: хостнеймы видны только изнутри слинкованного контейнера (адресата), поэтому для доступа в обратную сторону, а также из хост-системы и тем более с другой машины все так же придется использовать IP-адреса. Третья: при большом количестве контейнеров и связей между ними настройка с помощью —link чревата ошибками и очень неудобна. Четвертая: линковка не имеет побочного эффекта в виде балансировки нагрузки.
Можно ли управлять Docker через веб-интерфейс?
Официального GUI для Docker не существует. Однако можно найти несколько интересных проектов, решающих эту проблему. Первый — это DockerUI, очень простой веб-интерфейс, распространяемый в виде Docker-образа. Для установки и запуска выполняем такую команду:
$ sudo docker run -d -p 9000:9000 --privileged \
-v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui
Сам интерфейс будет доступен по адресу http://IP-dockerd:9000. В случае необходимости подключиться к другому хосту с запущенным dockerd просто указываем его адрес:порт с помощью опции -e:
$ sudo docker run -d -p 9000:9000 --privileged \
dockerui/dockerui -e http://127.0.0.1:2375
Другой вариант — это Shipyard, включающий в себя такие функции, как аутентификация, поддержка кластеров и CLI. Основное назначение интерфейса — управление кластерами из множества Docker-хостов. Как и в предыдущем случае, установка очень проста:
$ sudo docker run -it -p 8080:8080 -d --name shipyard \
--link shipyard-rethinkdb:rethinkdb shipyard/shipyard
Веб-интерфейс будет доступен на порту 8080, юзер admin, пароль shipyard.
Интерфейс DockerUIА так выглядит Shipyard
Я слышал, что вместо docker exec лучше использовать SSH. Почему?
В небольших проектах docker exec вполне справляется со своей задачей и никакого смысла в использовании SSH нет. Однако SSH дает ряд преимуществ и решает некоторые проблемы docker exec:
- Во-первых, SSH не имеет проблемы «повисших процессов», когда по какой-то причине при выполнении docker exec клиент Docker убивается или падает, а запущенная им команда продолжает работать.
- Во-вторых, для выполнения команды внутри контейнера клиент Docker должен иметь права root, чего не требует клиент SSH. Это вопрос не столько удобства, сколько безопасности.
- В-третьих, в Docker нет системы разграничения прав на доступ к контейнерам. Если тебе понадобится дать кому-то доступ к одному из контейнеров с помощью docker exec, придется открывать полный доступ к docker-хосту.
INFO
Доступ к Docker можно получить из контейнера. Достаточно пробросить внутрь него UNIX-сокет:
$ sudo docker run -it -v /var/run/docker.sock:/var/run/docker.sock\
nathanleclaire/devbox
У меня сложная настройка с зависимостями между контейнерами, поэтому —restart мне не подходит
В этом случае следует использовать стандартную систему инициализации дистрибутива. В качестве примера возьмем сервис MySQL. Чтобы запустить его внутри Docker на этапе инициализации Red Hat, CentOS и любого другого основанного на systemd дистрибутива, нам потребуется следующий unit-файл (mysql меняем на имя нужного контейнера):
[Unit]
Description=MySQL container
Author=Me
After=docker.service
[Service]
Restart=always
ExecStart=/usr/bin/docker start -a mysql
ExecStop=/usr/bin/docker stop mysql
[Install]
WantedBy=local.target
Копируем его в каталог /etc/systemd/system/ под именем docker-mysql.service и активируем:
$ sudo systemctl enable docker-mysql
В Ubuntu та же задача выполняется немного по-другому. Создаем файл /etc/init/docker-mysql.conf:
description "MySQL container"
author "Me"
start on filesystem and started docker
stop on runlevel [!2345]
respawn
script
/usr/bin/docker start -a mysql
end script
А далее отдаем команду Upstart перечитать конфиги:
$ sudo initctl reload-configuration
А теперь самое главное. Чтобы поставить другой сервис в зависимость от этого, просто создаем новый конфиг по аналогии и меняем строку After=docker.service на After=docker-mysql.service в первом случае или меняем строку start on filesystem and started docker на start on filesystem and started docker-mysql во втором.
Периодически некоторые мои контейнеры наполняются зомби-процессами. Почему так происходит?
Это одна из фундаментальных проблем Docker. В UNIX-системах в зомби превращается некорректно остановленный процесс, завершения которого до сих пор ждет родитель, — процесса уже нет, а ядро все равно продолжает хранить о нем данные. В нормальной ситуации зомби существуют недолго, так как сразу после их появления ядро пинает родителя (сигнал SIGCHLD) и тот разбирается с мертвым потомством. Однако в том случае, если родитель умирает раньше ребенка, попечительство над детьми переходит к первичному процессу (PID 1) и разбираться с зомби, в которых могут превратиться его подопечные, теперь его проблема.
В UNIX-системах первичный процесс — это демон init (или его аналог в лице Upstart или systemd), и он умеет разбираться с зомби корректно. Однако в Docker, с его философией «одно приложение на один контейнер» первичным процессом становится то самое «одно приложение». Если оно порождает процессы, которые сами порождают процессы, а затем умирают, попечительство над внуками переходит к главному процессу приложения, а оно с зомби (если они появятся) справляться совсем не умеет (в подавляющем большинстве случаев).
Решить означенную проблему можно разными способами, включая запуск внутри контейнера Upstart или systemd. Но это слишком большой оверхед и явное излишество, поэтому лучше воспользоваться образом phusion/baseimage (на основе последней Ubuntu), который включает в себя минималистичный демон my_init, решающий проблему зомби-процессов. Просто получаем образ из Docker Hub и используем его для формирования образов и запуска своих приложений:
$ sudo docker pull phusion/baseimage
$ sudo docker run -i -t phusion/baseimage:latest /sbin/my_init /bin/top
Кроме my_init, baseimage включает в себя также syslog-ng, cron, runit, SSH-сервер и фиксы apt-get для несовместимых с Docker приложений. Плюс поддержка скриптов инициализации, которые можно использовать для запуска своих сервисов. Просто пропиши нужные команды в скрипт и положи его в каталог /etc/my_init.d/ внутри контейнера.
Почему, собирая образ с помощью Dockerfile, я получаю толстый слоеный пирог?
Команда docker build собирает образ из инструкций Dockerfile не атомарно, а выполняя каждую команду по отдельности. Работает это так: сначала Docker читает команду FROM и берет указанный в ней образ за основу, затем читает следующую команду, запускает контейнер из образа, выполняет команду и делает commit, получая новый образ, затем читает следующую команду и делает то же самое по отношению к сохраненному в предыдущем шаге образу. Другими словами, каждая команда Dockerfile добавляет новый слой к существующему образу, поэтому стоит избегать длинных списков команд вроде таких:
RUN apt-get update
RUN apt-get upgrade
RUN apt-get install nginx
А вместо этого писать все одной строкой:
RUN apt-get update &&\
apt-get upgrade &&\
apt-get install nginx
Ну и в целом не особо увлекаться составлением длинных Dockerfile. Кстати, в Docker есть лимит на количество слоев, и он равен 127. Это искусственное ограничение, введенное с целью не допустить деградации производительности при большом количестве слоев и не позволить админам использовать саму идею слоев не по назначению.
Правильно написанный Dockerfile
Docker поддерживает различные механизмы сборки контейнера из слоев. В чем их различия?
Текущая версия Docker (1.5.0) поддерживает пять различных механизмов для сборки файловой структуры контейнера из слоев: AUFS, Device Mapper, BTRFS, overlay и VFS. Посмотреть, какая технология используется в текущий момент, можно с помощью команды docker info, а выбрать нужную — с помощью флага -s при запуске демона. Различия между технологиями следующие:
- AUFS — технология, применявшаяся в Docker с первых дней существования проекта. Отличается простотой реализации и очень высокой скоростью работы, однако имеет некоторые проблемы с производительностью при открытии громоздких файлов на запись и работе в условиях большого количества слоев и каталогов. Огромный минус: из мейнстримовых дистрибутивов доступна только в ядрах Debian и Ubuntu.
- Device Mapper — комплексная подсистема ядра Linux для создания RAID, шифрования дисков, снапшотинга и так далее. Главное преимущество в том, что Device Mapper доступен в любом дистрибутиве и в любом ядре. Недостаток — что Docker использует обычный заполненный нулями файл для хранения всех образов, а это приводит к серьезным проседаниям производительности при записи файлов.
- Btrfs — позволяет реализовать функциональность AUFS на уровне файловой системы. Отличается высокой производительностью, но требует, чтобы каталог с образами (/var/lib/docker/) находился на Btrfs.
- Overlay — альтернативная реализация функциональности AUFS, появившаяся в ядре 3.18. Отличается высокой производительностью и не имеет ярко выраженных недостатков, кроме требования к версии ядра.
- VFS — самая примитивная технология, опирающаяся на стандартные механизмы POSIX-систем. Фактически отключает механизм разбиения на слои и хранит каждый образ в виде полной структуры каталога, как это делает, например, LXC или OpenVZ. Может пригодиться, если есть проблема вынесения часто изменяемых данных контейнера на хост-систему.
По умолчанию Docker использует AUFS, но переключается на Device Mapper, если поддержки AUFS в ядре нет.
В большинстве случаев Docker будет работать поверх Device Mapper
Расскажите подробнее про Docker Machine, Swarm и Compose
Это три инструмента оркестрации, развиваемых командой Docker. Они все находятся в стадии активной разработки, поэтому пока не рекомендуются к применению в продакшене. Первый инструмент, Docker Machine позволяет быстро развернуть инфраструктуру Docker на виртуальных или железных хостах. Это своего рода инструмент zero-to-Docker, превращающий ВМ или железный сервер в Docker-хост. Бета-релиз Machine уже включает в себя драйверы для двенадцати различных облачных платформ, включая Amazon EC2, VirtualBox, Google Cloud Platform и OpenStack.
Главная задача Machine — позволить системному администратору быстро развернуть кластер из множества Docker-хостов без необходимости заботиться о добавлении репозиториев, установке Docker и его настройке; все это делается в автоматическом режиме. Разработчикам и пользователям Machine также может пригодиться, так как позволяет в одну команду создать виртуальную машину с минимальным Linux-окружением и Docker внутри. Особенно это полезно для юзеров Mac’ов, так как они могут не заморачиваться с установкой Docker с помощью brew или boot2docker, а просто выполнить одну команду:
$ sudo docker-machine create -d virtualbox dev
Второй инструмент, Docker Swarm позволяет добавить в Docker поддержку кластеров из контейнеров. С помощью Swarm можно управлять пулом контейнеров, с автоматической регулировкой нагрузки на серверы и защитой от сбоев. Swarm постоянно мониторит кластер, и, если один из контейнеров падает, он проводит автоматическую ребалансировку кластера с помощью перемещения контейнеров по машинам.
Swarm распространяется в виде контейнера, поэтому создать новый кластер с его помощью можно за считаные секунды:
$ sudo docker pull swarm
$ sudo docker run --rm swarm create
Работая со Swarm, ты всегда будешь иметь дело с сервером Swarm, а не с отдельными контейнерами, которые теперь будут именоваться нодами. На каждой ноде будет запущен агент Swarm, ответственный за принятие команд от сервера. Команды будут выполнены сразу на всех нодах, что позволяет разворачивать очень большие фермы однотипных контейнеров.
Третий инструмент, Docker Compose (в девичестве fig) позволяет быстро запускать мультиконтейнерные приложения с помощью простого описания на языке YAML. В самом файле можно перечислить, какие контейнеры и из каких образов должны быть запущены, какие между ними должны быть связи (используется механизм линковки), какие каталоги и файлы должны быть проброшены с хост-системы. К примеру, конфигурация для запуска стека LAMP из примера в предыдущей статье будет выглядеть так:
web:
image: example/www
command: /usr/sbin/httpd -DFOREGROUND
links:
- db
volumes:
- /root/html:/var/www/html
db:
image: example/mysql
command: /usr/bin/mysqld_safe --bind-address=0.0.0.0
Все, что нужно сделать для его запуска, — просто отдать такую команду:
$ sudo docker-compose up
Но что более интересно — ты можешь указать, сколько контейнеров тебе нужно. Например, ты можешь запустить три веб-сервера и две базы данных:
$ sudo docker-compose scale web=2 db=3
Выводы
При работе с Docker ты столкнешься со множеством других более мелких проблем и у тебя возникнет множество вопросов по реализации той или иной конфигурации. Однако на первых порах этот FAQ должен помочь.
У Docker прекрасная встроенная справка
Несколько простых советов
- Всегда выноси часто изменяемые данные на хост-систему. Логи, базы данных, каталоги с часто меняющимися файлами — все это не должно храниться внутри контейнера (во-первых, усложняется администрирование, во-вторых, при остановке контейнера данные потеряются). В идеале контейнер должен содержать только код, конфиги и статичные файлы.
- Не лепи новые слои при каждом изменении настроек или обновлении софта внутри контейнера. Используй вместо этого Dockerfile для автоматической сборки нового образа с обновлениями и измененными конфигами.
- Вовремя вычищай завершенные и давно не используемые контейнеры, чтобы избежать возможных конфликтов имен.
- По возможности используй SkyDNS или dnsmasq. Их несложно поднять, но они способны сэкономить уйму времени.
- Внимательно изучи хелп по команде run (docker run —help), там много интересного и полезного.
Шпаргалка по командам Dockerfile
- FROM <имя-образа> — какой образ использовать в качестве базы (должна быть первой строкой в любом Dockerfile).
- MAINTAINER <имя> — имя мантейнера данного Dockerfile.
- RUN <команда> — запустить указанную команду внутри контейнера.
- CMD <команда> — выполнить команду при запуске контейнера (обычно идет последней).
- EXPOSE <порт> — список портов, которые будет слушать контейнер (используется механизмом линковки).
- ENV <ключ> <значение> — создать переменную окружения.
- ADD <путь> <путь> — скопировать файл/каталог внутрь контейнера/образа (первый аргумент может быть URL).
- ENTRYPOINT <команда> — команда для запуска приложения в контейнере (по умолчанию /bin/sh -c).
- VOLUME <путь> — пробросить в контейнер указанный каталог (аналог опции -v).
- USER <имя> — сменить юзера внутри контейнера.
- WORKDIR <путь> — сменить каталог внутри контейнера.
- ONBUILD [ИНСТРУКЦИЯ] — запустить указанную инструкцию Dockerfile только в том случае, если образ используется для сборки другого образа (с помощью FROM).
Использование Docker в Ubuntu 20.04
Docker — программное обеспечение для автоматизации развёртывания и управления приложениями в среде виртуализации на уровне операционной системы. Позволяет «упаковать» приложение со всем его окружением и зависимостями в контейнер, который может быть перенесён на любую Linux-систему с поддержкой cgroups в ядре, а также предоставляет среду по управлению контейнерами.
С установкой Docker можно ознакомиться в данном руководстве.
Использование команды Docker
Команда docker позволяет использовать различные опции, команды с аргументами. Синтаксис выглядит следующим образом:
docker [option] [command] [arguments]
Для просмотра всех доступных подкоманд введите:
docker
Полный список будет выглядеть так:
root@kvmde68-19464:~# docker
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default "/root/.docker")
-c, --context string Name of the context to use to connect to the daemon
(overrides DOCKER_HOST env var and default context set with
"docker context use")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal")
(default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default
"/root/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/root/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Management Commands:
builder Manage builds
config Manage Docker configs
container Manage containers
context Manage contexts
engine Manage the docker engine
image Manage images
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
Commands:
attach Attach local standard input, output, and error streams 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 to files or directories 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 Docker objects
kill Kill one or more running containers
load Load an image from a tar archive or STDIN
login Log in to a Docker registry
logout Log out from a Docker registry
logs Fetch the logs of a container
pause Pause all processes within one or more containers
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 one or more containers
rm Remove one or more containers
rmi Remove one or more images
run Run a command in a new container
save Save one or more images to a tar archive (streamed to STDOUT by default)
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 one or more running containers
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
top Display the running processes of a container
unpause Unpause all processes within one or more containers
update Update configuration of one or more containers
version Show the Docker version information
wait Block until one or more containers stop, then print their exit codes
Run 'docker COMMAND --help' for more information on a command.
Для просмотра опций использования определенной команды введите:
docker docker-subcommand --help
Для просмотра всей информации о Docker можно использовать команду:
docker info
root@kvmde68-19464:~# docker info
Client:
Debug Mode: false
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 19.03.11
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd
init version: fec3683
Security Options:
apparmor
seccomp
Profile: default
Kernel Version: 5.4.0-37-generic
Operating System: Ubuntu 20.04 LTS
OSType: linux
Architecture: x86_64
CPUs: 1
Total Memory: 981.3MiB
Name: kvmde68-19464.fornex.org
ID: ZN2U:RFE6:H63F:QXh3:EZPM:L236:H5AX:KGLJ:ZFT3:SAAG:LSSS:TF4H
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Работа с образами Docker
По умолчанию Docker получает образы из хаба Docker Hub, представляющего собой реестр образов, который поддерживается компанией Docker.
Для проверки, можете ли вы осуществлять доступ и загружать образы из Docker Hub, введите следующую команду:
docker run hello-world
Корректный результат работы этой команды, который означает, что Docker работает правильно, представлен ниже:
root@kvmde68-19464:~# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
Изначально Docker не мог находить образ hello-world локально, поэтому загружал образ из Docker Hub, который является репозиторием по умолчанию. После загрузки образа Docker создавал из образа контейнер и запускал приложение в контейнере, отображая сообщение.
Образы, доступные в Docker Hub, можно искать с помощью команды docker и подкоманды search.
docker search debian
Скрипт просматривает Docker Hub и возвращает список всех образов, имена которых подходят под заданный поиск.
root@kvmde68-19464:~# docker search debian
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
ubuntu Ubuntu is a Debian-based Linux operating sys… 11031 [OK]
debian Debian is a Linux distribution that's compos… 3521 [OK]
arm32v7/debian Debian is a Linux distribution that's compos… 66
itscaro/debian-ssh debian:jessie 28 [OK]
arm64v8/debian Debian is a Linux distribution that's compos… 23
samueldebruyn/debian-git a minimal docker container with debian and g… 22 [OK]
multiarch/debian-debootstrap multiarch ports of debian-debootstrap 12
i386/debian Debian is a Linux distribution that's compos… 10
eboraas/debian Debian base images, for all currently-availa… 8 [OK]
vergissberlin/debian-development Docker debian image to use for development, … 6 [OK]
ppc64le/debian Debian is a Linux distribution that's compos… 4
amd64/debian Debian is a Linux distribution that's compos… 4
smartentry/debian debian with smartentry 4 [OK]
vicamo/debian Debian docker images for all versions/archit… 3
s390x/debian Debian is a Linux distribution that's compos… 2
vpgrp/debian Docker images of Debian. 2
arm32v5/debian Debian is a Linux distribution that's compos… 2
spritsail/debian-builder A Docker image based on debian:slim ideal fo… 1 [OK]
holgerimbery/debian debian multiarch docker base image 1
dockershelf/debian Repository for docker images of Debian. Test… 1 [OK]
fleshgrinder/debian Debian base images for production and multis… 0 [OK]
casept/debian-amd64 A debian image built from scratch. Mostly fo… 0
jdub/debian-sources-resource Concourse CI resource to check for updated D… 0 [OK]
1and1internet/debian-9-nginx-php-7.2-wordpress-4 debian-9-nginx-php-7.2-wordpress-4 0 [OK]
konstruktoid/debian Debian base image 0 [OK]
В столбце OFFICIAL строка OK показывает, что образ построен и поддерживается компанией, которая занимается разработкой этого проекта. Когда нужный образ выбран, можно загрузить его на ваш компьютер с помощью подкоманды pull.
Например, загрузка официального образа ubuntu на свой компьютер:
docker pull debian
Вы увидите подобный результат:
root@kvmde68-19464:~# docker pull debian
Using default tag: latest
latest: Pulling from library/debian
e9afc4f90ab0: Pull complete
Digest: sha256:46d659005ca1151087efa997f1039ae45a7bf7a2cbbe2d17d3dcbda632a3ee9a
Status: Downloaded newer image for debian:latest
docker.io/library/debian:latest
После загрузки образа можно запустить контейнер с загруженным образом с помощью подкоманды run.
Для просмотра загруженных образов, введите:
docker images
Вы увидите подобный результат:
root@kvmde68-19464:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 74435f89ab78 6 days ago 73.9MB
debian latest 1b686a95ddbf 2 weeks ago 114MB
hello-world latest bf756fb1ae65 5 months ago 13.3kB
Запуск контейнера Docker
Контейнер hello-world является примером контейнера, который запускается и завершает работу после вывода тестового сообщения. Контейнеры подобны виртуальным машинам, но являются менее требовательными к ресурсам.
Комбинация параметров -i и -t обеспечивает интерактивный доступ к командному процессору контейнера:
docker run -it ubuntu
Командная строка покажет что мы работаем в контейнере:
root@kvmde68-19464:~# docker run -it ubuntu
root@e83d97af5a32:/#
Далее можно запускать команды внутри контейнера.
apt update
Установим Node.js:
apt install nodejs
Данная команда устанавливает Node.js в контейнер из официального репозитория Ubuntu.
Проверим, что Node.js установлен:
node -v
root@e83d97af5a32:/# node -v
v10.19.0
Изменения, которые выполняются внутри контейнера, применяются только для этого контейнера.
Для выхода из контейнера, введите exit.
Управление контейнерами Docker
После начала использования Docker на вашей машине будет множество активных и неактивных контейнеров.
Для того чтобы просмотреть активные контейнеры введите команду:
docker ps
root@kvmde68-19464:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Чтобы увидеть активные и неактивные контейнеры, запустите docker ps с помощью параметра -a:
docker ps -a
root@kvmde68-19464:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@kvmde68-19464:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d3ef7e60d67 debian "bash" 23 seconds ago Exited (0) 14 seconds ago crazy_davinci
e83d97af5a32 ubuntu "/bin/bash" 7 minutes ago Exited (0) 3 minutes ago amazing_wilson
56aac1efc3f6 hello-world "/hello" 20 minutes ago Exited (0) 20 minutes ago vigorous_allen
Чтобы увидеть последние из созданных контейнеров, укажите параметр -l:
docker ps -l
root@kvmde68-19464:~# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d3ef7e60d67 debian "bash" 53 seconds ago Exited (0) 43 seconds ago crazy_davinci
Для запуска остановленного контейнера используем команду docker start и указываем идентификатор контейнера или его имя.
docker start 2d3ef7e60d67
Теперь для просмотра его статуса можно использовать docker ps:
root@kvmde68-19464:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d3ef7e60d67 debian "bash" 2 minutes ago Up 31 seconds crazy_davinci
Для остановки запущенного контейнера используем команду docker stop и указываем идентификатор контейнера или его имя.
docker stop crazy_davinci
Если контейнер больше не нужен, удаляем его командой docker rm с указанием идентификатора или имени контейнера. Чтобы найти идентификатор или имя контейнера, используйте команду docker ps -a. Затем контейнер можно удалить.
docker rm crazy_davinci
Сохранение изменений в контейнере в образ Docker
При запуске контейнера из образа Docker вы можете создавать, изменять и удалять файлы, как и на виртуальной машине.
После установки Node.js в контейнере Ubuntu у вас будет работать запущенный из образа контейнер, но он будет отличаться от образа, использованного для его создания. Однако вам может потребоваться такой контейнер Node.js как основа для будущих образов.
Затем подтверждаем изменения в новом образе Docker с помощью следующей команды.
docker commit -m "What you did to the image" -a "Author Name" container_id repository/new_image_name
Параметр -m позволяет задать сообщение подтверждения, параметр -a позволяет указать автора. Идентификатор контейнера container_id — это идентификатор, который использовался ранее. Если вы не создавали дополнительных репозиториев в Docker Hub, имя репозитория (repository) обычно является вашим именем пользователя в Docker Hub.
Например, для пользователя test и идентификатора контейнера 2d3ef7e60d67 команда будет выглядеть следующим образом:
docker commit -m "added Node.js" -a "test" 2d3ef7e60d67 test/ubuntu-nodejs
После подтверждения (commit) образа, новый образ сохраняется локально на вашем компьютере.
Если просмотреть список образов Docker, в нем окажутся и новый образ, и исходный образ, на котором он был основан:
docker images
Вы увидите подобный результат:
root@kvmde68-19464:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/ubuntu-nodejs latest 79fbfd36b4b3 12 seconds ago 114MB
ubuntu latest 74435f89ab78 6 days ago 73.9MB
debian latest 1b686a95ddbf 2 weeks ago 114MB
hello-world latest bf756fb1ae65 5 months ago 13.3kB
В данном примере ubuntu-nodejs — это новый образ, созданный на основе существующего образа ubuntu из Docker Hub.
Запуск и остановка контейнера Docker — Справка
На этой странице описывается, как остановить и запустить автономный сервер YouTrack.
В следующих ситуациях требуется остановить YouTrack:
Вы хотите изменить расположение базы данных.
Вы хотите изменить свойства или параметры JVM для автономного сервера YouTrack.
Вы хотите обновить автономную установку YouTrack.
Запустить контейнер докеров YouTrack
Чтобы запустить контейнер YouTrack, выполните команду:
docker run -it —name youtrack-server-instance \
-v <путь к каталогу данных>: / opt / youtrack / data \
-v <путь к каталогу conf>: / opt / youtrack / conf \
-v <путь к каталогу журналов>: / opt / youtrack / logs \
-v <путь к каталогу резервных копий>: / opt / youtrack / backups \
-p <порт на хосте>: 8080 \
jetbrains / youtrack: <версия>
Атрибуты команды:
-it
— флаг, который прикрепляет ввод и вывод контейнера YouTrack, включая журналы запуска, в окно терминала.Обратите внимание, что нажатие Ctrl + C, когда терминал подключен к выходу контейнера, приводит к закрытию контейнера. Используйте Ctrl + PQ, чтобы отключить терминал от вывода контейнера. Дополнительные сведения см. В официальной документации докеров.--name youtrack-server-instance
— произвольное имя для контейнера.-v <путь к каталогу NNN>: / opt / youtrack / NNN
— привязка специфичного для YouTrack каталога NNN на хост-компьютере к соответствующему каталогу/ opt / youtrack / NNN
внутри контейнера .-p <порт на хосте>: 8080
— параметр, определяющий отображение портов. Он инструктирует хост-машину прослушивать порт<порт на хосте>
и распространять весь трафик на порт8080
внутри контейнера докеров.
Остановить док-контейнер YouTrack
Чтобы корректно остановить службу YouTrack, выполните команду:
docker exec
Для плавного завершения работы вы также можете использовать стандартную команду ` docker kill --signal = SIGTERM
`.
Мы не рекомендуем использовать стандартную команду « docker stop
». По умолчанию эта команда отправляет сигнал SIGTERM
процессу YouTrack внутри контейнера Docker, а затем ожидает в течение 10 секунд. Если YouTrack не завершит завершение работы в течение этого периода времени, ядру будет отправлено сообщение « SIGKILL
», и процесс YouTrack будет остановлен. Это может привести к повреждению данных. Чтобы избежать этого результата, укажите соответствующее значение тайм-аута при использовании этой команды.Например, следующая команда оставляет достаточно времени для завершения работы службы YouTrack:
docker stop -t 60
Последнее изменение: 11 ноября 2020 г.
Запуск и остановка контейнера Docker — Справка
На этой странице описывается, как остановить и запустить сервер-концентратор.
В следующих ситуациях требуется остановить сервер-концентратор:
Вы хотите изменить расположение базы данных концентратора.
Вы хотите изменить свойства концентратора или параметры JVM для своего сервера-концентратора.
Вы хотите обновить установку концентратора.
Запустить док-контейнер Hub
Чтобы запустить контейнер Hub, выполните команду:
docker run -it —name хаб-сервер-экземпляр \
-v <путь к каталогу данных>: / opt / hub / data \
-v <путь к каталогу conf>: / opt / hub / conf \
-v <путь к каталогу журналов>: / opt / hub / logs \
-v <путь к каталогу резервных копий>: / opt / hub / backups \
-p <порт на хосте>: 8080 \
jetbrains / hub: <версия>
Атрибуты команды:
-it
— флаг, который прикрепляет ввод и вывод контейнера концентратора, включая журналы запуска, в окно терминала.Обратите внимание, что нажатие Ctrl + C, когда терминал подключен к выходу контейнера, приводит к закрытию контейнера. Используйте Ctrl + PQ, чтобы отключить терминал от вывода контейнера. Дополнительные сведения см. В официальной документации докеров.--name hub-server-instance
— произвольное имя контейнера.-v <путь к каталогу NNN>: / opt / hub / NNN
— привязка специфичного для концентратора каталога NNN на хост-машине к соответствующему каталогу/ opt / hub / NNN
внутри контейнера .-p <порт на хосте>: 8080
— параметр, определяющий отображение портов. Он инструктирует хост-машину прослушивать порт<порт на хосте>
и распространять весь трафик на порт8080
внутри контейнера докеров.
Stop Hub docker container
Чтобы корректно остановить службу Hub, выполните команду:
docker exec
Для плавного завершения работы вы также можете использовать стандартную команду docker kill
:
docker kill —signal = SIGTERM
`
Мы не рекомендуем использовать стандартную команду` docker stop
`.По умолчанию эта команда отправляет сигнал ` SIGTERM
` процессу Hub внутри контейнера Docker, а затем ожидает в течение 10 секунд. Если концентратор не завершает завершение работы в течение этого периода времени, ядру отправляется ` SIGKILL
` и процесс концентратора завершается. Это может привести к повреждению данных. Чтобы избежать этого результата, укажите соответствующее значение тайм-аута при использовании этой команды. Например, следующая команда оставляет достаточно времени для завершения работы службы концентратора:
docker stop -t 60
Запуск контейнера Hub как службы
Команда Docker рекомендует использовать кроссплатформенную встроенную политику перезапуска для запуска контейнера как службы.Для этого настройте службу Docker для запуска при загрузке системы и просто добавьте параметр --restart except-Stop
в команду docker run
, которая запускает Hub.
Однако, когда речь идет о последовательном запуске нескольких служб (включая Hub), метод политики перезапуска не подходит. Вместо этого вы можете использовать диспетчер процессов.
Вот пример того, как запустить контейнер концентратора как службу в Linux с помощью systemd
.
Для запуска контейнера концентратора как службы в Linux с помощью systemd
Создайте файл дескриптора службы
/etc/systemd/system/docker.hub.service
:[Ед. изм]
Описание = Hub Service
After = docker.service
Требуется = docker.service[Обслуживание]
TimeoutStartSec = 0
Перезагрузка = всегда
ExecStartPre = — / usr / bin / docker exec% n остановить
ExecStartPre = — / usr / bin / docker rm% n
ExecStartPre = / usr / bin / docker pull jetbrains / hub: <версия>
ExecStart = / usr / bin / docker run —rm —name% n \
-v: / opt / hub / data \
-v: / opt / hub / conf \
-v: / opt / hub / журналы \
-v: / opt / hub / backups \
-p: 8080 \
jetbrains / hub: <версия>[Установить]
WantedBy = по умолчанию.цельВключите запуск службы при загрузке системы с помощью следующей команды:
systemctl включить docker.hub
Вы также можете остановить и запустить службу вручную в любой момент с помощью следующих команд, соответственно:
sudo service docker.hub stop
sudo service docker.hub start
Последнее изменение: 20 октября 2020 г.
Изящная остановка контейнеров Docker — CenturyLink Cloud Developer Center
Большая часть внимания Docker уделяется процессу упаковки и запуска вашего приложения в изолированном контейнере.Существует бесчисленное количество руководств, которые объясняют, как запускать ваше приложение в контейнере Docker, но очень немногие из них обсуждают, как правильно остановить ваше контейнерное приложение. Это может показаться глупой темой — кого волнует, как остановить контейнер, верно?
Что ж, в зависимости от вашего приложения процесс остановки приложения может быть очень важным. Если ваше приложение обслуживает HTTP-запросы, возможно, вы захотите выполнить все невыполненные запросы перед выключением контейнера.Если ваше приложение выполняет запись в файл, вы, вероятно, захотите убедиться, что данные правильно сброшены и файл закрыт до выхода из контейнера.
Ежемесячные советы Docker
Все было бы просто, если бы вы просто запустили контейнер, и он работал вечно, но есть большая вероятность, что ваше приложение нужно будет остановить и перезапустить в какой-то момент, чтобы облегчить обновление или миграцию на другой хост. В тех случаях, когда вам нужно остановить работающий контейнер, было бы предпочтительнее, если бы процесс мог завершаться плавно, а не внезапно отключать пользователей и повреждать файлы.
Итак, давайте посмотрим, что вы можете сделать, чтобы корректно остановить ваши контейнеры Docker.
Отправка сигналов
Существует ряд различных команд Docker, которые можно использовать для остановки работающего контейнера.
докер-стоп
Когда вы запускаете команду docker stop
, Docker сначала вежливо попросит остановить процесс, и если он не выполнит задание в течение 10 секунд, он принудительно убьет его. Если вы когда-либо запускали команду docker stop
и приходилось ждать 10 секунд, пока команда вернется, вы видели это в действии
Команда docker stop
пытается сначала остановить работающий контейнер, отправив сигнал SIGTERM корневому процессу (PID 1) в контейнере.Если процесс не завершился в течение периода ожидания, будет отправлен сигнал SIGKILL.
В то время как процесс может игнорировать SIGTERM, SIGKILL направляется прямо к ядру, которое завершает процесс. Процесс даже не видит сигнала.
При использовании docker stop
единственное, что вы можете контролировать, — это количество секунд, которое демон Docker будет ждать перед отправкой SIGKILL:
докер остановка ---- время = 30 футов
docker kill
По умолчанию команда docker kill
не дает процессу контейнера возможность корректно завершиться — она просто выдает сигнал SIGKILL для завершения работы контейнера.Однако он принимает флаг --signal
, который позволит вам отправить что-то, кроме SIGKILL, в процесс контейнера.
Например, если вы хотите отправить SIGINT (эквивалент Ctrl-C на терминале) в контейнер «foo», вы можете использовать следующее:
docker kill ---- signal = SIGINT foo
В отличие от команды docker stop
, kill
не имеет какого-либо периода ожидания. Он выдает только один сигнал (либо SIGKILL по умолчанию, либо все, что вы укажете с флагом --signal
).
Обратите внимание, что поведение по умолчанию команды docker kill
отличается от стандартной команды Linux kill
, на основе которой она моделируется. Если другие аргументы не указаны, команда Linux kill
отправит SIGTERM (так же, как docker stop
). С другой стороны, использование docker kill
больше похоже на выполнение Linux kill -9
или kill -SIGKILL
.
докер rm -f
Последний вариант остановки работающего контейнера — использование флага --force
или -f
вместе с командой docker rm
.Обычно для удаления уже остановленного контейнера используется docker rm
, но использование флага -f
заставит его сначала выдать SIGKILL.
docker rm ---- force foo
Если ваша цель — стереть все следы работающего контейнера, то docker rm -f
— самый быстрый способ добиться этого. Однако, если вы хотите, чтобы контейнер корректно завершил работу, вам следует избегать этой опции.
Обработка сигналов
Хотя операционная система определяет набор сигналов, способ, которым процесс отвечает на конкретный сигнал, зависит от приложения.
Например, если вы хотите инициировать постепенное завершение работы сервера nginx, вы должны отправить SIGQUIT. Ни одна из команд Docker по умолчанию не выдает SIGQUIT, поэтому вам нужно использовать команду docker kill
следующим образом:
docker kill ---- signal = SIGQUIT nginx
Вывод журнала nginx после получения SIGQUIT будет выглядеть примерно так:
2015/05/11 20:30:20 [уведомление] 1 # 0: получен сигнал 3 (SIGQUIT), завершение работы
2015/05/11 20:30:20 [уведомление] 9 # 0: изящное завершение работы
2015/05/11 20:30:20 [уведомление] 9 # 0: выход
2015/05/11 20:30:20 [уведомление] 9 # 0: выход
2015/05/11 20:30:20 [уведомление] 1 # 0: сигнал 17 (SIGCHLD) получен
2015/05/11 20:30:20 [уведомление] 1 # 0: рабочий процесс 9 завершился с кодом 0
2015/05/11 20:30:20 [уведомление] 1 # 0: выход
Напротив, Apache использует SIGWINCH для плавного завершения работы:
docker kill ---- signal = SIGWINCH apache
Согласно документации Apache, SIGTERM заставит сервер немедленно выйти и прекратить выполнение любых выполняющихся запросов, поэтому вы можете , а не , захотеть использовать docker stop
в контейнере Apache.
Если вы запускаете стороннее приложение в контейнере, вы можете просмотреть документацию приложения, чтобы понять, как оно реагирует на различные сигналы. Простой запуск docker stop
может не дать желаемого результата.
При запуске вашего собственного приложения в контейнере вы должны решить, как различные сигналы будут интерпретироваться вашим приложением. Вам нужно будет убедиться, что вы улавливаете соответствующие сигналы в коде вашего приложения и предпринимаете необходимые действия для полного завершения процесса.
Если вы знаете, что собираетесь упаковать свое приложение в образ Docker, вы можете рассмотреть возможность использования SIGTERM в качестве сигнала постепенного завершения работы, поскольку именно это отправляет команда docker stop
.
Независимо от того, какой язык вы используете, велика вероятность, что он поддерживает некоторую форму обработки сигналов. Я собрал ссылки на соответствующий пакет / модуль / библиотеку для нескольких языков в списке ниже:
Если вы используете Go для своего приложения, обратите внимание на пакет tylerb / graceful, который автоматически включает плавное завершение работы http.Обработчик серверов в ответ на сигналы SIGINT или SIGTERM.
Прием сигналов
Кодирование вашего приложения для корректного завершения работы в ответ на определенный сигнал — хороший первый шаг, но вам также необходимо убедиться, что ваше приложение упаковано таким образом, чтобы у него была возможность получать сигналы, отправленные командами Docker. Если вы не будете осторожны при запуске своего приложения, оно может никогда не получить какие-либо сигналы, отправленные docker stop
или docker kill
.
Для демонстрации давайте создадим простое приложение, которое мы будем запускать внутри контейнера Docker:
#! / Usr / bin / env bash
ловушка 'выход 0' SIGTERM
пока правда; делать :; сделанный
Этот тривиальный сценарий bash просто входит в бесконечный цикл, но выйдет со статусом 0, если получит SIGTERM.
Мы упакуем это в образ Docker со следующим файлом Dockerfile:
ОТ ubuntu: верный
КОПИРОВАТЬ loop.sh /
CMD /loop.sh
Это просто скопирует наш цикл .sh bash в образ на основе Ubuntu и установите его как команду по умолчанию для запущенного контейнера.
Теперь давайте создадим этот образ, запустим контейнер и сразу остановим его.
$ docker build -t loop.
Отправка контекста сборки демону Docker 3.072 kB
Отправка контекста сборки демону Docker
Шаг 0: ИЗ ubuntu: trusty
--- & gt; 07f8e8c5e660
Шаг 1: КОПИРОВАТЬ loop.sh /
--- & gt; 161f583a7028
Снятие промежуточного контейнера e0988f66358a
Шаг 2: CMD / цикл.ш
--- & gt; Работает в 6d6664be02da
--- & gt; 18b3feccee90
Снятие промежуточного контейнера 6d6664be02da
Успешно построено 18b3feccee90
$ docker run -d цикл
64d39c3b49147f847722dbfd0c7976315533a729d9453c34cb6cbdaa11d46c21
$ docker stop 64d39c3b
Если вы следите за инструкциями, вы могли заметить, что выполнение приведенной выше команды docker stop
заняло около 10 секунд — обычно это признак того, что ваш контейнер не ответил на SIGTERM и должен быть принудительно завершено SIGKILL.
Мы можем проверить это, посмотрев на статус выхода контейнера.
$ docker inspect -f '{{.State.ExitCode}}' 64d39c3b
137
В зависимости от обработчика, который мы настроили в нашем приложении, если бы наш контейнер получил SIGTERM, мы должны были бы видеть статус выхода 0, а не 137. Фактически, статус выхода больше 128 обычно является признаком того, что процесс был завершен как в результате необработанного сигнала. 137 = 128 + 9 — означает, что процесс был остановлен в результате сигнала № 9 (SIGKILL).
Итак, что здесь произошло? Наше приложение закодировано так, чтобы перехватить SIGTERM и корректно завершить работу. Мы знаем, что docker stop
отправляет SIGTERM процессу контейнера. Тем не менее, похоже, что сигнал никогда не доходил до нашего приложения.
Чтобы понять, что здесь произошло, давайте запустим другой контейнер и посмотрим на запущенные процессы.
$ docker run -d цикл
512c36b5b517b3a43246b519bc5cdb756cdbc4d2ff1e0a3984e83b094f3db136
$ docker exec 512c36b5 ps -ef
UID PID PPID C STIME TTY TIME CMD
корень 1 0 0 16:03? 00:00:00 / bin / sh -c / loop.ш
корень 13 1 61 16:03? 00:00:10 баш /loop.sh
корень 14 0 0 16:03? 00:00:00 ps -ef
В выходных данных выше важно отметить, что наш сценарий loop.sh НЕ запускается как PID 1 внутри контейнера. Сценарий фактически выполняется как дочерний по отношению к процессу / bin / sh , запущенному с PID 1.
Когда вы используете docker stop
или docker kill
для сигнализации контейнера, этот сигнал отправляется только процессу контейнера, работающему как PID 1.
Поскольку / bin / sh не пересылает сигналы каким-либо дочерним процессам, отправленный нами SIGTERM так и не дошел до нашего скрипта. Очевидно, что если мы хотим, чтобы наше приложение могло получать сигналы от хоста, нам нужно найти способ запускать его как PID 1.
Для этого нам нужно вернуться в наш Dockerfile и посмотреть на инструкцию CMD, используемую для запуска нашего скрипта. На самом деле инструкция CMD может принимать несколько различных форм. В нашем Dockerfile выше мы использовали оболочку в форме , которая выглядит так:
CMD команда param1 param2
При использовании формы оболочки указанная команда выполняется с оболочкой / bin / sh -c
.Если вы посмотрите на список процессов для нашего контейнера, вы увидите, что процесс с PID 1 показывает командную строку «/ bin / sh -c /loop.sh». Итак, / bin / sh запускается как PID 1, а затем разветвляет / выполняет наш скрипт.
К счастью, Docker также поддерживает форму exec инструкции CMD, которая выглядит так:
CMD ["исполняемый файл", "параметр1", "параметр2"]
Обратите внимание, что содержимое, появляющееся после инструкции CMD, в этом случае форматируется как массив JSON.
Когда используется форма exec инструкции CMD, команда будет выполняться без оболочки.
Давайте изменим наш Dockerfile, чтобы увидеть это в действии:
ОТ ubuntu: верный
КОПИРОВАТЬ loop.sh /
CMD ["/loop.sh"]
Восстановите образ и посмотрите процессы, запущенные в контейнере:
$ docker build -t loop.
[усечено]
$ docker run -d цикл
4dda905ee902c91d1f56082d1092d6d72ef54b3d4582fe6b453cba90777554e2
$ docker exec 4dda905e ps -ef
UID PID PPID C STIME TTY TIME CMD
корень 1 0 30 16:42? 00:00:04 Баш / цикл.ш
корень 13 0 0 16:42? 00:00:00 ps -ef
Теперь наш сценарий работает как PID 1. Давайте отправим SIGTERM в контейнер и посмотрим на статус выхода:
$ докер-стоп 4dda905e
$ docker inspect -f '{{.State.ExitCode}}' 4dda905e
0
Это именно тот результат, на который мы рассчитывали! Наш скрипт получил SIGTERM, отправленный командой docker stop
, и завершил работу со статусом 0.
Суть в том, что вы должны проводить аудит процессов внутри вашего контейнера, чтобы убедиться, что они в состоянии принимать сигналы, которые вы собираетесь отправить.Использование exec формы инструкции CMD (или ENTRYPOINT) в вашем Dockerfile — хорошее начало.
Заключение
Довольно просто завершить работу контейнера Docker с помощью команды docker kill
, но если вы действительно хотите упорядоченно свернуть свои приложения, потребуется немного больше работы. Теперь вы должны понимать, как отправлять сигналы в свои контейнеры, как обрабатывать эти сигналы в ваших пользовательских приложениях и как гарантировать, что ваши приложения могут даже получать эти сигналы в первую очередь.
Как остановить все запущенные контейнеры Docker
Как остановить все запущенные контейнеры Docker
Люди, которые работают с Docker, знают, что легко создать большое количество контейнеров. Иногда вам нужно остановить все запущенные контейнеры на вашем компьютере. Это может быть выполнено с помощью docker stop
вместе с docker ps
(с использованием подстановки команд).
докер стоп $ (докер пс -q)
docker stop $ (докер ps -q) |
docker ps
используется для вывода списка всех запущенных контейнеров.Параметр -q
отображает только идентификаторы контейнеров. Использование команды docker ps
можно увидеть ниже:
Использование: docker ps [ОПЦИИ]
Список контейнеров
Параметры:
-a, —all Показать все контейнеры (по умолчанию показаны только запущенные)
-f, —filter value Отфильтровать вывод на основе предоставленных условий (по умолчанию [])
—format string Довольно печатные контейнеры с использованием шаблона Go
—help Использование печати
-n, —last int Показать n последних созданных контейнеров (включая все состояния) (по умолчанию -1)
-l, —latest Показать последний созданный контейнер (включая все состояния)
—no-trunc Не обрезать вывод
-q, —quiet Отображать только числовые идентификаторы
-s, —size Показать общий размер файлов
Использование: docker ps [ОПЦИИ] Список контейнеров Параметры: -a, —all Показать все контейнеры (по умолчанию отображаются только что запущенные) -f, —filter value Фильтр на основе вывода при заданных условиях (по умолчанию []) —format string Довольно печатные контейнеры с использованием шаблона Go —help Использование печати -n, —last int Показать n последних созданных контейнеров (включая все состояния) (по умолчанию -1) -l, —latest Показать последний созданный контейнер (включая все состояния) —no-trunc Не усекать вывод -q, —quiet Отображать только числовые идентификаторы -s, —size Показать общий размер файлов |
докер Linux / оболочка
Изящно останавливает контейнеры Docker — DZone Cloud
Это была моя площадка для тестирования, но после того, как я начал работать над развертыванием приложений корпоративного уровня с помощью Docker, я обнаружил, что во многих вещах я делал неправильно.Одним из них было то, как я запустил приложение внутри Docker.
Почти во всех моих файлах dockerfiles в конце есть скрипт bash для внесения небольших изменений внутри контейнеров и, наконец, для запуска приложения. Я обычно добавляю этот скрипт в инструкцию CMD в Dockerfile. Я думаю, в этом нет ничего плохого, кроме того, что Docker перестал работать как надо.
Проблема в том, что при запуске сценария bash он получит PID 1, а ваше приложение является дочерним процессом с PPID 1.Bash не будет пересылать сигнал SIGTERM вашему приложению, когда вы запускаете docker stop. Вместо этого контейнер будет убит через 10 секунд тайм-аута, который используется по умолчанию для команды docker stop. Этот таймаут можно изменить.
Есть простой способ справиться с этим с помощью команды «exec» внутри сценария bash. Он заменит оболочку без создания нового процесса, и ваше приложение получит PID 1. Давайте сначала протестируем оба сценария.
Для тестирования я буду использовать этот простой файл докеров redis:
ОТ ubuntu: trusty
ENV DEBIAN_FRONTEND не интерактивный
БЕГАТЬ \
apt-get update && \
apt-get -y установить \
общие свойства программного обеспечения && \
add-apt-репозиторий -y ppa: chris-lea / redis-server && \
apt-get update && \
apt-get -y установить \
Redis-сервер && \
rm -rf / var / lib / apt / lists / *
КОПИРОВАНИЕ начало.sh start.sh
ВЫБРАТЬ 6379
ЗАПУСТИТЬ rm /usr/sbin/policy-rc.d
CMD ["/start.sh"]
А вот скрипт start.sh, который изменит некоторые рекомендуемые настройки ядра для контейнера redis (докер должен быть запущен с привилегированным значением true):
#! / Usr / bin / env bash
# Отключить поддержку THP в ядре
эхо никогда> / sys / kernel / mm / transparent_hugepage / enabled
# Настройка TCP backlog (по умолчанию 128)
sysctl -w net.core.somaxconn = 16384
# ------------------------------------------------- ------------------------------
/ usr / bin / redis-server
Теперь создадим и запустим этот контейнер:
docker build -t my / redis.docker run -d --privileged --name test my / redis
Затем проверьте, что выполняется внутри контейнера redis:
docker exec test ps -ef
UID PID PPID C STIME TTY TIME CMD
корень 1 0 0 13:20? 00:00:00 bash /start.sh
корень 6 1 0 13:20? 00:00:00 / usr / bin / redis-server *: 6379
Хорошо, это проблема. Теперь давайте попробуем остановить этот контейнер докеров:
тест остановки докера
Через 10 секунд контейнер будет убит, а не остановлен корректно.Мы сможем убедиться в этом, если проверим журнал. Последнее сообщение будет о том, что redis готов принимать соединения:
docker logs test
Чтобы это заработало, нам нужно изменить последнюю строку в скрипте start.sh и пересобрать образ. Мы добавляем exec перед командой / usr / bin / redis-server:
#! / Usr / bin / env bash
# Отключить поддержку THP в ядре
эхо никогда> / sys / kernel / mm / transparent_hugepage / enabled
# Настройка TCP backlog (по умолчанию 128)
sysctl -w net.core.somaxconn = 16384
# ------------------------------------------------- ------------------------------
exec / usr / bin / redis-server
Создайте, запустите и снова выполните команду ps -ef:
docker exec test ps -ef
UID PID PPID C STIME TTY TIME CMD
корень 1 0 1 13:24? 00:00:00 / usr / bin / redis-server *: 6379
Как вы можете видеть, теперь redis работает как PID 1, и docker stop будет работать нормально. Давайте сначала попробуем и снова проверим журнал докеров.Вы должны увидеть это сообщение в журнале redis:
Получено SIGTERM планирование выключения ...
Вот как я использую exec с контейнерами postgres и tomcat, где процессы не выполняются с пользователем root:
exec sudo -E -u tomcat7 $ CATALINA_HOME / bin / catalina.sh запустить
exec su postgres -c "$ {POSTGRES_BIN} -D $ {PGDATA} -c config_file = $ {CONF}"
Здесь процессы не будут работать с PID 1 из-за sudo или su, однако Docker stop отлично работает в обоих случаи.Причина этого в том, что команды sudo и su будут передавать сигнал SIGTERM дочерним процессам.
Запуск контейнера — последняя документация docker-stacks
Для использования одного из стеков Jupyter Docker требуется два варианта:
- Какой образ Docker вы хотите использовать
- Как вы хотите запускать контейнеры Docker из этого образа?
В этом разделе содержится подробная информация о втором.
Использование Docker CLI
Вы можете запустить локальный контейнер Docker из стеков Jupyter Docker, используя интерфейс командной строки Docker.Существует множество способов настройки контейнеров с помощью интерфейса командной строки. Ниже приведены некоторые общие закономерности.
Пример 1 Эта команда извлекает образ jupyter / scipy-notebook
с тегом 2c80cf3537ca
из Docker Hub, если он еще не присутствует на локальном хосте. Затем он запускает контейнер, на котором запущен сервер Jupyter Notebook, и предоставляет сервер через порт хоста 8888. Журналы сервера появляются в терминале и включают URL-адрес сервера ноутбука.
docker run -p 8888: 8888 jupyter / scipy-notebook: 2c80cf3537ca Выполнение команды: jupyter notebook [Я 15:33:00.567 NotebookApp] Запись секрета cookie сервера записной книжки в /home/jovyan/.local/share/jupyter/runtime/notebook_cookie_secret [W 15: 33: 01.084 NotebookApp] ПРЕДУПРЕЖДЕНИЕ. Сервер ноутбука прослушивает все IP-адреса и не использует шифрование. Это не рекомендуется. [I 15: 33: 01.150 NotebookApp] Расширение предварительного просмотра альфа-версии JupyterLab загружено из /opt/conda/lib/python3.6/site-packages/jupyterlab [I 15: 33: 01.150 NotebookApp] Каталог приложения JupyterLab: / opt / conda / share / jupyter / lab [I 15: 33: 01.155 NotebookApp] Обслуживание ноутбуков из локального каталога: / home / jovyan [Я 15:33:01.156 NotebookApp] 0 активных ядер [I 15: 33: 01.156 NotebookApp] Jupyter Notebook работает по адресу: [I 15: 33: 01.157 NotebookApp] http: // [все IP-адреса в вашей системе]: 8888 /? Token = 112bb073331f1460b73768c76dffb2f87ac1d4ca7870d46a [I 15: 33: 01.157 NotebookApp] Используйте Control-C, чтобы остановить этот сервер и закрыть все ядра (дважды, чтобы пропустить подтверждение). [C 15: 33: 01.160 NotebookApp] Скопируйте / вставьте этот URL-адрес в свой браузер при первом подключении, для входа с токеном: http: // localhost: 8888 /? token = 112bb073331f1460b73768c76dffb2f87ac1d4ca7870d46a
Нажатие Ctrl-C
выключает сервер ноутбука, но оставляет контейнер нетронутым на диске для последующего перезапуска или окончательного удаления с помощью следующих команд:
# список контейнеров докер ps -a КОНТЕЙНЕР ИДЕНТИФИКАЦИЯ ИЗОБРАЖЕНИЕ КОМАНДА СОЗДАНА СОСТОЯНИЕ НАЗВАНИЯ ПОРТОВ d67fe77f1a84 jupyter / base-notebook "tini - start-noteb…" 44 секунды назад Завершился (0) 39 секунд назад cocky_mirzakhani # запускаем остановленный контейнер docker start -a d67fe77f1a84 Выполнение команды: jupyter notebook [Вт 16:45:02.020 NotebookApp] ПРЕДУПРЕЖДЕНИЕ. Сервер ноутбука прослушивает все IP-адреса и не использует шифрование. Это не рекомендуется. ... # удалить остановленный контейнер докер rm d67fe77f1a84 d67fe77f1a84
Пример 2 Эта команда извлекает образ jupyter / r-notebook
с тегом e5c5a7d3e52d
из Docker Hub, если он еще не присутствует на локальном хосте. Затем он запускает контейнер, на котором запущен сервер Jupyter Notebook, и предоставляет сервер на хост-порт 10000.Журналы сервера появляются в терминале и включают URL-адрес сервера ноутбука, но с внутренним портом контейнера (8888) вместо правильного порта хоста (10000).
docker run --rm -p 10000: 8888 -v "$ PWD": / home / jovyan / work jupyter / r-notebook: e5c5a7d3e52dВыполнение команды: jupyter notebook
[I 19: 31: 09.573 NotebookApp] Запись секрета cookie сервера записной книжки в /home/jovyan/.local/share/jupyter/runtime/notebook_cookie_secret
[W 19: 31: 11.930 NotebookApp] ПРЕДУПРЕЖДЕНИЕ.