平台升级后,组件共享单节点读写可用 PVC 的 SonarQube 实例 Pod 启动失败(Init),如何处理?
背景信息
-
v3.14 之前,平台支持使用单个 可用 PVC 部署 SonarQube;v3.14 之后,为满足各组件对存储的不同诉求(扩容、快照等),在平台上部署 SonarQube 时,需要为 SonarQube 的每个组件指定一个可用 PVC。
-
SonarQube 实例部署成功后,会启动 2 个 Pod postgresql 和 sonarqube,说明如下:
-
postgresql:运行 PostgreSQL 组件,名称如:
<SonarQube 实例的名称>-postgresql-<变量>。 -
sonarqube:运行 SonarQube 组件(SonarQube 主程序),名称如:
<SonarQube 实例的名称>-sonarqube-<变量>。
-
问题描述
在 v3.14 之前版本的平台上基于单个仅允许 单节点读写(RWO) 的 可用 PVC 部署的 SonarQube 实例,在平台升级至当前版本后,SonarQube 实例的 Pod 启动失败(有 1 个 Pod 持续处于 Init 状态)。
问题原因
使用单个 可用 PVC 部署的 SonarQube 实例的 2 个 Pod 会共用 1 个 PVC。
若 PVC 是基于 访问模式 为 单节点读写 的存储类创建的,且不支持延迟绑定。平台升级或其他原因(删除、新建)引发 SonarQube 实例的 Pod 重建时,一旦 2 个 Pod(postgresql 和 sonarqube)在重建过程中被调度至集群中的不同节点,就会导致 Pod 启动失败。
2 个 Pod 抢占 PVC 的顺序会使 Pod 处于不同的状态:
-
若 sonarqube 先占用了 PVC,则 postgresql 将处于
Init状态。这种情况下,由于 postgresql 未能正常初始化,会导致 sonarqube 进入CrashLoopBackOff状态。 -
若 postgresql 先占用了 PVC,sonarqube 会因为无法挂载 PVC,持续处于
Init状态。
临时解决方案:配置亲和性使实例 Pod 调度至同一节点
通过为 Sonarqube 资源配置 亲和性 (affinity),重建 Pod sonarqube 并使其调度至 Pod postgresql 所在节点。
操作步骤
-
查看并记录 Pod postgresql 的标签(labels)。
例如:
apiVersion: v1 kind: Pod metadata: labels: app: sonar-postgresql -
更新 Sonarqube 资源,添加亲和性(affinity)配置。
配置示例及说明如下:
apiVersion: operator.devops.alauda.io/v1alpha1 kind: Sonarqube metadata: annotations: overrideIntegratedName: sonar name: sonar namespace: default spec: # 添加 affinity 配置 helmValues: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: # postgresql Pod 标签的键 - key: app operator: In values: # postgresql Pod 标签对应的值 - sonar-postgresql topologyKey: kubernetes.io/hostname weight: 100
验证方法
更新 Sonarqube 资源后,会自动重启 Pod sonarqube。待 Pod 重启后,若 Pod postgresql、sonarqube 被调度至同一节点,则表明亲和性配置已生效。
说明:后续若 Pod postgresql 发生重启并被调度至其他节点,只需重启 Pod sonarqube,使其重新调度至 postgresql Pod 所在节点即可。
持久解决方案:为各组件配置独立的 PVC
通过将旧 PVC 中任一 Pod(sonarqube 或 postgresql)的数据迁移到一个新的 PVC,为 SonarQube 的每个组件配置独立的可用 PVC。
提示:本文将以迁移 PostgreSQL 的数据到新的 PVC 为例说明配置操作。
操作步骤
-
前往 Container Platform,在 SonarQube 实例所在的命名空间下,为 PostgreSQL 组件创建一个新的可用 PVC(持久卷声明)。
-
停用 Pod postgresql 和 sonarqube,避免迁移过程中,有新的数据写入。
-
切换至 平台管理 视图,卸载 SonarQube 实例所在集群上的 devops-tool-operator(位于 应用商店 > Operators > 已部署 Operators 页面)。
说明:
-
卸载 devops-tool-operator 是为了避免 Operator 还原 sonarqube 的配置,无法停用组件。
-
卸载期间,无法部署工具,但不影响其他组件的使用。
-
-
将 SonarQube 实例组件(SonarQube 和 PostgreSQL)的 Deployment 的 实例数(replicas) 更新为
0,停用 Pod postgresql 和 sonarqube。
-
-
在 SonarQube 实例所在命名空间下,创建一个 Pod(同时挂载新旧 PVC),用于将旧 PVC 上的数据拷贝至新的 PVC。
Pod 的 YAML 文件示例及说明如下:
注意:需将其中的变量(
<>中内容)替换为实际值。apiVersion: v1 kind: Pod metadata: name: copy-postgresql-data # Pod 的名称,可自定义 namespace: <SonarQube 实例所在的命名空间名称> spec: restartPolicy: Never containers: - name: copy image: <实际的带有 bash 的镜像地址> # 例如:registry.alauda.cn:60080/ops/ubuntu:23。可以在环境中查找 builder-go 的镜像(命令行:kubectl get clustertask -A -o yaml | grep builder-go) command: ["/bin/bash", "-c", "--"] args: # - "while true; do sleep 36000; done;" # 拷贝 postgresql 数据库数据至新的 PVC 中 - | set -ex ; mv -f /new/postgresql-db/ /new/postgresql-db-$(date +%Y%m%d%H%M%S) || true ; time cp --archive /old/postgresql-db/ /new/ ; ls -artl /new/postgresql-db/ ; du -sh /new/postgresql-db/ ; volumeMounts: - name: old-pvc mountPath: /old - name: new-pvc mountPath: /new resources: requests: cpu: 500m memory: 256Mi limits: cpu: 500m memory: 256Mi volumes: - name: old-pvc persistentVolumeClaim: claimName: <旧 PVC 的名称> - name: new-pvc persistentVolumeClaim: claimName: <新 PVC 的名称>提示:待 Pod 正常结束后,可以使用
kubectl logs <Pod 名称>命令查看拷贝日志。 -
重建 SonarQube 实例,为 PostgreSQL 组件指定新的 PVC。
-
在 SonarQube 实例所在集群的任一控制节点上,执行如下命令行更新 Sonarqube 资源。
# 请将 SonarQube 实例的名称替换为实际值。例如:kubectl get sonarqubes.operator.devops.alauda.io sonar -o yaml > sonar.yaml kubectl get sonarqubes.operator.devops.alauda.io <SonarQube 实例的名称> -o yaml > <SonarQube 实例的名称>.yaml资源配置示例及说明如下:
说明:仅需要更新 databaseExistingClaim 的值为 新 PVC 的名称 即可。
apiVersion: operator.devops.alauda.io/v1alpha1 kind: Sonarqube metadata: annotations: overrideIntegratedName: sonar creationTimestamp: "2024-01-07T07:47:21Z" finalizers: - finalizers.operator.devops.alauda.io generation: 1 name: sonar namespace: default resourceVersion: "192082" uid: 26b75d9f-903f-4578-8c04-d2e081f1a246 spec: account: ssoEnabled: false externalURL: http://192.168.131.28:32101 integratedIntoPlatform: true persistence: pvc: databaseExistingClaim: <新 PVC 的名称> webServiceExistingClaim: sonar-nfs-rwo # 旧 PVC 的名称,使用原有的实际值即可 type: PVC plugins: useDefaultPluginsPackage: true resourceAssign: globalAssign: resources: limits: cpu: "4" memory: 8Gi requests: cpu: "4" memory: 8Gi type: GlobalAssign service: nodePort: port: 32101 type: NodePort -
执行如下命令,重建 SonarQube 实例。
提示:命令执行时,会由于 devops-tool-operator 组件未就绪,导致该命令无法立即返回。请勿中断命令执行,并继续执行部署 devops-tool-operator 操作,待 Operator 部署成功并就绪后,该命令可正常返回。
cat <SonarQube 实例的名称>.yaml | kubectl replace --force -f -
-
-
重新部署 devops-tool-operator。
说明:devops-tool-operator 部署成功后,会自动设置 SonarQube 实例组件的 Deployment 的实例数并启动 Deployment。
验证方法
待 SonarQube 实例组件(SonarQube 和 PostgreSQL)的 Deployment 都启动成功后,若可以正常登录 SonarQube 查看完整的历史数据,且重新执行流水线时,SonarQube 正常可用,则表明配置已生效。
(可选)后续操作
待验证无误后,可通过清理旧 PVC 上的 PostgreSQL 组件相关的数据,释放存储空间。