踩坑:Linux 上修改系统盘 xfs ftype=0 以支持 overlay 的方法

1,547 阅读10分钟

背景

在我们的生产环境开通的虚拟主机上尝试安装 kubelet 的时候,发现报了这么一个错误:

[ERROR CRI]: container runtime is not running: output: time="2024-01-09T10:28:09+08:00" level=fatal msg="validate service connection: CRI v1 runtime API is not implemented for endpoint "[unix:///var/run/containerd/containerd.sock]()": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService"

现象就是 containerd 是好的,但是放到 crictl 去访问的时候就会报 CRI v1 runtime API is not implemented for endpoint 这个错误。

关于这个错误上网找的其他办法主要指向这个:serverfault.com/a/1126729/3…

简单来说就是 /etc/containerd/config.toml 这个文件里面需要关闭 disabled_plugins = ["cri"] 这个配置。但在我的场景下这个配置原本就没有。

然后一番排查,最后通过 systemctl status containerd -l 找到了问题的根源:

failed to create CRI service: failed to find snapshotter "overlayfs"

原本的系统盘 / 下面挂载的是 xfs,查了 xfs 对 overlayfs 的支持需要在格式化的时候指定 ftype=1,而这个没有,验证方法:

xfs_info /

输出是这样的:

meta-data=/dev/mapper/VG01-lv_root isize=256    agcount=4, agsize=2736896 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0        finobt=0 spinodes=0
data     =                       bsize=4096   blocks=10947584, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal               bsize=4096   blocks=5345, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

可以看到 ftype=0,说明文件系统不支持。

为了解决这个问题,至少需要保证 /var/lib/containerd 这个目录下的文件系统支持 overlayfs,其实之前用 docker 的时候也遇到过同样的问题,对应的目录是 /var/lib/docker

我们可以创建一个新的数据盘,将其格式化为 ext4 或者 xfs(ftype=1) 的方式,然后单独挂载到 /var/lib/containerd 上面,这样可以解决问题。

但是这样操作我们需要浪费一块额外的数据盘而且多出来一些很冗余的配置,不是一个特别完美的解决方案。

解决方案

更好的方案显然是直接把系统盘的文件系统改掉,但是显然这需要格式化。

原始的系统磁盘布局是这样的:

# lsblk
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                 8:0    0   50G  0 disk 
├─sda1              8:1    0  200M  0 part /boot
└─sda2              8:2    0 49.8G  0 part 
  ├─VG01-lv_root  253:0    0 41.8G  0 lvm  /
  └─VG01-lv_swap  253:1    0    8G  0 lvm  [SWAP]
sdb                 8:16   0    1G  0 disk 
└─data01vg-appslv 253:2    0  896M  0 lvm  /apps
sr0                11:0    1 1024M  0 rom  

# df -h
文件系统                     容量  已用  可用 已用% 挂载点
devtmpfs                     1.9G     0  1.9G    0% /dev
tmpfs                        1.9G     0  1.9G    0% /dev/shm
tmpfs                        1.9G   36M  1.9G    2% /run
tmpfs                        1.9G     0  1.9G    0% /sys/fs/cgroup
/dev/mapper/VG01-lv_root      42G  5.0G   37G   12% /
tmpfs                        378M     0  378M    0% /run/user/0
/dev/sda1                    197M  112M   86M   57% /boot
/dev/mapper/data01vg-appslv  866M   30M  776M    4% /apps
tmpfs                         60M     0   60M    0% /var/log/rtlog
tmpfs                        378M     0  378M    0% /run/user/1002
tmpfs                        378M     0  378M    0% /run/user/1000

我们申请的资源就是这样,50GB 的系统盘分了两块,其中 VG01-lv_root 有 41.8G 的空间,另外还有 8G 的空间用作 swap 目录。但是实际根目录 / 下面只有 5G 的数据。

受这个帖子的启发,superuser.com/q/1321926/5…,我决定尝试在现有的环境下做一次给系统盘做一个换头手术,将系统盘的文件系统化格式化掉,但是保留原本的所有信息再重新拉起。

注意到,swap 分区其实是多余的,我们装 kubernetes 的时候本来就要关 swap,我们是不是能够用 swap 分区的 8G 空间用来腾挪,从而把根目录的挂载盘格掉呢?这就是我的乾坤大挪移方案:

  1. 关闭 swap,然后将 swap 格式化成 xfs(ftype=1),挂载到 /mnt 上;
  2. 将根目录 / (VG01-lv_root) 的数据通过 rsync 全部复制到 /mnt (VG01-lv_swap);
  3. 修改启动条件,将 / 和 /mnt 的挂载盘 (VG01-lv_root 和 VG01-lv_swap) 互换,重启;
  4. 将 /mnt 挂载了 VG-lv_root 格式化成 xfs(ftype=1);
  5. 将根目录 / (VG01-lv_swap) 的数据通过 rsync 全部复制到 /mnt (VG01-lv_root);
  6. 修改启动条件,换回最初的版本,重启,大功告成。

我居然把上面这个试通了,下面是实际操作细节:

1. 关闭 swap,格式化并挂载到 /mnt 上

临时关闭 swap (后面再改 /etc/fstab)

swapoff -a

格式化成 xfs(ftype=1)

mkfs.xfs -f -n ftype=1 /dev/mapper/VG01-lv_swap

输出:

meta-data=/dev/mapper/VG01-lv_swap isize=512    agcount=4, agsize=524288 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0, sparse=0
data     =                       bsize=4096   blocks=2097152, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

将 VG01-lv_swap 临时挂载到 /mnt:

mount /dev/mapper/VG01-lv_swap /mnt

2. 将根目录的数据 rsync 到 /mnt 上

注意要加几个特殊的参数,同时忽略 /mnt 目录本身,/boot 目录,以及数据盘挂载的目录 /apps。

rsync -avxHAX --delete --devices --specials --exclude /mnt --exclude /apps --exclude /boot / /mnt/

然后 ls -la /mnt 看一下数据是不是都过去了。

3. 修改启动条件

修改 /mnt/etc/fstab (注意旧的位置 /etc/fstab 已经不 care 了),原版长这样:

#
# /etc/fstab
# Created by anaconda on Wed Feb 15 08:41:10 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/VG01-lv_root /                       xfs     defaults        0 0
UUID=faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3 /boot                   xfs     defaults        0 0
/dev/mapper/VG01-lv_swap swap                    swap    defaults        0 0
/dev/mapper/data01vg-appslv /apps ext4 defaults 0 0

将 VG01-lv_root 改成 VG01-lv_swap,同时注释掉 swap 那行,改完之后变成这样:

#
# /etc/fstab
# Created by anaconda on Wed Feb 15 08:41:10 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/VG01-lv_swap /                       xfs     defaults        0 0
UUID=faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3 /boot                   xfs     defaults        0 0
#/dev/mapper/VG01-lv_swap swap                    swap    defaults        0 0
/dev/mapper/data01vg-appslv /apps ext4 defaults 0 0

然后,启动点的配置也要修改,修改 /boot/grub2/grub.cfg(记得先备份原版),找到下面这个位置,将所有 VG01-lv_root 和 VG01-lv_swap 对调:

涉及的原版片段是这样的,共需修改六处:

### BEGIN /etc/grub.d/10_linux ###
menuentry 'CentOS Linux (3.10.0-1160.92.1.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-327.el7.x86_64-advanced-6e2cc491-a603-4ec9-9677-65d425c720e2' {
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_msdos
        insmod xfs
        set root='hd0,msdos1'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 --hint='hd0,msdos1'  faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3
        else
          search --no-floppy --fs-uuid --set=root faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3
        fi
        linux16 /vmlinuz-3.10.0-1160.92.1.el7.x86_64 root=/dev/mapper/VG01-lv_root ro crashkernel=auto rd.lvm.lv=VG01/lv_root rd.lvm.lv=VG01/lv_swap rhgb biosdevname=0 net.ifnames=0  quiet LANG=en_US.UTF-8
        initrd16 /initramfs-3.10.0-1160.92.1.el7.x86_64.img
}
menuentry 'CentOS Linux (0-rescue-5f1865c966684867b0a1a7aec6828fb7) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-0-rescue-5f1865c966684867b0a1a7aec6828fb7-advanced-6e2cc491-a60
3-4ec9-9677-65d425c720e2' {
        load_video
        insmod gzio
        insmod part_msdos
        insmod xfs
        set root='hd0,msdos1'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 --hint='hd0,msdos1'  faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3
        else
          search --no-floppy --fs-uuid --set=root faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3
        fi
        linux16 /vmlinuz-0-rescue-5f1865c966684867b0a1a7aec6828fb7 root=/dev/mapper/VG01-lv_root ro crashkernel=auto rd.lvm.lv=VG01/lv_root rd.lvm.lv=VG01/lv_swap rhgb biosdevname=0 net.ifnames=0  quiet
        initrd16 /initramfs-0-rescue-5f1865c966684867b0a1a7aec6828fb7.img
}

