Files
LokiStack/scripts/release.sh
T
opsx 8bea331183 chore(release): 发布脚本对齐"统一使用 latest"策略
清单本来就用 latest(deployment.yaml / docker-compose.yml / prod.yml 的
${IMAGE_TAG:-latest}),但上一版 release.sh 会把 deployment.yaml 改成
不可变 tag 并把 pullPolicy 改成 IfNotPresent —— 与"统一用 latest"相反,
而且改完不提交的话下次 apply 又会退回最新 tag,容易造成实际版本与预期不符。

改为:
  1) 一次构建打三个 tag:latest / v0.1 / <版本>-<日期>-<commit>,同时推送
  2) 推送后**校验三个远端 digest 一致**,不一致直接退出(并发构建覆盖
     latest 的经典事故,先拦住别部署)
  3) 部署走 kubectl apply + rollout restart(image=latest + Always 强制重拉)
  4) 部署完再核对 Pod digest 与远端 latest,不一致立即提示
  5) 不再默认改清单;需要"固定版本部署"时显式 `--pin`
  6) 回滚方式写清楚:把旧的不可变 tag 重新打成 latest 推上去

同时把集群里被我为了避开 tag 竞争临时改成 v11-1028-2252 的 Deployment
恢复成 latest + Always(已验证 Pod / Hub latest / Hub v0.1 三者 digest 一致
= 187062644c0f)。
2026-10-28 23:02:41 +00:00

