View on GitHub

blog

О программировании и не только

выбираем и настраиваем ВМ для облака

Если вы знакомы с FreeBSD, вы уже знаете её достоинства и недостатки по сравнению с Linux. Просуммирую некоторые отличия, важные для выбора облачной ВМ.

  1. FreeBSD не поддерживает контейнеры в привычном понимании, ни docker, ни podman пока не портированы
  2. FreeBSD использует свою собственную лицензию The FreeBSD Copyright, позволяющую не раскрывать т.н. derivative work. Как результат, оценить распространённость FreeBSD иногда затруднительно, возможно, часть её кода используется и в Windows(C), и в MacOS(C). Только иногда разработчики сами признают её использование, напр. Евгений Гаврилов, руководитель проекта vStack
  3. Метод разработки FreeBSD ближе к академическому стилю, есть базовый набор пакетов base, включающий не только ядро, но и достаточно инструментов для сборки остальной системы, именуемой ports. Развитие системы управляется elected Core Team, на данный момент 9 человек.
  4. Во FreeBSD давно используется система snapshots/rollback, позволяющая откатить изменения, если что-то сломалось.

Есть дистрибутив Linux, взявший некоторые из перечисленных особенностей - это Fedora Core. Первоначально эти идеи возникли в другом дистрибутиве, RedHat CoreOS. Во-первых, это система ориентирована на использование в облаках, предоставляются образы для всех возможных систем виртуализации и форматов дисков. Во-вторых, она разделена на базовую систему, часть из которой монтируется только в режиме только чтение (ro:read only), и остальное, доступное ч/з контейнеры. Обновление системы возможно автоматически, появилась особая утилита rpm-ostree, и можно откатить на предыдущую версию, если что-то не так. Таким образом, CoreOS становиться иммутабельной, изменения базовой части системы невозможны в обход коммитов rpm-ostree.

Я как-то сделал эксперимент, установил в Яндекс облаке версию FedoraCore 35, выпущенную в конце 2021г., с включённым по умолчанию сервисом zincati, автоматически обновляющем базовую систему, стабильные релизы. Посчитал, сколько коммитов сделано с тех пор: да, также как в git, используется CAS(content-addressable storage), уже привычные нам хэши. ВМ обновилась и рестартанула 8 раз, если я не пропустил какой-либо рестарт:

grep ^Linux uname*|cut -d" " -f1,3,7-|sed 's/Linux//;s/ x86.*//'
uname35: 5.15.7-200.fc35.x86_64 Dec 8 19:00:47 UTC 2021
uname36: 5.17.5-300.fc36.x86_64 Thu Apr 28 15:51:30 UTC 2022
uname37: 6.1.18-200.fc37.x86_64 Sat Mar 11 16:09:14 UTC 2023
uname38: 6.5.8-200.fc38.x86_64 Fri Oct 20 15:53:48 UTC 2023
uname39.0: 6.6.8-200.fc39.x86_64 Thu Dec 21 04:01:49 UTC 2023
uname39.1: 6.8.4-200.fc39.x86_64 Thu Apr  4 20:45:21 UTC 2024
uname40.0: 6.8.11-300.fc40.x86_64 Mon May 27 14:53:33 UTC 2024
uname40.1: 6.11.3-200.fc40.x86_64 Thu Oct 10 22:31:19 UTC 2024
uname41: 6.12.7-200.fc41.x86_64 Fri Dec 27 17:05:33 UTC 2024

Теперь можно сравнить с этой же системой, установленной из свежего имиджа - как и ожидалось, системы не отличались, хэши коммитов одинаковы. Это то, что имеется ввиду под иммутабельностью ОС. Кроме иммутабельности и смонтированных только по чтению некоторых каталогов, есть и другие меры безопасности. Используется версия Security-Enhanced Linux, никаких логинов по умолчанию, настройки пользователя либо через утилиту cloud-init, либо специальная утилита butane и т.н. ignition file.

У проекта Fedora есть подробнейшая документация, безусловно, самое авторитетное руководство по установке. Тем не менее, у меня возникла пара вопросов. Во-первых, для преобразования .yaml файла в ignition, который по сути обычный .json файл, используется особый инструмент butane, написанный на языке go, судя по размеру образа 130M, довольной увесистый, но никак не упоминаются другие утилиты для конвертациии .yaml в .json. Во-вторых, в простейшем случае, для первого логина, вообще не нужны ни butane, ни .yaml. Вот пример минимального ignition.json файла, который создаётся руками:

cat ignition.json
{
  "ignition": {
    "version": "3.3.0"
  },
  "passwd": {
    "users": [
      {
        "name": "core",
        "sshAuthorizedKeys": [
          "<ssh-ed25519 your ssh pub key content>"
        ]
      }
    ]
  }
}