搞定之后,reboot 重启(注意这个地方千万别搞错,否则启动不了就直接原地暴毙了)。

4. 重启后格式化 VG01-lv_root

如果能够正常重启,最难的这一关就已经渡过,先 lsblk 确认下挂载情况:

# lsblk
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                 8:0    0   50G  0 disk 
├─sda1              8:1    0  200M  0 part 
└─sda2              8:2    0 49.8G  0 part 
  ├─VG01-lv_swap  253:0    0    8G  0 lvm  /
  └─VG01-lv_root  253:1    0 41.8G  0 lvm  
sdb                 8:16   0    1G  0 disk 
└─data01vg-appslv 253:2    0  896M  0 lvm  /apps
sr0                11:0    1 1024M  0 rom  

可以看到根目录已经挂载到只有 8G 的 VG01-lv_swap 上了,现在 VG01-lv_root 已经腾挪出来了。

然后格式化:

mkfs.xfs -f -n ftype=1 /dev/mapper/VG01-lv_root

临时挂载到 /mnt:

mkdir -p /mnt  # 如果原来没有这个目录就创建一下
mount /dev/mapper/VG01-lv_root /mnt

5. 再挪一次系统盘数据

rsync -avxHAX --delete --devices --specials --exclude /mnt --exclude /apps --exclude /boot / /mnt/

