Docker
Dockerfile
Одна из важнейшей характеристик образа — это его размер. Компактный образ быстрее скачается с удаленного репозитория, займет меньше места, и ваш сервис быстрее стартует. Любой образ строится на основании базового образа, и рекомендуется выбирать наиболее минималистичный вариант. Хорошим вариантом является Alpine — полноценный дистрибутив Linux с минимумом пакетов.
Для начала попробуем написать Dockerfile «в лоб» (сразу скажу, что это плохой способ, не делайте так):
Здесь мы используем базовый образ на основе Alpine с уже установленным JDK для сборки нашего проекта. Командой ADD мы добавляем в образ текущую директорию src, отмечаем ее рабочей (WORKDIR) и запускаем сборку. Команда EXPOSE 8080 сигнализирует докеру, что приложение в контейнере будет использовать его порт 8080 (это не сделает приложение доступным извне, но позволит обратиться к приложению, например, из другого контейнера в той же сети докера).
Чтобы упаковать сервисы в образы надо выполнить команды из корня каждого проекта:
В результате получаем образ размером в 456 Мбайт (из них базовый образ JDK 340 занял Мбайт). И все притом, что классов в нашем проекте по пальцем пересчитать. Чтобы уменьшить размер нашего образа:
- Используем многошаговую сборку. На первом шаге соберем проект, на втором установим JRE, а третим шагом скопируем все это в новый чистый Alpine образ. Итого в финальном образе окажутся только необходимые компоненты.
- Воспользуемся модуляризацией java. Начиная с Java 9, можно с помощью инструмента jlink создать JRE только из нужных модулей
Для любознательных, вот хорошая статья про подходы уменьшения размеров образа https://habr.com/ru/company/ruvds/blog/485650/.
Итоговый Dockerfile:
Пересоздаем образ, и он в итоге похудел в 6 раз, составив 77 МБайт. Неплохо. После, готовые образы можно загрузить в реестр образов, чтобы ваши образы были доступны для скачивания из интернета.
Совместный запуск сервисов в Docker
Для начала наши сервисы должны быть в одной сети. В докере существует несколько типов сетей, и мы используем самый примитивный из них — bridge, позволяющий объединять в сеть контейнеры, запущенные на одном хосте. Создадим сеть следующей командой:
Далее запустим контейнер бекенда с именем ‘backend’ с образом microservices-backend:1.0.0:
Запускаем шлюз:
В этой команде мы указываем, что мы пробрасываем 80 порт нашего хоста на 8080 порт контейнера. Опции env мы используем для установки переменных среды, которые автоматически будут вычитаны спрингом и переопределят свойства из application.properties.
Резервное копирование и восстановление контейнера
Созданный нами контейнер можно сохранить в виде архива и, при необходимости, перенести на другой сервер или оставить как бэкап.
Создание резерва
И так, для создания резервной копии контейнера, смотрим их список:
docker ps -a
… и для нужного выполняем команду:
docker save -o /backup/docker/container.tar <container image>
* в данном примере мы создаем архив контейнера <container image> в файл /backup/docker/container.tar.
Чтобы уменьшить размер, занимаемый созданным файлом, раархивиркем его командой:
gzip /backup/docker/container.tar
* в итоге, мы получим файл container.tar.gz.
Восстановление
Сначала распаковываем архив:
gunzip container.tar.gz
После восстанавливаем контейнер:
docker load -i container.tar
Смотрим, что нужный нам контейнер появился:
docker images
Удаленный рабочий стол
Такой вариант выглядит более накладным, так как требует установки дополнительного софта — как в контейнере, так и на локальном компьютере. Но у него есть и преимущества, например:
- шифрование траффика
- оптимизация передачи данных по сети
- мультиплатформенность
- а также возможность запуска полноценного графического окружения
В контейнере кроме x2go-сервера также понадобится ssh-сервер. Поэтому код будет во многом похож на приведенный в примере №2. В той части, где ставится плеер, openssh-сервер, и сервер pulseaudio. То есть останется только добавить x2go-сервер и openbox:
Dockerfile:
Также немного изменим скрипт entrypoint.sh. Нам теперь нет необходимости настраивать переменную окружения PULSE_SERVER, поэтому от этого кода можно избавиться. Кроме того, пользователя для подключения следует добавить в группу x2gouser, иначе он не сможет запустить x2go-сессию:
Соберем образ:
Запустим контейнер в режиме демона:
Теперь, когда контейнер работает, к нему можно подключиться с помощью x2goclient, как мы подключались бы к любой удаленной машине. В настройках подключения в качестве хоста следует указать адрес либо самого контейнера, либо docker-хоста (в этом случае стоит также учесть нестандартный порт подключения ssh). Узнать логин и пароль для авторизации можно с помощью команды , как показано в примере №2. Для запуска openbox-сесcии в настройках «Session type» следует выбрать «Custom desktop» и в поле «Command» прописать «openbox-session».
После подключения можно запустить плеер через меню openbox (правый клик мышью) и проверить его работоспособность:
При желании можно добиться и более опрятного вида:
Но это уже совсем другая история.
Докер: контейнер или платформа?
Коротко: и то и другое
Подробный ответ:
Когда Docker начал использовать LXC в качестве среды выполнения контейнера, идея заключалась в том, чтобы создать API для управления средой выполнения контейнера, изолировать отдельные процессы, выполняющие приложения, и контролировать жизненный цикл контейнера и ресурсы, которые он использует. В начале 2013 года проект Docker должен был создать «стандартный контейнер», как мы можем видеть в этом манифесте.
Манифест стандартного контейнера был удален.
Docker начал создавать монолитное приложение с множеством функций — от запуска облачных серверов до создания и запуска образов / контейнеров. Docker использовал libcontainer для взаимодействия с такими средствами ядра Linux, как Control Groups и Namespaces.
Давайте создадим контейнер с использованием СGroups и Namespaces
В этом примере я использую Ubuntu, но это должно работать для большинства дистрибутивов. Начните с установки CGroup Tools and утилиты stress, поскольку мы собираемся выполнить некоторые стресс-тесты.
Эта команда создаст новый контекст исполнения:
Команда «unshare» разъединяет части контекста исполнения процесса
Теперь, используя cgcreate, мы можем создать группы управления и определить два контроллера: один в памяти, а другой — в процессоре.
Следующим шагом будет определение лимита памяти и его активация:
Теперь давайте запустим stress для изолированного namespace, которое мы создали с ограничениями памяти.
Мы можем заметить, что выполнение не удалось, значит ограничение памяти работает. Если мы сделаем то же самое на хост-машине, тест завершится без ошибки, если у вас действительно достаточно свободной памяти:
Выполнение этих шагов поможет понять, как средства Linux, такие как CGroups и другие функции управления ресурсами, могут создавать изолированные среды в системах Linux и управлять ими.
Интерфейс libcontainer взаимодействует с этими средствами для управления контейнерами Docker и их запуска.
Контейнеры или протокол без сохранения состояния?
Я упомянул выше, что каждый контейнер изолирован и не сохраняет состояние. Это означает, что после удаления контейнера его содержимое будет удалено навсегда.
Как вы сохраняете данные в таком случае?
Вы когда-нибудь слышали о томах? Тома позволяют сопоставлять каталоги на главной машине с каталогами внутри контейнера.
При создании нового контейнера добавьте флаг , чтобы указать, какой том создать. Эта команда привяжет текущий рабочий каталог на компьютере к директории внутри контейнера.
После запуска контейнера с помощью команды можно отредактировать код на главной машине и посмотреть изменения в контейнере. Теперь вы можете сохранять данные для различных вариантов использования – от хранения изображений до хранения файлов базы данных – и, конечно, для разработки, где нужны возможности живой перезагрузки.
Примечание. Можно запускать команды create и start одновременно с командой run.
Примечание. Единственным дополнением является флаг -d, который указывает контейнеру работать отдельно, в фоновом режиме.
Этап 2. «Мы тоже так хотим»
Когда вы все это развернете, то, конечно же, захотите похвастаться перед коллегами: «Смотрите, как у меня все работает!»
-
Конечно же, они скорее всего захотят свои проекты потестировать. Ну или у вас там не один проект, вы еще какие-то другие проекты захотите запускать. Получается, вам вашу схему надо отмасштабировать.
-
Поскольку у вас, скорее всего, по-прежнему останется один сервер, а проектов будет много, у вас начнут появляться очереди на сборку. То есть вы запускаете задание, оно сколько-то выполняется, а за ним следующее-следующее-следующее, и т.д.
-
Чтобы разгрузить эти очереди, вы будете заводить несколько агентов на сборку – то есть машины, на которых вы будете выполнять задания. Мы пробовали разные схемы – поднимали виртуальные машины, брали просто несколько компьютеров, в частности, запускали прямо на компьютерах разработчиков. Последний более-менее рабочий вариант у нас был – мы завели мультилогон на одном из серверов и просто в нескольких сессиях запускали тесты. В общем, это не очень хорошая история, не советую на ней долго останавливаться.
-
Вам придется идти на какой-то компромисс по окружению, а поскольку вы уже будете запускаться не один, то вам надо договориться – какое же программное обеспечение у вас будет стоять, на каких платформах вы будете тестироваться.
-
Ну и начинать потихонечку добавлять метрики и мониторинг, чтобы вся эта ваша система не упала.
Возможности Docker Desktop
Существует множество преимуществ:
- Поддерживает широкий спектр инструментов разработки.
- Обеспечьте быстрый и оптимизированный способ создания и публикации контейнерного образа на любой облачной платформе.
- Простота установки и настройки полной среды Docker
- Повышение производительности благодаря встроенной виртуализации Hyper-V для Windows и HyperKit для MAC.
- Возможность работать в Linux через WSL 2 на компьютерах с Windows.
- Легкий доступ к работающим контейнерам в локальной сети.
- Возможность поделиться любым приложением на облачной платформе, на разных языках и в разных средах.
- Для обеспечения безопасности и актуальности выполняются автоматические обновления.
- Включены последние версии Kubernetes.
- Возможность переключения между Linux и Windows сервером на Windows.
Однако
Также рекомендуем прочитать:
- Docker для начинающих — технология контейнеров
- В чем разница между Docker и Kubernetes?
- Введение в Docker Hub и все, что вы должны знать о нем
- Как установить Docker на Ubuntu, Windows, Debian и CentOS?
- Kubernetes — Введение для начинающих
- Docker посмотреть запущенные контейнеры, запустить или остановить контейнеры
Исследуем Docker-образ с помощью Dive
Даже после сборки с соблюдением всех правил и советов нужно исследовать образ на возможность дополнительных улучшений.
Dive — прекрасный инструмент на основе командной строки для исследования образа, содержимого слоев и поиска способов уменьшить размер Docker/OCI-образа. У него на GitHub больше 24 тыс. звезд. К тому же он очень прост в использовании.
У Dive есть две очень полезные метрики:
- возможная трата места на диске,
- оценка эффективности образа.
Но его лучшая возможность — интеграция с любым CI-инструментом. Для обеих метрик можно указать , и если оно не выполняется, то и CI-задача тоже не выполняется. Поэтому мы всегда можем доверять Docker-образу, созданному с помощью CI-задачи.
Этап 1. «А как это все устроено?»
Когда вы почитаете статьи в интернете, пообщаетесь с коллегами в чатах и узнаете, что 1С можно тестировать, а еще можно тестировать автоматически, а еще можно тестировать часто – вы, скорее всего:
-
поставите себе Jenkins на свою машину или на какой-нибудь сервер;
-
выберете какой-нибудь простой проект и запустите на нем тесты;
-
запускать, скорее всего, будете на одной машине – на своей или опять же на этом же сервере;
-
все окружение настроите руками – поставите нужную вам платформу, какие-то нужные вам дополнительные библиотеки;
-
ну и через какое-то время все это начнет работать как часы.
Что же происходит после этого этапа?
Режим пользователя
В режиме пользователя код всегда выполняется в отдельном процессе (пользовательском пространстве), у которого есть свой собственный набор областей памяти (собственное виртуальное адресное пространство). Так как виртуальное адресное пространство каждого приложения является собственным, одно приложение не может изменить данные, принадлежащие другому приложению. Каждое приложение выполняется в изоляции, и если приложение падает, то падение ограничено только этим приложением. В дополнение к тому, что виртуальное адресное пространство является собственным, в режиме пользователя оно ограничено. Процессор, работающий в режиме пользователя, не может получить доступ к виртуальным адресам, зарезервированным для операционной системы. Ограничение виртуального адресного пространства приложения в режиме пользователя не позволяет ему изменять, и, возможно, повреждать, критические данные операционной системы.
Переопределение конфигурации
Учитывая различия и то, что ваши зависимости могут отличаться в сценариях
разработки и продакшена, ясно, что нам потребуются разные конфигурационные файлы.
Docker compose поддерживает объединение различных compose-файлов для
получения окончательной конфигурации. Как это работает можно увидеть на примере:
Как было сказано, docker compose поддерживает объединение нескольких compose-
файлов, это позволяет переопределять различные параметры во втором файле. Например:
Такой синтаксис не очень удобен в процессе разработки, когда команду
понадобится выполнять множество раз.
К счастью, docker compose автоматически ищет специальный файл с именемdocker-compose.override.yml для переопределения значений docker-compose.yml. Если
переименовать второй файл, то получится тот же результат, только с помощью изначальной команды:
Хорошо, так запомнить проще.
Интерполяция переменных
Файлы конфигурации поддерживают и значения по умолчанию. То есть вы можете сделать следующее:
И если вы выполняете docker-compose build (или push) без переменной окружения$MY_SERVICE_VERSION, будет использовано значение latest, но если вы установите
значение переменной окружения до сборки, оно будет использовано при сборке или пуше
в регистр private.registry.mine.
Сборка Docker-образа для любого проекта на Python (CPU)
Чаще всего системы машинного обучения делают на Python, поэтому важно эффективно создавать любые Docker-образы на основе этого языка
Одноэтапная сборка
Во время одного общего процесса сборки будут выполнены все задачи. Последовательность действий: выберите базовый образ, установите пакеты ОС, скопируйте исходники, установите пакеты, задайте точку входа (если нужно) или другие команды.
Пример одноэтапной сборки Docker:
Для демонстрации я использовал эти пакеты:
После запуска команды размер образа был 1,64 Гб:
Одноэтапная сборка очень проста и подходит для многих сценариев. Это нормальная практика, но у нее есть фундаментальные недостатки, особенно с точки зрения проектов на Python
Важно c apt использовать флаг , а с pip — флаг. Нам не надо сохранять кэш, потому что он не нужен ни для среды разработки, ни для эксплуатационной среды
Если вы используете какую-нибудь CI/CD-платформу (вроде Github action) с ограниченным размером хранилища, то она будет работать только при таком методе.
Библиотеки Python из коробки не работают, сначала их нужно скомпилировать на С. Нас интересует лишь скомпилированная часть библиотек, остальное не нужно. При выполнении все библиотеки сначала скачиваются, а затем компилируются.
Нужно удалить все промежуточные и дополнительные компоненты, созданные при установке библиотек. Для этого можно использовать bash-команды. Если сделать неправильно, то будет много неприятностей или даже сломается библиотека. Это довольно сложно, многие стараются этого избежать и пускают в эксплуатацию более громоздкие образы. Но нам на помощь приходит многоэтапная сборка образа (Docker multi-stage builds).
Многоэтапная сборка
Это один из самых эффективных методов оптимизации, сохраняющий удобство чтения и сопровождения образов. Чтобы создать действительно эффективный Dockerfile, нужно применять ухищрения с оболочкой и прочую логику, чтобы уровни оставались как можно меньше и чтобы на каждом уровне были только те артефакты, которые нужны ему от предыдущего уровня.
При многоэтапной сборке вы применяете в Dockerfile выражения . Каждая такая инструкция может использовать другую основу, и каждая начинает новый этап сборки. Вы можете выборочно копировать артефакты из одного этапа в другой, отбрасывая все, что вам не нужно в конечном образе.
Давайте посмотрим на примере:
При таком способе размер Docker-образа стал 1,61 Гб вместо 1,64. Вроде бы разница невелика, но на самом деле отличий много. Пробежимся по ним.
Строки с 1 по 5 относятся к первому этапу — компилированию, когда мы устанавливаем библиотеки Python: они сначала скачиваются, а затем компилируются на С, поэтому мы даже установили gcc. Затем мы просто копируем скомпилированные библиотеки из первого этапа во второй (runtime) с помощью этой команды:
Но, как видно на скриншоте, размер уменьшился не сильно. С другими языками разница будет огромной, но у Python есть несколько фокусов в запасе:
-
Сейчас многие библиотеки распространяются в виде предварительно скомпилированных .whl-файлов, это формат wheel из PyPi, поэтому компилировать их не требуется.
-
То есть многоэтапной сборке негде развернуться в случае с проектами на Python? Совершенно верно! Однако не каждый пакет из PyPi предварительно скомпилирован в .whl, многие поставляются в устаревшем формате tar.gz (сжатые с помощью tarballs), и их нужно сначала скомпилировать. Здесь многоэтапная сборка будет работать по-своему.
-
Кроме того, многоэтапность применима, если вы собираете Python-пакет из исходника, либо с помощью setup.py используете локальный пакет, поскольку опять же их нужно компилировать.
-
Настаиваю, чтобы вы прочитали эту статью, в которой объясняется формат wheels в Python.
-
В файле req.txt, который я использовал для демонстрации, только вышеприведенные пакеты представлены не в формате wheel, а также они очень малы. Но если какой-то пакет не скомпилирован предварительно и занимает много места, то вы потеряете много места на диске.
Вступление
The future of Linux Containers
20 марта 2013 года на конференции PyCon 2013, Соломон Хайкс (CEO компании dotCloud) выступил с пятиминутной презентацией The future of Linux Containers. В ней широкой общественности впервые была представлена внутренняя разработка компании dotCloud под названием Docker, а спустя несколько дней ее исходный код был выложен в открытый доступ. Хотя технологии LXC и Aufs, на которых была основана первая версия Docker существовали и активно использовались уже порядка пяти лет, но именно появление Docker послужило началом стремительного роста и эволюции систем контейнеризации, что кардинально преобразило многие процессы разработки и деплоймента программного обеспечения.
Этой статьей я собираюсь начать небольшой цикл посвященный изучению развития исходного кода Docker на протяжении нескольких лет. В первой части мы посмотрим на то, что представлял собой код на момент создания git репозитория датированного январем 2013 года. Тогда исходный код Docker (за исключением тестов) состоял всего из шести файлов общим объемом ~600 строк кода, написанных на языке Go. Это больше походило на библиотеку/api, функционал которой состоял лишь в создании, удалении, запуске и остановке контейнеров. Мы разберем принцип работы и даже сможем запустить с ее помощью подготовленный контейнер.
Во второй части мы перенесемся на несколько месяцев вперед — в март 2013, когда докер был представлен на конференции PyCon. На тот момент он уже обладал практически всеми знакомыми нам функциями. А в третьей части я постараюсь рассмотреть переход Docker с LXC на собственную разработку, произошедший годом позже.
Open Containers Initiative
Как мы видели, Docker пожертвовал RunC Open Container Initiative (OCI), но что это?
OCI — это открытая структура, запущенная в 2015 году Docker, CoreOS и другими лидерами контейнерной индустрии.
Open Container Initiative (OCI) направлена на установление общих стандартов для контейнеров, чтобы избежать потенциальной фрагментации и разделения внутри экосистемы контейнеров.
Он содержит две спецификации:
-
runtime-spec: спецификация исполнения
-
image-spec: спецификация образов
Контейнер, использующий другую среду исполнения, можно использовать с Docker API. Контейнер, созданный с помощью Docker, должен работать с любым другим движком.
На этом статья заканчивается.
Монтирование устройств
Один из самых простых способов заставить контейнер говорить и показывать — это дать ему доступ к нашему экрану и звуковым устройствам.
Приставка «unix» к DISPLAY здесь для явного указания использования unix-сокетов, но чаще всего в этом нет необходимости.
Ошибка авторизации
При запуске можно столкнуться с ошибкой вида:
No protocol specified Error: cannot open display: unix:0.0
Или ограничиться разрешением только для root-пользователя:
В большинстве случаев этого должно быть достаточно.
А что со звуком?
Подключение звуковых устройств также не составляет большого труда. В версиях Docker до 1.2 их можно смонтировать с помощью параметра , при этом контейнер должен быть запущен в привилегированном режиме:
В Docker 1.2 был добавлен специальный параметр для подключения устройств. К сожалению, на данный момент (версия 1.2), в качестве значения может принимать только одно устройство за раз, а значит придется явно перечислять их все. Например:
В итоге
Суммируя, команда для запуска контейнера с графическим приложением выглядит примерно так:
Или, для Docker версии ниже 1.2:
Dockerfile:
Соберем образ:
Теперь можно запустить его и послушать, например, радио (если решите попробовать — учтите, что плеер запустится на полной громкости):
Результатом должен стать работающий плеер
О докере
О микросервисной архитектуре слышали практически все. Сам концепт разбития приложения на части не сказать чтобы новый. Но, новое – это хорошо забытое и переработанное старое.
Если постараться рассказать об архитектуре в нескольких словах, то веб приложение разбивается на отдельные унитарные части — сервисы. Сервисы не взаимодействуют между собой напрямую и не имеют общих баз данных. Это делается для возможности изменять каждый сервис без последствий для других. Сервисы упаковываются в контейнеры. Среди контейнеров правит балом Docker.
Для того, чтобы описать что такое Docker очень часто упрощенно используют термин «виртуальная машина». Сходство определенно есть, но говорить так неправильно. Проще всего это различие понять, посмотрев на следующие изображения с официальной документации докера:
Контейнеры используют ядро текущей операционной системы и делят его между собой. В то время как виртуальные машины с помощью hypervisor используют аппаратные ресурсы.
Образ/Image докера это read-only объект, который, по сути, хранит в себе шаблон для построения контейнера. Контейнер — это среда в которой выполняется код. Образы хранятся в репозиториях. Например, официальный репозиторий Docker Hub позволяет хранить только один образ приватно. Впрочем, это бесплатно, поэтому даже за это нужно их поблагодарить.
INFO
Докер не является единственным представителем контейнеризации. Кроме его существуют и другие технологии. Например:
rkt (произносится как ‘рокет’) от CoreOS
LXD (произносится как ‘лексди’) от Ubuntu
Windows Containers — ни за что не угадаете от кого.
Теперь, когда мы ознакомились с теорией, давайте перейдем к практике.
Установку докера особого смысла разбирать нет, ведь его можно установить на множество операционных систем. Укажу только, что скачать его под свою платформу можно из Docker Store. Если вы устанавливаете Docker под Windows, то необходимо чтобы в BIOS и в ОС была включена виртуализация. О том как включить ее в 10-ке можно прочитать в следующем артикуле: Установка Hyper-V вWindows10
Watchtower — автоматическое обновление Docker-контейнеров
Watchtower мониторит выполняющиеся контейнеры и отслеживает изменения в образах, на основе которых они были созданы. Если образ изменился, Watchtower автоматически перезапускает контейнер, используя новый образ. Это удобно при локальной разработке, если есть желание работать с самыми новыми версиями используемых инструментов.
Утилита Watchtower также поставляется в виде Docker-образа и выполняется в контейнере. Для ее запуска введите следующую команду:
Мы запустили Watchtower с примонтированным файлом /var/run/docker.sock. Это нужно для того, чтобы Watchtower мог взаимодействовать с демоном Docker через соответствующий API. Мы также передали опцию interval, равную 30 секундам, которая определяет интервал опроса. У Watchtower есть и другие опции, которые описаны в .
Давайте запустим контейнер и помониторим его с помощью Watchtower.
Теперь Watchtower начнет мониторинг контейнера friendlyhello. Если я размещу новый образ на Docker Hub, Watchtower во время очередного запуска это обнаружит. Затем она корректно остановит контейнер и перезапустит его из нового образа. Также контейнеру будут переданы указанные нами в команде опции, то есть он будет запущен с -p 4000:80.
По умолчанию Watchtower будет искать новые версии образов в реестре Docker Hub. При этом Watchtower может опрашивать закрытые реестры, используя для входа учетные данные из переменных окружения REPO_USER и REPO_PASS.
Более подробную информацию о Watchtower вы можете найти в документации.
Создание Dockerfile
Наше приложение подготовлено, настало время для Docker. Начнем с создания . Это простой текстовый файл, в котором содержатся инструкции по созданию образа для приложения. Его используют для установки зависимостей, задания переменных окружения по умолчанию, копирования кода в контейнер и т. д.
Для экономии дискового пространства я предпочитаю использовать базовый образ alpine-linux Ruby. Alpine linux — крошечный linux-дистрибутив, идеально подходящий для использования в контейнерах. В Docker доступен базовый образ , которым мы и воспользуемся.
Начнем с создания простого , который необходимо поместить в корневую директорию приложения.
Если вы воспользовались приведенным выше шаблоном, то редактировать файл не нужно.
А что если я не хочу использовать PostgreSQL?
Если вы используете другую СУБД (например, MySQL), то для установки соответствующих пакетов потребуется внести изменения в Dockerfile.
Произвести поиск необходимых пакетов можно с помощью следующей команды Docker:
Поскольку Dockerfile уже готов, пора запустить сборку Docker-образа для нашего приложения:
Собираем образ
Образ готов, можно начинать! Запустите контейнер следующей командой:
Мы передали команде несколько аргументов:
- — на самом деле это 2 аргумента, которые позволяют взаимодействовать с контейнером с помощью командной оболочки (например, чтобы передать комбинацию клавиш Ctrl+C);
- — позволяет передать контейнеру переменные окружения. Здесь они используются для установки параметров подключения к базе данных;
- — говорит докеру удалить контейнер после завершения его работы (например, после нажатия Ctrl+C);
- — пробрасывает порт 3000 контейнера на порт 3000 хоста. Таким образом у нас появляется возможность подключиться к сервису так, как будто он запущен напрямую на хосте (например, );
- — говорит докеру подмонтировать в контейнер текущую директорию хоста. Таким образом вы получаете возможность редактировать код на хосте, но при этом он будет доступен в контейнере. Без этого вам пришлось бы после каждого изменения кода заново создавать контейнер.
Что такое Docker Desktop?
Docker Desktop — это собственное настольное приложение, разработанное Docker для пользователей Windows и MAC. Это самый простой способ запуска, сборки, отладки и тестирования приложений Dockerized.
Docker Desktop предлагает важные и наиболее полезные функции, такие как быстрые циклы редактирования, уведомления об изменениях файлов, встроенная поддержка корпоративной сети и гибкость для работы с собственным выбором прокси и VPN.
Docker Desktop состоит из инструментов для разработчика, приложения Docker, Kubernetes и синхронизации версий. Он позволяет вам создавать сертифицированные образы и шаблоны языков и инструментов.
Прежде чем перейти к процессу установки, давайте разберемся с его версиями.
«Если runC и Containerd являются средами исполнения, какого черта мы используем оба для запуска одного контейнера?»
Это, наверное, один из самых частых вопросов. Поняв, почему Docker разбил свою архитектуру на runC и Containerd, вы понимаете, что оба являются средами исполнения.
Если вы следили за историей с самого начала, вы, вероятно, заметили использование сред исполнения высокого и низкого уровня. В этом практическая разница между ними.
Обе они могут называться средами исполнения, но каждая среда исполнения имеет разные цели и функции. Чтобы сохранить стандартизацию экосистемы контейнеров, среда исполнения низкоуровневых контейнеров позволяет запускать только контейнеры.
Среда исполнения низкого уровня (например, runC) должна быть легкой, быстрой и не конфликтовать с другими более высокими уровнями управления контейнерами. Когда вы создаете контейнер Docker, он фактически управляет двумя средами исполнения containerd и runC.
Вы можете найти множество сред исполнения контейнеров, некоторые из них стандартизированы OCI, а другие нет, некоторые являются средами исполнения низкого уровня, а другие представляют собой нечто большее и реализуют уровень инструментов для управления жизненным циклом контейнеров и многое другое:
-
передача и хранение образов,
-
завершение и наблюдение за контейнерами,
-
низкоуровневое хранилище,
-
сетевые настройки,
-
и т.п.
Мы можем добавить новую среду исполнения с помощью Docker, выполнив:
Например: