Linux查看进程占用内存最高,kill进程方法

前言

服务器内存吃紧、Swap被刷爆,往往是某个程序内存泄漏或卡死进程占着不放。要先找到"谁在吃内存",确认后再决定是优雅重启还是直接kill。本篇给出从查进程到杀进程的完整命令。

一、按内存占用排序找进程

ps 一次性列出并排序,输出RES(实际物理内存,单位KB):

# 按内存占用从高到低,取前10
ps aux --sort=-%mem | head -10
# 按CPU占用排序
ps aux --sort=-%cpu | head -10

列说明:USER 进程用户、%MEM 内存占比、RSS 实际物理内存、COMMAND 启动命令。

更直观的内存排行:

# 显示内存占用最高的进程,VSZ/RSS单位KB
ps -eo pid,ppid,user,%mem,%cpu,rss,comm --sort=-rss | head

二、用top/htop交互查看

top
# 进入后按 M 按内存排序,按 P 按CPU排序,k 输入PID杀进程,q退出

装个 htop 更清楚:

yum install -y htop
htop

三、按名字批量找进程PID

# 查php-fpm相关进程
pgrep -af php-fpm
# 或
ps -ef | grep php-fpm | grep -v grep

pgrep 只输出PID,-a 带命令行。

四、kill进程(先温柔再强杀)

# 正常结束(SIGTERM=15),给进程清理机会
kill PID
# 按名字结束所有同名进程
killall php-fpm
pkill -f "php think"
# 杀不掉再强制杀(SIGKILL=9),数据可能损坏,最后手段
kill -9 PID

信号优先级:

  • kill -15(默认):请求退出,进程可捕获;
  • kill -9:直接杀死,无法拦截。

服务类进程优先用它自己的重启命令而不是裸kill:

systemctl restart php-fpm

五、常见坑

  1. 只看%MEM不看RSS:共享内存会被重复算,判断真实占用看RSS;
  2. 上来就kill -9:数据库、日志服务强杀可能坏数据,先15再9;
  3. 杀错进程:杀之前用 ps -p PID -o cmd 确认这个PID到底是谁;
  4. 僵尸进程杀不掉:状态Z的僵尸要杀它的父进程,僵尸本身已经不占CPU只占PID表;
  5. kill完又自动起来:被systemd/守护进程托管,要用 systemctl stop 或它的管理命令,否则白杀。

总结

ps aux --sort=-%mem | head 或 top 按M排序找内存大户,确认PID后先用 kill(15)优雅结束,不行再 kill -9。服务类进程优先 systemctl restart,避免强杀造成数据损坏。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-21 07:24 

CentOS7重启网络报错,network与NetworkManager冲突处理

前言

CentOS7改完网卡配置后 systemctl restart network 报错,常见提示是 "Job for network.service failed because the control process exited with error code"。根源往往是老的 network 服务和新的 NetworkManager(简称NM)同时管一块网卡,配置互相打架。本篇讲清楚怎么判断、怎么处理。

一、先看报错到底是什么

# 重启网络看具体错误
systemctl restart network
# 查看详细失败原因
systemctl status network -l
journalctl -xe | grep network
# 看网卡配置有没有写错
cat /etc/sysconfig/network-scripts/ifcfg-ens33

常见报错:配置里 NM_CONTROLLED=no 与NM冲突、ONBOOT、网关或IP写重复、网卡名和配置文件名对不上。

二、方案一:服务器纯命令行,关掉NetworkManager

服务器一般用不到图形网络管理,直接停用NM,让network接管最干净:

# 停止并禁用NetworkManager
systemctl stop NetworkManager
systemctl disable NetworkManager
# 再重启network
systemctl restart network

同时确认网卡配置里写:

NM_CONTROLLED=no

这样两块不会抢同一张网卡。

三、方案二:保留NetworkManager,用它管网络

如果是桌面或需要NM的场景,就别用老network:

# 查看NM识别到的连接
nmcli connection show
# 重启某块网卡连接
nmcli connection down ens33 && nmcli connection up ens33
# 查看设备状态
nmcli device status

注意:用NM管理时,配置尽量通过 nmcli 或nmtui改,不要手改ifcfg后又让network重启。

四、常见配置文件检查项

/etc/sysconfig/network-scripts/ifcfg-ens33:

TYPE=Ethernet
BOOTPROTO=static
DEFROUTE=yes
NAME=ens33
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.1.100
NETMASK=255.255.255.0
GATEWAY=192.168.1.1
DNS1=114.114.114.114
  • NAME/DEVICE 要和实际网卡名一致;
  • 改完配置重启前,保留一个已登录SSH会话,防止网络断了把自己关外面。

五、常见坑

  1. network和NetworkManager同时重启:两个服务都抢网卡,IP时有时无,二选一;
  2. 网卡名变了配置文件没改:新系统网卡名是ens33不是eth0,文件名和DEVICE要对应;
  3. GATEWAY写了两个:多网卡配多个网关会路由冲突;
  4. 改完只restart不验证:用 ping、ip addr 确认IP真的起来了;
  5. 云服务器别乱关NM:部分云镜像靠NM配内网,关了可能丢内网,先确认再操作。

总结

重启网络报错,先 systemctl status network -l 看原因;服务器场景停掉并禁用NetworkManager、配置里 NM_CONTROLLED=no,再restart network;桌面场景改用nmcli管理。二选一,不要两个服务同时管网卡。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-21 07:23 

Linux日志过大占满磁盘,logrotate日志切割配置

前言

网站跑久了,Nginx、MySQL、PHP-FPM的日志几个G地涨,df -h一看磁盘被日志占满,服务直接报错。手动删不是长久之计,logrotate 是Linux自带的日志切割工具,按天/按大小自动切分并保留指定份数,配一次永久省心。

