[!NOTE] 说明
本篇中Gitlab的服务器环境:基于WSL的Ubuntu Linux操作系统本篇用"<>"括起来的内容,表示这部分内容与个人电脑设置相关,需要按实际情况替换成自己的内容
前期准备
服务器的准备
常用命令
pwd:显示当前工作目录路径,如:/home/<用户名>
whoami:显示当前用户名(就是<用户名>@<设备名>中前面的那个)
ls:列出目录内容
cd:切换目录
mkdir:创建目录
mv:移动文件
rm:删除文件
cat:查看文件
nano/vim:编辑文件
确认服务器信息
查看系统版本
cat /etc/os-release
输出类似:
PRETTY_NAME="Ubuntu 24.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.4 LTS (Noble Numbat)"
可确认以后安装 GitLab 应该参考哪个Ubuntu版本,如24.04.4版本。
查看内存
free -h
输出类似:
total used free shared buff/cache available
Mem: 62Gi 28Gi 32Gi 571Mi 2.4Gi 33Gi
Swap: 16Gi 0B 16Gi
Gitlab要求至少4GB,推荐8G以上。
查看CPU核数
nproc
输出一个数字,表示核数。
Gitlab要求至少2核。
查看磁盘空间
df -h
Gitlab要求至少4GB。最好至少剩余 15~20 GB。
查看IP地址
hostname -I
会输出一个IP地址。如果是WSL IP地址,可能会在WSL重启后发生变化。
查看CPU架构
uname -m
输出x86_64
(这条在排查问题[[#SSH服务启动失败]]的时候有用)
SSH服务的安装(如需要)
- 在服务器中执行:
sudo apt update
sudo apt install openssh-server
sudo表示需要提供管理员权限的操作,apt是Ubuntu中的软件包管理器。
sudo apt update表示列出所有可更新的软件清单
sudo apt install openssh-server表示安装ssh服务
2. 安装完后检查是否启动,输入:
sudo systemctl status ssh
输出信息类似:
● ssh.service - OpenBSD Secure Shell serverLoaded: loaded (/usr/lib/systemd/system/ssh.service; enabled; preset: enabled)Active: active (running) since <一个时间>
只要看到显示active状态,就表示ssh启动成功,可以在其他电脑上(比如你自己的Windows终端)ssh登录了。
3. 设置开机自动启动:
sudo systemctl enable ssh
远程登录服务器
信息准备
登录远程服务器需要的信息:
- 服务器 IP 地址
- 用户名(root或普通用户)
- 登录密码
SSH登录命令
打开powershell,输入:
ssh <用户名>@<ip地址>
如果有端口号(像我们这次的wsl虚拟环境):
ssh <用户名>@<ip地址> -p <端口号,如22>
第一次连接可能会提示:
Are you sure you want to continue connecting (yes/no)?
输入yes,然后输入密码。
password:<密码>
当命令行头变为<用户名>@<设备名>:~$时,即为登录成功。这时候,你敲的命令实际上是在服务器上执行,不是在自己的电脑上执行。
退出登录
要停止ssh连接,输入:
exit
Gitlab安装
方法一:Docker方式安装
[!NOTE] 说明
这种安装方式我在VMware虚拟机上体验过,但没有在团队的wsl服务器上使用。
Docker安装
方法一:官方Docker仓库(未采用,待测试)
- 安装依赖:
sudo apt install -y ca-certificates curl gnupg
- 添加 Docker 官方 GPG Key:
sudo install -m 0755 -d /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/ubuntu/gpg \
| sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpgsudo chmod a+r /etc/apt/keyrings/docker.gpg
- 添加 Docker 仓库:
echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \https://download.docker.com/linux/ubuntu \$(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
- 刷新:
sudo apt update
- 安装 Docker命令:
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
方法二:用Ubuntu官方提供的Docker(较简单,采用)
执行
sudo apt update
后,直接执行:
sudo apt install docker.io
测试是否成功安装
执行:
docker --version
若有输出(Docker version 版本号),说明安装成功。
hello-world测试
运行官方测试:
sudo docker run hello-world
如果看到(可能需要等待一段时间):
Hello from Docker!
说明:
- Docker Engine 正常
- Docker 网络正常
- Docker Hub 可以访问
- 容器能够正常启动
把自己加入 docker 组(可选)
执行:
sudo usermod -aG docker $USER
然后exit退出 SSH,重新登录,再测试:
docker ps
这样,就不用每次都用sudo。
Gitlab安装
- 创建 GitLab 数据目录:
sudo mkdir -p /srv/gitlab/config /srv/gitlab/logs /srv/gitlab/data
- config,logs,data表示配置,日志,数据文件
- 修改目录权限:
sudo chown -R $USER:$USER /srv/gitlab
解释:
- chown:修改文件所有者
- -R:递归修改
- $USER:当前用户名(不用自己改)
- 启动 GitLab
docker run --detach \--hostname <服务器ip地址> \--publish <网页端口>:<网页端口> \--publish <GitLab的SSH clone端口>:22 \--name gitlab \--restart always \--volume /srv/gitlab/config:/etc/gitlab \--volume /srv/gitlab/logs:/var/log/gitlab \--volume /srv/gitlab/data:/var/opt/gitlab \--shm-size 256m \--env GITLAB_OMNIBUS_CONFIG="external_url 'http://<服务器ip地址>:<网页端口>'; gitlab_rails['gitlab_shell_ssh_port'] = <GitLab的SSH clone端口>;" \gitlab/gitlab-ce:latest
其中关键参数含义:
--detach:后台运行容器;--hostname:设置容器主机名;--publish <网页端口>:<网页端口>:将宿主机 <网页端口> 映射到容器 <网页端口>;--publish <SSH clone端口>:22:将宿主机 <SSH clone端口> 映射到容器 SSH 22;--name gitlab:容器名设为gitlab;--restart always:容器异常退出或 Docker 重启后自动拉起;--volume:把配置、日志和数据持久化到宿主机;GITLAB_OMNIBUS_CONFIG:通过环境变量写入 GitLab Omnibus 配置;external_url:GitLab 对外生成链接时使用的基础地址;gitlab_shell_ssh_port:网页上显示的 Git Clone SSH 端口;gitlab/gitlab-ce:latest:GitLab CE 官方镜像。
[!NOTE] 说明
<服务器ip地址>:通过执行hostname -I得到;
<网页端口>:你自定义的Gitlab网页http端口号,不和已占用端口号冲突;
<GitLab的SSH clone端口>你自定义的GitLab的SSH clone端口号,不和已占用端口号冲突;
22:服务器默认SSH端口。
执行以上命令后,将会下载镜像,可能需等待一段时间。
- 启动后看状态:
docker ps
输出类似(应该有一个gitlab容器,且状态为Up):
CONTAINER ID IMAGE STATUS
xxxx gitlab/gitlab-ce:latest Up ...
- 看 GitLab 初始化日志:
docker logs -f gitlab
看到类似 “GitLab Reconfigured!”或者日志不再疯狂滚动(第一次启动需要一端时间)后,说明Gitlab安装成功。
方法二:Linux安装包方式安装
由于使用团队服务器环境测试 Docker 的 hello-world 时发现问题:连接 Docker Hub 超时。这表明,国内服务器从 Docker Hub 上下载镜像需要连接外网,否则无法下载。由于团队服务器没有配置镜像加速器,所以我们实际没有采用 Docker 方式安装,而是采用这种方式安装。(详见[[#Docker下载镜像失败]])
Ubuntu 20.04、22.04 和 24.04版本支持直接安装 gitlab-ce 或 gitlab-ee 软件包。
- 测试 GitLab 软件包仓库是否能访问:
curl -I --connect-timeout 10 https://packages.gitlab.com
curl -I --connect-timeout 10 https://storage.googleapis.com
若返回200或400,说明正常。
2. 安装基础依赖:
sudo apt update
sudo apt install -y curl ca-certificates openssh-server tzdata
- 添加 GitLab CE 仓库:
curl --silent \"https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh" \| sudo bash
- 安装。GitLab 网页可以使用一个未被占用的端口(如8080,8081等,也可以直接使用服务器IP):
sudo EXTERNAL_URL="http://101.6.48.103:<端口号>" apt install gitlab-ce
安装将会经过一段时间。期间会自动初始化 PostgreSQL、Redis、Puma、Sidekiq、GitLab Workhorse 和内置 Nginx。
[!NOTE]
确认服务器的端口能够从外部访问,而且没有被其他程序使用的方法:sudo ss -lntp | grep ':<端口号>'如果没有输出,表示本机目前没有程序占用它。
Gitlab相关管理和配置命令
查看状态命令
sudo gitlab-ctl status
正常状态下,所有服务都是run状态。
配置命令
sudo gitlab-ctl reconfigure
出现gitlab Reconfigured!才算成功。
网络访问请求
curl -I http://127.0.0.1:<端口号>
只有输出HTTP/1.1 302 Found或HTTP/1.1 200 OK时才表示正常。
日志查看
sudo gitlab-ctl tail puma
此命令按Ctrl+C退出。
puma是举例,可以写gitlab服务名字。
此命令可以查看最近内容,避免刷屏:
sudo tail -n 100 /var/log/gitlab/puma/current
应用层服务重启:
sudo gitlab-ctl restart puma
GitLab请求路径:浏览器 → Nginx → GitLab Workhorse → Puma → Rails/PostgreSQL
重启命令
sudo gitlab-ctl restart
一次配置流程
编辑配置文件的命令:
sudo nano /etc/gitlab/gitlab.rb
保存退出的方式:
Ctrl+O
Enter
Ctrl+X
保存后运行reconfigure才能实际生效
sudo gitlab-ctl reconfigure
Gitlab运行
Gitlab访问和第一次登录
初始账号root
- 在电脑浏览器打开:
http://<服务器ip地址>:<http端口号>
- 初始账号用户名是:root
- 初始密码查看命令:
sudo cat /srv/gitlab/config/initial_root_password
输出内容中包含一个密码。将此密码作为root的密码输入,即可登录root账号。
[!NOTE] 说明
- 这个root账号是第一次启动gitlab时自动创建的,承担gitlab网站超级管理员的角色,拥有最高权限。之后,它可以用于创建账号,包括其他超级管理员账号。
- 这个密码文件通常只短期保留(24小时),所以登录后尽快在网页里改密码。
修改密码
(网页操作)
点击右上角头像——Edit profile——左侧菜单栏Access——Password and authentication——Change Password
创建自己的gitlab账号
(网页操作)
现在Gitlab已经有了root超级账户。不过为了方便日常管理,我可以给自己创建一个日常账号,也设置为管理员。
- 在root账号界面,点击右上角的“Admin”按钮,进入管理员视图。
- 创建用户:点击左侧菜单栏的“Users”,再点击“New user”。
- 填写信息:填写Name、Username、Email等个人信息。其中Name为Gitlab用户之间相互看到的常用名,Username为登录用的用户名,Email为接收gitlab通知用的邮箱。
- 设置权限:User type有“Regular”和“Administrator”选项。这里选择“Administrator”。
- 设置登录密码:因为还没有配置SMTP,所以我们先用管理员手动设置密码的方式登录。用户创建成功后,点进用户主页,点击右上角“Edit”,进入用户信息编辑页面。找到“Password”,填写“Password”和“Password confirmation”,设置密码,最后点击下方“Save changes”。
- 退出登录,重新用刚刚创建的用户名和密码登录。登录后,应该同样能看到右上角的“Admin”按钮。
SMTP配置
发件邮箱准备
需要准备一个专门用于发送邮件的邮箱,用于gitlab通知的发送。建议不要使用个人主要邮箱,可以使用个人的闲置邮箱或专门注册。(我使用的是163邮箱)
邮箱需要先在邮箱设置中开启 SMTP 服务,并取得授权码。
修改配置文件
- 备份文件:
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-before-smtp
- 打开文件:
sudo nano /etc/gitlab/gitlab.rb
- 在打开的配置文件中,插入/修改以下配置:
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "<smtp.邮箱域名,如smtp.163.com>"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "<邮箱地址,如luoyq73@163.com>"
gitlab_rails['smtp_password'] = "<邮箱授权码,注意不是登录密码>"
gitlab_rails['smtp_domain'] = "<邮箱域名,如163.com>"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_tls'] = true
gitlab_rails['smtp_pool'] = falsegitlab_rails['gitlab_email_from'] = "<邮箱地址>" gitlab_rails['gitlab_email_display_name'] = "GitLab" gitlab_rails['gitlab_email_reply_to'] = "<邮箱地址>"
注:不同邮件服务商的服务器、端口和加密方式不同。使用其他邮箱时上面的配置可能需要按实际情况调整,不能直接套用。
4. 保存退出:
Ctrl+O
Enter
Ctrl+X
- 应用配置
sudo gitlab-ctl reconfigure
reconfigure 过程中没有报错,才能继续测试。
发送测试邮件
- 进入 Rails 控制台:
sudo gitlab-rails console
- 看到控制台提示符后执行:
Notify.test_email('<另一个用于接收测试邮件的邮箱>','GitLab SMTP 测试','收到这封邮件说明 163 SMTP 配置成功。'
).deliver_now
- 成功时通常会返回邮件对象或显示已投递。然后退出:
exit
- 查看用于接收测试邮件的邮箱的收件箱,检查是否收到来自之前配置的发送邮箱的邮件。
创建其他用户的账号
创建方法与[[#创建自己的gitlab账号]]相同。不过我们已经配置了SMTP,所以如果创建用户时填入邮箱信息,会在创建成功后自动向对应的邮箱发送“Account was created for you”邮件,不需要手动设置密码。
其他用户在收件箱收到邮件后,点击邮件中的链接“ Click here to set your password ”,即可自行设置密码。设置成功后,根据管理员创建账号时设置的username或者邮箱+刚刚设置好的密码即可登录。
链接2天内有效,如果过期,可以点击“ request a new one ”申请。
邮件有可能被归到垃圾箱,组员查收邮件时可以注意检查。
目前没有找到管理员能手动向组员重新发送“Account was created for you”邮件的方法。如果出现邮件没有发送成功/组员没有收到邮件/链接失效等各种问题,可以通过以下方式解决:
- 用户自行在gitlab登录页面点击“forgot your password”小字,然后系统会指引重新填写注册邮箱,提交后系统会重新向这个邮箱发送重置密码的邮件。
- 管理员手动设置用户的登录密码(方法与[[#创建自己的gitlab账号]]相同),再私下将密码告诉组员,组员直接登录。
Group和Project的创建
创建账号后,系统会有指引创建第一个Group和Project。也可以手动创建.
创建Group
- 点击左侧菜单栏“Group”。
- 点击“New group”。
- 点击“Create group”。
- 填写“Group name”。系统会根据填入的组名自动生成“https://<gitlab域名>/<组名>”格式的URL,也可以手动修改组名部分。
- 设置“Visibility level”,可以选择“Private”(私密),“Internal”(内部),“Public”(公开)三个可见等级。
- “personalize your GitLab experience”部分的选项为选填,可以不填。
- 点击下方“Create your group”,创建群组。
邀请组员
- 点击首页左侧菜单栏“Group”,再点击现有的一个Group,进入这个Group的页面。
- 点击Group页面的左侧菜单栏“Manage”——“Members”。
- 点击右上角“Invite members”。
- 输入组员的邮箱或username。注意组员必须是已经注册账号的用户,输入后可以在下拉栏选中系统自动匹配的用户。
- 设置Role。等级如下:
| 角色 | 中文理解 | 适合的人 | 主要权限 |
|---|---|---|---|
| Guest | 访客 | 只参与讨论、反馈问题,不需要查看代码的成员 | 不能访问代码;可以查看和评论 Issue、Epic 等协作内容 |
| Planner | 计划管理者 | 负责需求、任务、里程碑、迭代计划等项目管理工作的人 | 可以查看代码;可以创建和管理 Issue、Epic、Milestone、Iteration 等计划类内容 |
| Reporter | 报告者 / 观察者 | 需要查看代码、跟踪进展、参与评审,但不提交代码的人 | 可以查看代码;可以创建 Issue、查看项目状态、生成报告;不能提交代码 |
| Security Manager | 安全管理者 | 负责项目安全扫描、漏洞处理、安全策略管理的人 | 可以查看和管理项目或群组中的安全相关功能 |
| Developer | 开发者 | 实际参与代码开发、需要提交分支和发起合并请求的人 | 可以向非保护分支 push 代码;可以创建 Merge Request、运行 Pipeline;不能管理项目设置 |
| Maintainer | 维护者 / 项目维护人 | 负责维护仓库、管理分支、处理合并请求、配置 CI/CD 的核心成员 | 可以 push 代码;可以管理分支、Merge Request、CI/CD 设置和成员;不能删除项目 |
| Owner | 所有者 | 负责整个项目或群组最高权限管理的人 | 拥有完整控制权,可以管理项目或群组的核心设置 |
- 点击“Invite”。然后,用户会自动加入Group。同时,用户也会收到被成功邀请的通知邮件。
创建Project
- 点击首页左侧菜单栏“Project”。
- 点击右上角“New project”。
- 可以选择“Create blank project”创建空白项目,“Create from template”根据模板创建项目,或“Import project”从其他远程仓库导入已有项目。可以选择创建空白项目。
- 填写Project name。
- 确认项目URL。可以选择项目的归属为某个组或用户(自己)。可以修改URL中的项目缩写。
- 设置可见级别。如果项目属于组,可见级别一般与组一致。如果项目属于用户自己,可以设置级别为Private、Internal或Public。
- 设置其他项目配置选项。比如添加或者不加README。
- 点击“Create project”,项目创建成功。
其他经验
从WSL操作Windows宿主机的方法
Windows宿主机的powershell的路径为:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe
运行命令如:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "whoami"
如果觉得路径太长不方便,可以执行以下命令,在WSL中定义一个变量方便调用:
PS='/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'
运行命令的格式就如:
$PS -NoProfile -Command "需要运行的命令"
问题与解决
SSH服务启动失败
团队的服务器上没有出现这个问题,这个主要是在我自己电脑上把一台VMware虚拟机作为服务器进行测试时出现的。原因是这台虚拟机以前用于操作系统课程实验,在学习arm架构指令集的时候安装过arm架构。
发现问题
输入:
sudo systemctl status ssh
输出:
Unit ssh.service could not be found.
检查原因
查看CPU架构:
uname -m
输出:x86_64(正常)
查看 dpkg 主架构:
dpkg --print-architecture
输出:amd64(正常)
查看是否开启了额外架构:
dpkg --print-foreign-architectures
输出:arm64(异常,正常应该什么都不输出)
得出解释
以前为了操作系统实验时给 Ubuntu 加了 ARM 软件仓库,于是 apt 更新的时候,它除了下载binary-amd64,还去下载binary-arm64。但是 Ubuntu 官方很多镜像没有提供对应内容,于是整个 update 就失败了。
解决方法
方法一:删除ARM
第一步:删除ARM外部架构
执行删除操作:
sudo dpkg --remove-architecture arm64
然后确认(是否开启了额外架构):
dpkg --print-foreign-architectures
应该没有任何输出。
第二步:重新刷新apt
清理 apt 缓存:
sudo apt clean
删除旧索引:
sudo rm -rf /var/lib/apt/lists/*
重新下载索引:
sudo apt update
第三步:重新安装 SSH
如果 update 成功,再执行:
sudo apt install openssh-server
方法二:新设一个虚拟机
这是我采用的解决方法。因为我可能还需要学习arm架构,不想破坏原来的环境。所以更换环境同样可以解决安装失败的问题。
Docker下载镜像失败
发现问题
测试hello-world时输入:
sudo docker run hello-world
输出:
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: failed to resolve reference "docker.io/library/hello-world:latest": failed to do request: Head "https://registry-1.docker.io/v2/library/hello-world/manifests/latest": dial tcp 67.15.100.252:443: i/o timeout Run 'docker run --help' for more information
分析问题
Unable to find image 'hello-world:latest' locally这一步是正常的,因为本地本来就没有hello-world镜像,需要从Docker Hub上下载- 而
dial tcp 67.15.100.252:443: i/o timeout这一步说明连接时间超时了
这表明,国内服务器从Docker Hub上下载镜像需要连接外网。推测可能无法直接通过docker下载gitlab的镜像。
解决方法
方法一:配置镜像加速器(需要镜像源,未采用)
- 创建配置:
sudo mkdir -p /etc/docker
sudo nano /etc/docker/daemon.json
- 写入一个可用的镜像源,例如组织管理员或云厂商提供的加速地址:
{"registry-mirrors": ["https://你的镜像加速地址"]
}
- 重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
- 再次测试:
sudo docker run hello-world
方法二:配置HTTP/HTTPS 代理(未采用)
给服务器配置可用的 HTTP/HTTPS 代理,让 Docker daemon 能访问镜像仓库。
方法三:离线导入GitLab 镜像(未采用)
在能联网的机器上提前下载GitLab镜像,再导出成文件传到服务器离线导入。
方法四:改为用Linux软件包安装(采用)
团队服务器为了通过Docker安装而进行前述方法的配置较为麻烦,不如直接用Ubuntu支持的软件包安装,不用Docker(见正文)。
8080端口配置冲突导致配置中断
发现问题
ChatGPT教程:
接下来需要决定 GitLab 网页使用哪个地址。你的公网入口是
<ip地址>,但 SSH 使用的是外部端口2222。为了避免占用可能已经有用途的 80 端口,可以先让 GitLab Web 使用8080:
sudo EXTERNAL_URL="http://<ip地址>:8080" apt install -y gitlab-ce
我安装时直接执行了sudo EXTERNAL_URL="http://<ip地址>:8080" apt install -y gitlab-ce这一句,但是遇到问题,配置中断:
<以上省略>
Errors were encountered while processing: gitlab-ce
E: Sub-process /usr/bin/dpkg returned an error code (1)
排查问题
首先这个报错不是配置不足导致的。问题应该集中在 GitLab 内置 PostgreSQL 的日志子服务处于 down 状态,GitLab 在重新加载它时返回退出码 1,于是整个 gitlab-ctl reconfigure 被判定失败,最终 dpkg 也报错退出。
可以认为 GitLab 软件包已经下载并安装了大部分文件,但首次配置reconfigure中断。
确认服务器的设备类型(WSL有风险)
通过命令:
uname -r
输出6.18.33.2-microsoft-standard-WSL2
或者:
systemd-detect-virt
输出wsl
由此,我确认了服务器不是一个单独的物理设备,也不是普通 Ubuntu 虚拟机,而是在Windows Server 上运行的 WSL Ubuntu。
[!NOTE] 插曲
ChatGPT认为:这套安装有机会跑起来,但 WSL2 不适合作为团队正式 GitLab 服务器的长期部署环境。GitLab 官方 Linux package 的支持列表针对受支持的 Linux 发行版,而 WSL 并不是其中单独列出的服务器平台。主要风险包括:
- Windows 重启或 WSL 实例停止后,GitLab 服务可能需要重新启动;
- WSL 网络地址和端口转发可能发生变化;
- 时间同步可能出现你现在看到的
time warp;- Windows 宿主机、WSL、外部路由之间要配置多层端口转发;
- GitLab 数据备份和故障恢复比普通 Linux 虚拟机更麻烦。
网络请求错误——排除puma启动慢原因
此时执行本地网络请求命令:
curl -I http://127.0.0.1:8080
输出:
HTTP/1.1 502 Bad Gateway
Server: nginx
......
而不是正常的HTTP/1.1 302 Found或HTTP/1.1 200 OK。
ChatGPT认为可能是因为首次启动Puma 尚未完全启动或没有及时响应,这种情况下前端可能会返回502.
执行检查命令:
sudo gitlab-ctl status puma
sudo gitlab-ctl status gitlab-workhorsesudo tail -n 100 /var/log/gitlab/puma/current
sudo tail -n 100 /var/log/gitlab/gitlab-workhorse/current
结果是Puma 和 Workhorse都在run状态。进一步检查日志后,ai认为可能是端口配置冲突的原因。
找到原因:端口配置冲突
我之前 GitLab 的外部访问地址设成了:http://101.6.48.103:8080,
所以 GitLab 自带的 Nginx 正在监听 0.0.0.0:8080。而 GitLab Linux package 中,Puma 的内部默认监听端口也可能是 127.0.0.1:8080。Puma、Nginx 等服务端口发生占用或冲突时,会导致 Web 访问异常。
最简单稳妥的修复方式,是让外部 Nginx 使用 80 端口,不再使用 8080。(需要80 端口目前没有被占用)
解决方式(第一步)
编辑配置(遵循[[#一次配置流程]]):
sudo nano /etc/gitlab/gitlab.rb
在编辑器中找到:
external_url 'http://101.6.48.103:8080'
修改为:(即改为默认80)
external_url 'http://101.6.48.103'
保存后运行:
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
然后查看监听端口:
sudo ss -lntp | grep -E ':(80|8080)\b'
理想情况是:
- Nginx 监听
0.0.0.0:80 - 8080 不再被 Nginx 占用,或者只由 Puma 在
127.0.0.1上内部使用
遇到第二个问题(KAS卡死问题,可能遇到)
执行sudo gitlab-ctl reconfigure后,出现很多"FATAL",配置失败。截取输出如:
runit_service[gitlab-kas] (gitlab-kas::enable line 157) had an error: Mixlib::ShellOut::ShellCommandFailed: Expected process to exit with [0], but received '1'
经分析,是 gitlab-kas 收到终止信号后,90 秒内没有正常退出。gitlab-ctl reconfigure 等待超时,因此把整个配置过程判定为失败。gitlab-kas 是 GitLab Relay,主要用于 Kubernetes Agent、Runner Controllers 等功能。我们现在只是搭团队代码仓库,暂时不需要 Kubernetes 集群功能,可以直接禁用它。
解决方式(第二步,可能需要)
执行:
sudo gitlab-ctl stop gitlab-kas
但是我的服务器输出timeout: run: gitlab-kas: (pid 75846) 390s, want down, got TERM。
不过貌似没关系,因为执行ps -fp 75846后已经没有输出,所以进程最后应该已经结束。
确认KAS已经停掉:
sudo gitlab-ctl status gitlab-kas
看到down: gitlab-kas就正常。
再重试:
sudo gitlab-ctl reconfigure
这次再执行curl -I http://127.0.0.1,输出:
HTTP/1.1 302 Found
Server: nginx
而GitLab 的请求路径大致是:
浏览器 → Nginx → GitLab Workhorse → Puma → Rails/PostgreSQL
现在的结果说明从Nginx到PostgreSQL的链路已经通了。
缺少外部访问地址
虽然服务器内部curl -I http://127.0.0.1输出正常,但是现在无法通过我自己电脑的浏览器访问gitlab页面。也就是说,gitlab服务缺少公网访问地址。
因为我们的服务器较特殊,实际运行在 WSL 2 里。公网 IP 101.6.48.103 很可能属于 Windows 宿主机或外层网关,并不直接属于 WSL。此时需要管理员把公网 80 端口转发到 WSL 的 80 端口。
解决方法一(管理员临时用)
在自己的电脑终端执行:
ssh -L 8080:127.0.0.1:80 yluo@101.6.48.103 -p 2222
保持这个 SSH 窗口不要关闭,然后在自己的浏览器打开:
http://127.0.0.1:8080
就能快速进入网页。不过这只是个临时方案,因为以后网页还要跟组员共同开发,不能每次都走隧道。
排查问题
网络结构是:浏览器访问公网地址: http://101.6.48.103 ——Windows宿主机——WSL Ubuntu——GitLab nginx :80
而现在,Windows宿主机到WSL Ubuntu之间并没有端口转发。
公网端口被外层Nginx占用?
- 查看Ubuntu IP:
hostname -I
输出:172.24.187.44 172.17.0.1(在局域网)
(注:172.24.187.44 是 WSL 的内网地址,172.17.0.1 是 Docker 网桥地址)
2. 在 WSL 内执行:
curl -I http://101.6.48.103/
返回:
HTTP/1.1 200 OK
Server: nginx/1.29.8
Content-Length: 6624
这说明公网 101.6.48.103:80 已经被 Windows 宿主机或外层环境的一套 Nginx 占用。
现在的情况是:
- WSL 的内网地址是
172.24.187.44; - 公网
101.6.48.103:80已经有另一个 Nginx,占用并返回默认页面; - 所以浏览器直接访问
http://101.6.48.103/时,请求到的是 Windows 宿主机或外层服务器的 Nginx,不是 WSL 里的 GitLab
用Windows宿主机的powershell操作
接下来, 我可能需要从Windows宿主机修改那个占用80端口的Nginx。这一步需要我确认是否有Windows宿主机的powershell运行权限。
执行
powershell.exe -Command "whoami"
但输出:powershell.exe: command not found
这说明 Windows 路径没有加入 WSL 的 PATH。所以加上完整路径,重新执行:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "whoami"
成功输出:win-ua1ksvoi3cv\administrator,这说明我能够从 WSL 调用 Windows 宿主机,而且对应的是 Windows 的 Administrator 账户。
(从WSL中操作Windows宿主机的命令整理在[[#从WSL操作Windows宿主机的方法]]中)
公网IP是学校网关
继续通过Windows powershell检查:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command \
"Get-NetTCPConnection -State Listen -LocalPort 80 | Format-Table LocalAddress,LocalPort,OwningProcess -AutoSize"
输出:
LocalAddress LocalPort OwningProcess
------------ --------- -------------
127.0.0.1 80 3476
输入:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command \
"Get-NetTCPConnection -State Listen -LocalPort 80 | ForEach-Object { Get-Process -Id \$_.OwningProcess | Select-Object Id,ProcessName,Path } | Format-List"
输出:
Id : 3476
ProcessName : wslrelay
Path : C:\Program Files\WSL\wslrelay.exe
wslrelay 是 WSL 的本机端口转发组件。它的作用大致是让 Windows 本机访问localhost:80 时,可以转到 WSL 中监听的 80 端口。因为它只绑定在 127.0.0.1,外部设备不能通过它访问 GitLab。
这个结果非常关键:Windows 宿主机上并没有外层 Nginx 占用公网 80 端口。目前 Windows 只存在:127.0.0.1:80 → wslrelay.exe。之前关于外层Nginx的判断需要修正。
访问http://101.6.48.103/时看到的 nginx/1.29.8,很可能位于这台 Windows 服务器之外,例如学校的公网网关、端口映射设备、虚拟化平台,或者另一个共享该公网 IP 的服务。
检查 Windows 自己有哪些 IP 地址:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command \
"Get-NetIPAddress -AddressFamily IPv4 | Select-Object InterfaceAlias,IPAddress,PrefixLength | Format-Table -AutoSize"
输出:
InterfaceAlias IPAddress PrefixLength
-------------- --------- ------------
vEthernet (WSL) 172.24.176.1 20
以太网 2 169.254.219.34 16
以太网 192.168.1.120 24
Loopback Pseudo-Interface 1 127.0.0.1 8
里面没有:101.6.48.103
这表示 101.6.48.103 并不是直接配置在这台 Windows 上,而是学校或实验室网关的公网 IP。
SSH 之所以能用101.6.48.103:2222,是因为管理员预先配置了某种端口转发:
公网 101.6.48.103:2222↓
Windows/WSL 的 SSH 端口
更确切的公网访问链路应该是:
101.6.48.103↓ 外层网关 / NAT / Nginx
192.168.1.120↓ Windows / WSL 转发
172.24.187.44:80↓
GitLab
ssh链路:
公网客户端↓
101.6.48.103:2222↓ 上级网络设备/NAT/端口映射
Windows 宿主机 192.168.1.120:2222↓ Windows portproxy
WSL2 Ubuntu 172.24.187.44:2222↓
sshd
WSL 的出网路径是:
WSL 172.24.187.44
→ Windows 虚拟网关 172.24.176.1
→ Windows 物理网卡 192.168.1.120
→ 上级局域网网关
→ 外部网络
解决方法
最终解决方法:因为涉及服务器环境的公网配置,我无法独立解决,所以需要请管理员配置公网转发,设置公网入口为101.6.48.103:8181(8181是一个未被占用的端口号)。也就是说找管理公网入口的人,把外层 Nginx或 NAT 指向 192.168.1.120:8080。
最后的办法是找团队导师说明可能需要配置一个公网http端口,老师再跟网管联系。
他们需要在公网入口那一层建立一条端口映射,例如:
101.6.48.103:8181
→
192.168.1.120:8181
端口号只要满足两个条件:
- 公网侧没有被其他服务占用;
- Windows 侧可以监听并通过防火墙。、
之后我还需要在 Windows 上配置:
192.168.1.120:8081 → WSL 172.24.187.44:80
最终完整链路就是:
101.6.48.103:8081
→ 192.168.1.120:8081
→ 172.24.187.44:80
→ GitLab
其中,101.6.48.103:8081 → 192.168.1.120:8081的两个端口号不一定要相同。
确认Windows侧端口号没被占用的命令:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command "Get-NetTCPConnection -LocalPort 8181 -ErrorAction SilentlyContinue"
没有输出说明没被占用。
Windows 侧配置
- 宿主机上用
portproxy配置:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command "netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=8181 connectaddress=172.24.187.44 connectport=80"
含义是:
Windows 所有 IPv4 地址的 8181 端口
→ WSL 172.24.187.44 的 80 端口
因此也包括192.168.1.120:8181
2. 放行 Windows 防火墙的 8181 端口:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command "New-NetFirewallRule -DisplayName 'GitLab HTTP 8181' -Direction Inbound -Protocol TCP -LocalPort 8181 -Action Allow"
- 回退命令(比如需要换端口):
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command "netsh interface portproxy delete v4tov4 listenaddress=0.0.0.0 listenport=8081"
可以一起删除防火墙规则:
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -NoProfile -Command "Remove-NetFirewallRule -DisplayName 'GitLab HTTP 8081' -ErrorAction SilentlyContinue; Remove-NetFirewallRule -DisplayName 'GitLab WSL HTTP 8081' -ErrorAction SilentlyContinue"
external_url修改
配置完公网——宿主机,宿主机——WSL的转发后,接下来还差最后一步:修改external_url,确保和当前配置一致。方法:
- 修改:
sudo nano /etc/gitlab/gitlab.rb
- 把对应行改为:
external_url 'http://101.6.48.103:8181'
- 保存后执行:
sudo gitlab-ctl reconfigure
HTTP 502: Waiting for GitLab to boot报错
出现:初次登录Gitlab(Puma worker数过多)
页面显示:
HTTP 502: Waiting for GitLab to boot
It can take up to a few minutes for GitLab to boot completely.
This page will automatically reload every 5 seconds.
表明GitLab 的后端服务暂时没有正常响应。页面里的 Waiting for GitLab to boot 表示 Nginx 还能工作,但它连接不到 Puma、Workhorse 或 Rails。
这个场景里,比较常见的原因有三种。
- 第一种是 GitLab 刚重启或刚执行过
reconfigure,Puma 和 Sidekiq 还没完全起来。此时等一小会儿并刷新页面即可。 - 第二种是 Puma 崩了。通常
gitlab-ctl status会显示它是down,日志里会出现数据库连接失败、权限问题、配置错误或内存异常。 - 第三种是之前配置
external_url、KAS 或其他参数后,reconfigure没完全成功,导致部分服务状态看起来正常,但实际内部接口没有准备好。
排查问题
看Workhorse日志:
sudo tail -n 80 /var/log/gitlab/gitlab-workhorse/current
中间可以找到badgateway: failed to receive response: EOF的输出。创建群组、登录等 POST 请求多次出现这条,说明 Puma 工作进程会在处理较重请求时突然断开。静态资源还能打开,所以登录页看起来正常;一旦登录、创建项目或创建群组需要 Rails 真正处理,就出现 502。
看puma日志:
sudo tail -n 150 /var/log/gitlab/puma/current
有输出Workers: 61,于是定位根因:Puma 被自动配置成了 61 个 worker。
这说明 GitLab 根据服务器看到的 CPU 数量自动开了 61 个 Puma 工作进程。对现在 WSL2 环境来说,这个数量明显过大,导致启动很慢、worker 超时、请求偶发 EOF,最终表现为登录或创建项目时 502。当前数据库、Redis、Gitaly、迁移和权限检查都正常,所以不是数据库坏了。
GitLab 官方也说明,Puma 的默认 worker 数会根据 CPU 核心数计算;worker 数越多,同时占用的数据库连接和资源也越多。
解决方法
现在直接把 Puma worker 数限制为 4 个。对于几个人使用的实习项目,4 个已经完全足够。
- 备份配置:
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-puma
- 编辑配置:
sudo nano /etc/gitlab/gitlab.rb
- 在文件末尾加入:
puma['worker_processes'] = 4
puma['min_threads'] = 4
puma['max_threads'] = 4
- 重新生成配置:
sudo gitlab-ctl reconfigure
- 完成后,只重启 Puma 和 Workhorse:
sudo gitlab-ctl restart puma
sudo gitlab-ctl restart gitlab-workhorse
- 检查日志:
sudo tail -n 50 /var/log/gitlab/puma/current
直到看到:
Workers: 4
Listening on unix:///var/opt/gitlab/gitlab-rails/sockets/gitlab.socket
再确认进程数量:
ps -ef | grep '[p]uma'
正常应该只有:
1 个 Puma master
4 个 cluster worker
HTTP 422报错
报错文本
422: The change you requested was rejected
Make sure you have access to the thing you tried to change.
Please contact your GitLab administrator if you think this is a mistake.
出现时机:切换账号时
这次关闭gitlab页面后重新进入http://101.6.48.103:8181就解决了。
可能是切换账号时浏览器保留了旧会话或旧的 CSRF 令牌。GitLab 的 422 页面常见于表单请求携带的令牌与当前登录会话不一致;反向代理或访问地址配置不一致也可能造成同类问题。
环境问题:WSL不支持GPU
发现问题
团队导师发现服务器的WSL环境下不支持GPU和CUDA。因为工程项目后续涉及高性能操作,所以需要尽快解决这个问题。
输入:
nvidia-smi
结果输出:
Failed to initialize NVML: GPU access blocked by the operating system
Failed to properly shut down NVML: GPU access blocked by the operating system
排查问题
现在的问题发生在 WSL 访问宿主机 GPU这一层。我确认我能够从WSL中获取宿主机信息,因此下面开始从宿主机中排查问题。
(操作宿主机参见[[#从WSL操作Windows宿主机的方法]])
一、收集宿主机信息
定义一个方便调用 PowerShell 的变量:
PS='/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'
查看 Windows 版本:
$PS -NoProfile -Command \
"Get-ComputerInfo | Select-Object WindowsProductName,WindowsVersion,OsBuildNumber,OsArchitecture | Format-List"
输出:
WindowsProductName : Windows Server 2022 Datacenter
WindowsVersion : 2009
OsBuildNumber : 20348
OsArchitecture : 64 位
确认是Windows Server 2022。
执行:
$PS -NoProfile -Command \
"[System.Environment]::OSVersion.Version"
输出:
Major Minor Build Revision
----- ----- ----- --------
10 0 20348 0
查看 GPU 和设备状态
yluo@WIN-UA1KSVOI3CV:~$ $PS -NoProfile -Command \
"Get-CimInstance Win32_VideoController | Select-Object Name,AdapterCompatibility,DriverVersion,PNPDeviceID,Status | Format-List"
输出:
Name : ASPEED Graphics Family(WDDM)
AdapterCompatibility : ASPEED
DriverVersion : 9.0.10.112
PNPDeviceID : PCI\VEN_1A03&DEV_2000&SUBSYS_10001458&REV_52\5&678B371&0&00001D
Status : OK Name : NVIDIA A2
AdapterCompatibility : NVIDIA
DriverVersion : 32.0.15.9636
PNPDeviceID : PCI\VEN_10DE&DEV_25B6&SUBSYS_157E10DE&REV_A1\4&35D4D31&0&0009
Status : OK
输入:
yluo@WIN-UA1KSVOI3CV:~$ $PS -NoProfile -Command \
"Get-PnpDevice -Class Display | Format-Table Status,Class,FriendlyName,InstanceId -AutoSize"
输出:
Status Class FriendlyName InstanceId
------ ----- ------------ ----------
Unknown Display Microsoft Remote Display Adapter SWD\REMOTEDISPLAYENUM\RDPIDD_INDIRECTDISPLAY&SESSIONID_0002 OK Display ASPEED Graphics Family(WDDM) PCI\VEN_1A03&DEV_2000&SUBSYS_10001458&REV_52\5&678B371&0&00001D OK Display NVIDIA A2 PCI\VEN_10DE&DEV_25B6&SUBSYS_157E10DE&REV_A1\4&35D4D31&0&0009
查看是否存在 NVIDIA 驱动服务:
yluo@WIN-UA1KSVOI3CV:~$ $PS -NoProfile -Command \
"Get-Service | Where-Object { \$_.Name -match 'nvidia|nvdisplay' -or \$_.DisplayName -match 'NVIDIA' } | Format-Table Status,Name,DisplayName -AutoSize"
输出:
Status Name DisplayName
------ ---- -----------
Running NVDisplay.ContainerLocalSystem NVIDIA Display Container LS
直接运行 Windows 版 nvidia-smi
yluo@WIN-UA1KSVOI3CV:~$ $PS -NoProfile -Command \
"& 'C:\Windows\System32\nvidia-smi.exe'
这一步最关键:
- Windows 的
nvidia-smi也失败:先修 Windows NVIDIA 驱动,暂时不要动 WSL。 - Windows 的
nvidia-smi正常、WSL 中失败:说明问题集中在 Windows→WSL 的 GPU 映射。 - Windows 根本识别不到 NVIDIA GPU:需要检查物理设备、虚拟机直通或设备管理器,WSL 内无法解决。
输出:
" Thu Jul 9 17:10:55 2026
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 596.36 Driver Version: 596.36 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Driver-Model | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. | |=========================================+========================+======================|
| 0 NVIDIA A2 TCC |
00000000:41:00.0 Off | 0 |
| 0% 33C P8 5W / 60W | 9MiB / 15356MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
+-----------------------------------------------------------------------------------------+
| Processes:
|
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage | |=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
这一步出现关键证据:NVIDIA A2 当前运行在 TCC 模式,Windows Server 2022 无法把这张 TCC GPU 正常暴露给 WSL2。
查看 WSL 版本和发行版类型
输入:
$PS -NoProfile -Command "wsl.exe --status"
输出:
默认分发: Ubuntu-22.04
默认版本: 2
输入:
$PS -NoProfile -Command "wsl.exe --version"
输出:
WSL 版本: 2.7.3.0
内核版本: 6.6.114.1-1
WSLg 版本: 1.0.73
MSRDC 版本: 1.2.6676
Direct3D 版本: 1.611.1-81528511
DXCore 版本: 10.0.26100.1-240331-1435.ge-release
Windows: 10.0.20348.5020
输入:
$PS -NoProfile -Command "wsl.exe -l -v"
输出:
NAME STATE VERSION * Ubuntu-22.04 Running 2
目标是看到当前 Ubuntu 的 VERSION 为 2(满足)
yluo@WIN-UA1KSVOI3CV:~$ $PS -NoProfile -Command "wsl.exe --set-version Ubuntu 2"
不存在具有所提供名称的分发。
错误代码: Wsl/Service/WSL_E_DISTRO_NOT_FOUND
检查 Windows 功能和 Hyper-V 虚拟化
$PS -NoProfile -Command \
"Get-CimInstance Win32_ComputerSystem | Select-Object Manufacturer,Model,HypervisorPresent | Format-List"
输出:
Manufacturer : Giga Computing
Model : MZ72-HB2-00
HypervisorPresent : True
输入:
$PS -NoProfile -Command \
"systeminfo.exe | Select-String 'Hyper-V|Virtualization'"
输出:
Hyper-V 要求: 已检测到虚拟机监控程序。将不显示 Hyper-V 所需的功能。
至少需要:
Microsoft-Windows-Subsystem-Linux Enabled
VirtualMachinePlatform Enabled
收集 WSL 内部信息
在 Ubuntu 中执行:
uname -a cat /etc/os-release
输出:
Linux WIN-UA1KSVOI3CV 6.6.114.1-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Mon Dec 1 20:46:23 UTC 2025 x86_64 x86_64 x86_64 GNU/Linux PRETTY_NAME="Ubuntu 22.04.5 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.5
LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
检查 GPU 虚拟设备:
ls -l /dev/dxg
输出:
crw-rw-rw- 1 root root 10, 125 Jul 8 11:41 /dev/dxg
检查 WSL 映射进来的 NVIDIA 文件:
ls -lah /usr/lib/wsl/lib/ | grep -E 'nvidia|cuda|dxcore'
输出:
-r-xr-xr-x 4 root root 180K Apr 23 17:12 libcuda.so
-r-xr-xr-x 4 root root 180K Apr 23 17:12 libcuda.so.1
-r-xr-xr-x 4 root root 180K Apr 23 17:12 libcuda.so.1.1
-r-xr-xr-x 2 root root 12M Apr 23 17:12 libcudadebugger.so.1
-r-xr-xr-x 1 root root 920K Mar 31 2024 libdxcore.so
-r-xr-xr-x 3 root root 267K Apr 23 17:12 libnvidia-encode.so
-r-xr-xr-x 3 root root 267K Apr 23 17:12 libnvidia-encode.so.1
-r-xr-xr-x 2 root root 90M Apr 23 17:12 libnvidia-gpucomp.so lrwxrwxrwx 1 root root 20 Jul 8 11:41 libnvidia-gpucomp.so.595.71.01 -> libnvidia-gpucomp.so
-r-xr-xr-x 2 root root 279K Apr 23 17:12 libnvidia-ml.so.1
-r-xr-xr-x 2 root root 4.4M Apr 23 17:12 libnvidia-ngx.so.1
-r-xr-xr-x 3 root root 67K Apr 23 17:12 libnvidia-opticalflow.so
-r-xr-xr-x 3 root root 67K Apr 23 17:12 libnvidia-opticalflow.so.1
-r-xr-xr-x 2 root root 4.9M Apr 23 17:12 nvidia-ngx-updater
-r-xr-xr-x 2 root root 809K Apr 23 17:12 nvidia-smi
检查是否有人错误安装了 Linux 显卡驱动:
dpkg -l | grep -E 'nvidia-driver|cuda-drivers|nvidia-dkms|nvidia-kernel|linux-modules-nvidia'
输出:
ii nvidia-kernel-common-595 595.71.05-0ubuntu0.22.04.1 amd64
Shared files used with the kernel module
检查 CUDA Toolkit:
command -v nvcc && nvcc --version
无输出
这里要区分:
/dev/dxg不存在:宿主机没有向 WSL 提供 GPU 虚拟设备。/dev/dxg存在,但/usr/lib/wsl/lib/nvidia-smi仍报 blocked:通常是 Windows驱动、系统支持、GPU工作模式或虚拟化环境问题。/usr/lib/wsl/lib/nvidia-smi正常,而直接nvidia-smi失败:WSL 中安装了另一套冲突的 NVIDIA 程序或库。- 出现
nvidia-driver-*、nvidia-dkms-*:很可能错误地在 WSL 中安装了 Linux内核驱动。
NVIDIA 官方明确说明,WSL 使用的是 Windows 宿主机 NVIDIA 驱动映射,不能在 WSL 中再安装普通 Linux 显卡驱动。
初步结论
定位主要原因:NVIDIA A2 当前运行在 TCC 模式,Windows Server 2022 无法把这张 TCC GPU 正常暴露给 WSL2。
其他基础条件都基本正常:
- Windows 能正常识别 NVIDIA A2,驱动和 GPU 本身可工作。
- Windows 下
nvidia-smi正常,因此不是显卡损坏或 Windows 驱动完全失效。 - 当前发行版确实是 WSL2。
- WSL 和内核版本都很新。
/dev/dxg存在,说明 WSL GPU 通道已经创建。/usr/lib/wsl/lib/中存在 Windows 驱动映射进来的 CUDA、NVML 和nvidia-smi。- 服务器看起来是物理机,不是 VMware 等外层虚拟机;
HypervisorPresent : True是启用 WSL2/Hyper-V 后的正常表现。 - 当前没有
nvcc,但这不是nvidia-smi失败的原因。
WDDM 和 TCC的区别
NVIDIA 的 Windows CUDA文档区分了 WDDM 和 TCC:WDDM用于显示设备,TCC用于无显示的计算卡。WSL CUDA则依赖 Windows驱动通过 WSL/DXCore向 Linux环境暴露 GPU。
解决尝试
先确认 A2 是否允许切换到 WDDM:
$PS -NoProfile -Command \
"& 'C:\Windows\System32\nvidia-smi.exe' -q | Select-String 'Product Name|Driver Version|Driver Model|Virtualization Mode|Compute Mode'"
输出:
Driver Version : 596.36
Product Name : NVIDIA A2
Driver Model GPU
Virtualization Mode Virtualization Mode : None
Compute Mode : Default
尝试将 A2 切换为 WDDM(如果切换成功,接着要重启整个 Windows Server)
$PS -NoProfile -Command \
"& 'C:\Windows\System32\nvidia-smi.exe' -i 0 -dm 0"
结果输出:
Unable to set driver model for GPU 00000000:41:00.0: Not Supported Treating as warning and moving on.
All done.
无法切换。
也就是说,如果要WSL支持GPU,就必须将A2的驱动模式从TCC切换为WDDM。但现在的问题是在当前驱动和平台上只能使用 TCC,无法切换为 WDDM。
解决方法
ChatGPT提供了两种解决方案:
直接使用 Windows 原生 CUDA
当前 Windows中的nvidia-smi.exe已经能正常识别 A2,所以 Windows原生 CUDA程序可以使用这张 GPU。WSL仍然可以负责 Git、编译、脚本、文件处理和其他 Linux工作,但 GPU程序放在 Windows侧运行。
但是团队服务器所在的Windows Server还有其他用途,而且比较麻烦,我们没有采用这种方案。
服务器改为原生 Linux
可以保留当前服务器服务,另外准备 Linux GPU节点。但是这种方法更加麻烦,没有采用。
最终采用方案:安装GRID/vGPU授权驱动开放 WDDM 选项
既然有根本阻断点,那我们就解决如何开放WDDM驱动模式的问题。具体解决交给了学校的IT人员,这里不描述具体解决方式。
最终重启WSL后,WSL可以正常支持GPU了。
输入:
nvidia-smi
输出:
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 595.71.01 Driver Version: 596.36 CUDA Version: 13.2 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA A2 On | 00000000:41:00.0 Off | 0 |
| 0% 32C P8 5W / 60W | 0MiB / 15356MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------++-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+
[!NOTE] 说明
如果输入nvidia-smi后显示Command 'nvidia-smi' not found,可能是PATH没有包含的原因。可以替换成带路径的:/usr/lib/wsl/lib/nvidia-smi或输入永久加入PATH:
echo 'export PATH=/usr/lib/wsl/lib:$PATH' >> ~/.bashrc source ~/.bashrc
[!NOTICE] 注意
WSL重启后IP地址会发生变化。如果之前在[[#Windows 侧配置]]绑定过WSL IP地址的端口映射,重启后需要进行相应修改。
