background
r
j
A
7
=
8
8
h
A
%
I
K
@
5
<
e
/
f
G
o
s
A
1
)
2
r
(
*
n
h
/
)

favicon
favicon
回归终端
 

集群内部部署

Reverier-Xu at 2026-01-28 11:20:46 3.8+ R2SPL

新手推荐

如果你是首次部署回归终端,建议先看 Docker Compose 部署。Compose 方案可以用于生产环境,配置路径更直接;当你需要让平台服务、基础组件和题目容器都进入 Kubernetes 统一调度时,再使用本页的集群内部部署方案。

本文介绍将回归终端、平台基础组件和题目容器统一部署在 Kubernetes 集群内部的流程。该模式适合单服务器单节点办赛,也适合已经具备稳定 Kubernetes 运维能力的多节点集群。

使用 ret2boot

新建单服务器生产环境可以考虑使用 ret2boot。ret2boot 会通过交互式流程完成系统预检、Kubernetes、Helm、网关和平台部署。本页适合手工维护 Helm values、接入已有 Kubernetes 集群或规划多节点调度的场景。 不过也请注意,在网络环境不佳、与 GitHub 和 Docker 源通信存在较大问题的情况下本工具可能会出现问题。如果你对系统运维并不熟悉,不知道网络错误导致安装进程中断后如何清理,还是推荐使用手动操作部署方案。

集群内部部署会创建两个固定 namespace:

Namespace用途
ret2shell-platform运行平台服务、PostgreSQL、Valkey、NATS、registry 和 VictoriaLogs。
ret2shell-challenge运行平台动态创建的 challenge pods 和对应 Service。

平台工作负载以单副本 StatefulSet 运行。请保持平台副本数为 1,并通过节点标签、持久卷和备份策略保证该节点稳定。

安装 Helm

Helm 是 Kubernetes 的包管理工具。回归终端使用 Helm Chart 描述平台 StatefulSet、Service、Ingress、RBAC、ConfigMap、Secret、PVC 和各个基础组件。安装和升级时,Helm 会对比当前 release 与新的 Chart / values,并把差异应用到集群中。

Helm 3 官方提供了安装脚本。常规 Linux 环境可以直接执行下面的命令:

curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

需要审阅脚本、使用二进制包或使用系统包管理器时,可以参考 Helm 3 Installing Helm。安装完成后检查版本和 Kubernetes 连接:

helm version
kubectl cluster-info
kubectl get nodes -o wide

准备磁盘空间

正式部署前应先完成 环境准备中的资源估算。平台自身和基础组件的负载较低,主要资源压力来自 challenge pods。集群内部部署还需要为平台数据、数据库、缓存、队列、镜像仓库和日志组件准备持久化存储。

单场比赛的平台数据量通常可以控制在 20 GiB 以内。仅举办单场比赛时,100 GiB 磁盘通常可以覆盖系统、平台 PVC、镜像缓存、日志和少量备份。这个档位建议分配约 70Gi 给平台 PVC,剩余空间留给系统、k3s、容器运行时和镜像缓存。平台作为长期练习场持续维护时,磁盘建议从 1 TiB 起步。Chart 上游默认 PVC 容量偏向长期维护留量,单场比赛建议在 values.production.yaml 中按下表覆盖。

组件单场比赛 100Gi 磁盘档位长期练习场 1 TiB 档位保存内容规划建议
platform32Gi450Gibucket 仓库、附件、媒体文件、题目环境捕获文件、平台本地日志。与题目数量、附件大小、选手交互捕获量相关。
postgresql10Gi150Gi平台主数据库。与用户、队伍、提交、聊天、审计记录和配置数据相关。
valkey1Gi20Gi会话缓存、令牌、速率限制和临时状态。复制模式下每个 Valkey Pod 都会申请同等大小 PVC。
nats1Gi20GiJetStream 消息持久化。多副本模式下每个 NATS Pod 都会申请同等大小 PVC。
registry25Gi350Gi题目镜像层和 manifest。使用内置 registry 时需要。镜像较多时应优先扩容这一项。
victoriaLogs1Gi10Gi平台集中日志。日志留存周期较长时应提高容量,或接入外部日志系统。

