Часть 1. Разработка
Выступая в роли разработчиков, куча нашего времени обычно тратится на разработку приложений в нашей локальной среде.
В данной части мы шаг за шагом изучим как начать применять Docker в качестве фрагмента вашего локального рабочего потока разработки
Глава 1. Новый мир храбрых
Содержание
В этой главе мы намерены убедится что вы вооружены установкой рабочей версии Docker в вашей машине. Это важно, поэтому вы действительно должны попробовать самостоятельно и следовать приводимым примерам.
Далее мы сразу же погрузимся в пучину и выполним свою первую в истории команду Docker - запустив базовый скрипт на Ruby. Однако вместо того, чтобы полагаться на установленную на вашем локальном компьютере версию Ruby мы воспользуемся версей, поставляемой Docker.
Вы ознакомитесь с основами того как работает Docker, в том числе что такое образы
и контейнеры и зачем они нам потребовались. Мы освоим основы анатомии
docker run
- вероятно самой центральной команды для понимания Docker
Мы также начнём своё путешествие по встраиванию Docker в наш рабочий поток разработки через изучение того как выработать новый проект Rails без чего бы то ни было кроме Docker. Такое прикладное приложение станет основным предметом наших разнообразных размышлений и исследования на протяжении оставшейся части этой книги.
Давайте приступим к установке Docker на вашей машине.
Для меня имеется небольшое преимущество при проходе по пошаговому процессу установки: документация Docker проделала великолепную работу здесь и это остаётся актуальным и по сей день. Я просто укажу вам верное направление.
Мы намерены воспользоваться свободно распространяемой Community Edition (CE, Редакцию сообщества), вместо того чтобы применять Enterprise Edition (EE, Корпоративную редакцию). Сам по себе Docker CE поступает в двух качествах: Edge (находящемся на переднем крае), который содержит самые последние только что разработанные свойства, и Stable, который, ну, вы понимаете, ещё более стабильный. Убедитесь что вы устанавливаете последнюю версию, так как нам не нужны неожиданные сюрпризы, возникающие по ходу обучения пока вы следуете по данной книге.
Пройдите далее и прочтите инструкции по установки именно для вашей ОС, затем установите Docker и вернитесь ко мне обратно когда завершите. Не беспокойтесь, я подожду - ничто не будет плавать на моём борту лучше чем совершенно новая, резвая установка Docker.
Docker предоставляет выгружаемую установку с названием Docker for Mac, в которой имеется всё что вам нужно в одном чётком пакете (это примерно 115.6 МБ для выгрузки). Пройдите далее и установите его следуя инструкциям по установке.
После установки Docker for Mac добавляет некую панель меню прикладного приложения в правом верхнем углу вашего экрана, снабжённое логотипом Docker, которым является талисман кита, нежно именуемый "Моби Док". Эта панель меню не только сообщает вам запущен ли Docker, но также и предоставляет прочие полезные информацию и настройки. В можете найти дополнительные подробности о расширенных настройках доступными в документации.
К сожалению, начало работы с Docker в Linux слегка более вовлекает вас в процесс нежели прочие платформы. Как вы вероятно и ожидали, то как выполнять установку, зависит от вашего дистрибутива Linux.
Посетите соответствующую документацию по установке Docker CE, выберите свой дистрибутив Linux в меню навигации, а затем следуйте инструкциям. Обычно это вовлекает в процесс установки Docker диспетчер пакетов вашего дистрибутива, которому могут потребоваться самые последние пакеты из репозитория Docker, поскольку сами пакеты дистрибутива зачастую намного отстают.
Вам также потребуется пересмотреть инструкции пост- установки Docker, чтобы убедиться что вы всё сделали верно. Это поможет вам избежать какие бы то ни было проблемы, с которыми вы бы столкнулись.
В последующих главах мы будем полагаться на некий инструментарий с названием Docker Compose (компоновщик Docker), который в Linux устанавливается отдельно. Пройдите далее и установите его воспользовавшись надлежащей документацией. Если так получится, что он уже установлен, я рекомендую обновить его до самой последней, стабильной версии.
То как устанавливать Docker зависит от того поддерживает ли ваша система Hyper-V, доморощенную технологию виртуализации Microsoft. Редакции Professional, Enterprise или Education (в своих 64- битных версиях) Windows 8 и выше имеют его поддержку - если это допускают аппаратные средства - однако, в частности, редакция Windows Home нет. Подробнее....
Если ваша система поддерживает Hyper-V, выгрузите установщик Docker for Windows, запустите его и следуйте инструкциям Docker for Windows устанавливает некий виджет в области уведомлений вашего Windows в правом нижнем углу вашего экрана (вам может понадобиться кликнуть туда чтобы раскрыть его). Кликнув по данному виджету вы откроете некое меню в котором вы обнаружите дополнительную информацию относительно регулировок различных настроек.
Если ваша система не поддерживает Hyper-V, вам может потребоваться выгрузить и установить Docker Toolbox, некий наследуемый способ запуска Docker on Windows. {Прим. пер.: или, например, установить VirtualBox. Также отметим, что начиная с Windows Server 2019 Microsoft имеет поддержку MobyVM и LCOW, которые позволяют контейнерам Linux работать на хосте контейнеров Windows Server, даже работая бок о бок с контейнерами Windows!}
Давайте убедимся что Docker установлен и запущен правильно. Так как Docker является неким инструментарием работающим из командной строки, пройдите далее и вскройте свой любимый терминал {Прим. пер.: например, 64- битный File Commander Брайана Хэйварда}, а затем введите такую команду:
$ docker version
Если всё хорошо, вы должны получить что- то подобное следующему:
Client: Docker Engine - Community
Version: 18.09.0
API version: 1.39
Go version: go1.10.4
Git commit: 4d60db4
Built: Wed Nov 7 00:47:43 2018
OS/Arch: darwin/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 18.09.0
API version: 1.39 (minimum version 1.12)
Go version: go1.10.4
Git commit: 4d60db4
Built: Wed Nov 7 00:55:00 2018
OS/Arch: linux/amd64
Experimental: false
Не беспокойтесь, если у вас более новая версия, чем это показано здесь. У вас всё готово.
Получили установленный Docker? Отлично, вы как раз успели вовремя - я не начинал без вас. Прежде чем мы перепачкаем свои руки и начнём играться с Docker, полезно ознакомиться с двумя основными понятиями: контейнерами и образами.
На понятийном уровне некий контейнер представляет собой некую изолированную или "помещённую в песочницу" среду исполнения - какую- то пустую ёмкость для исполнения в ней программное обеспечение. Контейнеры полагаются на свойства виртуализации встроенного в него ядра Linux (а в последнее время и Windows), что позволяет вам создавать полностью изолированный набор процессов, которые не знают (или не озабочены) относительно всей прочей системы. На самом деле, внутри некого контейнера, всё представляется таким образом, как будто это полная система Linux (или - {Прим. пер.: а вас это волнует?} - Windows), хотя все его ресурсы и возможности исходят от той машины хоста, в которой он исполняется.
Контейнеры могут запускаться, приостанавливаться, возобновляться и останавливаться, что приводит многих людей к представлению сопоставимости с виртуальными машинами (ВМ). В действительности, однако, за пределами этого сходства, контейнеры это совершенно другие зверюги. В то время как ВМ требует некой ОС хоста, какого- то уровня абстракции программного обеспечения с названием гипервизор, и полной установки ОС для каждого экземпляра, контейнеры очень близки к железу. Каждый контейнер это всего лишь прицеп к имеющимся ресурсам отдельного ядра с достаточно тонким уровнем изоляции. Это означает, что вы можете запускать намного больше контейнеров в отдельной машине по сравнению с ВМ - они быстрее и используют меньше ресурсов.
Как мы только что сказали, некий контейнер, чисто абстрактно, это всего лишь пустая ёмкость для исполнения в ней программного обеспечения. Чтобы запустить некий контейнер, вам понадобится снабдить его некой особой средой или контекстом - то, что вам потребуется для запуска некого веб сервера NGINX в одном контейнере, будет совершенно отличаться от того, что вам понадобится для, допустим, MySQL, в другом контейнере.
Та среда или тот контекст, который вы предоставляете когда создаёте свой контейнер - и именуется как образ - это всё что делает такой контейнер уникальным. Например, как выглядит файловая система? Какие переменные окружения настроены? Какая команда выполняет запуск? Итак, некий образ это какой- то упакованный пакет всего что требуется для исполнения одного (определённого) контейнера.
Применяя некий образ, вы можете порождать столько контейнеров, сколько пожелаете, которые все будут на одно лицо. По этой причине вы можете найти полезным представлять себе некий образ как какую- то фабрику для выпечки особых контейнеров. Некоторые также сопоставляют образы с каким- то абстрактным классом при программировании, а контейнеры экземплярами этого класса.
Образы идеально подходят для совместного использования и распространения программного обеспечения: они применяют некий стандартный формат, который разработан с целью быть переносимым. Docker предоставляет встроенные инструменты для распространения образов. Совместно применяя образы вы можете сотрудничать в разработке программного обеспечения внутри своей команды и делать ваше программное обеспечение доступным для развёртывания.
Мы намерены предпринять нечто магическое. При помощи Docker мы собираемся запустить некое приложение Ruby без необходимости устанавливать Ruby в вашей системе.
Давайте взглянем на это:
$ docker run ruby:2.6 ruby -e "puts :hello"
Unable to find image 'ruby:2.6' locally
2.6: Pulling from library/ruby
cd8eada9c7bb: Pull complete
c2677faec825: Pull complete
fcce419a96b1: Pull complete
045b51e26e75: Pull complete
3b969ad6f147: Pull complete
f2db762ad32e: Pull complete
708e57760f1b: Pull complete
06478b05a41b: Pull complete
Digest: sha256:ad724f6982b4a7c2d2a8a4ecb67267a1961a518029244ed943e2d448d6fb7
994
Status: Downloaded newer image for ruby:2.6
hello
Фига себе! Это что сейчас было?
Если вы взглянете на самую последнюю строку вывода, вы обнаружите, что это тот самый вывод, который мы бы ожидали
получить из своего сценария Ruby: hello
. Итак, это каким- то образом работает.
Но как? Почему?
Наша команда docker run
имеет следующий формат:
$ docker run [OPTIONS] <image> <command>
Эта команда запускает некий новый контейнер, который основан на <image>
и
исполняет <command>
внутри этого контейнера. Вы можете найти полезным
представлять себе это в двух частях: docker run [OPTIONS] <image>
сообщает какой тип контейнера мы намерены запустить, в то время как <command>
сообщает что мы желаем сделать внутри этого контейнера.
Итак, вернёмся обратно к своей команде, у нас есть:
Наша первая часть сообщает что мы желаем запустить некий контейнер на основе образа ruby:2.6
.
Как мы уже обсуждали в Прежде чем вы начнёте, некий образ является
полностью экипированным пакетом всего что требуется для запуска (какого- то особенного) контейнера. Наш образ
ruby:2.6
не является исключением; он обладает предварительно установленным Ruby 2.6,
причём со всеми его зависимостями, что позволяет нам созлавать контейнеры, способные запускать эту версию Ruby.
Вторая имеющаяся часть нашей команды определяет что мы желаем запустить внутри этого
контейнера. В данном случае мы сообщаем, что хотели бы запустить соответствующий интерпретатор Ruby с неким сценарием, который
передаётся при помощи параметра командной строки -e
. Этот сценарий это
самое простое что только можно себе представить: он всего- навсего выводит слово "hello".
Терминология: Исполнение образа | |
---|---|
Может так случиться, что мы произносим исполнение некого образа, однако строго говоря, это неверно. Образы не могут исполняться непосредственно; они являются неизменными фабриками для создания контейнеров. Вместо этого мы имеем в виду, что мы создаём некий контейнер на основе данного образа, а уже именно этот контейнер может быть исполнен. |
Наша команда docker run
будет работать в любой машине которая имеет установленным
Docker - даже без самого Ruby.
Как это может быть? Это чудесно, что наш образ ruby:2.6
имеет установленным Ruby, но при помощи
какой магии мы получили его в своём компьютере?
На самом деле мы этого не делали.
Когда мы исполнили свою команду docker run
, вы могли заметить, что он нам сообщил
Unable to find image ’ruby:2.6’ locally
. Затем Docker осуществил выгрузку нужного нам
образа ruby:2.6
, и по этой причине данная команда потребовала какого- то времени
для исполнения. Вместо того чтобы выгрузить этот образ за один шаг, выгрузка была выполнена по частям - именуемым
уровнями - и это создало нам необходимый образ. Таким образом, Docker снабдил нас
бесшовным механизмом для доставки в точности тех образов что нам нужны, когда он нам требуются.
Если вы самостоятельно выполняли нашу предыдущую команду, имелся один небольшой изъян, который вы могли заметить: она потребовала многоооо времени. Я знаю, языки с интерпретацией, такие как Ruby, достаточно медленные, но это нелепо.
Наше предыдущее обсуждение может помочь пояснению почему исполнение команды потребовало так много времени.
Основной момент в том, что время понадобилось не на исполнение нашего мелкого сценария Ruby; оно было затрачено на
выгрузку нашего образа ruby:2.6
через нашу сетевую среду. Всякий раз когда мы
запускаем некий контейнер на базе какого- то образа, как мы это сделали ранее, Docker вначале должен выгрузить его.
Хотя образы обычно намного меньше чем ВМ - МБ вместо ГБ - дожидаться по 20 секунд для каждой команды Docker было бы достаточно разочаровывающим. К счастью, нам не нужно делать этого. Docker сохраняет выгружаемые образы локально, поэтому когда вы в следующий раз запускаете некий контейнер на основе того же самого образа, он запускается с почти естественной скоростью. Docker способен кэшировать индивидуальные уровни необходимого образа, делая возможным повторное использование уровней между образами, как мы это вскорости увидим.
Давайте рассмотрим это самостоятельно. Попробуем запустить свою команду повторно.
$ docker run ruby:2.6 ruby -e "puts :hello"
hello
Ух ты. На этот раз намного быстрее - не было вывода о подлежащих выгрузке образах.
Всякий раз когда мы запускаем свою команду docker run
. Docker создаёт некий
новый контейнер для исполнения такой команды. Мы теперь выполнили свой сценарий
Ruby дважды, поэтому у нас было два виртуальных идентичных контейнера для исполнения данного сценария Ruby.
Перечислив запущенные контейнеры мы имеем:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Как вы можете видеть, нет совсем никаких запущенных контейнеров - это потому, что когда наша команда Ruby завершилась, завершился также и исполнявшийся контейнер. Тем не менее, пока мы не скажем ему ничего иного, Docker будет хранить этот остановленный контейнер на тот случай, если мы пожелаем воспользоваться им снова.
Давайте перечислим все контейнеры, включая остановленные, добавив соответствующий
параметр -a
:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
974e2bcb8266 ruby:2.6 "ruby … 1 seco… Exited… dazzling_ba…
7f8d7dddd6b5 ruby:2.6 "ruby … 3 seco… Exited… hungry_heis…
Здесь у нас имеются два контейнера: по одному для каждого нашего запуска соответствующей команды Ruby. Темс не менее, они нам больше не нужны; мы можем удалить их:
$ docker rm <container id> [<container id2> ...]
Идентификатор вашего контейнера может отличаться от моего, поскольку они генерируются случайным образом. Для удаления своих контейнеров я выполнил:
$ docker rm 974e2bcb8266 7f8d7dddd6b5
Тем не менее, вам придётся подставить для выполнения этого идентификаторы своих контейнеров. Сделайте это чтобы вычистить ваши контейнеры.
На будущее, когда мы создаём некий контейнер, который мы делее не будем применять, мы можем воспользоваться соответствующим
параметром -rm
, который сообщит Doccker о необходимости удаления такого контейнера
по его завершению. Модным словечком для контейнером с коротким временем жизни, которые подлежат удалению после того как
они сослужили своё, это эфемерный (throwaway), но я предпочитаю своё слово
одноразовый. Вот как мы выполнили свой сценарий Ruby в неком одноразовом контейнере:
$ docker run --rm ruby:2.6 ruby -e "puts :hello"
Это достаточно распространённый шаблон, и вы будете его наблюдать на протяжении всей нашей книги.
Выполнение некого сценария Ruby это было круто, но что ещё мы можем делать?
Было бы неплохо начать применять Docker для каких- нибудь задач "реального мира"? Давайте представим себе, что мы пожелали бы создать некий новый проект Rails (это не так уж и надуманно... ведь мы помимо всего прочего разработчики Ruby). Можем ли мы это сделать? Ставьте на кон.
Мы намерены пожелать запустить последовательность множества команд в некотором контейнере чтобы сгенерировать соответствующий
проект Rails.Мы могли бы сделать вручную некий на самом деле длинный, корявый
docker run
, который исполняет эти инструкции одну за другой. Тем не менее это будет
трудно воспринимать.
Вместо этого мы можем сделать нечто слегка иное. Мы можем запустить некий контейнер, исполняющий какую- то интерактивную оболочку bash. Сделав это мы буквально получаем некий терминальный сеанс, исполняемый внутри самого контейнера. Отсюда мы можем запускать столько команд сколько пожелаем, во многом аналогично тому как если бы у нас имелся некий локальный сеанс Bash. Это очень полезный трюк чтобы развязать вам руки.
Давайте раскрутим это.
Однако, прежде чем мы начнём, вам понадобится найти некий каталог в своей машине, в котором вы желаете создать файлы своего проекта Rails. Поскольку наши предстоящие команды Docker будут оказывать воздействие на наши локальные файлы, (вскоре мы в точности разберёмся насколько близко), я рекомендую вам создать некую новую пустую папку, в которой вы будете исполнять свои шаги. Например:
$ mkdir ~/docker_for_rails_developers
$ cd ~/docker_for_rails_developers
Всё установлено? Отлично. Теперь мы готовы запустить некую интерактивную оболочку внутри контейнера на основе теперь уже
знакомого нам образа ruby:2.6
:
$ docker run -i -t --rm -v ${PWD}:/usr/src/app ruby:2.6 bash
Как вы можете видеть, мы применяем соответствующий параметр --rm
для
создания одноразового контейнера, который будет удалён когда мы покончим с ним. Тут также имеются и новые параметры
(-i
, -t
и
-v ${PWD}:/usr/src/app
), которые нам не встречались ранее. Через мгновение мы вернёмся
к ним. Теперь же, когда мы запустили эту команду, вас должно приветствовать некое приглашение терминала, которое выглядит
как- то так:
root@0c286e8bda42:/#
Это иное приглашение указывает теперь что мы теперь успешно запустили некую оболочку Bash внутри контейнера.
соответствующие root@
и #
указывают, что мы
являемся пользователем root
- это пользователь по умолчанию внутри некого
контейнера.
Пользуясь данным новым приглашением Bash мы теперь способны вызывать любые команды, которые мы пожелаем исполнить в своём контейнере. Это умоляет о соответствующем вопросе ... а что бы мы хотели исполнить? Помните: мы пытаемся выработать некий новый проект Rails. Итак, для начала давайте перейдём в ту папку, которую мы намерены применять для своего проекта:
root@0c286e8bda42:/# cd /usr/src/app
Теперь давайте установим gem Rails:
root@0c286e8bda42:/usr/src/app# gem install rails
Версии Rails | |
---|---|
Все примеры в этой книге были построены и проверены для Rails 5.2.2 - самой последней версии на момент написания книги. Однако, за исключением случаев в которым мы применяем новые свойства Rails, всё должно работать также и в предыдущих версиях Rails. |
Вы должны обнаружить установленными соответствующий gem Rails и все его зависимости. Это означает, что мы теперь готовы выработать свой новый проект:
root@0c286e8bda42:/usr/src/app# rails new myapp --skip-test --skip-bundle
Мы воспользовались соответствующим параметром --skip-test
чтобы сообщить Rails не
применять его установки по умолчанию Minitest. Это обусловлено тем, что в Главе 7
мы применяем RSpec для демонстрации того как настроить свои проверки в среде Docker.
Мы также применяем параметр --skip-bundle
. Он сообщает Rails не запускать
bundle install
после выработки данного проекта. Наш контейнер это всего лишь
временное средство передвижения для нас по выработке надлежащего проекта Rails - так как мы намерены избавиться от него,
нет накакой необходимости устанавливать все зависимости проекта.
Когда мы запустим свою команду rails new
, мы получим приводимый ниже вывод,
который отобразит создаваемые файлы нашего проекта Rails, в точности как мы и ожидали:
create
create README.md
create Rakefile
create .ruby-version
create config.ru
create .gitignore
create Gemfile
...
create vendor
create vendor/.keep
create storage
create storage/.keep
create tmp/storage
create tmp/storage/.keep
remove config/initializers/cors.rb
remove config/initializers/new_framework_defaults_5_2.rb
Отлично! Мы можем обнаружить созданными свои файлы Rails. Однако, помните, что мы находимся внутри своего контейнера, а нам необходимо получить эти файлы в своей локальной машине. Как нам это сделать?
Прежде всего прекратим свою оболочку Bash, что остановит наш контейнер:
root@0c286e8bda42:/usr/src/app# exit
Это вернёт нас к нашему знакомому приглашению терминала: $
Теперь давайте бросим взгляд вовнутрь своего текущего каталога в своей локальной машине:
$ ls
myapp
$ cd myapp
$ ls
Gemfile Rakefile bin config.ru lib package.json storage
vendor README.md app config db log public
tmp
Уф. Каким- то образом все выработанные внутри нашего контейнера файлы оказались здесь, в нашей локальной файловой системе. Разме контейнер не полностью изолирован? Как это произошло?
Наш ответ состоит в том параметре -v
, который мы проигнорировали в своей
команде docker run
. В стиле речи Docker, это монтирует
некий том - действенно разделяет некую часть нашей локальной файловой системы с используемым контейнером.
В частности, -v ${PWD}:/usr/src/app
просит: "Смонтируйте наш текущий каталог
внутри самого контейнера в /usr/src/app
" (${PWD}
является некоторой переменной среды Unix для указания на значение текущего каталога). Это означает, что все файлы в нашем
локальном каталоге будут видимыми в /usr/src/app
внутри этого контейнера.
Аналогично, если мы создаём, удаляем или изменяем файлы в каталоге этого контейнера, все изменения будут отражаться в нашей
локальной файловой системе.
Здесь монтирование нашего локального тома означает, что тот проект Rails, который вырабатывается внутри данного
контейнера (в /usr/src/app
) остаётся в нашем локальном каталоге, даже после прекращения
данного терминала. Кроме того, это свойство будет полезным при разработке, позволяя нам изменять файлы локально и
получать эти изменения автоматически прикреплёнными внутри нужного контейнера, причём без необходимости перестраивать сам
образ.
Будет не лишним отметить пару ключевых моментов относительно того как работает данное поведение монтирования. Во- первых,
если наш каталог /usr/src/app
ещё не существовал внутри данного каталога, Docker
создаст его. Во- вторых, если этот каталог уже имеется внутри данного контейнера, наш монтируемый каталог перекроет и
маскирует его содержимое пока имеет действие данное монтирование.
Только для пользователей Linux: смена владельца файла | |
---|---|
Вы обратите внимание, что наши файлы вновь создаваемого проекта Rails находятся во владении
Вам придётся делать это всякий раз при выработке файлов внутри некого контейнера. За дополнительными подробностями обратитесь к Дополнение A. Владельцы файлов и полномочия. |
Наконец, мы возвращаемся к брошенным нами параметрам -i
и
-t
. Для их понимания нам вначале придётся разобраться с архитектурой Docker.
Само сердце Docker - механизм (engine) Docker - является неким приложением клиент- сервер. CLI Docker
(а именно, команда docker
) это всего лишь некий тонкий клиент, который общается с
отдельной программой - самим демоном Docker - относительно того что мы просим. Этот демон отвечает за выполнение самого
тяжёлого поднятия в терминах запуска, останова и прочих распоряжений относительно наших контейнеров.
Наш следующий рисунок отображает архитектуру Docker в Linux на верхнем уровне:
Однако Docker строится на основе технологий контейнеризации Linux, которые не доступны естественным путём в Mac и Windows. Docker обходит это устанавливая виртуальную машину Linux с малым весом, которая исполняет необходимого демона Docker. Это приводит к слегка различимой архитектуре Docker for Mac/Windows, что отображено на следующей схеме.
Итак, как нам это поможет пояснит зачем нам требуется применять параметры -i
и -t
?
Процессы Unix имеют три канала ввода/ вывода: стандартный ввод (stdin
),
стандартный вывод (stdout
) и стандартные ошибки
(stderr
). Так как наш демон Docker запущен в некотором отдельном процессе,
Docker будет вынужден предпринимать некие активные действия для продвижения нашего ввода CLI в сам демон Docker.
По умолчанию, тем не менее, docker run
просто передаёт вывод самого контейнера
в нашего клиента. Это отлично когда мы желаем запускать некий контейнер, который не требует никакого ввода. Однако,
порой мы запускаем процессы, которые требуют ввод. Некий интерактивный сеанс Bash это прекрасный пример этого - он
ожидает получения вводимых нами команд. В данном случае нам требуется в явном виде сообщить Docker передавать наш CLI
ввод в имеющегося у нас демона Docker. Мы делаем это при помощи своего параметра
-i
- "i" для input (ввода). Если мы не определим его, наш контейнер
немедленно завершится, из- за Bash - отсутствие получения на ввод - влечёт за собой прекращение исполнения.
Однако только этого недостаточно. Некий интерактивный сеанс Bash должен исполняться внутри некого
эмулятора терминала, который отвечает за таки е моменты как отображение некого
приглашения и интерпретации escape- последовательностей, таких как Ctrl-C
.
Если мы запускаем некий контейнер для исполнения bash
, по умолчанию это
исполнение происходит не в интерактивном режиме, выполняя только предоставляемые команды и завершаясь по их
исполнению. Для достижения длительного времени жизни, интерактивного сеанса Bash внутри некого контейнера Docker,
нам придётся сообщить Docker о необходимости установки для на некого эмулятора терминала (технически, какого- то
псевдотерминала, иди pty), который сядет перед Bash. Мы осуществляем это определяя соответствующий параметр
-i
для docker run
.
Теперь, если всё это звучит слишком сложно, просто запомните что всякий раз когда вам требуется длительное время
жизни при интерактивном сеансе, вам необходимо определять оба параметра, и -i
, и
-t
. На практике мы обычно соединяем их в сокращённый вид
-it
, который, как вы могли догадаться, означает "i" -n- "t" -eractive.
Круто.
И на этом вся наша работа здесь завершена.
Теперь, когда мы впервые вкусили Docker, давайте остановимся на мгновение перевести дух и напомнить что мы изучили.
В этой главе:
-
Мы установили на своей машине Docker.
-
Мы запустили свою самую первую команду Docker, некий сценарий Ruby
helloworld
, причём без необходимости установки Ruby в вашей машине: $ docker run ruby:2.6 ruby -e "puts :hello"
-
Мы увидели как перечислять свои запущенные контейнеры при помощи
docker ps
, а также все контейнеры (включая остановленные) черезdocker ps -a
. -
Мы удалили свои старые контейнеры при помощи
docker rm <container id>
и увидели как создавать одноразовый контейнер при помощи параметраdocker run
--rm
. -
Мы выработали некий новый проект Rails при помощи контейнеров посредством:
-
Запуска некой интерактивной оболочки Bash исполняемым внутри контейнера
$ docker run -i -t --rm -v ${PWD}:/usr/src/app ruby:2.6 bash
-
Установки gem Rails внутри этого контейнера
root@0c286e8bda42:/usr/src/app# gem install rails
-
Применения нашего вновь установленного gem Rails для выработки нашего проекта
root@0c286e8bda42:/usr/src/app# rails new myapp --skip-test \ --skip-bundle
-
Замечательно. Мы на пути к мастерству в Docker. В следующей главе мы узнаем, как запускать наше новое приложение Rails.