如何在 Ubuntu 24.04 上使用 ZFS 存储搭建 LXD 容器

捣鼓日记
如何在 Ubuntu 24.04 上使用 ZFS 存储搭建 LXD 容器
Shot by Eberhard Grossgasteiger

一步步教你在 Ubuntu 24.04 上搭建 LXD 容器并配置 ZFS 存储,涵盖安装、VPS 与本地 ZFS 存储池创建、容器启动、镜像发布及 SSH 连接配置。

简介

随着服务器功能的发展,它们越来越多地被用作开发环境 —— 尤其是基于 Linux 的工具。在 Windows 上安装 Coq 等语言可能较为困难,而快速原型开发通常会留下杂乱的依赖项,事后很难彻底清理。像 Vim 这类支持 LSP 的工具,也需要多个步骤才能配置和卸载。LXD 提供了轻量级的、操作系统级别的容器,支持隔离的多用户环境。它允许用户共享通用文件(例如 Vim 配置),同时为每个容器提供完全隔离的环境。与 Docker 类似,你可以发布自己的镜像,并根据需要进一步更新它。

另一个使用场景是在实验室环境中,当像 GPU 这样的硬件资源有限时,你可能希望每个组员都能使用该资源,而不会互相干扰。在这种情况下,你也可以使用 LXD 为每个用户创建一个容器,共享相同的硬件资源。此外,你还可以挂载公共文件(如数据集)来减少内存使用。

如何通过 Snap 在 Ubuntu 上安装 LXD

只需复制以下命令粘贴到终端中即可:

bash
      snap install lxd
    

Ubuntu 已经预装了 snap,如果你使用的是其他系统,请先使用你的包管理器安装 snap

安装成功后,你应该能看到如下提示信息:

bash
      lxd (5.21/stable) 5.21.3-c5ae129 from Canonical installed
    

如果你不是使用 root 账户,并且不希望每次使用 lxdlxc 时都输入 sudo,可以将当前用户添加到 lxd 用户组中:

bash
      sudo usermod -aG lxd $USER
    

要使更改生效,可以选择注销并重新登录,或执行以下命令:

bash
      newgrp lxd
    

🔧 避免以 root 身份运行 lxdlxc

一个实用建议:不要用 root 来运行 lxdlxc。即使你已经在 lxd 用户组中,直接使用 sudo 或以 root 身份运行这些命令仍可能带来问题,因为有些情况下只需要特定权限而不是完全的 root 权限。

比如,如果你直接用 root 来执行命令,可能会看到这样的错误:

console
      permanently dropping privs did not work: File exists
    

为 LXD 配置 ZFS 存储池(VPS 与本地)

如果你在 VPS 上部署服务,内存通常是有限的。因此,我希望能更清晰、更高效地进行管理。我选择了 ZFS 作为存储后端,原因是它在复制容器时只保存增量修改,能显著减少冗余、节省空间。同时你也可以手动定义存储池,更方便备份及指定数据的物理位置。

先执行以下命令安装:

bash
      apt install zfsutils-linux -y
    

如果没有报错,那就说明安装成功了。你可以再确认一下版本信息:

bash
      zfs --version
    

🖥️ 在本地机器上创建 ZFS 存储池

⏩ 如果你用的是 VPS,可以直接跳到 ☁️ 在 VPS 上通过虚拟磁盘创建 ZFS 存储池 部分。

如果你在实验室用的是一台个人电脑,可以直接指定你想用的物理磁盘,比如:

bash
      zpool create pool /dev/sda
    

这条命令的意思是,在 /dev/sda 这个磁盘上创建一个名为 pool 的 ZFS 存储池。

如果你看到类似这样的错误:

bash
      cannot resolve path '/dev/sda'
    

说明你的机器上并没有 /dev/sda 这个磁盘。先用下面的命令确认你实际的磁盘设备名:

bash
      df /
    

输出可能类似这样:

bash
      Filesystem     1K-blocks     Used Available Use% Mounted on
/dev/sda        62328864 10646208  48947556  18% /
    

Filesystem 那一列下看到的就是你要找的设备名。我这个是在 VPS 上跑的命令,你的输出可能会不同。

确认磁盘名称无误后,再执行一次创建存储池的命令即可:

bash
      zpool create pool /dev/sda
    

👉 确认无误后,可以直接跳到 创建用于 LXD 的 ZFS 数据集 部分。

☁️ 在 VPS 上通过虚拟磁盘创建 ZFS 存储池

如果你像我一样在 VPS 上操作,通常是无法直接访问整块物理磁盘的,因此我们需要先创建一个虚拟磁盘文件。

首先查看当前可用的磁盘空间:

bash
      df -h /
    

示例输出:

bash
      Filesystem      Size  Used Avail Use% Mounted on
/dev/vda2        60G   11G   47G  18% /
    

然后创建一个目录来存放虚拟磁盘文件:

bash
      mkdir -p /var/zfs
    

这条命令会创建 /var/zfs 目录,带上的 -p 参数确保即使上层目录不存在也会一并创建。

可以用下面的命令验证是否成功:

bash
      ls /var/zfs
    

如果目录不存在,会看到类似这样的信息:

bash
      ls: cannot access '/var/non-existing': No such file or directory
    

我这里有 47G 可用空间,所以决定分配 24G 给虚拟磁盘。你可以根据自己实际的磁盘空间选择合适的大小:

bash
      fallocate -l 24G /var/zfs/zfs.img
    

这会在 /var/zfs/zfs.img 创建一个 24GB 的虚拟磁盘文件。文件名可以自定义。

接下来将这个镜像文件映射到一个可用的 loop 设备:

bash
      losetup -fP /var/zfs/zfs.img
    

这条命令会将该镜像文件挂载到一个空闲的 loop 设备上。

用以下命令获取被映射的 loop 设备名称:

bash
      LOOPDEV=$(losetup -a | grep zfs.img | cut -d: -f1)
    

可以通过下面的命令验证:

bash
      echo ${LOOPDEV}
    

例如,你可能会看到输出为 /dev/loop3

最后,用这个 loop 设备创建 ZFS 存储池:

bash
      zpool create pool $LOOPDEV
    

这样就使用虚拟磁盘创建了名为 pool 的 ZFS 存储池。

为 LXD 创建 ZFS 数据集

当你的 ZFS 存储池准备好之后,下一步就是在里面创建一个数据集:

bash
      zfs create pool/lxd
    

这条命令会在现有的 pool 存储池中创建一个名为 lxd 的数据集。

如果你希望启用重复数据删除功能(deduplication),可以设置 dedup 属性为 on

bash
      zfs set dedup=on pool/lxd
    

要查看数据集是否创建成功以及其各项属性,可以执行:

bash
      zfs list
    

示例输出:

bash
      NAME       USED  AVAIL  REFER  MOUNTPOINT
pool       142K  22.8G    24K  /pool
pool/lxd    24K  22.8G    24K  /pool/lxd
    

从输出中可以看到,pool/lxd 数据集已成功创建。

虽然看起来步骤较多,但我尽可能加入了每一步的验证方式和可能遇到的问题,确保过程顺利。

使用 lxd init 配置 LXD

现在终于可以开始初始化 LXD 了,运行以下命令:

bash
      lxd init
    

这会启动一个交互式的配置流程,你会被依次询问多个问题:

bash
      Would you like to use LXD clustering? (yes/no) [default=no]:
    

如果你没有多台服务器或设备,不需要集群功能,直接回车或输入 no

bash
      Do you want to configure a new storage pool? (yes/no) [default=yes]:
    

虽然我们之前已经配置了存储,但这一步是为了把它关联到 LXD 中。

bash
      Name of the new storage pool [default=default]:
    

这个名字不需要和你的 ZFS 池名一致,自定义即可。

bash
      Name of the storage backend to use (powerflex, zfs, btrfs, ceph, dir, lvm) [default=zfs]:
    

确保选择 zfs,如果系统未安装 ZFS,这个选项可能不会出现。

bash
      Create a new ZFS pool? (yes/no) [default=yes]: no
    

不需要,谢谢。我们已经手动创建了 ZFS 存储池。

bash
      Name of the existing ZFS pool or dataset: pool/lxd
    

输入之前创建的数据集名 pool/lxd

bash
      Would you like to connect to a MAAS server? (yes/no) [default=no]:
    

大多数情况选 no,除非你清楚在使用 MAAS。

bash
      Would you like to create a new local network bridge? (yes/no) [default=yes]:
    
  • yes —— 适用于大多数 VPS 情况。
  • 如果在实验室环境,建议先咨询 IT 或指导老师是否允许自建网络桥接。
bash
      What should the new bridge be called? [default=lxdbr0]:
    

默认的 lxdbr0 没问题,除非你有特殊命名需求。

bash
      What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
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]:
    

最后这些选项直接回车即可,默认值对大部分用户都很合适。

启动你的第一个 LXD 容器

现在可以启动你的第一个 LXD 容器了:

bash
      lxc launch ubuntu:24.04 temp
    

这条命令会基于 ubuntu:24.04 镜像启动一个名为 temp 的容器。

启动完成后,可以用下面的命令查看容器列表:

bash
      lxc list
    

示例输出:

bash
      +------+---------+------+----------------------------------------------+-----------+-----------+
| NAME |  STATE  | IPV4 |                     IPV6                     |   TYPE    | SNAPSHOTS |
+------+---------+------+----------------------------------------------+-----------+-----------+
| temp | RUNNING |      | fd42:c80b:480:4c53:216:3eff:fecd:7a41 (eth0) | CONTAINER | 0         |
+------+---------+------+----------------------------------------------+-----------+-----------+
    

从输出中可以确认容器 temp 正在运行中。

⚠️ 没有 IPv4 地址?别急,这样修

你可能会发现容器没有 IPv4 地址,这意味着你无法通过 SSH 访问它,也不能对外暴露服务。

👉 如果你的容器 已有 IPv4 地址 并打算发布镜像,可跳转到 发布并复用 LXD 容器镜像

如果确实没有 IPv4 地址,并且你是在 VPS 上操作,那很可能是防火墙阻止了网络请求。很多 VPS 提供商默认封锁了所有端口。

为了解决这个问题,你需要正确配置防火墙,使容器可以获取 IP 并访问外部网络。可以参考官方文档:LXD 网络桥接与防火墙设置

如果你使用的是 UFW(默认 Ubuntu 的防火墙工具),可以执行以下命令(来自官方文档):

bash
      # 允许容器从宿主机获取 IP(DHCP)
sudo ufw allow in on lxdbr0 to any port 67 proto udp
sudo ufw allow in on lxdbr0 to any port 547 proto udp

# 允许容器解析域名(DNS)
sudo ufw allow in on lxdbr0 to any port 53

# 允许容器访问外网
CIDR4="$(lxc network get lxdbr0 ipv4.address | sed 's|\.[0-9]\+/|.0/|')"
CIDR6="$(lxc network get lxdbr0 ipv6.address | sed 's|:[0-9]\+/|:/|')"

sudo ufw route allow in on lxdbr0 from "${CIDR4}"
sudo ufw route allow in on lxdbr0 from "${CIDR6}"
    

配置完后,重启容器:

bash
      lxc restart temp
    

然后查看容器状态:

bash
      lxc list
    

你应该能看到 IPv4 地址了,例如:

bash
      +------+---------+-----------------------+----------------------------------------------+-----------+-----------+