单场比赛建议 PVC 合计为 70Gi,长期练习场档位合计约 1TiB。启用外部 PostgreSQL、Valkey、NATS、registry 或 VictoriaLogs 时,可以从总量中扣除对应组件。registry 和 VictoriaLogs 支持 disabled 模式;PostgreSQL、Valkey 和 NATS 是平台运行必需组件,应使用内置或外部服务。

K3s 默认提供 local-path StorageClass。单节点部署可以使用它作为起点:

kubectl get storageclass

生产环境建议使用 SSD 承载 PVC,并保留额外空间给容器运行时镜像缓存、题目镜像拉取、challenge pods 临时存储和数据库备份。使用多节点集群时,平台和内置基础组件应绑定到稳定节点,题目容器应调度到题目节点。

示例节点标签:

kubectl label node platform-node ret.sh.cn/workload=platform
kubectl label node worker-1 ret.sh.cn/workload=challenge
kubectl label node worker-2 ret.sh.cn/workload=challenge

拉取 Helm Chart

推荐从 GitHub Release 拉取已经打包好的 Helm Chart。下面的命令会读取最新 release,下载 Chart 和 checksum,并导出默认 values 作为配置起点:

sudo apt install -y jq
 
mkdir -p ~/ret2shell-deploy
cd ~/ret2shell-deploy
 
R2S_VERSION="$(curl -fsSL https://api.github.com/repos/ret2shell/ret2shell/releases/latest | jq -r '.tag_name')"
 
curl -fLO "https://github.com/ret2shell/ret2shell/releases/download/${R2S_VERSION}/ret2shell-${R2S_VERSION}.tgz"
curl -fLO "https://github.com/ret2shell/ret2shell/releases/download/${R2S_VERSION}/ret2shell-${R2S_VERSION}.tgz.sha256"
 
EXPECTED_SHA256="$(awk '{print $1}' "ret2shell-${R2S_VERSION}.tgz.sha256")"
ACTUAL_SHA256="$(sha256sum "ret2shell-${R2S_VERSION}.tgz" | awk '{print $1}')"
test "$EXPECTED_SHA256" = "$ACTUAL_SHA256"
 
helm show values "./ret2shell-${R2S_VERSION}.tgz" > values.upstream.yaml
cp values.upstream.yaml values.production.yaml

values.upstream.yaml 用来保留上游默认值,values.production.yaml 用来记录当前环境的实际配置。后续升级时继续维护 values.production.yaml,不要直接修改 values.upstream.yaml

创建本地静态 PV

K3s 默认提供 local-path StorageClass。测试环境可以直接使用 local-path,生产环境的单节点部署建议创建静态 Local PV。静态 Local PV 会把平台、数据库、缓存、队列、registry 和日志分别绑定到宿主机固定目录,并使用 Retain 回收策略保留数据。

使用云厂商块存储、Longhorn、Rook Ceph、NFS CSI 等动态存储时,可以跳过本节,直接在 values.production.yaml 中填写对应 StorageClass。

从当前 release 拉取本地 PV 模板:

cd ~/ret2shell-deploy
 
curl -fL "https://raw.githubusercontent.com/ret2shell/ret2shell/${R2S_VERSION}/deploy/k8s/storage-local.yaml" \
  -o storage-local.yaml

模板中的 Local PV 通过 nodeAffinity 绑定到节点。先查看实际节点名,再替换模板中的默认节点名。多节点集群应选择运行平台和内置基础组件的稳定节点:

kubectl get nodes -o wide
 
NODE_NAME="$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')"
sed -i "s/ret2shell-node-master/${NODE_NAME}/g" storage-local.yaml

按前面的磁盘规划调整 storage-local.yaml 中每个 PV 的 spec.capacity.storage。单场比赛 100Gi 磁盘档位可以使用下面的容量,长期练习场可以使用 1 TiB 档位:

组件PV 名称StorageClass宿主机目录单场比赛档位长期练习场档位
平台数据ret2shell-storage-platform-pv1ret2shell-storage-platform/srv/ret2shell/backend/storage/platform-pv132Gi450Gi
PostgreSQLret2shell-storage-database-pv1ret2shell-storage-database/srv/ret2shell/backend/storage/database-pv110Gi150Gi
Valkeyret2shell-storage-cache-pv1ret2shell-storage-cache/srv/ret2shell/backend/storage/cache-pv11Gi20Gi
NATSret2shell-storage-queue-pv1ret2shell-storage-queue/srv/ret2shell/backend/storage/queue-pv11Gi20Gi
registryret2shell-storage-registry-pv1ret2shell-storage-registry/srv/ret2shell/backend/storage/registry-pv125Gi350Gi
VictoriaLogsret2shell-storage-logs-pv1ret2shell-storage-logs/srv/ret2shell/backend/storage/logs-pv11Gi10Gi

PVC 的 size 应小于或等于对应 PV 的容量。为了便于审计和扩容,建议让 Helm values 中的 *.persistence.size 与 PV 容量保持一致。

创建宿主机目录并应用 PV 模板:

sudo mkdir -p /srv/ret2shell/backend/storage/{platform-pv1,database-pv1,cache-pv1,queue-pv1,registry-pv1,logs-pv1}
sudo chmod 1777 /srv/ret2shell/backend/storage/database-pv1
 
kubectl apply -f storage-local.yaml
kubectl get storageclass
kubectl get pv -o wide

部署前应看到 6 个 Available 状态的 PV。Helm 安装后,平台 namespace 中的 PVC 会按 StorageClass 绑定到这些 PV。

配置 values

部署前至少需要修改 values.production.yaml 中的外部访问地址、密钥、密码、存储类和入口方式。下面的示例展示了使用本地静态 PV 和 Ingress 的常见配置:

global:
  storageClass: ""
 
platform:
  image:
    tag: "<version>"
  exposure:
    type: ingress
  ingress:
    className: nginx
    hosts:
      - host: ctf.example.com
        paths:
          - path: /
            pathType: Prefix
    tls:
      - secretName: ret2shell-tls
        hosts:
          - ctf.example.com
  config:
    auth:
      signingKey: "replace-with-strong-random-key"
    server:
      externalDomain: ctf.example.com
      externalHttps: true
      corsOrigins: "https://ctf.example.com"
    cluster:
      enabled: true
      nodeSelector: "challenge"
      enableCapture: true
  nodeSelector:
    ret.sh.cn/workload: platform
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    storageClass: ret2shell-storage-platform
    size: 32Gi
 
postgresql:
  mode: internal
  auth:
    password: "replace-with-random-password"
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    storageClass: ret2shell-storage-database
    size: 10Gi
 
valkey:
  mode: internal
  auth:
    enabled: true
    password: "replace-with-random-password"
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    storageClass: ret2shell-storage-cache
    size: 1Gi
 
nats:
  mode: internal
  auth:
    enabled: true
    token: "replace-with-random-token"
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    storageClass: ret2shell-storage-queue
    size: 1Gi
 
registry:
  mode: internal
  externalAccess:
    enabled: true
    serviceType: NodePort
    nodePort: 30310
    host: registry.example.com:30310
    insecure: true
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    storageClass: ret2shell-storage-registry
    size: 25Gi
 
victoriaLogs:
  mode: internal
  persistence:
    enabled: true
    accessMode: ReadWriteOnce
    storageClass: ret2shell-storage-logs
    size: 1Gi

示例中的 <version> 应替换为下载的 release 版本号,例如命令中的 $R2S_VERSION。使用 local-path 或其他动态 StorageClass 时,可以删除各组件的 storageClass 字段,并把 global.storageClass 设置为目标 StorageClass。

选择 NodePort + 外置 Web Server 时,平台入口配置通常改为:

platform:
  exposure:
    type: nodePort
  service:
    nodePort: 30307
  config:
    server:
      externalDomain: ctf.example.com
      externalHttps: true
      corsOrigins: "https://ctf.example.com"

外置 Web Server 负责 TLS、WebSocket、静态资源缓存和 /v2 registry 代理时,需要把 externalDomain 设置为用户实际访问的平台域名。

准备入口和 TLS

Ingress 部署需要集群中已经存在 Ingress Controller。使用 Nginx Ingress Controller 时,可以通过 Helm 安装:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
 
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx \
  --create-namespace \
  --wait
 
kubectl -n ingress-nginx get pods,svc -o wide

