跳转至

命名空间统一(→ server)变更 SOP

交付物目录:ns_migration/ 变更时间:2026-09-18 00:32 – 01:35(CST) 结果:成功 变更性质:跨组件命名空间迁移(含 GitLab 全栈重建、Traefik 卸载重装) 实现体说明:文件侧替换由 ns_migration/replace_ns.py 承载(幂等);集群侧为手工执行序列,无独立实现体,文末附录给出操作依据的完整来源

目录

1. 背景

集群重建(K8s v1.36.4)后,/home/rain/Documents/k8s_ins/ 下各交付工程自行选定了命名空间,导致同一套能力分散在多个命名空间:

组件 迁移前命名空间 说明
Traefik(入口代理) traefik 2026-09-17 部署
GitLab 全栈 gitlab / gitlab-pg / gitlab-redis / gitlab-minio 2026-09-17 部署,四命名空间为 chart 设计
LDAP 计划为 ldap 尚未部署,仅清单与文档
(监控栈) monitoring 本次明确排除

触发条件:要求「将 /home/rain/Documents/k8s_ins 下所有含 deploy 的目录对应的命名空间改为 server」,且变更层面为文件与集群同步迁移。

2. 目标

变更完成后应达到的可验证状态:

  1. Traefik release traefik 位于 server,入口行为不变(HTTP 404 / Dashboard 401→200 / 证书正确)。
  2. GitLab 全栈(GitLab chart + MinIO + PostgreSQL + Redis + Envoy Gateway)全部位于 server,登录页 200、/-/health 返回 GitLab OK,migrations Job 为 Completed。
  3. 存储落点与语义一致:gitlab-pg-1 → pv-postgres-ssd、redis-data → pv-redis-ssd、repo-data-gitlab-gitaly-0 → pv-gitaly-ssd、minio-data → nfs-csi。
  4. 旧命名空间 traefik / gitlab / gitlab-pg / gitlab-redis / gitlab-minio 已删除。
  5. 三个交付工程的文件中命名空间引用全部为 server(可 grep 复核,无旧值残留)。

3. 前置条件

项目 要求 本次实测
集群权限 cluster-admin kubeconfig kubernetes-admin@kubernetes,API 192.168.31.8:16443
执行机工具 helm / kubectl / openssl / curl / ssh helm v3.16.3、kubectl v1.36.4、OpenSSL 3.0.13
节点访问 免密 ssh 至 k8s-node-01(192.168.31.13)与 k8s-master-01(192.168.31.10) 免密可用
数据状态 确认被迁移组件无有效数据(决定走「重建路径」还是「数据搬迁路径」) GitLab 空:projects=0、Gitaly 仅 +gitaly、MinIO 仅空 bucket
存储策略 local PV 回收策略须为 Retain 三个 PV 均为 Retain(数据在 /mnt/ssd/k8s/ 下不随 PVC 删除而丢失)
变更前备份 四类快照导出完毕 见第 7 章
影响面确认 确认监控栈不在本次范围 用户明确排除 prometheus_deploy

数据状态检查命令(决定路径的关键判据):

# GitLab 是否有项目(预期 0 才可走重建路径)
kubectl -n gitlab-pg exec gitlab-pg-1 -c postgres -- \
  psql -U postgres -d gitlabhq_production -t -c "select count(*) from projects;"   # 预期 0
# Gitaly 是否有仓库(预期只有 +gitaly)
kubectl -n gitlab exec gitlab-gitaly-0 -- ls -1 /home/git/repositories             # 预期 +gitaly
# MinIO 桶是否为空
kubectl -n gitlab-minio exec sts/minio -- ls -1 /data                              # 预期仅有空桶目录

4. 影响范围

  • 命名空间:新建内容于 server;删除 traefik、gitlab、gitlab-pg、gitlab-redis、gitlab-minio(删除命名空间会级联删除其中的 PVC ——【高危】)。
  • 主机:
  • k8s-master-01(192.168.31.10):Traefik 的 hostPort 80/443/389/636 所在节点,迁移期间短暂释放再占用。
  • k8s-node-01(192.168.31.13):三个 local PV 的数据目录 /mnt/ssd/k8s/{gitaly,postgres,redis},本次以改名方式让出旧数据,不删除。
  • NFS 服务器(MinIO 卷后端):新建动态卷,旧卷随 PVC 删除(本次无有效数据)。
  • 停机:
  • Traefik 入口:卸载到重装之间,约 1–2 分钟无法提供 HTTP/HTTPS 入口。
  • GitLab 全栈:从 helm uninstall 到新实例 migrations 完成,约 20 分钟不可用(gitlab.rainchen.top 及 CI、registry、KAS 均受影响)。
  • 建议窗口:无 CI 任务、无 Git 推送的时段。
  • 不受影响:monitoring(监控栈,明确排除)、cnpg-system(CNPG operator 为集群级,不动)、Calico/Tigera 组件。