| NAME |  STATE  |         IPV4          |                     IPV6                     |   TYPE    | SNAPSHOTS |
+------+---------+-----------------------+----------------------------------------------+-----------+-----------+
| temp | RUNNING | 10.249.126.223 (eth0) | fd42:c80b:480:4c53:216:3eff:fecd:7a41 (eth0) | CONTAINER | 0         |
+------+---------+-----------------------+----------------------------------------------+-----------+-----------+
    

进入容器:

bash
      lxc exec temp bash
    

提示符可能变成这样:

bash
      root@temp:~#
    

注意提示符现在显示容器主机名,说明你已经进入容器环境。像操作普通 Linux 一样使用即可。退出容器输入:

bash
      exit
    

如何发布并复用 LXD 容器镜像

如果你的容器已经正常分配了 IPv4 地址,就可以像平常一样在终端中安装软件包。一旦你对容器的配置满意了,可以将它发布为一个本地镜像,方便今后重复使用。

首先,停止容器:

bash
      lxc stop temp
    

然后使用 publish 命令将容器发布为镜像:

bash
      lxc publish temp --alias template --public
    

可以用下面的命令查看本地镜像列表:

bash
      lxc image list
    

你设置的别名(例如 template)应该会出现在输出中,例如:

bash
      +----------+--------------+--------+---------------------------------------------+--------------+-----------+-----------+------------------------------+
|  ALIAS   | FINGERPRINT  | PUBLIC |                 DESCRIPTION                 | ARCHITECTURE |   TYPE    |   SIZE    |         UPLOAD DATE          |
+----------+--------------+--------+---------------------------------------------+--------------+-----------+-----------+------------------------------+
| template | 5c72fbce13bc | yes    | Ubuntu 24.04 LTS server (20250610)          | x86_64       | CONTAINER | 430.77MiB | Jun 18, 2025 at 8:21pm (UTC) |
+----------+--------------+--------+---------------------------------------------+--------------+-----------+-----------+------------------------------+
|          | 9c73fb6ca4c2 | no     | ubuntu 24.04 LTS amd64 (release) (20250610) | x86_64       | CONTAINER | 258.29MiB | Jun 18, 2025 at 7:52pm (UTC) |
+----------+--------------+--------+---------------------------------------------+--------------+-----------+-----------+------------------------------+
    

上面那个没有别名的镜像,是你最初用来创建 temp 容器的基础镜像 —— 大概率是通过如下命令创建的:

bash
      lxc launch ubuntu:24.04 temp
    

🔍 ZFS 增量存储在 LXD 中是怎么工作的

本节主要是探索 ZFS 如何存储和组织你的容器数据。 如果你对 ZFS 的细节不太感兴趣,而是想要通过 SSH 连接到容器,你可以直接跳转到这里

否则,你已经看到本指南的最后一节了 —— 感谢一路阅读!

你可以通过以下命令查看 ZFS 的存储情况:

bash
      zfs list
    

示例输出:

bash
      NAME                                                                               USED  AVAIL  REFER  MOUNTPOINT
pool                                                                               691M  22.1G    24K  /pool
pool/lxd                                                                           678M  22.1G    24K  legacy
pool/lxd/buckets                                                                    24K  22.1G    24K  legacy
pool/lxd/containers                                                                190M  22.1G    24K  legacy
pool/lxd/containers/temp                                                           190M  22.1G   667M  legacy
pool/lxd/custom                                                                     24K  22.1G    24K  legacy
pool/lxd/deleted                                                                   144K  22.1G    24K  legacy
pool/lxd/deleted/buckets                                                            24K  22.1G    24K  legacy
pool/lxd/deleted/containers                                                         24K  22.1G    24K  legacy
pool/lxd/deleted/custom                                                             24K  22.1G    24K  legacy
pool/lxd/deleted/images                                                             24K  22.1G    24K  legacy
pool/lxd/deleted/virtual-machines                                                   24K  22.1G    24K  legacy
pool/lxd/images                                                                    487M  22.1G    24K  legacy
pool/lxd/images/9c73fb6ca4c2ae7dd357696a2e16ff8ac2f140090deab77b95a24add2386a55a   487M  22.1G   487M  legacy
pool/lxd/virtual-machines                                                           24K  22.1G    24K  legacy
    

