最终使用 Linux 自己搭建 NAS 系统

2025 年 5 月 16 日
 lucheng0127

需求说明

心路历程

最开始纠结是 PVE 还是直接跑 Linux ,但是想了一下都 all in one 了干脆直接 Linux 省的套娃。

最后整体思路:

硬件说明

小黄鱼淘了一台 4 网口的 AMD 主板,16G 内存,CPU 型号忘了,看了一下是 es 的,无所谓了

root@shawn-aio:~# grep name /proc/cpuinfo | cut -f2 -d: | uniq -c
      4  AMD Eng Sample
root@shawn-aio:~# free -h
               total        used        free      shared  buff/cache   available
Mem:            14Gi       1.9Gi       9.8Gi       200Mi       3.4Gi        12Gi
Swap:          4.0Gi          0B       4.0Gi

网络结构

eno1 做 wan 口,通过 iptables nat 表做 masquerade 给内部提供外网访问能力。wan 口只开放 vpn 端口。

iptables -t nat -A POSTROUTING -o eno1 -j MASQUERADE

root@shawn-aio:~# iptables -S INPUT
-P INPUT DROP
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -i br-mgmt -j ACCEPT
-A INPUT -i eno1 -p udp -m udp --dport 51820 -j ACCEPT
-A INPUT -i wg0 -j ACCEPT
-A INPUT -i lxcbr0 -j ACCEPT
-A INPUT -i br-0ad574e582c7 -j ACCEPT
-A INPUT -i nerdctl0 -j ACCEPT

创建 br-mgmt ,接入 enp3s0 ,enp4s0 ,enp5s0 做 lan 口,同时通过 veth 将 netns gateway 接到 br-mgmt ,并在 netns 中提供 dhcp ,下发默认路由和 dns 地址。

root@shawn-aio:~# ip a show br-mgmt
6: br-mgmt: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether ea:a4:3d:6c:ce:d0 brd ff:ff:ff:ff:ff:ff
    inet 172.20.0.254/24 brd 172.20.0.255 scope global br-mgmt
       valid_lft forever preferred_lft forever
    inet6 fe80::e8a4:3dff:fe6c:ced0/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
root@shawn-aio:~# ip netns exec gateway ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host proto kernel_lo
       valid_lft forever preferred_lft forever
12: gTb@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 52:ef:a4:d0:0c:e9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.20.0.1/24 scope global gTb
       valid_lft forever preferred_lft forever
    inet6 fe80::50ef:a4ff:fed0:ce9/64 scope link proto kernel_ll
       valid_lft forever preferred_lft forever
root@shawn-aio:~# ip l show master br-mgmt
3: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel master br-mgmt state UP mode DEFAULT group default qlen 1000
    link/ether 20:76:93:5b:14:4f brd ff:ff:ff:ff:ff:ff
4: enp4s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel master br-mgmt state DOWN mode DEFAULT group default qlen 1000
    link/ether 20:76:93:5b:14:50 brd ff:ff:ff:ff:ff:ff
5: enp5s0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel master br-mgmt state DOWN mode DEFAULT group default qlen 1000
    link/ether 20:76:93:5b:14:51 brd ff:ff:ff:ff:ff:ff
13: bTg@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-mgmt state UP mode DEFAULT group default qlen 1000
    link/ether aa:36:50:e4:db:81 brd ff:ff:ff:ff:ff:ff link-netns gateway
15: veth18XBnR@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-mgmt state UP mode DEFAULT group default qlen 1000
    link/ether fe:e5:47:3f:90:3b brd ff:ff:ff:ff:ff:ff link-netnsid 1
16: vethdxnWc4@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-mgmt state UP mode DEFAULT group default qlen 1000
    link/ether fe:c1:4c:94:06:ba brd ff:ff:ff:ff:ff:ff link-netnsid 2

服务详情

nginx 反向代理

root@shawn-aio:~# systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Fri 2025-05-09 08:38:57 CST; 1 week 0 days ago
 Invocation: a6039cd62e984eb0bd649511a52f5060
       Docs: man:nginx(8)
   Main PID: 6945 (nginx)
      Tasks: 5 (limit: 17733)
     Memory: 8.6M (peak: 13.6M)
        CPU: 1.111s
     CGroup: /system.slice/nginx.service
             ├─6945 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
             ├─6946 "nginx: worker process"
             ├─6947 "nginx: worker process"
             ├─6948 "nginx: worker process"
             └─6949 "nginx: worker process"

May 09 08:38:57 shawn-aio systemd[1]: Starting nginx.service - A high performance web server and a reverse proxy server...
May 09 08:38:57 shawn-aio systemd[1]: Started nginx.service - A high performance web server and a reverse proxy server.

