外观
一次基于LXD的实验室GPU服务器环境配置及踩坑记录
前言
本文的受众为实验室的公用 GPU 服务器管理员,需要有一定的 Linux 基础,或者对此感兴趣的普通用户。
如果您并不关心为何这样配置服务器环境,以及对如何部署、管理这样一台服务器不感兴趣,可以选择跳过这些内容,前往管理员指北、用户指南。
事情的起因是实验室购入了一台公用 GPU 服务器,考虑到每个人所需的软件和环境差异巨大,因此,需要一种方案来满足多用户对服务器的使用的需求。
这里列出一些期望达到的目标:
- 不同用户之间不能相互影响
- 用户要能便捷地访问服务器
- 用户的操作不能影响到宿主机的稳定运行
- 用户应当可以自由地访问外部网络
- 用户应当可以使用 GPU,不同用户可以共享 GPU
- 用户应当可以自由地配置程序运行环境
- 管理员应该能轻松地管理用户
注意: 本次部署属于科研基础架构的部署,属于各种软件的工程实现和部署,本身并不具备科研价值,同样也无法基于该方法搭建的服务器实验运行结果、实验数据产出做出任何保障。
架构选择
从最初的需求出发,我们想要不同用户之间不能相互影响,也就是在用户级别进行隔离,主流的隔离方案包括 Docker(容器化)、LXC(Linux 容器)、OpenVZ(虚拟机)。
首先是容器方案,以 Docker 为例。考虑到开销问题,Ubuntu in Docker 首先被排除备选。Docker 有成熟的NVIDIA-Docker支持,可以允许不同容器之间共享 GPU,Docker 成熟的网络接口也使容器的可访问性得到了保障,同时可以使用 Linux 内核的cgroups灵活分配硬件资源,是一个不错的备选方案。
OpenVZ、KVM、Xen 等虚拟机方案在资源利用上不够轻量,管理上相较 Docker 也复杂很多,同样基于开销问题将其排除备选。
LXC 可以理解为内核级的容器,允许不同容器之间共享 GPU,相比 Docker 更加轻量,也可以更方便地使用系统资源。
Docker 的隔离更偏向于运行环境的隔离,将应用和全部依赖环境全部打包到一个单一对象中,可以在不包含完整的操作系统的情况下运行应用,而 LXC 基于 namespaces 进行隔离,可以用更低的开销提供灵活的系统环境,支持用户在系统层面肆意整活,这给了我充足的理由选择 LXC。甚至用户可以在 LXC 容器中再套娃一个 Docker,既然如此,也没什么好纠结的了。
什么?你问 LXD?
那只是我用原生 LXC 觉得太麻烦,并且解决了 LXC 支持跨主机容器迁移不完全、容器管理复杂等问题的备胎,不提也罢(×)。 by yuany3721
如果您对 LXC 与 LXD 的区别感兴趣,您可以展开这里了解一些信息
就在本次部署的两周前,LXD 于 2023 年 6 月 4 日正式移交给 Canonical,公告中展示了这一动作的具体变化。
就使用体验而言,LXD 无疑是远超 LXC 的,尽管它们似乎(可能就是)用的同一种实现方式和内核。
以下是一些功能在使用上的区别,也正是复杂性的问题,使我放弃了直接使用 LXC 的想法,转而由 LXD 实现本次部署。
功能 | LXC | LXD |
---|---|---|
新建容器 | lxc-create -t download -n template -- --dist ubuntu --release jammy --arch amd64 | lxc launch tuna-images:ubuntu/jammy template |
启动容器 | lxc-start -n template | lxc start template |
向容器中注入命令 | lxc-attach -n template -- apt update | lxc exec template -- apt update |
修改容器配置 | vi path/to/lxc/template/config | lxc config edit template |
挂载 GPU | for x in $(ls /dev/nvidia\*); do echo lxc.mount.entry = $x $(echo $x | cut -b 2-) none bind,optional,create=file >> path/to/lxc/template/config done | lxc config device add template gpu gpu |
环境说明及前置工作
环境说明
本次的物理机硬件环境如下:
CPU: Intel(R) Xeon(R) Platinum 8336C CPU
RAM: Samsung DDR4 SingleBitECC 3200 MT/s * 4
GPU: NVIDIA GeForce RTX 4090 * 2
Disk: Samsung SSD 980 1TB + WUH721816AL 14TB
宿主机所在网络环境如下:
gateway: 172.16.60.1/23
mask: 255.255.254.0
前置工作
- 安装 Ubuntu Server
选择 Ubuntu Server 22.04.2 作为宿主机的系统,使用 rufus 制作系统启动盘后插入服务器,使用 SuperMicroIPMIView 扫描 IPMI 端口,连接 IPMI 服务安装系统,细节省略。
这里其实可以通过网络安装 ubuntu,但是没有折腾,mark 一下以后有机会进行尝试
- 配置系统环境
配置系统联网超时
配置 swap 分区
配置数据盘挂载
将
WUH721816AL 14TB
格式化为/dev/sda1
,并挂载在/data
目录设置 data 目录权限:
$ sudo chmod 755 /data $ sudo chmod +t /data
配置 OpenSSH(可选)
允许密码登录、修改服务端口为 33
配置防火墙
$ sudo ufw allow 33/tcp $ sudo ufw enable $ sudo ufw reload
- 配置网桥
在类似我们的方案中,为实验室建立公用 GPU 服务器、实验室 GPU 服务器的 LXD 虚拟化等实操中(不知道为什么)采用了基于 iptables、端口映射的技术实现绕过宿主机访问容器。
但是!这一点也不优雅! 服务器就在内网,又不缺 IPv4 地址,为何要加上端口号访问呢?因此,我在宿主机中建立了一个网桥,lxc 容器通过网桥直接从网关 DHCP 获取 IP。
在 Ubuntu22.04 中,系统默认使用 netplan 配置网络。我们直接以修改 netplan 配置的方式新建一个网桥:
$ cd /etc/netplan
# 备份默认配置
$ sudo cp 00-installer-config.yaml 00-eno1-br0-bridge.yaml
$ sudo mv 00-installer-config.yaml 00-installer-config.yaml.bak
$ sudo vi 00-eno1-br0-bridge.yaml
network:
renderer: networkd
ethernets:
eno1:
# 固定IP
dhcp4: no
bridges:
br0:
interfaces: [eno1]
# 这是修改网桥之前eno1网卡获取到的ip,保持一致可以避免一些麻烦
addresses: [172.16.61.82/23]
routes:
# 配置默认网关,gateway4已经被标记为deprecated不建议使用
- to: default
via: 172.16.60.1
# 加上权重,不进行配置的话产生冲突,使用route命令查看路由表会看到eno1和br0两条default路由,导致无法联网
metric: 100
nameservers:
addresses:
# DNS服务器,edit as you like
- 114.114.114.114
- 8.8.8.8
- 8.8.4.4
version: 2
# 应用网络
$ sudo netplan try
检查网络配置,ip a
类似这样:
2: eno1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP group default qlen 1000
link/ether 3c:ec:ef:cc:60:28 brd ff:ff:ff:ff:ff:ff
altname enp6s0
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether d6:87:d1:44:19:7c brd ff:ff:ff:ff:ff:ff
inet 172.16.61.82/23 brd 172.16.61.255 scope global br0
valid_lft forever preferred_lft forever
inet6 fe80::d487:d1ff:fe44:197c/64 scope link
valid_lft forever preferred_lft forever
route
类似这样:
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default _gateway 0.0.0.0 UG 100 0 0 br0
172.16.60.0 0.0.0.0 255.255.254.0 U 0 0 0 br0
配置 LXD-GPU 环境
配置运行环境
- 安装 LXC、LXD
这里的 LXD 使用5.0/stable
版本,请使用前先查阅LXD 官方安装指引修改安装版本号。
$ sudo apt update
$ sudo apt install lxc
$ sudo snap instll lxd --channel=5.0/stable
如果需要的话,可能有必要进行apt 换源
由于 snap 只有 server outside the Great Firewall,所以提示Unable to contact snap store
的时候,可能需要一点点科学上网手段,以下是临时设置 snap 代理的方法:
$ sudo snap set system proxy.http="your proxy url"
$ sudo snap set system proxy.https="your proxy url"
- 检查 lxc 的依赖
使用lxc-checkconfig
检查 lxc 容器的必要组件。具体到本次配置,该命令显示系统存在以下问题:
Cgroup v1 systemd controller: missing
Cgroup v1 freezer controller: missing
Cgroup ns_cgroup: required
在 Ubuntu22.04 中,默认情况下不会启用 cgroup v1 systemd controller 和 ns_cgroup。编辑/etc/default/grub
:
GRUB_CMDLINE_LINUX="cgroup_enable=memory systemd.unified_cgroup_hierarchy=0 cgroup_namespace.enable=1"
保存并退出后,更新 GRUB 配置并重新启动系统以应用修改。
$ sudo update-grub
$ sudo reboot
- 配置防火墙以适应网桥
在 Ubuntu22.04(大概率以及未来的版本)中,ufw 改变了它的默认规则,需要修改网桥的 route 属性,否则会导致 lxc 容器无法从外部网关获取 IP,即使正确地配置了 daemon、dhcp 等所有服务。参考Ubuntu 22.04 LXD networking issue, not getting IPs, and solution
$ sudo ufw allow out on br0
$ sudo ufw route allow out on br0
配置基础镜像
注:以下配置过程中,$
开头表示命令在宿主机中运行,root@template:~#
开头表示命令在 template 容器中以 root 身份(exec 进去的)运行。
- 初始化 LXD 环境
$ lxd init
以下是我的配置选项,仅供参考:
Would you like to use LXD clustering? (yes/no) [default=no]: no
Do you want to configure a new storage pool? (yes/no) [default=yes]:
Name of the new storage pool [default=default]: lxd-pool
Name of the storage backend to use (cephobject, dir, lvm, zfs, btrfs, ceph) [default=zfs]:
Create a new ZFS pool? (yes/no) [default=yes]:
Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]:
Size in GiB of the new loop device (1GiB minimum) [default=30GiB]: 50GiB
Would you like to connect to a MAAS server? (yes/no) [default=no]:
Would you like to create a new local network bridge? (yes/no) [default=yes]: no
Would you like to configure LXD to use an existing bridge or host interface? (yes/no) [default=no]: yes
Name of the existing bridge or host interface: br0
Would you like the LXD server to be available over the network? (yes/no) [default=no]:
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]:
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]: yes
config: {}
networks: []
storage_pools:
- config:
size: 50GiB
description: ""
name: lxd-pool
driver: zfs
profiles:
- config: {}
description: ""
devices:
eth0:
name: eth0
nictype: bridged
parent: br0
type: nic
root:
path: /
pool: lxd-pool
type: disk
name: default
projects: []
cluster: null
修改默认配置文件,限制默认存储大小,避免一个容器占满硬盘
$ lxc profile edit default
config: {}
description: Default LXD profile
devices:
eth0:
name: eth0
nictype: bridged
parent: br0
type: nic
root:
path: /
pool: lxd-pool
size: 50GB
type: disk
name: default
used_by: []
- 创建基础容器模板
添加清华 LXC 镜像,创建template
容器模板
$ lxc remote add tuna-images https://mirrors.tuna.tsinghua.edu.cn/lxc-images/ --protocol=simplestreams --public
$ lxc launch tuna-images:ubuntu/jammy template
Creating template
Starting template
配置基本环境
# 进入容器
$ lxc exec template bash
# 修改默认用户的密码
root@template:~# passwd ubuntu
yourpassword
# 修改时区
root@template:~# timedatectl set-timezone Asia/Shanghai
# 修改apt源
root@template:~# cp /etc/apt/sources.list /etc/apt/sources.list.bak
root@template:~# vi /etc/apt/sources.list
# 注释掉源码部分加快apt update速度
deb https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
# deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
root@template:~# apt update
root@template:~# apt upgrade -y
# 安装ssh server
root@template:~# apt install -y openssh-server
# 安装必要软件
root@template:~# apt install -y ufw net-tools
- 配置 GPU 环境
# 挂载gpu
$ lxc config device add ContainerName gpu gpu
# 查看显卡设备文件
$ ls /dev/nvidia*
# 安装显卡驱动
root@template:~# apt install -y ubuntu-drivers-common
root@template:~# ubuntu-drivers devices
root@template:~# ubuntu-drivers autoinstall
root@template:~# nvidia-smi
# 禁用自动更新(23.08.11update)
root@template:~# apt-mark hold *nvidia*
root@template:~# apt-mark showhold
# 显卡驱动版本
dpkg -l | grep nvidia
# NVRM版本
cat /proc/driver/nvidia/version
出现类似以下界面说明显卡驱动配置成功。