你可以从中识别出 temp 容器和原始镜像(9c73fb6ca4c2)。

发布容器后,可以删除 temp 容器来释放空间:

bash
      lxc rm temp
    

此时的 ZFS 结构大致如下,唯一变化就是 temp 不见了:

bash
      NAME                                                                               USED  AVAIL  REFER  MOUNTPOINT
pool                                                                               499M  22.3G    24K  /pool
pool/lxd                                                                           487M  22.3G    24K  legacy
pool/lxd/buckets                                                                    24K  22.3G    24K  legacy
pool/lxd/containers                                                                 24K  22.3G    24K  legacy
pool/lxd/custom                                                                     24K  22.3G    24K  legacy
...
pool/lxd/images                                                                    487M  22.3G    24K  legacy
pool/lxd/images/9c73fb6ca4c2ae7dd357696a2e16ff8ac2f140090deab77b95a24add2386a55a   487M  22.3G   487M  legacy
pool/lxd/virtual-machines                                                           24K  22.3G    24K  legacy
    

说实话,这有点神奇 —— 你很难立即看出发布的镜像到底被存在哪。

如果你用刚发布的 template 镜像启动一个名为 cpp 的新容器:

bash
      lxc launch template cpp
    

你最终会看到你发布的镜像(5c72fbce13bc,别名为 template)开始占用独立的存储空间,大约为 667M。这很合理:压缩后的 template 镜像是 430.77MiB,原始镜像是 258.29MiB,两者之间的差值是约 172.48MiB

巧的是,这个差值也接近你当初的 temp 容器所占空间(190M),考虑到某些元数据开销,172.48MiB 是完全说得通的。

而新创建的 cpp 容器只用了大约 3.19M

bash
      NAME                                                                               USED  AVAIL  REFER  MOUNTPOINT
pool                                                                              1.15G  22.1G    24K  /pool
pool/lxd                                                                          1.13G  22.1G    24K  legacy
pool/lxd/buckets                                                                    24K  22.1G    24K  legacy
pool/lxd/containers                                                               3.21M  22.1G    24K  legacy
pool/lxd/containers/cpp                                                           3.19M  22.1G   668M  legacy
pool/lxd/custom                                                                     24K  22.1G    24K  legacy
...
pool/lxd/images                                                                   1.13G  22.1G    24K  legacy
pool/lxd/images/5c72fbce13bcbdfa41285d8b3af408a38f824c38c00b6694c10a4cdf814dae46   667M  22.1G   667M  legacy
pool/lxd/images/9c73fb6ca4c2ae7dd357696a2e16ff8ac2f140090deab77b95a24add2386a55a   487M  22.1G   487M  legacy
pool/lxd/virtual-machines                                                           24K  22.1G    24K  legacy
    

接着你又用 template 镜像启动了另一个容器 another-cpp

bash
      lxc launch template another-cpp
    

ZFS 的使用情况变成这样:

bash
      NAME                                                                               USED  AVAIL  REFER  MOUNTPOINT
pool                                                                              1.15G  22.1G    24K  /pool
pool/lxd                                                                          1.13G  22.1G    24K  legacy
pool/lxd/buckets                                                                    24K  22.1G    24K  legacy
pool/lxd/containers                                                               5.11M  22.1G    24K  legacy
pool/lxd/containers/another-cpp                                                   1.88M  22.1G   667M  legacy
pool/lxd/containers/cpp                                                           3.21M  22.1G   668M  legacy
pool/lxd/custom                                                                     24K  22.1G    24K  legacy
...
pool/lxd/images                                                                   1.13G  22.1G    24K  legacy
pool/lxd/images/5c72fbce13bcbdfa41285d8b3af408a38f824c38c00b6694c10a4cdf814dae46   667M  22.1G   667M  legacy
pool/lxd/images/9c73fb6ca4c2ae7dd357696a2e16ff8ac2f140090deab77b95a24add2386a55a   487M  22.1G   487M  legacy
pool/lxd/virtual-machines                                                           24K  22.1G    24K  legacy
    