软路由和透明代理

自己写了个脚本,创建了一个 netns 后,在 netns 中跑 dnsmasq 提供 dhcp 服务。里面跑 clash ,并设置 iptables 规则来实现透明代理。

root@shawn-aio:~# systemctl status gateway
● gateway.service - Gateway service enable tproxy
     Loaded: loaded (/etc/systemd/system/gateway.service; enabled; preset: enabled)
     Active: active (running) since Fri 2025-05-09 08:38:57 CST; 1 week 0 days ago
 Invocation: 76718511daa5473bad8931fedd4d54c9
   Main PID: 6873 (gateway.sh)
      Tasks: 3 (limit: 17733)
     Memory: 5.1M (peak: 6.8M)
        CPU: 1d 22h 47min 56.956s
     CGroup: /system.slice/gateway.service
             ├─6873 /usr/bin/bash /opt/gateway/gateway.sh start
             ├─6973 /usr/sbin/dnsmasq -C /opt/gateway/dnsmasq.conf
             └─6974 bash /opt/gateway/tproxy.sh start

May 15 08:25:45 shawn-aio dnsmasq-dhcp[6973]: DHCPREQUEST(gTb) 172.20.0.165 c8:a3:62:57:df:69
May 15 08:25:45 shawn-aio dnsmasq-dhcp[6973]: DHCPACK(gTb) 172.20.0.165 c8:a3:62:57:df:69 luchengdeMBP
May 15 13:53:13 shawn-aio dnsmasq-dhcp[6973]: DHCPREQUEST(gTb) 172.20.0.165 c8:a3:62:57:df:69
May 15 13:53:13 shawn-aio dnsmasq-dhcp[6973]: DHCPACK(gTb) 172.20.0.165 c8:a3:62:57:df:69 luchengdeMBP
May 15 19:10:29 shawn-aio dnsmasq-dhcp[6973]: DHCPREQUEST(gTb) 172.20.0.165 c8:a3:62:57:df:69
May 15 19:10:29 shawn-aio dnsmasq-dhcp[6973]: DHCPACK(gTb) 172.20.0.165 c8:a3:62:57:df:69 luchengdeMBP
May 16 00:33:54 shawn-aio dnsmasq-dhcp[6973]: DHCPREQUEST(gTb) 172.20.0.165 c8:a3:62:57:df:69
May 16 00:33:54 shawn-aio dnsmasq-dhcp[6973]: DHCPACK(gTb) 172.20.0.165 c8:a3:62:57:df:69 luchengdeMBP
May 16 06:08:01 shawn-aio dnsmasq-dhcp[6973]: DHCPREQUEST(gTb) 172.20.0.165 c8:a3:62:57:df:69
May 16 06:08:01 shawn-aio dnsmasq-dhcp[6973]: DHCPACK(gTb) 172.20.0.165 c8:a3:62:57:df:69 luchengdeMBP
root@shawn-aio:~# ip netns
gateway (id: 0)

VPN 服务

使用场景很简单,感觉没必要用 tailscale 或者 netmaker 。同时自己不太喜欢界面,所以自己模仿 wg-easy 写了个 wg 服务来提供 VPN 服务。客户端直接鉴权是模仿 k8s apiserver 和 kubectl 通过 tls 证书鉴权。

root@shawn-aio:~# wgctl subnet list
UUID                                  Name     Address       Public Key
e4212eda-233f-11f0-8903-2076935b144e  default  10.67.0.1/24  <pub key>
root@shawn-aio:~# wgctl peer list -s e4212eda-233f-11f0-8903-2076935b144e
UUID                                  User     Address       Public Key                                    Enable
f9615f20-233f-11f0-8903-2076935b144e  lucheng  10.67.0.2/24  <pub key>  true
root@shawn-aio:~# wg
interface: wg0
  public key: <server pubkey>
  private key: (hidden)
  listening port: 51820

peer: <peer pubkey>
  endpoint: 10.28.66.18:36303
  allowed ips: 10.67.0.2/32
  latest handshake: 29 minutes, 23 seconds ago
  transfer: 5.14 MiB received, 35.12 MiB sent

影音功能

lxc 跑 ubuntu 在里面装 jellyfin

root@shawn-aio:~# lxc-ls -f
NAME     STATE   AUTOSTART GROUPS IPV4       IPV6 UNPRIVILEGED
jellyfin RUNNING 1         -      172.20.0.2 -    false
ubuntu   RUNNING 1         -      172.20.0.3 -    false

照片备份

