2025-10-05

文字表达能力

今天看到这个 帖子 ,很有感触。现在很多工作为了快,都是先用 AI 打个底稿,再人工优化。久而久之写作水平就下降了。 此前一直想要多写点东西,但总是拘泥于形式不敢下手。现在看来再不下手就晚了,敢在自己写作水平下降之前多写点东西。无所谓什么,写就是了。

探索手机发送公众号的工作流

先用最简单的方式跑通,比如今天这个。

优秀的 SubReddit 清单

SubReddit 清单

  • SaaS 类
    • r/B2BSaaS(B2B SaaS 人群集中,~8k 成员)
    • r/NoCodeSaaS(无代码 SaaS,~22k 成员)
    • r/micro_saas(微型 SaaS,~10k 成员)
    • r/indiebiz(小体量&个人生意,~24k 成员)
    • r/SaaS(讨论/反馈/支持,~95k 成员)
    • r/startup_resources(工具/模版/手册,~24k 成员)
    • r/LaunchMyStartup(早期发布与反馈,~2k 成员)
    • r/ProductHunters(PH 发现与讨论,~23k 成员)
    • r/saasapps(平台/生态应用,成员数小)
    • r/BootstrappedSaaS(自筹/自启动,~935 成员)
  • 创业类
    • r/startups(最大创业社区,~1.2M 成员)
    • r/SideProject(副业/项目展示,~478k 成员)
    • r/indiehackers(独立开发者,~24k 成员)
    • r/EntrepreneurRideAlong(创业旅程分享,~517k 成员)
    • r/thesidehustle(点子与建议,~79k 成员)
    • r/growmybusiness(增长与扩张,~51k 成员)
    • r/startup(创业创建与增长,~300k 成员)
    • r/startups_promotion(集中自荐区,~3.1k 成员)
    • r/roastmystartup(犀利真反馈,成员数小)
    • r/sweatystartup(轻松创业讨论,成员数小)
    • r/SmallBusiness(日常经营与建议,成员数大)
    • r/Entrepreneur(泛创业与商业,成员数大)
  • 营销类
    • r/SaaSMarketing(从策略到落地,~11k 成员)
    • r/ProductMarketing(定位/叙事/布,~18k 成员)
    • r/MarketingHelp(众包建议与诊断,~16k 成员)
    • r/ecommerce_growth(电商增长(可迁移),~9k 成员)
    • r/email(送达率/MarTech,~11k 成员)
    • r/EmailOutreach(冷邮件技巧,~1k 成员)
    • r/AskGrowth(增长问答,数百成员)
    • r/Marketing(综合营销,成员数大)
    • r/AskMarketing(营销问答,成员数中)
    • r/SocialMedia(社媒策略,成员数大)
    • r/CopyWriting(文案/转化,成员数中)
    • r/Advertising(投放策略,成员数中)
    • r/WebMarketing(SEO/内容,成员数大)
    • r/EmailMarketing(邮件营销,成员数中)
    • r/Sales(销售与漏斗,成员数大)
    • r/PlugYourProduct(直推(先看规则),成员数小)
    • r/GrowthHacking(增长与病毒,成员数中)
  • 技术类
    • r/NextGenAITool(AI 工具展示,~4k 成员)
    • r/webdev(Web 开发,~1.4M 成员)
    • r/JAMstack_dev(落地页与性能,~2k 成员)
    • r/pocketbase(快速后端,~3k 成员)
    • r/SQLServer(多租户/性能,~58k 成员)
    • r/lowcode(流程自动化,~3k 成员)
    • r/DesignCritiques(设计求评,~85k 成员)
    • r/InternetIsBeautiful(创意展示,~16.2M 成员)
  • 测试类
    • r/TestMyApp(早期用户与反馈,~6k 成员)
    • r/AlphaandBetausers(寻找测试者,~10k 成员)
  • 支付类
    • r/stripe(订阅/计费,~19k 成员)
    • r/PaymentProcessing(网关/拒付,~5k 成员)
  • 运营类
    • r/FPandA(预算/收入模型,~53k 成员)
    • r/CustomerService(流程与工具,~39k 成员)
    • r/CustomerValue(价值定价/CS 对齐,成员数小)
    • r/CustomerSuccessHub(CS 社区,成员数小)
  • 融资类
    • r/VentureCapital(基金/募资洞见,成员数大)
    • r/Crowdfunding(众筹策略,成员数中)
    • r/Kickstarter(众筹项目,成员数大)
    • r/startupinvesting(早期投资,~1k 成员)
    • r/SaaSidea(点子分享,成员数小)
    • r/AISaaSHunter(AI SaaS 展示,成员数小)
    • r/LLMO_SaaS(LLM SEO 分发,数百成员)

Reddit增长地图

Refs

整理了一个 AI 提示词库