一、logrotate工作原理

logrotate本身靠系统定时任务(/etc/cron.daily/logrotate)每天跑一次,读取配置:

  • 全局配置:/etc/logrotate.conf
  • 自定义配置目录:/etc/logrotate.d/
    我们一般在 /etc/logrotate.d/ 下为某个服务单独写配置。

二、以Nginx日志为例配置

新建 /etc/logrotate.d/nginx:

/var/log/nginx/*.log {
    daily                 # 按天切割
    missingok             # 日志不存在不报错
    rotate 14             # 保留14份历史
    compress              # 切割后gzip压缩
    delaycompress         # 延迟一次再压缩,避免影响正在写的文件
    notifempty            # 空日志不切割
    create 0644 nginx nginx  # 新建日志文件属主权限
    sharedscripts         # 多个日志只执行一次postrotate
    postrotate
        if [ -f /run/nginx.pid ]; then
            kill -USR1 `cat /run/nginx.pid`
        fi
    endscript
}

关键点:切割后要给Nginx发USR1信号,让它重新打开日志文件,否则还会往旧文件写。

三、按大小切割(不限时间)

如果日志涨得快,按大小更合理:

/var/log/nginx/error.log {
    size 100M            # 超过100M切
    rotate 10
    compress
    missingok
    notifempty
    create 0644 nginx nginx
    postrotate
        kill -USR1 `cat /run/nginx.pid`
    endscript
}

四、测试与立即执行

不等到定时,手动试运行(调试用 -d 打印过程不真正执行):

# 调试,看会做什么
logrotate -d /etc/logrotate.d/nginx
# 强制立即切割一次
logrotate -f /etc/logrotate.d/nginx

切割后原日志变成 xxx.log-20261021,隔天再压成 .gz。

五、常见坑

  1. 切割后服务还往旧文件写:缺postrotate里的 kill -USR1,或pid路径写错,导致旧文件越来越大;
  2. 直接rm删正在写的日志:空间没释放,要 > 文件 清空或重启服务;
  3. compress和delaycompress一起用仍报错:第一次切出的文件先不压,第二次才压,别误以为没生效;
  4. 日志目录属主不对:create指定的用户组要真实存在,否则新建文件失败;
  5. cron没跑:确认 crond 服务开机自启,否则logrotate永远不触发。

总结

在 /etc/logrotate.d/ 为每个大日志写一份切割规则,配好daily/size、rotate份数和postrotate信号重开日志,logrotate -f 验证一次,磁盘就不会再被单个日志吃满。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-21 07:22 

PHP合并数组方法

PHP合并数组方法

引言

合并两个或多个数组是日常开发高频操作。索引数组用array_push或加号,关联数组用array_merge,+号和array_merge在键冲突时行为完全不同,搞混会踩坑。

array_merge 合并

$a = [1, 2];
$b = [3, 4];
$c = array_merge($a, $b);
// [1, 2, 3, 4]

索引数组会重排数字键,关联数组同名键后者覆盖前者。

加号 + 保留原键

$a = ['a' => 1, 'b' => 2];
$b = ['b' => 99, 'c' => 3];
$c = $a + $b;
// ['a'=>1,'b'=>2,'c'=>3]  左侧优先

+号左边数组的键优先,同键不覆盖,保留左侧值。

array_merge vs + 的区别

$a = ['a' => 1];
$b = ['a' => 99];
array_merge($a, $b);  // ['a'=>99] 后者覆盖
$a + $b;              // ['a'=>1]  左侧保留

关联数组合并要谁覆盖谁,选对函数很关键。

数组合并并去重

$all = array_unique(array_merge($a, $b));

合并后去重,适合标签、权限码这类集合。

追加元素到数组

$list[] = 'new';
array_push($list, 'a', 'b');

单元素直接[]追加最简洁,多元素用array_push。

注意事项

  • array_merge会重置数字索引,+号保留原数字键;
  • 关联数组同名键,array_merge后者覆盖,+号左侧优先;
  • 合并大数组注意内存,别在循环里反复merge;
  • array_merge递归合并多维要用array_replace_recursive;
  • 空数组合并不会报错;
  • 合并后记得检查是否都是数组类型再合并。

总结

array_merge合并数组,数字键重排、关联键后者覆盖;+号运算符左侧优先不覆盖。合并去重用array_unique包一层,追加元素直接[]最简洁。注意两者在同名键上的覆盖方向完全相反,多维递归合并用array_replace_recursive,大数组合并注意内存别在循环里反复操作。


emer 发布于  2026-10-21 07:05 

Redis删除键命令

Redis删除键命令

引言

清理缓存、下线业务时要删Redis键。DEL是基础删除,UNLINK异步删大键不阻塞主线程,别在线上KEYS加DEL一把删,那会把Redis打挂。

删除指定键

DEL user:1001

删除一个或多个键,返回删除个数,键不存在返回0。

一次删多个

DEL user:1001 user:1002 user:1003

跟多个键名,一次命令批量删,比循环DEL省网络往返。

异步删大键:UNLINK

UNLINK bigkey:list

大key(百万级list/hash)用UNLINK,后台异步释放内存,不阻塞主线程。

判断键是否存在再删

EXISTS user:1001
DEL user:1001

EXISTS返回1存在,删除前确认避免误删。

按模式删:别用KEYS

# 危险:线上禁用
# KEYS user:* | DEL

KEYS会遍历全库阻塞生产,按模式删除用SCAN渐进扫描再UNLINK。

删除整个库

FLUSHDB      # 清当前库
FLUSHALL     # 清所有库

高危命令,线上几乎不用,执行前再三确认。

注意事项

  • DEL是同步删除,大key会阻塞,大key用UNLINK;
  • 不要在生产用KEYS模式匹配删除,会卡死Redis;
  • 删除不影响持久化AOF/RDB里已写的文件,重启后残留;
  • 删错键没有回收站,重要数据先备份;
  • 集群下DEL只删当前节点,跨槽键要逐个节点删;
  • 定期删缓存要加随机TTL,别一次删一堆热点key。

总结

DEL删一个或多个键,UNLINK异步删大key不阻塞主线程,EXISTS先判断存在。线上禁止KEYS加DEL模式删除,要用SCAN渐进扫描。FLUSHDB清当前库、FLUSHALL清全库属高危操作。删除大key优先UNLINK,批量删缓存错开时间避免雪崩,删错无回收站需提前备份。


emer 发布于  2026-10-21 07:04 

Linux查看系统时间

Linux查看系统时间

引言

日志时间对不上、证书过期误判、定时任务不对点,常常是系统时间不准导致的。运维第一件事就是date看时间、timedatectl看时区,服务器必须用NTP同步,否则业务时间戳全乱。

查看当前时间:date

date

输出年月日、时分秒和时区,最常用。

自定义格式输出

date +"%Y-%m-%d %H:%M:%S"
# 2026-10-21 07:03:00

脚本里取指定格式时间做日志前缀。

查看时间戳

date +%s

输出当前Unix时间戳,排查时间差时用。

查看时区与NTP状态

timedatectl

显示本地时间、UTC、时区、NTP同步是否开启,比date信息全。

硬件时间 vs 系统时间

hwclock --show

读主板RTC硬件时钟,系统时间和硬件时间不一致会导致重启后时间跳变。

手动同步一次

timedatectl set-ntp true
# 或
ntpdate ntp.aliyun.com

开启NTP自动同步,国内用阿里云或ntp.aliyun.com源。

注意事项

  • 云服务器一般已开NTP,别手动硬改时间;
  • 时间跳变会影响日志排序和证书有效期判断;
  • 容器内时间默认跟宿主机,宿主机准了容器就准;
  • 时区不对用timedatectl set-timezone Asia/Shanghai;
  • 不要随便set-time往前拨,数据库主从时间差会出问题;
  • 定时任务执行异常先查系统时间和时区。

总结

date看当前时间、date+%s取时间戳、timedatectl看时区和NTP状态、hwclock看硬件时钟。服务器开启NTP自动同步,时区设Asia/Shanghai,时间不对先查NTP和时区,别手动硬改,时间跳变会影响日志、证书和数据库主从,定时任务异常优先核对系统时间。


emer 发布于  2026-10-21 07:03 

MySQL删除数据库

MySQL删除数据库

引言

下线测试库、清理废弃项目时需要DROP DATABASE。这是高危操作,一旦执行库和所有表立即删除不可回滚,必须先确认库名、确认备份,避免误删生产库。

删除指定数据库

DROP DATABASE db_test;

删除整个库及其下所有表、视图、存储过程,磁盘空间立即释放。

库存在才删除

DROP DATABASE IF EXISTS db_test;

加IF EXISTS库不存在时不报错,脚本里更安全,避免中断。

删除前先确认

SHOW DATABASES LIKE 'db_test';
USE db_test;
SHOW TABLES;

先列出库和表,确认删的是目标库而不是同名生产库。

删除后验证

SHOW DATABASES;

库从列表消失即删除成功;磁盘空间由MySQL异步释放,大库可能需等一小会。

不要删错:先备份

mysqldump -u root -p db_test > /backup/db_test.sql

删除前先mysqldump导出,万一删错还能恢复。生产库删除前务必先备份再操作。

注意事项

  • DROP DATABASE不可回滚,DELETE/TRUNCLE都比它温柔;
  • 库名区分大小写(Linux下),别拼错;
  • 需要DROP权限,普通业务账号不该有这个权限;
  • 删除大库会锁实例一小段时间,高峰期别执行;
  • 删除库不会自动删用户授权,残留grant需手动清理;
  • 删库前确认无业务连接,可先停应用再删。

总结

DROP DATABASE删除整库,加IF EXISTS避免报错中断,删前用SHOW DATABASES确认目标、mysqldump先备份,删后再SHOW DATABASES验证。这是高危不可回滚操作,业务账号不应有DROP权限,删大库避开高峰期,删库后记得清理残留的用户授权。


emer 发布于  2026-10-21 07:02 

PHP获取文件扩展名

PHP获取文件扩展名

引言

文件上传、附件处理、图片裁剪时都要先拿到后缀判断类型。别用explode切字符串硬切,pathinfo是PHP专门解析路径的函数,能干净返回扩展名、文件名、目录名各部分。

pathinfo 标准用法

$file = '/var/www/upload/photo.2024.jpg';
$info = pathinfo($file);
echo $info['extension'];   // jpg
echo $info['filename'];    // photo.2024
echo $info['dirname'];     // /var/www/upload

一次返回路径各组成部分,最稳妥。

只取扩展名

$ext = pathinfo($file, PATHINFO_EXTENSION);
// jpg

传第二个常量直接拿扩展名,省一次数组取值。

处理无扩展名文件

$ext = pathinfo('README', PATHINFO_EXTENSION);
// 空字符串,不会报错

没扩展名时返回空串,记得做空判断。

大写转小写统一

$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));

用户传JPG、Jpg、jpg都归一,上传校验用统一小写比较。

不要用explode硬切

// 反面例子:photo.2024.jpg 会切错
$parts = explode('.', $file);
$ext = end($parts);

多级点号文件名会取错,pathinfo按最后一个点正确取后缀。

注意事项

  • 扩展名不等于真实MIME类型,校验文件内容要用finfo;
  • 扩展名可伪造,上传安全不能只信后缀;
  • PATHINFO_EXTENSION对".htaccess"这种点开头文件返回空;
  • 中文文件名不受影响,pathinfo按字节正确处理;
  • 取到扩展名后trim并strtolower再做白名单校验;
  • 别把扩展名当安全依据,配合大小限制和重命名存储。

总结

pathinfo($file, PATHINFO_EXTENSION)取扩展名最稳,filename拿文件名、dirname拿目录,无扩展名返回空串记得判断,业务里统一strtolower小写。不要用explode切多级点号文件名。扩展名只是展示用,真实类型靠finfo判断,上传安全还需白名单加文件内容校验和重命名存储。


emer 发布于  2026-10-21 07:01 

服务器被暴力SSH爆破,如何防护与封禁IP

前言

公网服务器的22端口每天都会被机器人字典爆破,日志里满是 Failed password。口令弱就容易被撞库拿到root。本篇从"先排查→临时封禁→加固配置→自动防护"给出一套可落地的做法。

一、先确认是不是被爆破

# 统计失败次数最多的IP
lastb | awk '{print $3}' | sort | uniq -c | sort -rn | head
# 或看secure日志里的失败记录
grep "Failed password" /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -rn | head

出现大量同一IP的失败尝试,就是爆破。

二、手动封禁恶意IP(应急)

firewalld 临时/永久拉黑:

# 临时封禁
firewall-cmd --add-rich-rule='rule family=ipv4 source address=1.2.3.4 reject'
# 永久封禁
firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=1.2.3.4 reject'
firewall-cmd --reload
# 查看已加规则
firewall-cmd --list-rich-rules
# 解除封禁
firewall-cmd --permanent --remove-rich-rule='rule family=ipv4 source address=1.2.3.4 reject'

也可用 iptables -A INPUT -s 1.2.3.4 -j DROP。

三、加固sshd配置(根本)

编辑 /etc/ssh/sshd_config:

# 修改默认端口,别再用22
Port 2222
# 禁止root直接密码登录
PermitRootLogin no
# 禁用空密码
PermitEmptyPasswords no
# 建议改用密钥登录后关闭密码
PasswordAuthentication no
# 最大尝试次数
MaxAuthTries 3
# 10分钟无活动断开
ClientAliveInterval 600

改完 systemctl restart sshd,先保留当前会话再重启,防止把自己锁外面;新端口记得在防火墙放行。

四、自动防爆:fail2ban

自动统计失败次数并封IP,最省事:

yum install -y fail2ban fail2ban-firewalld

新建 /etc/fail2ban/jail.local:

[sshd]
enabled = true
port = 2222
maxretry = 5
findtime = 600
bantime = 86400

启动并自启:

systemctl enable --now fail2ban
# 查看封禁名单
fail2ban-client status sshd

五、常见坑

  1. 改端口只改sshd忘了防火墙放行:自己也连不上,务必先 firewall-cmd --permanent --add-port=2222/tcp;
  2. 关密码登录前没配好密钥:直接被锁门外;
  3. 只封一次IP:机器人换段继续扫,要用fail2ban自动封;
  4. 云安全组没限制:在安全组只放行你常用IP访问SSH端口,比本机防护更前置;
  5. 弱密码root:PermitRootLogin no + 密钥才是正解,改端口只是提高门槛。

总结

应急用firewalld拉黑,长期靠改端口、禁root密码、上密钥,再用fail2ban自动封高频失败IP,云安全组白名单兜底。层层叠加,爆破就很难成功。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:29 

Linux查看CPU内存负载,top命令详解

前言

服务器变慢、网站卡顿,第一反应是看 CPU 和内存谁吃满了。top 是 Linux 自带的实时监控命令,能动态看到系统负载和每个进程的占用。本篇讲清楚 top 界面每一行含义、常用按键,以及怎么判断到底是 CPU 满还是内存满。

一、启动top

top

进入动态刷新界面,默认每3秒刷新一次,按 q 退出。

二、顶部系统信息各行含义

  • 第一行:load average 三个数分别是1分钟、5分钟、15分钟平均负载,数值超过CPU核数说明忙不过来;
  • 第二行 Tasks:进程总数、运行中、睡眠、停止、僵尸进程数;
  • 第三行 CPU:%us 用户进程占用、%sy 系统内核占用、%id 空闲,id接近0说明CPU紧张;
  • 第四行 Mem:物理内存总量、已用、空闲、缓存;
  • 第五行 Swap:交换分区使用,频繁交换说明内存不足。

看核数判断负载:

nproc
# 假设2核,load average长期超过2就要警惕

三、进程列表关键列

  • PID 进程号;
  • %CPU CPU占用百分比;
  • %MEM 内存占用百分比;
  • RES 进程实际占用物理内存(KB);
  • COMMAND 启动命令。

四、常用交互按键

按键 作用
P 按CPU占用降序
M 按内存占用降序
1 展开每个核心单独显示
k 输入PID杀掉进程
q 退出
# 静态一次性输出top结果(适合脚本/采集)
top -bn1 | head -20
# 只看某个进程
top -p PID

五、判断思路

  1. CPU高:按P排序,找%CPU高的进程,常见是死循环PHP、爬虫、计算任务;
  2. 内存高:按M排序看%MEM和RES,常见内存泄漏、缓存过大;
  3. 负载高但CPU不高:可能是大量I/O等待或僵尸进程,配合 iostat、vmstat 看;
  4. Swap被大量使用:物理内存不够,系统把内存换到磁盘,会明显变慢。

常见坑

  1. 把load average绝对值当指标:要结合核数,1核负载3和8核负载3完全不同;
  2. 只看%CPU不看持续时间:瞬时冲高正常,持续90%以上才是问题;
  3. RES和SHR混淆:RES是独占物理内存,判断内存占用看RES;
  4. 僵尸进程多:Z 状态,父进程没回收,需要找父进程处理。

总结

top 里按P看CPU、按M看内存,先看顶部load和核数是否匹配,再定位具体进程。CPU满找%CPU高的,内存满找%MEM高的,再决定优化或kill。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:28 

服务器SSH连接慢,DNS反向解析问题解决办法

前言

用 ssh 登录服务器,输入密码前要等好几秒甚至十几秒才出现密码提示,连上之后命令回显也卡。很多人以为是网络差,其实最常见原因是 sshd 在做 DNS 反向解析,解析超时才放行。本篇给出关闭反向解析的标准做法。

一、为什么会慢:DNS反向解析

客户端连上后,sshd 默认会把客户端 IP 反查成主机名(UseDNS yes)。如果服务器配的 DNS 解析不了这个公网 IP,就一直等到超时,表现为卡在登录前。

二、关闭sshd反向解析(核心)

编辑配置文件:

vi /etc/ssh/sshd_config

找到或新增两行:

UseDNS no
GSSAPIAuthentication no
  • UseDNS no:关闭IP反查;
  • GSSAPIAuthentication no:关闭GSSAPI认证,这也会触发额外DNS查询导致卡顿。

三、重启sshd生效

systemctl restart sshd
# CentOS7 服务名是 sshd,部分发行版是 ssh
systemctl status sshd

注意:重启前保留一个已登录的会话,避免配置写错把自己锁在外面。

四、其他导致SSH慢的因素

  1. DNS配置本身慢:检查 /etc/resolv.conf,解析超时的nameserver会拖慢所有服务,可临时改成公共DNS测试;
  2. 反向解析写进hosts:不想关UseDNS也可在 /etc/hosts 里把常用客户端IP和主机名写死,跳过查询;
  3. GSSAPI:客户端侧也可加 -o GSSAPIAuthentication=no 临时绕过;
  4. 登录banner/反向PTR:某些版本还会尝试获取PTR记录,关UseDNS一并解决。

五、验证是否改善

# 客户端用详细模式看卡在哪一步
ssh -v root@服务器IP

观察卡在 Trying reverse mapping / GSSAPI 字样的位置,关掉对应项后应秒出密码提示。

常见坑

  1. 改完不重启sshd:配置不生效;
  2. 改错文件:是 sshd_config(服务端),不是 ssh_config(客户端);
  3. 重启时断开当前连接:保留旧会话再操作,防止锁死;
  4. 公司内网IP也被反查:UseDNS no 对所有连接生效,不影响安全登录。

总结

SSH登录慢九成是 UseDNS yes 反向解析超时,改 /etc/ssh/sshd_config 为 UseDNS no、GSSAPIAuthentication no,重启sshd即可秒连。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:27 

Linux crontab定时任务不执行常见原因与排查

前言

crontab 写好了却到点不执行,是服务器运维最常见的坑之一。手动跑脚本正常,定时任务却没反应,问题往往不在脚本本身,而在服务状态、环境变量、时间格式和权限。本篇按从易到难的顺序列出排查点。

一、先确认crond服务在运行

systemctl status crond
# 没启动就启动并设开机自启
systemctl start crond
systemctl enable crond

服务没跑,所有定时任务都不会触发,这是第一个要查的。

二、检查定时任务格式是否正确

标准格式:分 时 日 月 周 命令

# 查看当前用户的定时任务
crontab -l
# 例如:每10分钟执行一次脚本
*/10 * * * * /usr/bin/php /www/backup.php