Ingress Controller 通过 NodePort 暴露时,访问端口可以从 Service 中查看:

kubectl -n ingress-nginx get svc ingress-nginx-controller -o wide

使用真实域名和 80/443 入口时,可以把 Ingress Controller 调整为 hostNetwork DaemonSet。执行前应确认宿主机 80/443 没有被 Traefik、Nginx、Caddy 或其他服务占用:

helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx \
  --set controller.kind=DaemonSet \
  --set controller.hostNetwork=true \
  --set controller.service.type=ClusterIP \
  --wait

values 中配置了 platform.ingress.tls.secretName 时,需要先创建 TLS Secret:

kubectl create namespace ret2shell-platform --dry-run=client -o yaml | kubectl apply -f -
 
kubectl -n ret2shell-platform create secret tls ret2shell-tls \
  --cert=/path/to/fullchain.cer \
  --key=/path/to/private.key

使用 NodePort + 外置 Web Server 时,外置 Web Server 应转发到平台 NodePort,并保留 WebSocket 和真实客户端地址相关请求头。registry 代理需要把标准 /v2 路径转发到平台的 /api/cluster/registry/v2 路径。

入口配置完成后,应检查宿主机和 Kubernetes Service 的端口暴露状态:

sudo ss -lntp
kubectl get svc -A -o wide

公网通常只开放 80/443 或明确选择的业务入口端口。644310250、k3s Traefik 高位端口、平台 NodePort 和 registry NodePort 应通过云安全组、防火墙或网关访问控制限制到可信来源。

global

global.storageClass 是所有组件默认使用的 StorageClass。各组件的 *.persistence.storageClass 可以覆盖该值。global.imagePullSecrets 用于私有镜像仓库鉴权。global.labelsglobal.annotations 会附加到 chart 资源上,便于集群侧审计和资源分组。

platform

platform.image 控制平台镜像来源,默认仓库为 ghcr.io/ret2shell/ret2shellplatform.image.tag 应与下载的 Chart 版本保持一致,使用 digest 时以 digest 为准。

platform.exposure.type 支持 ingressnodePort。Ingress 适合使用集群内 Ingress Controller 统一暴露平台。NodePort 适合由宿主机 Nginx、Caddy 或已有网关转发到平台 Service。

platform.config.auth.signingKey 用于签发平台令牌,生产环境必须替换为随机强密钥。platform.config.server.externalDomainexternalHttps 决定平台生成的外部地址。corsOrigins 建议设置为实际访问源,例如 https://ctf.example.com

platform.config.cluster.enabled 控制动态题目容器功能。nodeSelector 会转换为 challenge pods 的 ret.sh.cn/workload=<值> 调度选择器。上面的示例会把题目容器调度到带有 ret.sh.cn/workload=challenge 标签的节点。enableCapture 会保存题目容器交互捕获数据,捕获量较大时应提高 platform.persistence.size

platform.persistence.size 对应 /var/lib/ret2shell。该目录保存平台 bucket、media、log 和 capture 数据。单场比赛 100Gi 磁盘档位建议从 32Gi 起步,长期练习场建议从 450Gi 起步。平台以单副本 StatefulSet 运行,platform.replicaCount 保持 1

platform.serviceAccountplatform.rbac 控制 Kubernetes API 权限。默认会创建 ServiceAccount 和 ClusterRoleBinding。受限集群可以预先创建账号和权限,然后设置 platform.serviceAccount.create=falseplatform.serviceAccount.name=<existing-sa>platform.rbac.create=false

postgresql

postgresql.mode 支持 internalexternal。内置模式会创建单实例 PostgreSQL StatefulSet、Secret、Service 和 PVC。单场比赛 100Gi 磁盘档位建议从 10Gi 起步,长期练习场建议从 150Gi 起步。外部模式需要填写 postgresql.external.hostportdatabaseusernamepasswordsslMode。自维护 PostgreSQL 可以参考 PostgreSQL Debian 安装文档

PostgreSQL 保存平台主数据。升级前建议先做数据库备份。已有云数据库或自维护 PostgreSQL 时,可以使用外部模式降低集群内磁盘压力。

valkey

