WSL2下的GPU虚拟化

本文主要记录WSL2下通过k8s管理GPU用于模型WDDM、推理和转码的原理和流程,分为部署、推理、转码三个部分。

部署

环境

  • 显卡:4卡L208卡 5090

  • WSL2

    image-20251223163542672

    1. Wsl2:2.6.2.0
    2. Ubuntu:24.04
  • Windows

    image-20251223163934497

    1. Windows:win11 23H2 22631.2428

架构图

GPU虚拟化.drawio

  • pod视角

pod--> nvidia-cuda-toolkit --> wsl2 --> nvidia-container-toolkit --> windows driver

image-20251223162232252

网络(Bridge)

  • 原理图

image-20251223164352885

  • 效果图

image-20251223164429834

  • 配置方式

    1. vSwitch: Hyper-V创建Virtual Switches

    2. .wslconfig:用户目录配置.wslconfig,内容如下:

      1
      2
      3
      4
      5
      [ws12]
      networkingMode-bridged
      vmSwitch=WSLBridge
      dhcp=false
      swap=0 ##K8S集群必须关闭SWAP
  • 注意点:

    1. Bridge模式主要用于K8S的混合节点通信,对齐同一网络平面
    2. 默认的Mirrored无法支持
    3. WSL2Bridge模式不支持挂载多张网卡

Nvidia

Nvidia-Container-Toolkit

  • 指南

    https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/1.9.0/nvidia-containerd.html

  • 安装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    # Install the prerequisites for the instructions below:
    sudo apt-get update && sudo apt-get install -y --no-install-recommends \
    curl \
    gnupg2

    # Configure the production repository:
    curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
    && curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
    sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
    sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

    # Install the NVIDIA Container Toolkit packages:
    sudo apt-get update
    export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.18.1-1
    sudo apt-get install -y \
    nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
    nvidia-container-toolkit-base=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
    libnvidia-container-tools=${NVIDIA_CONTAINER_TOOLKIT_VERSION} \
    libnvidia-container1=${NVIDIA_CONTAINER_TOOLKIT_VERSION}
  • 配置(Containerd)

    1
    2
    3
    4
    # Configure the container runtime by using the nvidia-ctk command:
    sudo nvidia-ctk runtime configure --runtime=containerd
    # Restart containerd:
    sudo systemctl restart containerd
  • 补丁

    image-20251223192245372

Nvidia-Device-Plugin

  • 作用

    1
    仅用于GPU的申请和调度
  • Prerequisites

    1
    2
    3
    4
    5
    6
    The list of prerequisites for running the NVIDIA device plugin is described below:

    NVIDIA drivers ~= 384.81
    nvidia-docker >= 2.0 || nvidia-container-toolkit >= 1.7.0 (>= 1.11.0 to use integrated GPUs on Tegra-based systems)
    nvidia-container-runtime configured as the default low-level runtime
    Kubernetes version >= 1.10
  • Quick Start

    1
    2
    3
    # 1. configure containerd
    # 2. Daemonset
    kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.17.1/deployments/static/nvidia-device-plugin.yml

Nvidia-CUDA-Toolkit

cuda 13.0.0和ubuntu 24.04为例

  • 13.0.0-base-ubuntu24.04(FFmpeg)

    1
    Includes the CUDA runtime (cudart)
  • 13.0.0-runtime-ubuntu24.04(推理)

    1
    Builds on the base and includes the CUDA math libraries, and NCCL. A runtime image that also includes cuDNN is available. Some images may also include TensorRT.
  • 13.0.0-devel-ubuntu24.04(构建)

    1
    Builds on the runtime and includes headers, development tools for building CUDA images. These images are particularly useful for multi-stage builds.

推理

