Примерно вот так я впервые познакомился с snap
Примерно вот так я впервые познакомился с snap

Распространение приложений в линуксе - это боль. Причем в наше время цикл обновлений приложений все уменьшается и эта боль чувствуется все сильнее. В связи с этим появляются технологии вроде snap, flatpak, которые декларируют решение этих проблем. Некоторые дистрибутивы (я смотрю на тебя, Ubuntu) даже начинают довольно агрессивную политику по их внедрению. Однако, несмотря на то, что про сами эти технологии много говорят (и ещё больше жалуются), про то, как они работают написано довольно мало. Попробуем исправить это.

Disclaimer: в этой статье я не буду давать каких-то оценок, пытаться говорить что лучше. Только как это работает. Я предполагаю, что читатель знает что такое контейнеризация и как это примерно работает.

Далее я буду использовать слово "приложение" как конечный продукт, который используют пользователи (например firefox), а слово "зависимость" как "динамическая библиотека", .so файл, который нужен приложению (например libsqlite.so)

В чем, собственно, проблема?

А проблема в том, как приложение получает свои зависимости.

В мире windows победила идеология, где каждое приложение тащит свои зависимости с собой (за исключением совсем системных). Нередко можно видеть, что приложение кладет в свою папку какой-нибудь gl.dll, sqlite.dll и подобное.

В линуксовых дистрибутивах идеология немножко другая. Приложение - это конструктор из других более мелких программ или библиотек, устанавливаемых независимо друг от друга. Если разным приложениям требуется одна и та же зависимость, они её переиспользуют. Чтобы не устанавливать сотни пакетов зависимостей вручную, можно пользоваться пакетным менеджером, который сделает это за нас.

Такой подход имеет свои плюсы и минусы. Из плюсов:

  • если какой-нибудь QT используется сотней приложений, то он будет установлен не 100 раз, а только 1.

  • Если в каком-то пакете, например openssl, обнаружатся проблемы, то вы можете один раз обновить один пакет и вся система сразу будет пользоваться пропатченой версией, разработчикам конечных приложений не нужно делать вообще ничего.

  • Это хорошо сочетается с идеологией линукса "программа должна делать одну вещь и делать её хорошо".

Из минусов:

  • Многообразие дистрибутивов и следовательно форматов пакетов. Для поддержки всех версий нужно прилагать огромное количество усилий. Посмотрите только на такие плашки, сам факт того, что они существуют, несколько пугает.

  • А что делать, если двум приложениям требуются разные, несовместимые версии пакетов?

  • А что делать если приложению нужен пакет, который не предоставляется дистрибутивом?

  • А что делать авторам приложений, если они хотят выкатить новую фичу, но она опирается на версию другого пакета, которого ещё нет в каких-то дистрибутивах. Скорость инноваций сильно падает и в некоторых областях это очень критично.

  • Если дистрибутив обновляется (а вместе с ним все пакеты), нужно проверять работоспособность приложения. При этом хочется верить, что ментейнеры у проекта ещё есть. И да, вам будут присылать баг репорты на старые версии приложения в старых дистрибутивах, то есть нужно ещё поддерживать старые версии продукта, нельзя просто сказать "это починено в новой версии, обновитесь"

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

А ещё есть очень специфический тип приложений - игры. Их особенность в том, что они практически всегда closed source, после определенного времени вообще не обновляются, но при этом они активно используют железо, то есть им нужна серьезная поддержка со стороны ОС и драйверов. И как, спрашивается, нам можно запустить игру 10 летней давности на современном дистрибутиве, где все библиотеки уже уехали очень далеко?

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

Appimage

Appimage в целом похожа на portable exe в винде. Основная идея довольно проста: мы берем все зависимости приложения (кроме совсем системных), упаковываем их в файл в формате squashfs и добавляем специальный хедер, чтобы этот файл стал ELF - исполняемым файлом. Внутри этого хедера находится программа, которая при запуске файла монтирует этот squashfs во временную папку и оттуда вызывает бинарник нашего приложения.

AppImage file structure
AppImage file structure

Для сборки этого файла юзеру нужно создать специальную папку (AppDir) и положить в неё все, что нужно приложению для запуска: бинарник, .so файлы, иконки и т.п. AppDir должен содержать в корне запускаемый файл AppRun, который будет запускать непосредственно ваше приложение. В нем можно выставлять переменные окружения (в том числе для линкера, чтобы он смотрел не в корневые папки, а в наши) и входные параметры приложения. Для тривиальных вариантов AppRun может быть просто симлинком на сам бинарник. Для запаковки этой папки в AppImage файл есть официальный тул AppImageTool.

Вообще желательно, чтобы AppDir содержал ещё некоторые служебные файлы (.desktop, иконки) для интеграции с хостовой системой, подробнее можно прочитать тут

Если вы хотите посмотреть внутренности AppDir конкретного AppImage, можно запустить его с ключиком --appimage-mount. Он примонтирует squashfs и выплюнет в stdout путь к примонтированной папке, в которую можно залезть и посмотреть что там делается

❯ ./appimagetool-x86_64.AppImage --appimage-mount
/tmp/.mount_appimaGnR7L6

В целом все довольно просто. Ну почти. Автору пакета нужно следить, чтобы приложение было "relocatable", то есть чтобы оно могло запускаться из любого места и в нем не было захардкоженных абсолютных путей к зависимостям. Это касается как динамических библиотек, так и ресурсов типа картинок. Вот с этим может быть проблема, так как поколения разработчиков на линукс опирались на конкретные захардкоженные пути и вычистить это все может быть не так просто.

Ещё один момент - приложение монтируется в read only файловую систему. Это значит, что приложение не может обновлять само себя, нужны внешние утилиты. Интеграция в desktop environment возможна при установке внешнего сервиса

