标签归档:linux

Debian 11 + PVE LACP Mac 冲突问题调查与解决方案

By TL.S

问题背景

在Debian 11 + Proxmox VE环境中,当配置LACP(Link Aggregation Control Protocol)绑定接口时,有一个环境遇到一个令人困惑的问题:相同硬件配置的多台服务器上,所有服务器的同名bond接口都被分配了相同的MAC地址。这种情况会导致严重的网络连接问题,特别是在集群环境中。

根本原因分析

经过深入调查,这个问题的根本原因可以归纳为以下几个方面:

1. systemd/udev MAC 地址生成策略变更

systemd 242 版本开始,引入了新的MACAddressPolicy=persistent策略。该策略的目标是为虚拟网络设备(如bond、bridge、vlan等)生成持久的MAC地址,避免重启后MAC地址变化。

核心变更:systemd通过哈希算法,基于机器的machine-id和接口名称来生成MAC地址,确保在同一台机器上重启后MAC地址保持不变。

Refs: https://github.com/systemd/systemd/pull/11382

2. machine-id 相同导致的问题

关键问题:当多台服务器具有相同的machine-id时,相同的接口名称会生成完全相同的MAC地址。

machine-id 相同的几种情况:

  • 克隆安装:从同一个镜像克隆安装的系统
  • 模板部署:使用相同模板部署的虚拟机或物理机
  • 其他原因:其他原因导致 /etc/machine-id文件内容重复

3. MAC地址分配类型

通过查看系统文件可以确认MAC地址的分配方式:

root@server:~# cat /sys/class/net/bond0/addr_assign_type
3

根据Linux内核文档,addr_assign_type=3表示”set using dev_set_mac_address”,即通过dev_set_mac_address函数设置的MAC地址。

4. 虚拟设备的特殊性

问题主要影响虚拟网络设备,因为这些设备缺少必要的ID_NET_NAME_*属性,udev 无法为它们分配稳定的MAC地址。systemd 的解决方案是使用 接口名称+machine-id 的组合来生成MAC地址。

技术原理深度解析

MAC地址生成算法

systemd使用以下算法生成MAC地址:

  1. 获取/etc/machine-id的内容
  2. 结合接口名称(如bond0、vmbr0等)
  3. 通过哈希算法计算出唯一的MAC地址
  4. 确保生成的MAC地址符合本地管理地址规范

解决方案

方案一:重置machine-id(推荐)

这是最根本的解决方案,适用于所有情况:

# 停止网络服务
systemctl stop networking

# 备份原machine-id(可选)
cp /etc/machine-id /etc/machine-id.backup

# 清空machine-id
echo -n > /etc/machine-id

# 重新生成machine-id
systemd-machine-id-setup

# 验证新的machine-id
cat /etc/machine-id

# 重启系统使配置生效
reboot

注意事项

  • 重置machine-id可能影响其他依赖machine-id的服务
  • 建议在维护窗口期间执行
  • 重启后所有虚拟网络接口的MAC地址都会更新

方案二:手动指定MAC地址

如果不想更改machine-id,可以在网络配置中手动指定MAC地址:

# 编辑网络配置文件
vim /etc/network/interfaces

# 为bond接口指定唯一的MAC地址
auto bond0
iface bond0 inet manual
    bond-slaves ens1f0 ens1f1
    bond-miimon 100
    bond-mode 802.3ad
    bond-xmit-hash-policy layer3+4
    hwaddress ether 02:01:02:03:04:05  # 手动指定MAC地址

# 为bridge接口指定唯一的MAC地址
auto vmbr0
iface vmbr0 inet static
    address 10.10.4.41/24
    gateway 10.10.4.1
    bridge-ports bond0
    bridge-stp off
    bridge-fd 0
    hwaddress ether 02:01:02:03:04:06  # 手动指定MAC地址

方案三:更改MAC地址策略

修改systemd的MAC地址策略来解决MAC冲突问题。首先了解当前策略和可选项:

当前默认策略查看

# 查看当前systemd网络配置
ls -la /etc/systemd/network/
ls -la /lib/systemd/network/

# 查看当前默认策略文件(如果存在)
cat /lib/systemd/network/99-default.link 2>/dev/null || echo "默认策略文件不存在"

# 查看当前接口的MAC策略
networkctl status bond0

MAC地址策略选项说明

systemd支持以下MACAddressPolicy选项:

策略 说明 适用场景
persistent 当前默认策略,基于machine-id和接口名生成稳定MAC 需要MAC地址在重启后保持不变
random 每次启动时生成随机MAC地址 不需要MAC地址稳定性
none 不设置MAC地址,使用驱动程序默认值 物理接口或需要保持原有MAC

当前问题的根源:默认的persistent策略在相同machine-id的服务器上会生成相同的MAC地址。

实施步骤

# 1. 检查是否已有配置文件
echo "检查现有配置..."
if [ -f /etc/systemd/network/99-default.link ]; then
    echo "发现现有配置文件:"
    cat /etc/systemd/network/99-default.link
    echo ""
    echo "是否要备份现有配置?(y/n)"
    read -r response
    if [ "$response" = "y" ]; then
        cp /etc/systemd/network/99-default.link /etc/systemd/network/99-default.link.backup.$(date +%Y%m%d_%H%M%S)
        echo "已备份到: /etc/systemd/network/99-default.link.backup.$(date +%Y%m%d_%H%M%S)"
    fi
