image Привет, Хаброжители!

Пришло время обновить свой контейнерный движок! Менеджер контейнеров Podman обеспечивает гибкое управление слоями образов и полную совместимость с Kubernetes, а также дает возможность пользователям без прав администратора создавать, запускать непривилегированные контейнеры и управлять ими. OCI-совместимая поддержка Docker API позволяет перевести существующие контейнеры на Podman, не ломая свои скрипты и не меняя привычного порядка работы.

«Podman в действии» познакомит вас с менеджером контейнеров Podman. Простые объяснения и примеры позволят быстро разобраться с тем, что такое контейнеры, как они работают и как управлять ими. Вы получите глубокие знания об используемых Podman компонентах Linux и даже узнаете больше о Docker. Особенно ценны соображения автора Дэна Уолша по поводу безопасности контейнеров.

Для разработчиков и системных администраторов, имеющих опыт работы с Linux и Docker.

Для кого эта книга
Книга «Podman в действии» написана для разработчиков программного обеспечения, которые хотят разобраться в том, что такое контейнеры, создавать и применять их, а также для системных администраторов, использующих контейнеры в выпускаемых продуктах. Прочитав эту книгу, вы получите более глубокое представление о том, что такое контейнеры. Для получения максимальной пользы от книги необходимо понимание процессов Linux и умение работать с его командной строкой.

Каждый, кто хочет использовать контейнеры, найдет что-то для себя в этой книге. Пользователи, хорошо знакомые с Docker, узнают о расширенных возможностях Podman, недоступных в Docker, и еще лучше разберутся в том, как работает Docker. Начинающие пользователи изучат основы работы с контейнерами и подами.

Структура книги
«Podman в действии» состоит из четырех частей и шести приложений.

  • Часть 1 «Основы» состоит из четырех глав и знакомит читателей с Podman. В главе 1 объясняется, что делает Podman, зачем он был создан и почему он важен. В следующих двух главах рассказывается об интерфейсе командной строки и о том, как использовать тома в контейнерах. В главе 4 вводится понятие подов и объясняется, как Podman работает с ними. Здесь каждый найдет что-то полезное для себя, но если у вас большой опыт работы с Docker, вам будет достаточно лишь бегло просмотреть бˆольшую часть содержимого главы 2.
  • Часть 2 «Архитектура» состоит из двух глав, в которых я глубоко погружаюсь в устройство Podman. Вы узнаете о непривилегированных (rootless) контейнерах и о том, как они работают. Прочитав эти главы, вы станете лучше понимать, что такое пользовательские пространства имен и безопасность непривилегированных контейнеров. Вы также узнаете, как настроить конфигурацию вашего Podman-окружения.
  • Часть 3 «Расширенные возможности Podman» состоит из трех глав и выходит за рамки основ Podman. В главе 7 показано, как Podman может работать в продакшене благодаря интеграции с systemd. В ней рассказывается о запуске systemd внутри контейнера и о том, как использовать его в качестве менеджера контейнеров. Вы узнаете, как настроить серверы с контейнерами Podman, где systemd управляет жизненным циклом контейнера. Podman позволяет легко генерировать файлы systemd-юнитов, помогая отправить в продакшен ваши контейнеризованные приложения. В главе 8 вы узнаете, как с помощью Podman можно перенести контейнеры в Kubernetes. Podman поддерживает запуск контейнеров с помощью тех же YAML-файлов, которые использует Kubernetes, а также дает возможность генерировать YAML для Kubernetes из ваших контейнеров. В главе 9 я покажу, как Podman работает в качестве службы, обеспечивая удаленный доступ к контейнерам Podman. Использование Podman в качестве службы позволяет вам применять другие языки программирования и инструменты для управления контейнерами. Вы увидите, как docker-compose может работать с контейнерами Podman. Вы также узнаете, как использовать для взаимодействия с сервисом Podman при управлении контейнерами такие библиотеки Python, как podman-py и docker-py.
  • Часть 4 «Безопасность контейнеров» состоит из двух глав, в которых речь идет о важных аспектах безопасности. В главе 10 описаны функции, используемые для обеспечения изоляции контейнеров. В этой главе анализируются такие подсистемы безопасности Linux, как SELinux, seccomp, привилегии Linux (Linux capabilities), файловые системы ядра и пространства имен (namespaces). Затем в главе 11 рассматриваются соображения безопасности, которые я считаю лучшими практиками для максимально безопасного запуска контейнеров.