117 lines
5.1 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
# ============================================================
# release.sh — 构建 + 推送(latest / v0.1 / 不可变 tag 三合一)
# ============================================================
# 本项目的发布策略:**清单与容器启动统一使用 latest**。
# kubernetes/deployment.yaml → lghost/lokistack:latest + imagePullPolicy: Always
# docker-compose.yml → lghost/lokistack:latest
# docker-compose.prod.yml → lghost/lokistack:${IMAGE_TAG:-latest}
#
# 所以每次发版必须让 latest 与 v0.1 **同时**指向新镜像,否则会出现
# "k8s 拉到新版、docker 起还是旧版"这种版本漂移。
#
# 本脚本另外额外打一个不可变 tag(<版本>-<日期>-<短commit>):
# - 它不写进任何清单,只用于事后追溯/回滚(仓库里能查到那一刻的确切镜像)
# - 需要"固定版本部署"的生产环境可以手动改成这个 tag
#
# 为什么不在脚本里改 yml 的 tag:
# 上一版脚本会把 deployment.yaml 的 image 改成不可变 tag 并把 pullPolicy
# 改成 IfNotPresent,结果与"统一用 latest"的约定相反,还容易忘记提交
# 导致下次 apply 退回旧 tag。改成默认不动清单。
#
# 用法:
# bash scripts/release.sh # 构建 + 推三个 tag + 滚动重启(拉 latest)
# bash scripts/release.sh --no-deploy # 只构建 + 推送,不碰集群
# bash scripts/release.sh --pin # 额外把 deployment.yaml 钉到不可变 tag(可选)
# ============================================================
set -euo pipefail
cd "$(dirname "$0")/.."
IMAGE_REPO="${IMAGE_REPO:-lghost/lokistack}"
VERSION="${VERSION:-2.0.0}"
MODE="${1:-}"
SHORT_SHA=$(git rev-parse --short=8 HEAD 2>/dev/null || echo "nogit")
DIRTY=""
if ! git diff --quiet 2>/dev/null; then
DIRTY="-dirty"
echo "⚠️ 工作区有未提交改动,不可变 tag 会带 -dirty(发布前建议先提交)"
fi
IMMUTABLE_TAG="${VERSION}-$(date +%Y%m%d)-${SHORT_SHA}${DIRTY}"
echo "=========================================="
echo " 构建: ${IMAGE_REPO} → latest / v0.1 / ${IMMUTABLE_TAG}"
echo "=========================================="
docker build \
-t "${IMAGE_REPO}:latest" \
-t "${IMAGE_REPO}:v0.1" \
-t "${IMAGE_REPO}:${IMMUTABLE_TAG}" .
IMAGE_ID=$(docker images --format '{{.ID}}' "${IMAGE_REPO}:latest" | head -1)
echo "✅ 构建完成 ${IMAGE_ID}"
echo ""
echo "推送三个 tag(latest 与 v0.1 必须同 digest,否则环境间会版本漂移)..."
docker push "${IMAGE_REPO}:latest" | tail -1
docker push "${IMAGE_REPO}:v0.1" | tail -1
docker push "${IMAGE_REPO}:${IMMUTABLE_TAG}" | tail -1
# ── 校验三个 tag 指向同一个镜像 ──
echo ""
echo "校验远端 digest 一致性..."
digest_of() {
curl -s "https://hub.docker.com/v2/repositories/${IMAGE_REPO}/tags/$1" 2>/dev/null \
| python3 -c 'import sys,json;print(json.load(sys.stdin).get("digest","?"))' 2>/dev/null \
|| echo "?"
}
D_LATEST=$(digest_of latest)
D_V01=$(digest_of v0.1)
D_IMM=$(digest_of "${IMMUTABLE_TAG}")
echo " latest : ${D_LATEST:0:19}"
echo " v0.1 : ${D_V01:0:19}"
echo " ${IMMUTABLE_TAG}: ${D_IMM:0:19}"
if [ "${D_LATEST:0:19}" != "${D_V01:0:19}" ] || [ "${D_LATEST:0:19}" != "${D_IMM:0:19}" ]; then
echo "❌ 三个 tag digest 不一致,先别继续部署(可能是并发构建覆盖了 latest)"
exit 1
fi
echo " → 一致 ✅"
if [ "${MODE}" = "--pin" ]; then
echo ""
echo "(--pin)把 kubernetes/deployment.yaml 钉到 ${IMMUTABLE_TAG}..."
sed -i "s|image: ${IMAGE_REPO}:.*|image: ${IMAGE_REPO}:${IMMUTABLE_TAG}|" kubernetes/deployment.yaml
sed -i "s|imagePullPolicy: Always|imagePullPolicy: IfNotPresent|" kubernetes/deployment.yaml
echo "⚠️ 记得提交 deployment.yaml,否则下次 apply 会退回 latest"
fi
if [ "${MODE}" != "--no-deploy" ]; then
export KUBECONFIG="${KUBECONFIG:-/root/.kube/config}"
echo ""
echo "应用清单(image: latest + Always → 强制重拉)..."
kubectl apply -f kubernetes/deployment.yaml
# 清单内容没变时 apply 不会触发滚动,这里显式重启一次
kubectl -n log rollout restart deploy/lokistack
kubectl -n log rollout status deploy/lokistack --timeout=300s
POD_DIGEST=$(kubectl -n log get po -l app=lokistack \
-o jsonpath='{.items[0].status.containerStatuses[0].imageID}' 2>/dev/null | sed 's/.*@//')
echo ""
echo "=========================================="
echo " 部署完成,核对 Pod 与远端 latest:"
echo " 远端 latest : ${D_LATEST:0:19}"
echo " Pod 运行中 : ${POD_DIGEST:0:19}"
if [ "${POD_DIGEST:0:19}" = "${D_LATEST:0:19}" ]; then
echo " → 一致 ✅"
else
echo " → 不一致 ❌(Pod 可能还在拉取,或节点镜像缓存异常)"
fi
echo "=========================================="
fi
echo ""
echo "回滚方法(latest 策略下靠不可变 tag 追溯):"
echo " docker pull ${IMAGE_REPO}:<上次的 ${VERSION}-日期-commit>"
echo " docker tag ${IMAGE_REPO}:<该 tag> ${IMAGE_REPO}:latest && docker push ${IMAGE_REPO}:latest"
echo " kubectl -n log rollout restart deploy/lokistack"