else
    echo "未发现现有配置文件,将创建新配置"
fi

# 2. 创建网络配置目录(如果不存在)
mkdir -p /etc/systemd/network

# 3. 根据需求选择策略创建配置文件

# 选项A:使用random策略(推荐用于解决冲突)
cat > /etc/systemd/network/99-default.link << EOF
# MAC地址策略配置
# 创建时间: $(date)
# 目的: 解决LACP MAC地址冲突问题

[Match]
OriginalName=*

[Link]
MACAddressPolicy=random
NamePolicy=keep kernel database onboard slot path
EOF

# 选项B:仅对虚拟接口使用random策略
cat > /etc/systemd/network/99-virtual.link << EOF
# 虚拟接口MAC地址策略配置
# 创建时间: $(date)

[Match]
Kind=bond bridge vlan macvlan veth

[Link]
MACAddressPolicy=random
EOF

# 选项C:禁用MAC地址自动分配
cat > /etc/systemd/network/99-no-mac.link << EOF
# 禁用MAC地址自动分配
# 创建时间: $(date)

[Match]
OriginalName=*

[Link]
MACAddressPolicy=none
EOF

# 4. 验证配置文件语法
echo "验证配置文件..."
systemd-analyze verify /etc/systemd/network/99-default.link

# 5. 应用新策略
echo "应用新的MAC地址策略..."
# 重新加载systemd配置
systemctl daemon-reload

# 重启网络服务(根据系统使用的网络管理器)
if systemctl is-active --quiet systemd-networkd; then
    systemctl restart systemd-networkd
    echo "已重启systemd-networkd"
elif systemctl is-active --quiet networking; then
    systemctl restart networking
    echo "已重启networking服务"
else
    echo "警告:未检测到活跃的网络服务,可能需要手动重启网络或系统"
fi

# 6. 验证策略是否生效
echo "等待网络接口重新初始化..."
sleep 5

echo "验证新的MAC地址策略:"
networkctl status | grep -E "(bond|vmbr|bridge)"

策略选择建议

推荐策略组合

  1. 对于生产环境

    # 仅对虚拟接口使用random策略,物理接口保持默认
    [Match]
    Kind=bond bridge vlan
    
    [Link]
    MACAddressPolicy=random
  2. 对于测试环境

    # 所有接口使用random策略
    [Match]
    OriginalName=*
    
    [Link]
    MACAddressPolicy=random
  3. 对于特定接口

    # 只对特定名称的接口应用
    [Match]
    OriginalName=bond* vmbr*
    
    [Link]
    MACAddressPolicy=random

回滚方案

如果新策略导致问题,可以快速回滚:

# 删除自定义策略文件
rm -f /etc/systemd/network/99-default.link

# 或者恢复备份
if [ -f /etc/systemd/network/99-default.link.backup.* ]; then
    cp /etc/systemd/network/99-default.link.backup.* /etc/systemd/network/99-default.link
fi

# 重启网络服务
systemctl daemon-reload
systemctl restart systemd-networkd

注意事项

  • 影响范围:此方案会影响系统中所有匹配的网络接口
  • 重启要求:配置更改后,需要重启网络服务或重启系统
  • 兼容性:确保你的系统使用systemd-networkd而不是NetworkManager
  • 监控:更改后应监控网络连接是否正常

方案四:运行时动态修改

如果需要立即解决问题而不重启:

# 生成随机MAC地址(注意要符合本地管理地址规范)
NEW_MAC=$(printf '02:00:00:%02x:%02x:%02x\n' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))

# 停止接口
ip link set dev bond0 down

# 设置新的MAC地址
ip link set dev bond0 address $NEW_MAC

# 启动接口
ip link set dev bond0 up

# 验证更改
ip link show bond0

预防措施

1. 标准化部署流程

在部署新系统时,确保:

# 在系统部署脚本中加入
if [ -f /etc/machine-id ]; then
    echo "重置machine-id..."
    echo -n > /etc/machine-id
    systemd-machine-id-setup
fi

2. 监控和检查

定期检查集群中的MAC地址冲突:

#!/bin/bash
# mac_check.sh - 检查MAC地址冲突

echo "检查集群MAC地址冲突..."
for host in server1 server2 server3; do
    echo "=== $host ==="
    ssh $host "ip link show | grep -E 'bond|vmbr' | grep 'link/ether'"
done

3. 文档化和记录

建立标准的网络配置文档,记录:

  • 每台服务器的machine-id
  • 手动分配的MAC地址范围
  • 网络拓扑和接口映射

影响范围和版本信息

受影响版本

  • Debian 10/11 + systemd >= 242
  • Proxmox VE 6.x/7.x/8.x
  • 其他基于systemd的Linux发行版

特别注意

  • Intel网卡比Broadcom网卡更容易出现此问题
  • 虚拟化环境中的问题更加常见
  • 集群环境影响更严重