root@shawn-aio:~# nerdctl ps
CONTAINER ID    IMAGE                                                COMMAND                   CREATED       STATUS    PORTS                     NAMES
05a100ce77d8    quay.io/shawnlu0127/immich/immich-server:v1.131.1    "tini -- /bin/bash s…"    6 days ago    Up        0.0.0.0:2283->2283/tcp    immich_server
414297ba5e5f    quay.io/shawnlu0127/immich/postgres:pg14-v0.2.0      "docker-entrypoint.s…"    6 days ago    Up                                  immich_postgres
6fa52ce3d9e1    quay.io/shawnlu0127/immich/redis:6.2-alpine          "docker-entrypoint.s…"    6 days ago    Up                                  immich_redis
9523 次点击
所在节点    NAS
64 条回复
lucheng0127
2025 年 5 月 16 日
@kid1412621 是的,只是因为当时 docker 跑的时候没法通过 bridge 固定 ip ,所以才 lxc 跑的
LindsayZhou
2025 年 5 月 17 日
@kid1412621 #2
软路由 + docker ?
您怕不是没吃过那坨 iptables 的亏。
ragnaroks
2025 年 5 月 18 日
我也是直接上 debian ,从 debian 10 一直用到现在,还装了个 i3wm 当 GUI
lucheng0127
2025 年 5 月 18 日
@ragnaroks i3wm 好评 O(∩_∩)O
ragnaroks
2025 年 5 月 18 日
@lucheng0127 就它占用最小了,和 cockpit 那一坨相近
kid1412621
2025 年 5 月 18 日
@LindsayZhou #42 和软路由啥关系,我用 fedora 也遇到了
kid1412621
2025 年 5 月 18 日
@LindsayZhou #42 话说 docker 直接支持 nftables 的 issue 放了几年了…
LindsayZhou
2025 年 5 月 18 日
@kid1412621 #46 软路由上要做 nat ,要做透明代理,再跑 docker ,规则不会乱吗?

挺担心容器间的网络互通隔离的规则会失效。

再说如果需要改条啥规则,还要单独重启一下 docker 让它再把容器的规则加上
kid1412621
2025 年 5 月 18 日
@LindsayZhou #48 没在软路上放太多东西,主要跑 docker 的放到 Linux 上
zyq2280539
2025 年 5 月 18 日
一直在用 ubuntu server 做服务器系统,软路由感觉最好单独放在一个硬件上,要不然很容易出问题
wolonggl
2025 年 5 月 19 日
@LindsayZhou 软路由上跑 docker 会引起一些莫名其妙的问题,比如 ipv6 lan 的设备无法上网,每次都反复折腾
SakuraYuki
2025 年 5 月 19 日
@lucheng0127 #41 可以用 macvlan 实现静态 ip 吧
SakuraYuki
2025 年 5 月 19 日
@gunner168 #4 刮削都是基于 tmdb 数据的,无非就是飞牛内置了个大模型来识别文件名,这些都能手搓出来
lucheng0127
2025 年 5 月 19 日
@SakuraYuki macvlan 虽然虚拟化更轻量,但是子接口和父接口无法直接通信,用着稍微有点不方便
lucheng0127
2025 年 5 月 19 日
@zyq2280539 其实在 netns 里面还好,性能和独享硬件差不多😂
然后网络环境也是隔离的 嘿嘿
SakuraYuki
2025 年 5 月 19 日
@lucheng0127 #54 我是搞了个 macvlan 网桥来让独立 ip 的容器和其他容器通信的,然后开机脚本自启,不过这样缺点就是要占掉 2 个 ip
zhzy
2025 年 5 月 19 日
@allplay 太真实了, 不过我是手搓了一个破产版的 samba UI, 反正就我一个人用, 感觉也还凑合, omv 复杂度还是有点高
zhzy
2025 年 5 月 19 日
@neilxu 我前几天刷到 immich 感觉跟 mtphoto 挺像的了, 而且 UI 感觉细节精致一点
molezznet
2025 年 5 月 19 日
armbian 或 pve + lxc
Senorsen
2025 年 5 月 19 日
跟我 10 年前( 2014-2017 )上大学时的方案简直一模一样呀,我是用 Ubuntu Linux 宿主机挂载机械硬盘当 NAS 存储,里边一个 lxc 写一堆 iptables 脚本+dnsmasq 规则+各种 VPN+ ss-redir 和 iptables-geoip 条件转发等等作为软路由。不过后来发现,OpenWrt 配置各种防火墙区域、规则、VPN 、静态路由、翻墙更省心,ipv6 在不同区域特殊的 input/forward 规则也不需要自己操心;群晖这种成品 NAS 系统图形界面配置各种共享、用户、配额、RAID 真省心…( ps 只对我自己而言,因为我是懒人,脚本堆多了很乱懒得收拾)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/1132178

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX