Java 17 для службы подписи: установщик ставит openjdk-17-jre-headless там, где Java по умолчанию старее (Ubuntu 22.04 — 11; crypto-service собран под 17), в слой образа кладётся Java 17; «Подготовка модулей» и проверка готовности не принимают падающую по кругу службу за работающую (работает ≥ 5 с) и называют причину — в том числе «слишком старая Java» с командой исправления

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-05 19:30:53 +03:00
parent 2c22974141
commit 64928cfed1
4 changed files with 97 additions and 13 deletions
+24 -3
View File
@@ -262,23 +262,44 @@ func memTotal() uint64 {
return 0
}
// javaPkg — Java для службы подписи (crypto-service собран под Java 17): где Java по умолчанию старше
// не бывает (Ubuntu 22.04, Astra 1.7 — Java 11), ставится openjdk-17; где по умолчанию 17 и новее
// (Debian 13 — Java 21), — пакет по умолчанию.
func javaPkg(debs []string) string {
if len(debs) > 0 { // пакеты с образа: какая Java положена в образ
for _, d := range debs {
if strings.HasPrefix(filepath.Base(d), "openjdk-17-jre-headless_") {
return "openjdk-17-jre-headless"
}
}
return "default-jre-headless"
}
out, err := run("apt-cache", "policy", "openjdk-17-jre-headless")
if err == nil && strings.Contains(out, "Candidate:") && !strings.Contains(out, "Candidate: (none)") {
return "openjdk-17-jre-headless"
}
return "default-jre-headless"
}
func (in *installer) stepPackages() (string, error) {
pkgs := []string{"ca-certificates", "curl", "postgresql", "postgresql-client", "default-jre-headless", "pcscd", "python3", "smbclient",
pkgs := []string{"ca-certificates", "curl", "postgresql", "postgresql-client", "pcscd", "python3", "smbclient",
// для интеграционного шлюза НРД без графики (ИШ 107: пароли в gnome-keyring, движок под виртуальным дисплеем)
"gnome-keyring", "dbus", "xvfb", "xauth", "libsecret-tools", "unzip"}
env := append(os.Environ(), "DEBIAN_FRONTEND=noninteractive")
// Образ кладёт пакеты со всеми зависимостями в /var/lib/bj/debs — ставим с диска, без сети.
// Не вышло (другая система, не хватает зависимости) — обычная установка из репозитория.
if debs, _ := filepath.Glob(in.P(debsDir) + "/*.deb"); len(debs) > 0 && in.root == "" {
withJava := append(pkgs, javaPkg(debs))
args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, debs...)
if _, err := runEnv(env, "apt-get", append(args, pkgs...)...); err == nil {
if _, err := runEnv(env, "apt-get", append(args, withJava...)...); err == nil {
_ = os.RemoveAll(in.P(debsDir))
return strings.Join(pkgs, ", ") + " (с образа, без загрузки)", nil
return strings.Join(withJava, ", ") + " (с образа, без загрузки)", 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))
}
pkgs = append(pkgs, javaPkg(nil))
args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, pkgs...)
if out, err := runEnv(env, "apt-get", args...); err != nil {
return "", fmt.Errorf("apt-get install: %v\n%s", err, tail(out, 12))
+2 -2
View File
@@ -18,8 +18,8 @@ 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 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip"
# пакеты — как в bj-installer (stepPackages); Java 17 — служба подписи собрана под неё (в 22.04 по умолчанию 11)
BJ_PKGS="ca-certificates curl postgresql postgresql-client openjdk-17-jre-headless pcscd python3 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip"
LAYER=ubuntu-server-minimal.ubuntu-server.bj.squashfs
mkdir -p "$OUT"
+64 -5
View File
@@ -9,6 +9,8 @@ import (
"context"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"time"
@@ -47,17 +49,19 @@ func prepareItems(rc *RuntimeConfig, running, finished bool, runErr string) []Pr
it := PrepareItem{Title: prepareTitle(c)}
_, statErr := os.Stat(c.Path)
present := statErr == nil
us := ""
var u unitRun
if present && c.Unit != "" {
us = unitState(c.Unit).State
u = unitRunState(c.Unit)
}
switch {
case us == "active":
case u.State == "active" && u.Up >= 5*time.Second:
it.State, it.Detail = "ok", "установлен, служба работает"
case us == "activating" || us == "reloading":
case (u.State == "activating" || u.State == "active") && u.Restarts >= 3:
it.State, it.Detail = "err", "установлен, но служба падает при запуске: "+unitFailCause(c)
case u.State == "activating" || u.State == "reloading" || u.State == "active":
it.State, it.Detail = "run", "установлен, служба запускается…"
case present && c.Unit == "bj-crypto":
it.State, it.Detail = "warn", "установлен, служба не запущена ("+us+") — обмену не мешает: пакеты для НРД подписывает шлюз"
it.State, it.Detail = "warn", "установлен, служба не запущена ("+u.State+") — обмену не мешает: пакеты для НРД подписывает шлюз"
case present:
it.State, it.Detail = "ok", "установлен; служба заработает, когда модуль будет настроен (глава «Запуск»)"
case running:
@@ -77,6 +81,61 @@ func prepareItems(rc *RuntimeConfig, running, finished bool, runErr string) []Pr
return out
}
// unitRun — служба: состояние, сколько работает после последнего запуска, сколько раз systemd её перезапускал.
type unitRun struct {
State string
Up time.Duration
Restarts int
}
func unitRunState(unit string) unitRun {
r := unitRun{State: "unknown"}
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
out, err := exec.CommandContext(ctx, "systemctl", "show", unit, "--property=ActiveState,NRestarts,ActiveEnterTimestampMonotonic").Output()
if err != nil {
return r
}
var since int64
for _, line := range strings.Split(string(out), "\n") {
k, v, _ := strings.Cut(strings.TrimSpace(line), "=")
switch k {
case "ActiveState":
r.State = v
case "NRestarts":
r.Restarts, _ = strconv.Atoi(v)
case "ActiveEnterTimestampMonotonic":
since, _ = strconv.ParseInt(v, 10, 64)
}
}
if b, err := os.ReadFile("/proc/uptime"); err == nil && since > 0 {
if f := strings.Fields(string(b)); len(f) > 0 {
if up, err := strconv.ParseFloat(f[0], 64); err == nil {
r.Up = time.Duration(up*float64(time.Second)) - time.Duration(since)*time.Microsecond
}
}
}
return r
}
// unitFailCause — причина падения службы модуля по последней строке её журнала ошибок — понятными словами.
func unitFailCause(c release.Component) string {
logs := map[string]string{"bj-crypto": "/var/log/bj/crypto-service.err", "bj-npr": "/var/log/bj/bj-npr.err", "bj-quikexport": "/var/log/bj/bj-quikexport.err"}
b, err := os.ReadFile(logs[c.Unit])
if err != nil {
return "подробности — в журнале службы " + c.Unit
}
lines := strings.Split(strings.TrimSpace(string(b)), "\n")
last := strings.TrimSpace(lines[len(lines)-1])
if strings.Contains(string(b[max(0, len(b)-4000):]), "UnsupportedClassVersionError") {
return "на сервере слишком старая Java (нужна 17 или новее): sudo apt-get install openjdk-17-jre-headless — служба запустится сама"
}
if len(last) > 240 {
last = last[:240] + "…"
}
return last
}
// prepareTitle — название модуля для экрана.
func prepareTitle(c release.Component) string {
switch c.Name {
+7 -3
View File
@@ -17,6 +17,8 @@ import (
"strings"
"sync"
"time"
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/release"
)
// cryptoJarPath — модуль службы пробной подписи bj-crypto (компонент crypto-service.jar).
@@ -134,10 +136,12 @@ func readinessChecks(ctx context.Context, rc *RuntimeConfig) []ReadyCheck {
// Служба bj-crypto — пробная подпись и проверка ключа из bj; пакеты для НРД подписывает шлюз, поэтому
// её остановка обмену не мешает. Модуль приезжает с обновлениями — до них службы нет, это не ошибка.
if _, err := os.Stat(cryptoJarPath); err == nil {
out, _ := exec.CommandContext(cctx, "systemctl", "is-active", "bj-crypto.service").Output()
if svc := strings.TrimSpace(string(out)); svc != "active" {
if u := unitRunState("bj-crypto"); !(u.State == "active" && u.Up >= 5*time.Second) {
c.State, c.Link = "warn", "/admin/setup#sec-crypto"
c.Detail += "; служба пробной подписи bj-crypto не запущена (" + pickStr(svc, "нет") + ") — обмену не мешает: пакеты для НРД подписывает шлюз"
c.Detail += "; служба пробной подписи bj-crypto не работает (" + pickStr(u.State, "нет") + ") — обмену не мешает: пакеты для НРД подписывает шлюз"
if cc, ok := release.ComponentByName("crypto-service.jar"); ok && (u.Restarts >= 3 || u.State == "failed") {
c.Detail += ". Причина: " + unitFailCause(cc)
}
} else {
c.Detail += ", служба пробной подписи bj-crypto работает"
}