总结

Debian 11 + PVE LACP Mac冲突问题的根本原因是systemd的MAC地址生成策略与machine-id机制的相互作用。当多台服务器具有相同的machine-id时,会为相同名称的虚拟网络接口生成相同的MAC地址,导致网络冲突。

References

Linux 自签名 CA 证书安装方法

在 Linux 中运行 docker, containerd, helm 等应用时需要信任自签署证书保护的内部仓库服务,此时需要注入自签名 CA 证书,以 Ubunut 为例。在 Ubuntu 系统中,CA 证书信任主要存储在以下目录:

主要目录:

  • /etc/ssl/certs/ – 系统级 CA 证书目录
  • /usr/share/ca-certificates/ – 预安装的 CA 证书包

关键文件:

  • /etc/ssl/certs/ca-certificates.crt – 合并的 CA 证书文件
  • /etc/ca-certificates.conf – CA 证书配置文件

管理方式:

  • 添加新的 CA 证书:将 .crt 文件放入 /usr/local/share/ca-certificates/ 目录
  • 更新证书:运行 sudo update-ca-certificates 命令

具体操作示例:

# 添加自定义 CA 证书
sudo cp your-ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates

# 查看当前信任的 CA 证书
ls /etc/ssl/certs/

系统会自动将 /usr/local/share/ca-certificates//usr/share/ca-certificates/ 中的证书合并到 /etc/ssl/certs/ca-certificates.crt 文件中,这个文件被大多数应用程序用作默认的 CA 证书束。

Linux 进程绑定NUMA节点或CPU核心

对于CPU和NUMA架构的介绍本文不再做叙述,感兴趣的可自行查看:Linux–CPU简述Linux–内存管理浅谈

进程绑定NUMA节点或cpu核心的意义

NUMA 架构将内存和cpu分散在不同的 NUMA 节点上,每个节点都有自己的本地内存和cpu处理器,将进程绑定到特定的 NUMA 节点或cpu上,可以让进程直接访问本地内存和CPU,减少访问远程节点开销,提高访问速度,从而提高程序性能

注:不可多进程绑定同一个节点或cpu,这样反而会使该节点产生资源竞争,降低性能。

查看NUMA架构的信息

[root@test ~]# yum -y install numactl
[root@test ~]# numactl -H
available: 2 nodes (0-1)    #有两个NUMA节点,编号0,1
node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30    #节点0包含的cpu核心编号
node 0 size: 65442 MB       #节点0共有65442MB的内存容量
node 0 free: 5901 MB        #节点0当前有5091MB的内存空闲
node 1 cpus: 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31
node 1 size: 65536 MB
node 1 free: 5133 MB
node distances:            #不同node之间发访问距离(跳数),如[0,0] 表示node 0的本地内存访问距离为10。跳数(hops):表示从一个节点到达另一个节点所需的内存访问步骤数,并不是一个精确的时间或距离单位,而是一种相对指标。
node   0   1 
  0:  10  21 
  1:  21  10 
[root@test ~]#
[root@test ~]# lscpu
...
#系统的NUMA节点和 CPU 核心编号的映射
NUMA node0 CPU(s):     0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30
NUMA node1 CPU(s):     1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31
...
[root@test ~]#
[root@test ~]# yum -y install hwloc
[root@test ~]# lstopo-no-graphics 
Machine (128GB total)       #总共有128GB内存
  NUMANode L#0 (P#0 64GB)    #节点 L#0 有64GB内存
    Package L#0 + L3 L#0 (20MB)
      L2 L#0 (256KB) + L1d L#0 (32KB) + L1i L#0 (32KB) + Core L#0
        PU L#0 (P#0)
        PU L#1 (P#16)
      L2 L#1 (256KB) + L1d L#1 (32KB) + L1i L#1 (32KB) + Core L#1
        PU L#2 (P#2)
        PU L#3 (P#18)
      L2 L#2 (256KB) + L1d L#2 (32KB) + L1i L#2 (32KB) + Core L#2
        PU L#4 (P#4)
        PU L#5 (P#20)
      L2 L#3 (256KB) + L1d L#3 (32KB) + L1i L#3 (32KB) + Core L#3
        PU L#6 (P#6)
        PU L#7 (P#22)
      L2 L#4 (256KB) + L1d L#4 (32KB) + L1i L#4 (32KB) + Core L#4
        PU L#8 (P#8)
        PU L#9 (P#24)
      L2 L#5 (256KB) + L1d L#5 (32KB) + L1i L#5 (32KB) + Core L#5
        PU L#10 (P#10)
        PU L#11 (P#26)
      L2 L#6 (256KB) + L1d L#6 (32KB) + L1i L#6 (32KB) + Core L#6
        PU L#12 (P#12)
        PU L#13 (P#28)
      L2 L#7 (256KB) + L1d L#7 (32KB) + L1i L#7 (32KB) + Core L#7
        PU L#14 (P#14)
        PU L#15 (P#30)
    HostBridge L#0
      PCIBridge
        PCI 1000:005d
          Block L#0 "sda"
      PCIBridge
        2 x { PCI 8086:154d }
      PCIBridge
        4 x { PCI 8086:1521 }
      PCI 8086:8d62
      PCIBridge
        PCIBridge
          PCIBridge
            PCIBridge
              PCI 102b:0534
                GPU L#1 "card0"
                GPU L#2 "controlD64"
      PCI 8086:8d02
  NUMANode L#1 (P#1 64GB) + Package L#1 + L3 L#1 (20MB)
    L2 L#8 (256KB) + L1d L#8 (32KB) + L1i L#8 (32KB) + Core L#8
      PU L#16 (P#1)
      PU L#17 (P#17)
    L2 L#9 (256KB) + L1d L#9 (32KB) + L1i L#9 (32KB) + Core L#9
      PU L#18 (P#3)
      PU L#19 (P#19)
    L2 L#10 (256KB) + L1d L#10 (32KB) + L1i L#10 (32KB) + Core L#10
      PU L#20 (P#5)
      PU L#21 (P#21)
    L2 L#11 (256KB) + L1d L#11 (32KB) + L1i L#11 (32KB) + Core L#11
      PU L#22 (P#7)
      PU L#23 (P#23)
    L2 L#12 (256KB) + L1d L#12 (32KB) + L1i L#12 (32KB) + Core L#12
      PU L#24 (P#9)
      PU L#25 (P#25)
    L2 L#13 (256KB) + L1d L#13 (32KB) + L1i L#13 (32KB) + Core L#13
      PU L#26 (P#11)
      PU L#27 (P#27)
    L2 L#14 (256KB) + L1d L#14 (32KB) + L1i L#14 (32KB) + Core L#14
      PU L#28 (P#13)
      PU L#29 (P#29)
    L2 L#15 (256KB) + L1d L#15 (32KB) + L1i L#15 (32KB) + Core L#15
      PU L#30 (P#15)
      PU L#31 (P#31)
