Использование Docker для сборки и запуска программ.
Опорные статьи:
https://habr.com/ru/post/253877/
https://tproger.ru/translations/docker-terms/
https://tproger.ru/translations/docker-instuction/
https://xakep.ru/2015/06/01/docker-usage/
https://xakep.ru/2015/06/04/docker-faq/
Официальная документация:
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Для обычного бытового использования Docker интересен тем, что позволяет собирать программы без смешивания их пакетов с системными и запускать изолированно в контейнере. Это особенно важно при использовании дистрибутивов с длительным сроком поддержки (LTS).
Для новой версии программы могут потребоваться более свежие пакеты, чем есть в репозитории дистрибутива. В процессе решения зависимостей можно изрядно насобирать новых пакетов, что нежелательно для стабильности и безопасности. Бывают случаи, что в более новых версиях пакетов убирается устаревший функционал, который при этом используется какой-нибудь программой, входящей в дистрибутив, что закономерно приведёт к проблемам. Такова плата за длительный срок поддержки, увы. Поэтому программы, требующие более новые пакеты, чем есть в репозитории, лучше собирать и использовать из контейнера, в чём Docker очень удобен.
Ещё одним большим плюсом Docker является то, что можно собрать программу и все её зависимости в образ, которым можно легко поделиться с коллегами. При этом совершенно некритично, что у них могут быть другие дистрибутивы с собственной пакетной базой, так как все необходимые зависимости будут в образе.
Основы использования Docker.
Установка:
sudo apt install docker.io
Базовая проверка работоспособности docker:
sudo docker run hello-world
В выводе должно оказаться нечто подобное:
Если демон Docker по какой-то причине не запустился, будет показано предупреждение:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
Запуск можно сделать вручную:
sudo service docker start
Образы.
Образы загружаются из специального хранилища — docker-реестра: https://hub.docker.com/ В нём хранятся публичные и приватные образы. Можно сформировать собственное хранилище образов или экспортировать образ в файл, что позволит поделиться им любым удобным образом.
Если упрощённо, то образ — это шаблон, содержащий в себе программные компоненты, из копий которого создаются контейнеры.
Образы делятся на базовые (Base images) и дочерние (Child images).
Базовые образы — как правило, содержат в себе операционную систему. (Ubuntu, Debian и подобные).
Дочерние образы — зависят от базовых.
Существуют официальные и пользовательские образы. Названия пользовательских начинаются с имени пользователя, загрузившего образ.
Загрузить образ из https://hub.docker.com/:
sudo docker pull <название_образа>
Можно выбрать конкретную версию образа:
sudo docker pull ubuntu:20.04
В данном случае это Ubuntu 20.04.
Поиск образов в реестре. Найти все образы Ubuntu 20.xx:sudo docker search ubuntu-20
Вывести список всех загруженных образов:
sudo docker images
Удалить образ:
sudo docker rmi <IMAGE ID>
Пример:
sudo docker rmi 56def654ec22
Принудительно удалить образ:
sudo docker rmi -f <IMAGE ID>
—f — отфильтровать образы или контейнеры по предоставленному условию. В данном случае по ID образа.
Удалить образы, которые не используются ни одним контейнером:
sudo docker image prune -a -f
Контейнеры.
Контейнеры создаются из образов, тем самым являются исполняемыми экземплярами образов. В них содержится всё, что требуется для запуска конкретных программ. Контейнеры изолированы от основной системы с помощью пространства имён.
Вывести список всех контейнеров:
sudo docker ps -a
Вывести список только запущенных контейнеров:
sudo docker ps --no-trunc
—no-trunc — не обрезать вывод. К примеру, будут полностью отображены ID, вместо их сокращения.
Место хранения контейнеров: /var/lib/docker/
Создание и запуск контейнера осуществляется с помощью команды run.
docker run --help
Создание и запуск контейнера на примере busybox (комплект консольных утилит):
sudo docker run busybox echo "hello world"
Из реестра (хранилища) hub.docker.com будет загружен образ busybox (если его нет на локальной машине), из образа будет создан контейнер, внутри которого будет запущена утилита echo, которая отправит в вывод строку «hello world».
Запустить ранее созданный контейнер:
sudo docker start <ID>
Остановить работу контейнера:
sudo docker stop <ID>
Подключение к запущенному контейнеру:
sudo docker exec -it <ID> sh
Контейнеры можно удалить по имени и по ID. Команда:
sudo docker rm <ID>
Пример:
sudo docker rm 791834eb255d
Принудительное удаление запущенного контейнера:
sudo docker rm -f <ID>
Удалить все остановленные контейнеры:
sudo docker container prune -f
Создание и запуск контейнера с желаемым именем (в примере my-busy) с последующим запуском echo, которая выведет строку «hello world»:
sudo docker run --name my-busy busybox echo "hello world"
Создать и запустить контейнер, а затем удалить его по завершению работы (опция —rm).
sudo docker run --rm busybox echo "hello world"
Полезно для одноразового запуска и быстрого тестирования, чтобы не создавалось нагромождение однотипных контейнеров.
Вывести потребляемые контейнером ресурсы:
sudo docker stats <ID>
В контейнеры можно монтировать каталоги основной системы.
Dockerfile и создание собственных образов.
Официальная документация: https://docs.docker.com/engine/reference/builder/
Это текстовый файл, в котором содержатся инструкции для клиента Docker. Используется для создания собственных образов.
Позволяет указать какой образ будет базовым, какие дополнительные пакеты должны быть доустановлены, какие команды должны быть выполнены при старте контейнера, созданного из образа, и ещё ряд возможностей.
Собственные образы и контейнеры очень удобно применять для сборки программ. Это особенно актуально при использовании дистрибутивов с длительным сроком поддержки (LTS). Ключевым является то, что не придётся смешивать различные зависимости и самосборные пакеты с системными пакетами. Все они будут изолированы в контейнере, что позитивно скажется на безопасности и стабильности системы.
Наполнение Dockerfile.
Пример содержимого Dockerfile:
# базовый образ
FROM ubuntu:20.04
# установка желаемых пакетов и зависимостей
RUN apt-get update
RUN apt-get install -y wget && echo "We installed wget!"
# создание каталога /mytest
WORKDIR /mytest
# создание файла testfile
RUN touch testfile
# ls и echo будут выполняться при запуске контейнера
CMD ls && echo "Show me mytest"
Каждая строка с командами является инструкцией. Инструкции выполняются одна за другой.
Примечание: Docker рекомендует использовать именно apt-get.
Основные инструкции.
FROM — указать имя базового образа, из которого будет создан собственный образ. Базовый образ будет загружен, если его нет на локальной машине. Dockerfile всегда должен начинаться с FROM.
RUN — выполнить команды в окружении образа. Таким образом можно выполнить команды на установку зависимостей. Пакеты зависимостей будут загружены только в пространство образа, то есть не будут смешиваться с пакетами основной системы.
Команды выполняются последовательно, то есть результат первой порции команд, перечисленных в инструкции RUN, не влияет на команды, перечисленные в следующей RUN. Пример: RUN cd /tmp не будет действовать для RUN ls, то есть ls выведет каталоги корня, а не /tmp.
При выполнении инструкции RUN Docker будет сохранять изменения в файловой системе, накладывая новые изменения поверх старых по слоям. Каждая инструкция RUN создаёт слой. Это используется для ускорения создания образов. Те слои, что не изменились, будут использоваться без повторной обработки.
WORKDIR — создать и/или выбрать рабочий каталог внутри образа, в котором будут выполнены последующие команды, перечисленные в инструкциях RUN и CMD. Особенно полезно при сборке программ.
COPY — копировать указанные файлы и каталоги из каталога контекста сборки в создаваемый образ по пути, указанному в инструкции WORKDIR или напрямую.
ENV — объявить переменную окружения, которая будет использоваться в контейнере.
ENTRYPOINT — запускает указанную программу с желаемыми параметрами при старте контейнера. Команды можно записывать в синтаксисе JSON. Обычно указывается в конце Dockerfile. Пример: ENTRYPOINT [«/bin/sh», «-c»]
CMD — указать команды и аргументы к выполнению при запуске контейнера из созданного образа. Может использоваться только одна инструкция этого типа, остальные будут проигнорированы. Обычно указывается в конце Dockerfile.
Прочие инструкции можно посмотреть в официальной документации: https://docs.docker.com/engine/reference/builder/
Пример создания образа.
Команда на создание образа:
sudo docker build -f </путь/до/Dockerfile> -t <имя_создаваемого_образа> </путь/до/контекста>
-f — используется, чтобы указать путь до конкретного Dockerfile. Без этой опции демон Docker будет искать Dockerfile в каталоге выполнения.
-t — имя (тэг) создаваемого образа. В обычном случае создаётся локальный образ с указанным именем, но можно загрузить создаваемый образ в репозиторий, если указать имя пользователя в реестре hub.docker.com, а затем аутентификационные данные. Пример: docker build -t myuser/myimage .
</путь/до/контекста> — это аргумент, указывающий демону контекст сборки, то есть какие файлы и каталоги использовать для сборки образа. Если не планируется добавлять какие-то конкретные файлы и каталоги, то достаточно указать любой пустой каталог. Sending build context to Docker daemon — процесс упаковки и отправки файлов демону для сборки образа. Образ будет собираться в специальном временном каталоге. Указанные файлы будут скопированы, упакованы (tar-архив) и помещены в специальный каталог для последующей сборки. После сборки скопированные файлы будут удалены, а оригиналы останутся без изменений.
Если в качестве контекста указать . (точку), то демон будет искать файлы для образа в том же каталоге, где выполнена команда на сборку. То есть перед сборкой необходимо перейти в каталог контекста сборки (с файлами и каталогами для сборки образа), а только потом выполнить команду на сборку. Иначе демон посчитает за контекст сборки весь домашний каталог активного пользователя.
Перед началом выполнения инструкций из Dockerfile демон Docker проверяет его на корректность и возвращает ошибку, если есть проблемы.
Альтернативный способ создания образа.
Docker позволяет сохранить изменения, которые были сделаны в контейнере, в отдельный образ. То есть можно настроить содержимое контейнера (установить желаемые пакеты и тому подобное), затем сохранить всё в новый образ, из которого можно будет запускать уже настроенный контейнер.
sudo docker commit <ID_контейнера> <имя_нового_образа>
Пример использования можно найти далее в статье.
Поделиться Docker-образом без использования hub.docker.com.
Образ можно специальным образом сохранить в файл, которым можно поделиться любым удобным способом.
sudo docker save --output <имя_файла_образа>
<ID_исходного_образа>
Пример сохранения образа ubuntu в файл-образ с именем my-exp-ubuntu.tar в каталог ~/Documents/.
sudo
docker save --output
~/Documents/
my-exp-ubuntu.tar ubuntu
Чтобы использовать сохранённый образ, его необходимо импортировать:
sudo docker load --input <имя_файла_образа>
sudo docker load --input ~/Downloads/my-exp-ubuntu.tar
Примеры использования Docker.
Монтирование каталогов в контейнер Docker.
Официальная документация:
https://docs.docker.com/storage/volumes/
Docker позволяет монтировать каталоги основной системы в контейнеры, что делает доступным их содержимое для утилит внутри контейнера. К примеру, можно смонтировать в контейнер каталог ~/Video/, чтобы обработать его содержимое с помощью ffmpeg, который находится в контейнере.
Использование на примере busybox.
Создать контейнер из образа busybox, примонтировать каталог ~/Documents/ к контейнеру, запустить утилиту ls внутри контейнера для смонтированного в него каталога, вывести результат и удалить контейнер:
sudo docker run -v ~/Documents/:/mnt/ --rm busybox ls /mnt/
-v — указывает хранилище, которое будет смонтировано в контейнер. Хранилище представляет собой обычный ранее созданный каталог, в котором могут содержаться другие каталоги и файлы. При монтировании контейнер получит доступ к этому каталогу и всему его содержимому. В примере это каталог ~/Documents/.
/mnt — обозначает точку монтирования. В примере это ~/Documents/. Пример пути: /mnt/каталог/файл, что является аналогичным ~/Documents/каталог/файл.
—rm — удалить контейнер после завершения его работы.
Использование интерпретатора sh из контейнера.
sudo docker run --rm -it busybox sh
Образ busybox будет загружен из реестра hub.docker.com (если его нет на локальной машине), из образа busybox будет создан контейнер, внутри которого будет запущена утилита sh, после чего станет доступным ввод команд, которые будут выполняться утилитами busybox внутри контейнера. После завершения работы контейнера он будет удалён (опция —rm).
-i — интерактивный режим.
-t — проброс терминала в контейнер.
Благодаря опции -it будет выделен псевдо-tty, работающий в интерактивном режиме, а не только на вывод, что позволит выполнять команды внутри контейнера через терминал. Без опции -it утилита sh будет запущена единожды, отобразится вывод и работа контейнера завершится.
Команда ls покажет набор каталогов от корня внутри контейнера:
Пример перехода в каталог /bin и выполнение команды ls:
На иллюстрации показано всё многообразие утилит из комплекта busybox.
Для завершения работы этого контейнера необходимо ввести exit
.
Использование браузера elinks из контейнера.
Ниже рассмотрен пример установки в контейнер текстового браузера elinks и сохранения изменений в образ.
На базе образа ubuntu будет создан контейнер с именем —name my-base-ubuntu:
sudo docker run -it --name my-base-ubuntu ubuntu sh
При этом с помощью опции -it пробрасывается терминал в интерактивной сессии и запускается утилита sh.
Теперь можно выполнять команды непосредственно внутри контейнера.
Установка браузера elinks:
apt-get update
apt-get install elinks
После завершения установки пакетов можно завершить сессию:
exit
Проверяем наличие контейнера в списке контейнеров:
sudo docker ps -a
Теперь предстоит сохранение изменений, которые были сделаны в контейнере (установлены пакеты для браузера elinks), в образ:
sudo docker commit my-base-ubuntu my-ubuntu-elinks
Сначала указывается имя контейнера (можно указать ID), а потом имя для образа, который будет создан. В данном случае будет создан образ с именем my-ubuntu-elinks.
Проверим наличие образа. Вывести список всех доступных образов:
sudo docker images
Теперь можно удалить контейнер, из которого был создан образ:
sudo docker rm my-base-ubuntu
Создание контейнера из свежесозданного образа и запуск браузера в интерактивной сессии проброшенного терминала:
sudo docker run -it --rm my-ubuntu-elinks elinks
Результат:
Благодаря опции —rm, контейнер будет удалён после завершения работы.
Использование на примере Cmake-Converter.
Официальная документация: https://cmakeconverter.readthedocs.io/en/latest/intro.html
Программа предназначена для автоматического преобразования солюшена (.sln) Visual Studio в CmakeLists.txt. Программа написана на Python и доступна из репозитория посредством пакетного менеджера pip.
Установка и использование очень просты, что очень удобно для освоения Docker.
Настройка Dockerfile.
FROM python
RUN pip install cmake_converter
# Программа располагается здесь: /usr/local/bin/cmake-converter
WORKDIR /usr/local/bin
Для примера Dockerfile будет сохранён по следующему пути: ~/Documents/docker/cmake-converter/
Создание образа.
Перейти в каталог с Dockerfile. Пример:
cd ~/Documents/docker/cmake-converter/
Создать образ с именем my-cmake-converter:
sudo docker build -t my-cmake-converter .
Точка в конце указывает на то, что контекст сборки располагается в том же каталоге, где выполняется команда на сборку. В данном случае это место хранения Dockerfile: ~/Documents/docker/cmake-converter/
Запуск контейнера и использование cmake-converter.
Примечание: В данном примере sln-файл находится в ~/Documents/.
Запустить контейнер my-cmake-converter, смонтировать каталог ~/Documents/ в каталог /mnt/ внутри контейнера, выполнить программу cmake-converter и указать ей путь до sln-файла, который нужно преобразовать в CMakeLists.txt:
sudo docker run -v ~/Documents/:/mnt/ --rm my-cmake-converter cmake-converter -s /mnt/my-project.sln
Готово.
Использование на примере утилиты untrunc.
Официальный репозиторий: https://github.com/ponchio/untrunc
Утилита предназначена для исправления следующей проблемы: moov atom not found audio.mp4: Invalid data found when processing input.
То есть позволяет исправить битое видео в формате mov. К примеру, видео может побиться, если некорректно завершить процесс записи. Утилита позволяет подсунуть moov атом из нормального видео в битое, тем самым решая проблему.
Docker-файл: https://github.com/ponchio/untrunc/blob/master/Dockerfile
Открыть в терминале желаемый каталог, куда планируется загрузить Dockerfile. Пример:
cd ~/Documents/docker/untrunc/
Загрузить файл:
wget https://github.com/ponchio/untrunc/blob/master/Dockerfile
В этой же сессии терминала выполнить команду на создание образа:
sudo docker build -t untrunc .
Создание контейнера и запуск утилиты:
sudo docker run -v ~/Video/:/mnt/ --rm untrunc /mnt/video_good.mp4 /mnt/video_bad.mp4
-v — указать каталог, который следует смонтировать в контейнер. Контейнер получит доступ ко всему содержимому каталога. В данном примере монтируется каталог ~/Video/ в каталог /mnt/ внутри контейнера.
—rm — удалить контейнер после завершения выполнения команды. В данном случае после завершения обработки видео.
untrunc — непосредственно сама утилита, находящаяся в контейнере. Сначала указывается видео-донор, в котором всё в порядке с moov атомом, а следом указывается видео с повреждённым.
Если всё правильно, то пойдёт процесс обработки, который зависит от объёма видео. Обработка видео 1 Гб занимает до 10 минут. Если moov атом не подходит, то будет указана ошибка.
Восстановление видео не гарантировано. Может восстановиться только звук, а видеоряд будет представлен набором графических артефактов.
Использование утилиты alien.
Утилита применяется для конвертации rpm-пакетов в deb-пакеты. Она требует довольно много зависимостей, которые не хочется тащить в систему. Поэтому сделаем собственный Docker-образ на базе Ubuntu.
Настройка Dockerfile.
FROM ubuntu
# настройка часовых поясов для tzdata
ENV TZ=Europe/Moscow
RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime
RUN apt-get update
# установка с автоматическим подтверждением в диалогах
RUN apt-get install -y alien
# выполнить /usr/bin/alien при старте контейнера
ENTRYPOINT ["/usr/bin/alien"]
В данном примере Dockerfile будет сохранён в ~/Documents/docker/alien/.
Создание образа.
Перейти в каталог с Dockerfile:
cd ~/Documents/docker/alien/
Создать образ с именем alien:
sudo docker build -t alien .
Создание контейнера и запуск утилиты:
sudo docker run -v ~/Downloads/:/mnt/ --rm alien /mnt/пакет.rpm
В данном примере rpm-пакет находится в каталоге ~/Downloads/, который был смонтирован в каталог /mnt/ внутри контейнера.
deb-пакет будет создан с тем же именем, что и исходный rpm-пакет.