很多人第一次装 Ubuntu,并不是因为"想学 Linux",而是被现实推过去的:公司服务器只给 Linux 环境、要跑的某个工具链只支持 Ubuntu、课程实验必须交 Linux 截图、或者手上那台旧笔记本想再战两年。于是打开搜索框,安装教程一抓一大把,图文齐全,跟着点下一步似乎也不难。
但真正卡人的地方,几乎从来不在安装界面。装完之后才是分水岭:软件源慢得像回到拨号时代、中文输入法切不出来、显卡驱动装完直接黑屏、sudo 密码忘了进不去、SSH 连不上服务器、Permission denied 反复出现却不知道到底谁没权限。这些问题在教程里往往被一句"然后重启即可"带过,而它们恰恰是新手从"装上了"到"用得起来"之间最宽的那条沟。
这篇文章的判断是:Ubuntu 的价值不在于"开源免费"这四个字,而在于它把 Linux 生态里最琐碎的那部分标准化了——包管理、驱动、桌面环境、社区文档、长期支持周期。标准化的代价是你要接受它的一套约定,收益是你遇到问题时有极大概率能搜到答案。所以学 Ubuntu 的正确姿势不是背命令,而是先理解它的约定,再动手。
下面从版本选择、安装前准备、完整安装流程、装后初始化、命令与目录、开发环境搭建、服务部署、排错和工程实践几个层面,把"从装系统到真能跑业务"这条路径走一遍。适合 Windows 转 Linux 的开发者、需要自己维护服务器的后端同学,以及第一次认真接触 Linux 的学生。
1. 这篇文章真正要解决的问题
先明确一件事:Ubuntu 教程在网上并不稀缺,稀缺的是"能让你在装完之后继续往下走"的那部分内容。大量教程的终点是登录桌面、看到紫色壁纸、截个图,而真实使用中的大部分时间花在终端里,花在配置、部署、排错上。如果一篇文章只教你怎么点安装向导,它解决的其实是一个几乎不存在的需求。
这篇文章要解决的是三段断层。第一段是从"安装完成"到"系统可用":软件源、更新、输入法、驱动、时间同步,这些不做完,系统是半成品。第二段是从"系统可用"到"能开发":JDK、Python、Node、Docker 这些运行时怎么装、装哪一版、路径在哪、多版本怎么共存。第三段是从"能开发"到"能上线":服务怎么托管、开机自启怎么配、防火墙怎么开、日志在哪看、出问题先查什么。这三段断层,才是 Ubuntu 新手真正的成本所在。
还有一类读者容易被忽略:手上只有一台机器,既想当日常电脑用,又想跑本地服务和容器。这类场景对 Ubuntu 的要求比纯服务器高得多——既要桌面流畅、驱动正常,又要能开 SSH、跑 Docker、做端口转发。文章后面会专门提到双系统分区、Secure Boot 和 Docker 权限这几处高频坑。
读完之后你应该能做到:独立完成一次 Ubuntu 安装(含手动分区)、完成装后初始化、用 apt 和 snap 管理软件、用 systemd 托管一个自写服务、用 Nginx 做反向代理、用 ufw 控制端口,并且在遇到 Permission denied、驱动异常、源报错时知道第一步该看哪里。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Ubuntu 的核心概念与版本选择
Ubuntu 是一个基于 Debian 的 Linux 发行版。这句话里有两个关键词值得拆开:发行版意味着它把内核、GNU 工具、桌面环境、包管理器、默认配置打包成一个可交付的整体;基于 Debian 意味着它继承了 Debian 的 .deb 包格式和 apt 包管理体系。理解这一点很重要,因为你在网上搜到的很多 Linux 命令,能不能直接用,取决于它属于 Debian 系(apt)还是 RedHat 系(yum/dnf)。
发行版之上还有一层是内核。内核负责进程调度、内存管理、设备驱动、网络栈这些底层工作,桌面只是跑在内核之上的一层图形程序。这解释了一个常见误区:很多人以为"桌面卡"是 Ubuntu 不行,实际上更可能是显卡驱动没装好或者用了开源驱动跑在核显上,和发行版本体关系不大。同理,"Ubuntu 吃内存"这个说法,多数情况下是桌面环境(GNOME)的开销,而不是内核。
版本选择上,Ubuntu 分为 LTS(Long Term Support)和普通版本两类。LTS 每两年发布一次,维护周期长;普通版本每半年发布一次,维护周期短,更适合想尝鲜的桌面用户。对绝大多数开发者和服务器场景,选 LTS 是默认答案,因为它的软件版本更稳、文档更全、第三方软件(Docker、数据库、驱动)的兼容测试也更充分。具体选哪个版本,以官方当前提供的 LTS 为准,本文不写死版本号,重点讲通用方法和配置逻辑。
| 对比项 | LTS 版本 | 普通版本 |
|---|---|---|
| 发布频率 | 每两年一次 | 每半年一次 |
| 支持周期 | 长,适合生产 | 短,适合尝鲜 |
| 软件版本 | 偏保守,经过验证 | 较新,可能不稳定 |
| 推荐人群 | 服务器、企业、长期开发 | 桌面体验、新硬件尝鲜 |
| 升级风险 | 低 | 相对高 |
另一个要区分的是 Desktop 和 Server 两个镜像。它们的核心系统完全相同,差别在外层:Desktop 带图形界面和一堆桌面应用,Server 默认无图形界面,装完直接给你一个终端。这里有个实用建议:如果你打算长期用 SSH 管理这台机器,Server 版更合适,因为少掉图形界面意味着更少的内存占用、更小的攻击面和更少的自动更新干扰。但如果你只有一台物理机,既要写代码又要查资料,Desktop 版更实际,Server 的能力可以通过装包补上。
包管理层面,Ubuntu 有三套并存的机制需要理清:dpkg 是最底层的包操作工具,处理单个 .deb 文件;apt 建立在 dpkg 之上,负责依赖解析和仓库管理,是日常主力;snap 是 Canonical 主推的容器化包格式,自带运行时、版本隔离,但启动稍慢、占用稍大。三者不是替代关系,是分层关系。新手最容易犯的错是拿 snap 装的软件去 apt 里找配置文件路径,然后发现目录结构完全不一样。
还有一组必须提前建立的概念:root 和 sudo。root 是超级用户,拥有系统内一切权限;sudo 是"以 root 身份执行单条命令"的临时提权机制。Ubuntu 默认禁用 root 直接登录,安装时创建的第一个用户属于 sudo 组,可以提权。这个设计的意义是让你每一次高危操作都要显式确认,而不是长期待在 root 下误删系统文件。后文所有涉及系统级改动的命令都会带 sudo,请理解它不是装饰。
最后是目录结构。Linux 没有 C 盘 D 盘的概念,一切从 / 开始。/etc 放配置文件,/var/log 放日志,/home 放用户数据,/opt 常放第三方软件,/usr 放系统级程序和库,/tmp 放临时文件。这个约定在排错时非常有用:服务起不来先看 /var/log,配置错了去 /etc 找,路径找不到大概率在 /usr/bin 或 /usr/local/bin。
3. 安装前的准备工作
安装 Ubuntu 最容易出问题的地方,其实在按下"安装"之前。准备工作没做好,后面全是补救。第一步是确认硬件条件:官方对桌面版有最低配置建议,但真实体感是内存 8GB 起步、磁盘留出 40GB 以上空间才够用,因为系统本身、常用软件、Docker 镜像和几个开发工具加起来很容易吃掉二三十 GB。如果打算跑容器和数据库,磁盘 100GB 以上更安心。
第二步是下载镜像。请只从官方站点或官方认可的区域镜像站下载,不要用来源不明的第三方打包版。下载完成后必须校验完整性,这一步很多人跳过,结果是安装到一半报错、或者装完出现奇怪的随机崩溃,排查半天才发现是镜像损坏。
bash复制# 计算下载文件的 SHA256 摘要
sha256sum ubuntu-xx.xx.x-desktop-amd64.iso
# 与官方页面公布的校验值逐字符比对
# 注意:不同版本、不同架构的校验值不同,请以官方发布页为准
第三步是制作启动盘。这里的原则是:用一个至少 8GB 的 U 盘,制作过程会清空盘内全部数据,提前备份。工具层面,Windows 下可以用 Rufus 或 balenaEtcher,macOS/Linux 下可以直接用 dd。用 dd 时务必确认目标设备名,写错设备名会直接抹掉你的硬盘数据,这是新手最危险的操作之一。
bash复制# 查看当前块设备,确认 U 盘对应的设备名(如 /dev/sdb)
lsblk
# 写入镜像,of= 后面必须是 U 盘设备,绝对不能是系统盘
sudo dd if=ubuntu-xx.xx.x-desktop-amd64.iso of=/dev/sdb bs=4M status=progress oflag=sync
第四步是规划分区和启动模式。现代机器基本都是 UEFI 启动,需要一块 FAT32 格式的 EFI 分区(通常 512MB 到 1GB)。如果你打算和 Windows 双系统共存,最稳妥的做法是在 Windows 里先用磁盘管理压缩出未分配空间,不要在 Ubuntu 安装器里删除 Windows 分区。同时要留意 Secure Boot:Ubuntu 对 Secure Boot 有官方签名支持,一般可以直接装,但如果后续要装第三方内核模块(比如某些显卡驱动、虚拟机加速模块),可能需要临时关闭它。
第五步,也是最重要的一步:备份。安装系统会改动分区表和引导记录,操作失误的代价是整个磁盘的数据。至少要确保重要文件有独立副本,最好在动手前把 Windows 的恢复介质准备好。如果这台机器是生产服务器,请务必确认你有带外管理权限、有数据备份、有回滚方案,并在测试环境先验证一遍流程,再动生产机。
4. Ubuntu 安装流程的完整拆解
把 U 盘插上,开机时按对应的功能键(常见是 F2、F12、Esc、Del,具体看主板品牌)进入启动菜单,选择 U 盘启动。如果机器直接进了原来的系统,多半是 Secure Boot 或启动顺序的问题,需要进 BIOS 调整。进入安装器后,第一步选语言和键盘布局,这一步影响的不只是界面语言,还会影响后续的输入法和时区默认值。
接下来是网络和更新选项。这里有个取舍:勾选"安装时下载更新"能让系统装完就是较新状态,但会显著拉长安装时间,而且如果网络不稳可能中途失败。更稳妥的做法是先不勾,装完系统后自己换源再更新,这样出问题时定位更清晰。同理,"安装第三方驱动"这一项可以在装完系统后再处理,避免驱动问题导致安装器卡死。
最关键的一步是"安装类型"。安装器通常提供几个选项:清除整个磁盘、与其他系统共存、手动分区。前两个选项省事但不可控,手动分区麻烦但最清楚。建议至少理解手动分区的逻辑,因为服务器部署几乎一定会遇到需要自定义挂载点的情况。
| 挂载点 | 建议大小 | 文件系统 | 作用说明 |
|---|---|---|---|
/boot/efi |
512MB - 1GB | FAT32 | UEFI 启动分区,必须存在 |
/ |
40GB 以上 | ext4 | 根分区,系统和软件都在这 |
swap |
内存 1-2 倍或固定 8GB | swap | 内存不足时兜底,休眠也需要 |
/home |
剩余空间 | ext4 | 用户数据,独立分区便于重装保留 |
关于 swap,这里要说明一个常见误解:swap 不是"内存不够才用"的补丁,它在休眠(hibernate)场景下是必需的,而且合理的 swap 能避免内存紧张时内核直接杀掉进程。现代机器内存大时可以适当减小 swap,但不建议完全不要。另外,如果 /home 单独分区,重装系统时可以只格式化 /,用户数据得以保留,这是长期使用中最省事的一个设计。
分区确认后是用户设置:用户名、主机名、密码。这里有两点提醒。第一,密码不要设得太简单,因为后面 SSH 登录、sudo 提权都依赖它;第二,记住这个密码,Ubuntu 默认不给 root 设密码,忘记用户密码虽然能通过恢复模式重置,但过程对新手并不友好。
等待安装完成后重启,拔出 U 盘,进入系统。如果卡在启动画面或者黑屏,先不要慌,多数情况是显卡驱动或 Secure Boot 的问题,可以在 GRUB 菜单里选择 recovery mode 或者临时加内核参数排查。第一次进入系统后,建议先做一次完整更新,再开始装其他软件,这样能避免新旧包混装导致的依赖冲突。
5. 安装完成后的系统初始化
装完系统只是拿到一个半成品,接下来的初始化决定了这个系统好不好用。第一件事是换软件源。默认源在境外,国内访问速度不稳定,换成国内镜像站能明显改善体验。注意 Ubuntu 在不同版本里配置文件格式有变化:较早的版本用 /etc/apt/sources.list 单文件,较新的版本(比如 24.04)默认使用 /etc/apt/sources.list.d/ubuntu.sources 的 deb822 格式。改之前先备份,改错了能立刻还原。
bash复制# 备份原始源配置
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
properties复制# 文件路径:/etc/apt/sources.list
# 说明:以下以 22.04(jammy)为例,实际使用时请把代号换成你系统的版本代号
# 版本代号可以用 lsb_release -cs 查看
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-security main restricted universe multiverse
yaml复制# 文件路径:/etc/apt/sources.list.d/ubuntu.sources
# 说明:deb822 格式,适用于使用新格式的版本,代号请按实际系统替换
Types: deb
URIs: https://mirrors.tuna.tsinghua.edu.cn/ubuntu/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
Types: deb
URIs: https://mirrors.tuna.tsinghua.edu.cn/ubuntu/
Suites: noble-security
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
改完源之后执行更新。这里会出现一个高频错误:源里的版本代号写错,apt update 直接报 404。遇到这种情况,第一反应应该是核对版本代号,而不是怀疑网络。
bash复制# 查看当前系统版本代号
lsb_release -cs
# 更新软件包索引并升级已安装的包
sudo apt update
sudo apt upgrade -y
第二件事是中文环境。桌面用户需要装输入法,常见方案是 Fcitx 系列配合拼音引擎。这里的关键不是装哪个包,而是环境变量配置:GTK_IM_MODULE、QT_IM_MODULE、XMODIFIERS 这三项如果没有正确设置,输入法会出现"能装不能用"或者"只有部分程序能打字"的现象。排错时优先检查这三项,而不是反复重装输入法。
bash复制# 安装中文语言支持和输入法框架(包名以发行版仓库实际提供为准)
sudo apt install -y language-pack-zh-hans fonts-noto-cjk fcitx5 fcitx5-chinese-addons
properties复制# 文件路径:~/.pam_environment 或 /etc/environment
# 说明:不同桌面环境读取位置不同,装完需要重新登录生效
GTK_IM_MODULE=fcitx
QT_IM_MODULE=fcitx
XMODIFIERS=@im=fcitx
第三件事是显卡驱动。这一步是新手最容易踩坑的地方,因为装错驱动的后果往往是重启后黑屏。稳妥的做法是先用系统自带的驱动管理工具查看推荐驱动,确认后再安装,不要一上来就手动编译。如果装完黑屏,可以在 GRUB 里进 recovery 模式卸载驱动,或者用 nomodeset 参数临时进入系统。
bash复制# 查看可用的显卡驱动和推荐项
ubuntu-drivers devices
# 安装推荐驱动(autoinstall 会根据硬件选择)
sudo ubuntu-drivers autoinstall
# 如果是 NVIDIA 独显,装完建议重启并验证
nvidia-smi
第四件事是时间同步和主机名。服务器上时间不同步会导致日志时间戳错乱、证书校验失败、分布式任务调度异常。Ubuntu 默认使用 systemd-timesyncd 做时间同步,一般不需要额外配置,但可以用命令确认状态。主机名则在多机环境中很重要,hostnamectl set-hostname 改完之后记得同步更新 /etc/hosts。
bash复制# 查看时间同步状态
timedatectl status
# 修改主机名
sudo hostnamectl set-hostname dev-node-01
6. Ubuntu 常用命令与目录操作实战
Ubuntu 的日常使用有 80% 集中在一小批命令上,把这些用熟,剩下的查文档就够。建议按"文件操作、权限管理、进程管理、网络排查、磁盘查看"五类去记,而不是按字母顺序背。
| 分类 | 命令 | 典型用途 |
|---|---|---|
| 文件 | ls -alh |
查看目录内容并显示权限和大小 |
| 文件 | cp -r / mv / rm -rf |
复制、移动、删除(rm -rf 慎用) |
| 查找 | find / grep -rn |
按文件名或内容检索 |
| 权限 | chmod / chown |
修改权限位和属主 |
| 进程 | ps aux / top / kill |
查看和终止进程 |
| 网络 | ss -lntp / curl / ping |
查看端口、发请求、测连通 |
| 磁盘 | df -h / du -sh |
查看分区占用和目录大小 |
| 服务 | systemctl status / journalctl -u |
查看服务状态和日志 |
权限是新手最容易困惑的部分。Linux 的权限分三组:文件所有者、所属组、其他用户,每组有读(r)、写(w)、执行(x)三个位。用 ls -l 看到类似 -rw-r--r-- 的字符串,就是这个含义。chmod 755 里的三个数字分别对应用户、组、其他,7 = 4+2+1 = 读写执行,5 = 4+1 = 读和执行。理解这个换算,比死记数字有用得多。
bash复制# 创建一个脚本文件并赋予执行权限
cat > hello.sh <<'EOF'
#!/usr/bin/env bash
echo "hello ubuntu"
EOF
chmod 755 hello.sh
./hello.sh
这里有一个必须强调的安全原则:不要图省事用 chmod 777 或者 chown root:root 去"解决"权限问题。777 意味着任何用户都能改这个文件,如果它是服务脚本或配置文件,等于给攻击者留了后门。正确的做法是搞清楚这个文件应该属于谁、需要什么权限,然后用最小权限原则去设置。
另一个高频场景是查找。grep -rn "关键词" /etc 能在配置目录里全文检索,find / -name "*.conf" 2>/dev/null 能按文件名找配置,ss -lntp 能看清哪个进程占了哪个端口。这几个命令组合起来,能解决大部分"我不知道东西在哪"的问题。
7. Ubuntu 开发环境搭建实战
这一节把常见的开发环境搭建串起来。原则是:能用系统包管理器装的就用包管理器,需要特定版本或者多版本共存的,用版本管理工具或容器,不要手动解压到 /usr/local 然后改 PATH,那样后期升级和维护会很痛苦。
先看 Python。Ubuntu 系统自带 Python,但那是给系统工具用的,不建议直接往里 pip install,容易和系统包冲突。更稳妥的做法是装 python3-venv 和 python3-pip,然后每个项目建独立虚拟环境。
bash复制sudo apt update
sudo apt install -y python3 python3-pip python3-venv
# 在项目目录下创建虚拟环境
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
Java 场景稍微复杂,因为生产环境通常需要指定 JDK 版本。Ubuntu 仓库里有多个 openjdk-xx-jdk 包,可以同时安装多个版本,再用 update-alternatives 切换默认版本。这是比手动配置 JAVA_HOME 更规范的做法,尤其是在同一台机器上跑多个服务时。
bash复制sudo apt install -y openjdk-17-jdk
# 注册并切换默认 JDK(数字按实际路径调整)
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-17-openjdk-amd64/bin/java 1
sudo update-alternatives --config java
java -version
Node.js 建议用版本管理工具而不是系统仓库的旧版本,因为前端工具链对 Node 版本有明确要求。用 nvm 可以在用户态安装和切换多个 Node 版本,不影响系统环境。
bash复制# 安装 nvm(脚本内容请以官方发布为准)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
source ~/.bashrc
nvm install --lts
node -v
Docker 的安装建议走官方 apt 仓库,而不是系统自带的 docker.io 包,后者版本通常偏旧。安装后有一个必知的安全点:把用户加入 docker 组等价于给这个用户 root 级权限,因为它可以直接挂载宿主机目录、启动特权容器。如果这台机器是多用户共享的,请不要随便加。
bash复制# 安装 Docker 官方仓库(命令以官方文档为准,注意按实际系统代号替换)
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 验证安装
sudo systemctl status docker
sudo docker run --rm hello-world
如果想让普通用户免 sudo 用 Docker,执行 sudo usermod -aG docker $USER 后重新登录。再次提醒:这个操作等于放大权限,只在你能信任这台机器上所有用户的前提下做。
8. 服务部署实战:从脚本到 systemd 托管
装好环境之后,下一步是把服务跑起来并且能长期稳定运行。很多教程教的是 python app.py & 或者 nohup,这些方式在测试时够用,在生产环境是隐患:进程挂了不会自动拉起、日志散落、开机不会自启、多实例管理混乱。正确做法是用 systemd 托管。
先准备一个最小可运行的服务。这里用一个 Flask 应用做示例,实际项目中换成你的技术栈即可。
python复制# 文件路径:/opt/demo/app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "hello from ubuntu service"
@app.route("/health")
def health():
return {"status": "ok"}
if __name__ == "__main__":
# 生产环境建议用 gunicorn 等 WSGI 服务器,这里仅为演示
app.run(host="127.0.0.1", port=8080)
然后写 systemd 单元文件。注意几个关键点:User 指定运行用户(不要用 root)、WorkingDirectory 指定工作目录、Restart 配置失败重启、Environment 传环境变量。改完必须 daemon-reload,否则 systemd 读的还是旧配置。
ini复制# 文件路径:/etc/systemd/system/demo.service
[Unit]
Description=Demo Flask Service
After=network.target
[Service]
Type=simple
User=demo
Group=demo
WorkingDirectory=/opt/demo
Environment=PORT=8080
ExecStart=/opt/demo/.venv/bin/python /opt/demo/app.py
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
bash复制# 重载配置、启动服务、设置开机自启
sudo systemctl daemon-reload
sudo systemctl enable --now demo.service
# 查看状态和日志
sudo systemctl status demo.service
sudo journalctl -u demo.service -f
服务起来之后,通常还需要一个反向代理把 80/443 暴露出去,并做 TLS 终止。Nginx 是最常见的选择。配置时最容易忽略的是转发头:如果不传 X-Forwarded-For 和 Host,后端拿到的全是 127.0.0.1,日志和限流都会失效。
nginx复制# 文件路径:/etc/nginx/sites-available/demo
server {
listen 80;
server_name example.com;
access_log /var/log/nginx/demo.access.log;
error_log /var/log/nginx/demo.error.log;
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_connect_timeout 5s;
proxy_read_timeout 30s;
}
}
bash复制# 启用站点并检查配置语法
sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled/demo
sudo nginx -t
sudo systemctl reload nginx
最后是防火墙。Ubuntu 默认带 ufw,上手简单。原则是默认拒绝入站、允许出站、只放行必要端口。注意一个顺序问题:如果你通过 SSH 管理这台机器,务必先放行 SSH 端口再启用防火墙,否则会把自己关在门外。
bash复制sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
9. 运行结果与效果验证
部署完成之后,要有一套明确的验证路径,而不是"看起来没报错就算成功"。第一步验证本地服务是否在监听:ss -lntp | grep 8080 应该能看到进程在 127.0.0.1:8080 上 LISTEN。第二步验证本机 HTTP 请求:curl -i http://127.0.0.1:8080/ 应该返回 200 和预期内容。第三步验证反向代理:curl -i http://127.0.0.1/(带 Host 头)应该能拿到同样的内容。第四步从外部验证:在另一台机器上 curl 域名或 IP,确认防火墙和 DNS 都没问题。
bash复制# 逐层验证,从内向外
ss -lntp | grep 8080
curl -i http://127.0.0.1:8080/
curl -i -H "Host: example.com" http://127.0.0.1/
curl -i http://example.com/health
# 查看服务日志,确认请求确实到了后端
sudo journalctl -u demo.service --since "5 min ago"
sudo tail -f /var/log/nginx/demo.access.log
如果某一步失败,排查顺序应该是从内到外,而不是从外到内。具体说:先确认进程活着(systemctl status),再确认端口在听(ss -lntp),再确认本机能访问(curl 127.0.0.1),再确认代理转发正常(nginx -t 加访问日志),最后才怀疑 DNS 和防火墙。这个顺序能帮你快速定位问题在哪一层,避免一上来就改配置、越改越乱。
10. 常见问题与排查方法
下面这张表覆盖了新手最常遇到的一批问题,建议收藏备用。每一条都按"现象、原因、排查、解决"四段式组织,方便对照。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
apt update 报 404 或 Release 文件不存在 |
源地址或版本代号写错 | 用 lsb_release -cs 核对代号,看源文件是否拼写错误 |
修正源文件中的代号和地址,重新 apt update |
| 安装后重启黑屏 | 显卡驱动不兼容或 Secure Boot 冲突 | 进 GRUB 加 nomodeset 尝试进入系统 |
恢复模式下卸载驱动,改用推荐驱动或临时关 Secure Boot |
| 输入法装了但打不出中文 | 环境变量未设置或未重新登录 | echo $GTK_IM_MODULE 检查是否为空 |
补齐三项环境变量并重启会话 |
Permission denied |
文件权限或属主不对 | ls -l 看权限位,id 看当前用户和组 |
用 chown/chmod 按最小权限修正,不要用 777 |
sudo 提示用户不在 sudoers 中 |
用户未加入 sudo 组 | groups 查看当前用户所属组 |
用已有管理员账号执行 usermod -aG sudo 用户名 |
| 服务启动失败 | 单元文件路径错误、用户无权限、端口被占 | systemctl status 加 journalctl -u 看具体错误 |
修正 ExecStart 路径和 User,用 ss -lntp 查端口占用 |
| SSH 连接被拒 | 服务未启动、防火墙拦截、密钥或密码策略 | systemctl status ssh,ufw status,ssh -v 看握手 |
启动 ssh 服务,放行 22 端口,检查 sshd_config 配置 |
| Docker 提示权限不足 | 当前用户不在 docker 组 | groups 检查 |
加入 docker 组后重新登录,或继续用 sudo |
| 磁盘空间莫名占满 | 日志文件过大、旧内核堆积、容器镜像堆积 | df -h 定位分区,du -sh /* 逐层下钻 |
清理日志、apt autoremove 删旧内核、docker system prune |
| 系统时间不对 | 时区设置错误或时间同步未生效 | timedatectl 查看时区和同步状态 |
设置正确时区,启用时间同步服务 |
这十条里,最值得单独说的是 SSH 相关。很多人在服务器上改 sshd_config,改完直接 systemctl restart ssh,如果配置有语法错误,服务起不来,当前会话虽然还在,但一断开就再也连不上。稳妥的做法是:改完先 sshd -t 检查语法,再重启;并且在改配置时保留一个已登录的会话不要关,作为兜底。另外,生产环境应该禁用 root 直接登录、优先使用密钥认证,密码认证是否保留要按实际情况评估风险。
bash复制# 检查 sshd 配置语法,确认无误再重启
sudo sshd -t
sudo systemctl restart ssh
# 查看 SSH 登录失败的具体原因
sudo journalctl -u ssh --since "10 min ago"
11. 最佳实践与工程建议
前面讲的是"怎么做",这一节讲"怎么做得能长期维护"。第一是用户与权限的最小化。日常操作使用普通用户加 sudo,服务运行使用专门的系统用户(比如 demo 用户只拥有 /opt/demo 目录),不要所有东西都跑在 root 下。这不是形式主义,一旦服务被攻破,权限边界决定了损失范围。
第二是配置管理。/etc 下的配置文件在修改前必须备份,重要的配置建议纳入版本管理或者用配置管理工具维护。手工改配置而不记录,半年后你自己都不记得当时为什么加那一行。一个简单有效的习惯是:改配置时在文件末尾用注释写上日期和原因。
第三是日志与可观测性。systemd 托管的服务日志默认进 journal,用 journalctl -u 服务名 查看。要注意 journal 会占用磁盘,长期运行需要配置保留策略,避免磁盘被日志吃满。应用日志建议单独输出到 /var/log 下并配置 logrotate 轮转。
bash复制# 限制 systemd journal 的最大磁盘占用
sudo sed -i 's/^#SystemMaxUse=.*/SystemMaxUse=500M/' /etc/systemd/journald.conf
sudo systemctl restart systemd-journald
第四是更新策略。apt upgrade 在生产机器上不应该随手执行,尤其是涉及内核和关键库的更新。更稳妥的流程是:先在测试环境验证,再在低峰期执行,执行前做快照或备份,并准备好回滚方案。对于无人值守的服务器,可以只开启安全更新,把功能更新留到计划窗口。
第五是安全基线。除了 ufw,还应该关注:SSH 是否禁用了弱算法、是否有不必要的服务在监听、系统账户是否有空密码、是否配置了登录失败锁定。这些内容不是装完系统就自动做好的,需要按需配置。
bash复制# 检查当前监听的所有端口,找出不必要的服务
ss -lntup
# 查看所有可登录用户,确认没有多余的账户
awk -F: '$3 >= 1000 && $1 != "nobody" {print $1}' /etc/passwd
第六是备份与快照。对物理机,建议在重大变更前用系统快照工具做一次全盘快照;对云主机,用云厂商的快照功能。对数据本身,遵循"至少一份异地副本"的原则。很多人在装系统、改引导、调分区时出事,靠的就是快照救回来。
第七是团队协作层面的约定。如果这是一个团队共用的环境,应该有明确的规范:谁负责更新、配置放在哪、服务命名规则是什么、端口分配有没有登记、变更是否需要评审。这些看起来是流程问题,实际上决定了这套环境三个月后是井然有序还是一团乱麻。
12. 总结与继续深入的方向
这篇内容覆盖了一条完整的路径:理解 Ubuntu 的构成和版本差异、做好安装前的准备、走完安装流程、完成装后初始化、建立命令和目录的直觉、搭起开发环境、用 systemd 和 Nginx 把服务跑起来、建立从内到外的验证顺序、以及一批常见问题的排查路径。
如果只记三件事,建议记这三件。第一,Ubuntu 的稳定来自它的标准化约定,遇到问题先想"它属于哪一层"——是包管理、是权限、是网络、还是服务托管,定位对了,解决方案通常不复杂。第二,权限和日志是排错的两把钥匙,ls -l 和 journalctl 能解决大部分"不知道为什么不行"的问题,不要急着改配置。第三,任何涉及分区、驱动、生产环境配置的操作,先备份、先测试、留后路,这条经验在 Linux 上比任何命令都值钱。
接下来的深入方向可以按需选择。想做后端服务的,建议继续研究系统资源限制、容器网络、进程与线程模型、日志采集;想做运维自动化的,可以从 Ansible、systemd 单元进阶、包管理和镜像构建入手;想深入系统本身的,从文件系统、进程调度、内核参数调优、eBPF 观测这些方向往下走。对大多数开发者来说,先把"能稳定跑起一个服务、出问题能定位"这个目标吃透,收益远大于一开始就追求各种进阶技巧。
最后留一个实用提醒:把这篇文章里用到的命令和配置片段整理成自己的笔记或脚本片段库,下次装机器的时候直接复用,会比重新搜一遍教程快得多。装系统这件事,做第二遍和做第十遍的差别,主要就体现在你有没有把自己的流程固化下来。