[root@test ~]#

绑定

注:下述绑定方法都属于临时绑定,进程重启后失效;要永久生效,请在应用程序中设置绑定或配置文件里提前配置。

绑定NUMA节点

#运行前执行,将进程运行的cpu跟内存绑定到节点0上
numactl --cpunodebind=0 --membind=0 程序名

#接启动命令
numactl --cpunodebind=0 --membind=0 /usr/sbin/nginx

查看进程运行内存信息

[root@test ~]# numastat -p 223736

Per-node process memory usage (in MBs) for PID 223736 (nginx)
                           Node 0          Node 1           Total
                  --------------- --------------- ---------------
Huge                         0.00            0.00            0.00  #使用的大页内存
Heap                         0.94            0.00            0.94  #堆内存
Stack                        0.02            0.00            0.02  #栈内存
Private                      1.17            0.07            1.24  #其他使用内存
----------------  --------------- --------------- ---------------
Total                        2.13            0.07            2.21

查看进程绑定cpu核心

[root@test ~]# taskset -c -p 223736
pid 223736's current affinity list: 0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30

查看进程的 PID、所运行的 CPU 核心编号以及命令名称

[root@test ~]# ps -o pid,psr,comm -p 223736
   PID PSR COMMAND
223736  22 nginx

绑定cpu核心

#绑定运行在6号cpu上
taskset -c 6 /usr/sbin/nginx

#绑定运行在0-6号cpu上
taskset -c 0,6 /usr/sbin/nginx#绑定到NUMA架构1号节点上numactl --cpubind=1 /usr/sbin/nginxnumactl --cpunodebind=1 /usr/sbin/nginx

查看进程运行在哪个cpu上

[root@test ~]# taskset -c -p 219877
pid 219877's current affinity list: 6

[root@test ~]# ps -o pid,psr,comm -p 219877
   PID PSR COMMAND
219877   6 nginx

References

ArchLinux pacman 一键找到最快的镜像源清单

curl -s "https://archlinux.org/mirrorlist/?country=CN&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 -

运行这个命令,即可自动从 archlinux 官方 mirror 清单获取中国 (CN) 的镜像清单,并调用 rankmirrors 测速得到速度最快的前5个。

配置到 /etc/pacman.d/mirrorlist 目录中即可使用。

References

VirtualBox VERR_NO_LOW_MEMORY 解决

Archlinux 下内存有很多,但 VB 报错内存不足 VM 无法启动,free 可以看到内存大部分被 buffer 占用。

free -h
              total        used        free      shared  buff/cache   available  
内存:          30Gi        13Gi        1Gi       2.3Gi       18.7Gi        17Gi  
交换:          31Gi       1.7Gi        30Gi

论坛找到一种强制驱逐 buffer 占用的方法:

echo 3 > /proc/sys/vm/drop_caches

执行后可正常启动。

References

Linux buffer-cache 占用过高性能调整

什么是 buff/cache?

Linux 2.4 的内存管理中,buffer 指 Linux 内存的:Buffer cachecache 指 Linux 内存中的:Page cache。一般呢,是这么解释两者的。

  • A buffer is someting that has yet to be ‘written’ to disk.
  • A cache is someting that has been ‘read’ from the disk and stored for later use.

翻译过来就是说:

  1. buffer (buff) 是用来缓存尚未 “写入” 磁盘的内容。
  2. cache 是用来缓存从磁盘 “读取” 出来的东西。

所以 buffer 被用来当成对 io 设备写的缓存。而 cache 被用来当作对 io 设备的读缓存。这里的 io 设备,主要指的是块设备文件和文件系统上的普通文件。

但是在 Linux 2.6 以后,它们的意义不一样了。

Linux 2.6 之后 Linux 将他们统一合并到了 Page cache 作为文件层的缓存。而 buffer 则被用作 block 层的缓存。
block 层的缓存是什么意思呢,你可以认为一个 buffer 是一个 physical disk block 在内存的代表,用来将内存中的 pages 映射为 disk blocks,这部分被使用的内存被叫做 buffer

buffer 里面的 pages,指的是 Page cache 中的 pages,所以,buffer 也可以被认为 Page cache 的一部分。

或者简单来说,buffer 负责裸设备相关的缓存,cache 负责文件系统的缓存。

Buffer 的具体职责

在当前的系统实现里,buffer 主要是设计用来在系统对块设备进行读写时作为缓存来使用。这意味着对块的操作会使用 buffer 进行缓存,比如我们在格式化文件系统的时候。

但是一般情况下两个缓存系统是一起配合使用的,比如当我们对一个文件进行写操作的时候,cache 的内容会被改变,而 buffer 则用来将 cachepage 标记为不同的缓冲区,并记录是哪一个缓冲区被修改了。

这样,内核在后续执行脏数据的回写(writeback)时,就不用将整个 page 写回,而只需要写回修改的部分即可。

Cache 的具体职责

cache 主要用来作为文件系统上的文件数据的缓存来用,当进程对文件有 read/write 操作的时候。包括将文件映射到内存的系统调用 mmap,就会用到 cache

因为 cache 被作为文件类型的缓存来用,所以事实上也负责了大部分的块设备文件的缓存工作。

怎么回收 buff/cache?

Linux 内核会在内存将要耗尽的时候,自动触发内存回收的工作,以便释放出内存给急需内存的进程使用。

但是这种回收的工作也并不是没有成本。

理解 cache 是干什么的就知道,cache 中存在着一部分 write 操作的数据。所以必须保证 cache 中的数据跟对应文件中的数据一致,才能对 cache 进行释放。

于是伴随着 cache 清除的行为的,一般都是系统 IO 飙高。这是因为内核要将 cache 中缓存的 write 数据进行回写。

我们可以使用下面这个文件来人工触发缓存清除的操作,Linux 提供了三种清空方式:

  1. echo 1 > /proc/sys/vm/drop_caches # 仅清除页面缓存
  2. echo 2 > /proc/sys/vm/drop_caches # 清除目录项和 inode
  3. echo 3 > /proc/sys/vm/drop_caches # 清除页面缓存、目录项以及 inode

但是这种放时只能在执行的当时起作用,过一段时间之后又会发现内存被占满,怎么办呢?

实际上内核提供了 vm.vfs_cache_pressure 参数用来控制缓冲区的回收频率,我们可以调整它。

这个参数是用来控制内核回收 VFS 缓存的频率。修改这个值会提高或者降低回收 VFS 缓存的频率。值可以设置为 0-200 中的任意值。越大回收频率越快,可以把 vm.vfs_cache_pressure 赋值为 200 来获得最快的回收频率。这个值默认值一般为 100

另外也可以使用 slabtop 分析内存使用情况。一般情况下,dentry*_inode_cache 值越高回收的效果越好。

为什么是 dentry*_inode_cache 呢,这是因为当读写文件时内核会为该文件对象建立一个 dentry,并将其缓存起来,方便下一次读写时直接从内存中取出提高效率。

一些措施

内核参数

可以通过配置 vm.vfs_cache_pressure 参数,控制系统回收 directory entries 和 inode 缓存的倾向程度

Linux Kernel 文档描述如下:

此百分比值控制内核回收用于缓存 directoryinode 对象的内存的趋势。 在默认值 vfs_cache_pressure=100 时,内核将尝试以 pagecacheswapcache 回收的“公平”速率回收 dentryinode。减小 vfs_cache_pressure 会导致内核更倾向于保留 dentryinode 缓存。当 vfs_cache_pressure=0 时,由于内存压力,内核永远不会回收 dentryinode,这很容易导致内存不足的情况。将 vfs_cache_pressure 增加到 100 以上会导致内核更喜欢回收 dentryinode。 将 vfs_cache_pressure 显著增加到 100 以上可能会对性能产生负面影响。回收代码需要使用各种锁来查找可释放的目录和 inode 对象。当 vfs_cache_pressure=1000 时,它将查找比可用对象多 10 倍的可用对象。

可尝试将该值调整为 200 使得比默认值更积极地回收缓存,要持久化配置 vm.vfs_cache_pressure 为 200你可以通过以下几种方法:

  1. 通过 /etc/sysctl.conf 文件(推荐方法):
# 使用文本编辑器打开 /etc/sysctl.conf
sudo vim /etc/sysctl.conf

