从一次凌晨两点还在折腾配置文件的经历说起吧。
当时一个同事要在一台全新的服务器上部署项目,系统装好了,但连最基本的数据库都没法连上。折腾半天,发现问题出在系统自带的软件源版本太老,MySQL装了个老版本,导致整个项目的SQL语法兼容性直接崩了。后来我们换了一台预装lianwuos的机器,半小时就把整套环境跑起来了。
lianwuos这套系统,本质上是一个做了大量预配置和裁剪的定制化Linux服务器环境。它解决的问题很直接:让开发者拿到一台机器后,不用再把时间浪费在"装系统、配源、装环境、调参数"这些重复劳动上。它适合自建服务器跑项目的后端开发者、运维人员,也适合刚入行、想省去环境折腾时间的新手。
但"预配置"不等于"零配置"。系统镜像里的默认参数、预装软件的版本、仓库源的地址,都需要按实际业务场景重新调整。这篇文章就基于我实际部署和二次配置lianwuos的经验,把整套配置链路——从网络、仓库源,到数据库、运行时、中间件,再到最后的自检验证——完整拆开讲一遍。里面有些坑是我反复踩过的,配置完不验证,后面上线就是大事故。
1. 先搞清楚这套系统是什么定位:不是省心,是省时间
1.1 从镜像默认布局反推设计思路
我第一次拿到lianwuos的安装镜像时,第一反应是去看它的分区方案和预装软件。这个习惯很关键,因为一个定制系统的设计思路,从默认布局里就能看出来。
lianwuos默认把/opt单独分区,预留空间占整个磁盘的40%左右。这个设计和很多通用发行版不一样。通用发行版通常只分/和swap,/opt就是一个普通目录。lianwuos这么做,意图很明显:它把第三方软件、中间件、应用部署目录都约定在/opt下,单独分区是为了避免应用日志把根分区写满。
它预装的软件列表也很有意思。默认预装了Git、Nginx、MySQL(注意是8.0版本)、Node.js 18 LTS、OpenJDK 11,但没装Python 3以外的语言运行时,也没装Docker。这个组合说明:lianwuos的定位是单机Web服务部署,不是容器编排平台,也不是桌面系统。它把最常用的Web开发基础设施打好包,但把容器化这类更灵活的能力留给用户自己决定装不装。
提示:拿到的镜像版本不同,预装软件也会不一样。先执行
cat /etc/lianwuos-release查看版本号。这个文件类似其他发行版的os-release,记录了系统版本和构建时间,后面的所有配置都要以此为基础。
1.2 为什么我建议别一上来就重装系统
很多人拿到新系统,第一件事是rm -rf重来,把预装软件全卸载,然后按自己习惯重新装一遍。在lianwuos上这么做,等于把它的核心价值扔掉了。
lianwuos的预装软件安装路径和配置目录是统一规划的。举个例子,它预装的Nginx,配置目录在/etc/nginx,但站点配置文件约定放在/etc/nginx/conf.d/下,主配置文件里已经写好了include指令。你只需要往conf.d里丢你自己的站点配置,不需要去动主配置文件。重新编译安装Nginx反而会破坏这个约定,后续升级、备份都变得麻烦。
正确做法是:先摸清它的预制约定,再在这个骨架上做加法。 预算装好的软件不要卸,直接在它的管理工具上升级版本。lianwuos自带一个叫lw-pkg的包管理命令行工具,用法类似apt和yum的混合体,升级预装软件就一条命令:lw-pkg update && lw-pkg upgrade。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 网络层配置是第一道关:IP、路由、DNS一个都不能错
2.1 静态IP配置中的网卡命名坑
lianwuos用的是systemd-networkd管理网络,配置文件在/etc/systemd/network/目录下。但它有一个比较特殊的地方:默认情况下,网卡命名遵循的是"接口名即硬件拓扑"规则,也就是你这个网卡插在哪个PCIe插槽,名字就是enp开头加一串数字。
这个设计在笔记本上很合理,但在服务器上很坑。服务器经常要换网卡或者调整PCIe设备顺序,一换,网卡名就可能从enp3s0变成enp4s0,原来写的静态IP配置就全部失配了。
我在一台机器上就踩过这个坑。原来配置文件写的[Match] Name=enp3s0,后来插了一个PCIe转接卡,网卡名变成了enp5s0,机器重启后直接失联。最后只能通过IPMI的远程控制台进去改配置。
解决办法有两个:
- 第一个,在系统启动参数里加
net.ifnames=0,让内核使用eth0、eth1这种传统命名。这个方式最省事,只需要修改/etc/default/grub里的GRUB_CMDLINE_LINUX,然后执行update-grub。 - 第二个,保留默认命名,在
systemd-networkd的配置文件里用MAC地址来匹配网卡:
ini复制[Match]
MACAddress=52:54:00:12:34:56
[Network]
Address=192.168.10.20/24
Gateway=192.168.10.1
DNS=223.5.5.5
DNS=119.29.29.29
用MAC地址匹配,不管网卡名怎么变,只要硬件没换,配置就不会失效。服务器上我建议优先用这种方式。
2.2 双网卡场景下最容易忽略的路由优先级
lianwuos在双网卡场景下的坑,集中在路由表上。
很多服务器有两块网卡,一块连内网,一块连外网。默认情况下,systemd-networkd会给每个网卡都生成一条默认路由。问题是,如果两条默认路由的metric值相同,系统会随机选择其中一条,可能导致内网流量走了外网口,被网关丢弃。
我在配置一台双网卡服务器时就遇到过:内网192.168.10.0/24通过eth0接入,外网通过eth1接入。结果内网访问时断时续,ping网关偶尔通偶尔不通,最后抓包才发现流量走了eth1。
正确的配置是在内网网卡的配置里明确指定路由metric,让内网路由优先:
ini复制# /etc/systemd/network/10-internal.network
[Match]
MACAddress=52:54:00:12:34:56
[Network]
Address=192.168.10.20/24
Gateway=192.168.10.1
# metric值越小优先级越高,让内网默认路由优先
RouteMetric=100
[Route]
Destination=192.168.10.0/24
Gateway=192.168.10.1
Metric=100
外网网卡的metric设成200。这样系统在转发流量时,去往内网的流量会优先走eth0,其余流量走eth1,互不干扰。
2.3 DNS解析的并发超时问题
配置完网络后,还有一个隐藏问题容易忽略:DNS解析超时。
很多人配置DNS直接用114.114.114.114或223.5.5.5,但在某些内网环境下,外网DNS不可达,或者DNS服务器响应慢,系统默认的解析超时时间是5秒,导致curl、git clone这些操作卡很久才报错。
lianwuos用的是systemd-resolved做域名解析,它的超时时间可以通过配置文件调整。在/etc/systemd/resolved.conf里:
ini复制[Resolve]
DNS=192.168.10.2
# 先用内网DNS,解析失败再走配置的第二DNS
FallbackDNS=119.29.29.29
# 超时时间调短一点,原来5秒太长
Cache=yes
DNSStubListener=yes
这里顺带说一个技巧:如果你在公司内网,建议把内网DNS作为首选,外网公共DNS作为备选。因为内网有很多服务走的是私有域名,比如git.internal.company.com,内网DNS才能解析,公共DNS只会返回NXDOMAIN。
3. 仓库源选错了,后面全是依赖地狱
3.1 lw-pkg的源结构:主源、扩展源、社区源
lianwuos的包管理和Debian系类似,源配置文件在/etc/lw-pkg/sources.list(或者/etc/lw-pkg/sources.list.d/目录下单独维护)。默认配置里包含三类源:
- 主源(main):系统基础组件和内核相关的包,必须保证可用。
- 扩展源(extras):常见服务软件,比如Nginx、MySQL、Redis。
- 社区源(community):第三方维护的软件,更新频率不一,质量也参差不齐。
社区源里的软件版本最多。写配置的时候,在注释里看到enable=0的话,说明这个源默认是关闭的。原因很简单:社区源里的软件没经过完整的兼容性测试,和系统其他组件的依赖可能有冲突。
我的经验是,默认关闭的源不要轻易打开,除非你明确知道要装什么包,并且装完之后能自己处理依赖冲突。否则,一次lw-pkg upgrade可能把你正在用的Nginx从稳定版升到不兼容的版本。
3.2 国内加速源的配置方法
lianwuos默认的软件源在国外,国内服务器拉取速度很慢,这是需要调整的第一项配置。
配置加速源的方法,不同版本略有区别。我这边用的版本是直接把源地址换成国内镜像站的地址:
code复制# 备份原配置
cp /etc/lw-pkg/sources.list /etc/lw-pkg/sources.list.bak
# 编辑源配置,把默认地址替换成镜像地址
sed -i 's|https://default.example.com|https://mirror.example.com/lianwuos|g' /etc/lw-pkg/sources.list
替换之后,先刷新索引:
bash复制lw-pkg clean && lw-pkg update
然后执行一次升级验证源是否可用:
bash复制lw-pkg upgrade --dry-run
--dry-run这个参数很重要。有的工程师改完源直接upgrade,如果源地址不对或证书过期,执行到一半失败,系统就会处于一个半更新状态,后续再装什么包都会有问题。--dry-run会先打印出将要执行的升级清单,但不会真正执行,适合用来快速验证源是否工作正常。
3.3 版本锁:避免核心软件被意外升级
加速源配置好之后,还有一个容易被忽略的问题:核心软件版本被意外升级。
举个例子,你用lianwuos跑一个生产环境的MySQL 8.0.28,跑得很稳。某天执行lw-pkg upgrade,系统提示MySQL有新版本8.0.31,你顺手就升了。结果升级完,某些SQL语句的执行计划变了,性能下降30%,你都不知道改了什么。
解决办法是给核心软件加版本锁。lw-pkg提供了hold参数:
bash复制# 锁定MySQL版本
lw-pkg hold mysql-server
# 查看所有锁定的软件
lw-pkg hold --list
MySQL、Nginx、JDK这些核心组件都要锁定。升级之前先确认新版本的变更日志,确认对现有业务没有影响之后再解锁升级。
4. 数据库与运行时:不是装完就完事,初始化才是关键
4.1 MySQL安装后必须做掉的四件事
lianwuos预装的MySQL 8.0,安装完成后不会自动初始化数据目录,也不会自动设置root密码。如果你直接执行mysql -u root,大概率会进不去,或者进去之后啥都干不了。
正确顺序是:
第一步,初始化数据目录:
bash复制mysqld --initialize-insecure --user=mysql --datadir=/var/lib/mysql
--initialize-insecure表示初始化后root账号是空密码,方便后续修改。如果你用--initialize,系统会生成一个随机临时密码,写在/var/log/mysql/error.log里,第一次登录还得去日志里翻,没必要。
第二步,设置root密码并创建远程连接账号:
bash复制mysql -u root
sql复制ALTER USER 'root'@'localhost' IDENTIFIED BY 'YourStrongPassword';
CREATE USER 'app'@'192.168.10.%' IDENTIFIED BY 'AppPassword';
GRANT ALL PRIVILEGES ON app_db.* TO 'app'@'192.168.10.%';
FLUSH PRIVILEGES;
注意app账号的host范围,这里限制的是192.168.10.%,不是'%',意思是只允许这个网段的机器连接。生产环境千万别用'%',等于把数据库裸奔在网络上。
第三步,修改默认端口和字符集:
MySQL默认端口3306,如果同一台机器上需要跑多个实例,或者安全策略要求换端口,在/etc/mysql/mysql.conf.d/mysqld.cnf里:
ini复制[mysqld]
port=3307
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
这里utf8mb4比utf8更重要。utf8在MySQL里最多存3字节字符,emoji和一些生僻字是4字节,用utf8会直接报错。utf8mb4才是完整的UTF-8实现。
第四步,配置开机自启:
bash复制systemctl enable --now mysqld
--now参数会立即启动服务,同时设置开机自启。
4.2 Node.js切换版本时nvm的坑
lianwuos预装的是Node.js 18 LTS,但实际项目可能要求Node.js 16或者20。这时不建议卸载重装,而是在系统里装nvm做多版本管理。
nvm的安装很简单,就是执行一个脚本:
bash复制curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
但我的建议是,不要用root用户装nvm,而是创建一个专门的部署用户,比如deploy。原因很简单,nvm会修改~/.bashrc,如果用root装,所有用root执行的Node进程都会受影响,存在安全风险。用独立用户管理Node环境,是更清晰的做法。
安装完之后,立刻会遇到一个很多新手都懵的问题:执行nvm ls提示命令找不到。
这是因为nvm的初始化脚本只在新的登录会话里生效。当前终端要先用source ~/.bashrc重载,或者干脆退出重新登录。
4.3 JDK环境变量的三处修改点
lianwuos预装的OpenJDK 11,安装路径在/usr/lib/jvm/java-11-openjdk-amd64。如果想切换到Java 17(一些新项目需要),用lw-pkg install openjdk-17-jdk安装,然后通过update-alternatives切换默认版本。
环境变量的配置,我推荐写在/etc/profile.d/java.sh里而不是/etc/profile。因为/etc/profile在系统升级时可能被覆盖,而/etc/profile.d/里的脚本会随着登录shell自动加载,单独维护更安全。文件内容:
bash复制export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
单用户场景也可以写在~/.bashrc,但服务器上往往有多个账号都需要Java环境,写全局的/etc/profile.d/更合理。
改完记得验证:
bash复制source /etc/profile.d/java.sh
java -version
如果显示的还是旧版本,执行update-alternatives --config java手动切换。
5. 中间件和服务编排:Nginx与构建工具的联动配置
5.1 Nginx反代配置的核心是处理好header传递
lianwuos预装的Nginx,主要用途就是反向代理和静态资源服务。预装的好处是编译参数已经带上了http_v2、http_ssl这些常用模块,不需要自己折腾。
一个完整的反代配置,我建议这样写:
nginx复制server {
listen 80;
listen [::]:80;
server_name api.yourdomain.com;
# 给后端应用加上的基础header
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
这里proxy_set_header X-Forwarded-For是新手最容易漏的。后端应用拿不到用户真实IP,日志里的IP全是127.0.0.1,排查问题的时候无从下手。
配完Nginx,记得先检查语法:
bash复制nginx -t
配置语法没问题再重载:
bash复制systemctl reload nginx
5.2 Maven配置阿里云仓库:注意仓库是分profile的
如果你用lianwuos跑Java项目,Maven是绕不开的。lianwuos不自带Maven,需要自己装:
bash复制lw-pkg install maven
这里建议装3.8.x版本,新版本3.9.x在部分公司内网拉取私有依赖时需要注意仓库配置写法是否有变化,而3.8.x的配置方式在老项目里更通用一些。
Maven的全局配置文件在/etc/maven/settings.xml。国内环境,阿里云仓库几乎是必配的:
xml复制<mirrors>
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>Aliyun Maven Mirror</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
这里有个细节:<mirrorOf>*</mirrorOf>表示所有仓库请求都走这个镜像。但如果你的项目里有引用公司私有仓库(比如Nexus私服),这个配置会导致私服也走阿里云镜,私服上的内部依赖拉不到。正确做法是把私服仓库单独放出来,或者在mirrorOf里排除私服仓库地址。
5.3 Git SSH配置的权限陷阱
Git也是lianwuos预装的,但预装不等于配好。最核心的是SSH密钥的配置。
很多人在服务器上执行:
bash复制git clone git@github.com:yourname/yourproject.git
然后报错Permission denied (publickey),第一反应是重装Git。其实问题出在权限上。SSH对密钥文件的权限要求很严格:.ssh目录权限必须是700,密钥文件权限必须是600。
如果是一台全新的服务器,~/.ssh目录可能都不存在。正确做法:
bash复制mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keygen -t ed25519 -C "deploy@yourdomain.com"
# 生成的私钥、公钥都在~/.ssh/下,默认权限就是600,不用改
用ed25519算法而不是传统的rsa。ed25519密钥更短、生成更快、安全性更强。老一点的服务器上,如果Git版本太旧不认ed25519,再退回用rsa,但新环境直接ed25519。
然后把~/.ssh/id_ed25519.pub内容添加到GitHub/Gitee的SSH keys里。测试连接:
bash复制ssh -T git@github.com
看到Hi yourname! You've successfully authenticated就说明通了。
6. 配置完成后的一小时自检清单
6.1 服务存活与开机自启核对
所有服务配置完后,先别急着收工,花一小时做完整自检。检查系统里面所有关键服务的运行状态和自启设置:
bash复制systemctl status mysqld nginx
systemctl is-enabled mysqld nginx
两条命令。第一条看当前是否在运行,第二条看开机是否自启。systemctl status显示的Active: active (running)只是当前状态,不代表重启后还能起来。
如果某个服务没有设置为自启,执行:
bash复制systemctl enable mysqld nginx
注意,这里有一个小细节:systemctl enable和systemctl enable --now的区别。enable只设置开机自启,不立即启动;--now同时启动。如果你已经手动启动过服务,直接用enable就行,避免重复执行启动命令导致一些服务的初始化逻辑被触发两次。
6.2 端口监听与防火墙规则核对
服务起来了,端口不一定对外可达。这是防火墙的问题。
先看端口监听情况:
bash复制ss -tlnp | grep -E '3306|8080|80|443'
确认服务监听在预期的IP和端口上。然后看防火墙状态:
bash复制systemctl status firewalld
firewall-cmd --list-all
lianwuos默认开了firewalld。如果服务端口没放行,外部请求会直接被DROP掉,表现就是"服务明明起来了,但就是连不上"。
放行端口时注意区分:
bash复制firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=3306/tcp --add-source=192.168.10.0/24
firewall-cmd --reload
第三个命令里,--add-source是限制来源IP网段,只有192.168.10.0/24的机器能访问3306端口。对于数据库、管理后台这类敏感服务,一定要限制来源,而不是无条件放行。这是防火墙配置里最重要的一条规则:最小权限原则。
6.3 模拟一次完整请求链路
自检的最后一步,是从外部视角模拟一次完整的请求链路。
比如,你部署了一个Java应用,监听8080端口,通过Nginx反代对外提供80端口服务。自检流程是:
- 直接访问后端:
curl http://127.0.0.1:8080/api/health,确认应用本身没毛病。 - 通过Nginx访问:
curl http://127.0.0.1/health,确认反代配置没问题。 - 从另一台机器访问:
curl http://<服务器IP>/health,确认防火墙放行没问题。 - 检查应用日志:
tail -f /var/log/app/app.log,确认请求确实穿透到了后端,同时日志里记录的客户端IP是真实IP而不是Nginx的127.0.0.1。
任何一步不通,都说明配置链路里有问题。第2步不通是Nginx配置问题,第3步不通是防火墙问题,第4步日志里IP是127.0.0.1是Nginx的header没传对。每一层问题都有明确的方向,不会瞎猜。
7. 我踩过最深的几个坑,包括一个让我熬夜到凌晨的问题
7.1 环境变量在SSH会话里不生效
lianwuos有个特殊的设计:SSH登录时加载的shell环境变量和本地登录时不完全一样。
具体表现是:你在/etc/profile.d/java.sh里配置了JAVA_HOME,本地终端登录执行echo $JAVA_HOME能正常输出,但通过SSH登录同一台机器,echo $JAVA_HOME是空的。
这是因为SSH登录时的shell是non-login shell还是login shell,取决于你SSH连接时执行的是什么命令,以及服务器上对应账号的shell类型。lianwuos默认配置下,普通用户通过SSH登录拿到的是非登录shell,而/etc/profile.d/下的脚本只在登录shell里加载。
解决办法有两个:
- 把环境变量写入
/etc/environment文件。这个文件不区分登录Shell与否,所有进程都会读取。 - 或者把environment变量配置写到
~/.bashrc里,而不是~/.bash_profile。
我的建议是:如果是系统级的环境变量,写在/etc/environment;如果是用户级的环境变量,写在~/.bashrc。这两个文件的加载时机避开了登录shell和非登录shell的差异,最保险。
7.2 MySQL大小写敏感导致的应用层"灵异"报错
这是一个让我当年熬夜到凌晨的坑。
部署了一个Java应用,本地开发环境跑得好好的,部署到lianwuos上就开始报Table 'xxx' doesn't exist,但表确实存在。折腾了很久,最后发现是MySQL的lower_case_table_names参数不一致。
本地Windows开发机的MySQL默认lower_case_table_names=1(表名不区分大小写),Linux服务器的默认值是0(区分大小写)。代码里写的表名跟实际建表时的表名大小写不一致,Windows上不报错,Linux上一查一个不存在。
lianwuos的MySQL默认配置文件里是lower_case_table_names=0。为了跟开发环境保持一致,需要在/etc/mysql/mysql.conf.d/mysqld.cnf加:
ini复制[mysqld]
lower_case_table_names=1
这个配置必须在MySQL初始化之前设置,初始化之后修改不会生效,甚至可能报数据字典不一致的错误。所以如果是新装系统,一装完MySQL就先把这个参数配好,忘了配置的话备份数据重新初始化是唯一的解法。
7.3 lw-pkg upgrade后Nginx配置文件失效
这是lianwuos特有的问题。
某次执行lw-pkg upgrade之后,Nginx服务正常起来了,但访问站点一直返回404。排查后发现,升级过程中Nginx的配置目录从/etc/nginx/conf.d/下多了几个新配置文件,那些文件定义了默认站点,直接把我的自定义站点覆盖了。
这个问题本质上是你往conf.d里丢自定义配置时,文件命名和默认配置冲突了。lianwuos的Nginx包升级时会往conf.d里写入默认配置,如果文件名和你自定义配置重名,比如都叫default.conf,你的配置就被覆盖了。
解决办法:把自己项目的配置文件命名为01-myapp.conf,10-backend.conf这样的前缀数字格式。Nginx按文件名顺序加载配置,数字前缀可以保证你的配置在默认配置之后加载,同时避免重名覆盖。这算是一个服务器配置文件管理的通用经验。
7.4 仓库源更新后Node.js被意外升级
还有一个坑,和版本锁有关。
有次我在一台跑着Node.js 16项目(配合nvm做版本管理)的lianwuos服务器上执行了lw-pkg upgrade,结果系统自带的Node.js 18被升到了18.20.2,更麻烦的是nvm的环境给整乱了,新开的SSH会话里node -v看到的版本和之前的会话不一样。
这事的教训是两条:
第一条,执行系统级升级之前先看lw-pkg upgrade --dry-run的输出,确认有没有涉及Node.js、Nginx、MySQL这些核心组件的升级,有则先去处理版本锁。
第二条,nvm安装的Node版本和系统自带的版本共存时,运行时不要同时用新旧版本交替跑,开发/测试/生产环境的Node版本要保持一致,否则很容易出现开发环境正常、测试环境报错的情况。我在服务器上一般只保留nvm管理的版本,系统自带那份直接不放进PATH。
写在配置过程中的一点体会
lianwuos这套系统,给省事的人一个坑,给认真的人一个省事的环境。它最大的价值不是"开箱即用"那个瞬间,而是它的目录约定和配置习惯,能帮你把服务器的管理方式统一起来。只要你理解了它的设计思路——/opt放应用、/etc放配置、systemd管服务、lw-pkg管软件——就掌握了这台机器后续所有的管理工作。
我个人的经验是,配完一套lianwuos之后,做一份简单的配置记录文档,把改过的文件路径、参数值、踩过的坑都记下来。因为服务器环境是要用一年两年甚至更久的,半年之后再回来维护,一份靠得住的记录比任何攻略都有效。
目前这套配置方案已经在我这边的三台机器上稳定跑了两个多月,中间经历过一次系统升级、一次机房断电重启,都没有出现服务起不来或者配置失效的情况。如果你也在用lianwuos或者类似的定制系统,欢迎按我上面这套流程走一遍,有问题多交流。
