本文导读 :很多 Java 开发者登上 Linux 服务器后,面对满屏的
/etc、/var、/opt、/usr完全不知所措。本文是系列第 2 篇,带你彻底搞清楚 Linux 目录结构的设计逻辑,并建立一套 Java SaaS 项目的标准化部署目录规范,同时讲解 AI 大模型文件的存放最佳实践。
一、"一切皆文件"------理解 Linux 的核心哲学
在 Windows 里,硬件设备、网络连接、系统配置分散在设备管理器、注册表、控制面板等不同的地方。而在 Linux 里,有一个非常优雅的设计哲学:一切皆文件(Everything is a file)。
这句话不是比喻,是字面意思:
- 你的硬盘:是文件(
/dev/sda) - 你插入的 U 盘:是文件(
/dev/sdb) - 你的网卡:是文件(
/dev/eth0) - 系统的 CPU 信息:是文件(
/proc/cpuinfo) - 进程之间通信用的管道:是文件
- 鼠标键盘等输入设备:是文件
这意味着什么?意味着操作系统里几乎所有东西,都可以用读写文件的方式来操作 。你可以用 cat /proc/meminfo 读取内存信息,用 echo 1 > /proc/sys/net/ipv4/ip_forward 开启内核路由转发,用 cat /dev/urandom 生成随机数据。这种一致性让 Linux 的工具链极其强大------任何能读写文件的工具,都能和系统的任何部分交互。
Linux 的文件类型实际上有 7 种:
| 类型标识 | 类型名称 | 示例 | 说明 |
|---|---|---|---|
- |
普通文件 | /etc/hosts |
文本、二进制、图片等 |
d |
目录文件 | /var/log/ |
本质也是文件,存储文件名列表 |
l |
符号链接 | /usr/bin/python3 -> python3.10 |
类似 Windows 快捷方式 |
c |
字符设备 | /dev/tty |
按字符流读写的设备(键盘、串口) |
b |
块设备 | /dev/sda |
按块读写的设备(硬盘、U盘) |
p |
命名管道 | /tmp/test.fifo |
进程间通信 |
s |
Socket 文件 | /var/run/docker.sock |
网络/本地进程通信 |
用 ls -l 命令可以看到文件类型标识(第一个字符):
bash
# 查看文件类型(第一列第一个字符就是类型标识)
ls -la /dev/ | head -20
# 输出示例(节选)
# brw-rw---- 1 root disk 8, 0 Jun 20 10:00 sda ← b 是块设备(硬盘)
# crw-rw-rw- 1 root tty 5, 0 Jun 20 10:00 tty ← c 是字符设备
# lrwxrwxrwx 1 root root 4 Jun 20 10:00 rtc -> rtc0 ← l 是符号链接
# 查看 docker.sock 类型(很多 Java 项目会挂载它)
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker 0 Jun 20 10:00 /var/run/docker.sock
# 第一个字符 s 表示这是一个 Socket 文件
💡 生产提示 :理解
/var/run/docker.sock是 Socket 文件非常重要。当你的 Spring Boot 应用需要在容器内调用 Docker API(比如动态创建容器),需要把这个 socket 文件挂载到容器内。如果你不懂"一切皆文件"的概念,就不会理解为什么一个.sock后缀的东西是"Docker 的接口"。
二、Linux 目录树全景图:每个目录的职责
Linux 遵循 FHS(Filesystem Hierarchy Standard,文件系统层次结构标准) 来组织目录结构。这是一个国际标准,规定了各目录的用途,所有主流发行版都遵循它。
bash
/(根目录)
├── bin/ → 所有用户可用的基础命令(ls、cp、mv、cat 等)
├── sbin/ → 系统管理命令(reboot、fdisk、iptables 等,通常需要 root)
├── etc/ → 系统和软件的配置文件(最重要的目录之一)
├── var/ → 可变数据(日志、缓存、数据库文件等会变化的内容)
├── tmp/ → 临时文件(重启后清空,不要在这里放重要数据)
├── home/ → 普通用户的主目录(/home/ubuntu、/home/dengkui 等)
├── root/ → root 用户的主目录(注意不在 /home/ 下)
├── opt/ → 可选的第三方软件(手动安装的大型软件包)
├── usr/ → 用户程序和文件(最大的目录,包含大量子目录)
│ ├── bin/ → 用户命令(非系统启动必须的)
│ ├── lib/ → 程序依赖的库文件(.so 动态链接库)
│ ├── local/ → 本地安装的软件(编译安装的软件默认在这里)
│ └── share/ → 共享文件(文档、图标、man 手册等)
├── lib/ → 系统启动必需的库文件(内核模块也在这里)
├── boot/ → 启动加载器和内核文件(grub、vmlinuz、initrd)
├── dev/ → 设备文件(硬盘、终端、随机数生成器等)
├── proc/ → 虚拟文件系统,反映内核和进程状态(不占磁盘空间)
├── sys/ → 虚拟文件系统,内核和硬件信息(比 /proc 更结构化)
├── mnt/ → 临时挂载点(手动 mount 磁盘时常用)
├── media/ → 可移动设备自动挂载点(U盘、光盘)
├── run/ → 运行时数据(PID 文件、Socket 文件,重启后清空)
└── srv/ → 服务数据(FTP、HTTP 的数据,不太常用)
⚠️ 踩坑记录 :很多初学者搞不清
/bin和/usr/bin的区别。历史上/bin存放系统启动时就需要的命令(在/usr分区还没挂载时就能用),/usr/bin存放系统正常运行后才需要的命令。在现代的 Ubuntu 22.04 上,/bin已经是/usr/bin的符号链接,两者是同一个目录,这个区别已经消失了。但你在看老教程时会看到这个区分,知道历史背景就不会困惑。
三、重点目录深度解析
3.1 /etc:配置文件的家
/etc 是"et cetera"(等等、其他)的缩写,历史上存放各种杂项配置,现在是所有系统级配置文件的标准存放地。
bash
# 查看 /etc 下最常用的配置文件
ls /etc/ | grep -E "^(host|resolv|passwd|fstab|ssh|apt|nginx|mysql)"
# 一些关键配置文件的作用:
cat /etc/hosts # 本地 DNS 解析(IP 到域名的映射)
cat /etc/resolv.conf # DNS 服务器配置(向哪里查询域名)
cat /etc/passwd # 用户账户信息(不含密码,密码在 /etc/shadow)
cat /etc/fstab # 磁盘挂载配置(开机自动挂载的规则)
cat /etc/hostname # 当前主机名
# 查看 SSH 服务配置
cat /etc/ssh/sshd_config | grep -v "^#" | grep -v "^$"
对 Java SaaS 开发者来说,/etc 里最常接触的是:
/etc/nginx/→ Nginx 配置目录/etc/mysql/→ MySQL 配置目录/etc/redis/→ Redis 配置目录/etc/systemd/system/→ 自定义 systemd 服务文件
⚠️ 踩坑记录 :
/etc/hosts文件是一个经常被忽视的排查入口。很多微服务环境中,服务之间通过域名互相调用,如果 DNS 解析出问题,可以临时在/etc/hosts里加一行IP 域名来绕过,先让业务恢复,再排查 DNS 根因。另外,Docker 容器启动时有自己的/etc/hosts,容器内的域名解析和宿主机是隔离的,不要混淆。
3.2 /var:变化的数据
var 是 "variable" 的缩写,存放运行中会不断变化的数据。
bash
# 查看 /var 的主要子目录
ls -lh /var/
# 关键子目录说明:
ls -lh /var/log/ # 日志文件(系统日志、应用日志都在这里)
ls -lh /var/lib/ # 应用的持久化数据(MySQL数据文件、Docker数据等)
ls -lh /var/cache/ # 缓存文件(apt 的包缓存)
ls -lh /var/run/ # 同 /run(PID 文件、Socket 文件)
# 查看系统日志(最近 50 行)
tail -50 /var/log/syslog # Ubuntu 系统日志
tail -50 /var/log/auth.log # 认证日志(SSH 登录记录)
# 查看 MySQL 数据文件位置
ls -lh /var/lib/mysql/
# 查看 Docker 数据目录大小(容器、镜像都在这里)
du -sh /var/lib/docker/
⚠️ 踩坑记录 :
/var/log是生产故障排查的第一现场,但也是最常见的"磁盘打满"元凶。Java 应用如果没有配置 logrotate(日志轮转),日志文件会无限增长,最终把磁盘撑满,导致数据库无法写入、应用崩溃。用df -h发现磁盘快满时,先用du -sh /var/log/*找出哪个日志文件最大。后续第 13 篇会专门讲日志管理。
3.3 /opt:第三方软件的标准位置
opt 是 "optional" 的缩写,专门用来存放手动安装的第三方大型软件 。包管理器安装的软件(apt/dnf)会分散在 /usr/ 下,而你手动下载的 JDK、Elasticsearch、Kafka 等,放在 /opt 是标准做法。
bash
# 查看 /opt 下通常有哪些内容(一台配置好的服务器)
ls -la /opt/
# 典型的 /opt 目录结构示例
# /opt/jdk/ → JDK 安装目录
# /opt/elasticsearch/ → Elasticsearch
# /opt/kafka/ → Kafka
# /opt/node/ → Node.js
# /opt/ollama/ → Ollama 大模型工具
3.4 /proc 和 /sys:内核的"实时仪表盘"
这两个目录很特殊------它们不是真实的磁盘目录,而是内核动态生成的虚拟文件系统,占用零磁盘空间,实时反映系统状态。
bash
# 查看 CPU 信息(实时从内核读取)
cat /proc/cpuinfo | grep "model name" | head -1
# 查看内存详情(比 free 命令更详细)
cat /proc/meminfo | head -20
# 查看系统运行时间(单位:秒)
cat /proc/uptime
# 查看某个进程的详情(以 PID 1234 为例)
ls /proc/1234/ # 每个进程都在 /proc 下有自己的目录
cat /proc/1234/cmdline # 进程启动时的完整命令行
cat /proc/1234/status # 进程状态、内存使用等
# 查看系统 TCP 连接数(对排查 Java 应用连接池泄漏很有用)
cat /proc/net/tcp | wc -l
# 通过 /sys 调整内核参数(临时生效,重启恢复)
cat /sys/block/sda/queue/scheduler # 查看磁盘 IO 调度策略
💡 生产提示 :当 Java 应用出现"Too many open files"错误,可以通过
/proc/进程PID/limits查看该进程的文件描述符限制,通过/proc/进程PID/fd目录的文件数量来确认当前打开了多少文件。这比用其他工具排查快得多。
四、绝对路径 vs 相对路径:新手必须搞清楚的区别
这是最基础也最容易让新手犯错的概念。
绝对路径 :从根目录 / 开始的完整路径,无论你当前在哪个目录,它都指向同一个位置。
相对路径:相对于当前目录的路径,会随着你所在的位置变化而变化。
bash
# 查看当前所在目录(pwd = print working directory)
pwd
# 输出:/home/ubuntu
# 用绝对路径访问文件(从 / 开始,永远有效)
cat /etc/hosts
# 用相对路径访问文件(相对于当前目录)
# 假设当前在 /home/ubuntu
cat ../../etc/hosts # ../../ 表示向上两级(到 /),再进 etc/
# 几个特殊的相对路径符号
cd ~ # 回到当前用户的主目录(~ 是主目录的简写)
cd .. # 进入上一级目录
cd - # 回到上一次所在的目录(在两个目录间快速切换非常好用)
cd . # 当前目录(. 表示当前目录,.. 表示父目录)
⚠️ 踩坑记录 :Shell 脚本里用相对路径是很危险的习惯。脚本的当前目录取决于从哪里调用它,在不同位置执行结果可能完全不同。在脚本里一定要用绝对路径 ,或者在脚本开头用
cd "$(dirname "$0")"把当前目录切换到脚本所在目录。否则 cron 定时任务执行你的脚本时,当前目录是 root 的主目录而不是脚本目录,所有相对路径都会失效。
五、Java SaaS 项目标准化部署目录规范
这是本篇最有实战价值的部分。很多项目因为没有统一的目录规范,导致部署混乱、日志难找、备份困难。以下是一套经过生产验证的 Java SaaS 项目目录规范:
bash
# 建议的标准目录结构
/opt/
└── appname/ # 应用根目录,appname 替换为实际项目名
├── current/ # 当前运行版本的 JAR 包(符号链接或实际文件)
│ └── app.jar # Spring Boot 打包后的可执行 JAR
├── releases/ # 历史版本存档(蓝绿部署时保留上一个版本)
│ ├── app-1.0.0.jar
│ └── app-1.0.1.jar
├── config/ # 外部化配置文件(不打入 JAR 包)
│ ├── application-prod.yml
│ └── application-local.yml
├── logs/ # 应用日志(建议软链到 /var/log/appname/)
├── temp/ # 临时文件(文件上传中转)
└── scripts/ # 运维脚本(启动、停止、健康检查)
├── start.sh
├── stop.sh
└── health-check.sh
/var/log/appname/ # 日志文件(系统日志规范目录)
├── app.log # 应用主日志
├── error.log # 错误日志
└── access.log # 访问日志
/var/lib/appname/ # 应用持久化数据(如本地文件存储)
用以下命令一键创建这套目录结构:
bash
# 替换 myapp 为你的实际项目名
APP_NAME="myapp"
# 创建应用目录结构
sudo mkdir -p /opt/${APP_NAME}/{current,releases,config,logs,temp,scripts}
sudo mkdir -p /var/log/${APP_NAME}
sudo mkdir -p /var/lib/${APP_NAME}
# 创建专用运行用户(不要用 root 运行 Java 应用!)
sudo useradd -r -s /sbin/nologin -d /opt/${APP_NAME} ${APP_NAME}
# 设置目录所有权(应用目录归专用用户,日志目录同样)
sudo chown -R ${APP_NAME}:${APP_NAME} /opt/${APP_NAME}
sudo chown -R ${APP_NAME}:${APP_NAME} /var/log/${APP_NAME}
sudo chown -R ${APP_NAME}:${APP_NAME} /var/lib/${APP_NAME}
# 验证目录创建结果
tree /opt/${APP_NAME}/
⚠️ 踩坑记录 :很多开发者图省事直接用 root 用户跑 Spring Boot 应用,这在安全上是非常危险的。一旦应用有安全漏洞被攻击者利用,攻击者就直接获得了 root 权限,可以对服务器为所欲为。正确做法是创建一个专用的低权限用户(上面的
useradd -r创建的是系统用户,没有 shell 登录权限,权限最小化),让应用以这个用户身份运行。Spring Boot 的 systemd 服务文件里用User=myapp指定运行用户,后续第 08 篇会详细讲。
六、AI 大模型文件的存放最佳实践
在 Linux 上部署 Ollama 或 vLLM 时,模型文件(.gguf、.safetensors 格式)动辄几 GB 到几十 GB,存放位置需要认真规划:
bash
# Ollama 默认模型存放路径(可自定义)
# 默认:~/.ollama/models/ (如果用 root 跑,在 /root/.ollama/)
# 生产环境建议独立挂载大容量磁盘到 /data 目录
# 查看 Ollama 当前模型存储位置
ls -lh ~/.ollama/models/blobs/ # 实际的模型文件(按哈希存储)
ls -lh ~/.ollama/models/manifests/ # 模型元数据
# 推荐的生产环境 AI 模型目录结构
/data/ # 独立大容量磁盘挂载点
├── models/ # 模型文件根目录
│ ├── ollama/ # Ollama 模型(通过软链或环境变量指向)
│ ├── huggingface/ # HuggingFace 格式模型(用于 vLLM)
│ │ ├── Qwen2.5-7B-Instruct/
│ │ └── DeepSeek-R1-8B/
│ └── gguf/ # llama.cpp 使用的 GGUF 格式
│ └── qwen2.5-7b-instruct.Q4_K_M.gguf
# 通过环境变量修改 Ollama 模型存储路径
export OLLAMA_MODELS=/data/models/ollama
# 永久生效(写入 /etc/environment 或 systemd 服务文件)
echo 'OLLAMA_MODELS=/data/models/ollama' | sudo tee -a /etc/environment
⚠️ 踩坑记录 :模型文件绝对不能放在系统盘根分区(
/)下。7B 参数量的模型文件大约 4-15 GB(取决于量化级别),70B 模型动辄 40-80 GB。如果不独立规划存储,很快就会把系统盘撑满,轻则应用崩溃,重则系统无法启动(根分区满了连登录都很困难)。生产环境应该单独挂载一块大容量数据盘到/data,专门用于存储模型文件和向量数据库数据。
七、实用命令速查:目录与文件操作
bash
# ===== 目录导航 =====
pwd # 显示当前路径
cd /var/log # 进入指定目录
cd ~ # 回主目录
cd - # 回上次所在目录
ls -la # 列出所有文件(含隐藏文件)详情
ls -lhS # 按文件大小排序(-S),人性化单位(-h)
tree -L 2 /opt # 显示 /opt 目录树,最多展开 2 层
# ===== 磁盘空间检查 =====
df -h # 查看各分区使用率
df -h /opt # 只查某个目录所在分区
du -sh /var/log/* # 查看 /var/log 下每个文件/目录的大小
du -sh /* 2>/dev/null # 查看根目录各一级子目录的大小(排查磁盘占用)
# ===== 文件类型查看 =====
file /bin/ls # 查看文件类型(会告诉你是 ELF 可执行文件还是脚本)
file /etc/hosts # 查看文本文件类型
readlink -f /usr/bin/python3 # 追踪符号链接的最终真实路径
八、常见问题解答(FAQ)
Q1:/usr 和 /opt 都能放软件,该选哪个?
规则是:通过包管理器(apt/dnf)安装的软件放在 /usr 下 (自动管理,不需要你操心);手动下载解压安装的大型软件放在 /opt 下 (自己管理)。比如用 apt install nginx 安装的 Nginx 在 /usr/sbin/nginx,但如果你从官网下载 Nginx 源码自己编译,建议安装到 /opt/nginx。
Q2:/tmp 和 /var/tmp 有什么区别?
/tmp 里的文件在系统重启后会被清空 (实际上是 tmpfs,存在内存里);/var/tmp 里的文件重启后保留 (存在磁盘上,但系统会定期清理超过 30 天未访问的文件)。Java 应用的文件上传临时目录不要用 /tmp,除非你能保证上传在本次会话内完成。
Q3:/proc 里的文件能删除吗?
不能也删不掉。/proc 是内核动态生成的虚拟文件系统,不占磁盘空间,对里面文件的"读写"实际是与内核交互,不存在真正意义上的删除操作。
Q4:日志为什么建议放在 /var/log 而不是应用目录下?
标准化的目录便于统一管理。运维团队通常会对 /var/log 配置 logrotate 统一轮转,配置 ELK/Loki 统一采集,配置监控告警(磁盘使用率超 80% 报警)。如果日志分散在各个应用目录,这些统一管理就很难做。
Q5:/etc 下的配置文件被误改了,怎么恢复?
这正是建议你在修改任何配置文件前先备份原文件 的原因:sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak。备份了就可以随时恢复:sudo cp /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf。如果系统层面有使用 Git 来管理 /etc(工具叫 etckeeper),可以用 git diff 和 git checkout 来查看和撤销修改。
本篇小结与系列导航
📌 核心结论 :Linux 目录结构遵循 FHS 标准,核心规律是:
/etc放配置、/var放变化数据(含日志)、/opt放手动安装软件、/proc和/sys是内核的实时镜像。Java SaaS 项目应建立标准化目录规范,应用运行在/opt/appname/,日志输出到/var/log/appname/,由专用低权限用户运行。AI 大模型文件因体积庞大,应独立挂载数据盘存储,避免占满系统盘。