vLLM

  • 兼容性:

    1. Qwen3-VL-32Bqwen3-vl需要vLLM>=0.11.0,单卡显存不足,需要多卡
    2. 由于WSL2不支持NCCLP2P,需要使用SHM进行多卡通信
  • 部署

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    namespace: dev
    name: vllm-v1
    labels:
    app: vllm-v1
    spec:
    replicas: 1
    strategy:
    type: Recreate
    selector:
    matchLabels:
    app: vllm-v1
    template:
    metadata:
    labels:
    app: vllm-v1
    spec:
    nodeSelector:
    node-role.kubernetes.io/worker-gpu: "true"
    containers:
    - name: vllm
    env:
    - name: NCCL_CUMEM_ENABLE
    value: "0"
    image: docker.1ms.run/vllm/vllm-openai:v0.11.1
    imagePullPolicy: IfNotPresent
    command:
    - sh
    - -c
    - |
    vllm serve "/models/Qwen/Qwen3-VL-32B-Instruct" \
    --served-model-name "Qwen3-VL-32B-Instruct" \
    --tensor-parallel-size 4 \
    --port 9999 \
    --max-model-len 32768 \
    --gpu-memory-utilization 0.85 \
    --max-num-seqs 10 \
    --max-num-batched-tokens 32768 \
    --dtype bfloat16
    ports:
    - containerPort: 9999
    name: http
    resources:
    limits:
    nvidia.com/gpu: "4"
    requests:
    nvidia.com/gpu: "4"
    volumeMounts:
    - mountPath: /models
    name: qwen-data
    subPathExpr: models
    - mountPath: /dev/shm
    name: dshm
    volumes:
    - name: qwen-data
    persistentVolumeClaim:
    claimName: pvc-nas-test
    - name: dshm
    emptyDir:
    medium: Memory
    sizeLimit: 16G

转码

FFmpeg

  • 兼容性:

    1. 安装:来源于https://github.com/BtbN/FFmpeg-Builds,也可以编译定制
    2. WSL2:由于WSL2默认只会自动挂载libcuda.soffmpeg对应的编解码需要手动挂载
  • 部署

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    namespace: dev
    name: ffempeg-cuda-v1
    labels:
    app: ffempeg-cuda-v1
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: ffempeg-cuda-v1
    template:
    metadata:
    name: ffempeg-cuda-v1
    labels:
    app: ffempeg-cuda-v1
    spec:
    containers:
    - name: ffempeg-cuda-v1
    image: docker.1ms.run/nvidia/cuda:12.6.3-base-ubuntu22.04
    imagePullPolicy: IfNotPresent
    command:
    - sh
    - -c
    - |
    tail -f /dev/null
    restartPolicy: Always
  • 测试用例

    1
    2
    3
    4
    5
    6
    ./ffmpeg -hwaccel cuda -hwaccel_output_format cuda \
    -threads 2 \
    -i input.mp4 \
    -vf "scale_cuda=854:480" \
    -c:v h264_nvenc -preset p1 -cq 28 \
    -c:a copy -y output.mp4

WDDM

L20

  • 作用:Windows宿主机通过WDDM使用dxdiag识别显卡,支持运行windows的图形软件

  • 支持模式

    image-20251223182017230

  • 开启Physical Display Modes

    image-20251223182124657

  • TCC改为WDDM

    image-20251223182359183

参考链接

分享到

Nas迁移恢复

Nas主要包含JellyfinMergefsMiniDlnaSmb等套件,其中MiniDlnaSmb无须处理,开机启动即可,主要需要修改JellyfinMergefs套件配置。

  • Jellyfin

  • Mergefs

分享到

mesh下Gateway落地

本文主要讨论Istio Ingress Gateway入口方案,网关选型为Istio Ingress Gateway(Envoy网关的一种),部署方式为中心化网关进行部署,在腾讯云落地方式目前主要考虑两种方式:

CLB复用

  • 架构图:

  • 优点:

    1. 对现有入口调整改动小,可以复用CLB,落地周期短
    2. 可通过CLB规则控制灰度流量到URL粒度
  • 缺点:

    1. 需维护CLB和Service NodePort的手动绑定关系,多一层的DNAT开销
    2. 长远来看随着流量的增大超过单CLB入口流量仍需采用GTM方案

iGTM

  • 构架图:
  • 优点:
    1. 对现有入口调整改动大,借助DNS负载均衡,实现多CLB共存,长远来看增强入口的扩展性和可用性
    2. 结合HttpDNS可实现入口流量秒级容灾,如果无需秒级可以不用开启(作为纯入口方案也可以不引入)
    3. 借助腾讯云的网关管理控制器可实现CLB与Istio Ingress Gateway的自动绑定关系,方便维护
  • 缺点:
    1. 依赖于iGTM和Mesh的SSL证书上浮等产品的稳定性,落地周期长
    2. HttpDNS秒级容灾会产生费用开销,费用成本上升(作为纯入口方案也可以不引入)

另附Envoy Gateway与传统的SpringCloud Gateway对比:

  • 优点:
    1. Envoy Gateway相对于Java异步网关更能贴合K8s,与k8s的资源对象结合更紧密
    2. Envoy Gateway在大流量、扩展性、可配置性方面强于Java异步网关
    3. Envoy Gateway是面向未来的网关选型,相信社区会逐步增强和标准化Envoy网关产品以适应更广泛的应用场景
  • 缺点
    1. Envoy Gateway在领域认知度和技术栈熟悉方面方面逊于Java异步网关
    2. 基于Envoy Gateway定制化扩展的框架较少,定制化难度目前相对较高

参考链接:

亲历者复盘:网易的 Envoy 网关选型、开发与改造

诗和远方:蚂蚁金服 Service Mesh 深度实践 | QCon 实录

分享到

mesh腾讯云物理架构

Mesh物理架构

分享到

mesh与cvm的对比

Mesh与CVM的对比

物理对比

监控对比

  • 日志
    1. 业务日志
      • CVM: ES,通过host.hostname区分
      • Mesh:ES,通过pod.namespace,pod.ip,pod.name区分
    2. Tomcat访问日志
      • CVM:cvm机器,通过jumpserver访问cvm机器
      • Mesh:ES,通过pod.namespace,pod.ip,pod.name区分
    3. GC日志
      • CVM:同Tomcat访问日志
      • Mesh:原理是通过kubectl查询,UI可采用Zadig或者Tke控制台查看
    4. Sentinel日志
      • CVM:同Tomcat访问日志
      • Mesh:存储介质为cfs,可通过cvm机器挂载后查看
  • 指标
    1. Prometheus
      • CVM:原理是通过JVM应用获取并上传,新增cvm需运维手动绑定
      • Mesh:原理同cvm,新增pod会自动绑定
    2. Cat
      • CVM:Cat Client埋点上传
      • Mesh:同cvm
  • 链路
    1. Skywalking
      • CVM:javaagent引入Skywalking Agent,由插件织入埋点上传
      • Mesh:同cvm,sidecar埋点暂缺失

排障对比

  • 日志
    1. 报错日志
      • CVM:结合具体日志,通过metadata前往cvm下监控系统的日志、指标、链路定位和处理,对象主要为cvm机器
      • Mesh:结合具体日志,通过metadata前往Mesh下监控系统的日志、指标、链路定位和处理,对象主要为k8s
  • 权限
    1. 权限划分
      • CVM:用户账号划分
      • Mesh:登录容器与应用进程为同一用户,看运维最终如何处理?
  • 工具
    1. 安装
      • CVM:申请运维安装,cvm安装一次后终身存在
      • Mesh:申请运维安装,pod安装一次后,重启即消失,常用工具可提前放置镜像
    2. 调试
      • CVM:进入cvm简单调试,深度调试需要申请运维进行账号权限提级,并且可以单独摘除流量查看
      • Mesh:通过k8s简单调试,深度调试情况比较复杂,目前借助nacos在应用层面可以单独摘除流量查看
分享到

jmx远程调试

Remote Debug开启

  • 开启远程调试
1
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8739
  • idea开启debug

img

分享到

lily新手入门

概述

元数据定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
metadata:
group: mdd
version: 1.0
region: region1
env: env1
zone: zone1
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
name: ${spring.application.name}

