Как запустить контейнеры Podman под Systemd с помощью Quadlet
Quadlet — это бесплатный инструмент с открытым исходным кодом, написанный на C, который позволяет нам создавать и запускать контейнеры Podman под Systemd. Этот инструмент позволяет нам объявлять контейнеры, тома, сети и их отношения, используя специальные модули Systemd.
В этом руководстве мы узнаем, как использовать Quadlet для создания контейнеров, сетей и томов Podman, а также как создавать стеки из нескольких контейнеров.
В этом уроке вы узнаете:
- Как создавать контейнеры, тома и сети с соответствующими модулями Systemd
- Как создать стеки из нескольких контейнеров с помощью Quadlet
Базовый пример: создание контейнера MariaDB.
В этом первом базовом примере мы определяем модуль для сервера базы данных MariaDB. Вот как это выглядит:
[Unit]
Description=MariaDB container
[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
[Install]
WantedBy=multi-user.target
С помощью этих нескольких строк мы определили контейнер на основе последнего официального образа MariaDB, доступного на Dockerhub. Как видите, «.container» — это специальный тип модуля Systemd. Уникальной особенностью этого типа юнитов является раздел «Контейнер», в котором мы можем указать параметры, эквивалентные тем, которые мы можем передать в командную строку Podman. Единственная обязательная опция в разделе «Контейнер» — это Image
, чтобы указать базовое изображение для контейнера.
Если мы хотим, чтобы контейнер запускался от имени пользователя root, мы сохраняем файл в каталоге /etc/containers/systemd
; чтобы запустить его от имени нашего непривилегированного пользователя, вместо этого мы сохраняем его в ~/.config/containers/systemd
. В этом примере мы будем использовать последний подход и сохраним наш модуль «.container» как ~/.config/containers/systemd/mariadb-service.container
. Чтобы позволить systemd сгенерировать сервис на основе контейнера, мы запускаем следующую команду:
$ systemctl --user daemon-reload
После запуска команды мы можем просмотреть сгенерированный сервис, запустив:
$ systemctl --user cat mariadb-service
Чтобы запустить контейнер, как и любой другой сервис, мы можем запустить:
$ systemctl --user start mariadb-service
При первом запуске службы может потребоваться некоторое время, чтобы запуститься, если базовый образ контейнера не существует в нашей системе. Мы можем использовать командную строку podman для проверки статуса контейнера:
$ podman ps
Команда возвращает следующий вывод, который подтверждает, что контейнер запущен и работает:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
151226b10a1c docker.io/library/mariadb:latest mariadbd 59 seconds ago Up About a minute systemd-mariadb-service
Как вы, возможно, заметили, по умолчанию контейнер назван в честь юнит-файла «.container» плюс префикс «systemd-» (в данном случае systemd-mariadb-service). Однако, чтобы явно присвоить имя контейнеру, мы можем передать его как значение опции ContainerName
внутри раздела «[Container]».
Создание единицы «объема»
Мы используем тома для сохранения данных контейнеров. В этом случае мы хотим, чтобы данные нашей базы данных сохранялись, даже если мы удалим контейнер systemd-mariadb-service. Мы определяем именованный том в соответствующем модуле «.volume». В приведенном ниже примере мы предоставляем только описание тома. Параметры, специфичные для тома, можно указать в разделе [Volume]
:
[Unit]
Description=MariaDB Volume
[Volume]
Мы сохраняем модуль как ~/.config/containers/systemd/mariadb-volume.volume
. Чтобы указать контейнеру MariaDB использовать том, мы используем параметр Volume
внутри раздела [Container]
модуля «.container» и сопоставляем том с Каталог /var/lib/mysql
внутри контейнера:
[Unit]
Description=MariaDB container
[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
Volume=mariadb-volume.volume:/var/lib/mysql
[Install]
WantedBy=multi-user.target
Когда мы используем именованный том, Systemd автоматически назначает ему зависимость в сервисном модуле, который он генерирует для нашего контейнера. Чтобы позволить systemd повторно сгенерировать необходимые службы, мы снова используем команду daemon-reload
. Как только мы (пере)запустим контейнер MariaDB, том будет создан автоматически, в чем мы можем убедиться с помощью утилиты командной строки podman:
$ podman volume ls
Как и ожидалось, наш том появился в списке, сгенерированном командой:
DRIVER VOLUME NAME
local systemd-mariadb-volume
Мы также можем убедиться, что том смонтирован в /var/lib/mysql внутри контейнера, используя команду podman Inspect
, передав имя контейнера в качестве аргумента и просмотрев «Монтирование». раздел:
$ podman inspect systemd-mariadb-service
Вот соответствующий раздел вывода:
"Mounts": [
{
"Type": "volume",
"Name": "systemd-mariadb-volume",
"Source": "/home/doc/.local/share/containers/storage/volumes/systemd-mariadb-volume/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "",
"Options": [
"nosuid",
"nodev",
"rbind"
],
"RW": true,
"Propagation": "rprivate"
}
],
Использование привязок
Что, если мы хотим использовать привязку вместо именованных томов? Привязка-монтирование полезно во время разработки, поскольку позволяет нам монтировать хост-файлы внутри контейнера. Недостатком использования привязки является то, что контейнеры становятся зависимыми от файловой системы хоста. Чтобы использовать привязку внутри модуля «.container», мы просто указываем относительный или абсолютный путь к хост-файлу, который мы хотим смонтировать. Предположим, например, что мы хотим смонтировать каталог /var/lib/mysql
внутри контейнера с каталогом ~/mysql
на хосте. Вот что мы могли бы написать в модуле «.container»:
[Unit]
Description=MariaDB container
[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
Volume=%h/mysql:/var/lib/mysql
[Install]
WantedBy=multi-user.target
В приведенном выше примере вы можете заметить, что мы использовали заполнитель %h
, который Systemd автоматически расширяет до абсолютного пути к нашему собственному каталогу HOME. Также возможно использовать относительные пути, которые разрешаются относительно позиции файла модуля.
Создание сетей
Чтобы создать стек, состоящий из нескольких контейнеров, аналогично тому, что мы делаем с docker-compose, мы должны поместить контейнеры в одну сеть, чтобы они могли взаимодействовать друг с другом. Чтобы создать сеть с помощью Quadlet, мы можем использовать устройства с расширением «.network».
Как и в случае с контейнерами и томами, по умолчанию сети именуются по имени файла модуля, в котором они определены, плюс префикс «systemd-». Мы можем явно указать имя или любой другой параметр, специфичный для сети, в разделе [Network]
файла. В приведенном ниже примере мы указываем подсеть и адрес шлюза для сети (это эквивалент запуска podman с --subnet 192.168.30.0/24
и --gateway 192.168.30.1).
параметры):
[Unit]
Description=MariaDB Network
[Network]
Subnet=192.168.30.0/24
Gateway=192.168.30.1
Мы сохраняем файл как ~/.config/containers/systemd/mariadb.network
. Чтобы «поместить» контейнер в определенную сеть, мы используем параметр Network
внутри раздела «Контейнер» его контейнерного модуля. Контейнер MariaDB, который мы определили ранее, становится:
[Unit]
Description=MariaDB container
[Container]
Image=docker.io/mariadb:latest
Environment=MYSQL_ROOT_PASSWORD=rootpassword
Environment=MYSQL_USER=testuser
Environment=MYSQL_PASSWORD=testpassword
Environment=MYSQL_DATABASE=testdb
Volume=mariadb-volume.volume:/var/lib/mysql
Network=mariadb.network
Создание стека из нескольких контейнеров
У нас есть контейнер, на котором работает наш сервер MariaDB. Чтобы добавить простой способ управления нашей базой данных через веб-интерфейс, мы можем создать контейнер на основе phpMyAdmin. Чтобы два контейнера могли общаться друг с другом, мы должны поместить их в одну сеть.
Поскольку для правильной работы контейнера phpMyAdmin требуется, чтобы сервер MariaDB был активен и работал, мы можем объявить эту зависимость, как и для любой другой службы Systemd, с помощью Requires
и . После опций
в разделе «[Unit]». Первый устанавливает жесткую зависимость от службы, передаваемой в качестве значения; последний гарантирует, что наш сервис запустится после него. Кстати, если вы не знакомы с Systemd, вы можете посмотреть наше руководство о том, как создать службу Systemd. Вот как выглядит модуль-контейнер для phpMyAdmin:
[Unit]
Description=phpMyAdmin container
Requires=mariadb-service.service
After=mariadb-service.service
[Container]
Image=docker.io/phpmyadmin:latest
Network=mariadb.network
Environment=PMA_HOST=systemd-mariadb-service
PublishPort=8080:80
[Install]
WantedBy=multi-user.target
В приведенном выше примере мы также использовали параметр PublishPort
, который является эквивалентом podman -p
(--publish
): он используется для сопоставления порта хоста с портом контейнера. В данном случае мы сопоставили порт 8080
в хост-системе с портом 80
внутри контейнера.
Мы также предоставили значение для переменной среды PMA_HOST с помощью опции Environment
: она используется для указания адреса сервера базы данных (в этом случае мы использовали имя контейнера MariaDB, которое разрешается в фактический адрес). Обратите внимание, что в качестве значения переменной среды мы использовали имя контейнера MariaDB, а не имя модуля «.container». Чтобы начать нашу новую настройку, мы еще раз запускаем:
$ systemd --user daemon-reload
Затем мы можем запустить сгенерированный сервис «phpmyadmin-service», который, в свою очередь, запускает контейнер phpMyAdmin. Поскольку он жестко зависит от MariaDB, последний тоже запустится автоматически. У нас должна быть возможность связаться с phpMyadmin по адресу «localhost:8080». Для входа мы можем использовать учетные данные, указанные в контейнере MariaDB:
Заключительные мысли
В этом уроке мы узнали, как создавать и запускать контейнеры, тома и сети Podman в Systemd с помощью Quadlet. Вместо определения стеков из нескольких контейнеров в одном файле, как мы делаем при использовании docker-compose, в Quadlet мы определяем контейнеры, тома и сети, используя выделенные модули Systemd.