Кроме того, в книге шесть приложений, посвященных связанным с Podman темам:

  • В приложении A описаны все связанные с Podman инструменты, включая Buildah, Skopeo и CRI-O.
  • Приложение B подробно рассматривает различные среды выполнения OCI, доступные как для Podman, так и для Docker. В нем обсуждаются runc, crun, Kata и gVisor.
  • Приложение C описывает, как установить Podman на вашу локальную систему, будь то Linux, Mac или Windows.
  • В приложении D рассказывается о сообществе разработчиков открытого исходного кода Podman и о том, как к нему присоединиться.
  • Приложения E и F посвящены запуску Podman на компьютерах c операционными системами Mac и Windows.

Перезапуск контейнеров


Podman использует systemd для запуска контейнерных сервисов с помощью загрузки Podman в юнит-файлах systemd. Команда podman run позволяет указать, следует ли перезапускать контейнер (--restart), если он не остановлен пользователем (например, при сбое контейнера или перезагрузке системы). В табл. 7.4 приведены доступные в Podman политики перезапуска.

Один из простых способов, который имеется в systemd, — это запуск контейнеров с политикой перезапуска always. Если вы установили опцию always и система перезагрузилась, Podman использует две службы systemd для автоматического перезапуска контейнеров, помеченных параметром --restart=always. Одна служба обслуживает rootful-контейнеры, другая — все rootless-контейнеры в системе.

image

При загрузке системы systemd выполняет следующую команду Podman для запуска всех контейнеров с установленной политикой перезапуска always:

/usr/bin/podman start --all --filter restart-policy=always

ПРИМЕЧАНИЕ Podman поставляется с двумя файлами служб systemd, используемыми для перезапуска сервисов, — один для rootful, другой для rootless:
/usr/lib/systemd/system/podman-restart.service
/usr/lib/systemd/user/podman-restart.service
Параметр --restart=always работает отлично, но он требует создания контейнера в системе и перезапускает контейнеры даже в случае их сбоя. Systemd был разработан для запуска сервисов; в следующем разделе я покажу, как можно просто создать сервисный юнит-файл с помощью Podman для запуска вашего контейнеризированного сервиса.

Контейнеры Podman как службы systemd


Итак, systemd использует юнит-файлы для определения порядка работы сервиса. На рис. 7.5 показано, как systemd работает с Podman для запуска контейнера.

image

На рис. 7.5 я показал, что systemd может отслеживать все процессы, выполняющиеся в юнит-файле systemd. Это позволяет ему легко запускать и останавливать процессы. Процесс conmon также работает в рамках службы systemd, отслеживая процессы контейнера. conmon замечает, когда контейнер завершает работу, после этого он сохраняет код выхода и аккуратно останавливает контейнерное окружение. Systemd не знает о самом контейнере, ему известно только о процессах, выполняющихся в юнит-файле, включая процессы контейнера.

В юнит-файлах Systemd имеется множество различных способов выполнения и запуска процессов, а в Podman — множество вариантов запуска контейнеров. Конфигурирование этих файлов может быть очень сложным. Многие пользователи писали свои юнит-файлы для запуска контейнеров, но при этом сталкивались с различными проблемами. Наиболее распространенной проблемой является выполнение команды podman run --detach внутри юнит-файла. Когда команда Podman отсоединяется и выходит из системы, systemd отключает службу как завершенную, хотя conmon и контейнер все еще работают. «Как я должен запускать свой контейнер внутри юнит-файла systemd?» — один из самых распространенных вопросов, который я слышу от пользователей.

В Podman есть функция генерации юнит-файлов с оптимальными настройками по умолчанию. Сначала пересоздайте контейнер из myimage, а затем с помощью podman systemd generate создайте юнит-файл службы systemd для управления контейнером.

Создайте контейнер на основе образа, созданного в главе 2:

$ podman create -p 8080:8080 --name myapp quay.io/rhatdan/myimage
...
8879112805e976b4b6d97c07c9426bdde22ee4ffc7ba4daa59965ae25aa08331

Теперь с помощью Podman сгенерируйте из этого контейнера юнит-файл:

$ mkdir -p $HOME/.config/systemd/user
$ podman generate systemd myapp > $HOME/.config/systemd/user/myapp.service

Обратите внимание, что в скрипте myapp.service Podman создал поле ExecStart. При начале работы службы systemd выполнит команду ExecStart, которая просто запустит созданный вами контейнер:

ExecStart=/usr/bin/podman start 8879112805...

При остановке сервиса systemd выполняет команду ExecStop, добавленную в юнит-файл:

ExecStop=/usr/bin/podman stop -t 10 8879112805...
Let's take a look at the generated service file:
$ cat $HOME/.config/systemd/user/myapp.service
# container-
     8879112805e976b4b6d97c07c9426bdde22ee4ffc7ba4daa59965ae25aa08331.service
# autogenerated by Podman 3.4.1
# Wed Nov 10 08:23:06 EST 2021
[Unit]
Description=Podman container-8879112805...service
Documentation=man:podman-generate-SystemD(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=/run/user/3267/containers
[Service]
Environment=PODMAN_SYSTEMD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStart=/usr/bin/podman start 8879112805...
ExecStop=/usr/bin/podman stop -t 10 8879112805...
ExecStopPost=/usr/bin/podman stop -t 10 8879112805...
PIDFile=/run/user/3267/containers/overlay-containers/8879112805.../
         userdata/conmon.pid
Type=forking
[Install]
WantedBy=multi-user.target default.target

Чтобы все это заработало, необходимо указать systemd на перезагрузку своей базы данных, тогда она заметит изменения в юнит-файлах:

$ systemctl --user daemon-reload

Запустите службу с помощью следующей команды:

$ systemctl --user start myapp

Убедитесь в том, что служба запущена:

$ systemctl --user status myapp
• myapp.service - Podman container-8879112805....service
   Loaded: loaded (/home/dwalsh/.config/SystemD/user/myapp.service;
➥ disabled; vendor preset: disabled)
  Active: active (running) since Thu 2021-11-11 07:19:08 EST; 3min 9s ago
...
$ podman ps
CONTAINER ID  IMAGE                  COMMAND
➥ CREATED      STATUS    PORTS    NAMES
8879112805e9 quay.io/rhatdan/myimage:latest /usr/bin/run-http...
➥ 23 hours ago Up 5 minutes ago 0.0.0.0:8080->8080/tcp myapp

Запустите в браузере localhost на порте 8080, чтобы убедиться, что все работает (рис. 7.6):

$ web-browser localhost:8080

image

Чтобы выключить службу, выполните команду:

$ systemctl --user stop myapp

Возможность генерировать сервисные файлы systemd предоставляет пользователям значительную гибкость и умышленно стирает разницу между контейнером и любой другой программой или сервисом на хосте.

Одна из проблем с рассмотренным юнит-файлом заключается в том, что он привязан к созданному контейнеру. Необходимо сначала создать контейнер и сгенерировать конкретные файлы. Вы не можете передать юнит-файл другому пользователю, чтобы он запустил вашу службу на своей машине.

К счастью, Podman поддерживает создание переносимого юнит-файла systemd: podman generate systemd --new.

Распространение юнит-файлов systemd для управления контейнерами Podman


Как было показано ранее, команда podman generate systemd сгенерировала юнит-файл, который запускал и останавливал существующий контейнер. Флаг --new передает Podman инструкцию генерировать юнит-файлы, которые запускают, останавливают и удаляют контейнеры. Попробуйте это применить в том же контейнере:

$ podman generate systemd --new myapp > $HOME/.config/systemd/user/
➥ myapp-new.service

Обратите внимание, что при использовании параметра --new Podman создает несколько отличающийся юнит-файл. Посмотрите на следующую команду ExecStart, и вы увидите, что первоначальная команда podman create -p 8080:8080 --name myapp quay.io/rhatdan/myimage, которую вы использовали для создания контейнера, была изменена на команду podman run. Обратите внимание, что в Podman добавлены дополнительные параметры для упрощения работы под управлением systemd (--cidfile =%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --replace).

Podman добавляет команду ExecStop (/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id), указывающую systemd, как остановить контейнер, если кто-то выполнит команду systemctl stop или система выключится.

Наконец, Podman добавляет команду ExecStopPost (/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-idType=notify), которую systemd выполняет после завершения выполнения команды ExecStop. Эта команда удаляет контейнер из системы:

$ cat $HOME/.config/systemd/user/myapp-new.service
# container-8879112805....service
# autogenerated by Podman 3.4.1
# Thu Nov 11 07:40:34 EST 2021
[Unit]
Description=Podman container-8879112805...service
Documentation=man:podman-generate-SystemD(1)
Wants=network-online.target
After=network-online.target
RequiresMountsFor=%t/containers
[Service]
Environment=PODMAN_SystemD_UNIT=%n
Restart=on-failure
TimeoutStopSec=70
ExecStartPre=/bin/rm -f %t/%n.ctr-id
ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon –
➥ rm --sdnotify=conmon -d --replace -p 8080:8080 --name myapp
➥ quay.io/rhatdan/myimage
ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-idType=notify
NotifyAccess=all
[Install]
WantedBy=multi-user.target default.target

Вы можете удалить контейнер и его образ из своей системы, а когда вы сообщите systemctl о запуске службы, Podman загрузит образ и создаст новый контейнер. Это означает, что юнит-файлом myapp-new.service можно поделиться с другим пользователем. Когда пользователь запустит эту службу, Podman подобным же образом загрузит образ и запустит контейнер в его системе, при этом предварительно создавать контейнер не понадобится. В табл. 7.5 приведены различные команды, добавляемые в юнит-файл в зависимости от того, использовался ли флаг --new.

image

Так как контейнерный сервис теперь запущен на множестве машин, необходимо подумать о его поддержке. В Podman есть способ сделать это без вмешательства человека: автообновление.

Автоматическое обновление контейнеров Podman


В главе 2 мы говорили о том, что образы контейнеров стареют «как сыр». Если образ контейнера обновляется с помощью нового программного обеспечения или исправления уязвимостей, необходимо связаться с использующими его машинами, загрузить обновленные образы и заново создать контейнерные службы. Когда машины сами управляют обновлениями, это требует гораздо меньше усилий.

Представьте, что вы настраиваете службу для работы с образом контейнера на сотнях узлов. Спустя несколько месяцев вы добавляете новые функции в приложение в образе или, что более важно, обнаруживается новая уязвимость. Тогда необходимо обновить образ, а затем заново создать службу на всех узлах.

Podman автоматизирует этот процесс с помощью автообновления. Каждый узел следит за появлением новых образов в реестре контейнеров. Когда образ появляется, нода загружает его и заново создает контейнер. Никакого взаимодействия с человеком при этом не требуется.

Автообновление позволяет использовать Podman в нестандартных сценариях, обновлять рабочие среды после их подключения к сети и откатывать сбои до заведомо исправного состояния. Кроме того, запуск контейнеров необходим для реализации граничных вычислений (edge computing) в удаленных центрах обработки данных или на устройствах интернета вещей. Автоматическое обновление Podman позволяет обновлять рабочие нагрузки после их подключения к сети и снижать затраты на их обслуживание.

Для реализации автообновления Podman требует, чтобы контейнеры имели специальную метку, --label «io.containers.autoupdate=registry», а сам контейнер запускался в systemd-юните, сгенерированном командой podman generate systemd --new. В табл. 7.6 представлены доступные режимы автообновления.

image

Сначала остановите службу systemd, если она запущена, и удалите существующий контейнер myapp:

$ systemctl --user stop myapp-new
$ podman rm myapp --force -t 0

Пересоздайте контейнер myapp со специальной меткой «io.containers.autoupdate= registry»:

$ podman create --label "io.containers.autoupdate=registry" -p 8080:8080
➥ --name myapp quay.io/rhatdan/myimage
397ad15601868eb6fd77fe0b67136869cde9e0ffad90ee5095a19de5bb4b999e

Пересоздайте юнит-файл systemd с помощью параметра --new:

$ podman generate systemd myapp --new > $HOME/.config/systemd/user/
➥ myapp-new.service

Сообщите systemd об изменении юнит-файла, выполнив команду daemon-reload, и запустите службу:

$ systemctl --user daemon-reload
$ systemctl --user start myapp-new

Теперь служба myapp-new готова к автоматическому обновлению. При получении команды podman auto-update Podman проверяет запущенные контейнеры на наличие метки io.containers.autoupdate в image. Для каждого контейнера с такой меткой Podman обращается к реестру контейнеров и проверяет, не изменился ли образ с момента создания контейнера. Если образ изменился, Podman перезапускает соответствующий systemd-юнит. Напомним, что при перезапуске systemd выполняются следующие действия:
  1. Systemd останавливает службу, выполняя команду podman stop:
    ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id
  2. Systemd выполняет скрипт ExecStopPost. После остановки контейнера этот скрипт удаляет контейнер с помощью команды podman rm:
    ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/
    ➥ %n.ctr-idType=notify
  3. Systemd перезапускает службы с помощью команды podman run, включая параметр --label «io.containers.autoupdate=registry»:
    ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon –rm
    ➥ --sdnotify=conmon -d --replace –label
    ➥ io.containers.autoupdate=registry -p 8080:8080
    ➥ --name myapp quay.io/rhatdan/myimage
Команда podman run на третьем шаге обратится к реестру, получит обновленный образ контейнера и пересоздаст на его основе контейнерное приложение. Контейнер, его окружение и все зависимости перезапускаются.

Это можно проверить, изменив образ, разместив его в реестре и выполнив команду podman auto-update следующим образом:

$ podman exec -i myapp bash -c 'cat > /var/www/html/index.html' << _EOF
<html>
 <head>
 </head>
 <body>
  <h1>Welcome to the new Hello World<h1>
 </body>
</html>
_EOF

Теперь выполните коммит образа под именем myimage-new и добавьте его в реестр с исходным именем myimage. Наконец, удалите образ из локального хранилища, имитируя, что образ никогда не находился в вашей системе:

$ podman commit myapp quay.io/rhatdan/myimage-new
...
226ec055eef82ac185c53a26de9e98da4e6403640e72c7461a711edcbcaa2422
$ podman push quay.io/rhatdan/myimage-new quay.io/rhatdan/myimage
...
$ podman rmi quay.io/rhatdan/myimage-new

Как только новый образ окажется в реестре и вы удалите его из локального хранилища, можно запустить команду podman auto-update, которая заметит новый образ и перезапустит сервис. Это вызовет Podman для загрузки нового образа и пересоздания контейнерного сервиса:

$ podman auto-update
Trying to pull quay.io/rhatdan/myimage...
Getting image source signatures
Copying blob ecfb9899f4ce done
Copying config 37e5619f4a done
Writing manifest to image destination
Storing signatures
UNIT CONTAINER IMAGE
➥ POLICY UPDATED
myapp-new.service c8888d1319c4 (myapp) quay.io/rhatdan/myimage registry
➥ true

Ваше приложение обновлено до последней версии образа.

Ниже приводятся некоторые часто используемые параметры команды podman auto-update:
  • --dry-run. Параметр полезен для проверки необходимости обновления каких-либо контейнеров без их фактического обновления.
  • --roll-back. Параметр указывает Podman на откат к предыдущему образу в случае неудачного обновления, о чем будет рассказано в следующем разделе.

Таймеры SYSTEMD, инициирующие обновления Podman


Podman содержит два таймера автообновления systemd и два сервис-юнита автообновления — по одному для rootful- и rootless-контейнеров. Раз в сутки systemd запускает следующие таймеры:
  • /usr/lib/systemd/system/podman-auto-update.timer;
  • /usr/lib/systemd/user/podman-auto-update.timer.
Таймеры указывают systemd на выполнение соответствующего юнит-файла автообновления:
  • /usr/lib/systemd/system/podman-auto-update.service;
  • /usr/lib/systemd/user/podman-auto-update.service.
С помощью этой функции systemd запускает Podman, который ищет контейнеры с меткой «io.containers.autoupdate=registry» подобно тем, что вы создали в предыдущем разделе. Как только Podman находит контейнер с такой меткой, он проверяет, был ли обновлен образ контейнера в реестре. Если образ изменился, Podman запускает процесс обновления. Это означает, что вы можете эксплуатировать свои системы без постоянного сопровождения, и они будут обновляться все 24 часа, получая новейшую версию образа контейнера каждый раз, когда вы отправляете обновленный образ в реестр. Если вы поделитесь созданным юнит-файлом с другими пользователями, то они также получат автоматическое обновление.

При использовании автообновления серьезное беспокойство вызывает вопрос о том, что произойдет, если обновление будет сбойным. В этом случае сотни узлов будут обновлены до неисправной версии сервиса. В systemd есть функция sd-notify, которая позволяет службе сообщить, что ее инициализация завершена и она готова к использованию.

ПРИМЕЧАНИЕ Часть этого раздела основана на моих ранее написанных статьях из блога «Как использовать автообновления и откаты в Podman» («How to use auto-updates and rollbacks in Podman») (https://www.redhat.com/sysadmin/podman-auto-updates-rollbacks), соавторами которых являются мои коллеги Валентин Ротберг (Valentin Rothberg) и Прити Томаc (Preethi Thomas).
Об авторе
Дэниэль Уолш (Daniel Walsh) возглавляет команду, которая создала Podman, Buildah, Skopeo, CRI-O и связанные с ними инструменты. Дэн — ведущий инженер компании Red Hat, работает там с августа 2001 года. Более 40 лет он занимается компьютерной безопасностью. Дэна иногда называют «мистер SELinux», так как он руководил разработкой SELinux в Red Hat до того, как возглавил группу, занимающуюся контейнерами.

Более подробно с книгой можно ознакомиться на сайте издательства:

» Оглавление
» Отрывок

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — Podman

Комментарии (0)