大模型的能力越来越强,但是如何发挥大模型真正的实力?

这是我一直在思考的问题,在平常的使用过程中很容易发现,提示词的使用技巧对于生成结果的质量至关重要。多看看优秀的提示词,还能启发我们利用大模型的更多用法。

在网上看到许多优秀的提示词仓库,但一般都是针对某个模型或场景的,一直想整理一个更方便易读,根据模态、模型分类,从各处搜集优秀的提示词汇集到一处的仓库。

于是最近制作了这个网站:https://prmbr.com/

一个精选提示词库,包括 Text-To-Text, Text-To-Image, Text-To-Video 等提示词用法,还有针对 Nano Banana GPT-Image-1 等模型的专门分区,内容全部来自于各大开源的 Prompt 仓库、社交媒体、网站,以及我在各种地方偶然看到的内容。

初期内容还比较有限,后期逐渐完善,分享给大家。

代码仓库已经开源,可以参与共建:https://github.com/songtianlun/awesome-prompts

让 LLM 看到真实世界的 Playwright MCP

Playwright MCP 是一个模型上下文协议(MCP)服务器,使用 Playwright 提供浏览器自动化功能。该服务器使 LLM 能够通过结构化的可访问性快照与网页交互,从而绕过对屏幕截图或视觉调整模型的需求。

使用方法

以 codex 为例,创建或编辑配置文件 ~/.codex/config.toml 并添加:

[mcp_servers.playwright]
command = "npx"
args = ["@playwright/mcp@latest"]

References

磁盘占用分析利器 ncdu

现在使用 ncdu 的话,只需要执行一次就可以查询目录大小并排序,且删除文件也很方便,不会出错。

基本用法

安装:

# ubuntu
sudo apt install ncdu
# centos
sudo yum install ncdu
# macOS
brew install ncdu

使用

# 统计当前所在目录及子目录的文件占用情况
ncdu

# 统计指定的 /data 目录
ncdu /data

# 将 /data 目录的情况输出到 ~/ncdu.txt
ncdu /data -o ~/ncdu.txt

# 加载本地根据,而不是进行实时统计
ncdu -f ~/ncdu.txt 

References

自建 gitlab 徽标问题导致项目 500 问题解决

问题

最近内部 gitlab 某些项目打开就 500 了, 看 gitlab 报错日志如下:

gitlab  | {"method":"GET","path":"/xxx/xxx","format":"html","controller":"ProjectsController","action":"show","status":500,"time":"2025-08-28T00:51:41.511Z","params":[{"key":"namespace_id","value":"xxx"},{"key":"id","value":"xxx"}],"remote_ip":"10.17.7.63","user_id":74,"username":"xxxgitlab | {"method":"GET","path":"/xxx/xxx","format":"html","controller":"ProjectsController","action":"show","status":500,"time":"2025-08-28T00:51:41.511Z","params":[{"key":"namespace_id","value":"xxx"},{"key":"id","value":"xxx"}],"remote_ip":"10.17.7.63","user_id":74,"username":"xxx","ua":"Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0","correlation_id":"01K3Q2F5DXP8Y33B25TW9SSMYM","meta.user":"xxx","meta.project":"xxx/xxx","meta.root_namespace":"storage","meta.caller_id":"ProjectsController#show","meta.remote_ip":"10.17.7.63","meta.feature_category":"projects","meta.client_id":"user/74","redis_calls":21,"redis_duration_s":0.007123,"redis_read_bytes":2997,"redis_write_bytes":2370,"redis_cache_calls":20,"redis_cache_duration_s":0.006591,"redis_cache_read_bytes":2816,"redis_cache_write_bytes":1035,"redis_shared_state_calls":1,"redis_shared_state_duration_s":0.000532,"redis_shared_state_read_bytes":181,"redis_shared_state_write_bytes":1335,"db_count":41,"db_write_count":0,"db_cached_count":10,"cpu_s":2.291446,"mem_objects":394125,"mem_bytes":52397272,"mem_mallocs":198648,"mem_total_bytes":68162272,"queue_duration_s":0.009214,"exception.class":"Rack::Timeout::RequestTimeoutException","exception.message":"Request ran for longer than 60000ms","exception.backtrace":["lib/gitlab/url_blocker.rb:113:in `getaddrinfo'","lib/gitlab/url_blocker.rb:113:in `get_address_info'","lib/gitlab/url_blocker.rb:48:in `validate!'","app/validators/addressable_url_validator.rb:83:in `validate_each'","app/models/badge.rb:43:in `build_rendered_url'","app/models/badge.rb:36:in `rendered_image_url'","app/models/badges/project_badge.rb:15:in `rendered_image_url'","app/views/projects/_home_panel.html.haml:93","app/views/projects/_home_panel.html.haml:89","app/views/projects/_home_panel.html.haml:87","app/views/projects/show.html.haml:14","app/controllers/application_controller.rb:128:in `render'","app/controllers/application_controller.rb:538:in `block in allow_gitaly_ref_name_caching'","lib/gitlab/gitaly_client.rb:341:in `allow_ref_name_caching'","app/controllers/application_controller.rb:537:in `allow_gitaly_ref_name_caching'","app/controllers/application_controller.rb:487:in `set_current_admin'","lib/gitlab/session.rb:11:in `with_session'","app/controllers/application_controller.rb:478:in `set_session_storage'","lib/gitlab/i18n.rb:99:in `with_locale'","lib/gitlab/i18n.rb:105:in `with_user_locale'","app/controllers/application_controller.rb:472:in `set_locale'","app/controllers/application_controller.rb:466:in `set_current_context'","lib/gitlab/metrics/elasticsearch_rack_middleware.rb:16:in `call'","lib/gitlab/middleware/rails_queue_duration.rb:33:in `call'","lib/gitlab/metrics/rack_middleware.rb:16:in `block in call'","lib/gitlab/metrics/web_transaction.rb:21:in `run'","lib/gitlab/metrics/rack_middleware.rb:16:in `call'","lib/gitlab/middleware/speedscope.rb:13:in `call'","lib/gitlab/request_profiler/middleware.rb:17:in `call'","lib/gitlab/jira/middleware.rb:19:in `call'","lib/gitlab/middleware/go.rb:20:in `call'","lib/gitlab/etag_caching/middleware.rb:21:in `call'","lib/gitlab/middleware/multipart.rb:172:in `call'","lib/gitlab/middleware/read_only/controller.rb:50:in `call'","lib/gitlab/middleware/read_only.rb:18:in `call'","lib/gitlab/middleware/same_site_cookies.rb:27:in `call'","lib/gitlab/middleware/handle_malformed_strings.rb:21:in `call'","lib/gitlab/middleware/basic_health_check.rb:25:in `call'","lib/gitlab/middleware/handle_ip_spoof_attack_error.rb:25:in `call'","lib/gitlab/middleware/request_context.rb:21:in `call'","config/initializers/fix_local_cache_middleware.rb:11:in `call'","lib/gitlab/middleware/rack_multipart_tempfile_factory.rb:19:in `call'","lib/gitlab/metrics/requests_rack_middleware.rb:74:in `call'","lib/gitlab/middleware/release_env.rb:12:in `call'"],"db_duration_s":0.23847,"view_duration_s":0.0,"duration_s":73.05203}","ua":"Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0","correlation_id":"01K3Q2F5DXP8Y33B25TW9SSMYM","meta.user":"xxx","meta.project":"xxx/xxx","meta.root_namespace":"storage","meta.caller_id":"ProjectsController#show","meta.remote_ip":"10.17.7.63","meta.feature_category":"projects","meta.client_id":"user/74","redis_calls":21,"redis_duration_s":0.007123,"redis_read_bytes":2997,"redis_write_bytes":2370,"redis_cache_calls":20,"redis_cache_duration_s":0.006591,"redis_cache_read_bytes":2816,"redis_cache_write_bytes":1035,"redis_shared_state_calls":1,"redis_shared_state_duration_s":0.000532,"redis_shared_state_read_bytes":181,"redis_shared_state_write_bytes":1335,"db_count":41,"db_write_count":0,"db_cached_count":10,"cpu_s":2.291446,"mem_objects":394125,"mem_bytes":52397272,"mem_mallocs":198648,"mem_total_bytes":68162272,"queue_duration_s":0.009214,"exception.class":"Rack::Timeout::RequestTimeoutException","exception.message":"Request ran for longer than 60000ms","exception.backtrace":["lib/gitlab/url_blocker.rb:113:in `getaddrinfo'","lib/gitlab/url_blocker.rb:113:in `get_address_info'","lib/gitlab/url_blocker.rb:48:in `validate!'","app/validators/addressable_url_validator.rb:83:in `validate_each'","app/models/badge.rb:43:in `build_rendered_url'","app/models/badge.rb:36:in `rendered_image_url'","app/models/badges/project_badge.rb:15:in `rendered_image_url'","app/views/projects/_home_panel.html.haml:93","app/views/projects/_home_panel.html.haml:89","app/views/projects/_home_panel.html.haml:87","app/views/projects/show.html.haml:14","app/controllers/application_controller.rb:128:in `render'","app/controllers/application_controller.rb:538:in `block in allow_gitaly_ref_name_caching'","lib/gitlab/gitaly_client.rb:341:in `allow_ref_name_caching'","app/controllers/application_controller.rb:537:in `allow_gitaly_ref_name_caching'","app/controllers/application_controller.rb:487:in `set_current_admin'","lib/gitlab/session.rb:11:in `with_session'","app/controllers/application_controller.rb:478:in `set_session_storage'","lib/gitlab/i18n.rb:99:in `with_locale'","lib/gitlab/i18n.rb:105:in `with_user_locale'","app/controllers/application_controller.rb:472:in `set_locale'","app/controllers/application_controller.rb:466:in `set_current_context'","lib/gitlab/metrics/elasticsearch_rack_middleware.rb:16:in `call'","lib/gitlab/middleware/rails_queue_duration.rb:33:in `call'","lib/gitlab/metrics/rack_middleware.rb:16:in `block in call'","lib/gitlab/metrics/web_transaction.rb:21:in `run'","lib/gitlab/metrics/rack_middleware.rb:16:in `call'","lib/gitlab/middleware/speedscope.rb:13:in `call'","lib/gitlab/request_profiler/middleware.rb:17:in `call'","lib/gitlab/jira/middleware.rb:19:in `call'","lib/gitlab/middleware/go.rb:20:in `call'","lib/gitlab/etag_caching/middleware.rb:21:in `call'","lib/gitlab/middleware/multipart.rb:172:in `call'","lib/gitlab/middleware/read_only/controller.rb:50:in `call'","lib/gitlab/middleware/read_only.rb:18:in `call'","lib/gitlab/middleware/same_site_cookies.rb:27:in `call'","lib/gitlab/middleware/handle_malformed_strings.rb:21:in `call'","lib/gitlab/middleware/basic_health_check.rb:25:in `call'","lib/gitlab/middleware/handle_ip_spoof_attack_error.rb:25:in `call'","lib/gitlab/middleware/request_context.rb:21:in `call'","config/initializers/fix_local_cache_middleware.rb:11:in `call'","lib/gitlab/middleware/rack_multipart_tempfile_factory.rb:19:in `call'","lib/gitlab/metrics/requests_rack_middleware.rb:74:in `call'","lib/gitlab/middleware/release_env.rb:12:in `call'"],"db_duration_s":0.23847,"view_duration_s":0.0,"duration_s":73.05203}

问题分析

经过 claude 的分析,发现是 badge 加载失败导致的

以下是完整回答:

根据这个 GitLab 日志,我可以看到问题的核心原因和解决方案:

问题分析

从日志中可以看到关键错误信息:

  1. 超时错误: Rack::Timeout::RequestTimeoutException (Request ran for longer than 60000ms) – 请求超过了60秒限制
  2. 根本原因: 错误发生在项目徽章(badge)渲染过程中,具体是在 lib/gitlab/url_blocker.rb:113:in 'getaddrinfo'
  3. 调用链: 项目页面渲染 → 徽章URL验证 → DNS解析超时

可能的原因

  1. 网络连通性问题: GitLab 尝试验证项目徽章的外部URL时,DNS解析或网络连接超时
  2. 徽章配置问题: 该项目配置了无效或无法访问的徽章URL
  3. 防火墙/代理问题: 网络策略阻止了对外部徽章服务的访问

解决方案

1. 临时解决方案 – 禁用徽章验证

方案无效。

2. 根本解决方案 – 检查项目徽章配置

  1. 通过管理员界面检查
    • 进入该项目的设置页面
    • 查看 “General” → “Badges” 部分
    • 删除或修复无效的徽章URL
  2. 通过数据库直接检查

    按照这个步骤成功解决。

    
    # 进入GitLab控制台
    gitlab-rails console

查找该项目的徽章

project = Project.find_by_full_path(‘xxx/xxx’) project.badges.each { |badge| puts “#{badge.id}: #{badge.link_url} | #{badge.image_url}” }

删除有问题的徽章

Badge.where(id: [有问题的徽章ID]).delete_all


### 3. 网络诊断

检查 GitLab 服务器的网络连通性:

```bash
# 测试DNS解析
nslookup [徽章URL的域名]

# 测试网络连通性
curl -I [完整的徽章URL]

4. 增加调试信息

临时启用详细日志来定位具体的URL:

# 在 /etc/gitlab/gitlab.rb 中
gitlab_rails['log_level'] = 'debug'

建议先尝试解决方案1作为紧急修复,然后通过解决方案2找到并修复具体的徽章配置问题。这样既能快速恢复服务,又能从根本上解决问题。

结论

没想到一个 badge 会导致这么大问题,可能因为版本比较低,不知道新版本有没有解决这个问题。

harbor Chartmuseum helm 图表缓存刷新

harbor 集成 Chartmuseum 实现 helm 图表的存储。

Chartmuseum 对于图表索引缓存的处理有问题,删除、重传 chart 不会刷新索引,需要手动刷新。

TL;DR

操作步骤如下:

  1. 停止 harbor,例如: docker compose stop
  2. 删除 /data/chart_storage/{project}/index-cache.yaml
  3. 删除 /data/redis/*
  4. 启动 harbor,例如: docker compose up -d

记得备份。

Refs

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