三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Gitlab搭建笔记

Gitlab搭建笔记

[!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服务的安装(如需要)

  1. 在服务器中执行:
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仓库(未采用,待测试)

  1. 安装依赖:
sudo apt install -y ca-certificates curl gnupg
  1. 添加 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
  1. 添加 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
  1. 刷新:
sudo apt update
  1. 安装 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安装

  1. 创建 GitLab 数据目录:
sudo mkdir -p /srv/gitlab/config /srv/gitlab/logs /srv/gitlab/data
  • config,logs,data表示配置,日志,数据文件
  1. 修改目录权限:
sudo chown -R $USER:$USER /srv/gitlab

解释:

  • chown:修改文件所有者
  • -R:递归修改
  • $USER:当前用户名(不用自己改)
  1. 启动 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端口。

执行以上命令后,将会下载镜像,可能需等待一段时间。

  1. 启动后看状态:
docker ps

输出类似(应该有一个gitlab容器,且状态为Up):

CONTAINER ID   IMAGE                    STATUS
xxxx           gitlab/gitlab-ce:latest  Up ...
  1. 看 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-cegitlab-ee 软件包。

  1. 测试 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
  1. 添加 GitLab CE 仓库:
curl --silent \"https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh" \| sudo bash
  1. 安装。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 FoundHTTP/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

  1. 在电脑浏览器打开:
http://<服务器ip地址>:<http端口号>
  1. 初始账号用户名是:root
  2. 初始密码查看命令:
sudo cat /srv/gitlab/config/initial_root_password

输出内容中包含一个密码。将此密码作为root的密码输入,即可登录root账号。

[!NOTE] 说明

  1. 这个root账号是第一次启动gitlab时自动创建的,承担gitlab网站超级管理员的角色,拥有最高权限。之后,它可以用于创建账号,包括其他超级管理员账号。
  2. 这个密码文件通常只短期保留(24小时),所以登录后尽快在网页里改密码。

修改密码

(网页操作)
点击右上角头像——Edit profile——左侧菜单栏Access——Password and authentication——Change Password

创建自己的gitlab账号

(网页操作)
现在Gitlab已经有了root超级账户。不过为了方便日常管理,我可以给自己创建一个日常账号,也设置为管理员。

  1. 在root账号界面,点击右上角的“Admin”按钮,进入管理员视图。
  2. 创建用户:点击左侧菜单栏的“Users”,再点击“New user”。
  3. 填写信息:填写Name、Username、Email等个人信息。其中Name为Gitlab用户之间相互看到的常用名,Username为登录用的用户名,Email为接收gitlab通知用的邮箱。
  4. 设置权限:User type有“Regular”和“Administrator”选项。这里选择“Administrator”。
  5. 设置登录密码:因为还没有配置SMTP,所以我们先用管理员手动设置密码的方式登录。用户创建成功后,点进用户主页,点击右上角“Edit”,进入用户信息编辑页面。找到“Password”,填写“Password”和“Password confirmation”,设置密码,最后点击下方“Save changes”。
  6. 退出登录,重新用刚刚创建的用户名和密码登录。登录后,应该同样能看到右上角的“Admin”按钮。

SMTP配置

发件邮箱准备

需要准备一个专门用于发送邮件的邮箱,用于gitlab通知的发送。建议不要使用个人主要邮箱,可以使用个人的闲置邮箱或专门注册。(我使用的是163邮箱)
邮箱需要先在邮箱设置中开启 SMTP 服务,并取得授权码

修改配置文件

  1. 备份文件:
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-before-smtp
  1. 打开文件:
sudo nano /etc/gitlab/gitlab.rb
  1. 在打开的配置文件中,插入/修改以下配置:
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
  1. 应用配置
sudo gitlab-ctl reconfigure

reconfigure 过程中没有报错,才能继续测试。

发送测试邮件

  1. 进入 Rails 控制台:
sudo gitlab-rails console
  1. 看到控制台提示符后执行:
Notify.test_email('<另一个用于接收测试邮件的邮箱>','GitLab SMTP 测试','收到这封邮件说明 163 SMTP 配置成功。'
).deliver_now
  1. 成功时通常会返回邮件对象或显示已投递。然后退出:
exit
  1. 查看用于接收测试邮件的邮箱的收件箱,检查是否收到来自之前配置的发送邮箱的邮件。

创建其他用户的账号

创建方法与[[#创建自己的gitlab账号]]相同。不过我们已经配置了SMTP,所以如果创建用户时填入邮箱信息,会在创建成功后自动向对应的邮箱发送“Account was created for you”邮件,不需要手动设置密码。
其他用户在收件箱收到邮件后,点击邮件中的链接“ Click here to set your password ”,即可自行设置密码。设置成功后,根据管理员创建账号时设置的username或者邮箱+刚刚设置好的密码即可登录。
链接2天内有效,如果过期,可以点击“ request a new one ”申请。
邮件有可能被归到垃圾箱,组员查收邮件时可以注意检查。
目前没有找到管理员能手动向组员重新发送“Account was created for you”邮件的方法。如果出现邮件没有发送成功/组员没有收到邮件/链接失效等各种问题,可以通过以下方式解决:

  1. 用户自行在gitlab登录页面点击“forgot your password”小字,然后系统会指引重新填写注册邮箱,提交后系统会重新向这个邮箱发送重置密码的邮件。
  2. 管理员手动设置用户的登录密码(方法与[[#创建自己的gitlab账号]]相同),再私下将密码告诉组员,组员直接登录。

Group和Project的创建

创建账号后,系统会有指引创建第一个Group和Project。也可以手动创建.

创建Group

  1. 点击左侧菜单栏“Group”。
  2. 点击“New group”。
  3. 点击“Create group”。
  4. 填写“Group name”。系统会根据填入的组名自动生成“https://<gitlab域名>/<组名>”格式的URL,也可以手动修改组名部分。
  5. 设置“Visibility level”,可以选择“Private”(私密),“Internal”(内部),“Public”(公开)三个可见等级。
  6. “personalize your GitLab experience”部分的选项为选填,可以不填。
  7. 点击下方“Create your group”,创建群组。

邀请组员

  1. 点击首页左侧菜单栏“Group”,再点击现有的一个Group,进入这个Group的页面。
  2. 点击Group页面的左侧菜单栏“Manage”——“Members”。
  3. 点击右上角“Invite members”。
  4. 输入组员的邮箱或username。注意组员必须是已经注册账号的用户,输入后可以在下拉栏选中系统自动匹配的用户。
  5. 设置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 所有者 负责整个项目或群组最高权限管理的人 拥有完整控制权,可以管理项目或群组的核心设置
  1. 点击“Invite”。然后,用户会自动加入Group。同时,用户也会收到被成功邀请的通知邮件。

创建Project

  1. 点击首页左侧菜单栏“Project”。
  2. 点击右上角“New project”。
  3. 可以选择“Create blank project”创建空白项目,“Create from template”根据模板创建项目,或“Import project”从其他远程仓库导入已有项目。可以选择创建空白项目。
  4. 填写Project name。
  5. 确认项目URL。可以选择项目的归属为某个组或用户(自己)。可以修改URL中的项目缩写。
  6. 设置可见级别。如果项目属于组,可见级别一般与组一致。如果项目属于用户自己,可以设置级别为Private、Internal或Public。
  7. 设置其他项目配置选项。比如添加或者不加README。
  8. 点击“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的镜像。

解决方法

方法一:配置镜像加速器(需要镜像源,未采用)

  1. 创建配置:
sudo mkdir -p /etc/docker
sudo nano /etc/docker/daemon.json
  1. 写入一个可用的镜像源,例如组织管理员或云厂商提供的加速地址:
{"registry-mirrors": ["https://你的镜像加速地址"]
}
  1. 重启 Docker:
sudo systemctl daemon-reload
sudo systemctl restart docker
  1. 再次测试:
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 FoundHTTP/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占用?

  1. 查看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 侧配置

  1. 宿主机上用 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"
  1. 回退命令(比如需要换端口):
/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,确保和当前配置一致。方法:

  1. 修改:
sudo nano /etc/gitlab/gitlab.rb
  1. 把对应行改为:
external_url 'http://101.6.48.103:8181'
  1. 保存后执行:
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 个已经完全足够。

  1. 备份配置:
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-puma
  1. 编辑配置:
sudo nano /etc/gitlab/gitlab.rb
  1. 在文件末尾加入:
puma['worker_processes'] = 4
puma['min_threads'] = 4
puma['max_threads'] = 4
  1. 重新生成配置:
sudo gitlab-ctl reconfigure
  1. 完成后,只重启 Puma 和 Workhorse:
sudo gitlab-ctl restart puma
sudo gitlab-ctl restart gitlab-workhorse
  1. 检查日志:
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 的 VERSION2(满足)

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地址的端口映射,重启后需要进行相应修改。

← 返回列表