Скопируем этот файл в каталог /tmp, он нужен 1 раз, для первого входа, не содержит секретов, к нему нужно будет указать полный путь. Как и ранее, скачиваем свежий образ для libvirt/qemu: выбрать нужную архитектуру и формат диска, проверяем контрольные суммы и цифровые подписи. Команды для создания ВМ будут немного отличаться от уже опробованной из прошлой статье - сначала сделаем для нашего ignition.json опцию, которую понимает virt-install:

export IGN=(--qemu-commandline="-fw_cfg name=opt/com.coreos/config,file=/tmp/ignition.json")
echo $IGN
--qemu-commandline=-fw_cfg name=opt/com.coreos/config,file=/tmp/ignition.json

Затем распаковываем образ в стандартный каталог и создаём ВМ:

xz -cdv fedora-coreos-41.20250215.3.0-qemu.x86_64.qcow2.xz > /var/lib/libvirt/images/fc41stab.qcow2
virt-install --import --memory 4096 --vcpus 1 -w bridge=br0 \
 --os-variant="fedora-coreos-stable" --graphics=none \
 -n fc41stab "${IGN[@]}" --disk /var/lib/libvirt/images/fc41stab.qcow2

Созданная заранее переменная шелл использована как "${IGN[@]}". virt-install выдаст интереснейший лог процесса инсталяции, в самом конце, перед приглашением, покажет IP адрес вновь созданной ВМ. Переходим в другую консоль и проверяем наш ssh ключ: ssh -v [email protected] в моём случае. После успешного логина выполняем первоочерёдные проверки и настройки:

$ id
uid=1000(core) gid=1000(core) groups=1000(core),4(adm),10(wheel),16(sudo),190(systemd-journal) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
$ sudo -l
Matching Defaults entries for core on fc41stab:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR
    LS_COLORS", env_keep+="MAIL QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT
    LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET
    XAUTHORITY", secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/var/lib/snapd/snap/bin

User core may run the following commands on fc41stab:
    (ALL) ALL
    (ALL) NOPASSWD: ALL
$ sudo hostnamectl hostname fc41stab
$ sudo passwd core

Теперь ВМ можно остановить и сделать финальные настройки нашего персонального облака для этой ВМ. Добавим MAC и статический IP адреса в dnsmasq.conf, зададим имя новой ВМ в сети, см. пример из прошлой статьи. Откроем в virt-manager окно overview, затем откроем вкладку XML и в самом конце удалим конфиг для qemu, целиком вот этот фрагмент:

<qemu:commandline>
  <qemu:arg value="-fw_cfg"/>
  <qemu:arg value="name=opt/com.coreos/config,file=/tmp/ignition.json"/>
</qemu:commandline>

Далее кнопка Apply. Возможно, virt-manager попросит сначала установить Edit -> Preferences -> General -> Enable XML editing. То же самое можно сделать из командной строки: sudo virsh edit fc41stab. Ignition файл нужен только для установки, далее эту ВМ можно донастраивать, клонировать, копировать её образ куда угодно, в том числе в большие облака.

Ну а мы продолжим знакомство с этой необычной системой, как её рекламирует проект Федора:

“Fedora CoreOS — это автоматически обновляемая, минимальная, монолитная, ориентированная на контейнеры операционная система, разработанная для кластеров, но также работоспособная автономно, оптимизированная для Kubernetes, но также отлично работающая и без него”

Проверим, что dnsmasq обновился, и начнём по порядку.

$ host fc41stab
fc41stab.local has address 192.168.100.118
fc41stab.local mail is handled by 1 fc41stab.local.
$ ssh -v fc41stab

Далее выполняем команды внутри ВМ. Основное, к чему придётся привыкнуть - что часть каталогов смонтированы readonly, запись в них в обход утилиты rpm-ostree невозможна даже судоерам:

core@fc41stab:~$ mount|grep ro,|grep ^/dev
/dev/vda4 on /sysroot type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,prjquota)
/dev/vda3 on /boot type ext4 (ro,nosuid,nodev,relatime,seclabel)
core@fc41stab:~$ mount|grep rw,|grep ^/dev
/dev/vda4 on /etc type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,prjquota)
/dev/vda4 on /sysroot/ostree/deploy/fedora-coreos/var type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,prjquota)
/dev/vda4 on /var type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,prjquota)

Я специально грепнул только реальные устройства, потому что кроме них смонтированы ещё 19 виртуальных, пока в них углубляться не будем. Как видно, в режиме rw смонтированы /etc и /var. Даже стандартый home это линк /home -> var/home.

