Linux第02篇:文件系统全景图——一切皆文件的哲学与 Java SaaS 部署目录规范

本文导读 :很多 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 diffgit checkout 来查看和撤销修改。


本篇小结与系列导航

📌 核心结论 :Linux 目录结构遵循 FHS 标准,核心规律是:/etc 放配置、/var 放变化数据(含日志)、/opt 放手动安装软件、/proc/sys 是内核的实时镜像。Java SaaS 项目应建立标准化目录规范,应用运行在 /opt/appname/,日志输出到 /var/log/appname/,由专用低权限用户运行。AI 大模型文件因体积庞大,应独立挂载数据盘存储,避免占满系统盘。