跳到主要内容
版本:Dev

在 OpenShift 或 OKD 上升级 AISIX OSS

AISIX OSS 无需控制面运行,因此其声明式资源必须对目标网关镜像保持有效。本流程记录现有发布,使用目标镜像验证资源,执行 Helm 滚动更新,并在之后验证 OpenShift 或 OKD Route 与安全上下文。

前提条件​

开始升级前:

  • 配置 oc,使其能够在网关项目中管理工作负载、Service、Secret 和 Route,读取 Pod 日志并运行 oc exec。
  • 如果 Route 证书尚未受信任,请取得入口 CA 证书。下方命令需要读取 openshift-config-managed 中 default-ingress-cert ConfigMap 的权限;也可以由集群管理员提供 CA 文件。
  • 安装 Helm 3.15 或更高版本的 Helm 3、cURL、jq 和 Docker。
  • 阅读已安装版本之后每个版本的发布说明,按顺序应用所需的配置或可观测性变更。
  • 确认负载均衡器会移除 /readyz 检查失败的 Pod。
  • 对于多副本部署,如果限流计数器或缓存条目需要在副本间保持一致,请确认已配置共享 Redis。如果计划在升级前增加单副本部署的副本数,请先配置共享 Redis,或明确接受临时的各副本独立行为。参见为生产环境做准备。

示例使用 aisix-dp 作为发布名称和命名空间,与在 OpenShift 或 OKD 上部署 AISIX OSS一致。如果安装使用其他值,请运行 helm list -A,找到 CHART 列为 aisix-<version> 的发布,并在本流程中始终使用其发布名称和命名空间。错误的值会使 helm upgrade 报告该发布没有已部署的版本,并使下方 Deployment 查询结果为空。

设置示例所用的发布信息:

export GATEWAY_NAMESPACE="aisix-dp"
export GATEWAY_RELEASE="aisix-dp"
export TARGET_VERSION="<target-version>"
export GATEWAY_UPGRADE_TIMEOUT="60m"

修改发布前,记录当前 Route 及其目标 Service:

oc get routes -n "$GATEWAY_NAMESPACE"

60 分钟超时是针对 Chart 默认设置预留的保守起始值。默认设置包括两个副本、每个 Pod 最长 1,230 秒的终止时间和五分钟的启动探针预算。这并不是预计执行时长。

Kubernetes 可以并行终止 Pod,Helm 也不要求旧 Pod 全部终止后才将更新后的 Deployment 视为就绪。修改副本数、更新策略、终止宽限期、探针预算,或镜像拉取与调度变慢时,请重新计算超时时间。

确认集群能够调度滚动更新所增加的 Pod;延长超时无法解决配额或容量拒绝。如果原子升级失败,Helm 回滚时可能再使用一个完整的超时周期,因此应在维护窗口内预留这段时间。

刷新 API7 Chart 仓库,并确认目标 Chart 可用:

helm repo add api7 https://charts.api7.ai
helm repo update
helm show chart api7/aisix --version "$TARGET_VERSION"

信任 Route 证书​

默认应用域名通常使用由集群内部入口 CA 签发的通配符证书。如果工作站尚未信任此 CA,请导出它,供升级后的 curl 检查使用:

oc get configmap default-ingress-cert -n openshift-config-managed \
-o jsonpath='{.data.ca-bundle\.crt}' > ingress-ca.crt
export CURL_CA_BUNDLE="$PWD/ingress-ca.crt"

如果 CA 文件由管理员提供,请将其保存为 ingress-ca.crt,并只运行 CURL_CA_BUNDLE 导出命令。如果 Route 或默认 Ingress Controller 使用工作站已信任的证书,则跳过此导出步骤。

记录当前发布​

记录已安装的 Chart 和应用版本:

helm list -n "$GATEWAY_NAMESPACE"
oc get pods -n "$GATEWAY_NAMESPACE"

通过 Helm 发布标签确定 Deployment 名称。Chart 使用名称覆盖时,此方法仍然适用:

export GATEWAY_DEPLOYMENT="$(oc get deployment -n "$GATEWAY_NAMESPACE" \
-l "app.kubernetes.io/instance=$GATEWAY_RELEASE,app.kubernetes.io/component=gateway" \
-o jsonpath='{.items[0].metadata.name}')"

printf 'Gateway: %s\n' "$GATEWAY_DEPLOYMENT"

如果值为空,请停止并核对发布名称和命名空间。

仅保存用户提供的 values。文件可能包含凭据,因此应使用严格权限创建。不要使用 helm get values --all 导出所有计算后的默认值;沿用旧默认值可能使新 Chart 的默认值无法生效。

umask 077
touch gateway-values.yaml
chmod 600 gateway-values.yaml
helm get values "$GATEWAY_RELEASE" -n "$GATEWAY_NAMESPACE" -o yaml > gateway-values.yaml

将此文件与升级记录一同保留,并在观察期结束后删除本地副本。

检查 values 是否固定了镜像标签:

helm get values "$GATEWAY_RELEASE" -n "$GATEWAY_NAMESPACE" -o json \
| jq '{gateway_image_tag: (.image.tag // "")}'

空标签会使用目标 Chart 的应用版本。如果该值固定到旧 AISIX 镜像,请从保存的 values 中移除它,或更新为目标版本。保留有意设置的自定义镜像仓库,并为自定义构建使用与目标版本兼容的标签。

渲染目标网关镜像​

渲染目标 Deployment,并获取 Helm 将应用的准确网关镜像。此操作会保留 Chart 的镜像注册表及有意设置的仓库或标签覆盖:

