From d7d7e2105f2e2832417d1a38d225e3e7eb39ec97 Mon Sep 17 00:00:00 2001 From: zuevav Date: Thu, 24 Sep 2026 19:46:04 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=80=D0=B0=D0=B7=20Ubuntu:=20?= =?UTF-8?q?=D1=81=D0=BB=D0=BE=D0=B9=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B9=20(full-upgrade=20+=20=D1=8F=D0=B4?= =?UTF-8?q?=D1=80=D0=BE)=20=D0=B8=20=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D1=8B?= =?UTF-8?q?=20bj=20=D0=BD=D0=B0=20=D0=B4=D0=B8=D1=81=D0=BA=D0=B5=20?= =?UTF-8?q?=E2=80=94=20=D1=83=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D0=BE=D1=87=D1=82=D0=B8=20=D0=BD=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=B0=D1=87=D0=B0=D0=B5=D1=82=20=D0=B8=D0=B7=20=D1=81=D0=B5?= =?UTF-8?q?=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - deploy/iso/build-ubuntu-updates-layer.sh: слой ubuntu-server-minimal.ubuntu-server.bj.squashfs поверх «Ubuntu Server» (оригинальные слои и среда установщика не меняются) + debs/ для bj-installer; - build-iso-ubuntu.sh LAYER=<каталог>: слой — источник установки по умолчанию, старые ядро HWE и прошивки из репозитория образа убираются; - bj-installer: пакеты из /var/lib/bj/debs ставятся с диска, иначе — из репозитория. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi --- cmd/bj-installer/main.go | 12 ++++ deploy/iso/build-iso-ubuntu.sh | 47 ++++++++++++++-- deploy/iso/build-ubuntu-updates-layer.sh | 72 ++++++++++++++++++++++++ deploy/iso/ubuntu/user-data.tmpl | 1 + 4 files changed, 128 insertions(+), 4 deletions(-) create mode 100755 deploy/iso/build-ubuntu-updates-layer.sh diff --git a/cmd/bj-installer/main.go b/cmd/bj-installer/main.go index 31271ce..67ea4c8 100644 --- a/cmd/bj-installer/main.go +++ b/cmd/bj-installer/main.go @@ -259,6 +259,15 @@ func memTotal() uint64 { func (in *installer) stepPackages() (string, error) { pkgs := []string{"ca-certificates", "curl", "postgresql", "postgresql-client", "default-jre-headless", "pcscd", "python3"} env := append(os.Environ(), "DEBIAN_FRONTEND=noninteractive") + // Образ кладёт пакеты со всеми зависимостями в /var/lib/bj/debs — ставим с диска, без сети. + // Не вышло (другая система, не хватает зависимости) — обычная установка из репозитория. + if debs, _ := filepath.Glob(in.P(debsDir) + "/*.deb"); len(debs) > 0 && in.root == "" { + args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, debs...) + if _, err := runEnv(env, "apt-get", append(args, pkgs...)...); err == nil { + _ = os.RemoveAll(in.P(debsDir)) + return strings.Join(pkgs, ", ") + " (с образа, без загрузки)", nil + } + } if out, err := runEnv(env, "apt-get", "update", "-qq"); err != nil { return "", fmt.Errorf("apt-get update: %v\n%s", err, tail(out, 8)) } @@ -682,6 +691,9 @@ func (in *installer) componentsFromBundle() (string, error) { return detail, nil } +// debsDir — системные пакеты для bj (PostgreSQL, Java, pcscd…), положенные образом ISO. +const debsDir = "/var/lib/bj/debs" + // validataDir — пакеты «Валидата Клиент L», положенные образом ISO (zpki, zsdk и совместимая libldap-2.4-2). const validataDir = "/var/lib/bj/validata" diff --git a/deploy/iso/build-iso-ubuntu.sh b/deploy/iso/build-iso-ubuntu.sh index ce1d14b..e4702b4 100755 --- a/deploy/iso/build-iso-ubuntu.sh +++ b/deploy/iso/build-iso-ubuntu.sh @@ -3,6 +3,9 @@ # # deploy/iso/build-iso-ubuntu.sh <версия> <каталог релиза> <каталог Валидаты> [канал] [выходной.iso] # +# LAYER=<каталог> — слой обновлений Ubuntu и пакеты bj из build-ubuntu-updates-layer.sh: установка +# ставит систему уже с обновлениями и не качает PostgreSQL/Java из сети. Без LAYER — как в Ubuntu 22.04.5. +# # Каталог релиза — dist с подписанным manifest.json (в образ кладётся ядро bj: установка без ключа продукта). # Каталог Валидаты — zpki/zsdk (ПК «Валидата Клиент L» для Linux, fs.moex.com ClientL_Other.zip) # и libldap-2.4-2 из Ubuntu 20.04 (нужна zpki, в 22.04 её нет). Ubuntu 22.04 — ОС, на которой @@ -35,6 +38,36 @@ FILES=$(python3 -c 'import json,sys,base64;s=json.load(open(sys.argv[1]));m=json [ -n "$FILES" ] || { echo "в релизе нет компонентов ядра"; exit 1; } for f in $FILES; do cp "$BUNDLE/$f" "$WORK/bj/bundle/"; done +# слой обновлений Ubuntu (необязательно) +LAYER=${LAYER:-} +LNAME=ubuntu-server-minimal.ubuntu-server.bj +LMAP="" +LRM="" +if [ -n "$LAYER" ]; then + for f in $LNAME.squashfs $LNAME.size $LNAME.manifest; do [ -s "$LAYER/$f" ] || { echo "в $LAYER нет $f"; exit 1; }; done + mkdir -p "$WORK/bj/debs"; cp "$LAYER"/debs/*.deb "$WORK/bj/debs/" + # источник установки «Ubuntu Server + обновления Bridge&Join» — по умолчанию; слои minimal и + # ubuntu-server под ним установщик подключает сам по цепочке имён + xorriso -osirrox on -indev "$SRC" -extract /casper/install-sources.yaml "$WORK/is.orig" >/dev/null 2>&1 + { sed 's/^ default: true$/ default: false/;s/^- default: true$/- default: false/' "$WORK/is.orig" + echo "- default: true" + echo " description:" + echo " en: Ubuntu Server with updates applied when the Bridge&Join image was built." + echo " id: ubuntu-server-bj" + echo " locale_support: locale-only" + echo " name:" + echo " en: Ubuntu Server (Bridge&Join, updated)" + echo " path: $LNAME.squashfs" + echo " size: $(( $(cat "$LAYER/$LNAME.size") + $(awk '/path: ubuntu-server-minimal.ubuntu-server.squashfs/{f=1} f&&/size:/{print $2; exit}' "$WORK/is.orig") ))" + echo " type: fsimage-layered" + echo " variant: server" + } > "$WORK/install-sources.yaml" + # Ядро и прошивки новее — в слое; их старые копии в репозитории образа (и ядро HWE, которое + # мы не ставим) не нужны: установка не будет их брать, пакет linux-generic уже стоит в слое. + LRM="/pool/main/l/linux-firmware /pool/main/l/linux-hwe-6.8 /pool/main/l/linux-meta-hwe-6.8" + LMAP="-map $LAYER/$LNAME.squashfs /casper/$LNAME.squashfs -map $LAYER/$LNAME.size /casper/$LNAME.size -map $LAYER/$LNAME.manifest /casper/$LNAME.manifest -map $WORK/install-sources.yaml /casper/install-sources.yaml" +fi + # Валидата n=0; for p in zpki zsdk libldap-2.4-2; do f=$(ls "$VALIDATA"/${p}_*.deb "$VALIDATA"/${p}-*.deb 2>/dev/null | grep -E 'amd64\.deb$' | head -1 || true) @@ -67,7 +100,13 @@ xorriso -osirrox on -indev "$SRC" -extract /boot/grub/grub.cfg "$WORK/grub/grub. # пересчитываем изменённые и добавляем наши. xorriso -osirrox on -indev "$SRC" -extract /md5sum.txt "$WORK/md5.orig" >/dev/null 2>&1 { - grep -vE '\./casper/(hwe-vmlinuz|hwe-initrd|ubuntu-server-minimal\.ubuntu-server\.installer\.generic-hwe\.squashfs)$|\./boot/grub/grub\.cfg$' "$WORK/md5.orig" + grep -vE '\./pool/main/l/linux-(firmware|hwe-6\.8|meta-hwe-6\.8)/' "$WORK/md5.orig" | grep -vE '\./casper/(hwe-vmlinuz|hwe-initrd|ubuntu-server-minimal\.ubuntu-server\.installer\.generic-hwe\.squashfs|install-sources\.yaml)$|\./boot/grub/grub\.cfg$' + if [ -n "$LAYER" ]; then + echo "$(md5sum < "$WORK/install-sources.yaml" | cut -d' ' -f1) ./casper/install-sources.yaml" + for f in $LNAME.squashfs $LNAME.size $LNAME.manifest; do echo "$(md5sum < "$LAYER/$f" | cut -d' ' -f1) ./casper/$f"; done + else + grep -E '\./casper/install-sources\.yaml$' "$WORK/md5.orig" + fi echo "$(md5sum < "$WORK/grub/grub.cfg" | cut -d' ' -f1) ./boot/grub/grub.cfg" (cd "$WORK" && find bj nocloud -type f | sort | while read -r f; do echo "$(md5sum < "$f" | cut -d' ' -f1) ./$f"; done) } > "$WORK/md5sum.txt" @@ -77,7 +116,7 @@ xorriso -indev "$SRC" -outdev "$OUT" -boot_image any replay \ -volid "BJ_UBUNTU22" \ -map "$WORK/bj" /bj -map "$WORK/nocloud" /nocloud \ -map "$WORK/grub/grub.cfg" /boot/grub/grub.cfg \ - -map "$WORK/md5sum.txt" /md5sum.txt \ - -rm /casper/hwe-vmlinuz /casper/hwe-initrd /casper/ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.squashfs -- \ + -map "$WORK/md5sum.txt" /md5sum.txt $LMAP \ + -rm_r /casper/hwe-vmlinuz /casper/hwe-initrd /casper/ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.squashfs $LRM -- \ -end >/dev/null 2>"$WORK/xorriso.log" || { cat "$WORK/xorriso.log"; exit 1; } -echo "Образ собран: $OUT ($(du -h "$OUT" | cut -f1)), канал $CHANNEL; ядро: $FILES; Валидата: $n пакета" +echo "Образ собран: $OUT ($(du -h "$OUT" | cut -f1)), канал $CHANNEL; ядро: $FILES; Валидата: $n пакета; слой обновлений: ${LAYER:-нет}" diff --git a/deploy/iso/build-ubuntu-updates-layer.sh b/deploy/iso/build-ubuntu-updates-layer.sh new file mode 100755 index 0000000..c5a68a5 --- /dev/null +++ b/deploy/iso/build-ubuntu-updates-layer.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# Слой обновлений для образа Bridge&Join на Ubuntu 22.04: всё, что вышло для 22.04 после выпуска +# 22.04.5, ставится в образ при сборке — установка тогда докачивает только вышедшее после сборки. +# +# sudo deploy/iso/build-ubuntu-updates-layer.sh <выходной каталог> +# +# Результат в выходном каталоге: +# ubuntu-server-minimal.ubuntu-server.bj.squashfs (+ .size, .manifest) — слой поверх «Ubuntu Server»: +# full-upgrade + ядро linux-generic (ядра в слоях Ubuntu нет, его ставит отдельный шаг установки); +# установщик Ubuntu собирает систему из слоёв по цепочке имён (minimal → ubuntu-server → bj), +# оригинальные слои и среда самого установщика не меняются; +# debs/ — пакеты, которые ставит bj-installer (PostgreSQL, Java, pcscd…), со всеми зависимостями, +# которых нет в системе: при первой загрузке они ставятся с диска, а не из сети. +# Нужны root (монтирование, chroot), squashfs-tools, xorriso и доступ к archive.ubuntu.com. +# Образ стоит пересобирать раз в месяц, чтобы слой не устаревал. +set -euo pipefail +SRC=${1:?ubuntu-22.04.x-live-server-amd64.iso} +OUT=${2:?выходной каталог} +[ "$(id -u)" = 0 ] || { echo "нужен root"; exit 1; } +for c in mksquashfs xorriso; do command -v $c >/dev/null || { echo "нужен $c"; exit 1; }; done +# пакеты — как в bj-installer (stepPackages) +BJ_PKGS="ca-certificates curl postgresql postgresql-client default-jre-headless pcscd python3" +LAYER=ubuntu-server-minimal.ubuntu-server.bj.squashfs + +mkdir -p "$OUT" +W=$(mktemp -d "$OUT/work.XXXXXX") +M="$W/merged" +cleanup() { + for d in dev/pts dev proc sys run; do mountpoint -q "$M/$d" 2>/dev/null && umount -l "$M/$d" || true; done + for d in "$M" "$W/l2" "$W/l1"; do mountpoint -q "$d" 2>/dev/null && umount -l "$d" || true; done + rm -rf "$W" +} +trap cleanup EXIT +mkdir -p "$W/l1" "$W/l2" "$W/upper" "$W/wk" "$M" +xorriso -osirrox on -indev "$SRC" \ + -extract /casper/ubuntu-server-minimal.squashfs "$W/minimal.squashfs" \ + -extract /casper/ubuntu-server-minimal.ubuntu-server.squashfs "$W/server.squashfs" >/dev/null 2>&1 +mount -o loop,ro "$W/minimal.squashfs" "$W/l1" +mount -o loop,ro "$W/server.squashfs" "$W/l2" +mount -t overlay overlay -o "lowerdir=$W/l2:$W/l1,upperdir=$W/upper,workdir=$W/wk" "$M" +mount --bind /dev "$M/dev"; mount --bind /dev/pts "$M/dev/pts" +mount -t proc proc "$M/proc"; mount -t sysfs sys "$M/sys" +mount -t tmpfs tmpfs "$M/run" +# DNS в chroot: resolv.conf в Ubuntu — ссылка в /run (tmpfs, в слой не попадает) +mkdir -p "$M/run/systemd/resolve"; grep '^nameserver' /etc/resolv.conf > "$M/run/systemd/resolve/stub-resolv.conf" +# службы в chroot не запускать +printf '#!/bin/sh\nexit 101\n' > "$M/usr/sbin/policy-rc.d"; chmod 755 "$M/usr/sbin/policy-rc.d" + +in_chroot() { chroot "$M" /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin DEBIAN_FRONTEND=noninteractive LANG=C.UTF-8 "$@"; } +APT="apt-get -y -q -o Dpkg::Options::=--force-confold -o Dpkg::Options::=--force-confdef" +echo "== apt update"; in_chroot apt-get -q update +echo "== full-upgrade"; in_chroot $APT full-upgrade +echo "== ядро"; in_chroot $APT install --no-install-recommends linux-generic +in_chroot apt-get clean +echo "== пакеты bj (только скачать)" +in_chroot $APT install --download-only --no-install-recommends $BJ_PKGS +rm -rf "$OUT/debs"; mkdir -p "$OUT/debs" +cp "$M"/var/cache/apt/archives/*.deb "$OUT/debs/" +in_chroot apt-get clean +in_chroot dpkg-query -W -f '${Package}\t${Version}\n' > "$OUT/${LAYER%.squashfs}.manifest" + +# убрать следы сборки: policy-rc.d, списки пакетов, кэш, журналы +rm -f "$M/usr/sbin/policy-rc.d" +rm -rf "$M"/var/lib/apt/lists/* "$M"/var/cache/apt/*.bin "$M"/var/log/apt/* "$M"/tmp/* 2>/dev/null || true +for d in dev/pts dev proc sys run; do umount -l "$M/$d"; done +umount "$M" + +echo "== слой" +rm -f "$OUT/$LAYER" +mksquashfs "$W/upper" "$OUT/$LAYER" -comp xz -xattrs -noappend -no-progress >/dev/null +du -sb "$W/upper" | cut -f1 > "$OUT/${LAYER%.squashfs}.size" +echo "Слой: $OUT/$LAYER ($(du -h "$OUT/$LAYER" | cut -f1)), пакетов bj: $(ls "$OUT/debs" | wc -l) ($(du -sh "$OUT/debs" | cut -f1)), ядро: $(grep -P '^linux-image-[0-9]' "$OUT/${LAYER%.squashfs}.manifest" | cut -f1 | tr '\n' ' ')" diff --git a/deploy/iso/ubuntu/user-data.tmpl b/deploy/iso/ubuntu/user-data.tmpl index 183c318..0d22326 100644 --- a/deploy/iso/ubuntu/user-data.tmpl +++ b/deploy/iso/ubuntu/user-data.tmpl @@ -30,6 +30,7 @@ autoinstall: - mkdir -p /target/var/lib/bj /target/etc/bj /target/usr/local/sbin - cp -r /cdrom/bj/bundle /target/var/lib/bj/bundle - cp -r /cdrom/bj/validata /target/var/lib/bj/validata + - sh -c 'if [ -d /cdrom/bj/debs ]; then cp -r /cdrom/bj/debs /target/var/lib/bj/debs; fi' - install -m 0755 /cdrom/bj/bj-installer /cdrom/bj/bj-firstboot /cdrom/bj/bj-welcome /target/usr/local/sbin/ - install -m 0644 /cdrom/bj/bj-firstboot.service /cdrom/bj/bj-welcome.service /cdrom/bj/bj-welcome.timer /target/etc/systemd/system/ - install -m 0644 /cdrom/bj/channel /target/etc/bj/channel