5. 分步操作

实现文件:ns_migration/replace_ns.py(文件侧替换,幂等) 集群侧无对应实现文件,以下为实际执行序列的等价表达,供阅读、复核与手工复现。 本次变更以实现体(脚本)+ 现场执行记录(apply-*.txt)为准,本段代码块与二者如有出入以二者为准。

阶段 A:文件侧命名空间替换(可离线,不影响集群)

cd /home/rain/Documents/k8s_ins                                   # 交付物根目录
# A.1 先预览:打印每一处将改动的行号与前后内容,不写盘
python3 ns_migration/replace_ns.py --old traefik --new server --dry-run \
  traefik_deploy/SOP.md traefik_deploy/docs/Helm使用手册-Traefik.md \
  traefik_deploy/manifests/10-dashboard-auth.yaml                 # 预期 57 处
python3 ns_migration/replace_ns.py --old ldap --new server --dry-run ldap-deploy/SOP.md    # 预期 36 处
for ns in gitlab gitlab-pg gitlab-redis gitlab-minio; do         # GitLab 四个旧 ns 逐个替换
  python3 ns_migration/replace_ns.py --old "$ns" --new server --dry-run gitlab-deploy-v2/SOP.md
done                                                             # 预期合计 47 处
# A.2 落盘(脚本自动在同目录生成 .bak.<时间戳> 备份,未经用户确认不删除)
#     实际执行时已先手工另存一份原始文件到 ns_migration/(见第 7 章)
# A.3 幂等复核:再跑一次 dry-run,预期全部输出 [无变化]

替换规则与边界:脚本仅匹配三种命名空间语境 —— -n <旧值>、namespace: <旧值>、namespace <旧值>,并用前后边界断言排除域名与资源名(实测未误伤 ldap.rainchen.com、ldap-svc、openldap-0、traefik-system)。

跨命名空间地址需单独处理(不属上述三种语境,脚本不覆盖):

# GitLab values 中的 DB / Redis 地址(2 处)
#   gitlab-pg-rw.gitlab-pg.svc.cluster.local  ->  gitlab-pg-rw.server.svc.cluster.local
#   redis.gitlab-redis.svc.cluster.local      ->  redis.server.svc.cluster.local
# MinIO 端点(SOP 内 4 处 s3cfg / object-storage / registry-storage 模板)
#   minio.gitlab-minio.svc.cluster.local:9000 ->  minio.server.svc.cluster.local:9000

阶段 B:Traefik 迁移(traefik → server)

BASE="/home/rain/Documents/k8s_ins/traefik_deploy"               # Traefik 交付物目录
helm get values traefik -n traefik > "$BASE/backup/values-before-ns-move.yaml"   # 先备份现网 values

helm uninstall traefik -n traefik                                # 【高危】卸载,入口即中断
# 在目标命名空间重建两个命名空间级资源(复用原证书与口令,避免客户端重新适配)
kubectl -n server create secret tls traefik-selfsigned-tls \
  --cert="$BASE/certs/tls.crt" --key="$BASE/certs/tls.key" \
  --dry-run=client -o yaml | kubectl apply -f -
kubectl -n server create secret generic traefik-dashboard-auth \
  --from-literal=users="admin:$(openssl passwd -apr1 "$(取口令)")" \
  --dry-run=client -o yaml | kubectl apply -f -                  # 口令取自 secrets/dashboard-basic-auth.txt,不写本文档
kubectl apply -f "$BASE/manifests/10-dashboard-auth.yaml"        # Dashboard 认证中间件(ns 已改为 server)
helm upgrade --install traefik "$BASE/chart/traefik-41.6.0.tgz" \
  -n server -f "$BASE/values.yaml" --skip-crds --wait --timeout 5m   # 部署到 server

阶段 C:GitLab 全栈迁移(4 个命名空间 → server)

数据为空,采用重建路径;若数据非空,须改走「数据搬迁 + PV claimRef 迁移」路径(见 GitLab迁移到server-方案与风险.md)。