TARGET_GATEWAY_IMAGE="$(
helm template "$GATEWAY_RELEASE" api7/aisix \
--namespace "$GATEWAY_NAMESPACE" \
--version "$TARGET_VERSION" \
--values gateway-values.yaml \
--show-only templates/deployment.yaml \
| awk '
$1 == "-" && $2 == "name:" && $3 == "aisix" { in_gateway = 1; next }
in_gateway && $1 == "image:" { gsub(/"/, "", $2); print $2; exit }
'
)"
export TARGET_GATEWAY_IMAGE

if [ -z "$TARGET_GATEWAY_IMAGE" ]; then
echo "Target gateway image was not rendered" >&2
else
printf 'Target gateway image: %s\n' "$TARGET_GATEWAY_IMAGE"
fi

如果未能渲染出目标网关镜像,请勿继续。

验证 AISIX OSS 资源​

确定发布所用的资源来源,但不输出内联资源文档:

helm get values "$GATEWAY_RELEASE" -n "$GATEWAY_NAMESPACE" -o json \
| jq '{inline_resources: (((.standalone.resources // {}) | length) > 0), existing_secret: (.standalone.existingSecret // ""), existing_config_map: (.standalone.existingConfigMap // "")}'

export TARGET_RESOURCES_FILE="$(mktemp "$PWD/aisix-resources.XXXXXX")"
chmod 600 "$TARGET_RESOURCES_FILE"

使用与报告的资源来源对应的命令。对于内联资源,请渲染目标 Chart 管理的 Secret,并提取完整文档:

helm template "$GATEWAY_RELEASE" api7/aisix \
--namespace "$GATEWAY_NAMESPACE" \
--version "$TARGET_VERSION" \
--values gateway-values.yaml \
--show-only templates/secret.yaml \
| awk '
/^ resources\.yaml: \|$/ { in_resources = 1; next }
in_resources && /^---$/ { exit }
in_resources { sub(/^ /, ""); print }
' > "$TARGET_RESOURCES_FILE"

对于现有 Secret 或 ConfigMap,请将占位符替换为前一条 jq 命令报告的名称:

# 选择其中一条命令。
oc get secret -n "$GATEWAY_NAMESPACE" \
<existing-secret-name> \
-o jsonpath='{.data.resources\.yaml}' \
| base64 --decode > "$TARGET_RESOURCES_FILE"

oc get configmap -n "$GATEWAY_NAMESPACE" \
<existing-config-map-name> \
-o jsonpath='{.data.resources\.yaml}' > "$TARGET_RESOURCES_FILE"

使用从目标 Deployment 渲染结果中获取的 TARGET_GATEWAY_IMAGE 验证提取出的文档。导出资源文件引用的每个环境变量,并为每个变量添加一个 -e 选项。示例使用 OPENAI_API_KEY 和 CALLER_API_KEY;请替换为文件中实际使用的变量。

该命令将文档流式传入短生命周期容器,而不是绑定挂载宿主机文件。因此,网关镜像能够读取文档,无需放宽宿主机文件权限或更改其 SELinux 标签。

docker run --rm -i \
-e OPENAI_API_KEY \
-e CALLER_API_KEY \
--entrypoint /bin/sh "$TARGET_GATEWAY_IMAGE" \
-c 'umask 077; cat > /tmp/resources.yaml && exec /usr/local/bin/aisix validate --resources /tmp/resources.yaml' \
< "$TARGET_RESOURCES_FILE" && {
rm -f "$TARGET_RESOURCES_FILE" &&
unset TARGET_RESOURCES_FILE
}

继续之前,请解决所有验证错误。验证失败会保留提取出的文件以供检查,并返回非零状态。如果资源文件未引用环境变量,请省略 -e 选项。

检查目标发布​

修改发布前,先让 Helm 针对运行中的集群渲染升级。--dry-run=server 允许集群查询和 OpenAPI 验证,--hide-secret 则防止 Helm 将渲染出的 Secret 打印到终端或 CI 日志:

helm upgrade "$GATEWAY_RELEASE" api7/aisix \
--namespace "$GATEWAY_NAMESPACE" \
--version "$TARGET_VERSION" \
--values gateway-values.yaml \
--dry-run=server \
--hide-secret

该命令不会将渲染对象提交给准入控制。继续之前,请解决报告的所有错误。

升级网关​

运行 Helm 前,打开第二个终端。选择相同的集群上下文,并使用原终端中的值导出 GATEWAY_NAMESPACE 和 GATEWAY_RELEASE;终端之间不共享 Shell 变量。其他诊断终端也需要重复此设置。观察网关 Pod,直到 Helm 返回:

export GATEWAY_NAMESPACE="aisix-dp"
export GATEWAY_RELEASE="aisix-dp"

oc get pods -n "$GATEWAY_NAMESPACE" \
-l "app.kubernetes.io/instance=$GATEWAY_RELEASE,app.kubernetes.io/component=gateway" \
--watch

如果新 Pod 停滞或重启,请按 Ctrl+C 停止观察,或打开第三个终端。在原终端中的 Helm 仍在等待时,记录 Pod 描述和日志。原子回滚可能在 Helm 命令返回前移除失败的目标 Pod;参见排查未就绪的网关。

在原终端中运行升级:

helm upgrade "$GATEWAY_RELEASE" api7/aisix \
--namespace "$GATEWAY_NAMESPACE" \
--version "$TARGET_VERSION" \
--values gateway-values.yaml \
--atomic \
--timeout "$GATEWAY_UPGRADE_TIMEOUT"

--atomic 会阻塞,直到目标发布就绪,或 Helm 在失败后完成回滚。如果命令以非零状态退出,不要将之后成功的 oc rollout status 视为目标版本成功的证据;它可能报告的是恢复后的修订。保留 Helm 运行期间收集的诊断信息,然后检查发布历史并验证恢复后的网关发布:

helm history "$GATEWAY_RELEASE" -n "$GATEWAY_NAMESPACE"

使用默认滚动更新策略和就绪探针时,新网关只有加载了可用配置后才会加入 Service 端点。请保持 readinessProbe.enabled 为 true;如果升级必须避免流量中断,不要选择 Recreate 策略。单副本部署在唯一 Pod 被替换期间仍可能中断。

如果 Helm 命令成功,请确认目标滚动更新已完成,且所有网关 Pod 均已就绪:

oc rollout status -n "$GATEWAY_NAMESPACE" \
"deployment/$GATEWAY_DEPLOYMENT" \
--timeout="$GATEWAY_UPGRADE_TIMEOUT"

oc get pods -n "$GATEWAY_NAMESPACE"

验证升级​

将 AISIX_PROXY 设置为不带末尾斜杠的网关源地址,然后列出调用方可用的模型:

export AISIX_PROXY="YOUR_AISIX_GATEWAY_URL"
export AISIX_API_KEY="<caller-api-key>"

curl -fsS "$AISIX_PROXY/v1/models" \
-H "Authorization: Bearer $AISIX_API_KEY"

为列表中的一个模型发送已知有效的推理请求。检查部署所依赖的访问日志、指标和其他遥测。在网关按变更策略规定的观察期正常运行之前,保留保存的 values。

确认 Route 仍然指向网关 Service,且网关 Pod 仍使用 restricted-v2 和项目范围内的 UID:

oc get routes -n "$GATEWAY_NAMESPACE"

export GATEWAY_POD="$(oc get pod -n "$GATEWAY_NAMESPACE" \
-l "app.kubernetes.io/instance=$GATEWAY_RELEASE,app.kubernetes.io/component=gateway" \
-o json | jq -er --arg image "$TARGET_GATEWAY_IMAGE" '
[.items[]
| select(.metadata.deletionTimestamp == null)
| select(any(.status.conditions[]?; .type == "Ready" and .status == "True"))
| select(any(.spec.containers[]; .name == "aisix" and .image == $image))
][0].metadata.name // empty
')"

此查询选择运行渲染所得目标镜像的 Ready Pod,并排除仍在排空流量的旧 Pod。如果没有返回 Pod,请停止,然后检查目标 Pod 的 SCC 和运行时 UID:

oc get pod -n "$GATEWAY_NAMESPACE" "$GATEWAY_POD" \
-o jsonpath='{.metadata.annotations.openshift\.io/scc}{"\n"}'
oc exec -n "$GATEWAY_NAMESPACE" "$GATEWAY_POD" -- id -u

第一行应显示 restricted-v2,第二行应显示运行中进程使用的 UID。请确认该 UID 位于项目分配的范围内。

回滚​

如果 Helm 原子升级失败,它会自动恢复之前的发布。重试前,请先诊断失败原因。

如需在升级成功后回滚,请确定之前的修订并恢复:

helm history "$GATEWAY_RELEASE" -n "$GATEWAY_NAMESPACE"

helm rollback "$GATEWAY_RELEASE" <previous-revision> \
--namespace "$GATEWAY_NAMESPACE" \
--wait \
--timeout "$GATEWAY_UPGRADE_TIMEOUT"

确认恢复后的 Pod 已就绪,并重复流量验证。

排查未就绪的网关​

停止观察后,在 Helm 仍在等待时,针对观察到的新目标 Pod 执行以下诊断。也可以在第三个终端中运行。原子升级失败并返回后,失败的 Pod 可能已经消失;请使用此前记录的输出和事件,再通过上方发布历史命令确认恢复后的修订。

oc get events -n "$GATEWAY_NAMESPACE" --sort-by=.lastTimestamp

oc get pods -n "$GATEWAY_NAMESPACE" \
-l "app.kubernetes.io/instance=$GATEWAY_RELEASE,app.kubernetes.io/component=gateway"

oc describe pod -n "$GATEWAY_NAMESPACE" <non-ready-gateway-pod>

oc logs -n "$GATEWAY_NAMESPACE" \
<non-ready-gateway-pod> \
--all-containers \
--tail=300

如果容器发生重启,请使用 oc describe pod 确定其名称,再通过 oc logs --previous 读取该容器的前一次日志。

对于 AISIX OSS 资源验证错误,请确认保存的 standalone values 指定了完整资源文件,且网关可以访问每个已配置的上游端点。

如果 SCC 准入拒绝目标 Pod,请检查 FailedCreate 事件,并移除未由项目分配的固定 runAsUser、runAsGroup 或 fsGroup 值。不要向网关 ServiceAccount 授予 anyuid。

如果 CRI-O 对自定义镜像报告 exec: Operation not permitted,请确认容器安全上下文保留了 NET_BIND_SERVICE。其他能力应继续全部丢弃。