Flatpak

Flatpak как технология ставит перед собой более амбициозные задачи. Если AppImage - это просто средство упаковки и запуска приложения, то flatpak реализует также sandboxing и распространение.

При разговоре о flatpak нужно помнить, что он задумывался как контейнер именно для GUI приложений. Это повлияло на некоторые решения в дизайне.

В Flatpak вы точно также собираете свои зависимости с собой (кроме тех, что есть в базовом рантайме, о нем дальше), но запускается это все в изолированном контейнере (да, это больше похоже на то, что делает Docker).

Основные технологии, на которой основана flatpak

  • bubblewrap - rootless контейнеры

  • OSTree - система дистрибуции файлов. Идеологически похожа на гит: есть репозиторий, ветки между которыми можно переключаться. Но оптимизированно все для работы с бинарными файлами (изначально это вообще делалось для разворачивания рабочих операционных систем). При скачивании приложения вы по сути вычекиваете ветку из репозитория. Бонусом идет то, что если разные приложения (ветки) используют один и тот же файл, он копируется только 1 раз.

Сборка пакета осуществляется тулами flatpak. Для этого нужно описать сборку в yml файле - пример такого файла для telegram-desktop Посмотрим на верхушку этого файла

id: org.telegram.desktop
runtime: org.freedesktop.Platform
runtime-version: '21.08'
sdk: org.freedesktop.Sdk
command: telegram-desktop
rename-icon: telegram
finish-args:
  - --share=ipc
  - --share=network
  - --socket=x11
  - --socket=wayland
  - --socket=pulseaudio
  - --device=all
  - --own-name=org.mpris.MediaPlayer2.tdesktop
  - --talk-name=org.freedesktop.Notifications
  - --talk-name=org.freedesktop.ScreenSaver
  - --talk-name=org.gnome.Mutter.IdleMonitor
  - --talk-name=org.kde.StatusNotifierWatcher
  - --talk-name=com.canonical.AppMenu.Registrar
  - --talk-name=com.canonical.Unity
  - --talk-name=com.canonical.indicator.application
  - --talk-name=org.ayatana.indicator.application
  - --filesystem=xdg-download
  - --filesystem=xdg-run/pipewire-0
  - --filesystem=host:ro
  - --nofilesystem=~/.TelegramDesktop
  - --env=QT_PLUGIN_PATH=/app/lib64/plugins:/app/lib/plugins
  - --env=PATH=/app/lib/webview/bin:/app/bin:/usr/bin

Из него видно, что помимо всяких id продукта, мы выбираем "runtime" и что мы хотим использовать из хостовой системы.

Runtime предоставляет базовые системные зависимости, там есть D-Bus, GLib, Gtk3, PulseAudio, X11, Wayland и прочее. Есть 3 стандартных рантайма на flathub: Freedesktop, GNOME и KDE. Также есть разные версии рантайма, для вашего контейнера подключится именно та, которую указали. Также это значит, что скорее всего у вас на диске будет лежать несколько версий (а они не маленькие).

Далее указываются дополнительные привилегии: на ipc, сеть, файловую систему и т.п. По-умолчанию контейнер создается с минимальным количество прав. Он не может писать в файловую систему (кроме своей папки внутри $XDG_RUNTIME_DIR/app/$FLATPAK_ID), не может общаться с сетью, получать доступ к устройствам или другим процессам. Также у приложения усеченный список syscalls.

Для доступа к хосту можно или запросить привилегии, или использовать xdg-portals - специальное api, которое может запрашивать что-то в хостовом desktop environment. Например можно вызвать окошко выбора файла, код которого будет работать со стороны хоста. Также через порталы можно открывать ссылки, показывать нотификации, делать скриншоты и т.п. Поддержка порталов встроена в qt и gtk.

Далее в файле указываются шаги для сборки приложения и всех его зависимостей. Приложение собирается не на хосте, а внутри SDK (это то же самое что и runtime, но с дополнительными сборочными утилитами внутри). Также помимо runtime существуют "BaseApp", базовые приложение которые не нужно собирать и можно просто утянуть себе в контейнер. Обычно это большие фреймворки которые не хочется настраивать самому, наподобие Electron. Но это используется редко, сами авторы просят не создавать их без реальной необходимости.

Кстати говоря, при желании можно войти в контейнер и что-нибудь там поделать. Делается это командой

flatpak run --command=sh --devel <application-id>

(--devel позволяет запустить контейнер с расширенным рантаймом, в котором есть дебажные тулы типа gdb, strace и т.п.)

Подробнее про все это можно посмотреть в этом выступлении автора flatpak.

Snap

Snaps поставляются в виде .snap файлов (лежат в /var/lib/snapd/snaps), содержащие squashfs приложения. Чем-то похоже на appImage, но в отличии от него, все snap постоянно смонтированны в папку /snap (это на убунте, в других дистрибутивах папка может отличаться).

❯ mount | grep snap
/var/lib/snapd/snaps/bare_5.snap on /snap/bare/5 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core18_2344.snap on /snap/core18/2344 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/gnome-3-34-1804_72.snap on /snap/gnome-3-34-1804/72 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/gnome-3-34-1804_77.snap on /snap/gnome-3-34-1804/77 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/gnome-3-38-2004_99.snap on /snap/gnome-3-38-2004/99 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/snap-store_547.snap on /snap/snap-store/547 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/snap-store_558.snap on /snap/snap-store/558 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/gtk-common-themes_1519.snap on /snap/gtk-common-themes/1519 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/snapd_15534.snap on /snap/snapd/15534 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core20_1434.snap on /snap/core20/1434 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/snapcraft_7201.snap on /snap/snapcraft/7201 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core18_2409.snap on /snap/core18/2409 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/gtk-common-themes_1534.snap on /snap/gtk-common-themes/1534 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/gopls_885.snap on /snap/gopls/885 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/snapd_15904.snap on /snap/snapd/15904 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core20_1494.snap on /snap/core20/1494 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/core_13250.snap on /snap/core/13250 type squashfs (ro,nodev,relatime,x-gdu.hide)
/var/lib/snapd/snaps/hello-world_29.snap on /snap/hello-world/29 type squashfs (ro,nodev,relatime,x-gdu.hide)
tmpfs on /run/snapd/ns type tmpfs (rw,nosuid,nodev,noexec,relatime,size=802216k,mode=755,inode64)
nsfs on /run/snapd/ns/hello-world.mnt type nsfs (rw)
/var/lib/snapd/snaps/gnome-3-38-2004_106.snap on /snap/gnome-3-38-2004/106 type squashfs (ro,nodev,relatime,x-gdu.hide)

Ещё одно отличие от appImage - squashfs смонтированны через loop device, а не через fuse (поэтому lsblk выдается столько "мусора").

Давайте посмотрим в такую папку. В данном случае у меня установлен "hello-world" пакет версии 29. При наличии несколько версий, будет несколько копий дерева файлов. На "текущую" версию указывает ссылка current.

❯ tree /snap/hello-world
/snap/hello-world
├── 29
│   ├── bin
│   │   ├── echo
│   │   ├── env
│   │   ├── evil
│   │   └── sh
│   └── meta
│       ├── gui
│       │   └── icon.png
│       └── snap.yaml
└── current -> 29

Но предполагается, что пользователь в эти места не лезет, а запускает приложения из другого места - /snap/bin/, который обычно добавлен в $PATH. При этом видно, что бинарники в этой папке указывают просто на /usr/bin/snap.

~
❯ hello-world
Hello World!

~
❯ where hello-world
/snap/bin/hello-world

~
❯ ls -l /snap/bin/hello-world
lrwxrwxrwx 1 root root 13 мая 29 09:53 /snap/bin/hello-world -> /usr/bin/snap

Чтобы понять разницу, запустим hello-world.evil двумя способами

/snap/hello-world
❯ hello-world.evil
Hello Evil World!
This example demonstrates the app confinement
You should see a permission denied error next
/snap/hello-world/29/bin/evil: 9: /snap/hello-world/29/bin/evil: cannot create /var/tmp/myevil.txt: Permission denied

/snap/hello-world
❯ ./current/bin/evil
Hello Evil World!
This example demonstrates the app confinement
You should see a permission denied error next
If you see this line the confinement is not working correctly, please file a bug

/snap/hello-world
❯ cat /var/tmp/myevil.txt
Haha

И глянем в вывод dmesg

❯ dmesg
...

[348350.198906] audit: type=1400 audit(1653807204.078:105): apparmor="DENIED" operation="capable" profile="/snap/snapd/15904/usr/lib/snapd/snap-confine" pid=627336 comm="snap-confine" capability=4  capname="fsetid"
[348354.281614] audit: type=1400 audit(1653807208.162:106): apparmor="DENIED" operation="mknod" profile="snap.hello-world.evil" name="/var/tmp/myevil.txt" pid=627395 comm="evil" requested_mask="c" denied_mask="c" fsuid=1000 ouid=1000

Это говорит нам о том, что при запуске snap понимает какое приложение хотели
запустить через него, и запускает его, но только через профайл apparmor (его можно посмотреть в /var/lib/snapd/apparmor/profiles/snap.hello-world.evil). То есть если в flatpak sandboxing нашего приложение делалось через namespaces (контейнеризацию), то тут оно реализовано через правила apparmor. Посмотреть какие права есть у приложение можно через его манифест. В hello-world никаких дополнительных прав нет

❯ cat /snap/hello-world/current/meta/snap.yaml
name: hello-world
version: 6.4
architectures: [ all ]
summary: The 'hello-world' of snaps
description: |
    This is a simple snap example that includes a few interesting binaries
    to demonstrate snaps and their confinement.
    * hello-world.env  - dump the env of commands run inside app sandbox
    * hello-world.evil - show how snappy sandboxes binaries
    * hello-world.sh   - enter interactive shell that runs in app sandbox
    * hello-world      - simply output text
apps:
 env:
   command: bin/env
 evil:
   command: bin/evil
 sh:
   command: bin/sh
 hello-world:
   command: bin/echo

Для сравнения можно посмотреть snap.yaml от firefox. В терминологии snap приложение, которому нужны дополнительные возможности, должны поставить плагины (plugs)

/snap/firefox/current/meta/snap.yaml
❯ cat /snap/firefox/current/meta/snap.yaml
name: firefox
version: 101.0.1-1
summary: Mozilla Firefox web browser
description: Firefox is a powerful, extensible web browser with support for modern
  web application technologies.