# C.1 备份(见第 7 章与 backup/README.md)
helm get values gitlab -n gitlab -o yaml > ns_migration/backup/gitlab-values-before.yaml
for ns in gitlab gitlab-pg gitlab-redis gitlab-minio; do
  kubectl -n "$ns" get secret -o yaml > "ns_migration/backup/secrets-$ns.yaml"   # 含敏感内容,权限 600
done

# C.2 下线旧实例
helm uninstall gitlab -n gitlab                                   # 【高危】卸载 GitLab release
kubectl -n gitlab-pg delete cluster gitlab-pg --wait=false         # 删除 CNPG 集群定义
kubectl delete namespace gitlab gitlab-pg gitlab-redis gitlab-minio   # 【高危】级联删除其内 PVC

# C.3 让出旧数据(改名而非删除,保留可回滚)
ssh root@192.168.31.13 'for d in gitaly postgres redis; do
  mv /mnt/ssd/k8s/$d /mnt/ssd/k8s/$d.bak.20260918005523; mkdir -p /mnt/ssd/k8s/$d; done'
kubectl delete pv pv-gitaly-ssd pv-postgres-ssd pv-redis-ssd       # 【高危】删除已 Released 的 PV

# C.4 重建 PV,并用 claimRef 预绑定锁定「卷↔PVC」对应关系(关键,防交叉绑定)
kubectl apply -f - <<'YAML'
apiVersion: v1
kind: PersistentVolume
metadata: {name: pv-gitaly-ssd}
spec:
  capacity: {storage: 20Gi}
  accessModes: [ReadWriteOnce]
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-ssd
  local: {path: /mnt/ssd/k8s/gitaly}
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - {key: kubernetes.io/hostname, operator: In, values: ["k8s-node-01"]}
YAML
# pv-postgres-ssd / pv-redis-ssd 同构,容量 5Gi,路径分别指向 postgres / redis
# 预绑定:claimRef 只写 apiVersion/kind/namespace/name,不写 uid(写 uid 会变 Released 而无法再绑定)
kubectl patch pv pv-gitaly-ssd   -p '{"spec":{"claimRef":null}}'
kubectl patch pv pv-postgres-ssd -p '{"spec":{"claimRef":null}}'
kubectl patch pv pv-gitaly-ssd --type=merge \
  -p '{"spec":{"claimRef":{"apiVersion":"v1","kind":"PersistentVolumeClaim","namespace":"server","name":"repo-data-gitlab-gitaly-0"}}}'
kubectl patch pv pv-postgres-ssd --type=merge \
  -p '{"spec":{"claimRef":{"apiVersion":"v1","kind":"PersistentVolumeClaim","namespace":"server","name":"gitlab-pg-1"}}}'

# C.5 在 server 重建依赖组件(口令随机生成,落盘于 ns_migration/secrets/,权限 600)
kubectl create namespace server --dry-run=client -o yaml | kubectl apply -f -
kubectl -n server create secret generic gitlab-pg-app --from-literal=username=gitlab --from-literal=password="$PG_PASS" ...
kubectl -n server create secret generic gitlab-redis --from-literal=password="$REDIS_PASS" ...
kubectl -n server create secret generic minio-root-credentials --from-literal=root-user=... --from-literal=root-password=... ...
# MinIO:PVC(nfs-csi 50Gi) + Service + StatefulSet,随后用 mc 建 6 个桶并构造 3 个存储 Secret
# PostgreSQL:apply Cluster CR(不预建 PVC —— CNPG 必须自行创建,见附录 B)
# Redis:PVC(volumeName=pv-redis-ssd) + Service + StatefulSet

# C.6 部署 GitLab chart(values 中 DB/Redis 地址已指向 server)
kubectl delete gatewayclass gitlab-gw --ignore-not-found          # 清理旧 release 残留(见附录 B)
cd /home/rain/Documents/k8s_ins/gitlab-deploy-v2
helm template gitlab ./gitlab -n server -f values-gitlab.yaml > /dev/null   # 先本地渲染复核,预期 0 个监控类资源
helm upgrade --install gitlab ./gitlab -n server -f values-gitlab.yaml --timeout 900s

6. 验证

# 1) Traefik 入口(迁移后行为应与迁移前一致)
helm list -n server | grep traefik                                # 预期 STATUS=deployed
curl -s -o /dev/null -w '%{http_code}\n' http://192.168.31.10/    # 预期 404(已接管且无匹配路由)
curl -sk -o /dev/null -w '%{http_code}\n' \
  --resolve traefik.rainchen.top:443:192.168.31.10 \
  https://traefik.rainchen.top/dashboard/                         # 预期 401(未带凭据)

