第 0 课:Homelab 从零进阶指南-Linux 服务器优化
本文最后更新于 2026-09-29,文章内容可能已过时,如需更新请留言。部分素材来自网络,若不小心影响到您的利益,请联系我删除。
写在前面
欢迎来到 Homelab 从零进阶指南。在这里,我们主要折腾科学上网、VPS 玩机、软路由组网、应用部署以及 AI 模型等各种好玩的技术。
不过,在正式开启第一课之前,咱们得先花点时间给服务器上个“防盗锁”。毕竟,谁也不想哪天一觉醒来,发现自己的服务器正悄悄在暗地里帮别人挖矿,或者成了黑客攻击别人大本营的“肉鸡”。
把地基打扎实,后面的折腾才能稳如老狗。废话不多说,咱们马上开始。
第一章:起航前的整备(创建非 Root 用户)
在 Linux 的世界里,天天顶着root(上帝)账号在系统里裸奔,就像是把核弹发射按钮搁在网吧前台。当你使用root账号登录服务器后,我们需要先创造一个普通人类账号,并赋予他“特异功能”(sudo)。
1. 创建普通用户
在终端输入以下指令,创建一个名为 operator 的船员账号(你可以把 operator 换成你喜欢的名字):
adduser operatoradduser:调用系统的添加用户脚本,系统会提示你依次输入密码和一些个人资料(直接回车跳过即可)。
2. 赋予该用户 sudo 权限(特异功能)
让这个普通船员在必要时可以行使最高权力:
usermod -aG sudo operatorusermod:修改用户的属性。
-aG sudo:将该用户追加(Append)到 sudo 权限组里。以后只要在命令前加 sudo,他就能短暂化身root。
第二章:本地生成 SSH 密钥并推送到服务器
密码这玩意儿,只要是人类想出来的就一定会被暴力破解。我们要彻底抛弃密码,用高强度非对称加密密钥来做“通行证”。
1.在【你自己的本地电脑】上生成密钥对
注意:这步是在你日常使用的电脑(Windows 的 PowerShell、Mac 终端或 Linux)上运行,而不是在服务器上!
打开你本地电脑的终端,输入:
ssh-keygen -t ed25519 -C "vps1-key" -f ~/.ssh/id_ed25519_vps1ssh-keygen:指定加密算法为现代的 ed25519(基于椭圆曲线加密,比老旧的 RSA 更安全、体积更小、运算速度飞快)。
-t ed25519:指定加密算法为 ed25519。
-C "vps1-key":备注信息(Comment),通常写个注释标签,方便你在密密麻麻的密钥文件中一眼认出这把钥匙是给谁准备的。
-f ~/.ssh/id_ed25519_vps1:指定生成的私钥文件保存的绝对路径和自定义文件名(这里把名字后缀设为了 _vps1,防止默认生成时覆盖掉其他服务器的钥匙)。
执行后会提示你按几次回车(默认路径和空密码即可)。完成后,你的本地电脑里会多出两个文件:id_rsa(私钥,绝对不能泄露)和 id_rsa.pub(公钥,可以发给全世界)。
2.多台服务器密钥管理
你只需要在本地电脑的 ~/.ssh/ 目录下创建一个名为 config 的文件(如果没有的话直接新建),然后把你的多台服务器信息像下面这样登记进去:
# --- 服务器 A(比如你的 Debian 1号) ---
Host debian-vps1
HostName 1.1.1.1
Port 52022
User operator
IdentityFile ~/.ssh/id_ed25519_vps1
# --- 服务器 B(比如你的 Debian 2号) ---
Host debian-vps2
HostName 2.2.2.2
Port 22222
User root
IdentityFile ~/.ssh/id_ed25519_vps23.把公钥发送到服务器
在本地电脑上执行以下命令,把你的公钥“贴”到远程服务器上:
ssh-copy-id -i ~/.ssh/id_ed25519_vps1.pub -p 52022 operator@vps1的IPssh-copy-id:自动将本地电脑的公钥上传并追加到远程服务器指定用户的授信白名单(~/.ssh/authorized_keys)中的专属工具。
-i ~/.ssh/id_ed25519_vps1.pub:指定(Identity)要发送的公钥文件路径(这里精准选中了我们刚才为 vps1 量身定制的 .pub 公钥,拒绝盲目发送默认钥匙)。
-p 52022:指定远程服务器当前正在监听的 SSH 端口(因为前面我们在安全加固时把默认的 22 端口改成了高位的 52022)。
operator@vps1的IP:登录凭据。以 operator(你在第一章创建的普通船员账号)的身份,去敲响 vps1的IP 这台远程服务器的大门。
(执行后需要输入一次你在第一章为 operator 设置的密码,之后登录就再也不需要密码了)。
4.登录服务器
ssh debian-vps1第三章:SSH 深度加固
现在我们已经有了安全钥匙,接下来要冷酷地把所有企图用密码硬撬大门的黑客拒之门外。
使用具有 sudo 权限的用户登录服务器,编辑 SSH 配置文件:
sudo nano /etc/ssh/sshd_configsudo:借用管理员权限。
nano:Linux 下最直观的文本编辑器(新手友好,按 Ctrl + O 保存,Ctrl + X 退出)。
在文件中找到(或直接在末尾追加)以下关键参数并修改:
# 1. 移花接木:把默认的 22 端口改成一个冷门高位端口(比如 52022),避开全网无差别的自动化扫描
Port 52022
# 2. 斩断上帝之手:绝对不允许 root 账号直接远程 SSH 登录
PermitRootLogin no
# 3. 闭门羹:关闭密码认证,只认钥匙不认人
PasswordAuthentication no
PubkeyAuthentication yes
PermitEmptyPasswords no
# 4. 防线加固:限制最大尝试次数,防止被连续恶意撞库
MaxAuthTries 3保存后退出,输入命令来重启ssh服务
sudo systemctl restart ssh重启之后不要急着退出会话,新启一个会话验证ssh服务是否能通过新的配置连接。
第四章:自动安全更新
服务器上线后,最容易被忽略的就是打补丁。漏洞一公布,全网的扫描器几小时内就会开工,而我们不可能天天盯着更新。那就请一位"自动值班员"来帮忙。
1.unattended-upgrades
1.安装并启用
sudo apt update
sudo apt install unattended-upgrades apt-listchanges -y
# 交互式启用(弹窗里选 Yes)
sudo dpkg-reconfigure -plow unattended-upgradesunattended-upgrades:无人值守升级工具,默认只自动安装来自 Debian 官方源(含安全源)的更新。
apt-listchanges:升级时显示变更说明(可选,不装也行)
启用后会生成 /etc/apt/apt.conf.d/20auto-upgrades,内容应该是
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";2.按需调整
编辑 /etc/apt/apt.conf.d/50unattended-upgrades,找到下面几项并去掉行首的 //:
// 自动清理不再需要的依赖
Unattended-Upgrade::Remove-Unused-Dependencies "true";
// 自动清理旧内核,防止 /boot 被塞满
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
// 内核更新后是否自动重启(默认关闭)
Unattended-Upgrade::Automatic-Reboot "false";内核和 glibc 这类更新要重启才会真正生效。跑着业务的机器建议保持 false,自己挑时间重启,用 ls /var/run/reboot-required 就能看到是否需要重启。
3.验证
# 模拟运行,不会真的安装
sudo unattended-upgrade --dry-run --debug
# 查看定时器是否在工作
systemctl list-timers 'apt-daily*'
# 查看执行日志
sudo tail -n 50 /var/log/unattended-upgrades/unattended-upgrades.log注意:Docker 来自第三方仓库,不在自动更新范围内,需要手动 sudo apt update && sudo apt upgrade。这样也不会出现容器被意外重启的情况。
4.自动重启
Debian 自带的 unattended-upgrades 已经支持"内核更新需要重启时,在指定时间自动重启"。
# 编辑
sudo nano /etc/apt/apt.conf.d/50unattended-upgrades
# 找到并修改这几项(去掉行首的//)
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";
Unattended-Upgrade::Automatic-Reboot-WithUsers "false";Automatic-Reboot:升级后如果存在 /var/run/reboot-required,才会重启。没有内核等关键更新时,不会白重启。
Automatic-Reboot-Time:在这个时刻重启,而不是升级完立刻重启。时间按系统时区算,先用 timedatectl 确认时区对不对。
Automatic-Reboot-WithUsers:设为 false,有用户登录时不重启,避免把你正在操作的会话踢掉。
2.needrestart
apt upgrade 只替换磁盘上的文件,已经在运行的进程仍然用着内存里的旧版本,直到它们被重启。也就是说,补丁打完了,漏洞可能还开着。needrestart 就是补丁打完之后来复查的那位巡检员,它会告诉你哪些进程还在用旧库、内核有没有新版本等着重启。
1.安装与手动检查
sudo apt install needrestart -y
# 手动扫描一次
sudo needrestart输出大致分三块:
Running kernel:当前内核是否已是最新。提示 "Pending kernel upgrade" 就说明装了新内核但还没生效,需要重启整台机器。
Services:哪些服务还在用旧库,需要重启。
Sessions:哪些用户会话需要重新登录。
只想看结果、不想弹窗,加 -r l(list):
sudo needrestart -r l2.升级时它会自动出现
安装后,needrestart 会挂在 apt 的 dpkg 钩子上,每次 apt upgrade 结束时自动运行。交互式终端里会弹出一个选择窗口:"Daemons using outdated libraries":
方向键移动,空格勾选/取消,Tab 切到 OK,回车确认。
勾选的服务会被立刻重启,不勾就跳过,之后随时可以手动再跑。
3.控制它的行为(重点)
配置文件是 /etc/needrestart/needrestart.conf,不要直接改它,在 conf.d 下新建自己的文件,升级软件包时不会被覆盖:
sudo nano /etc/needrestart/conf.d/99-local.conf# 重启模式:l = 只列出不重启,i = 交互询问,a = 自动重启
$nrconf{restart} = 'l';
# 在 i / a 模式下,永远不重启这些服务(按服务名做正则匹配)
$nrconf{override_rc}{qr(^sing-box)} = 0;
$nrconf{override_rc}{qr(^nginx)} = 0;为什么建议设成 l:在没有交互终端的场景(比如 unattended-upgrades 在半夜触发的升级),needrestart 的行为取决于版本和环境,有些系统上会直接自动重启受影响的服务。像 sing-box 这种在给你转发流量的服务,被半夜重启就会断连接。设成 l 之后它只提示,什么时候重启由你决定。
注意:override_rc 只在 i 和 a 模式下起作用,l 模式本来就不会重启任何东西,用不着。想在 a 模式下"自动重启大部分服务,但排除几个关键的",再用它。
4.日常工作流
sudo apt update && sudo apt upgrade -y
sudo needrestart -r l然后按提示处理:
# 逐个重启需要更新的服务(把服务名换成实际的)
sudo systemctl restart nginx
# 提示需要重启内核时,挑个没人用的时间
sudo reboot别忘了 SSH:如果 ssh.service 出现在列表里,重启它不会断开已有连接,但保险起见先另开一个会话确认能登录,再操作。
5.给脚本和监控用的批处理模式
sudo needrestart -b输出是机器可读的格式,类似:
NEEDRESTART-VER: 3.6
NEEDRESTART-KCUR: 6.1.0-28-amd64
NEEDRESTART-KEXP: 6.1.0-30-amd64
NEEDRESTART-KSTA: 3
NEEDRESTART-SVC: nginx.serviceKSTA 为 1 表示内核已是最新,2 或 3 表示有待生效的内核更新,0 表示无法判断;每一行 SVC 是一个需要重启的服务。可以简单过滤:
sudo needrestart -b | grep -E 'KSTA|SVC'关于 Docker 容器:needrestart 主要检查宿主机上的进程,容器里的库版本它管不了。容器要靠拉取新镜像并重建来更新:
docker compose pull
docker compose up -d第五章:时间同步
时间不准的后果比想象中严重:TLS 证书校验失败、二步验证码对不上、日志时间线错乱,CrowdSec 分析日志也会受影响。
1.先看现状
timedatectl重点看两行:System clock synchronized: yes 和 NTP service: active。如果都正常、时区也对,这章可以直接跳过,很多镜像已经配好了。
2.设置时区并开启同步
# 查看可用时区
timedatectl list-timezones | grep Asia
# 设置时区(按需修改)
sudo timedatectl set-timezone Asia/Shanghai
# 开启 NTP 同步
sudo timedatectl set-ntp true如果提示找不到 NTP 服务,说明系统里没有同步组件,看下一步安装 chrony。
3.进阶:换成 chrony
sudo apt install chrony -y
# 查看同步状态
chronyc tracking
# 查看时间源
chronyc sources -v安装 chrony 会自动顶替 systemd-timesyncd,两者只能留一个。chronyc tracking 里的 System time 显示当前偏差,sources 里带 ^* 的是正在使用的时间源。
注意:OpenVZ / LXC 这类容器型 VPS 无法自己修改系统时间,同步由宿主机负责,这一章对它们没用。
第六章:日志瘦身
日志是"温水煮青蛙"式的杀手,平时无声无息,某天磁盘突然 100%,服务全挂。小硬盘的 VPS 尤其要注意。
1.限制 journald 日志大小
新建一个 drop-in 配置,不去动主配置文件:
sudo mkdir -p /etc/systemd/journald.conf.d
sudo nano /etc/systemd/journald.conf.d/99-limit.conf
# 写入
[Journal]
SystemMaxUse=200M
RuntimeMaxUse=100M
MaxRetentionSec=1month
# 生效并查看占用
sudo systemctl restart systemd-journald
# 查看当前日志占用
journalctl --disk-usage
# 立即清理到指定大小(一次性)
sudo journalctl --vacuum-size=200MSystemMaxUse:持久化日志(/var/log/journal)最多占用的空间。
RuntimeMaxUse:内存日志(/run/log/journal)的上限,没开持久化的系统走这一项。
MaxRetentionSec:日志最长保留时间。
第七章:DNS 优化
DNS 慢的时候,apt update、拉镜像、访问域名都会莫名"卡一下",很多"网速慢"其实是解析慢。
1.先测再改
sudo apt install bind9-dnsutils -y
# 当前默认 DNS 的耗时
dig example.com | grep "Query time"
# 对比指定 DNS 的耗时
dig @1.1.1.1 example.com | grep "Query time"
# 看当前用的是谁
cat /etc/resolv.conf几十毫秒以内都算正常,动辄几百毫秒或者超时才需要优化。VPS 商家自带的内网 DNS 往往又快又稳,别盲目替换,测出来更慢再换。
2.systemd-resolved:本地缓存 + 指定上游
sudo apt install systemd-resolved -y
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo nano /etc/systemd/resolved.conf.d/99-dns.conf
# 写入
[Resolve]
DNS=1.1.1.1 8.8.8.8
FallbackDNS=9.9.9.9
DNSOverTLS=opportunistic
Cache=yesDNS:首选上游。海外机房可以用 1.1.1.1、8.8.8.8,国内机房换成 223.5.5.5、119.29.29.29 更合适。
FallbackDNS:首选都不可用时的后备。
DNSOverTLS=opportunistic:能加密就加密,不支持就自动退回普通 DNS,不会把解析搞挂。
Cache=yes:开启本地缓存,重复域名直接本机应答。
然后切换:
# 先备份(-a 会保留软链接属性)
sudo cp -a /etc/resolv.conf /etc/resolv.conf.bak
sudo systemctl enable --now systemd-resolved
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved操作前保持一个 SSH 会话不要关,万一解析挂了还能回滚:
sudo mv -f /etc/resolv.conf.bak /etc/resolv.conf3.验证
resolvectl status
resolvectl query example.com
resolvectl statistics
# 连续查两次,第二次应该是 0 msec,SERVER 是 127.0.0.53
dig example.com | grep -E "Query time|SERVER"resolvectl statistics 里的 Cache Hits 会随着查询增加,说明缓存在工作。
踩坑提醒:
如果你以后要在这台机器上跑 AdGuard Home、Pi-hole、dnsmasq,它们要占用 53 端口,会和 resolved 冲突。此时在 99-dns.conf 里加一行 DNSStubListener=no,或者干脆不装 resolved。
网络由 cloud-init 或 DHCP 管理的机器,重启后 resolv.conf 可能被改回去,改完记得重启一次再验证。
Docker 容器在创建时读取宿主机的 DNS 配置,换 DNS 后已运行的容器需要重启才会生效。
第八章:禁ping(可选)
黑客在扫描肉鸡时,通常会先用 ping(ICMP 协议)试探目标主机在不在。开启“隐身模式”可以让你的服务器在赛博空间里直接化身成“幽灵”。
编辑系统的内核控制配置文件:
sudo nano /etc/sysctl.d/99-ping-block.conf
# 忽略所有来自外部的 ICMP Echo 请求(也就是禁止别人 ping 你)
net.ipv4.icmp_echo_ignore_all = 1
# 让配置立刻生效
sudo sysctl --system第九章:UFW防火墙
1.安装ufw
# 更新软件源
sudo apt update
# 安装ufw防火墙
sudo apt install ufw -y2.ufw默认设置
# 入站流量全部禁止
ufw default deny incoming
# 出站流量全部放行
ufw default allow outgoing3.ufw开放端口
# 开放ssh端口
ufw allow 52022/tcp comment 'ssh'
# 开放http和https默认端口
ufw allow http comment 'http'
ufw allow https comment 'https'4.ufw常用命令
# 启动防火墙
ufw enable
# 关闭防火墙
ufw disable
# 重载防火墙(修改防火墙配置文件后)
ufw reload
# 查看防火墙规则
ufw status numbered
# 删除防火墙规则
ufw delete 3
# 只允许1.1.1.1访问52022端口
ufw allow from 1.1.1.1 to any port 52022 comment 'ssh'5.ufw优化
5.1解决ufw防火墙和docker服务冲突
修改ufw配置文件/etc/ufw/after.rules,在文末尾添加如下规则,更改文件后重启ufw,如未生效,请重启服务器
# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
-A DOCKER-USER -j RETURN
-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP
COMMIT
# END UFW AND DOCKER如果容器端口为:-p 8080:80,应该使用容器端口80,而非主机端口8080。如果有多个容易,服务端口都为80,但只希望外网访问某个容器,如果容器的私有地址是127.17.0.2,命令如下:
ufw route allow proto tcp from any to 172.17.0.2 port 805.2ufw配置文件优化
修改ufw配置文件/etc/default/ufw,找到IPT_SYSCTL并删除/ufw
# 修改前
IPT_SYSCTL=/etc/ufw/sysctl.conf
# 修改后
IPT_SYSCTL=/etc/sysctl.conf第十章:crowdsec
1.crowdsec安装
CrowdSec 是云原生时代的开源安全威胁检测系统,它能协作全球威胁情报,且完美兼容 nftables/iptables 与 Docker。
# 添加官方仓库
curl -s https://install.crowdsec.net | sudo sh
sudo apt update
sudo apt install crowdsec -y
# 安装引擎 + 防火墙 bouncer(二选一:iptables 或 nftables)
sudo apt install crowdsec crowdsec-firewall-bouncer-nftables
# 装完后检查
sudo cscli metrics # 查看日志读取、解析情况(最重要的自检命令)
sudo cscli collections list # 已安装的规则集合
sudo cscli bouncers list # bouncer 是否已注册并在线
sudo cscli decisions list # 当前封禁列表
sudo cscli alerts list # 历史告警可选:注册 app.crowdsec.net 控制台,网页查看告警:
sudo cscli console enroll <你的key>2.crowdsec使用
CrowdSec 装好后,平时主要通过 cscli(CrowdSec Command Line Interface)进行管理:
# 常用命令
sudo cscli hub update && sudo cscli hub upgrade # 更新规则
sudo cscli collections install crowdsecurity/nginx # 安装nginx插件
sudo cscli decisions add --ip 1.2.3.4 --duration 10m # 手动封禁(测试用)
sudo cscli decisions delete --ip 1.2.3.4 # 解封
sudo cscli explain --file /var/log/nginx/access.log --type nginx # 调试:日志为什么没被识别可以添加白名单,避免把自己封了,单台云服务器不是很有必要。创建 /etc/crowdsec/parsers/s02-enrich/mywhitelists.yaml:
# /etc/crowdsec/parsers/s02-enrich/mywhitelists.yaml
name: me/whitelists
description: "public server whitelist"
whitelist:
reason: "my admin ips"
ip:
- "203.0.113.10" # 你家/公司的固定公网出口 IP,没有固定 IP 就不要写
# cidr: # 有固定的办公网段再写,不要写宽泛的段
# - "198.51.100.0/24"
# 重载服务
sudo systemctl reload crowdsec3.日志采集配置
采集源在 /etc/crowdsec/acquis.yaml 或 /etc/crowdsec/acquis.d/*.yaml 中配置:
# 文件日志
filenames:
- /var/log/nginx/*.log
labels:
type: nginx
---
# SSH(journald 方式)
source: journalctl
journalctl_filter:
- "_SYSTEMD_UNIT=ssh.service"
labels:
type: syslog再安装对应 collection,常见如 crowdsecurity/linux(含 sshd)、nginx、apache2、mysql、traefik 等,可在 hub.crowdsec.net 搜索。改完执行 sudo systemctl restart crowdsec,再用 cscli metrics 确认 "Acquisition Metrics" 里有读取行数。
4.监控端口
ufw 会把被拒绝的包以 [UFW BLOCK] 前缀记录,日志在 /var/log/ufw.log 或 kern.log。
sudo ufw logging low加入 acquis 的写法和文件采集一样:
# /etc/crowdsec/acquis.d/firewall.yaml
filenames:
- /var/log/kern.log
labels:
type: syslog不要同时写 ufw.log 和 kern.log,同一条日志会被重复计数。
Debian/Ubuntu 默认的 acquis 已经采集了 /var/log/syslog,内核日志可能已经在里面了,先看 cscli metrics 里有没有读到。
没有 rsyslog、内核日志只在 journal 里的系统,用 journald 方式:
source: journalctl
journalctl_filter:
- "_TRANSPORT=kernel"
labels:
type: syslog5.Docker 服务日志监控
CrowdSec 支持 docker 数据源,直接通过 Docker socket 读取容器 stdout 日志:
# /etc/crowdsec/acquis.d/docker.yaml
source: docker
container_name:
- nginx
- traefik
labels:
type: nginx
---
source: docker
container_name_regexp:
- "^app-.*"
labels:
type: syslog注意:labels.type 必须和该服务日志格式对应(nginx 写 nginx,traefik 写 traefik,否则解析不了)。不同类型的容器要分成不同的块。运行 CrowdSec 的用户需要有访问 /var/run/docker.sock 的权限。
另一种做法是让容器把日志写到挂载卷(如 /var/log/nginx),然后用普通 filenames 方式采集,更稳定,也方便保留日志。
5.1防火墙 bouncer 默认拦不住 Docker 发布的端口
Docker 映射的端口流量走的是 FORWARD/DOCKER-USER 链,而不是 INPUT 链。需要修改 /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml:
nftables_hooks:
- INPUT
- FORWARD(也可以只针对 Docker 使用 DOCKER-USER,具体以你所用版本的官方文档为准。)改完重启 crowdsec-firewall-bouncer。或者干脆在反向代理层使用 nginx/Traefik bouncer,在应用层拦截。
5.2真实客户端 IP
如果前面有 Cloudflare、CDN 或反向代理,日志里记录的可能是代理 IP,会导致误封或封不到人。需要让代理正确传递 X-Forwarded-For/X-Real-IP 并在日志格式中记录真实 IP。
第十一章:docker
1.安装docker
# 1. 卸载旧版本(如果有的话)
sudo apt-get remove -y docker docker-engine docker.io containerd runc
# 2. 设置 Docker 官方 apt 仓库
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# 3. 安装 Docker 引擎以及最新的 Docker Compose 插件
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 4. 验证安装(注意:Compose 现在推荐用空格形式)
docker --version
docker compose version2.docker常用命令
# 启动一个或多个已经被停止的容器
docker start <容器名或ID>
# 优雅停止一个或多个运行中的容器(发送 SIGTERM 信号)
docker stop <容器名或ID>
# 重启容器
docker restart <容器名或ID>
# 删除已经停止的容器(加 -f 可强制删除运行中的)
docker rm <容器名或ID>
# 查看当前正在运行的容器列表
docker ps
# 查看所有容器(包括已停止的)
docker ps -a
# 查看容器的实时日志输出(-f 实时追踪,--tail=100 最后100行)
docker logs -f --tail=100 <容器名>
# 查看容器的详细配置和底层元数据(JSON格式)
docker inspect <容器名>
# 实时查看容器的 CPU、内存、网络等资源占用情况
docker stats
# 查看容器内部正在运行的进程
docker top <容器名>
# 进入运行中的容器内部执行交互式终端
docker exec -it <容器名> bash
# 在宿主机与容器之间复制文件或目录
docker cp local_file.txt <容器名>:/app/
# 查看本地所有已下载的镜像列表
docker images
# 从远程仓库(如 Docker Hub)下载镜像
docker pull ubuntu:22.04
# 根据 Dockerfile 构建自定义镜像
docker build -t my-image:1.0 .
# 删除本地的镜像
docker rmi <镜像ID或仓库名>
# 将镜像打包保存为 tar 文件
docker save -o my-image.tar my-image:1.0
# 从 tar 文件导入镜像
docker load -i my-image.tar
# 清理所有处于停止状态的容器、无用镜像和未使用的网络
docker system prune
# 连同未使用的镜像和数据卷一并清理(生产环境慎用)
docker system prune -a --volumes
# 注:如果你使用的是现代版,请用 "docker compose"(带空格)也行
# 在后台构建、(重新)创建、启动 yml 文件中定义的所有服务
docker-compose up -d
# 停止并删除由 up 创建的容器、网络等资源
docker-compose down
# 查看当前编排项目中的容器运行状态
docker-compose ps
# 查看所有或指定服务的日志
docker-compose logs -f <服务名>
# 重启指定服务
docker-compose restart <服务名>3.Docker 日志轮转
Docker 默认的 json-file 日志驱动不限制大小,一个话痨容器能把磁盘写爆。编辑 /etc/docker/daemon.json(不存在就新建,已存在则合并进去,别直接覆盖):
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
},
"live-restore": true,
"no-new-privileges": true,
"userland-proxy": false,
"default-address-pools": [
{ "base": "172.30.0.0/16", "size": 24 }
],
"default-ulimits": {
"nofile": { "Name": "nofile", "Hard": 65536, "Soft": 65536 }
}
}max-size:单个日志文件最大 10MB。
max-file:最多保留 3 个,也就是每个容器最多占 30MB。
重点:这个配置只对新创建的容器生效,已有容器需要重建。配置完后还需要重启docker
sudo systemctl restart docker第十二章:内核超频与 BBR 加速
1.开启 Google BBR 拥塞控制算法
BBR 可以把高延迟、高丢包的惨烈网络直接优化成丝滑的高速公路。 将配置写入专属文件:
echo "net.core.default_qdisc=fq" | sudo tee /etc/sysctl.d/99-bbr.conf
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.d/99-bbr.conf
# 让内核加载新参数
sudo sysctl --system
# 验证是否成功
sysctl net.ipv4.tcp_congestion_control
# 如果输出 net.ipv4.tcp_congestion_control = bbr,说明超频成功,你的网络引擎已经点燃tee:把标准输入的内容写入文件的同时输出到屏幕。
fq:Fair Queue(公平队列),是 BBR 算法能够完美运行的黄金搭档。
2.内存防爆盾——Swap
当物理内存小于2g时,Swap可以设置为内存的 2 倍
当物理内存在2 GB ~ 8 GB时,Swap可以设置为等于内存大小
当物理内存大于8 GB时,Swap设置在0.5倍-8g之间,最大不要超过8g,设置再大纯属浪费空间
# 创建swap空间,如果想设置成2g,就把1024000改成2048000
dd if=/dev/zero of=/swapfile bs=1024 count=1024000
# 制作为Swap文件
mkswap /swapfile
# 让Swap文件生效
swapon /swapfile
# 添加至/etc/fstab
nano /etc/fstab
# 在文本的最后添加
/swapfile swap swap defaults 0 0当物理内存(RAM)被大应用(如数据库或各种编译任务)吃光时,Linux 默认会直接触发 OOM Killer(内存海啸),粗暴杀死你的关键进程。我们需要调校 Swap(虚拟内存),并在内存见底时有尊严地缓冲。
# 编辑系统的内存调度配置
sudo nano /etc/sysctl.d/99-swappiness.conf
# 写入
vm.swappiness = 40
# 内核在回收匿名页和文件缓存之间的倾向权重,值越低越倾向保留内存里的数据
vm.vfs_cache_pressure = 50
# 内核回收目录项和 inode 缓存的倾向,设为 50 表示更倾向保留这些缓存
# 应用生效
sudo sysctl --system第十三章:zram——给内存上"压缩包"
zram 会在内存里划出一块区域当 swap,写进去的数据会被实时压缩,通常能压到原来的一半到三分之一。相当于把内存"压缩打包",小内存机器能多撑一会儿,而且不占硬盘 IO,比磁盘 swap 快得多。
1.安装与配置
sudo apt install zram-tools -y
sudo nano /etc/default/zramswap写入或修改:
ALGO=zstd
PERCENT=50
PRIORITY=100ALGO:压缩算法。zstd 压缩率高,lz4 更快更省 CPU。
PERCENT:zram 设备容量占物理内存的百分比(按压缩前的大小算)。
PRIORITY:swap 优先级,数值越大越先用。磁盘 swap 默认是 -2,所以 zram 会被优先使用。
不同版本的 zram-tools 配置项可能略有差异,以文件里的注释为准。
sudo systemctl restart zramswap2.验证
zramctl
swapon --show
free -h正常情况下 swapon --show 会同时看到 /dev/zram0(优先级 100)和第八章创建的 /swapfile。zram 在前面顶着,磁盘 swap 当后备仓。
3.和第十二章的搭配
用了 zram 之后,swappiness 不必压得太低,把冷数据压缩换出比直接丢掉文件缓存更划算,可以先用默认值 60 观察一阵。如果你保留了第十二章的 99-swappiness.conf,记得把行尾注释挪到单独一行,sysctl 不认行尾注释。
注意:LXC / OpenVZ 容器型 VPS 通常不能用 zram,可以先用 sudo modprobe zram 试试,报错就说明不支持。
第十四章:提高文件描述符上限
Linux 里万物皆文件,每个网络连接、每个打开的文件都占一个文件描述符(fd)。单个进程默认只能开 1024 个左右,跑代理节点、反向代理、数据库时,并发一高就会看到 Too many open files。
1.先看现状
# 当前 shell 的上限
ulimit -n
# 系统级总上限
cat /proc/sys/fs/file-max
# 某个运行中服务的实际上限(把 nginx 换成你的服务名)
grep "open files" /proc/$(systemctl show -p MainPID --value nginx)/limits现代内核的 fs.file-max 会按内存自动计算,通常已经很大,一般不用动。真正卡脖子的是单个进程的上限。
2.给 systemd 服务提高上限(最常用)
sudo systemctl edit nginx在打开的编辑器里写入:
[Service]
LimitNOFILE=65535保存后重启服务,再用上一步的命令验证:
sudo systemctl restart nginxsystemctl edit 会创建 override.conf 覆盖文件,不改原始服务文件,软件升级也不会把你的配置冲掉。很多安装脚本已经设置过这一项,先用 systemctl show nginx -p LimitNOFILE 看看再决定要不要改。
3.Docker 容器
在 compose 里写:
services:
app:
ulimits:
nofile:
soft: 65535
hard: 65535docker run 则用 --ulimit nofile=65535:65535。
4.登录会话(可选)
如果你在 SSH 会话里手动跑程序,可以编辑 /etc/security/limits.d/99-nofile.conf:
* soft nofile 65535
* hard nofile 65535
root soft nofile 65535
root hard nofile 65535这个文件只对通过 PAM 登录的会话有效,对 systemd 服务无效,服务请用第 2 步。另外单进程上限不能超过内核参数 fs.nr_open(默认 1048576)。
第十五章:连接跟踪与连接队列
UFW、iptables、Docker 的 NAT 都依赖内核的 conntrack(连接跟踪)表来记录每一条连接。表一旦满了,新连接就会被无情丢弃,表现为偶发超时。
原则:先诊断,没症状就别改。
1.先诊断
# 当前跟踪的连接数 / 上限
cat /proc/sys/net/netfilter/nf_conntrack_count
cat /proc/sys/net/netfilter/nf_conntrack_max
# 有没有因为表满而丢包
sudo dmesg | grep -i conntrack
# 连接队列有没有溢出
nstat -az TcpExtListenOverflows TcpExtListenDrops
# 连接总览
ss -s如果前两个文件不存在,说明 conntrack 模块没加载(没用防火墙和 NAT),这章可以跳过。count 长期接近 max,或者 dmesg 里出现 table full, dropping packet,才需要调大。ListenOverflows 持续增长,说明连接队列不够用。
2.调整参数
# 让模块开机先加载,避免 sysctl 启动时找不到参数
echo "nf_conntrack" | sudo tee /etc/modules-load.d/nf_conntrack.conf
sudo nano /etc/sysctl.d/99-network-tuning.conf写入:
net.netfilter.nf_conntrack_max = 262144
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096sudo sysctl --system
# 验证
sysctl net.netfilter.nf_conntrack_max net.core.somaxconnnf_conntrack_max:连接跟踪表上限。默认值按内存自动计算,小内存机器常见只有几万。每条记录会占用几百字节内存,调大意味着多吃内存,小 VPS 别贪多,表满了再翻倍。
somaxconn:全连接队列的上限。新内核(5.4+)默认已经是 4096,先用 sysctl net.core.somaxconn 看看,已经够大就不用改。应用自己 listen 的 backlog 也要够大,比如 nginx 要写 listen 80 backlog=4096;。
tcp_max_syn_backlog:半连接队列上限。
注意:容器有自己独立的网络命名空间,宿主机的 net.core.* 不会自动继承到容器里,需要在 compose 里单独设置:
services:
app:
sysctls:
- net.core.somaxconn=4096顺带一提,网上常见的 net.ipv4.tcp_fastopen = 3 需要客户端和服务端同时支持,个别网络设备还会拦截,收益有限,遇到玄学连接问题可以先把它关掉。别一次性抄一大堆参数,改一项测一项。
第十六章:Lynis:服务器的"体检报告"
Lynis 是一款开源的安全审计工具,会检查系统配置、内核参数、SSH、防火墙、软件包、文件权限等几百个项目,最后给出警告、建议和一个加固指数。它只检查,不修改,所有改动都要你自己来。
1.安装
最省事的是直接从系统仓库装:
sudo apt install lynis -y
lynis show version仓库里的版本往往比较旧,旧版本的检测项会滞后(你的报告里第一条就提示版本超过 4 个月)。想用最新版,可以用官方 Git 仓库:
sudo apt install git -y
git clone --depth 1 https://github.com/CISOfy/lynis.git
sudo chown -R 0:0 lynis
cd lynis
sudo ./lynis audit systemchown -R 0:0:让目录归 root 所有,否则 Lynis 会因为"文件不属于 root"而警告并拒绝完整运行。以后升级,在目录里执行 git pull 即可。此外,CISOfy 也提供官方 apt 仓库,添加步骤请以官方文档为准。
2.运行审计
sudo lynis audit system --quick--quick:每个阶段结束后不再停下来等你按回车,一口气跑完。
审计结束后,屏幕最下方是汇总:Warnings(警告)、Suggestions(建议)、Hardening index(加固指数)。
3.报告文件在哪、怎么查
屏幕上的内容一滚就没了,完整结果保存在两个文件里:
# 详细日志(每项测试的过程和结果)
sudo less /var/log/lynis.log
# 机器可读的报告(警告、建议、各类系统信息)
sudo less /var/log/lynis-report.dat常用的提取命令:
# 只看警告和建议
sudo grep -E "^(warning|suggestion)\[\]" /var/log/lynis-report.dat
# 只看加固指数
sudo grep hardening_index /var/log/lynis-report.dat
# 查看某条建议的详细说明(换成你的编号)
sudo lynis show details SSH-7408show details 会告诉你这条建议检查的具体对象,比如 KRNL-6000 究竟是哪些 sysctl 参数不达标、FILE-7524 是哪些文件权限偏松,处理之前一定要先看它。
4.怎么读结果
不会读的人可以把告警和建议丢给AI让它告诉你哪些是必须修的,哪些是建议修,哪些是不用修的。