此时,两个容器 cppanother-cpp 都在运行,并且额外占用的空间非常少。

出于好奇,你在 another-cpp 里安装了 neovim。我们来看空间变化:

bash
      NAME                                                                               USED  AVAIL  REFER  MOUNTPOINT
pool                                                                              1.30G  22.0G    24K  /pool
pool/lxd                                                                          1.28G  22.0G    24K  legacy
pool/lxd/buckets                                                                    24K  22.0G    24K  legacy
pool/lxd/containers                                                                160M  22.0G    24K  legacy
pool/lxd/containers/another-cpp                                                    157M  22.0G   791M  legacy
pool/lxd/containers/cpp                                                           3.21M  22.0G   668M  legacy
...
pool/lxd/images                                                                   1.13G  22.0G    24K  legacy
pool/lxd/images/5c72fbce13bcbdfa41285d8b3af408a38f824c38c00b6694c10a4cdf814dae46   667M  22.0G   667M  legacy
pool/lxd/images/9c73fb6ca4c2ae7dd357696a2e16ff8ac2f140090deab77b95a24add2386a55a   487M  22.0G   487M  legacy
pool/lxd/virtual-machines                                                           24K  22.0G    24K  legacy
    

现在 another-cpp 使用了大约 157MB,正好体现了安装 neovim 和其他依赖所占用的空间。

💡 ZFS 增量存储到底省了多少

在这种设置下,ZFS 的增量存储特性真正发挥了优势:

  • 基于同一个镜像启动的容器能够高效共享存储空间。
  • 只有变更的部分会被写入,从而保持磁盘使用的最小化。
  • 即使某个容器产生了较大的差异,相较于完整复制,其空间节省仍然非常可观。

因此,无论你的容器保持原样,还是经过了定制,ZFS 都能为你带来存储上的优势。

通过 SSH 连接 LXD 容器:端口转发与防火墙配置(可选)

这一节是可选的,但如果你想从远程机器通过 SSH 连接到容器,它会很有用。

要将容器的 SSH 端口暴露到外部网络,可以使用以下命令:

bash
      lxc config device add cpp sshproxy proxy listen=tcp:0.0.0.0:3000 connect=tcp:127.0.0.1:22
    

没错,它看起来有点长 —— 但你只需要改下面这两处:

  • cpp 替换成你的容器名。
  • 3000 换成你想对外开放的端口号。

如果成功了,你会看到:

console
      Device sshproxy added to cpp
    

🔥 别忘了防火墙 —— 用 UFW 开放端口

如果你是第一次接触这些,一定别忘了在防火墙里打开端口,这样外部才能访问。

比如,使用 ufw 的话:

bash
      ufw allow 3000
    

这个命令会允许 3000 端口的流量,输出看起来像这样:

console
      Rule added
Rule added (v6)
    

你可以通过以下命令确认规则是否添加成功:

bash
      ufw status
    

Sample output:

bash
      Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
67/udp on lxdbr0           ALLOW       Anywhere
547/udp on lxdbr0          ALLOW       Anywhere
53 on lxdbr0               ALLOW       Anywhere
3000                       ALLOW       Anywhere
22/tcp (v6)                ALLOW       Anywhere (v6)
67/udp (v6) on lxdbr0      ALLOW       Anywhere (v6)
547/udp (v6) on lxdbr0     ALLOW       Anywhere (v6)
53 (v6) on lxdbr0          ALLOW       Anywhere (v6)
3000 (v6)                  ALLOW       Anywhere (v6) 