- 发布
template
作为基础容器模板
$ lxc stop template
$ lxc publish template --alias template --public
- 基于
template
建立带桌面环境的容器模板
$ lxc launch template template-gnome
$ lxc exec template-gnome
# 安装gnome桌面
root@template-gnome:~# apt install --no-install-recommends -y ubuntu-desktop
# 安装xrdp远程
root@template-gnome:~# apt install -y xrdp
root@template-gnome:~# systemctl enable xrdp
发布template-gnome
$ lxc stop template-gnome
$ lxc publish template-gnome --alias template-gnome --public
至此,本次服务器的环境部署就完成了,以下为管理员与用户的使用说明。
管理员指北
注意: 宿主机的权限对您是完全开放的,如果您需要在宿主机中执行任何本节之外的命令,请您执行前务必完全明确您的操作会带来什么后果。
如何访问宿主机
您有两种连接宿主机的方式:
使用 ssh 工具访问宿主机 IP 的 33 端口,并使用用户名和密码进行认证;
在浏览器中访问宿主机的 IPMI 端口 IP,并使用IPMI 用户名和IPMI 密码进行认证,进入“远程控制”选项卡,启动控制台。
这两种方法的区别在于,ssh 访问的是 Ubuntu 系统,您只能在系统级别对宿主机进行控制,使用 IPMI 访问则可以获得宿主机包括 BIOS、硬件在内的完全控制。如非必要,不建议访问 IPMI 端口进行管理
新建容器
当前已经配置好了template
、template-gnome
、template-docker
三种容器模板,分别代表最基础的命令行容器、带 gnome 桌面和 xrdp 远程的容器、带 docker 环境的命令行容器,推荐用户使用template
进行操作。
以用户提供的容器名为yuany3721
为例,以下操作可以为该用户新建一个容器:
# 新建容器
$ lxc launch template-docker yuany3721
# 挂载GPU
$ lxc config device add yuany3721 gpu gpu
# 挂载硬盘
$ sudo mkdir /data/yuany3721/
$ sudo chown -R 1000:1000 /data/yuany3721
$ lxc config device add yuany3721 data disk source=/data/yuany3721/ path=/data/
$ lxc config device add yuany3721 sdata disk source=/data/share/ path=/sdata/
# 查看容器详情
$ lxc list
然后将查看到的容器 IP(eth0)告知用户,用户即可使用ssh ubuntu@container-IP
访问容器。
如果用户需要在容器中使用 docker,需要额外修改容器配置如下:
$ lxc config set yuany3721 security.nesting true
$ lxc config set yuany3721 security.privileged true
$ lxc restart yuany3721
删除容器
$ lxc stop delcontainer
$ lxc delete delcontainer
用户指南
联系管理员获取授权
您需要告知管理员的信息包括:您的用户名、您是否需要图形界面(推荐无图形界面,性能更好)、您是否希望使用 docker
管理员会告知您一个 IP,您可以通过该 IP 访问服务器,身份认证的账号为ubuntu
,密码为123456
。
注意: 访问服务器的账号与您的用户名无关 ;您在获取授权后请尽快访问服务器并修改密码
默认情况下,数据卷将被挂载于/data
(用户目录)和/sdata
(共享目录),共享目录的/sdata/model
中有常用模型文件。
从命令行访问服务器并修改密码
Ubuntu 在输入密码时不会显示输入内容,请不要惊慌,正常输入完毕回车即可。
以 Windows 系统为例,Win+R打开cmd
,输入命令ssh ubuntu@您的IP
,访问服务器。
提示以下信息时,输入 yes 回车,然后输入默认密码123456
The authenticity of host '172.16.61.xx (172.16.61.xx)' can't be established.
xxxx key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxx/s.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