valkey.mode 支持 internalexternal。内置模式默认创建单实例 Valkey StatefulSet,开启鉴权和 AOF 持久化。单场比赛 100Gi 磁盘档位建议从 1Gi 起步,长期练习场建议从 20Gi 起步。外部模式使用 valkey.external.url,格式为 redis://:<password>@host:6379/0。自维护 Valkey 可以参考 Valkey Installation

valkey.architecture=replication 会创建主从结构。复制模式下,每个 Valkey Pod 都会申请 valkey.persistence.size 指定的 PVC。

nats

nats.mode 支持 internalexternal。内置模式会创建 NATS StatefulSet,启用 JetStream。单场比赛 100Gi 磁盘档位建议从 1Gi 起步,长期练习场建议从 20Gi 起步。外部模式需要填写 nats.external.hostporttokenuserpasswordtls 中实际使用的字段。自维护 NATS 可以参考 Installing a NATS Server,Kubernetes 环境可以参考 NATS and Kubernetes

nats.replicaCount 大于 1 时会启用 NATS 集群路由。每个 NATS Pod 都会申请同等大小 PVC。

registry

registry.mode 支持 disabledinternalexternal。内置 registry 用于题目镜像上传、保存和集群侧拉取。单场比赛 100Gi 磁盘档位建议从 25Gi 起步,长期练习场建议从 350Gi 起步。题目镜像较多、镜像层较大或保留多个版本时,应优先扩容 registry。

registry.externalAccess.host 必须是 Kubernetes 节点可以访问的地址。平台推送镜像时使用 chart 内部 Service,节点拉取镜像时使用 externalAccess.host。使用 Docker Hub、GHCR、Harbor 或云厂商镜像仓库时,可以设置 registry.mode=external 并填写 registry.external.serverexternalusernamepassword。自建 registry 可以参考 CNCF Distribution 部署文档Harbor Installation and Configuration。使用托管仓库可以参考 Docker Hub RepositoriesGitHub Container Registry。不使用受控题目镜像仓库时,可以设置 registry.mode=disabled

registry.replicaCount 大于 1 需要 RWX 共享存储或等价的共享后端。默认 RWO 本地卷只适合单副本 registry。

victoriaLogs

victoriaLogs.mode 支持 disabledinternalexternal。内置模式会创建单实例 VictoriaLogs StatefulSet。单场比赛 100Gi 磁盘档位建议从 1Gi 起步,长期练习场建议从 10Gi 起步。外部模式使用 victoriaLogs.external.url。自维护 VictoriaLogs 可以参考 VictoriaLogs Quick Start

VictoriaLogs 用于集中查询平台日志和比赛行为记录。正式比赛建议保留该组件,或接入已有日志系统。

通用工作负载配置

各组件都支持 resourcesnodeSelectortolerationsaffinitypodAnnotationspodLabelspriorityClassNametopologySpreadConstraintspodDisruptionBudget。多节点部署时建议为平台和内置基础组件设置 nodeSelector,把它们固定在稳定节点上,再通过 platform.config.cluster.nodeSelector 把 challenge pods 调度到题目节点。

各组件的 metricsserviceMonitor 配置用于 Prometheus 生态监控。集群中已经安装 Prometheus Operator 时,可以开启对应的 ServiceMonitor。

安装平台

先创建平台 namespace。Ingress TLS Secret、外部镜像拉取 Secret 和预创建 ServiceAccount 都应放在这个 namespace 中:

kubectl create namespace ret2shell-platform --dry-run=client -o yaml | kubectl apply -f -

再渲染 Chart,检查 Kubernetes 资源和 values:

helm template ret2shell "./ret2shell-${R2S_VERSION}.tgz" \
  -n ret2shell-platform \
  -f values.production.yaml \
  > rendered.yaml
 
less rendered.yaml

确认配置后执行安装:

helm upgrade --install ret2shell "./ret2shell-${R2S_VERSION}.tgz" \
  -n ret2shell-platform \
  --create-namespace \
  -f values.production.yaml \
  --wait \
  --timeout 15m

安装完成后检查 release、Pod、PVC 和入口:

helm status ret2shell -n ret2shell-platform
 
kubectl -n ret2shell-platform get pods,pvc,svc,ingress -o wide
kubectl get pv -o wide
kubectl -n ret2shell-challenge get all