# 2) GitLab 工作负载与迁移任务
kubectl -n server get deploy,sts --no-headers                     # 预期全部 1/1 或 2/2
kubectl -n server get job | grep migrations                       # 预期 Completed
kubectl -n server run gl-verify --rm -i --restart=Never \
  --image=curlimages/curl:latest --quiet -- \
  sh -c 'curl -s -o /dev/null -w "%{http_code}\n" -H "Host: gitlab.rainchen.top" \
         http://envoy-server-gitlab-gw-80b44f81/users/sign_in'     # 预期 200
# 注意:数据面 Service 是 envoy-server-<gateway 名>;不要连 envoy-gateway(那是控制面 18000/19001)

# 3) 存储对应关系(语义正确性)
kubectl -n server get pvc --no-headers | awk '{print $1, $2, $3}'
# 预期:gitlab-pg-1→pv-postgres-ssd、redis-data→pv-redis-ssd、
#       repo-data-gitlab-gitaly-0→pv-gitaly-ssd、minio-data→pvc-*(nfs-csi)

# 4) 旧命名空间已消失、文件侧无残留
kubectl get ns | grep -E 'traefik|gitlab' || echo "旧命名空间已清理"
grep -rn -- '-n traefik\|-n ldap\|-n gitlab\b' traefik_deploy ldap-deploy gitlab-deploy-v2 \
  --include='*.md' --include='*.yaml' | grep -v '\.bak\.' || echo "文件侧无旧命名空间引用"

幂等复验:文件侧重跑 replace_ns.py --dry-run 应全部输出 [无变化](本次已实测通过);集群侧重跑 helm upgrade 后 Deployment generation 不变、Pod 无重启。

7. 回滚

备份位置(不散落在被改文件旁),均已登记在各自的 backup/README.md:

备份 位置 内容 能回退什么
GitLab 迁移前全套 ns_migration/backup/ values / manifest / Secret / CNPG Cluster / PV-PVC / 命名空间清单 GitLab 全栈配置与凭据
Traefik 迁移前 values traefik_deploy/backup/values-before-ns-move.yaml 迁移前 release values Traefik 配置
文件替换前原文 各文件旁 .bak.<时间戳>(traefik_deploy/backup/、gitlab-deploy-v2/backup/、ldap-deploy/backup/) 替换前的文档与清单 文件侧改动
节点数据目录 k8s-node-01:/mnt/ssd/k8s/{gitaly,postgres,redis}.bak.20260918005523 迁移前数据目录 旧数据(本次为空)

回滚步骤:

# 回滚 A:Traefik 回到 traefik 命名空间
kubectl create namespace traefik --dry-run=client -o yaml | kubectl apply -f -
helm upgrade --install traefik "$BASE/chart/traefik-41.6.0.tgz" -n traefik \
  -f "$BASE/backup/values-before-ns-move.yaml" --skip-crds --wait --timeout 5m

# 回滚 B:GitLab 回到原四个命名空间(【高危】,需重建命名空间与 PVC 绑定)
#   1) 从 ns_migration/backup/ 恢复 Secret 与 Cluster 定义
#   2) 删除 server 中的 PVC,将 PV claimRef 改回原 命名空间/PVC 名
#   3) 在原命名空间重装 chart(使用 backup/gitlab-values-before.yaml)
# 回滚 C:文件侧
cp traefik_deploy/backup/SOP.md.bak.<时间戳> traefik_deploy/SOP.md     # 逐个还原,勿用通配删除

回滚后验证:Traefik helm list -n traefik 有 release 且 curl http://192.168.31.10/ 返回 404;GitLab 在旧命名空间 kubectl get pods 全部 Ready、登录页 200。

8. 风险说明

风险 等级 说明 缓解措施
数据丢失(GitLab) 高 删除命名空间会级联删除 PVC;MinIO 卷为 Delete 策略,删 PVC 即删数据 迁移前复核数据为空(projects=0);local PV 为 Retain 且数据目录改用改名保留;无有效数据才走重建路径
入口中断 中 Traefik 单副本 + hostPort,卸载到重装期间 80/443 无监听 与 GitLab 迁移合并到同一窗口;迁移后立即验证
GitLab 停机超预期 中 首次 migrations 耗时约 8 分钟,叠加镜像与 CNPG initdb 预留 30 分钟窗口;--timeout 900s 给足时间
PV 交叉绑定 中 两个 5Gi PV 同规格,K8s 随机分配(本次实际发生) 用 claimRef 预绑定锁定对应关系;验证章节逐条核对
CNPG 无法 bootstrap 中 预建 PVC 会让 CNPG 状态机卡死(本次实际发生) 不预建 CNPG 的 PVC,改用 PV 预绑定;详见附录 B
集群级残留阻塞部署 中 旧 release 的 GatewayClass 因 finalizer 卡在删除中(本次实际发生) 部署前清理旧 release 的集群级资源;详见附录 B
凭据散落 中 备份中含明文 Secret;新口令落盘在 ns_migration/secrets/ 备份文件权限 600;backup/README.md 已标注敏感项与优先删除建议
文件与集群不一致 低 只改文件不改集群会造成"文档说 server、实际在旧 ns" 本次要求两者同步;若分步执行,必须在文件中标注当前实际状态

