Имитация 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 и тестировании!