Имитация systemd в docker-контейнере и применение testinfra
Наши команды активно используют ansible
для создания шаблонов и финальной
конфигурации виртуальных машин. Поэтому перед нами встаёт вопрос обеспечения
работоспособности и корректности сценариев развёртывания.
В этой заметке будет рассказано об имитации systemd
внутри docker
-контейнера
и валидации финального результата с помощью фреймворка testinfra
.
Работа с molecule
будет рассмотрена в других заметках.
Задача
- Предоставить команде простой, быстрый и воспроизводимый инструмент для
локальной проверки корректности плейбуков и ролей
ansbile
. - Обеспечить доступ к контейнеру по протоколу
ssh
для запускаansbile
. - И, самое важное, дать возможность использовать
модуль
ansible.builtin.systemd
.
Подход к выбору решения
Наша задача — обеспечить быструю проверку на ноутбуке разработчика. Поэтому:
virtualbox
+vagrant
:- долгий старт виртуальной машины;
- не подходит для счастливчиков на M1.
multipass
:- быстрый, но ощутимый старт виртуальной машины;
- для доступа по
ssh
требуется несложная дополнительная настройка.
docker
:- молниеносный старт контейнера;
- возможность один раз “зашить” переиспользуемую конфигурацию
sshd
; - не спроектирован для использования
systemd
в качествеinit
-процесса.
В своей работе мы используем два варианта:
multipass
+multipass-compose
, когда нам требуется работа с “настоящими” виртуальными машинами;docker
, когда необходимо быстро проверить изменения.
Рассмотрим решение с docker
-образом.
Решение в лоб
Очевидное решение — запустить systemd
как init
-процесс контейнера. И,
конечно, это так работать не будет.
# syntax=docker/dockerfile:1
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
RUN <<EOF
apt update
apt install -y --no-install-recommends \
systemd
EOF
CMD ["/bin/systemd"]
Контейнер будет запущен, но, как только вы обратитесь к systemctl
, будет
получен следующий результат:
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
Решение, удовлетворяющее условиям поставленной задачи
Подготовим образ, внутри которого будет установлены и настроены:
- пакет
systemd
(нужен, фактически, только для создания структуры каталогов); - заглушка
gdraheim/docker-systemctl-replacement
для имитацииsystemctl
иjournalctl
; - пользователь, входящий в группу
sudo
.
Точкой входа будет выступать sshd
.
# syntax=docker/dockerfile:1
FROM ubuntu:focal
ENV DEBIAN_FRONTEND=noninteractive
RUN <<EOF
apt update
apt install -y --no-install-recommends \
openssh-server \
sudo \
gnupg \
systemd \
python3
useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo -u 1000 ubuntu
echo 'ubuntu:ubuntu' | chpasswd
service ssh start
EOF
COPY systemctl3.py /usr/bin/systemctl
COPY journalctl3.py /usr/bin/journalctl
RUN chmod +x /usr/bin/systemctl /usr/bin/journalctl
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]
При работе sshd
будет игнорировать сигнал SIGTERM
для остановки контейнера,
поэтому запускать его следует либо с опцией --init
, либо с указанием сигнала
остановки --stop-signal
.
После запуска контейнера, в него можно будет зайти по ssh
, что нам необходимо
для запуска ansible
и testinfra
.
ssh \
-o UserKnownHostsFile=/dev/null \
-o ControlMaster=auto -o \
ControlPersist=60s \
ubuntu@localhost
testinfra
Для проверок состояния сервисов с помощью модуля Service
testinfra
выполняет
ряд инспекций, выбирая, какая подсистема инициализации используется на данной
машине.
Чтобы явно сообщить об использовании systemd
, нужно использовать довольно
безобидный и компактный monkeypatch
:
import testinfra.modules.service
testinfra.modules.service.Service.get_module_class = classmethod(
lambda *args, **kwargs: testinfra.modules.service.SystemdService
)
Это явно указывает testinfra
опираться на systemctl
.
Итого
Что получили в итоге?
- Быстрый запуск контейнера с имитацией
systemd
и доступом поssh
. - Простой способ проверки плейбуков и ролей
ansbile
. - Возможность прогонять тесты
testinfra
. - Воспроизводимость и переносимость между машинами разработчиков.
Всё в комплексе можно посмотреть и пощупать у меня на github
:
Успехов в работе с ansible
и тестировании!