apps:
  firefox:
    command: firefox.launcher
    environment:
      GTK_USE_PORTAL: 1
      HOME: $SNAP_USER_COMMON
      PIPEWIRE_CONFIG_NAME: $SNAP/usr/share/pipewire/pipewire.conf
      PIPEWIRE_MODULE_DIR: $SNAP/usr/lib/x86_64-linux-gnu/pipewire-0.3
      SPA_PLUGIN_DIR: $SNAP/usr/lib/x86_64-linux-gnu/spa-0.2
    slots:
    - dbus-daemon
    - mpris
    plugs:
    - desktop
    - desktop-legacy
    - gsettings
    - opengl
    - wayland
    - x11
    - audio-playback
    - audio-record
    - avahi-observe
    - browser-sandbox
    - camera
    - cups-control
    - hardware-observe
    - home
    - joystick
    - network
    - network-observe
    - removable-media
    - screen-inhibit-control
    - system-packages-doc
    - u2f-devices
    - unity7
    - upower-observe
    command-chain:
    - snap/command-chain/snapcraft-runner
    - snap/command-chain/desktop-launch
  geckodriver:
    command: usr/lib/firefox/geckodriver
    plugs:
    - desktop
    - desktop-legacy
    - gsettings
    - opengl
    - wayland
    - x11
    - network-bind
    - audio-playback
    - audio-record
    - avahi-observe
    - browser-sandbox
    - camera
    - cups-control
    - hardware-observe
    - home
    - joystick
    - network
    - network-observe
    - removable-media
    - screen-inhibit-control
    - system-packages-doc
    - u2f-devices
    - unity7
    - upower-observe
    slots:
    - dbus-daemon
    - mpris
    command-chain:
    - snap/command-chain/snapcraft-runner
    - snap/command-chain/desktop-launch
architectures:
- amd64
assumes:
- command-chain
- snapd2.43
base: core20
confinement: strict
environment:
  SNAP_DESKTOP_RUNTIME: $SNAP/gnome-platform
  GTK_USE_PORTAL: '1'
grade: stable
hooks:
  configure:
    command-chain:
    - snap/command-chain/hooks-configure-desktop
    plugs:
    - desktop
layout:
  /usr/share/libdrm:
    bind: $SNAP/gnome-platform/usr/share/libdrm
  /usr/lib/x86_64-linux-gnu/webkit2gtk-4.0:
    bind: $SNAP/gnome-platform/usr/lib/x86_64-linux-gnu/webkit2gtk-4.0
  /usr/share/xml/iso-codes:
    bind: $SNAP/gnome-platform/usr/share/xml/iso-codes
plugs:
  browser-sandbox:
    interface: browser-support
    allow-sandbox: true
  desktop:
    mount-host-font-cache: false
  dot-mozilla-firefox:
    interface: personal-files
    read:
    - $HOME/.mozilla/firefox
  etc-firefox-policies:
    interface: system-files
    read:
    - /etc/firefox/policies
  gnome-3-38-2004:
    interface: content
    target: $SNAP/gnome-platform
    default-provider: gnome-3-38-2004
  gtk-3-themes:
    interface: content
    target: $SNAP/data-dir/themes
    default-provider: gtk-common-themes
  icon-themes:
    interface: content
    target: $SNAP/data-dir/icons
    default-provider: gtk-common-themes
  sound-themes:
    interface: content
    target: $SNAP/data-dir/sounds
    default-provider: gtk-common-themes
slots:
  dbus-daemon:
    interface: dbus
    bus: session
    name: org.mozilla.firefox

Для некоторых приложений (например для VS Code) используется --classic ограничения (вместо дефолтного --strict), тогда он будет запущен без sandboxing'а и ему будет доступны все функции хоста.

Layouts

Хоть snap не использует полноценные контейнеры, он использует mount namespace для решения проблемы захардкоженных путей типа /etc/, /usr/share/ и т.п. Я упоминал об этой проблеме в разделе про AppImage, там она решается только переписыванием приложения или патчем бинарника (абсолютные пути заменяются на относительные).

В snap есть механизм layout, в котором можно отобразить файлы и папки из доступных snap'у путей в глобальную иерархию. Это настраивается в том же snapcraft.yaml, например

layout:
  /var/lib/foo:
    bind: $SNAP_DATA/var/lib/foo
  /usr/share/foo:
    bind: $SNAP/usr/share/foo
  /etc/foo.conf:
    bind-file: $SNAP_DATA/etc/foo.conf

Почему холодный старт долгий?

Snap печально известен тем, что его приложения долго запускаются. Я пытался разобраться почему (не залезая в сами исходники), но видимо это не такой простой вопрос. Кажется, что тут нет одного узкого места, скорее все части системы добавляют задержку, которая потом аккумулируется. Вот несколько основных причин тормозов

Внутренности snap заархивированы, соответственно при запуске их нужно разархивировать. Судя по всему команда snap выбрала неудачный метод сжатия (в отличие от AppImage), который лучше сжимает, но работает долго. Вот тут они пишут, как попытались это исправить.

Также при старте нужно раскрутить apparmor и проставить все указанные layout, то есть сделать правильные mount namespace. Если layout очень развесистый, это действительно может тормозить запуск, о чем они сами пишут в документации.

По поводу snap нужно также понимать, что изначально эта технология была сделана для IoT, а потом её натянули на десктопы. Насколько была удачна идея, покажет история. Вот тут пользователем popeydc составлен довольно неплохой список почему люди не очень любят snap.

Заключительные мысли

Судя по тенденциям, эти технологии придут в нашу обычную жизнь и мы от этого никуда не денемся. Можно по разному относится к этому, но знать как это работает нужно.

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

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


  1. IAMBIRD
    26.06.2022 19:18
    +4

    Что думаете про Haiku-шные hpkg? Выглядят продуманно.


    1. myxo Автор
      26.06.2022 22:44
      +1

      Если честно, впервые о них услышал. Выглядит как appImage, но с довольно крутой поддержкой прям на уровне ОС. Выглядит действительно интересно.


      1. Self_Perfection
        28.06.2022 10:17

        На хабре даже есть перевод цикла обзоров HaikuOS от создателя AppImage) Вот самая релевантная статья https://habr.com/ru/company/southbridge/blog/466301/


  1. iwram
    26.06.2022 19:34
    +1

    в snap есть еще такая проблема, например с vlc, установил, а при попытки запустить видео с другого примонтированного (с luks шифрованием) диска ошибка.

    Помнится даже microsoft анонсировала запуск всех приложений в песочнице и использованием контейнеризации, но что то сейчас тишина. Поэтому думаю, что вскоре появятся новые рантаймы и запуск приложений будет идти в эту сторону.


    1. myxo Автор
      26.06.2022 22:46

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


  1. DistortNeo
    26.06.2022 19:46
    +1

    Сжатие образов в snap — это не только минус (время на распаковку), но и плюс.
    Например, с продуктами JetBrains пользоваться snap очень удобно, а каких-либо проблем с производительностью я не заметил. Зато сразу бросается в глаза экономия места (от полгига до гига на одну IDE вместо несколько гигов + многих тысяч файлов) и супербыстрая установка/обновление.


    1. kovserg
      26.06.2022 21:00
      +3

      Зато сразу бросается в глаза экономия места

      Это когда любая утилита тянет за собо 200мб runtime-а. Если пак один то да, а если сотни то ситуация обратная.


    1. staticmain
      26.06.2022 21:52
      +4

       Зато сразу бросается в глаза экономия места 

      Да, он настолько значительная, что Ubuntu пришлось отказаться от LiveCD и перейти на LiveDVD и LiveUSB потому что всего несколько приложений типа калькулятора в snap отжирали столько места в установочном образе что остальным пришлось потесниться.


      1. DistortNeo
        26.06.2022 22:18
        +2

        У всякого инструмента должно быть оптимальное применение.
        Пихать в snap офисный пакет, жирную IDE — это хорошо.
        А вот пихать мелкие приложения — зло.


    1. Spym
      26.06.2022 22:28
      -4

      Зато сразу бросается в глаза экономия места

      Я понимаю, почему бизнесам в области хранения данных есть дело до места. Но вам-то, как конечному пользователю, какая разница? Диски меньше чем на полтерабайта сегодня большая редкость. Ну съест ваш пакет гигабайт-другой, ну десять, на что это повлияет? Стоимость хранения данных продолжает падать, в то время как вычислительные мощности практически не растут последнее десятилетие (если не считать прогресса в области SIMD систем, но это таки другое), так что размен производительности на экономию места в общем случае выглядит неразумным.


      1. staticmain
        26.06.2022 23:50
        +5

        Ну съест ваш пакет гигабайт-другой, ну десять, на что это повлияет? Стоимость хранения данных продолжает падать

        Дада, поэтому производители всё еще делают ноутбуки с 256 ГБ SSD


    1. 0x131315
      26.06.2022 23:17

      Экономия места - она за счёт сжатия. Отсюда быстрая установка и обновление, но медленный старт и работа. И по-моему основное - это как раз работа.

      Мы берём современные nvme не для того, чтобы экономить место, а чтобы софт максимально быстро работал с файлами. И с этой точки зрения сжатие - не самая умная идея: оно тормозит работу с ФС, и дополнительно нагружает процессор.

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

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

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


      1. DistortNeo
        27.06.2022 00:09
        +1

        Ну да, копирование с NVMe накопителя происходит немного быстрее, чем из snap-образа:


        # sync; echo 3 > /proc/sys/vm/drop_caches
        /var/lib/snapd/snap/rider$ time cp -r current/ /tmp/1
        
        real    0m2,586s
        user    0m0,013s
        sys     0m2,137s
        
        # sync; echo 3 > /proc/sys/vm/drop_caches
        /home/andrew/tmp/rider$ time cp -r current/ /tmp/1
        
        real    0m1,881s
        user    0m0,003s
        sys     0m1,041s

        Целых 2.5 секунды в SNAP против 1.8 секунд напрямую для 3 гигабайт данных.
        Эта разница настолько микроскопическая, что увидеть её в реальности невозможно.


        Более того, я не замечаю разницы даже между SATA II SSD и NVMe, потому что мало уметь читать данные с бешеной скоростью, надо их уметь с такой скоростью обрабатывать. Если бы загрузка IDE заключалась в простом копировании файлов в память, то она бы стартовала за полсекунды.


        Мы берём современные nvme не для того, чтобы экономить место

        Ну да, то же самое можно и про оперативную память сказать. JetBrain ToolBox съел полгига памяти — да не жалко, у нас же их целых 64 гига.


        Так что если есть возможность сэкономить 10 гигов просто за счёт эффективного сжатия, не вижу проблем. Есть возможность отказаться от прожорливого ToolBox — тоже хорошо.


        P.S. Лично я NVMe брал, в первую очередь, ради экономии места, причём физического. Маленькая плашка занимает сильно меньше места в корпусе компьютера, чем диск формата 2.5 или вообще 3.5.


  1. AcckiyGerman
    26.06.2022 21:18
    +1

    @myxoсистема установки приложений в MacOS ведь тоже работает с примонтированными образами, как и snap. Знаете ли вы плюсы и минусы их подхода, и пробовал ли кто-то использовать их наработки в линукс?


    1. myxo Автор
      26.06.2022 22:48

      Если честно, ничего не знаю про то как это работает в Mac


    1. Xazzzi
      27.06.2022 05:36
      +1

      Нету там особых наработок, установка приложения это по сути копирование его папки с образа в Applications.


  1. funca
    26.06.2022 22:24
    +6

    Почему холодный старт долгий?

    Линукс умеет разделять в памяти код общих библиотек между приложениями, загружая их с диска в память только один раз при помощи mmap. Когда условный Firefox установлен deb пакетом, то к моменту запуска приложения большая часть околосистемных зависимостей уже находятся в памяти и фактически подгружается лишь небольшой кусок.

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

    В прошлом году Canonical изобразили жест отчаяния, сделав в Ubuntu установку FF из Snap безальтернативной (а может это заговор?). На что мозилле пришлось спешно рассказывать пользователям как устанавливать браузер с помощью deb из PPA. В январе была статья, что Canonical собираются в очередной раз все сломать починить https://ubuntu.com/blog/the-future-of-snapcraft. Но результатов я ещё не видел.

    У flatpack в этом плане все гораздо лучше, поскольку они распаковывают приложение на файловой системе, что позволяет задействовать имеющиеся механизмы для дедупликации.


    1. tzlom
      26.06.2022 23:07
      +2

      Дедупликация работает только если libfoobar.so.1.0 загружается множеством приложений. Если же каждое привозит свою копию - дедупликации не будет.

      flatpack предлагает т.н рантаймы которые будут переиспользовать библиотеки, но только если используется одинаковая версия рантайма.

      К сожалению все эти системы все равно повязаны на способность взаимодействовать с конкретными системными библиотеками, и что делать тем приложениям которые например повязаны на старые версии видеодров пока не ясно.


    1. dartraiden
      26.06.2022 23:16
      +3

      На что мозилле пришлось спешно рассказывать пользователям как устанавливать браузер с помощью deb из PPA.
      Мозилла ничего против snap-пакета не имеет. Более того, этот snap-пакет именно Мозилла и собирает.

      «Спешно рассказывает» некий Joey Sneddon из сетевого издания OMG! Ubuntu!


    1. lazyest
      27.06.2022 15:44
      +2

      максимально точное обьяснение, почему никаких снапов и аппимеджей не будет на моём персональном компьютере по возможности никогда.

      флатпак такое же зло что и первые два. И вообще экономить на (однократном) создании системы нормальных билдов под пяток популярных архитектур и дистров за счёт многапользователей впоследствии - плохая идея.


  1. Nasreddin_Hodja
    26.06.2022 22:37
    +6

    Из этих трёх я предпочёл бы AppImage за его простоту. Snap и Flatpak какие-то overengineered вундервафли, излишне всё усложняющие. А sandboxing должен быть реализован отдельным инструментом, без привязки к пакетному менеджеру.

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

    Кстати, идея не нова, много лет уже был проект Zero Install https://0install.net/ и по мне так реализация получше Snap и Flatpak. Но за ним не стоит корпорация, поэтому без агрессивного навязывания известен только в узких кругах.

    Ну и ещё по своему проблему решают в дистрах Nix, Gobolinux (не в курсе жив ли ещё) и прочих.


    1. funca
      26.06.2022 22:59

      Жизнь всем этим начинаниям сильно портит то, что во многих дистрибутивах часть библиотек идут со специфичными модификациями. Поэтому даже при номинальном совпадении версий, GUI приложений, собранных в разных условиях, визуально может заметно отличаться - начиная с банального рендеринга шрифтов, нюансов тем, поддержки кодеков и т.п. AppImage это не решает ни как. tgz либо так же (при статической линковке), либо уповают на проведение что все звёзды сойдутся и приложение вообще запустится (при динамической). Flatpack собирают приложения в контролируемой среде, что обеспечивает консистентность между ними хотя бы внутри экосистемы.


    1. 0x131315
      26.06.2022 23:21

      Nix кстати на ubuntu пользуюсь, там бывают пакеты, которые сложно найти в других местах.


  1. DaneSoul
    26.06.2022 22:58

    Спасибо за статью, но хотелось бы еще в финале какое-то обобщенное сравнение с критериями практического выбора для пользователя.
    Вот нужно мне приложение Х для Linux, оно распространяется разработчиком в виде нескольких разных таких упакованных вариантов — какой мне лучше выбирать и от чего это зависит?


    1. pfg21
      27.06.2022 00:43

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

      и как обычно, ответ стандартный: поставить все доступные - поробовать и оставить понравившийся :)


  1. 13werwolf13
    27.06.2022 06:58
    +1

    Многообразие дистрибутивов и следовательно форматов пакетов. Для поддержки всех версий нужно прилагать огромное количество усилий. Посмотрите только на такие плашки, сам факт того, что они существуют, несколько пугает.

    достаточно использовать сборщик который умеет сам из одной инструкции собирать пакеты для 100500 версий дистрибутивов и пм.

    А что делать, если двум приложениям требуются разные, несовместимые версии пакетов?

    бить разработчиков использующих версии либ который появились "вот только вчера)

    А что делать если приложению нужен пакет, который не предоставляется дистрибутивом?

    а как простите пакет с неудовлетворённой зависимостью попал в репы? в вашем дистрибутиве мейнтейнеры предпочитают мефедрон?

    А что делать авторам приложений, если они хотят выкатить новую фичу, но она опирается на версию другого пакета, которого ещё нет в каких-то дистрибутивах. Скорость инноваций сильно падает и в некоторых областях это очень критично.

    всё просто, покласть нужную версию нужной либы в отдельное репо с отдельным именем. уживались же не один год python2 и python3 вместе в куче разных дистрибутивов (и да есть ещё куча примеров).

    Если дистрибутив обновляется (а вместе с ним все пакеты), нужно проверять работоспособность приложения. При этом хочется верить, что ментейнеры у проекта ещё есть. И да, вам будут присылать баг репорты на старые версии приложения в старых дистрибутивах, то есть нужно ещё поддерживать старые версии продукта, нельзя просто сказать "это починено в новой версии, обновитесь"

    чтобы работало нужно работать. это претензия?

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

    см предыдущий пункт

    вообще я не понимаю этого - чтобы избавиться от зоопарка проблем породили новый зоопарк с ещё большим количеством проблем.
    мегать несколько способов поставки ПО в одной системе это выстрел себе в ногу. хотите мешать в одном дистре пакеты из реп и снапофлатпаки будьте готовы к геморрою.


    1. kovserg
      27.06.2022 12:10

      Потому что они пытаются решить проблему не в месте её возникновения, а с помощью костылей. Проблема в динамических библиотеках, их формат способствует их появлению.

      ps: По поводу снапов им подобным, есть же статическая линковка, что мешает? И на крайняк есть LD_LIBRARY_PATH, и --rpath=$ORIGIN/libs


      1. 13werwolf13
        27.06.2022 13:07
        -1

        ps: По поводу снапов им подобным, есть же статическая линковка, что мешает? И на крайняк есть LD_LIBRARY_PATH, и --rpath=$ORIGIN/libs

        статическая линковка тоже костыль, причём костыль влияющий как на вес бинаря так и на скорость холодного старта (в случае go и rust не так сильно конечно, но ведь не весь софт на них написан).


        1. kovserg
          27.06.2022 15:34

          А разве бинарь сразу весь загружается в память?


          1. 13werwolf13
            27.06.2022 16:02

            А разве дело только в этом?


      1. lxfrmn
        27.06.2022 13:08
        -1

        Проблема в динамических библиотеках

        В самих динамических библиотеках нет проблемы, проблема, как обычно бывает, в абсолютизации концепции. У всякого подхода есть границы применимости, но адепты "построим ВСЮ систему на централизованных репозиториях и разделяемых библиотеках" почему-то считаются светочами мысли.


        1. kovserg
          27.06.2022 15:18

          Нет проблема именно в том что динамические библиотеки нельзя проверить на избыточность, вынуть часть кода или убрать, нет версионности как в системах контроля версий и бывает что отсутствует обратной сомвестимость (как минимум она не контролируется) помимо этого нет возможности иметь код под разные архитектуры одновременно (например SSE2, SSE4, AVX, AVX2 ...), не говоря уже о получении информации что именно этой библиотеке надо что бы всё завелось и что там вообще есть, что используется, а что нет. Они динамические только в смысле загрузки, но не в смысле библиотеки.

          Аналогия: Представьте пришли вы в библиотеку взять несколько книг, а вам говорят надо брать весь фонд иначе никак. Даже выписать пару страниц из книги нельзя, только целиком библиотеку берите. А некоторые книги еще и в разных библиотеках.


    1. lxfrmn
      27.06.2022 13:05

      хотите мешать в одном дистре пакеты из реп и снапофлатпаки будьте готовы к геморрою.

      Нет никакого геморроя, потому что традиционные репы + FHS никак не пересекаются с Flatpak. И практика тоже это показывает.


      1. 13werwolf13
        27.06.2022 13:12
        +1

        желаю вам и дальше не сталкиваться ну или хотя бы не замечать.


  1. fishHook
    27.06.2022 10:30
    +6

    А что делать, если двум приложениям требуются разные, несовместимые версии пакетов?


    Объясните, пожалуйста, тупому, а почему зависимости нельзя просто версионировать? Допустим, приложению Калькулятор нужна библиотека libabc версии 0.25, а для приложения Календарь libabc версии 0.31. Ну и бога ради, пусть они обе одновременно присутствуют в соответствующих подкаталогах. Пакетный менеджер при установке пакета пишет в какой-нибудь реестр, какая приложенька зависит от какой версии, а ядро при линковке обращается к этому реестру и выбирает нужную версию. При удалении пакета можно пройтись по реестру и вычислить, нужна ли зависимость какому-то другому пакету, и если не нужна, то она удаляется. То же самое при обновлении - в реестр записали новые зависимости и удалили старые, если они больше никому не нужны. Для замособранных программ, которых нет в реестре, по дефолту вибирается самая свежая версия библиотеки.


    1. NecroRomnt
      27.06.2022 11:56

      Как по мне и реестр не нужен. Кидать эти библиотеки в те же папки, но указывать версии с разными подробностями: lib.so.1.2.3, lib.so.2.1.3, lib.so.2.2.4, lib.so.1 -> lib.1.2.3, lib.so.2 -> lib.2.2.4. lib.so.1.2 -> lib.so.1.2.3, lib.so.2.2 -> lib.so.2.2.4.
      Менеджеру пакетов только устанавливать ссылки на библиотеки без указания минорной версии и патча.

      Почему так не делается непонятно. Наверняка есть какая-то весомая причина.


      1. staticmain
        27.06.2022 13:33
        +1

        Потому что версии библиотек устанавливаются тремя уровнями (-> = симлинк)

        lib.so -> lib.so.2
        lib.so.2 -> lib.so.2.8
        lib.so.2.8 -> lib.so.2.8.1
        lib.so.2.8.1

        Когда вы установите библиотеку более свежей версии, то появится такая цепочка:

        lib.so -> lib.so.2
        lib.so.2 -> lib.so.2.9
        lib.so.2.9 -> lib.so.2.9.2
        lib.so.2.9.2
        
        lib.so.2.8 -> lib.so.2.8.1
        lib.so.2.8.1

        Т.е. lib.so.2 стала указывать на lib.so.2.9 вместо 2.8. А теперь заставьте всех программистов в мире не ломать обратную совместимость на минорном релизе.

        При этом нельзя чтобы все приложения линковались с X.Y.Z версией, потому что иначе вам придется пересобирать всё что там по цепочке вверх при обновлении версии билда.


        1. NecroRomnt
          27.06.2022 14:11
          +1

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

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

          В обоих случаях есть 2 решения:

          1. Рекомендовать разработчикам не ломать обратную совместимость в рамках мажорной версии или повысить мажорную версию.

          2. Внутри дистрибутива вести своё сквозное версионирование библиотек.

          Да, оба варианта сопряжены с некоторыми сложностями, но разве эти сложности сопоставимы со сложностью приложений описанных в статье?


          1. staticmain
            27.06.2022 15:39
            +3

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

            Заставить разработчиков не ломать совместимость сложнее чем переписать весь софт в linux с нуля. Потому что всегда найдется тот, который это проигнорирует. Или перейдет на релизы 234, 235, 236 вместо 2.3.4, 2.3.5

            1. Внутри дистрибутива вести своё сквозное версионирование библиотек.

            А для этого нужно выделить армию разработчиков, которые будут проверять все библиотеки в репозиториях на предмет изменения кодовой базы, мастерски определять что поменялось, затрагивает ли это совместимость. И сквозное версионирование никак не поможет, потому что линковать приложение нужно с "какой-то" версией. Если это будет одна сквозная цифра, то вы не сможете обновить условный openssl 2.4.8 до 2.4.9 без того, чтобы не перекомпилировать весь софт в системе который её использует (потому что ваша сквозная нумерация сменилась с openssl.56 на openssl.57


  1. lxfrmn
    27.06.2022 11:24

    AppImage хорош для игр и всякого специфического софта, которому не нужно плотное сопровождение со стороны мантейнеров.

    Flatpak — ну, норм. Вижу его репозитории много всякого софта, которого нет в дистрибутивных репах и даже AUR, так что его полезность положительна. Однако есть пара проблем: каталог со служебными файлами софтины (настройки, кэши, базы) лежит не в хомяке, а где-то в дебрях иерархии flatpak, плюс ещё бывает, запустишь программу из флатпака, а она вместо нормальной отрисовки шрифтов — пилой по глазам.

    Snap возненавидел за первые несколько минут использования за его тормоза и загаживание mount. Ужасное поделие. Надеюсь, умрёт в пользу Flatpak.


  1. smarttowel0
    27.06.2022 13:42

    Ещё один момент - приложение монтируется в read only файловую систему.
    Это значит, что приложение не может обновлять само себя, нужны внешние утилиты.

    Не совсем так. Для AppImage есть https://github.com/AppImage/AppImageUpdate, который можно использовать как библиотеку для self-update.


    1. myxo Автор
      27.06.2022 20:52

      Но ведь прямо в тексте, который вы цитируете, присутствует именно эта же ссылка =)
      Я имел в виду, что некоторые приложения (например тот же телеграмм) умеют самостоятельно выкачивать свои обновления и заменять свой бинарник. С AppImage же нужна именно внешняя тула, которая будет обновлять .appimage файл.

      Ну или учить приложение, что оно может быть AppImage'ом и передавать ему путь к .appimage файлу, но это такое себе…


      1. smarttowel0
        28.06.2022 12:46

        С помощью этой библиотеки (не внешней утилиты) можно выкачивать свои обновления и заменять свой бинарник.


  1. Revertis
    27.06.2022 19:08

    Ага, ясно. Самое главное в статье:

    Распространение приложений в линуксе - это боль. Причем в наше время цикл обновлений приложений все уменьшается и эта боль чувствуется все сильнее.


    1. lxfrmn
      27.06.2022 22:14
      +1

      Оно так и есть ???? У меня есть знакомый, который под винду накалякал утилиту ещё в эпоху ХР. Почти сразу он на неё забил, но утилита свои задачи выполняет до сих пор. И пусть она не мегапопулярна, но десятки тысяч людей все эти годы благодаря ей получали решение своей проблемы. На винде. Что было бы, вздумай он сделать такую же утилиту для Linux и забить? Уже через пару-тройку лет она перестала бы собираться и/или запускаться.

      Ну и зачем производелям софта нужен такой головняк? Даже крупные конторы с сомнением смотрят на эту шизофреническую спецолимпиаду под названием "удержи своё Linux-ПО на плаву", а мелким конторам и отдельным энтузиастам это и подавно не нужно.


      1. Revertis
        27.06.2022 22:40

        Полностью с вами согласен. В студенческие времена тоже много утилит на Дельфи писал, и они работают до сих пор. А вот под линукс надо раскорячиваться очень сильно. Благо, есть Rust, который можно собрать под musl, чтобы хотя бы не зависеть от кучи разных версий glibc.


        1. kovserg
          27.06.2022 23:12
          +1

          А что мешало собирать с -static-libgcc -static-libstdc++


          1. Revertis
            27.06.2022 23:52
            -1

            Религия линукса :) В статье же написано, что выбран другой путь - экономия на спичках места, которое занимают либы.


      1. legolegs
        28.06.2022 22:41
        +1

        Что было бы, вздумай он сделать такую же утилиту для Linux и забить?

        У меня на сравнительно свежей федоре стоит и работает бинарный пакет Альтлинукса 2005 года сборки 2010 года установки. И ещё что-то с Мандривы 2011 года.


  1. ilmarin77
    28.06.2022 03:57

    snap плохо работает, когда надо работать с данным на nfs shares, и всё совсем фигово, когда /home тоже на nfs.


  1. x2v0
    28.06.2022 07:56

    Что господа думают об https://spack.io/?


  1. StraNNicK
    28.06.2022 11:32

    Блин, проголосовал неправильно. Выбрал "Репозиторий моего любимого дистрибутива", тогда как надо было ткнуть и в snap (да, у меня Ubuntu и, нет, я не выпиливал из неё ВЕСЬ snap), и в AppImage.

    AppImage удобен когда хочешь просто посмотреть на приложение, но не заморачиваться с добавлением PPA и установкой всех его зависимостей. Например Cool-Retro-Term. Забавная софтинка, но собирать её руками и потом поддерживать? Проще скачать один файл.

    snap-ы выглядят как-то странно, но тем не менее, joplin и microk8s (ну и ещё всякое) я ставил из них.
    Из тех же соображений, что и AppImage, но с возможностью автоматического обновления.

    В целом, если всё это не навязывается (привет FF в Ubuntu), использовать можно, иногда даже удобно.