За автоматические обновления отвечает сервис zincati, если его отключить, обновления можно запускать вручную: rpm-ostree --bypass-driver upgrade. После рестарта смотрим новое состояние системы:

core@fc41stab:~$ rpm-ostree status
State: idle
AutomaticUpdatesDriver: Zincati
  DriverState: active; periodically polling for updates (last checked Tue 2025-03-18 12:22:26 UTC)
Deployments:
● fedora:fedora/x86_64/coreos/stable
                  Version: 41.20250302.3.2 (2025-03-17T22:11:15Z)
                   Commit: aa4d65c700789b34bb953c2ee8250c6643802ed57ab1f4f5dd61e821b2be8709
             GPGSignature: Valid signature by 466CF2D8B60BC3057AA9453ED0622462E99D6AD1

  fedora:fedora/x86_64/coreos/stable
                  Version: 41.20250215.3.0 (2025-03-04T19:05:12Z)
                   Commit: d0742b8c6b45095a6e4f856d787a65f4d2702316856d78227f0b68f8f85511b5
             GPGSignature: Valid signature by 466CF2D8B60BC3057AA9453ED0622462E99D6AD1

rpm-ostree rollback позволяет откатить обновления к предыдущему комиту. Другие полезные команды:

  1. rpm-ostree db diff - список обновлённых пакетов между комитами
  2. rpm-ostree db list - список и версии пакетов в комитах

А как же устанавливать пакеты привычным dnf? - своевременный вопрос. Посмотрим сначала что уже стоит:

core@fc41stab:~/temp$ rpm-ostree db list aa4d65> rpm-ostree.db.list.250317
core@fc41stab:~/temp$ head -1 rpm-ostree.db.list.250317
ostree commit: aa4d65 (aa4d65c700789b34bb953c2ee8250c6643802ed57ab1f4f5dd61e821b2be8709)
core@fc41stab:~/temp$ wc -l rpm-ostree.db.list.250317
440 rpm-ostree.db.list.250317

Это напоминает FreeBSD base, но важно, что этот набор включает podman, docker, git - для остального есть утилита toolbox. Она делает доступными все пакеты федоры, упакованные в контейнер. Заглянем в toolbox:

core@fc41stab:~/temp/tbox$ toolbox
Error: missing command

create    Create a new Toolbx container
enter     Enter an existing Toolbx container
list      List all existing Toolbx containers and images

Run 'toolbox --help' for usage.
core@fc41stab:~/temp/tbox$ toolbox create tbox
Image required to create Toolbx container.
Download registry.fedoraproject.org/fedora-toolbox:41 (384.2MB)? [y/N]: y
Created container: tbox
Enter with: toolbox enter tbox
core@fc41stab:~/temp/tbox$ podman images
REPOSITORY                                 TAG         IMAGE ID      CREATED      SIZE
registry.fedoraproject.org/fedora-toolbox  41          2288b69816a7  7 hours ago  2.29 GB
core@fc41stab:~/temp/tbox$ toolbox enter tbox
⬢ [core@toolbx tbox]$

Мы оказались внутри контейнера, об это говорит изменившееся приглашение $PS1. Если снова посмотреть mount, увидим ещё больше и хитрее подмонтированных каталогов, этакий оверлей из базовой и контейнерной ФС. Но зато работает dnf, можно устанавливать любые пакеты. Добавим привычные, без них неуютно в новой системе:

⬢ [core@toolbx tbox]$ sudo dnf update
Updating and loading repositories:
 Fedora 41 openh264 (From Cisco) - x86_64                                                            100% |   7.2 KiB/s |   4.8 KiB |  00m01s
 Fedora 41 - x86_64                                                                                  100% |  16.8 MiB/s |  35.2 MiB |  00m02s
 Fedora 41 - x86_64 - Updates                                                                        100% |   5.2 MiB/s |  11.7 MiB |  00m02s
Repositories loaded.
Nothing to do.
⬢ [core@toolbx tbox]$ sudo dnf install tmux btop

Как видим, пакеты доступны, но желательно иметь их вне контейнера. Нет проблем, скопируем их в свой ~/bin каталог, он одинаков и внутри, и снаружи контейнера:

⬢ [core@toolbx tbox]$ type tmux
tmux is /usr/bin/tmux
⬢ [core@toolbx tbox]$ mkdir ~/bin
⬢ [core@toolbx tbox]$ cp /usr/bin/tmux ~/bin
⬢ [core@toolbx tbox]$ type btop
btop is /usr/bin/btop
⬢ [core@toolbx tbox]$ cp /usr/bin/btop ~/bin

Выйдем из контейнера и проверим:

⬢ [core@toolbx tbox]$ 
logout
core@fc41stab:~/temp/tbox$ ldd ~/bin/*
/var/home/core/bin/btop:
        linux-vdso.so.1 (0x00007f45e8231000)
        libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f45e7fc3000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f45e7edd000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f45e7eae000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f45e7cbb000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f45e8233000)
/var/home/core/bin/tmux:
        linux-vdso.so.1 (0x00007ff7f2acb000)
        libsystemd.so.0 => /lib64/libsystemd.so.0 (0x00007ff7f28c8000)
        libutempter.so.0 => /lib64/libutempter.so.0 (0x00007ff7f28c3000)
        libtinfo.so.6 => /lib64/libtinfo.so.6 (0x00007ff7f2895000)
        libevent_core-2.1.so.7 => /lib64/libevent_core-2.1.so.7 (0x00007ff7f285d000)
        libm.so.6 => /lib64/libm.so.6 (0x00007ff7f2777000)
        libresolv.so.2 => /lib64/libresolv.so.2 (0x00007ff7f2764000)
        libc.so.6 => /lib64/libc.so.6 (0x00007ff7f256f000)
        libcap.so.2 => /lib64/libcap.so.2 (0x00007ff7f2562000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007ff7f2533000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ff7f2acd000)

Будем считать что знакомство закончено, попробуем что-то посерьёзнее - настроить на fedora core сервер для команды разрабов, для начала полноценный dev env: репозитарий кода, образов контейнеров, инструменты для сборки и сервер приложений.

В команде, как обычно, есть админ, или несколько. Уже созданный пользователь core - это админ, у него есть права судо, ему доступен контейнер tbox. В нашей федоре каталог с секретами устроен особым образом:

core@fc41stab:~$ tree ~/.ssh/
/var/home/core/.ssh/
└── authorized_keys.d
    └── ophil

2 directories, 1 file

Первоначально обычный файл authorized_keys назывался ignition и располагался в каталоге ~/.ssh/authorized_keys.d, что подразумевает, что можно ещё добавить ключей, назвав их никами других админов. Так же можно поступить с разрабами, настроить для всех один dev env.

Создадим нового пользователя без прав судо:

sudo adduser dev;
sudo usermod -aG docker dev
sudo passwd dev
sudo cp -a ~/.ssh/ ~dev/
sudo chown -R dev:dev ~dev/.ssh

Как мы уже знаем, мелкие утилиты можно просто скопировать из тулбокса в ~/bin или /usr/local/bin. Для языков программирования и прочих инструментов есть замечательная программа mise. Сейчас мы её установим для dev и немного познакомимся. Мой ключ уже добавил в ~dev/.ssh/authorized_keys.d/, так что логинимся и настраиваем инструменты для команды разрабов.

ssh -v dev@fc41stab
dev@fc41stab:~$ id
uid=1001(dev) gid=1001(dev) groups=1001(dev),983(docker) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
dev@fc41stab:~$ curl https://mise.run | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  9557  100  9557    0     0  26211      0 --:--:-- --:--:-- --:--:-- 26255
mise: installing mise...
######################################################################## 100.0%
mise: installed successfully to /var/home/dev/.local/bin/mise
mise: run the following to activate mise in your shell:
echo "eval \"\$(/var/home/dev/.local/bin/mise activate bash)\"" >> ~/.bashrc

mise: run `mise doctor` to verify this is setup correctly
dev@fc41stab:~$ echo "eval \"\$(/var/home/dev/.local/bin/mise activate bash)\"" >> ~/.bashrc
dev@fc41stab:~$ . ~/.bashrc

mise позволяет устанавливать и использовать различные версии языков программирования, инструментов и библиотек. Другие менеджеры версий, например aqua, asdf, ubi - собрали репозитарии таких инструментов, mise их использует в качестве бэкендов. Огласим весь список:

dev@fc41stab:~/src$ mise registry > mise.registry
dev@fc41stab:~/src$ wc -l mise.registry 
883 mise.registry
dev@fc41stab:~/src$ mise ls-remote python|tail
3.12.6
3.12.7
3.12.8
3.12.9
3.13.0
3.13-dev
3.13.1
3.13.2
3.14.0a6
3.14-dev
dev@fc41stab:~/src$ mise -v use -g [email protected]
dev@fc41stab:~/src$ mise ls
Tool    Version  Source                      Requested 
python  3.12.9   ~/.config/mise/config.toml  3.12

Да, список великоват, сохранил в файл, 883 шт., и он постоянно растёт. Это только названия пакетов, поиск версий, команда mise ls-remote python, выдала 911 вариантов для python.

Теперь можно настраивать пайплайны для наших разработчиков.

вернуться обратно в блог