使用静态 Local PV 时,所有平台 PVC 都应处于 Bound 状态,并且 VOLUME 列应指向前面创建的 ret2shell-storage-* PV。

Chart 内置了基本连通性测试:

helm test ret2shell -n ret2shell-platform --logs

平台 Pod 正常运行后,通过配置的 Ingress 域名或外置 Web Server 地址访问平台。

后续升级

升级前先保存当前 values 和 release 状态:

cd ~/ret2shell-deploy
 
helm get values ret2shell -n ret2shell-platform > "values.installed.$(date +%F).yaml"
helm history ret2shell -n ret2shell-platform

拉取新的 Chart:

R2S_VERSION="$(curl -fsSL https://api.github.com/repos/ret2shell/ret2shell/releases/latest | jq -r '.tag_name')"
 
curl -fLO "https://github.com/ret2shell/ret2shell/releases/download/${R2S_VERSION}/ret2shell-${R2S_VERSION}.tgz"
curl -fLO "https://github.com/ret2shell/ret2shell/releases/download/${R2S_VERSION}/ret2shell-${R2S_VERSION}.tgz.sha256"
 
EXPECTED_SHA256="$(awk '{print $1}' "ret2shell-${R2S_VERSION}.tgz.sha256")"
ACTUAL_SHA256="$(sha256sum "ret2shell-${R2S_VERSION}.tgz" | awk '{print $1}')"
test "$EXPECTED_SHA256" = "$ACTUAL_SHA256"
 
helm show values "./ret2shell-${R2S_VERSION}.tgz" > "values.upstream.${R2S_VERSION}.yaml"

根据新的默认 values 检查字段变化,更新 values.production.yaml 后执行升级:

helm upgrade --install ret2shell "./ret2shell-${R2S_VERSION}.tgz" \
  -n ret2shell-platform \
  -f values.production.yaml \
  --wait \
  --timeout 15m

升级后检查状态:

kubectl -n ret2shell-platform rollout status statefulset/ret2shell-platform --timeout=5m
kubectl -n ret2shell-platform get pods,pvc -o wide
helm test ret2shell -n ret2shell-platform --logs

回滚到上一版本时,先查看 revision,再执行 rollback:

helm history ret2shell -n ret2shell-platform
helm rollback ret2shell <revision> -n ret2shell-platform --wait --timeout 15m

PVC 可以扩容,前提是对应 StorageClass 支持 allowVolumeExpansion。PVC 无法直接缩容。调整 *.persistence.size 后执行 Helm upgrade,再检查 PVC 容量和 Pod 状态。

日常检查与排障

查看平台组件:

kubectl -n ret2shell-platform get pods -o wide
kubectl -n ret2shell-platform get statefulset,deploy,svc,pvc -o wide
kubectl -n ret2shell-platform get events --sort-by=.lastTimestamp

查看平台日志:

kubectl -n ret2shell-platform logs statefulset/ret2shell-platform -c platform --tail=200
kubectl -n ret2shell-platform logs statefulset/ret2shell-platform -c platform -f
kubectl -n ret2shell-platform logs -l app.kubernetes.io/component=platform --since=1h

查看题目容器:

kubectl -n ret2shell-challenge get pods -o wide
kubectl -n ret2shell-challenge get svc -o wide
kubectl -n ret2shell-challenge get pods -l ret.sh.cn/challenge=<challenge_id> -o wide
kubectl -n ret2shell-challenge get pods -l ret.sh.cn/user=<user_id> -o wide
kubectl -n ret2shell-challenge get pods -l ret.sh.cn/team=<team_id> -o wide
kubectl -n ret2shell-challenge get pods -l 'ret.sh.cn/challenge=<challenge_id>,ret.sh.cn/user=<user_id>' -o wide

调试单个题目容器:

kubectl -n ret2shell-challenge describe pod <pod-name>
kubectl -n ret2shell-challenge logs <pod-name> -c <container-name> --tail=200
kubectl -n ret2shell-challenge exec -it <pod-name> -c <container-name> -- sh

检查调度和资源:

kubectl top nodes
kubectl top pods -A
kubectl describe node <node-name>
kubectl -n ret2shell-challenge describe pod <pod-name>

调试 PostgreSQL:

DB_POD="$(kubectl -n ret2shell-platform get pod -l app.kubernetes.io/component=postgresql -o jsonpath='{.items[0].metadata.name}')"
 
kubectl -n ret2shell-platform exec -it "$DB_POD" -- sh -lc 'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"'
kubectl -n ret2shell-platform logs "$DB_POD" --tail=200

调试 Valkey:

CACHE_POD="$(kubectl -n ret2shell-platform get pod -l app.kubernetes.io/component=valkey -o jsonpath='{.items[0].metadata.name}')"
CACHE_PASS="$(kubectl -n ret2shell-platform get secret ret2shell-valkey -o jsonpath='{.data.password}' | base64 -d)"
 
kubectl -n ret2shell-platform exec -it "$CACHE_POD" -- valkey-cli -a "$CACHE_PASS" ping
kubectl -n ret2shell-platform logs "$CACHE_POD" --tail=200

调试 NATS:

NATS_TOKEN="$(kubectl -n ret2shell-platform get secret ret2shell-nats -o jsonpath='{.data.token}' | base64 -d)"
 
kubectl -n ret2shell-platform run nats-debug \
  --rm -it \
  --image=natsio/nats-box \
  --restart=Never \
  --env=NATS_TOKEN="$NATS_TOKEN" \
  -- sh -lc 'nats --server nats://ret2shell-nats:4222 --auth "$NATS_TOKEN" stream ls'

调试 registry:

kubectl -n ret2shell-platform port-forward svc/ret2shell-registry 5000:5000
curl -fsSL http://127.0.0.1:5000/v2/
curl -fsSL http://127.0.0.1:5000/v2/_catalog

调试 VictoriaLogs:

kubectl -n ret2shell-platform port-forward svc/ret2shell-victoria-logs 9428:9428
curl -fsSL http://127.0.0.1:9428/metrics >/tmp/victoria-logs.metrics
curl -G 'http://127.0.0.1:9428/select/logsql/query' --data-urlencode 'query=* | limit 20'

诊断入口问题:

kubectl -n ret2shell-platform get ingress -o wide
kubectl -n ret2shell-platform describe ingress ret2shell-platform
kubectl -n ret2shell-platform get svc ret2shell-platform -o wide
kubectl -n ret2shell-platform get svc ret2shell-platform-nodeport -o wide --ignore-not-found
kubectl -n ingress-nginx get pods,svc -o wide

诊断 PVC 问题:

kubectl -n ret2shell-platform get pvc -o wide
kubectl -n ret2shell-platform describe pvc <pvc-name>
kubectl get storageclass -o wide
kubectl get pv -o wide
kubectl describe pv <pv-name>

使用本地静态 PV 时,还应在节点上检查宿主机目录是否存在并且可以写入:

sudo ls -al /srv/ret2shell/backend/storage
sudo du -sh /srv/ret2shell/backend/storage/*

排障时通常先看 ret2shell-platform namespace 的平台日志和事件,再看 ret2shell-challenge namespace 中对应题目 Pod 的状态、日志、Service 和节点调度结果。题目容器的标签可以直接定位到用户、队伍、题目和 traffic token。

上线前检查

正式开放前建议完成以下检查:

项目检查方式
TLS 与域名浏览器访问平台域名,确认证书链、HTTPS 跳转和 WebSocket 正常。
PV / PVCkubectl get pvkubectl -n ret2shell-platform get pvc 均显示已绑定,节点重启后数据仍在宿主机目录中。
平台文件上传图片、题目附件和题目镜像,确认上传、下载和展示正常。
动态容器启动一题带在线环境的题目,确认 Pod、Service、访问地址和回收流程正常。
registry使用内置 registry 时,确认 /v2 代理、镜像上传和节点拉取均正常。
邮件启用 SMTP 后发送测试邮件,确认验证邮件和找回密码邮件内容正确。
端口暴露公网只开放业务入口所需端口,Kubernetes API、kubelet、Traefik 高位端口、平台 NodePort 和 registry NodePort 应受安全组或防火墙限制。

后续维护

集群内部部署完成后,应继续阅读 日常维护手册,建立数据库备份、日志保留、镜像清理、平台升级、题目容器巡检和比赛期间应急处理流程。