1. 项目概述
最近在部署CVAT(Computer Vision Annotation Tool)计算机视觉标注系统时,遇到了一系列生产环境特有的挑战。作为一个基于Kubernetes的开源标注工具,CVAT虽然提供了Helm Chart,但在实际企业环境中部署时,往往会遇到各种官方文档未提及的"坑"。
本文将详细记录在Kubernetes 1.21集群上部署CVAT 2.56的全过程,重点解决以下核心问题:
- 如何在特定GPU节点(带有污点)上调度CVAT服务
- 如何将CVAT的持久化存储对接Ceph集群
- 如何配置HTTPS访问以解决浏览器安全策略问题
- 如何处理内网环境下的依赖下载问题
2. 环境准备与Chart处理
2.1 下载Chart包
首先需要从官方仓库拉取CVAT的Helm Chart:
helm pull oci://registry-1.docker.io/cvat/cvat --version 2.55.0 tar -xzf cvat-2.55.0.tgz cd cvat这里需要注意,由于公司内网环境限制,可能需要配置HTTP代理才能成功拉取Chart:
export http_proxy=http://proxy.example.com:8080 export https_proxy=http://proxy.example.com:80802.2 解决Kubernetes版本兼容性问题
官方Chart的Chart.yaml中明确要求Kubernetes版本>=1.23.0,而我们的生产环境仍在使用1.21.10。直接安装会报版本不兼容错误。
解决方案是修改Chart.yaml文件:
# Chart.yaml kubeVersion: '>= 1.21.0-0' # 将1.23改为1.21注意:这种版本降级操作存在一定风险,需要确保API兼容性。经过测试,CVAT 2.56的核心功能在K8s 1.21上可以正常运行。
2.3 初始化配置文件
为了保持原始配置文件的完整性,建议创建一份覆盖配置:
cp values.yaml values.override.yaml3. 核心配置修改
3.1 存储配置:对接Ceph集群
CVAT默认使用20Gi的本地存储,这显然不适合生产环境。我们需要将其修改为使用Ceph存储:
# values.override.yaml defaultStorage: enabled: true storageClassName: rook-cephfs # 指定Ceph StorageClass accessModes: - ReadWriteMany # 多节点读写模式 size: 1000Gi # 生产环境建议大容量对于KV Rocks(Redis)的存储也需要类似配置:
kvrocks: persistence: enabled: true storageClassName: rook-cephfs accessModes: - ReadWriteOnce size: 100Gi3.2 节点调度配置
我们的CVAT需要运行在特定的GPU节点gpusrv14上,该节点有两个特点:
- 有GPU资源
- 被打上了污点
dedicated=ceph:NoSchedule
首先给目标节点打上标签:
kubectl label node gpusrv14.aa.com cvat-node=true然后在values文件中配置调度策略:
cvat: backend: # 节点亲和性配置 affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - gpusrv14.aa.com # 节点选择器 nodeSelector: cvat-node: "true" # 污点容忍配置 tolerations: - key: "dedicated" operator: "Equal" value: "ceph" effect: "NoSchedule"关键点:CVAT Chart设计得比较好的一点是backend配置会被worker等子组件继承,所以不需要每个微服务都单独配置调度策略。
4. 部署与问题排查
4.1 执行安装命令
准备好配置后,执行Helm安装:
helm install gpu-cvat . -n gpu-cvat --create-namespace -f values.yaml -f values.override.yaml4.2 OPA服务连接问题
部署后遇到的第一个问题是OPA(Open Policy Agent)服务不断重启。查看日志发现健康检查失败:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='opa', port=8181): ... Connection refused原因分析:
- OPA默认只监听localhost(127.0.0.1)
- Kubernetes的健康检查是通过Pod IP访问的
- 导致探针连接被拒绝,服务被判定为不健康
临时解决方案是直接编辑Deployment:
kubectl edit deployment gpu-cvat-opa -n gpu-cvat在容器参数中添加--addr=0.0.0.0:8181:
containers: - args: - run - --server - --addr=0.0.0.0:8181 # 关键修改 - --bundle永久解决方案是在values文件中配置:
cvat: opa: extraArgs: - "--addr=0.0.0.0:8181"4.3 浏览器安全策略问题
配置好Nginx转发后,访问页面出现白屏或报错:
- API请求报405 Not Allowed或401 Unauthorized
- 控制台报错:
The Cross-Origin-Opener-Policy header has been ignored... - JS报错:
ReferenceError: structuredClone is not defined
原因分析: CVAT使用了SharedArrayBuffer等现代浏览器特性,这些特性要求页面必须在安全上下文(Secure Context)中运行,即必须是HTTPS或localhost。
解决方案是配置Nginx SSL并添加必要的安全头:
server { listen 443 ssl; server_name gpu-cvat.aa.com; ssl_certificate /etc/nginx/conf.d/sans_crt2/server.crt; ssl_certificate_key /etc/nginx/conf.d/sans_crt2/server.key; # 后端API转发 location ~ ^/(api|auth|admin|django-static)/ { proxy_pass http://gpu-cvat-backend-service.gpu-cvat.svc.cluster.local:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 前端页面转发 location / { proxy_pass http://gpu-cvat-frontend-service.gpu-cvat.svc.cluster.local:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 必须的安全头 add_header Cross-Origin-Opener-Policy 'same-origin'; add_header Cross-Origin-Embedder-Policy 'require-corp'; } }5. 后期配置与管理
5.1 创建管理员账号
CVAT默认不创建任何账号,需要通过Django命令创建超级用户:
export HELM_RELEASE_NAMESPACE="gpu-cvat" export HELM_RELEASE_NAME="gpu-cvat" BACKEND_POD_NAME=$(kubectl get pod --namespace $HELM_RELEASE_NAMESPACE -l tier=backend,app.kubernetes.io/instance=$HELM_RELEASE_NAME,component=server -o jsonpath='{.items[0].metadata.name}') kubectl exec -it --namespace $HELM_RELEASE_NAMESPACE $BACKEND_POD_NAME -c cvat-backend -- python manage.py createsuperuser5.2 验证存储配置
确认Ceph存储是否正确挂载:
kubectl get pv | grep gpu-cvat查看PV详情,确认使用的是Ceph存储:
kubectl describe pv/pvc-ec561f53-c54b-47d7-9ddb-6e01850046df | grep subvolumePath6. 经验总结与注意事项
微服务架构的运维复杂性: CVAT采用微服务架构,虽然提高了灵活性,但也增加了运维复杂度。特别是当需要统一配置(如节点调度策略)时,需要确保配置能正确继承到所有相关组件。
生产环境默认配置不足: 很多默认配置(如OPA监听地址)在开发环境可能没问题,但在生产环境会导致严重问题。部署前需要仔细检查各服务的默认配置。
HTTPS不是可选项: 现代浏览器安全策略要求类似CVAT这样的复杂前端应用必须运行在HTTPS下,这一点官方文档强调不足,容易导致部署后出现难以排查的前端问题。
内网环境适配: 在内网环境中部署时,需要注意:
- 配置HTTP代理
- 可能需要预先下载依赖镜像
- 某些服务(如Grafana插件)可能需要离线安装
- 版本兼容性测试: 虽然我们通过修改Chart.yaml成功在K8s 1.21上部署了CVAT,但这种操作存在风险。建议:
- 充分测试核心功能
- 记录所有修改点
- 制定回滚方案
- 监控与日志收集: CVAT包含多个组件,建议部署后立即配置:
- 集中式日志收集
- 资源使用监控
- 健康检查告警
在实际使用过程中,我们还发现CVAT的Redis实例(KV Rocks)在高负载下可能出现性能问题,建议根据实际使用情况调整资源配置和持久化策略。