挪完看一下是不是数据都在了,进入下一步。

6. 把启动条件改回去

修改 /mnt/etc/fstab,把根目录的挂载设备从 lv_swap 改回 lv_root

#
# /etc/fstab
# Created by anaconda on Wed Feb 15 08:41:10 2017
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/VG01-lv_root /                       xfs     defaults        0 0
UUID=faa7906a-6ff7-4d7b-bf5e-5ecc4ac54fb3 /boot                   xfs     defaults        0 0
#/dev/mapper/VG01-lv_swap swap                    swap    defaults        0 0
/dev/mapper/data01vg-appslv /apps ext4 defaults 0 0

修改启动配置,修改 /boot/grub2/grub.cfg,直接把原来备份那个挪回去就好。

中间出了点小插曲,发现 /boot 里面东西不见了,对照一下其他没改过的主机,是挂在在 /dev/sda1 上面的,因此临时挂载一下就 OK (mount /dev/sda1 /boot),不影响。

重启,大功告成。

结果验证

搞完之后,看一下挂载情况:

# lsblk
NAME              MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
sda                 8:0    0   50G  0 disk 
├─sda1              8:1    0  200M  0 part /boot
└─sda2              8:2    0 49.8G  0 part 
  ├─VG01-lv_root  253:0    0 41.8G  0 lvm  /
  └─VG01-lv_swap  253:1    0    8G  0 lvm  
sdb                 8:16   0    1G  0 disk 
└─data01vg-appslv 253:2    0  896M  0 lvm  /apps
sr0                11:0    1 1024M  0 rom  

众神归位,好,再看一下磁盘格式:

# xfs_info /
meta-data=/dev/mapper/VG01-lv_root isize=512    agcount=4, agsize=2736896 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0 spinodes=0
data     =                       bsize=4096   blocks=10947584, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal               bsize=4096   blocks=5345, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

看到 ftype=1 了,说明系统盘格式已经转换成功。

再看看 CRI 接口的支持(如果还是不支持 overlay 的话就会报开始那个错误):

# crictl images
IMAGE               TAG                 IMAGE ID            SIZE

没有报错就是已经好了。

不确定通过 rsync 同步数据的方式是否会造成某些问题,但是目前来看一切正常,如果大家参考这个实践的话可能实际的硬盘挂载情况会有很大的不同,这里主要参考思路,切勿照搬,操作风险也比较大,请谨慎操作。