ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用systemd实现FRP客户端开机自启与崩溃自动恢复

使用systemd实现FRP客户端开机自启与崩溃自动恢复 折腾过 FRP 的人应该都有同感隧道配好、服务端也打通了结果机器一重启所有内网穿透立马失效。家里的 NAS、办公室的机器、云上的开发板全部失联只能老老实实再 SSH 进去敲一遍./frpc -c frpc.toml。一次两次能忍次数多了就烦了。这事儿其实有标准解法就是把 FRP 客户端交给 systemd 托管做成开机自启的服务。Linux 下几乎所有主流发行版CentOS、Ubuntu、Debian包括现在常见的国产 Linux 发行版都在用 systemd它天然就是干这个的管理进程、设置开机启动、崩溃自动拉起、统一看日志。这篇文章我会把完整的配置过程、服务单元文件每一行的含义、常见的坑都拆开讲清楚照着做就能实现“开机即连、断了自愈”的效果。1. 为什么要把 FRP 客户端交给 systemd 托管1.1 开机自启要解决的核心痛点FRP 的客户端本质上就是一个长时间运行的命令行程序它在本地建立加密通道把内网服务映射到公网服务器上。问题在于这个进程太“脆弱”了机器重启它不会自己跑起来进程意外退出也没人管网络闪断后虽然 FRP 自身有心跳重连但整个进程崩掉的话一切都白搭。手动启动的痛点非常集中。第一是重启之后必须人工介入NAS 在家里、服务器在机房人不在现场就干瞪眼。第二是没有守护机制frpc 进程因为内存压力被系统杀掉、或者因为配置触发 panic不会自动再拉起来隧道就彻底断了。第三是日志没人管前台运行时终端关了日志就没了进程崩溃前的报错信息很难追溯。所以要解决的本质问题是三件事开机自动拉起、崩溃自动恢复、统一日志管理。systemd 恰好完整覆盖了这三个需求这也是它成为几乎所有现代 Linux 发行版默认服务管理器的原因。1.2 systemd、rc.local、crontab 三种自启方案对比网上教程里有不少“野路子”最常见的是改/etc/rc.local或者在 crontab 里写reboot命令甚至有人建议在.bashrc里塞启动命令。这些方法都能“让进程跑起来”但都解决不了守护和日志的问题。方案开机自启崩溃拉起日志管理依赖控制综合评价systemd 服务支持支持支持 journald支持官方推荐功能完整rc.local支持不支持需自己重定向不支持能用但太粗糙crontab reboot支持不支持需自己重定向不支持环境变量容易出问题手动 nohup不支持不支持需自己重定向不支持只适合临时调试这里要特别说明一下 crontab 的坑。cron 在执行 reboot 任务时环境变量极度精简PATH都未必包含/usr/local/bin如果脚本里用到相对路径很容易出现“开机命令执行了但进程根本没起来”的诡异现象。rc.local 虽然环境比 cron 好一点但同样没有“进程挂掉自动拉起”的能力而且在新版 systemd 体系里 rc.local 本身也是被包装成服务的等于多绕了一圈。Windows 用户可能习惯了写 PowerShell 脚本放到启动目录Linux 对应的标准做法就是 systemd 服务。这套体系成熟、可追溯、可管理systemctl status一条命令就能看到进程状态和最近日志没有任何理由舍近求远去用那些旁门左道。2. 动手前把 FRP 客户端环境理顺2.1 先确认本机架构与现有 FRP 安装情况写 systemd 服务文件之前先把 FRP 客户端本身准备好。检查机器架构用一条命令uname -m输出x86_64说明是 64 位 x86 机器下载linux_amd64版本输出aarch64或arm64说明是 64 位 ARM 机器下载linux_arm64树莓派这类 32 位 ARM 系统输出armv7l则要下载linux_arm版本。选错架构的结果就是执行时直接报Exec format error这一步踩坑的人非常多。然后再确认是否已经装过 FRP。如果以前手动跑起来过直接用which frpc或find / -name frpc -type f 2/dev/null找一下二进制路径。我习惯统一规划目录二进制放在/usr/local/frp/frpc配置文件放在/etc/frp/frpc.toml。这个路径后面服务单元文件里要反复引用先定下来后面省事。2.2 下载安装 FRP 客户端推荐目录与文件规划从 FRP 项目 Releases 页面下载对应架构的压缩包我以 Linux amd64 为例# 下载版本号以实际发布为准 wget https://github.com/fatedier/frp/releases/download/vX.Y.Z/frp_X.Y.Z_linux_amd64.tar.gz # 解压 tar -zxvf frp_X.Y.Z_linux_amd64.tar.gz # 切换到解压目录 cd frp_X.Y.Z_linux_amd64 # 创建目标目录并拷贝文件 mkdir -p /usr/local/frp /etc/frp install -m 755 frpc /usr/local/frp/frpc install -m 644 frpc.toml /etc/frp/frpc.toml # 验证版本 /usr/local/frp/frpc -v注意压缩包里有frps和frpc两个文件frps是服务端跑在公网服务器上这里做开机自启的是frpc客户端跑在内网机器上。别拷错了。安装包里自带的frpc.toml是默认配置我通常会直接覆盖成自己最终的配置。配置文件路径建议固定为/etc/frp/frpc.toml这样服务单元文件写死路径后续改配置也只动这一个文件思路清晰。2.3 第一次前台运行验证配置文件任何服务单元文件写出来之前先保证 “手动执行这条命令一定能跑起来”。前台验证这一步绝对不能省否则后面排查问题的时候根本分不清是 systemd 配置写错还是 FRP 配置本身有问题。/usr/local/frp/frpc -c /etc/frp/frpc.toml正常情况会看到类似start frpc service、login to server success之类的日志输出。如果报错token 不匹配、服务端连不上、配置格式不对当场就能改不用反复重启 systemd 服务。确认前台运行正常之后按CtrlC停掉再进入下一步写服务单元文件。这个顺序非常重要——我见过太多人直接写完 service 文件就systemctl start结果从头排查到尾发现是配置文件里的一个参数大小写写错了。3. 编写 frpc 的 systemd 服务单元文件3.1 服务单元文件逐行讲解在/etc/systemd/system/frpc.service创建服务单元文件这是整个开机自启方案的核心。[Unit] DescriptionFRP Client Service Afternetwork-online.target Wantsnetwork-online.target [Service] Typesimple Userfrpc Groupfrpc Restarton-failure RestartSec5s ExecStart/usr/local/frp/frpc -c /etc/frp/frpc.toml ExecReload/bin/kill -HUP $MAINPID LimitNOFILE1048576 NoNewPrivilegestrue [Install] WantedBymulti-user.target逐段拆解这段配置[Unit]段里Description就是给这个服务写一句人话说明systemctl status会显示它。最重要的两行是Afternetwork-online.target和Wantsnetwork-online.target它们明确了“网络就绪之后再启动 frpc”。如果没有这两行开机时 systemd 可能在网络还没配置好的时候就拉起了 frpc结果是服务状态显示 active但隧道根本连不上排查起来特别迷惑。[Service]段是核心。Typesimple告诉 systemd 这个进程启动后就会常驻前台不会 fork 到后台。ExecStart写的是实际执行的命令路径必须和实际安装路径严格一致。Restarton-failure表示进程异常退出时自动拉起RestartSec5s是重启前的等待时间这两个参数一起实现了“断了自愈”。Userfrpc和Groupfrpc指定运行身份避免直接用 root 跑常驻网络服务安全上一个重要的习惯。ExecReload/bin/kill -HUP $MAINPID支持通过systemctl reload frpc热加载部分配置不用重启进程就能重新读取隧道配置。LimitNOFILE1048576把文件描述符上限调到很大防止 frpc 在大量隧道连接场景下达到系统默认的 1024 限制。NoNewPrivilegestrue进一步收紧权限提升能力属于安全加固。[Install]段的WantedBymulti-user.target是开机自启的关键。它声明了服务应该跟随多用户模式启动systemctl enable就是利用这个声明在启动目标里创建软链接。3.2 frpc.toml 配置文件要点新版 TOML 格式FRP 从 v0.52.0 开始默认配置格式从 ini 切换到了 TOML文件后缀是.toml。网上大量老教程还在用frpc.ini照着写在新版客户端上会直接报解析错误。这是兼容性上的一个大坑。一个完整的 frpc.toml 至少要包含服务端信息和隧道规则两部分serverAddr your-public-server.com serverPort 7000 auth.token your-strong-token transport.heartbeatInterval 30 transport.heartbeatTimeout 90 log.to /var/log/frp/frpc.log log.level info log.maxDays 7 webServer.addr 127.0.0.1 webServer.port 7400 webServer.user admin webServer.password your-dashboard-password [[proxies]] name ssh-tunnel type tcp localIP 127.0.0.1 localPort 22 remotePort 6000 [[proxies]] name web-tunnel type http localIP 127.0.0.1 localPort 8080 customDomains [dev.example.com]serverAddr和serverPort是公网 frps 服务端的地址和监听端口。auth.token必须和服务端的 token 完全一致不一致会反复报login to server failed。transport.heartbeatInterval是心跳间隔transport.heartbeatTimeout是心跳超时这两个参数决定了断线检测的灵敏度默认值是 30 秒和 90 秒在弱网环境下通常够用。[[proxies]]是隧道规则定义。type tcp是最常用的模式localPort是本地服务端口remotePort是公网服务端映射出来的端口。需要注意remotePort不能与 frps 服务端其他隧道或服务冲突否则要么连不上要么顶掉别人的配置。webServer段是客户端自带的 Dashboard绑到127.0.0.1:7400后可以从本机浏览器查看隧道状态。这个对排查问题很有帮助尤其是 “进程活着但隧道不通” 的时候Dashboard 里能看到每个 tunnel 的实时状态。3.3 启动服务、设置开机自启与检查状态配置文件和服务单元文件都就位后按顺序执行下面几条命令# 重新加载 systemd让新写的 frpc.service 生效 systemctl daemon-reload # 启动服务 systemctl start frpc # 查看运行状态 systemctl status frpc看到active (running)并且日志中没有错误就说明服务已经起来了。下一步设置开机自启systemctl enable frpcenable命令的输出通常会给出提示本质上是在/etc/systemd/system/multi-user.target.wants/目录下创建了一个指向/etc/systemd/system/frpc.service的软链接。可以用下面命令确认软链接已创建ls -l /etc/systemd/system/multi-user.target.wants/frpc.service指令执行顺序记住一句话先daemon-reload再start确认没问题后enable。如果你先enable后改服务文件改完也一样要重新daemon-reload否则 systemd 可能还在用旧的配置。4. 验证开机自启是否真实生效4.1 立即重启验证的完整流程服务手动能起来还不够开机自启到底行不行必须实际重启一次验证。完整流程是这样的# 重启前先确认状态 systemctl is-enabled frpc # 输出 enabled 说明开机自启已设置 # 确认配置无误后重启 reboot重启完成后等系统完全起来SSH 登录进去第一件事是检查 frpc 服务是否在运行systemctl status frpc再配合几条命令确认隧道真实可连# 确认 frpc 进程存在 ps -ef | grep frpc # 确认本地端口有监听如果有本地 webServer 的话 ss -tlnp | grep 7400更直接的方式是从公网服务器的角度看frps 服务端日志里应该能看到这个客户端重新登录的记录。如果只停留在status层面就下结论有可能进程起来了但隧道没有实际连通所以一定要做端到端验证。4.2 systemd 自启链路与 enable 背后做了什么为什么systemctl enable frpc之后就能开机自启把这条链路理清楚以后排查其他服务也能举一反三。系统开机后内核启动 PID 为 1 的 systemdsystemd 根据默认目标启动对应服务。multi-user.target是常规服务器落地目标它下面挂着一堆Wants服务。执行systemctl enable frpc时systemd 会读取服务文件里的WantedBymulti-user.target自动在/etc/systemd/system/multi-user.target.wants/创建软链接。下次开机时 systemd 加载multi-user.target发现frpc.service被 want就会按照依赖关系把它拉起来。network-online.target在这一链路中扮演重要角色。普通network.target只代表网络服务脚本被执行了不代表 IP 地址已经拿到network-online.target则需要网络管理组件NetworkManager、systemd-networkd确认网络真正在线后才完成。所以 frpc 服务单元里同时写了Afternetwork-online.target和Wantsnetwork-online.target一行管排序等网络在线一行管关联启动时主动拉取网络在线状态。有些精简系统默认没有启用NetworkManager-wait-online.service导致network-online.target永远等不到。这种情况下 frpc 可能会因为网络未就绪而重试失败多次但只要配置了Restarton-failure它就会一直重试直到连上。5. 常见问题与排查技巧实录5.1 服务启动失败类型的排查systemctl start frpc后如果状态不是 running第一件事就是看日志journalctl -u frpc -n 50 --no-pager最常见的失败原因是配置格式错误。新版 frpc 用 TOML 格式如果你把老教程里的 ini 配置直接拿来用会看到类似toml: line X: key xxx is not recognized这样的报错。解决办法就是改成 TOML 语法注意号、双引号、数组写法。第二个常见问题是 token 不匹配。服务端和客户端配置的 token 不一致客户端日志会反复出现login to server failed: token is not consistent。把两边 token 改成一致再重启即可。第三是二进制文件没有执行权限。install -m 755已经确认过权限但如果手动拷贝的 frpc 没加执行权限启动会报Permission denied。手动chmod x /usr/local/frp/frpc可解决。5.2 网络未就绪导致隧道没起来现象很典型开机后 frpc 服务状态是active (running)但隧道就是连不上手动systemctl restart frpc马上就好了。这几乎都是网络未就绪导致的。解决方式有两个层面。服务单元文件里写好Afternetwork-online.target和Wantsnetwork-online.target是第一层保障。第二层是确认系统里NetworkManager-wait-online.service或systemd-networkd-wait-online.service真正启用如果用的是 NetworkManager执行systemctl enable NetworkManager-wait-online.service对于机器上只有一张网卡、IP 是固定内网地址的简单场景最直接的办法不依赖network-online.target而是给服务加一个等待 IPv4 地址的预执行命令ExecStartPre/bin/bash -c until ip addr show | grep -q inet ; do sleep 3; done这条命令在启动 frpc 之前先轮询网卡是否已拿到 IPv4 地址拿到才继续逻辑非常直观特别适合跑在拨号网络或者 DHCP 分配较慢的环境里。5.3 端口冲突、SELinux、权限等其他问题端口冲突是最隐蔽的一类问题。frpc 配置的remotePort如果和 frps 服务端其他隧道或服务端口撞了客户端日志可能只显示一个笼统的connect to server error或者连接看似建立但隧道数据不通。排查方法是在服务端跑ss -tlnp | grep remotePort看端口有没有被占用。SELinux 开启的机器上从非标准路径启动的网络服务可能被策略拦截。临时验证可以用setenforce 0如果这样就好了说明是 SELinux 策略问题。最省事的做法是把 frpc 二进制的 SELinux 上下文设置成标准可执行文件类型chcon -t bin_t /usr/local/frp/frpc权限问题同样值得重视。如果服务单元里指定了Userfrpc要确保/etc/frp/frpc.toml对 frpc 用户可读。经常有人遇到“手动 root 能跑systemd 跑不起来”的情况查到最后是配置文件的读取权限不对。SSH 隧道场景还要注意localPort22时目标服务通常由 root 管理但 frpc 客户端本身只要以普通用户运行 SSH 客户端连接即可权限问题集中在文件读权限上不涉及特权端口绑定。5.4 配置修改后的重载方式改完 frpc.toml 后推荐的做法是systemctl daemon-reload systemctl restart frpcrestart会完整停止再启动进程适合serverAddr、auth.token这类全局参数变更。systemctl reload frpc则是通过 SIGHUP 信号触发热加载适合新增隧道、调整隧道参数这类场景进程不会中断。注意 TOML 配置文件语法错误时reload 可能悄然失败所以 reload 之后一定要用systemctl status frpc确认状态。这里有一个非常常见的坑账户有 sudo 权限但是环境变量里手动设置了FRPC_*之类的环境变量干扰配置。systemd 启动时环境变量非常干净和你在 shell 里执行时不一样。如果本地验证正常、systemd 启动异常检查一下有没有全局环境变量/etc/environment或 bashrc 里的相关配置。6. 让 frpc 长时间稳定运行的几个补充细节6.1 日志到底交给谁管frpc.toml 里配置了log.to /var/log/frp/frpc.log自定义日志文件便于自己用tail -f跟踪。但要注意 frpc 本身不带日志轮转跑几个月后日志文件可能膨胀到几百兆甚至几个 G。两种省心的处理方式方式一是不在 frpc.toml 里配置log.to让 frpc 把日志打到标准输出由 journald 统一接管。systemd 会把 stdout 自动收进 journal用journalctl -u frpc查看日志不会无限增长还可以设置/etc/systemd/journald.conf里的SystemMaxUse限制总容量。对于单个自用隧道这是最省心的方案。方式二是保留日志文件但加 logrotate 配置。在/etc/logrotate.d/frpc写一个简单规则/var/log/frp/frpc.log { daily rotate 7 compress delaycompress missingok notifempty copytruncate }copytruncate对 frpc 很重要因为 frpc 没有针对日志文件被 rotate 后重新打开文件的原生支持直接 rename 会导致日志丢失copytruncate 先复制再清空进程不受影响。6.2 进程账号与安全加固前文提到创建专用运行账号这里给出完整命令useradd -M -s /usr/sbin/nologin frpc mkdir -p /etc/frp chown -R frpc:frpc /etc/frp-M不创建 home 目录-s /usr/sbin/nologin禁止该账号交互式登录这样 frpc 即使被攻破也无法通过这个账号登录系统。用专用账号替代 nobody 的好处是权限边界更清晰nobody 在很多系统上被多个服务共用文件权限管理很容易错乱。进一步加固可以在服务单元文件里追加ProtectSystemstrict ProtectHometrue PrivateTmptrue这些参数会为进程建立沙箱限制它对系统目录和用户目录的写权限一般场景下够用。但注意加了ProtectSystemstrict后如果 frpc 需要写日志文件路径必须显式在ReadWritePaths里允许否则日志会静默失败。配置日志文件场景下我一般只加PrivateTmptrue避免过度限制导致排查复杂。6.3 网络环境差时的自动重连调优frpc 天然支持断线重连但调优参数能显著改善弱网场景体验。transport.heartbeatInterval和transport.heartbeatTimeout决定断线感知速度默认 30/90 秒意味着物理断网后最多 90 秒才意识到掉线并触发重连。如果你的使用场景是 4G/5G 移动网络可以适当调短transport.heartbeatInterval 10 transport.heartbeatTimeout 30伴随断线还有一个现象值得解释有时候公网服务器重启了客户端的 TCP 连接看起来还“活着”但实际上整个 frp 链路已经废了。把心跳时间调短配合 systemd 的Restarton-failure可以尽快恢复链路。如果链路长期处于半死状态frpc 自身会因心跳超时退出然后 systemd 再次拉起形成完整的自愈闭环。对于网络非常不稳定的远程设备可以考虑把RestartSec提高到 10 秒。加延时不是为了拖延恢复而是给网络栈一点缓冲时间避免 frpc 在拨号连接刚就绪、DNS 还没刷新的情况下疯狂重连导致死循环。我在实际使用中发现这套配置字符串全部写完后最考验人的反而是心态遇到问题不要急着改参数先journalctl -u frpc -f看日志大部分答案都在里面。systemd 的优势就是把“启动、守护、日志”都标准化了只要你静下心顺着这条链路排查FRP 开机自启这件事基本能一次性搞定。最后分享一个小技巧如果你有多台内网机器要配置可以先在一台机器上完全验证通过然后把/usr/local/frp/frpc二进制的哈希值记录下来其他机器直接用同样版本、同样的 service 文件部署能少踩不少版本差异的坑。
返回列表