包管理、环境变量与日志
一台机器长期稳定,靠的不是"记忆",而是把依赖、配置和日志各放在标准位置。
三个最小规范
| 项目 | 规范 | 违反后果 |
|---|---|---|
| 包管理 | 记录安装来源与版本,关键包锁定版本 | apt upgrade 意外升级,服务行为变化 |
| 环境变量 | 放进 systemd EnvironmentFile 或专用文件 | 服务读不到变量,或密钥暴露在进程列表 |
| 日志 | 区分 stdout/错误日志/访问日志,设置轮转 | 磁盘被日志撑满,关键错误淹没在噪声中 |
包管理实操
# 更新软件源(每次安装前)
sudo apt update
# 安装时记录版本
sudo apt install nginx=1.24.0-1~jammy
apt show nginx | grep Version # 查看当前版本
# 锁定关键包版本(防止 apt upgrade 升级)
sudo apt-mark hold nginx postgresql-15
apt-mark showhold # 查看已锁定的包
# 解除锁定
sudo apt-mark unhold nginx
# 查看已安装包
dpkg -l | grep nginx
dpkg -l | grep "^ii" # 所有已安装包
# 查看某包从哪里安装(用于排查来源)
apt-cache policy nodejs
版本记录建议(写入运维手册或 README):
# /etc/myapp/package-versions.txt(手动维护)
nginx: 1.24.0-1~jammy
postgresql-15: 15.4-1.pgdg22.04+1
nodejs: 20.11.0
环境变量:正确的注入方式
为什么 .bashrc / export 对服务无效
graph TD
A[你在 shell 中 export DB_HOST=...] --> B[只对当前 shell 会话有效]
C[systemd 启动服务] --> D[独立于登录 shell 的进程]
D --> E[不会继承 .bashrc 或手动 export 的变量]
E --> F[服务读到空值或默认值,行为异常]
正确做法:EnvironmentFile
# /etc/myapp/myapp.env(权限 640,root:app 组)
DATABASE_URL=postgresql://app:pass@localhost:5432/myapp
REDIS_URL=redis://localhost:6379
APP_ENV=production
LOG_LEVEL=info
PORT=3000
# /etc/systemd/system/myapp.service
[Service]
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/node server.js
验证服务实际读到的环境变量
# 方法 1:查看进程环境(需要 root)
PID=$(systemctl show -p MainPID myapp | cut -d= -f2)
sudo cat /proc/$PID/environ | tr '\0' '\n' | grep -E "DATABASE|PORT|APP_ENV"
# 方法 2:在应用健康检查接口中输出 APP_ENV(开发时有用)
# 不要输出 DATABASE_URL 等敏感变量!
# 方法 3:用 systemctl show 查看加载的环境文件
systemctl show myapp | grep EnvironmentFiles
日志管理实操
三类日志及其位置
| 日志类型 | 推荐位置 | 查看方式 |
|---|---|---|
| systemd 服务日志 | journald(内存+磁盘) | journalctl -u myapp |
| 应用自定义日志 | /var/log/myapp/app.log | tail -f /var/log/myapp/app.log |
| Nginx 访问日志 | /var/log/nginx/access.log | tail -f /var/log/nginx/access.log |
| Nginx 错误日志 | /var/log/nginx/error.log | tail -f /var/log/nginx/error.log |
journalctl 高频命令
journalctl -u myapp -f # 追踪实时日志
journalctl -u myapp -n 100 --no-pager # 最后 100 行
journalctl -u myapp --since "30 min ago" # 最近 30 分钟
journalctl -u myapp -b # 本次开机以来的日志
journalctl -u myapp -b -p err # 本次开机的 ERROR 级别日志
journalctl --disk-usage # journal 磁盘占用
# 多服务同时查看(排查依赖关系)
journalctl -u myapp -u postgresql --since "10 min ago"
防止 journal 撑满磁盘
# 设置上限(写入 /etc/systemd/journald.conf)
sudo tee -a /etc/systemd/journald.conf <<'EOF'
[Journal]
SystemMaxUse=500M
MaxRetentionSec=30day
EOF
sudo systemctl restart systemd-journald
常见误区
| 误区 | 正确做法 |
|---|---|
把密钥写进 ExecStart 命令行参数 | 用 EnvironmentFile;命令行参数在 ps aux 中可见 |
export DB_HOST=... 写在 .bashrc | 服务由 systemd 启动,不会读 .bashrc |
apt upgrade 全量升级后服务行为异常 | 先 apt-mark hold 锁定关键服务,再升级 |
| 日志不轮转,半年后磁盘爆满 | 配置 logrotate 或限制 SystemMaxUse |
本节执行清单
- [ ] 记录关键软件包版本,用
apt-mark hold锁定 - [ ] 把应用环境变量迁到
/etc/myapp/myapp.env(权限 640) - [ ] 用
/proc/$PID/environ验证服务实际读到的变量 - [ ] 设置 journal 大小上限(
SystemMaxUse=500M) - [ ] 确认服务日志能从
journalctl -u myapp统一查看
下一章:网络、端口与远程访问——开始建立真正的网络排障能力。