附录 A:执行时间线

时间(CST) 动作 结果
00:32 Traefik 卸载并在 server 重装 release deployed;入口 404/401/200
00:38 删除空命名空间 traefik 成功
00:48 文件侧批量替换(93 处)与幂等复验 零变更通过
00:49 GitLab 四类快照备份 ns_migration/backup/
00:55 下线旧 GitLab(release + 4 ns + 旧 PV) PV 转入 Released
00:55 节点数据目录改名保留、重建 PV 三个 PV Available
01:06 server 内重建 MinIO / PostgreSQL / Redis MinIO Ready;PG 经排障后 Ready
01:13 GitLab chart 部署成功 release deployed
01:22 migrations 完成、登录页 200 验证通过
01:35 文档注记与台账收尾 完成

附录 B:排障记录

变更过程中遇到三个阻塞性问题,均已在实施中解决;根因与处置的完整记录见 INCIDENT-20260918-01-ns迁移期排障.md(同目录)。摘要:

  1. 两个同规格 local PV 被交叉绑定:PG 绑到了 redis 卷、Redis 绑到了 pg 卷;修复时新 PVC 又抢占了 20Gi 的 gitaly 卷。根因是 K8s 对同规格 Available PV 先到先得。处置:改用 PV claimRef 预绑定锁定对应关系。
  2. CNPG 卡在 BootstrapPending:预先手工创建的 gitlab-pg-1 PVC 让 CNPG 无法识别归属,operator 每个 reconcile 都重试创建 initdb Job(即使 Job 已 Complete)。处置:删除手工 PVC,改用 PV claimRef 预绑定,让 CNPG 自行创建 PVC。
  3. GatewayClass gitlab-gw 卡在删除中:Gateway API 标准 finalizer(gateway-exists-finalizer)阻止删除,而负责清理的 Envoy Gateway controller 已随旧命名空间消失,导致新 release 无法接管(报 release-namespace must equal "server")。处置:确认无 Gateway 引用后手工摘除 finalizer 并删除,重新部署成功。

附录 C:纳入与排除的判定

判定依据:该目录对应的资源是否具有业务命名空间语义。

目录 判定 理由
traefik_deploy 纳入 release 命名空间明确,迁移代价低
ldap-deploy / ldap 纳入(仅文件) LDAP 尚未部署,清单与文档先行统一
gitlab-deploy-v2 纳入(集群 + 文件) 用户明确要求;数据为空,走重建路径
prometheus_deploy 排除 用户明确排除;监控栈迁移会牵涉历史数据
k8s_136_cluster_deploy 排除 集群构建工程(Python),其内 YAML 为 kube-system/tigera-operator 备份,无业务 ns 语义
gitlab-deploy-v2/gitlab 排除 GitLab 官方 chart 源码,命名空间由 {{ .Release.Namespace }} 决定,改模板即破坏 chart

附录 D:交付物清单

文件 用途
SOP.md 本文档:本次变更的实施、验证与回滚档案
RUNBOOK.md server 命名空间内各服务的日常运行与故障处置手册
INCIDENT-20260918-01-ns迁移期排障.md 本次三个阻塞故障的台账与复盘
replace_ns.py 文件侧命名空间替换实现体(幂等、带边界断言)
apply-*.txt / dryrun-*.txt 每一轮替换的逐行变更记录(可回溯改了什么)
backup/ + backup/README.md 迁移前集群资源快照与台账
secrets/gitlab-credentials.env 新建的 PG / Redis / MinIO 口令(权限 600)
render-check-server.yaml 部署前的 chart 渲染复核产物
GitLab迁移到server-方案与风险.md 数据非空场景下的迁移方案(本次未走该路径)

来源:/home/rain/Documents/k8s_ins/ns_migration/SOP.md | 最后更新:2026-09-19 16:54(由 build/sync.py 同步,请勿在站点侧修改)