# 添加或修改以下行
vm.vfs_cache_pressure = 200

# 使配置生效
sudo sysctl -p
  1. 通过创建 /etc/sysctl.d/ 目录下的配置文件:
# 创建新的配置文件
sudo vim /etc/sysctl.d/99-vfs-cache-pressure.conf

# 添加以下行
vm.vfs_cache_pressure = 200

# 使配置生效
sudo sysctl --system

手动清理

$ sync
$ echo 1 > /proc/sys/vm/drop_caches
$ echo 2 > /proc/sys/vm/drop_caches
$ echo 3 > /proc/sys/vm/drop_caches

自动定时清理

不推荐这么做

1、创建脚本cleanCache.sh

#!/bin/bash#每两小时清除一次缓存
echo "开始清除缓存"
sync;sync;sync #写入硬盘,防止数据丢失
sleep 10#延迟10秒
echo 1 > /proc/sys/vm/drop_caches
echo 2 > /proc/sys/vm/drop_caches
echo 3 > /proc/sys/vm/drop_caches

2、创建定时任务

crontab -e #弹出配置文件

3、添加定时任务执行频率

#分  时  日  月  周  命令
0 */2 * * * /usr/local/bin/cleanCache.sh

4、设置crond启动以及开机自启

systemctl start crond.service
systemctl enable crond.service

5、查看定时任务是否被执行

cat /var/log/cron | grep cleanCache

References

配置 harbor 及 docker 等使用 https

默认情况下,Harbor不提供证书。可以在没有安全性的情况下部署Harbor,这样您就可以通过HTTP连接到它。但是,只有在没有连接到外部internet的空间隙测试或开发环境中才可以使用HTTP。在没有空间隙的环境中使用HTTP会暴露给中间人攻击。在生产环境中,始终使用HTTPS。如果启用带公证人的内容信任对所有images进行正确签名,则必须使用HTTPS。

要配置HTTPS,必须创建SSL证书。您可以使用由受信任的第三方CA签名的证书,也可以使用自签名证书。本节介绍如何使用OpenSSL创建CA,以及如何使用CA签署服务器证书和客户端证书。您可以使用其他CA提供程序,例如:Let’s Encrypt。

下面的过程假设您的Harbor注册表的主机名是 yourdomain.com,并且它的DNS记录指向运行Harbor的主机。

生成证书颁发机构的证书

在生产环境中,应该从CA获取证书。在测试或开发环境中,可以生成自己的CA。若要生成CA证书,请运行以下命令。

生成CA证书私钥。

openssl genrsa -out ca.key 4096

生成CA证书。 

调整 -subj选项中的值以反映您的组织。如果使用 FQDN 连接Harbor主机,则必须将其指定为 common name(CN)属性。

公用名(Common Name)一般来讲就是填写你将要申请SSL证书的域名 (domain)或子域名(sub domain)。

例1:打算为“chinassl.net”申请SSL证 书,那这个公用名(Common Name)就要填写“chinassl.net”,而不能填写 “www.chinassl.net”,因为在申请SSL证书时发证机构认为“www.yourdomain.com”和 “yourdomain.com”是不同的两个域名;

例2:如将要为bill.chinassl.net申请SSL证书,那么这里公用名(Common Name)就 要填写“bill.chinassl.net”而不能填写“chinassl.net”或“www.chinassl.net”
openssl req -x509 -new -nodes -sha512 -days 3650 \
 -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=yourdomain.com" \
 -key ca.key \
 -out ca.crt

生成服务器证书

证书通常包含.crt文件和.key文件,例如yourdomain.com.crt和yourdomain.com.key。

1、生成私钥。

openssl genrsa -out yourdomain.com.key 4096

 2、生成证书签名请求(CSR)。

调整-subj选项中的值以反映您的组织。如果使用FQDN连接Harbor主机,则必须将其指定为common name(CN)属性,并在key和CSR文件名中使用它。

openssl req -sha512 -new \
    -subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=yourdomain.com" \
    -key yourdomain.com.key \
    -out yourdomain.com.csr

 3、生成 x509 v3 扩展文件。

无论您是使用 FQDN 还是使用IP地址连接到您的 Harbor 主机,都必须创建此文件,以便您可以为 Harbor 主机生成符合使用者替代名称(SAN)和 x509 v3 扩展要求的证书。替换DNS条目以反映您的域。

cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1=yourdomain.com
DNS.2=yourdomain
DNS.3=hostname
IP.1 = 192.168.25.8
EOF

 4、使用 v3.ext 文件为您的港口主机生成证书。

CRSCRT 文件名中的 yourdomain.com 替换为 Harbor 主机名。

openssl x509 -req -sha512 -days 3650 \
    -extfile v3.ext \
    -CA ca.crt -CAkey ca.key -CAcreateserial \
    -in yourdomain.com.csr \
    -out yourdomain.com.crt

向 Harbor 和 Docker 提供证书

生成 ca.crtyourdomain.com.crtyourdomain.com.key 文件后,必须将它们提供给Harbor和Docker,并重新配置Harbor以使用它们。