Pom引入

  • Mdd Dependencies

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <dependencyManagement>
    <dependencies>
    <dependency>
    <groupId>com.mdd</groupId>
    <artifactId>mdd-dependencies</artifactId>
    <version>${version.mdd-dependencies}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>${version.spring-cloud}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>

    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
    <version>${version.spring-cloud-alibaba}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>
    </dependencies>
    </dependencyManagement>
  • SpringCloud Gateway

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <dependencies>
    <dependency>
    <groupId>com.mdd</groupId>
    <artifactId>mdd-cloud-gateway-starter</artifactId>
    </dependency>

    <dependency>
    <groupId>com.mdd</groupId>
    <artifactId>mdd-lily-strategy-mdc-starter</artifactId>
    </dependency>

    <!-- <dependency>-->
    <!-- <groupId>com.mdd</groupId>-->
    <!-- <artifactId>mdd-lily-strategy-skywalking-starter</artifactId>-->
    <!-- </dependency>-->
    </dependencies>

  • Service

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <dependencies>
    <dependency>
    <groupId>com.mdd</groupId>
    <artifactId>mdd-cloud-servlet-starter</artifactId>
    </dependency>

    <dependency>
    <groupId>com.mdd</groupId>
    <artifactId>mdd-lily-strategy-mdc-starter</artifactId>
    </dependency>

    <!-- <dependency>-->
    <!-- <groupId>com.mdd</groupId>-->
    <!-- <artifactId>mdd-lily-strategy-skywalking-starter</artifactId>-->
    <!-- </dependency>-->
    </dependencies>

Http Header推送

image-20210830152652480

配置中心推送

配置中心推送

分享到

lily解决方案

概述

核心模块

主要分为注册中心、配置中心、管理中心、策略编排

  • 注册中心

    基于Nacos注册中心,接管ServiceRegistry和ServerList处理Instance的Metadata(version,env,group,region,zone)

  • 配置中心

    基于Nacos配置中心,处理本地、远程等配置更新

  • 管理中心

    暴露Rest Endpoint接口,提供核心Api

  • 策略编排

    1. 实现网关和服务的路由功能

    2. 处理Header参数的拦截、传递

    3. 处理策略的过滤匹配

    4. 输出调用链和日志

全链路监控

  • 调用链监控
    1. 基于SkyWalking链路
    2. 输出l-h-*链路策略信息
    3. 输出l-h-s-*输出服务自身信息
  • 日志监控
    1. 基于MDC链路
    2. 输出l-h-*链路策略信息
    3. 输出l-h-s-*输出服务自身信息

元数据自定义

  • 本地配置

    本地Metadata(version,env,group,region,zone)配置

  • header配置

    http请求header指定Metadata

  • 注册中心配置

    注册中心动态化,依赖Nacos

分享到

lily概述

概述

核心功能

基于Spring Cloud、Spring Cloud Alibaba服务注册发现,Ribbon负载均衡,Feign和Rest Template调用,Spring Cloud Gateway

  • 全链路版本、环境、IP地址和端口匹配动态路由
  • 全链路自定义网关、服务的过滤器、负载均衡策略发布
  • 全链路条件匹配、非条件匹配
  • 服务实时性流量无损下线:IP和端口屏蔽
  • 异步场景下全链路追踪和路由:异步跨线程Agent插件
  • 全链路调用链追踪、日志监测
  • 提供服务端、消费端隔离、注册级别隔离和准入
  • 本地和远程,局部和全局配置策略驱动
  • 配置中心(Nacos)、Swagger和Rest规则策略推送
  • 基于Header参数化规则策略驱动
  • 限流、熔断、降级防护

版本兼容列表

Mdd Dependencies Version Spring Cloud Version Spring Cloud Alibaba Version Spring Boot Version
2.0.0 Hoxton.SR9 2.2.5.RELEASE 2.3.8.RELEASE

概念介绍

  • 滚动发布
    1. 概念:每次滚动升级一个或者多个服务,升级完成后监控观察,直到所有旧版本服务升级到新版本。属于有损发布
    2. 优点:升级快捷,影响范围小,只会影响滚动发布的服务。
    3. 缺点:在滚动升级过程中,无法快速无损回滚,必须降级部署
  • 全链路路由

image-20210830143225872

分享到

Jedis引发GC问题

背景介绍

线上应用经常收到跨服务间SocketTimeout的异常,服务间的调用设置超时时间为500ms,查看Cat监控发现,发生超时异常都伴随着FGC,并且FGC时间都大于500ms,于是决定dump heap镜像。

问题定位分析

通过mat查看heap dump镜像,发现Finalizer引用队列在活跃对象占比高达31.43%,猜测可能有泄漏。

1621478132

进一步分析发现,SocksSocketImpl对象高达113529多个,大量的对象都是Jedis链接,由此确定Jedis可能发生泄漏,同时查看Old区空间稳步斜率增长,怀疑对象未充分YGC即进入Old区,造成下次FGC较长的STW,引发超时。

1621478647

1621478719

GC问题验证

接下来就是验证上述猜测了,通过获取gc相关的参数,查看JVM各区的容量,发现S0S1容量被调整非常小大约2m左右,结合收集器配置,情况如下:

1621479168

应用采用默认的PSPO收集器,并且默认开启-XX:+UseAdaptiveSizePolicy,系统会通过吞吐量(cpugc时间)计算,优先保证吞吐量来分配EdenFromTosize,最终导致S0,S1区降为2m。结合GC日志和Cat监控,导致Old的问题就明朗了,每次YGC结束,由于PSPO收集器的缘故,S0S1区太小无法,导致配置的晋升参数8几乎失效,对象晋升过快被拷贝到Old区,于是添加-XX:+PrintTenuringDistribution上线验证,得到如下大量GC日志:

Desired survivor size 5048576 bytes, new threshold 1 (max 8)

GC问题调整

上述日志验证猜想,对象由于S0S1size太小,晋升过快,问题得到验证,解决就简单了:

  1. 更换收集器,PN+CMS或者G1均可
  2. 仍然采用PS+PO收集器,关闭-XX:-UseAdaptiveSizePolicy,手动设置XmnSurvivorRatio的值

考虑heap内存比较小,4g左右,根据R大的一些分享,采用方案2,进行解决,通过Cat获取S0S1Eden的大小以及After FGC后活跃对象的占比,设置对应参数即可,以下来自于美团的设置分享:

1621480549

根据项目实际监控情况,我们未完全上述经验值,对项目进行更合适的设置如下:

1
2
3
4
5
-Xms4G -Xmx4G -Xmn2560M -XX:MetaspaceSize=256M

-XX:-UseAdaptiveSizePolicy

-XX:MaxTenuringThreshold=15 -XX:TargetSurvivorRatio=80 -XX:SurvivorRatio=18

Jedis问题解决

上述Jedis垃圾一方面由于GC配置参数不合理导致晋升不合理,查看Jedis的连接池的配置,也进行了调整,查看GenericObjectPool代码,定时任务检测驱逐对象,关键代码如下:

20181018222116696

20181018222136328

2018101822220313920181018222149994

从上面代码我们看出,每隔一段时间,就会检测对象池里面对象,要是发现对象空闲时间超过一定时间,就会强制回收;然后又发现链接少于minIdle了,开始创建对象,以满足mindle。调整Redis client 设置的检测轮询时间为1分钟,设置miniIdle为5,修改后上线观察。

调整前后对比:

调整前:

1621480961

1621481201

调整后:

1621481022

1621481096

通过上述对比可以看到调整后YGC的次数比调整前减半,调整后的Oldgen也几乎稳定不增长了,YGC由于少了大量的From区往Old区的拷贝(减少Survivor拷贝,GC标记较快,主要耗时为拷贝),YGC时间耗时大幅下降,GC吞吐量获得提升。

参考链接:

JVM GC 之「AdaptiveSizePolicy」实战

分享到