在新出现的命令行中输入passwd ubuntu
修改密码,修改成功后就可以使用新的密码登录服务器了。

通过 xrdp 登录图形界面
在 Windows 下,可以使用系统自带的 RDP 软件,Win+R打开mstsc
,输入管理员给您的 IP,点击连接(若出现提示称无法验证远程计算机身份,忽略提示并点击“是”继续连接)。

连接后出现以下界面,在界面中输入账号ubuntu
,密码为默认密码或您修改过的密码,点击“OK”,如果信息无误,则可以正常连接到服务器。

如果您不是 Windows 机器,Linux 可安装 remmina 访问,Mac 可安装 Microsoft Remote Desktop 访问,操作类似,在此不再赘述。
该连接方式的优点是可以获得完整的 Linux 桌面体验,但当网络环境不佳时操作延时特别大、桌面长时间运行可能会卡死。
文件传输
系统配置了一个端口为 22 的 sftp 服务器,您可以使用 Filezilla 等 ftp 工具连接并使用 ftp 进行文件传输。
当然,您也可以通过远程桌面进行文件拖放,或者使用 ssh 进行文件传输,任何可以与 Linux 实体机器进行网络文件传输的工具都可以在该服务器上使用。
其它服务器使用方式
这里列举几种推荐的使用方式,不同方式的使用场景各不相同,请您在了解各方式的优劣之后,结合自己的需求进行选择:
使用 MobaXterm 进行连接,该方法可以在没有桌面环境的情况下,转发单个应用的图形界面。例如您可以选择一个高性能的命令行界面,安装 Pycharm 之后在远程机器上使用图形化的 Pycharm 界面。
使用 vscode 的 remote-ssh 插件远程连接,该方法可以省去 ftp 服务器,直接打开远程服务器中的工程,并进行文件的上传下载和修改、程序的修改和运行。
直接使用 powershell 等工具 ssh 远程连接。