Files
senmesh-zc/deployment/ow/scripts/install.sh
KodouWZF 9be37fe7f8 fix(部署): 交付包容器内路径与老系统对齐,日志与临时目录落数据盘
库里 xs_file_record.file_path 存的是 file.upload-dir 拼出来的绝对路径,老系统是
/var/s-file/upload,新包之前写 /data/upload 会让接老库的历史文件全部读不到(只能迁库),
现在容器内逐字一致,宿主机侧仍由 .env 的 SFILE_DATA_ROOT 决定。

顺带把两处老配置没真正落地的补上:
- logging.file.name 沿用 /usr/local/logs/s-file.log。之前 /app/logs 挂载是空的——yml 没设
  文件日志,输出只进 docker 的 json-file 且不封顶;同时给 app 的 json 日志加 20m×5 上限
- java.io.tmpdir 用 -D 指到 upload 挂载内。老项目把这行写在 application.properties 里
  并不会改 JVM 系统属性,等于没生效;现在 500MB 上传缓冲落在数据盘而不是容器 overlay 层

摆渡目录保持容器内 /data/transfer/*:光闸需要能单独挂它,嵌进 upload 树会把整个文件库
暴露给光闸,而传输包是过路文件、路径不入库。宿主机侧照旧可与老机器同路径(GATE_*)。
内外网两套 compose/config/install.sh 仍字节一致。
2026-11-05 01:07:54 +08:00

200 lines
9.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# =============================================================================
# s-file 一键安装(在本机执行;内网机用 iw/ 那份,外网机用 ow/ 那份)
#
# cd /opt/sfile-iw && ./scripts/install.sh
#
# 做六件事:环境自检 -> 校验交付物 -> 校验并载入镜像 tar -> 建数据目录 -> 起栈 -> 等健康
# 幂等:重复执行只重建有差异的容器,不动已初始化好的数据库
# 本目录就是配置:角色/口令/端口/目录全在同级 .env 里,改完重跑本脚本即可
# =============================================================================
set -euo pipefail
HERE="$(cd "$(dirname "$0")/.." && pwd)"
cd "$HERE"
envget() { grep -E "^[[:space:]]*$1=" .env | tail -1 | cut -d= -f2- | tr -d '"' | sed 's/[[:space:]]*$//'; }
step() { printf '\n\033[1;36m==> %s\033[0m\n' "$1"; }
die() { printf '\033[1;31m!!! %s\033[0m\n' "$1" >&2; exit 1; }
[ -f .env ] || die "缺少 .env(交付包不完整,本目录下应有一份出厂 .env)"
ROLE="$(envget SFILE_ROLE)"
case "$ROLE" in
iw|ow) ;;
*) die ".env 里 SFILE_ROLE=「$ROLE」不合法,只能是 iw(内网机)或 ow(外网机)" ;;
esac
echo "本机角色:$ROLE(传输方向 location=$(envget SFILE_TRANSFER_LOCATION))"
# ---------- 1. 前置自检 ----------
step "检查 docker 与 compose"
command -v docker >/dev/null 2>&1 || die "未安装 docker,请先装 Docker Engine(信创系统用厂商源,版本号 >= 20.10)"
docker info >/dev/null 2>&1 || die "docker 守护进程不可用,检查服务是否启动、当前用户是否在 docker 组"
if docker compose version >/dev/null 2>&1; then
DC="docker compose"
elif command -v docker-compose >/dev/null 2>&1; then
DC="docker-compose"
else
die "缺少 compose:安装 docker compose v2 插件,或用 docker-compose 命令"
fi
echo " 使用:$DC"
echo " docker server:$(docker version --format '{{.Server.Version}}')"
if grep -q "CHANGE_ME" .env; then
# MeshSafe 的 secret 只有真的启用检测时才必须填,否则现场没接 MeshSafe 就装不了机
if [ "$(envget MESHSAFE_ENABLED)" = "true" ] && grep -qE "^MESHSAFE_SECRET=.*CHANGE_ME" .env; then
die "启用了 MeshSafe 但 MESHSAFE_SECRET 还是占位值:按 MeshSafe 交付单改成白名单里的 secret,或把 MESHSAFE_ENABLED 改成 false"
fi
rest="$(grep -n CHANGE_ME .env | grep -v '^.*MESHSAFE_SECRET=' | cut -d: -f2 | tr '\n' ' ')"
[ -z "$rest" ] || die ".env 里还有 CHANGE_ME 未替换:$rest"
fi
step "检查交付物"
[ -d images ] || die "缺少 images/ 目录(镜像 tar 应在本目录的 images/ 下)"
tar_count=$(find images -maxdepth 1 -name '*.tar' | wc -l)
[ "$tar_count" -eq 5 ] || die "images/ 下应有 5 个 .tar(app/nginx/postgres/redis/rabbitmq),实际 $tar_count 个:$(ls images)"
for f in sql/01-init-table.sql sql/02-init-data.sql config/application.yml nginx/web.conf; do
[ -s "$f" ] || die "缺少 $f(交付包不完整)"
done
# 前端是静态包,缺了 nginx 起来只有默认页,看着像装好了其实没有界面
[ -s web/index.html ] || die "缺少 web/index.html —— 前端静态包没随包带过来"
[ -n "$(ls -A web/assets 2>/dev/null)" ] || die "web/assets 是空的 —— 前端静态包不完整"
echo " 镜像 tar:5 个,SQL / 后端配置 / nginx 配置 / 前端包就位"
# 端口占用先探一次:装到一半发现 80 被占,回滚比改 .env 麻烦
WEB_PORT="$(envget WEB_PORT)"
if command -v ss >/dev/null 2>&1 && ss -ltn 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${WEB_PORT:-80}$"; then
printf ' \033[1;33m提示:宿主机 %s 端口已被占用,改 .env 的 WEB_PORT 后重跑\033[0m\n' "${WEB_PORT:-80}"
fi
# ---------- 2. 校验交付盘 + 镜像架构 ----------
# 650M 的 tar 经 U 盘/内网 scp 拷坏是最常见的现场故障,坏包 load 到一半才报错更难查
if [ -f images/SHA256SUMS.txt ] && [ "${SKIP_CHECKSUM:-0}" != "1" ]; then
step "校验镜像 tar 完整性(sha256,约半分钟)"
( cd images && sha256sum -c SHA256SUMS.txt ) \
|| die "有 tar 与 SHA256SUMS.txt 不一致:交付盘拷坏了,重新拷一份;确认无碍可 SKIP_CHECKSUM=1 跳过"
fi
step "核对镜像架构"
HOST_ARCH="$(uname -m)"
case "$HOST_ARCH" in
x86_64) EXPECT="amd64" ;;
aarch64|arm64) EXPECT="arm64" ;;
*) EXPECT="unknown" ;;
esac
echo " 本机:$HOST_ARCH (期望镜像架构 ${EXPECT})"
# 架构在镜像的 config blob 里,不在 manifest.json —— manifest.json 只给 Config 的路径
tar_arch() {
local t="$1" cfg
cfg="$(tar -xOf "$t" manifest.json 2>/dev/null | tr -d '\n' \
| { grep -oE '"Config":"[^"]+"' || true; } | awk -F'"' 'NR==1{print $4}')"
[ -n "$cfg" ] || { echo ""; return 0; }
tar -xOf "$t" "$cfg" 2>/dev/null | tr -d '\n' \
| { grep -oE '"architecture":"[a-z0-9-]+"' || true; } | awk -F'"' 'NR==1{print $4}'
}
if [ "$EXPECT" != "unknown" ]; then
# app 和 pg 各查一个:一个是自己构建的,一个是基础镜像,架构错了必是出盘时选错平台
for t in images/sfile-app.tar images/postgres.tar; do
[ -f "$t" ] || continue
# 不能用 `| head -1`:pipefail 下 head 提前关管道会让上游 grep 收到 SIGPIPE(141),脚本静默中断
IMG_ARCH="$(tar_arch "$t")"
if [ -n "${IMG_ARCH:-}" ] && [ "$IMG_ARCH" != "$EXPECT" ]; then
die "$(basename "$t") 是 $IMG_ARCH,本机是 $EXPECT —— 别硬装,回来重出对应架构的包"
fi
printf ' %-18s %s\n' "$(basename "$t")" "${IMG_ARCH:-未识别,继续}"
done
fi
# ---------- 3. 载入镜像 ----------
step "docker load 载入离线镜像"
for t in images/*.tar; do
echo " <- $t"
docker load -i "$t"
done
# compose 用的是 .env 里的 tag,缺一个就会去联网拉,离线机上直接卡死
for img in "$(envget APP_IMAGE)" "$(envget NGINX_IMAGE)" "$(envget PG_IMAGE)" \
"$(envget REDIS_IMAGE)" "$(envget MQ_IMAGE)"; do
docker image inspect "$img" >/dev/null 2>&1 \
|| die "载入后仍看不到镜像 $img —— tar 与 .env 的 tag 不一致,别继续,回来看 images/ 和 .env"
echo " ✓ $img"
done
# ---------- 4. 目录 ----------
step "创建数据与摆渡目录"
DATA_ROOT="$(envget SFILE_DATA_ROOT)"
GATE_SEND="$(envget GATE_SEND_DIR)"
GATE_RECV="$(envget GATE_RECV_DIR)"
[ -n "$DATA_ROOT" ] || die ".env 里 SFILE_DATA_ROOT 为空"
# upload/tmpdir:JVM 的 java.io.tmpdir 指到这里(大文件上传先在此落盘),Tomcat 会在下面建 work 目录
mkdir -p "$DATA_ROOT"/{upload,upload/tmpdir,logs/nginx,pgdata,redis,mq}
mkdir -p "$GATE_SEND" "$GATE_RECV"
# 数据盘挂载点没起来时直接建目录会落到根分区,先确认一下
case "$DATA_ROOT" in
/*) mountpoint -q "$DATA_ROOT" 2>/dev/null \
|| printf ' \033[1;33m提示:%s 不是独立挂载点,确认容量够(上传+库+日志)\033[0m\n' "$DATA_ROOT" ;;
*) echo " 数据目录用本目录下的相对路径:$(cd "$DATA_ROOT" && pwd)" ;;
esac
# pgdata 提前给到容器内 postgres 的 uid=999,避免镜像入口脚本 chown 失败;
# redis/mq 的官方入口脚本自己会改属主,这里只保证目录存在且可写
chown -R 999:999 "$DATA_ROOT/pgdata" 2>/dev/null || true
chmod -R 750 "$DATA_ROOT" 2>/dev/null || true
# SELinux(麒麟/统信多为 enforcing):无 :z 标记的绑定挂载需要放开标签,否则库起不来、
# nginx 读不到前端包(表现为 403)。这里把 compose 里所有 bind 挂载点都标一遍
if command -v chcon >/dev/null 2>&1; then
chcon -Rt svirt_sandbox_file_t \
"$HERE/config" "$HERE/nginx" "$HERE/web" "$HERE/sql" \
"$DATA_ROOT" "$GATE_SEND" "$GATE_RECV" 2>/dev/null \
|| echo " chcon 失败(未启用 SELinux 可忽略)"
fi
echo " $DATA_ROOT / $GATE_SEND / $GATE_RECV 就位"
# ---------- 5. 起栈 ----------
step "启动服务(首次建库约 1-3 分钟)"
$DC up -d
step "等待健康检查"
for svc in pg redis mq app web; do
name="sfile-${ROLE}-${svc}"
printf ' %-24s ' "$name"
st=""
for _ in $(seq 1 60); do
st="$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$name" 2>/dev/null || echo missing)"
case "$st" in
healthy|running) printf '\033[32m%s\033[0m\n' "$st"; break ;;
missing) sleep 2 ;;
*) sleep 5 ;;
esac
done
if [ "$st" != "healthy" ] && [ "$st" != "running" ]; then
printf '\033[31m超时(最后状态:%s)\033[0m\n' "${st:-unknown}"
$DC ps
echo " 看日志:docker logs --tail 200 ${name}"
exit 1
fi
done
# ---------- 6. 出口信息 ----------
IP="$(hostname -I 2>/dev/null | tr ' ' '\n' | { grep -E '^[0-9]+\.' || true; } | awk 'NR==1{print}')"
step "完成"
cat <<EOF
访问地址: http://${IP:-<本机IP>}:${WEB_PORT:-80}
角色: ${ROLE}(传输方向 location=$(envget SFILE_TRANSFER_LOCATION))
数据目录: $(cd "$DATA_ROOT" && pwd)
摆渡: 发出 $GATE_SEND / 收到 $GATE_RECV
MeshSafe: $(envget MESHSAFE_ENABLED) -> $(envget MESHSAFE_BASE_URL)
常用命令:
$DC ps 看状态
docker logs -f sfile-${ROLE}-app 看后端日志
$DC restart app 改完 .env / config 后重启后端
$DC restart web 替换 web/ 里的前端文件后刷新站点
EOF
echo " 首次登录后立刻改掉 admin 的默认口令。"