常见格式错误:分钟位置写成了小时、用了 * 却想表示"第1个"、命令路径用了相对路径。

三、查看cron日志,判断有没有触发

# CentOS7 日志位置
tail -f /var/log/cron

到点看日志里有没有出现这条任务:

  • 日志里根本没有:格式没被识别、服务没跑、或时间写错;
  • 日志有执行记录但没效果:是脚本/环境问题,往下查。

四、脚本环境变量问题(高频)

cron 的环境比手动登录干净得多,很多命令找不到、相对路径失败。解决:

  1. 用绝对路径:脚本里和任务里都写全,比如 /usr/bin/php、/www/backup.php;
  2. 开头加载环境:
    */10 * * * * source /etc/profile; /usr/bin/php /www/backup.php
  3. 先手动用cron环境复现:env -i /bin/bash script.sh 模拟干净环境跑一遍。

五、权限与重定向

  • 脚本要有执行权限:chmod +x /www/backup.sh;
  • 脚本没加 shebang(#!/bin/bash)会找不到解释器;
  • 把错误输出留下来方便定位:
    */10 * * * * /usr/bin/php /www/backup.php >> /www/log/cron.log 2>&1

    加了 2>&1,报错就写进日志,不再"静默失败"。

六、其他易漏点

  1. 时区不一致:timedatectl 看系统时区,容器里时间错会导致任务错位;
  2. 日和周同时指定是"或"关系,容易误触发,注意语义;
  3. root和普通用户任务混淆:sudo crontab -l 看root的,普通用户看自己的;
  4. 磁盘满了:日志写不进去,任务看似没执行。

总结

排查顺序:crond服务是否运行 → 任务格式对不对 → /var/log/cron 有没有触发 → 绝对路径和环境变量 → 执行权限与错误重定向 → 时区。手动正常、定时不跑,九成是环境变量和绝对路径问题。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:26 

Linux磁盘占用100%怎么排查,大文件查找清理方法

前言

服务器磁盘爆满到100%,会导致网站写不了日志、数据库报错、服务异常退出。很多人只看到根目录满,却找不到到底是谁占的。本篇按"先看分区→再找大目录→再找大文件→安全清理"的顺序给出可直接执行的命令。

一、先看哪个分区满了

df -h

看 Use% 列,找到达到100%的挂载点(常见是 / 根分区或 /data)。注意区分分区,别在没满的分区里瞎找。

二、逐级定位占用最大的目录

# 从根目录开始,显示一级目录大小,排序,排除挂载点干扰
du -h --max-depth=1 / 2>/dev/null | sort -rh | head

进入最大的那个目录重复执行,比如:

du -h --max-depth=1 /var 2>/dev/null | sort -rh | head

一层层往下钻,直到定位到具体目录。

三、查找超过指定大小的大文件

# 找出/var下大于500MB的文件
find /var -type f -size +500M -exec ls -lh {} \; 2>/dev/null | awk '{print $5, $9}'
# 全盘找大于1G的文件
find / -type f -size +1G 2>/dev/null | xargs ls -lh

常见大头:日志文件、MySQL binlog、Docker镜像/容器、上传目录、core dump、旧备份。

四、安全清理方式

  1. 日志文件:确认无用后清空(不要直接删正在写的文件,空间不释放):
    # 清空而不是删除,释放空间且进程句柄仍在
    > /var/log/nginx/access.log
  2. 已删除但仍被进程占用的文件(df满但du对不上):
    lsof | grep deleted
    # 找到对应PID后重启该服务释放句柄
  3. Docker清理:
    docker system df          # 先看占用
    docker system prune -a    # 清理无用镜像、停止容器、悬空卷
  4. 旧备份/binlog:确认可删后 rm -f,MySQL binlog 用 PURGE BINARY LOGS 正规清理。

五、常见坑

  1. 直接rm正在写入的大日志:空间不立刻释放,要 > 文件 清空或重启进程;
  2. df满du小:典型是已删除文件被进程占用,用 lsof | grep deleted 找;
  3. 只删根目录表面:--max-depth=1 逐级下钻才找得到真凶;
  4. 误删数据库文件/系统日志:清理前先 ls -lh 确认,重要数据先备份;
  5. Docker不清理:镜像层和停止容器很容易吃掉几十G,docker system prune 定期跑。

总结

df -h 找满的分区,du --max-depth=1 逐级定位,find -size 找大文件,日志用清空、Docker用prune、被占用文件重启释放。按这条链路,100%磁盘很快能查清楚。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:26 

CentOS7关闭SELinux详细步骤,临时与永久关闭

前言

CentOS7 默认开启 SELinux,它是一套强制访问控制机制,经常和自行搭建的 Nginx、PHP-FPM、网站目录权限冲突,表现为页面 403、文件写不进去、代理被拦截。生产环境如果不想精细配置策略,最直接的做法就是关闭。本篇区分临时关闭和永久关闭,并给出验证方法。

一、先查看SELinux当前状态

# 查看运行状态
getenforce
# 返回 Enforcing 表示强制开启,Permissive 只记录不拦截,Disabled 已关闭
sestatus
# 也可看配置文件
grep SELINUX= /etc/selinux/config

二、临时关闭(重启后恢复)

# 切换为宽容模式,立即生效但重启复原
setenforce 0
# 验证
getenforce
# 返回 Permissive 即临时关闭成功

setenforce 1 可重新切回 Enforcing。临时关闭适合先定位问题,不想动配置时使用。

三、永久关闭(重启仍生效)

编辑配置文件:

vi /etc/selinux/config

把这一行:

SELINUX=enforcing

改为:

SELINUX=disabled

保存后必须重启服务器才完全生效:

reboot

重启后执行 getenforce 返回 Disabled 即永久关闭成功。

四、两种方式区别速记

方式 命令/改文件 是否立即生效 重启后
临时 setenforce 0 是 恢复为Enforcing
永久 /etc/selinux/config 改disabled 否,需重启 保持关闭

五、常见坑

  1. 只 setenforce 0 不重启改文件:重启后又自动开启,问题复现;
  2. 改了配置不重启:getenforce 仍显示 Enforcing,以为没改成功;
  3. 改错变量名:是 SELINUX= 而不是 SELINUXTYPE=,后者是策略类型别动;
  4. 急着排查时建议先临时关:验证是不是SELinux拦截,确认后再决定永久关,避免一刀切损失安全性;
  5. 某些面板/容器环境改完仍受影响,可用 sestatus 复核当前模式。

总结

临时关用 setenforce 0,永久关改 /etc/selinux/config 为 disabled 后重启,最后用 getenforce 确认。排查403、写入失败时,SELinux是经常被忽略的一层。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:25 

Linux查看服务器开放端口,netstat和ss命令教程

前言

排查网站打不开、端口是否被占用、服务有没有监听,都要先看服务器开放了哪些端口。Linux 上最常用的两个工具是 netstat 和 ss,ss 是新系统推荐的替代,速度更快。本篇讲清楚两个命令的常用组合和怎么判断监听状态。

一、ss命令(推荐,CentOS7自带)

# 查看所有正在监听的TCP端口
ss -tlnp
# 查看所有正在监听的UDP端口
ss -ulnp
# 统计各类连接数量
ss -s

参数含义:-t TCP、-u UDP、-l 只看监听、-n 数字形式显示端口号(不解析域名/服务名)、-p 显示占用进程。

二、netstat命令(老系统常用,需net-tools)

CentOS7 最小化可能没装,先装:

yum install -y net-tools

常用:

# 查看所有监听的TCP端口和进程
netstat -tlnp
# 查看UDP监听
netstat -ulnp
# 查看80端口被谁占用
netstat -tlnp | grep ':80'
# 查看所有已建立连接
netstat -anp | grep ESTABLISHED

三、判断端口是否在监听

以80端口为例:

ss -tlnp | grep ':80'
# 或
netstat -tlnp | grep ':80'
  • LISTEN 表示正在监听,服务正常;
  • 没有任何输出说明该端口没被程序监听;
  • 最后一列能看到进程名和PID,比如 nginx: worker。

四、常用排查组合

# 查某个具体端口(443)
ss -tlnp | grep 443
# 查某进程监听了哪些端口
ss -tlnp | grep nginx
# 实时看连接数最多的IP
ss -tn state established | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head

五、常见坑

  1. 看不到进程名要加 -p,且需要root:普通用户只能看到端口,看不到占用程序,用 sudo 或 root 执行;
  2. 只看监听不够:服务在本机监听但防火墙/安全组没放行,外网依然连不上,还要配合 firewalld 和云安全组检查;
  3. netstat 找不到命令:是没装 net-tools,yum install -y net-tools;
  4. -n 别漏:不加会把80显示成http,grep端口号时容易匹配不到。

总结

记住 ss -tlnp 看 TCP 监听、ss -ulnp 看 UDP、| grep 端口号 精准定位,老机器用 netstat 同参数即可。先确认本机监听,再查防火墙和安全组,排查链路才完整。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:24 

CentOS7 firewalld开放80、443端口永久生效命令

前言

CentOS7 默认用 firewalld 管理防火墙,新装系统后外网访问 80、443 经常被拒,浏览器打不开网站。很多人只在临时会话里加规则,重启或 reload 后端口又被关。本篇给出永久开放 80、443 的完整命令和验证方法。

一、先确认防火墙状态

systemctl status firewalld
firewall-cmd --state

返回 running 表示防火墙在运行;如果是 not running,先启动并设为开机自启:

systemctl start firewalld
systemctl enable firewalld

二、永久开放80、443端口

关键是 --permanent 参数,不加它规则只在当前运行时生效,reload 或重启就丢失。

# 永久开放HTTP 80
firewall-cmd --permanent --add-port=80/tcp
# 永久开放HTTPS 443
firewall-cmd --permanent --add-port=443/tcp
# 重载使永久规则立即生效
firewall-cmd --reload

也可以用服务名方式(firewalld 自带 http/https 服务定义):

firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

三、查看已开放端口,确认生效

# 查看永久规则里已开放的端口
firewall-cmd --permanent --list-ports
# 查看当前运行时生效的服务
firewall-cmd --list-services

能看到 80/tcp 443/tcp 或 http https 即成功。

四、开放单个/多个端口常用命令汇总

# 永久开放单个端口
firewall-cmd --permanent --add-port=8080/tcp
# 永久移除某个端口
firewall-cmd --permanent --remove-port=8080/tcp
# 开放一段端口范围(如30000-30100)
firewall-cmd --permanent --add-port=30000-30100/tcp
# 开放UDP(DNS、视频流用)
firewall-cmd --permanent --add-port=53/udp

每加完一条都要 firewall-cmd --reload。

五、常见坑

  1. 加了规则但不 reload:运行时看不到,外网照样访问不通;
  2. 忘记 --permanent:重启服务器后规则消失,以为没生效;
  3. 协议选错:Web 是 tcp,写成 udp 无效;
  4. 云服务器安全组没放行:系统防火墙开了,云厂商控制台安全组还拦着,两边都要开;
  5. 服务本身没监听:ss -tlnp 确认 nginx 在 80/443 上监听,否则放了端口也连不上。

总结

记住三步:--add-port=端口/tcp --permanent → --reload → --list-ports 验证。系统防火墙和云安全组同时放行,网站才能从外网访问。

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-20 07:22 

Linux查看登录用户

Linux查看登录用户

引言

服务器被多人SSH登录或排查异常连接时,需要看清当前是谁在线、从哪里连进来、在做什么。who、w、id、last几个命令配合,能区分当前身份、在线会话和历史登录记录。

我是谁:whoami

whoami

直接输出当前执行命令的用户名,脚本里判断身份最常用。

查看自己的UID和组

id

输出uid、gid和所属附加组,比whoami信息更全,判断权限时用。

谁在线:who

who

列出所有已登录用户、终端tty、登录时间和来源IP。

在线会话详情:w

w

比who多一列当前正在执行的命令和空闲时长,能看到每个会话在干什么。

历史登录记录:last

last -10

读取/var/log/wtmp,列出最近登录的用户、终端、来源IP和时长,排查入侵看历史。

查特定用户

w root
last root

指定用户名过滤,聚焦某账号的在线和历史。

注意事项

  • whoami只看当前身份,who/w看所有在线会话;
  • w的JCPU是终端累计时间,PCPU是当前进程时间;
  • last依赖wtmp日志,日志轮转后只保留近期;
  • 异常来源IP、陌生账号要结合last和/var/log/secure排查;
  • 系统服务进程不占登录会话,who看不到;
  • 切换用户su后whoami变新用户,但w仍显示原始登录终端。

总结

whoami看当前用户名,id看UID和组,who列在线用户与来源IP,w多显示正在执行的命令,last查历史登录记录。排查时先w看当前在线会话,再last回溯历史,陌生IP和账号结合secure日志,su切换后身份以whoami为准但终端归属原始登录人。


emer 发布于  2026-10-20 07:21 

Redis设置过期时间

Redis设置过期时间

引言

缓存键必须带TTL自动失效,否则热点数据永久驻留直到内存写满。EXPIRE设置秒级过期,PEXPIRE毫秒,SET组合NX/EX可一步完成写入带过期,是防止缓存永久堆积的基本功。

写入时直接带过期

SET code:1001 123456 EX 300 NX

EX 300表示5分钟后过期,NX只在键不存在时写入,一次命令完成防重复与失效。

给已有键补过期

EXPIRE code:1001 300

键存在返回1,键不存在返回0,只设置剩余存活秒数。

查看剩余存活

TTL code:1001

返回剩余秒数;-1表示永久不过期,-2表示键已不存在。

毫秒级过期

PEXPIRE code:1001 300000
PTTL code:1001

对验证码、限流等需要毫秒精度的场景使用。

取消过期变永久

PERSIST code:1001

移除TTL让键永久保留,需要长期数据时用。

绝对值过期

EXPIREAT code:1001 1760000000

以Unix时间戳设定具体到期时刻,适合定时失效场景。

注意事项

  • EXPIRE针对键整体,hash内部field不单独过期;
  • 覆盖写值(SET同名键)会清掉旧TTL,需重新设置;
  • TTL是相对当前时间,持久化恢复后仍按剩余时间走;
  • 分布式锁用EXPIRE要配合SET NX原子写入,别先SET再EXPIRE;
  • 大量键同时过期要错开时间,避免雪崩;
  • TTL返回-1和-2含义不同,排查时别混。

总结

SET加EX一步写入带秒级TTL,EXPIRE给已有键补过期,TTL查剩余秒数,PEXPIRE/PTTL用毫秒,PERSIST取消过期变永久,EXPIREAT按时间戳定点失效。注意覆盖写会清TTL需重设,hash字段无单独过期,分布式锁要原子NX加过期,批量过期错开避免雪崩。


emer 发布于  2026-10-20 07:20 

PHP获取客户端IP

PHP获取客户端IP

引言

记录访问日志、做地域统计或IP限定时要拿真实访客IP。经过Nginx反代或CDN后,REMOTE_ADDR是代理地址,真实IP藏在转发头里,直接取单一字段容易拿到代理IP,需要按可信链兜底并校验。

直连场景

$ip = $_SERVER['REMOTE_ADDR'] ?? '';

没有反代时,这就是直连客户端IP。

反代后取真实IP

$headers = [
    'HTTP_X_FORWARDED_FOR',
    'HTTP_X_REAL_IP',
    'HTTP_CLIENT_IP',
];
foreach ($headers as $h) {
    if (!empty($_SERVER[$h])) {
        $ip = explode(',', $_SERVER[$h])[0];
        break;
    }
}

X-Forwarded-For可能是逗号分隔的IP链,取第一个才是原始客户端。

封装函数

function getClientIp(): string {
    foreach (['HTTP_X_FORWARDED_FOR','HTTP_X_REAL_IP'] as $h) {
        if (!empty($_SERVER[$h])) {
            $ip = trim(explode(',', $_SERVER[$h])[0]);
            if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
        }
    }
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

filter_var校验格式,非法就回退到REMOTE_ADDR。

代理头必须由服务端写入

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

头要由可信Nginx设置,不能完全信任客户端自带。

注意事项

  • XFF是逗号链,取最左原始IP;
  • 客户端可伪造XFF,只信任自己配置的代理;
  • filter_var校验IPv4/IPv6格式;
  • 不要用伪造IP做安全鉴权,登录风控仍需服务端;
  • CDN场景真实IP在CDN回源头,需查厂商文档;
  • 内网代理REMOTE_ADDR是网关IP,别当访客IP。

总结

直连取REMOTE_ADDR,反代后从X-ForwardedFor、X-Real-IP按序兜底,XFF逗号链取第一个,filter_var校验后回退,代理头由可信Nginx写入。关键是客户端可伪造转发头,只信任自己配置的代理链,IP适合统计不适合单独做安全判定,CDN需用厂商回源头。


emer 发布于  2026-10-20 07:19 

Nginx绑定域名配置

Nginx绑定域名配置

引言

一台服务器跑多个站点,靠server_name区分访问哪个域名,配合DNS解析和站点根目录,Nginx按Host头把请求路由到对应虚拟主机。配置顺序写错或server_name不匹配,会落到默认站点。

基础域名站点

server {
    listen 80;
    server_name www.example.com example.com;
    root /var/www/example;
    index index.html index.php;
}

server_name可并列写主域名和裸域名,root指向站点目录。

裸域名跳转到www

server {
    listen 80;
    server_name example.com;
    return 301 https://www.example.com$request_uri;
}

避免两个域名都解析内容造成重复收录,统一到一个规范域名。

多个域名并列

server_name api.example.com admin.example.com;

同一套站点对应多个子域名时直接空格分隔。

反向代理绑定域名

server {
    listen 80;
    server_name app.example.com;
    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
    }
}

域名指向后端服务,记得把Host头透传给上游。

配置后检查重载

nginx -t
nginx -s reload

先测语法再平滑重载,reload不断连接。

匹配规则

  • 精确域名优先,然后通配符,最后默认站点;
  • 没匹配上server_name的请求落到default_server;
  • 通配符*.example.com不能匹配example.com裸域;
  • 多站点注意别让无关域名被默认站点接住。

注意事项

  • DNS要先把A记录解析到服务器IP,否则域名打不开;
  • server_name区分大小写但通常小写;
  • 80改443需另配SSL证书块;
  • return 301统一域名利于SEO避免重复;
  • 防火墙和安全组放行80、443;
  • 多个server块listen 80,靠server_name分流。

总结

server_name在server块绑定域名,可并列多个,裸域301跳www统一规范,反代时透传Host,改完nginx -t检查再reload。匹配精确优先于通配符,未命中走默认站点,前提是DNS解析到位、端口放行,统一域名有助于SEO避免重复收录。


emer 发布于  2026-10-20 07:18