1、将服务器证书和密钥复制到Harbor主机上的 certcificates 文件夹中。

cp yourdomain.com.crt /data/cert/
cp yourdomain.com.key /data/cert/

 2、将 yourdomain.com.crt 转换为 yourdomain.com.cert ,供Docker使用。

Docker守护进程将 .crt 文件解释为 CA 证书,.cert文件解释为客户端证书。

openssl x509 -inform PEM -in yourdomain.com.crt -out yourdomain.com.cert

 3、将服务器证书、密钥和CA文件复制到港口主机上的Docker certificates文件夹中。必须先创建适当的文件夹。

cp yourdomain.com.cert /etc/docker/certs.d/yourdomain.com/
cp yourdomain.com.key /etc/docker/certs.d/yourdomain.com/
cp ca.crt /etc/docker/certs.d/yourdomain.com/

 如果将默认nginx端口443映射到其他端口,请创建文件夹 /etc/docker/certs.d/yourdomain.com:port/etc/docker/certs.d/harbor_IP:port

4、重新启动Docker引擎。

systemctl restart docker

您可能还需要在操作系统级别信任证书。有关详细信息,请参阅harbor安装疑难解答

下面的示例演示了使用自定义证书的配置。

/etc/docker/certs.d/
    └── yourdomain.com:port
       ├── yourdomain.com.cert  <-- Server certificate signed by CA
       ├── yourdomain.com.key   <-- Server key signed by CA
       └── ca.crt               <-- Certificate authority that signed the registry certificate

部署或重新配置harbor

如果尚未部署Harbor,请参阅配置Harbor YML文件,以获取有关如何通过在Harbor.YML中指定主机名和https属性来配置Harbor以使用证书的信息。

如果您已经使用HTTP部署了Harbor并希望将其重新配置为使用HTTPS,请执行以下步骤。

1、运行prepare脚本以启用HTTPS。

Harbor使用nginx实例作为所有服务的反向代理。使用prepare脚本将nginx配置为使用HTTPS。prepare 位于Harbor安装包中,与 install.sh 脚本处于同一级别。

./prepare

2、如果Harbor正在运行,请停止并删除现有实例。

images数据保留在文件系统中,因此不会丢失任何数据。

docker-compose down -v

3、Restart Harbor:

docker-compose up -d

验证HTTPS连接

在为Harbor设置HTTPS之后,您可以通过执行以下步骤来验证HTTPS连接。

1、打开浏览器并输入 https://yourdomain.com。它应该显示 harbor 界面。

某些浏览器可能会显示一条警告,指出证书颁发机构(CA)未知。使用非来自可信第三方 CA 的自签名 CA 时会发生这种情况。您可以将 CA 导入浏览器以删除警告。

2、在运行Docker守护进程的计算机上,检查 `文件,确保没有为https://yourdomain.com设置-unsecure-registry` 选项。

3、从Docker客户端登录到Harbor。

docker login yourdomain.com

 如果您已经将nginx 443端口映射到另一个端口,请在login命令中添加该端口。

docker login yourdomain.com:port

其他工具接入

Docker

cp yourdomain.com.cert /etc/docker/certs.d/yourdomain.com/
cp yourdomain.com.key /etc/docker/certs.d/yourdomain.com/
cp ca.crt /etc/docker/certs.d/yourdomain.com/

cp x.x.x.x:xxx.cert /etc/docker/certs.d/x.x.x.x:xxx/
cp x.x.x.x:xxx.key /etc/docker/certs.d/x.x.x.x:xxx/
cp ca.crt /etc/docker/certs.d/x.x.x.x:xxx/

Containerd

cp yourdomain.com.cert /etc/containerd/certs.d/yourdomain.com/
cp yourdomain.com.key /etc/containerd/certs.d/yourdomain.com/
cp ca.crt /etc/containerd/certs.d/yourdomain.com/

cp x.x.x.x:xxx.cert /etc/containerd/certs.d/x.x.x.x:xxx/
cp x.x.x.x:xxx.key /etc/containerd/certs.d/x.x.x.x:xxx/
cp ca.crt /etc/containerd/certs.d/x.x.x.x:xxx/

# example
mkdir -p /etc/containerd/certs.d/192.168.25.8:10443
cd /etc/containerd/certs.d/192.168.25.8:10443
wget http://192.168.25.9/raw/general/wz_harbor_ssl/192.168.25.8%3A10443.cert
wget http://192.168.25.9/raw/general/wz_harbor_ssl/192.168.25.8%3A10443.key
wget http://192.168.25.9/raw/general/wz_harbor_ssl/ca.crt
systemctl restart containerd.servic

skopeo

直接支持 /etc/docker/certs.d/ 目录下的证书。

helm

References

收藏一个上古软件,在 Linux 终端上使用行编辑器 ed

这个看似简单的编辑器为用户提供了许多易于学习和使用的命令。 这款产生自资源极其有限时期的产物,似乎还很有助于理解 vi/vimemacs 的一些设计。

GNU ed 命令是一个行编辑器。它被认为是标准的 Unix 文本编辑器,因为它是首个出现在 Unix 的文本编辑器,并且它曾经无处不在,你在任何一个 POSIX 系统中都能找到它(通常来说,你现在也可以)。在某种程度上,你可以很容易看出来它是第一个文本编辑器,因为它在许多方面的功能都十分基础。和其他大多数的文本编辑器不同,它不会打开一个属于自己的窗口或显示区域,事实上,在默认情况下,它甚至不会提示用户输入文字。从另一个方面来说,它在交互功能上的缺失也可以成为一个优点。它是一个多功能的编辑器,你可以用简短的命令控制它,无论是在交互式的命令行中,还是在编写的 shell 脚本里。

安装 ed

如果你正在使用 Linux 或者 BSD 的话,你很可能已经默认安装了 ed(在 Linux 上是 GNU 版 ed,而在 BSD 上是 BSD 版 ed)。但是,一些极简的环境可能没有包括 ed,这也没关系,你的发行版的软件仓库中很可能有 ed 可供下载。macOS 默认安装了 BSD 版 ed

# archlinux
$ sudo pacman -S ed

启动 ed

当你启动 ed 的时候,你的终端提示符不见了,看起来好像是 ed 停止运行了。其实它没有,它只是在等待你输入指令而已。

$ ed

为使 ed 显示更详细的信息,你可以输入命令 p 让它返回一个提示符:

$ ed
p
?

这个问号(?)是默认的 ed 提示符。

缓冲区

当 ed 激活时,你其实是在和一个叫 缓冲区buffer 的东西打交道。缓冲区是内存中的一块区域。你并不会直接编辑文件,而是在编辑它对应的缓冲区。当你退出 ed 却没有把修改保存到磁盘的文件上时,所有的修改都会丢失,因为它们只在缓冲区里存在。(这对于一个已经习惯了初始的 草图缓冲区scratch buffer 的资深 Emacs 用户可能很耳熟。)

使用 ed 输入文本

启动 ed 后,你处于命令模式。这意味着你可以向编辑器发送指令,比如让它显示一个提示符,而不是空白区域。你可以使用 a 命令开始附加文本到当前的缓冲区,使用一个实心的点 . 来终止输入。比如,下面的这个例子往缓冲区里附加了两行文字(“hello world” 和 “hello ed”):

?
a
hello world
hello ed
.

使用点 . 终止输入后,你将回到命令模式。

查看缓冲区

怎样查看当前缓冲区里都有什么呢?你可以输入想要查看的行号,也可以使用 ,p 命令来显示所有的行:

?
1
hello world
2
hello ed
,p
hello world
hello ed

写入文件

如果你现在对文本很满意,你可以使用 w 命令把缓冲区写入到文件中,后面跟上目标文件名:

?
w example.txt
19

写操作后显示的那个数字代表着写入到文件中的字符数。

读取文件

除了使用 ed 来读取文本,你也可以使用 r 命令把一个已经存在的文件加载到到缓冲区里:

?
r myfile.txt

另外,你也可以在启动 ed 时,在它后面加上你想要加载到缓冲区里的文件名:

$ ed myfile.txt

编辑缓冲区

鉴于 ed 是一个文本编辑器,你当然可以使用一种特殊的语法来编辑缓冲区里的文本。使用 sed 或 vim 的用户或许会觉得这个语法很熟悉。假设现在缓冲区里已经加载了一个文件:

$ ed myfile.txt
,p
This is an example document.
There is some text, but not much.
There is some errors, but not much.

如果你要把第一句话中的 document 修改为 file,你可以先选择目标行(1),然后使用 s 命令调用搜索函数,后面跟着搜索文本和替换文本:

?
1
This is an example document.
s/document/file/
1
This is an example file.

如果你要编辑其他行,步骤也是一样的,只需提供一个不同的行号即可:

?
3
There is some errors, but not much.
s/is/are/
s/much/many/

你可以使用 ,p 命令来看到你对缓冲区的历史编辑记录:

This is an example file.
There is some text, but not much.
There are some errors, but not many.

当然,这些修改只存在于缓冲区里。你如果在 ed 编辑器外查看这个文件,你只会看到原始的文本:

$ cat myfile.txt
This is an example document.
There is some text, but not much.
There is some errors, but not much.

如果你要把这些修改保存回文件中,使用 w 命令即可:

w myfile.txt
258

清空缓冲区

如果想要得到一个新的缓冲区,以此来打开一个新的文件,或者把一个新的文件加载到不同的环境中,你可以使用 c 命令。使用这个清空缓冲区后,什么也不会输出,因为缓冲已经是空的了:

c
,p

退出

如果要退出当前的 ed 会话,你可以使用 q 命令。它并不会给你一个保存缓冲区的机会,所以你要确保自己在这之前执行了保存操作。

尝试一下 ed 吧

ed 还可以做到很多事情,学习 ed 可以让你知道它和部分的 vim 是如何工作的。我并没有尝试使用 ed 来写这篇文章,老实说,我也不认为它是通常意义上的最佳文本编辑器。但是,ed 仍然是一个出色的编辑器。通过阅读它的文档,你可以很轻松地学会它。在 GNU 系统上,你可以使用 info ed 来查看它的操作手册。

References