This commit is contained in:
Yvan 2026-09-05 08:51:14 +08:00
parent ce4ed32dfc
commit c968c2f1f0
3 changed files with 26 additions and 17 deletions

View File

@ -160,9 +160,7 @@ gitlabBranch集成/推送触发)
1. 服务启动时数据库未就绪,首页显示"前往初始化"。 1. 服务启动时数据库未就绪,首页显示"前往初始化"。
2. 走完 `/init/initdb` 向导:建库建表 + 写入种子数据(默认账号 admin 2. 走完 `/init/initdb` 向导:建库建表 + 写入种子数据(默认账号 admin
3. 向导最后一步把数据库连接**写回**节点目录中的 `/data/conf/config.yaml`,配置不会随 Pod 重建消失。 3. 向导最后一步把数据库连接**写回**节点目录中的 `/data/conf/config.yaml`,配置不会随 Pod 重建消失。
4. 4. 当前 Jenkins 默认将前后端都部署为 1 个副本,更新策略为 `Recreate`MySQL 和 Redis 也默认启动。若环境已有外部 MySQL可将 `KRA_DEPLOY_MYSQL` 设为 `false`;若不需要 Redis可将 `KRA_DEPLOY_REDIS` 设为 `false`。关闭时对应工作负载副本数为 0backend 不等待内置 MySQL并关闭 Redis 使用。未来如需扩容,必须先迁移到真正的共享存储,再调整副本数和更新策略。
5.
6. 当前 Jenkins 默认将前后端都部署为 1 个副本MySQL 和 Redis 也默认启动。若环境已有外部 MySQL可将 `KRA_DEPLOY_MYSQL` 设为 `false`;若不需要 Redis可将 `KRA_DEPLOY_REDIS` 设为 `false`。关闭时对应工作负载副本数为 0backend 不等待内置 MySQL并关闭 Redis 使用。单节点下保持 `KRA_REPLICAS=1`;未来如需扩容,必须先迁移到真正的共享存储,再调整副本数并重新部署。
> `<APP_NAME>-config-template` Secret 只负责首次创建配置文件。初始化后,运行时配置以 hostPath 中的 `/data/conf/config.yaml` 为准;更新 Secret 或 Jenkins JWT 凭据不会覆盖已有配置避免覆盖初始化写入的数据库连接。JWT 后续轮换应通过系统设置完成并滚动重启后端。 > `<APP_NAME>-config-template` Secret 只负责首次创建配置文件。初始化后,运行时配置以 hostPath 中的 `/data/conf/config.yaml` 为准;更新 Secret 或 Jenkins JWT 凭据不会覆盖已有配置避免覆盖初始化写入的数据库连接。JWT 后续轮换应通过系统设置完成并滚动重启后端。

8
deploy/Jenkinsfile vendored
View File

@ -226,8 +226,12 @@ pipeline {
esac esac
RENDERED_MANIFEST="$TMP_DIR/manifest.yaml" RENDERED_MANIFEST="$TMP_DIR/manifest.yaml"
envsubst "$K8S_RENDER_VARS" < deploy/k8s/kra.yaml > "$RENDERED_MANIFEST" envsubst "$K8S_RENDER_VARS" < deploy/k8s/kra.yaml > "$RENDERED_MANIFEST"
if grep -nE '\$\{[A-Z_][A-Z0-9_]*\}' "$RENDERED_MANIFEST"; then
echo 'Rendered Kubernetes manifest contains unresolved template variables' >&2
exit 1
fi
MYSQL_SERVICE_IP=$(kubectl -n "$KRA_NAMESPACE" get service "${APP_NAME}-mysql" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true) MYSQL_SERVICE_IP=$(kubectl -n "$KRA_NAMESPACE" get service "${APP_NAME}-mysql" -o jsonpath='{.spec.clusterIP}' 2>/dev/null || true)
if [ "$KRA_DEPLOY_MYSQL" = 'true' ] && [ -n "$MYSQL_SERVICE_IP" ] && [ "$MYSQL_SERVICE_IP" != 'None' ]; then if [ -n "$MYSQL_SERVICE_IP" ] && [ "$MYSQL_SERVICE_IP" != 'None' ]; then
echo "${APP_NAME}-mysql already exists as a ClusterIP Service; delete it during a maintenance window before switching to headless mode" >&2 echo "${APP_NAME}-mysql already exists as a ClusterIP Service; delete it during a maintenance window before switching to headless mode" >&2
exit 1 exit 1
fi fi
@ -242,7 +246,9 @@ pipeline {
--patch-file="$PATCH_FILE" --patch-file="$PATCH_FILE"
kubectl -n "$KRA_NAMESPACE" rollout status statefulset/${APP_NAME}-mysql --timeout=10m kubectl -n "$KRA_NAMESPACE" rollout status statefulset/${APP_NAME}-mysql --timeout=10m
fi fi
if [ "$KRA_DEPLOY_REDIS" = 'true' ]; then
kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-redis --timeout=10m kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-redis --timeout=10m
fi
kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-backend --timeout=10m kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-backend --timeout=10m
kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-frontend --timeout=10m kubectl -n "$KRA_NAMESPACE" rollout status deployment/${APP_NAME}-frontend --timeout=10m
''' '''

View File

@ -203,10 +203,8 @@ spec:
replicas: ${KRA_REPLICAS} replicas: ${KRA_REPLICAS}
revisionHistoryLimit: 3 revisionHistoryLimit: 3
strategy: strategy:
type: RollingUpdate # 单节点 hostPath 不能让新旧 backend Pod 并存,否则会同时操作同一份配置和上传目录。
rollingUpdate: type: Recreate
maxUnavailable: 1
maxSurge: 1
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: ${KRA_APP}-backend app.kubernetes.io/name: ${KRA_APP}-backend
@ -224,12 +222,21 @@ spec:
- name: wait-for-mysql - name: wait-for-mysql
image: ${KRA_MYSQL_WAIT_IMAGE} image: ${KRA_MYSQL_WAIT_IMAGE}
imagePullPolicy: IfNotPresent imagePullPolicy: IfNotPresent
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: ${KRA_APP}-runtime
key: mysql-root-password
optional: true
command: ["sh", "-c"] command: ["sh", "-c"]
# 用字面量块(|)而非折叠标量(>-):嵌套缩进会让折叠标量保留换行,
# 把一条命令拆成多行导致 "command not found"。
args: args:
- >- - |
if [ "${KRA_DEPLOY_MYSQL}" = "true" ]; then if [ "${KRA_DEPLOY_MYSQL}" = "true" ]; then
until mysqladmin ping -h "${KRA_APP}-mysql" -P 3306 test -n "$MYSQL_ROOT_PASSWORD" || { echo "MYSQL_ROOT_PASSWORD is required when bundled MySQL is enabled" >&2; exit 1; }
--connect-timeout=2 --silent; do sleep 2; done; until mysqladmin ping -h "${KRA_APP}-mysql" -P 3306 -uroot -p"$MYSQL_ROOT_PASSWORD" --connect-timeout=2 --silent; do sleep 2; done;
fi fi
# 这里只等待 MySQL 服务可达,不要求应用数据库已经初始化;首次部署 # 这里只等待 MySQL 服务可达,不要求应用数据库已经初始化;首次部署
# 仍需进入 bootstrap 模式完成 /init/initdb 向导。 # 仍需进入 bootstrap 模式完成 /init/initdb 向导。
@ -241,7 +248,7 @@ spec:
runAsGroup: 0 runAsGroup: 0
command: ["sh", "-c"] command: ["sh", "-c"]
args: args:
- >- - |
mkdir -p /data/conf /app/uploads; mkdir -p /data/conf /app/uploads;
if [ ! -f /data/conf/config.yaml ]; then if [ ! -f /data/conf/config.yaml ]; then
cp /bootstrap/config.yaml /data/conf/config.yaml; cp /bootstrap/config.yaml /data/conf/config.yaml;
@ -380,10 +387,8 @@ spec:
replicas: ${KRA_REPLICAS} replicas: ${KRA_REPLICAS}
revisionHistoryLimit: 3 revisionHistoryLimit: 3
strategy: strategy:
type: RollingUpdate # 单节点部署只保留一个前端 Pod避免更新期间短暂扩出第二个 Pod。
rollingUpdate: type: Recreate
maxUnavailable: 1
maxSurge: 1
selector: selector:
matchLabels: matchLabels:
app.kubernetes.io/name: ${KRA_APP}-frontend app.kubernetes.io/name: ${KRA_APP}-frontend