Anywhere                   ALLOW FWD   10.219.247.0/24 on lxdbr0
Anywhere (v6)              ALLOW FWD   fd42:2bf9:abb2:6256::/64 on lxdbr0
    

🧑‍💻 终于,SSH 进你的 LXD 容器

注意你需要使用服务器的 IP 地址。用户名是出现在终端提示符 @ 前面的那个 —— 例如在 root@cpp:~# 中,用户名是 root,不是 cpp。端口则是你之前指定的那个。

下面是一个示例命令:

bash
      ssh root@155.xxx.xxx.xxx -p 3000
    

如果一切正常,它会提示你输入密码:

bash
      root@155.xxx.xxx.xxx's password:
    

如果你还没给这个用户设置密码,可以回到容器中运行:

bash
      passwd
    

示例:

console
      root@cpp:~# passwd
New password:
Retype new password:
passwd: password updated successfully
    

💡 提示:输入密码时不会显示任何内容 —— 这是 Linux 在输入密码时的正常行为。

🚫 出现 “Permission denied (publickey)”?别慌

如果你看到这个错误:

console
      root@155.xxx.xxx.xxx: Permission denied (publickey).
    

这通常说明你的 SSH 服务器被配置为不允许密码登录。要解决这个问题:

bash
      vim /etc/ssh/sshd_config
    

在容器内部编辑 SSH 配置文件:

bash
      vim /etc/ssh/sshd_config
    

找到并修改以下行:

bash
      PasswordAuthentication yes
    

如果你是以 root 身份登录,还要确认这一行:

bash
      PermitRootLogin yes
    

保存文件后,重启 SSH 服务:

bash
      systemctl restart ssh
    

现在你应该可以使用密码通过 SSH 连接了。

🛠️ 还是被拒?修复 sshd_config 的覆盖问题

如果你还是无法访问,别担心 —— 我自己也遇到过这个问题。

在你的 /etc/ssh/sshd_config 文件中,可能会有这样一行:

console
      Include /etc/ssh/sshd_config.d/*.conf
    

这表示 /etc/ssh/sshd_config.d/ 目录下的所有 .conf 文件都有可能覆盖主配置文件中的设置。

要检查这些文件,运行:

bash
      ls -l /etc/ssh/sshd_config.d/
    

示例输出:

console
      total 1
-rw-r--r-- 1 root root 26 Jun 10 12:54 60-cloudimg-settings.conf
    

在这个例子中,只有一个覆盖文件。查看里面的内容:

bash
      cat /etc/ssh/sshd_config.d/*.conf
    

如果你看到类似这样的内容:

bash
      PasswordAuthentication no
    

那问题就找到了。它会覆盖你在主配置中设定的内容,并禁用了密码登录。

打开刚才看到的那个文件:

bash
      vim /etc/ssh/sshd_config.d/60-cloudimg-settings.conf
    

你有两个选择:要么用 # 注释掉那一行(比如 # PasswordAuthentication no),要么显式设置为允许密码登录:

bash
      PasswordAuthentication yes
    

然后重启 SSH 服务:

bash
      systemctl restart ssh
    

确认更改是否生效:

bash
      sshd -T | grep passwordauthentication
    

你应该会看到:

bash
      passwordauthentication yes
    

✅ 再来一次 —— 这次该成了

现在,再次尝试登录:

bash
      ssh root@155.xxx.xxx.xxx -p 3000
    

如果成功了 —— 欢迎进入!

🎉 收尾:Hello World!

庆祝一下吧,我们来编译个经典示例:

bash
      root@cpp:~# vim main.cpp
 [New] 6L, 71B written
root@cpp:~# g++ main.cpp
root@cpp:~# ./a.out
Hello World!
    

尽情享受你的开发环境吧!