0. 写在前面:为什么你需要“神器”而非“常用命令
大家好,欢迎来到干货、技术、专业全方位遥遥领先的老杨的博客.
帮老杨点赞、转发、在看以及打开小星标哦
攒今世之功德,修来世之福报
背锅这个词咋来的?
老杨今天给各位补习一下这个看似没用,实则一点都用不着的知识点。
传说古代军中如果有人犯错,就会被降级去炊事班并且负责背着大锅行军。军队做大锅饭那个铸铁锅可不是一般的沉。而且这个锅到底有多重要,当年项羽要拼命了才下令破釜沉舟,也就是把锅砸了。
你背这玩意,而且要像初恋一样的保护它!
慢慢的这个岗位就被引申到了职场中。
蝙蝠侠、蜘蛛侠、闪电侠、背锅侠,这四个都是拯救苍生的英雄人物。唯一的区别大概只有背锅侠不戴面具了。
看老杨的文章涨知识吧!
进入今天的主题.
见过太多同行因为一些看似无关紧要的操作,最后成了大铁锅。
不对!是背锅侠。
有的是技术问题,有的是流程问题,但更多的是意识问题。
比如公司数据库崩了,老板直接问运维搞咋回事。结果查来查去,发现是开发改了个配置没通知,但最后还是运维背锅——"你们怎么不知道?监控呢?"
反正运维就别想跑,出了问题第一个被怀疑,背锅是常态。但有些锅其实可以避免。
系列文章:
运维做了变更不通知相关人员,出问题的时候才说:"哦,我刚改了个配置。" 这种情况下,即使不是你的问题,也会被认为是你引起的。
比如某网站访问慢,开发排查了半天找不到原因。最后发现是运维调整了Nginx的worker_connections参数,但没通知任何人。虽然这个调整本身是对的,但因为没有沟通,浪费了大家半天时间。
正确的变更通知流程:
#!/bin/bash
# 变更通知脚本
CHANGE_DESC="调整Nginx worker_connections从1024到4096"
AFFECTED_SERVICES="前端网站,API接口"
CHANGE_TIME=$(date'+%Y-%m-%d %H:%M:%S')
OPERATOR="老杨"
# 1. 记录变更信息到系统日志
cat >> /var/log/system_changes.log << EOF
=====================================
变更时间: $CHANGE_TIME
操作人员: $OPERATOR
变更描述: $CHANGE_DESC
影响服务: $AFFECTED_SERVICES
变更前配置: worker_connections 1024;
变更后配置: worker_connections 4096;
=====================================
EOF
# 2. 发送企业微信通知
send_wework_message() {
local message="📋 系统变更通知
🕒 变更时间: $CHANGE_TIME
👤 操作人员: $OPERATOR
📝 变更内容: $CHANGE_DESC
🎯 影响服务: $AFFECTED_SERVICES
如有异常请及时联系运维团队"
curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$message\"}}"
}
# 3. 发送邮件通知
send_email_notification() {
echo"$message" | mail -s "【系统变更通知】$CHANGE_DESC" \
dev-team@company.com,qa-team@company.com
}
echo"执行变更: $CHANGE_DESC"
# 4. 执行实际的配置变更
sed -i 's/worker_connections 1024;/worker_connections 4096;/g' /etc/nginx/nginx.conf
# 5. 验证配置
nginx -t
if [ $? -eq 0 ]; then
nginx -s reload
echo"配置变更成功"
# 6. 发送通知
send_wework_message
send_email_notification
else
echo"配置验证失败,回滚变更"
sed -i 's/worker_connections 4096;/worker_connections 1024;/g' /etc/nginx/nginx.conf
exit 1
fi很多运维不重视日志收集,出问题的时候无法快速定位原因。老板问什么时候开始出问题的,影响了多少用户,你说不知道,这就是失职。
而且日志分散在各个服务器上,排查问题的时候要SSH到每台机器查看,效率低得要命。
正确的日志收集方案:
# docker-compose.yml - ELK日志收集
version:'3.8'
services:
elasticsearch:
image:docker.elastic.co/elasticsearch/elasticsearch:7.17.0
environment:
-discovery.type=single-node
-"ES_JAVA_OPTS=-Xms512m -Xmx512m"
ports:
-"9200:9200"
volumes:
-es_data:/usr/share/elasticsearch/data
kibana:
image:docker.elastic.co/kibana/kibana:7.17.0
ports:
-"5601:5601"
environment:
-ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
-elasticsearch
logstash:
image:docker.elastic.co/logstash/logstash:7.17.0
ports:
-"5044:5044"
volumes:
-./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
depends_on:
-elasticsearch
volumes:
es_data:# logstash.conf
input {
beats {
port => 5044
}
}
filter {
if [fields][log_type] == "nginx" {
grok {
match => {
"message" => '%{IPORHOST:remote_ip} - %{DATA:user_name} \[%{HTTPDATE:access_time}\] "%{WORD:http_method} %{DATA:url} HTTP/%{NUMBER:http_version}" %{NUMBER:response_code} %{NUMBER:body_sent_bytes} "%{DATA:http_referer}" "%{DATA:http_user_agent}"'
}
}
date {
match => [ "access_time", "dd/MMM/yyyy:HH:mm:ss Z" ]
}
mutate {
convert => { "response_code" => "integer" }
convert => { "body_sent_bytes" => "integer" }
}
}
if [fields][log_type] == "application" {
if [message] =~ /ERROR/ {
mutate {
add_tag => [ "error" ]
}
}
if [message] =~ /FATAL/ {
mutate {
add_tag => [ "fatal" ]
}
}
}
}
output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "logs-%{+YYYY.MM.dd}"
}
}# filebeat.yml - 在每台服务器部署
filebeat.inputs:
-type:log
enabled:true
paths:
-/var/log/nginx/*.log
fields:
log_type:nginx
server:web-01
fields_under_root:true
-type:log
enabled:true
paths:
-/var/log/app/*.log
fields:
log_type:application
server:web-01
fields_under_root:true
multiline.pattern:'^\d{4}-\d{2}-\d{2}'
multiline.negate:true
multiline.match:after
output.logstash:
hosts: ["logstash.internal.com:5044"]
processors:
-add_host_metadata:
when.not.contains.tags: forwarded这样设置后,所有日志都集中到Elasticsearch,可以在Kibana界面快速检索:
# 查询最近1小时的错误日志
GET logs-*/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"@timestamp": {
"gte": "now-1h"
}
}
},
{
"terms": {
"tags": ["error", "fatal"]
}
}
]
}
},
"sort": [
{
"@timestamp": {
"order": "desc"
}
}
]
}很多运维都是被动响应,系统慢了才想到扩容,用户投诉了才想到优化。这种情况下出问题,领导会质疑你的专业能力。
我见过一个电商网站,双11前没有做任何容量评估,结果活动开始后系统直接崩了。运维临时加机器,但配置和部署都需要时间,白白损失了几个小时的黄金销售时间。
正确的容量规划方法:
# 容量规划脚本 capacity_planning.py
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import psutil
import mysql.connector
classCapacityPlanner:
def__init__(self):
self.metrics = []
defcollect_metrics(self):
"""收集系统指标"""
# CPU使用率
cpu_percent = psutil.cpu_percent(interval=1)
# 内存使用率
memory = psutil.virtual_memory()
memory_percent = memory.percent
# 磁盘使用率
disk = psutil.disk_usage('/')
disk_percent = (disk.used / disk.total) * 100
# 网络流量
net_io = psutil.net_io_counters()
# 数据库连接数
db_connections = self.get_db_connections()
metrics = {
'timestamp': datetime.now(),
'cpu_percent': cpu_percent,
'memory_percent': memory_percent,
'disk_percent': disk_percent,
'network_bytes_sent': net_io.bytes_sent,
'network_bytes_recv': net_io.bytes_recv,
'db_connections': db_connections
}
self.metrics.append(metrics)
return metrics
defget_db_connections(self):
"""获取数据库连接数"""
try:
conn = mysql.connector.connect(
host='localhost',
user='monitor',
password='password'
)
cursor = conn.cursor()
cursor.execute("SHOW STATUS LIKE 'Threads_connected'")
result = cursor.fetchone()
returnint(result[1]) if result else0
except:
return0
finally:
if'conn'inlocals():
conn.close()
defpredict_capacity(self, days_ahead=30):
"""预测未来容量需求"""
iflen(self.metrics) < 24: # 至少需要24小时数据
print("数据不足,无法进行预测")
return
df = pd.DataFrame(self.metrics)
# 计算增长趋势
cpu_trend = np.polyfit(range(len(df)), df['cpu_percent'], 1)[0]
memory_trend = np.polyfit(range(len(df)), df['memory_percent'], 1)[0]
db_trend = np.polyfit(range(len(df)), df['db_connections'], 1)[0]
# 预测未来值
future_cpu = df['cpu_percent'].iloc[-1] + cpu_trend * days_ahead * 24
future_memory = df['memory_percent'].iloc[-1] + memory_trend * days_ahead * 24
future_db = df['db_connections'].iloc[-1] + db_trend * days_ahead * 24
print(f"=== {days_ahead}天后容量预测 ===")
print(f"CPU使用率: {future_cpu:.1f}% (当前: {df['cpu_percent'].iloc[-1]:.1f}%)")
print(f"内存使用率: {future_memory:.1f}% (当前: {df['memory_percent'].iloc[-1]:.1f}%)")
print(f"数据库连接数: {int(future_db)} (当前: {df['db_connections'].iloc[-1]})")
# 容量建议
if future_cpu > 80:
print("⚠️ 预警:CPU使用率将超过80%,建议增加计算资源")
if future_memory > 80:
print("⚠️ 预警:内存使用率将超过80%,建议增加内存")
if future_db > 100:
print("⚠️ 预警:数据库连接数过高,建议优化连接池或增加数据库节点")
return {
'cpu_prediction': future_cpu,
'memory_prediction': future_memory,
'db_prediction': future_db
}
defgenerate_report(self):
"""生成容量报告"""
ifnotself.metrics:
print("没有收集到监控数据")
return
df = pd.DataFrame(self.metrics)
# 生成趋势图
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# CPU趋势
axes[0,0].plot(df['timestamp'], df['cpu_percent'])
axes[0,0].set_title('CPU使用率趋势')
axes[0,0].set_ylabel('CPU %')
# 内存趋势
axes[0,1].plot(df['timestamp'], df['memory_percent'])
axes[0,1].set_title('内存使用率趋势')
axes[0,1].set_ylabel('Memory %')
# 磁盘趋势
axes[1,0].plot(df['timestamp'], df['disk_percent'])
axes[1,0].set_title('磁盘使用率趋势')
axes[1,0].set_ylabel('Disk %')
# 数据库连接数趋势
axes[1,1].plot(df['timestamp'], df['db_connections'])
```python
# 数据库连接数趋势
axes[1,1].plot(df['timestamp'], df['db_connections'])
axes[1,1].set_title('数据库连接数趋势')
axes[1,1].set_ylabel('Connections')
plt.tight_layout()
plt.savefig('/tmp/capacity_report.png', dpi=300, bbox_inches='tight')
print("容量报告已生成: /tmp/capacity_report.png")
# 使用示例
if __name__ == "__main__":
planner = CapacityPlanner()
# 模拟收集24小时数据
for i inrange(24):
metrics = planner.collect_metrics()
print(f"收集第{i+1}小时数据: CPU {metrics['cpu_percent']}%, 内存 {metrics['memory_percent']}%")
time.sleep(3600) # 实际使用中每小时收集一次
# 生成预测报告
planner.predict_capacity(30)
planner.generate_report()而且要建立容量告警机制:
#!/bin/bash
# 容量预警脚本 capacity_alert.sh
# 获取当前资源使用情况
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | awk -F% '{print $1}')
MEMORY_USAGE=$(free | grep Mem | awk '{printf("%.2f", $3/$2 * 100.0)}')
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//')
# 数据库连接数
DB_CONNECTIONS=$(mysql -N -e "SHOW STATUS LIKE 'Threads_connected'" | awk '{print $2}')
echo"当前资源使用情况:"
echo"CPU: ${CPU_USAGE}%"
echo"内存: ${MEMORY_USAGE}%"
echo"磁盘: ${DISK_USAGE}%"
echo"数据库连接: ${DB_CONNECTIONS}"
# 容量预警阈值
CPU_WARN_THRESHOLD=70
CPU_CRIT_THRESHOLD=85
MEMORY_WARN_THRESHOLD=75
MEMORY_CRIT_THRESHOLD=90
DISK_WARN_THRESHOLD=80
DISK_CRIT_THRESHOLD=95
send_alert() {
local level=$1
local message=$2
local emoji=""
case$levelin
"WARNING") emoji="⚠️" ;;
"CRITICAL") emoji="🚨" ;;
esac
# 发送企业微信告警
curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$emoji 容量预警 $emoji\n\n$message\n\n时间: $(date)\n服务器: $(hostname)\"}}"
}
# 检查CPU
if (( $(echo "$CPU_USAGE > $CPU_CRIT_THRESHOLD" | bc -l) )); then
send_alert "CRITICAL""CPU使用率达到${CPU_USAGE}%,超过临界阈值${CPU_CRIT_THRESHOLD}%,请立即处理!"
elif (( $(echo "$CPU_USAGE > $CPU_WARN_THRESHOLD" | bc -l) )); then
send_alert "WARNING""CPU使用率达到${CPU_USAGE}%,超过预警阈值${CPU_WARN_THRESHOLD}%,请关注"
fi
# 检查内存
if (( $(echo "$MEMORY_USAGE > $MEMORY_CRIT_THRESHOLD" | bc -l) )); then
send_alert "CRITICAL""内存使用率达到${MEMORY_USAGE}%,超过临界阈值${MEMORY_CRIT_THRESHOLD}%,请立即处理!"
elif (( $(echo "$MEMORY_USAGE > $MEMORY_WARN_THRESHOLD" | bc -l) )); then
send_alert "WARNING""内存使用率达到${MEMORY_USAGE}%,超过预警阈值${MEMORY_WARN_THRESHOLD}%,请关注"
fi
# 检查磁盘
if [ "$DISK_USAGE" -gt "$DISK_CRIT_THRESHOLD" ]; then
send_alert "CRITICAL""磁盘使用率达到${DISK_USAGE}%,超过临界阈值${DISK_CRIT_THRESHOLD}%,请立即清理!"
elif [ "$DISK_USAGE" -gt "$DISK_WARN_THRESHOLD" ]; then
send_alert "WARNING""磁盘使用率达到${DISK_USAGE}%,超过预警阈值${DISK_WARN_THRESHOLD}%,请关注"
fi很多运维觉得内网系统很安全,不重视安全补丁的更新。等系统被攻击了,发现用的还是几年前的老版本,到处都是已知漏洞。
前年我见过一个案例,某公司用的还是Apache Struts 2.3版本,早就有远程代码执行漏洞了。黑客直接通过这个漏洞拿到了服务器权限,删除了大量数据。事后调查发现,这个漏洞早在3年前就被公开了,官方也发布了补丁,但运维一直没有更新。
正确的安全管理流程:
#!/bin/bash
# 安全漏洞扫描脚本 security_scan.sh
echo"开始系统安全扫描..."
echo"扫描时间: $(date)"
echo"================================"
# 1. 检查系统版本和补丁状态
echo"1. 系统版本信息:"
cat /etc/os-release
echo""
echo"2. 检查可用更新:"
ifcommand -v yum >/dev/null 2>&1; then
yum check-update | grep -E "(kernel|glibc|openssl|openssh)" || echo"核心组件暂无可用更新"
elifcommand -v apt >/dev/null 2>&1; then
apt list --upgradable 2>/dev/null | grep -E "(kernel|libc|openssl|openssh)" || echo"核心组件暂无可用更新"
fi
echo""
# 2. 检查已安装软件版本
echo"3. 关键软件版本检查:"
check_version() {
local software=$1
local current_version=""
case$softwarein
"apache")
ifcommand -v httpd >/dev/null 2>&1; then
current_version=$(httpd -v | grep "Apache" | awk '{print $3}')
echo"Apache版本: $current_version"
# 检查是否存在已知漏洞
if [[ "$current_version" < "Apache/2.4.41" ]]; then
echo"⚠️ 警告: Apache版本过低,存在安全风险"
fi
fi
;;
"nginx")
ifcommand -v nginx >/dev/null 2>&1; then
current_version=$(nginx -v 2>&1 | awk -F/ '{print $2}')
echo"Nginx版本: $current_version"
if [[ "$current_version" < "1.18.0" ]]; then
echo"⚠️ 警告: Nginx版本过低,建议升级"
fi
fi
;;
"mysql")
ifcommand -v mysql >/dev/null 2>&1; then
current_version=$(mysql --version | awk '{print $5}' | awk -F, '{print $1}')
echo"MySQL版本: $current_version"
fi
;;
"php")
ifcommand -v php >/dev/null 2>&1; then
current_version=$(php -v | head -n1 | awk '{print $2}')
echo"PHP版本: $current_version"
if [[ "$current_version" < "7.4" ]]; then
echo"🚨 严重: PHP版本过低,存在重大安全风险!"
fi
fi
;;
esac
}
check_version "apache"
check_version "nginx"
check_version "mysql"
check_version "php"
echo""
# 3. 检查SSH配置安全性
echo"4. SSH安全配置检查:"
ssh_config="/etc/ssh/sshd_config"
if [ -f "$ssh_config" ]; then
echo"检查SSH配置文件: $ssh_config"
# 检查root登录
root_login=$(grep "^PermitRootLogin"$ssh_config | awk '{print $2}')
if [ "$root_login" != "no" ]; then
echo"⚠️ 建议: 禁用root SSH登录 (PermitRootLogin no)"
else
echo"✓ SSH root登录已禁用"
fi
# 检查密码认证
password_auth=$(grep "^PasswordAuthentication"$ssh_config | awk '{print $2}')
if [ "$password_auth" != "no" ]; then
echo"⚠️ 建议: 禁用密码认证,使用密钥登录 (PasswordAuthentication no)"
else
echo"✓ SSH密码认证已禁用"
fi
# 检查SSH协议版本
protocol=$(grep "^Protocol"$ssh_config | awk '{print $2}')
if [ "$protocol" != "2" ] && [ -n "$protocol" ]; then
echo"⚠️ 建议: 使用SSH协议版本2 (Protocol 2)"
else
echo"✓ SSH协议配置正确"
fi
fi
echo""
# 4. 检查防火墙状态
echo"5. 防火墙状态检查:"
ifcommand -v ufw >/dev/null 2>&1; then
ufw_status=$(ufw status | head -n1 | awk '{print $2}')
echo"UFW防火墙状态: $ufw_status"
if [ "$ufw_status" != "active" ]; then
echo"⚠️ 建议: 启用防火墙保护"
fi
elifcommand -v firewall-cmd >/dev/null 2>&1; then
firewalld_status=$(systemctl is-active firewalld)
echo"Firewalld状态: $firewalld_status"
if [ "$firewalld_status" != "active" ]; then
echo"⚠️ 建议: 启用防火墙保护"
fi
elifcommand -v iptables >/dev/null 2>&1; then
iptables_rules=$(iptables -L | wc -l)
echo"Iptables规则数: $iptables_rules"
if [ "$iptables_rules" -lt 10 ]; then
echo"⚠️ 建议: 配置防火墙规则"
fi
fi
echo""
# 5. 检查异常登录
echo"6. 异常登录检查:"
echo"最近10次登录记录:"
last -n 10 | grep -v "reboot"
echo""
echo"失败登录尝试:"
lastb -n 5 2>/dev/null | head -n 5 || echo"暂无失败登录记录"
echo""
# 6. 检查可疑进程
echo"7. 系统进程检查:"
echo"高CPU占用进程:"
ps aux --sort=-%cpu | head -n 6
echo""
echo"高内存占用进程:"
ps aux --sort=-%mem | head -n 6
echo""
# 7. 检查网络连接
echo"8. 网络连接检查:"
echo"监听端口:"
netstat -tlnp | grep LISTEN | head -n 10
echo""
echo"外部连接:"
netstat -tn | grep ESTABLISHED | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -nr | head -n 5
echo""
# 8. 生成安全评分
security_score=100
issues=0
# 根据发现的问题扣分
if [[ "$root_login" != "no" ]]; then
security_score=$((security_score - 15))
issues=$((issues + 1))
fi
if [[ "$password_auth" != "no" ]]; then
security_score=$((security_score - 10))
issues=$((issues + 1))
fi
ifcommand -v php >/dev/null 2>&1; then
php_version=$(php -v | head -n1 | awk '{print $2}')
if [[ "$php_version" < "7.4" ]]; then
security_score=$((security_score - 20))
issues=$((issues + 1))
fi
fi
echo"================================"
echo"安全评估结果:"
echo"发现问题: $issues 个"
echo"安全评分: $security_score/100"
if [ $security_score -ge 90 ]; then
echo"✅ 安全状态: 良好"
elif [ $security_score -ge 70 ]; then
echo"⚠️ 安全状态: 一般,建议改进"
else
echo"🚨 安全状态: 危险,请立即处理!"
fi
echo""
echo "扫描完成时间: $(date)"定期更新系统补丁:
#!/bin/bash
# 自动化补丁更新脚本 patch_update.sh
LOG_FILE="/var/log/patch_update.log"
BACKUP_DIR="/backup/system_backup"
log_message() {
echo"[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}
log_message "开始系统补丁更新流程"
# 1. 系统备份
log_message "创建系统备份..."
mkdir -p $BACKUP_DIR/$(date +%Y%m%d)
# 备份关键配置文件
tar -czf $BACKUP_DIR/$(date +%Y%m%d)/system_config.tar.gz \
/etc/nginx/ \
/etc/apache2/ \
/etc/mysql/ \
/etc/ssh/sshd_config \
/etc/crontab \
/etc/fstab \
2>/dev/null
log_message "系统配置备份完成"
# 2. 更新软件包列表
ifcommand -v apt >/dev/null 2>&1; then
log_message "更新APT软件包列表..."
apt update >> $LOG_FILE 2>&1
# 检查可用更新
UPDATES=$(apt list --upgradable 2>/dev/null | wc -l)
log_message "发现 $((UPDATES-1)) 个可用更新"
if [ $UPDATES -gt 1 ]; then
# 更新安全补丁
log_message "安装安全更新..."
apt upgrade -y >> $LOG_FILE 2>&1
# 更新内核(如果需要)
if apt list --upgradable 2>/dev/null | grep -q linux-image; then
log_message "发现内核更新,安装中..."
apt install -y linux-image-generic >> $LOG_FILE 2>&1
echo"需要重启系统以应用内核更新" | tee -a $LOG_FILE
fi
fi
elifcommand -v yum >/dev/null 2>&1; then
log_message "更新YUM软件包..."
yum check-update >> $LOG_FILE 2>&1
# 只更新安全补丁
log_message "安装安全更新..."
yum update -y --security >> $LOG_FILE 2>&1
# 检查内核更新
if yum check-update kernel 2>/dev/null | grep -q kernel; then
log_message "发现内核更新,安装中..."
yum update -y kernel >> $LOG_FILE 2>&1
echo"需要重启系统以应用内核更新" | tee -a $LOG_FILE
fi
fi
# 3. 清理旧的软件包
log_message "清理系统缓存..."
ifcommand -v apt >/dev/null 2>&1; then
apt autoremove -y >> $LOG_FILE 2>&1
apt autoclean >> $LOG_FILE 2>&1
elifcommand -v yum >/dev/null 2>&1; then
yum autoremove -y >> $LOG_FILE 2>&1
yum clean all >> $LOG_FILE 2>&1
fi
# 4. 验证关键服务状态
log_message "验证服务状态..."
services=("nginx""apache2""mysql""ssh")
for service in"${services[@]}"; do
if systemctl is-active --quiet $service; then
log_message "✓ $service 服务运行正常"
elif systemctl is-enabled --quiet $service; then
log_message "⚠️ $service 服务已启用但未运行,尝试重启..."
systemctl restart $service
if systemctl is-active --quiet $service; then
log_message "✓ $service 服务重启成功"
else
log_message "❌ $service 服务重启失败,需要人工处理"
fi
fi
done
# 5. 发送更新报告
UPDATED_PACKAGES=$(grep "upgraded"$LOG_FILE | tail -n 10)
RESTART_REQUIRED=$([ -f /var/run/reboot-required ] && echo"是" || echo"否")
cat > /tmp/patch_report.txt << EOF
系统补丁更新报告
更新时间: $(date)
服务器: $(hostname)
操作系统: $(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)
更新摘要:
$(echo "$UPDATED_PACKAGES" | head -n 5)
需要重启: $RESTART_REQUIRED
详细日志请查看: $LOG_FILE
EOF
# 发送企业微信通知
curl -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your-key" \
-H "Content-Type: application/json" \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"📋 系统补丁更新完成\n\n$(cat /tmp/patch_report.txt)\"}}"
log_message "补丁更新流程完成"
rm -f /tmp/patch_report.txt很多运维觉得写文档浪费时间,所有的操作步骤、配置细节都记在脑子里。等你请假或者离职了,其他人接手就抓瞎了。出了问题没人能处理,最后还是要找你背锅。
我见过一个运维,负责公司的核心业务系统,从来不写文档。有次他生病住院一周,期间系统出了个小问题,其他同事完全不知道怎么处理,只能等他出院。老板很不满意:"怎么离了一个人系统就维护不了?"
正确的文档管理方式:
# 系统运维手册
## 1. 系统架构概览
### 1.1 服务器清单
| 服务器名称 | IP地址 | 角色 | 配置 | 负责人 |
|-----------|--------|------|------|--------|
| web-prod-01 | 192.168.1.10 | Web服务器 | 8C16G | 老杨 |
| web-prod-02 | 192.168.1.11 | Web服务器 | 8C16G | 老杨 |
| db-prod-01 | 192.168.1.20 | MySQL主库 | 16C32G | 老杨 |
| db-prod-02 | 192.168.1.21 | MySQL从库 | 16C32G | 小张 |
### 1.2 网络拓扑Internet ↓ [负载均衡器 F5] ↓ [Web服务器集群] ↓ [数据库集群]
### 1.3 监控系统
- Zabbix: http://monitor.internal.com
- Grafana: http://grafana.internal.com
- 日志系统: http://logs.internal.com
## 2. 常见故障处理
### 2.1 网站无法访问
**症状**: 用户反馈网站打不开,返回502错误
**排查步骤**:
1. 检查负载均衡器状态
```bash
curl -I http://192.168.1.10
# 预期结果: HTTP/1.1 200 OK2. 检查Web服务器状态
systemctl status nginx
# 预期结果: Active: active (running)3. 检查应用程序状态
ps aux | grep php-fpm
# 应该看到php-fpm进程运行4. 查看错误日志
tail -f /var/log/nginx/error.log
tail -f /var/log/php7.4-fpm.log常见原因及解决方案:
nginx -tsystemctl restart php7.4-fpm症状: 应用程序报"Database connection failed"错误
排查步骤:
1. 检查MySQL服务状态
systemctl status mysql
mysqladmin -u root -p ping2. 检查连接数
SHOW PROCESSLIST;
SHOW STATUS LIKE 'Threads_connected';
SHOW VARIABLES LIKE 'max_connections';3. 检查磁盘空间
df -h /var/lib/mysql4. 检查错误日志
tail -f /var/log/mysql/error.log解决方案:
症状: 系统响应慢,load average > 10
排查步骤:
1. 查看系统负载
uptime
top -c
htop # 如果安装了2. 查看IO情况
iostat -x 1
iotop # 查看哪个进程IO最高3. 查看内存使用
free -h
ps aux --sort=-%mem | head -204. 查看网络连接
netstat -ant | wc -l
ss -s#!/bin/bash
# daily_check.sh - 每日系统检查脚本
echo"=== 每日系统检查 $(date) ==="
# 1. 系统负载
echo"1. 系统负载:"
uptime
# 2. 磁盘空间
echo"2. 磁盘使用率:"
df -h | grep -E "(Filesystem|/dev/)"
# 3. 内存使用
echo"3. 内存使用情况:"
free -h
# 4. 服务状态
echo"4. 关键服务状态:"
services=("nginx""mysql""php7.4-fpm""redis")
for service in"${services[@]}"; do
if systemctl is-active --quiet $service; then
echo"✓ $service: 运行正常"
else
echo"❌ $service: 异常"
fi
done
# 5. 数据库状态
echo"5. 数据库连接数:"
mysql -N -e "SHOW STATUS LIKE 'Threads_connected'" | awk '{print "当前连接数: " $2}'
# 6. 备份状态
echo"6. 昨日备份检查:"
YESTERDAY=$(date -d "yesterday" +%Y%m%d)
if [ -f "/backup/mysql/db_${YESTERDAY}.sql.gz" ]; then
BACKUP_SIZE=$(stat -c%s "/backup/mysql/db_${YESTERDAY}.sql.gz")
echo"✓ 数据库备份存在,大小: $(($BACKUP_SIZE / 1024 / 1024))MB"
else
echo"❌ 数据库备份不存在"
fi
echo "=== 检查完成 ==="#!/bin/bash
# weekly_maintenance.sh - 每周维护任务
echo"开始每周维护任务..."
# 1. 清理旧日志
find /var/log -name "*.log.*" -mtime +7 -delete
find /var/log -name "*.[0-9].gz" -mtime +7 -delete
# 2. 清理临时文件
find /tmp -type f -mtime +3 -delete
# 3. 更新系统软件包列表
apt update
# 4. 检查可用更新
apt list --upgradable > /tmp/available_updates.txt
# 5. 清理Docker镜像(如果使用)
ifcommand -v docker >/dev/null 2>&1; then
docker system prune -f
fi
# 6. 检查磁盘碎片(ext4文件系统)
for partition in $(df -t ext4 --output=source | grep -v "Filesystem"); do
echo"检查分区 $partition 的碎片率..."
e2fsck -fn $partition 2>&1 | grep "non-contiguous"
done
echo "每周维护任务完成"角色 | 姓名 | 电话 | 微信 | 邮箱 |
|---|---|---|---|---|
运维主管 | 老杨 | 138-0000-0000 | yang_devops | yang@company.com |
运维工程师 | 小张 | 139-0000-0000 | zhang_ops | zhang@company.com |
DBA | 小李 | 137-0000-0000 | li_dba | li@company.com |
开发负责人 | 小王 | 136-0000-0000 | wang_dev | wang@company.com |
⚠️ 注意: 密码信息存储在密码管理系统中,具体位置:
版本 | 日期 | 修改人 | 修改内容 |
|---|---|---|---|
v1.0 | 2024-01-01 | 老杨 | 初始版本 |
v1.1 | 2024-02-01 | 老杨 | 增加Docker维护步骤 |
v1.2 | 2024-03-01 | 小张 | 更新联系方式 |
而且要建立知识分享机制:
```bash
#!/bin/bash
# knowledge_share.sh - 知识分享脚本,记录每次操作
KNOWLEDGE_DB="/var/log/ops_knowledge.md"
add_knowledge() {
local category=$1
local title=$2
local content=$3
local operator=${4:-$(whoami)}
cat >> $KNOWLEDGE_DB << EOF
## $category - $title
**时间**: $(date)
**操作人**: $operator
### 问题描述
$content
### 解决步骤
\`\`\`bash
# 在这里记录具体的操作命令
\`\`\`
### 经验总结
-
-
---
EOF
echo "知识条目已添加到 $KNOWLEDGE_DB"
}
# 使用示例
# add_knowledge "故障处理" "MySQL主从同步延迟" "发现主从延迟超过100秒,影响数据一致性"这样做的好处是,任何人都能快速上手系统维护,不会因为某个人不在就无法处理问题。而且新人入职时,有完整的文档可以参考,能快速熟悉系统。
这里老杨先声明一下,日常生活中大家都叫老杨波哥,跟辈分没关系,主要是岁数大了.就一个代称而已. 老杨的00后小同事老杨喊都是带哥的.张哥,李哥的. 但是这个称呼呀,在线下参加一些活动时.金主爸爸也这么叫就显的不太合适. 比如上次某集团策划总监,公司开大会来一句:“今个咱高兴!有请IT运维技术圈的波哥讲两句“ 这个氛围配这个称呼在互联网这行来讲就有点对不齐! 每次遇到这个情况老杨就想这么接话: “遇到各位是缘分,承蒙厚爱,啥也别说了,都在酒里了.老杨干了,你们随意!” 所以以后咱们改叫老杨,即市井又低调.还挺亲切,老杨觉得挺好.
运维X档案系列文章:
企业级 Kubernetes 集群安全加固全攻略( 附带一键检查脚本)
看完别走.修行在于点赞、转发、在看.攒今世之功德,修来世之福报
老杨AI的号: 98dev