项目环境:CentOS 7.9 Kafka 3.6.1 Celery5.0 redis Nginx Filebeat 7.x。
项目描述:设计并部署高可用日志收集与分析平台,实现从nginx反向代理、采集nginx日志数据(Filebeat)、传输到(Kafka)、到设置定时任务(Celery+redis)进行监控告警的全链路自动化。
-
基于Kafka KRaft模式构建无ZooKeeper集群,支持高吞吐日志存储与多消费者并发处理。
-
利用Nginx反向代理后端的flask程序,保障服务高可用性。
-
通过Filebeat实时采集Nginx日志并推送至Kafka。
-
使用python-kafka并发消费数据,并把清洗后的数据存入MySQL数据库。
-
结合Celery+Redis定时监控日志数据中的流量,超过阈值时发送邮件警告。
目录
项目规划图
项目步骤
1. 环境准备
-
依赖软件安装
yum源配置:
cd /etc/yum.repos.d
mkdir repo
mv *.repo repo/
下载阿里云源:
curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
下载依赖软件:
yum install epel-release -y
yum install wget vim java-11-openjdk.x86_64 -y
-
配置静态ip地址,修改/etc/sysconfig/network-scripts/ifcfg-ens33
[root@wcw ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO="none"
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
NAME=ens33
UUID=246e853f-0055-482d-b669-4d3d97bf163a
DEVICE=ens33
ONBOOT=yes
IPADDR=192.168.10.146
PREFIX=24
#NETMASK=255.255.255.0
GATEWAY=192.168.10.2
DNS1=114.114.114.114
DNS2=192.168.10.2
-
配置主机名(以kafka1为例)
hostnamectl set-hostname kafka1
-
修改/etc/hosts文件,添加主机名和ip地址映射
192.168.10.146 kafka1
192.168.10.147 kafka2
192.168.10.148 kafka3
-
关闭防火墙与selinux
关闭防火墙:
iptables -F #清空防火墙规则
systemctl stop firewalld #关闭防火墙服务
systemctl disable firewalld #设置开机不自启
关闭selinux,编辑/etc/selinux/config 文件
[root@kafka1 kafka_2.13-3.6.1]# vim /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
#SELINUX=enforcing
SELINUX=disabled
# SELINUXTYPE= can take one of three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targeted
重启系统:
reboot
2.部署kafka集群
使用kafka的好处:
高吞吐:支持海量日志实时写入,避免数据丢失。
持久化:日志可保留多天,供后续回溯或批量分析。
分区与副本:通过分区提升并行处理能力,副本机制保障高可用性。
-
下载kafka
cd /opt
wget https://archive.apache.org/dist/kafka/3.6.1/kafka_2.13-3.6.1.tgz
-
解压缩
tar xf kafka_2.13-3.6.1.tgz
cd kafka_2.13-3.6.1
-
修改配置文件,位于kafka目录下config/kraft/server.properties
[root@kafka1 opt]# cd kafka_2.13-3.6.1
[root@kafka1 kafka_2.13-3.6.1]# ls
bin config libs LICENSE licenses logs NOTICE site-docs tmp_random
[root@kafka1 kafka_2.13-3.6.1]# vim /etc/selinux/config
[root@kafka1 kafka_2.13-3.6.1]# vim config/kraft/server.properties
三个节点都需要配置(每个节点id唯一)
#修改节点id,每个节点唯一
node.id=1
#修改控制器投票列表
controller.quorum.voters=1@192.168.10.146:9093,2@192.168.10.147:9093,3@192.168.10.148:9093
#修改监听器和控制器,绑定ip。其中kafka1为主机名,可用本机ip地址代替
listeners=PLAINTEXT://kafka1:9092,CONTROLLER://kafka1:9093
# 侦听器名称、主机名和代理将向客户端公布的端口.(broker 对外暴露的地址)
# 如果未设置,则使用"listeners"的值.
advertised.listeners=PLAINTEXT://kafka1:9092
-
配置文件详解
############################# Server Basics #############################
# 此服务器的角色。设置此项将进入KRaft模式(controller 相当于主机、broker 节点相当于从机,主机类似 zk 功能)
process.roles=broker,controller
# 节点 ID
node.id=1
# 全 Controller 列表
controller.quorum.voters=1@192.168.10.146:9093,2@192.168.10.147:9093,3@192.168.10.148:9093
############################# Socket Server Settings #############################
# 套接字服务器侦听的地址.
# 组合节点(即具有`process.roles=broker,controller`的节点)必须至少在此处列出控制器侦听器
# 如果没有定义代理侦听器,那么默认侦听器将使用一个等于java.net.InetAddress.getCanonicalHostName()值的主机名,
# 带有PLAINTEXT侦听器名称和端口9092
# FORMAT:
# listeners = listener_name://host_name:port
# EXAMPLE:
# listeners = PLAINTEXT://your.host.name:9092
#不同服务器绑定的端口
listeners=PLAINTEXT://kafka1:9092,CONTROLLER://kafka1:9093
# 用于代理之间通信的侦听器的名称(broker 服务协议别名)
inter.broker.listener.name=PLAINTEXT
# 侦听器名称、主机名和代理将向客户端公布的端口.(broker 对外暴露的地址)
# 如果未设置,则使用"listeners"的值.
advertised.listeners=PLAINTEXT://kafka1:9092
# controller 服务协议别名
# 控制器使用的侦听器名称的逗号分隔列表
# 如果`listener.security.protocol.map`中未设置显式映射,则默认使用PLAINTEXT协议
# 如果在KRaft模式下运行,这是必需的。
controller.listener.names=CONTROLLER
# 将侦听器名称映射到安全协议,默认情况下它们是相同的。(协议别名到安全协议的映射)有关更多详细信息,请参阅配置文档.
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,SSL:SSL,SASL_PLAINTEXT:SASL_PLAINTEXT,SASL_SSL:SASL_SSL
# 服务器用于从网络接收请求并向网络发送响应的线程数
num.network.threads=3
# 服务器用于处理请求的线程数,其中可能包括磁盘I/O
num.io.threads=8
# 套接字服务器使用的发送缓冲区(SO_SNDBUF)
socket.send.buffer.bytes=102400
# 套接字服务器使用的接收缓冲区(SO_RCVBUF)
socket.receive.buffer.bytes=102400
# 套接字服务器将接受的请求的最大大小(防止OOM)
socket.request.max.bytes=104857600
############################# Log Basics #############################
# 存储日志文件的目录的逗号分隔列表(kafka 数据存储目录)
log.dirs=/usr/kafka/kafka_2.13-3.6.1/datas
# 每个主题的默认日志分区数。更多的分区允许更大的并行性以供使用,但这也会导致代理之间有更多的文件。
num.partitions=1
# 启动时用于日志恢复和关闭时用于刷新的每个数据目录的线程数。
# 对于数据目录位于RAID阵列中的安装,建议增加此值。
num.recovery.threads.per.data.dir=1
############################# Internal Topic Settings #############################
# 组元数据内部主题"__consumer_offsets"和"__transaction_state"的复制因子
# 对于除开发测试以外的任何测试,建议使用大于1的值来确保可用性,例如3.
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
############################# Log Flush Policy #############################
# 消息会立即写入文件系统,但默认情况下,我们只使用fsync()进行同步
# 操作系统缓存延迟。以下配置控制将数据刷新到磁盘.
# 这里有一些重要的权衡:
# 1. Durability(持久性): 如果不使用复制,未清理的数据可能会丢失
# 2. Latency(延迟): 当刷新发生时,非常大的刷新间隔可能会导致延迟峰值,因为将有大量数据要刷新.
# 3. Throughput(吞吐量): 刷新通常是最昂贵的操作,较小的刷新间隔可能导致过多的寻道.
# 下面的设置允许配置刷新策略,以便在一段时间后或每N条消息(或两者兼有)刷新数据。这可以全局完成,并在每个主题的基础上覆盖
# 强制将数据刷新到磁盘之前要接受的消息数
#log.flush.interval.messages=10000
# 在我们强制刷新之前,消息可以在日志中停留的最长时间
#log.flush.interval.ms=1000
############################# Log Retention Policy #############################
# 以下配置控制日志段的处理。可以将该策略设置为在一段时间后删除分段,或者在累积了给定大小之后删除分段。
# 只要满足这些条件中的任意一个,segment就会被删除。删除总是从日志的末尾开始
# 日志文件因使用年限而有资格删除的最短使用年限
log.retention.hours=168
# 基于大小的日志保留策略。除非剩余的段低于log.retention.bytes,否则将从日志中删除段。独立于log.retention.hours的函数。
#log.retention.bytes=1073741824
# 日志segment文件的最大大小。当达到此大小时,将创建一个新的日志segment
log.segment.bytes=1073741824
# 检查日志segments以查看是否可以根据保留策略删除它们的间隔
log.retention.check.interval.ms=300000
-
创建集群
cd /opt/kafka_2.13-3.6.1
# 在其中一台执行,生成集群UUID命令,拿到集群UUID保存在当前tmp_random文件中
bin/kafka-storage.sh random-uuid >tmp_random
# 查看uuid
[root@kafka1 kafka_2.13-3.6.1]# cat tmp_random
GYTCxaoiRRidOSrStGI20g
# 在所有机器上执行,它会初始化存储区域,为 Kafka 集群的元数据存储和后续操作做好准备。GYTCxaoiRRidOSrStGI20g为自己生成的集群uuid
bin/kafka-storage.sh format -t GYTCxaoiRRidOSrStGI20g -c /opt/kafka_2.13-3.6.1/config/kraft/server.properties
-
启动
1. 命令行启动:
启动:
bin/kafka-server-start.sh -daemon /opt/kafka_2.13-3.6.1/config/kraft/server.properties
关闭:
bin/kafka-server-stop.sh
2. 使用systemctl管理服务 -- systemd(更推荐)
## 编辑文件 /usr/lib/systemd/system/kafka.service
[root@kafka1 kafka_2.13-3.6.1]# vim /usr/lib/systemd/system/kafka.service
[Unit]
Description=Apache Kafka server (KRaft mode)
Documentation=http://kafka.apache.org/documentation.html
After=network.target
[Service]
Type=forking
User=root
Group=root
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/lib/jvm/java-11-openjdk-11.0.23.0.9-2.el7_9.x86_64/bin/"
ExecStart=/opt/kafka_2.13-3.6.1/bin/kafka-server-start.sh -daemon /opt/kafka_2.13-3.6.1/config/kraft/server.properties
ExecStop=/opt/kafka_2.13-3.6.1/bin/kafka-server-stop.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target
#重新加载systemd配置
systemctl daemon-reload
#启动kafka服务
systemctl start kafka
#关闭kafka服务
systemctl stop kafka
#设置开机自启
systemctl enable kafka
确认Kafka已启动
-
测试集群
# 创建topic
bin/kafka-topics.sh --create --bootstrap-server kafka3:9092 --replication-factor 3 --partitions 3 --topic test_topic
** --replication-factor指定副本因子,--partitions指定分区数,--topic指定主题名称。
# 查看topic
bin/kafka-topics.sh --list --bootstrap-server kafka3:9092
#创建生产者,发送消息,测试用
bin/kafka-console-producer.sh --broker-list kafka3:9092 --topic test_topic
#创建消费者,获取数据,测试用
bin/kafka-console-consumer.sh --bootstrap-server kafka2:9092 --topic test_topic --from-beginning
在kafka3 消费数据
3.nginx反向代理集群搭建
反向代理:代理服务器
目的:隐藏后端真实服务器,更加安全负载均衡和高可用。
例如:
-
安装nginx
yum install epel-release -y
yum install nginx -y
-
编辑配置文件 /etc/nginx/conf.d/sc.conf
upstream flask {
server 192.168.10.147:5000;
server 192.168.10.148:5000;
}
server {
listen 80;
server_name www.sc.com;
location / {
proxy_pass http://flask;
}
}
- 启动nginx
systemctl start nginx
4.后端flask程序
- 安装flask环境
yum install python3 -y
pip3 install flask -i https://pypi.tuna.tsinghua.edu.cn/simple
- 编辑/opt/python-flask/app.py文件(kafka2)
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "this is flask web kafka2"
app.run(host = "0.0.0.0")
- 编辑/opt/python-flask/app.py文件(kafka3)
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "this is flask web kafka3"
app.run(host = "0.0.0.0")
- 启动flask
python3 app.py
可以通过nginx访问kafka2和kafka3。
5.部署filebeat(在nginx服务器上)
Filebeat是用于转发和集中日志数据的轻量级传送工具。属于 Elastic Stack(ELK Stack)的一部分,主要用于从服务器、容器、云服务等环境中收集日志数据,并将其传输到 Elasticsearch 或 Logstash 进行存储、分析和可视化。
这里是用来实时采集nginx日志数据并推到kafka集群中。好处是日志集中方便管理,不需要登录到每一台服务器上查看日志,同时对日志处理解耦,不影响到nginx。
- 安装
1、rpm --import https://packages.elastic.co/GPG-KEY-elasticsearch
2、编辑 vim /etc/yum.repos.d/fb.repo
[elastic-7.x]
name=Elastic repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
3、yum安装
yum install filebeat -y
rpm -qa |grep filebeat 可以查看filebeat有没有安装 rpm -qa 是查看机器上安装的所有软件包
rpm -ql filebeat 查看filebeat安装到哪里去了,牵扯的文件有哪些
- 配置,修改配置文件/etc/filebeat/filebeat.yml
filebeat.inputs:
- type: log
# Change to true to enable this input configuration.
enabled: true
# Paths that should be crawled and fetched. Glob based paths.
paths:
- /var/log/nginx/access.log
- /var/log/nginx/error.log
#==========------------------------------kafka-----------------------------------
output.kafka:
hosts: ["192.168.10.146:9092","192.168.10.147:9092","192.168.10.148:9092"]
topic: nginxlog
keep_alive: 10s
- 创建主题
cd /opt/kafka_2.13-3.6.1
bin/kafka-topics.sh --create --bootstrap-server kafka3:9092 --replication-factor 3 --partitions 3 --topic nginxlog
- 启动服务
systemctl start filebeat
systemctl enable filebeat #设置开机自启
- 使用kafka-python并发消费日志
使用kafka-python并发消费日志数据,并对日志数据进行清洗,提取(时间,IP地址,省份,运营商,流量数据),其中省份和运营商通过解析ip得到。
解析IP地址的网页:http://whois.pconline.com.cn/ipJson.jsp?ip=123.123.123.123&json=true
config.py文件:
IP_URL = "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip="
DB_HOST = 数据库IP
DB_PORT = 端口号
DB_USER = 用户名
DB_PASSWD = 密码
DB = 数据库名称
lib.py文件:
import json
import requests
from config import *
import time
# message --> dict
def json_to_dict(message: str) ->dict: # 期望输入为str类型,返回值为dict类型
d1 = {}
try:
d1 = json.loads(message)
except:
print("传递非json格式字符串")
return d1
def resolv_ip(ip):
# 使用requests库 --相当于python浏览器,请求网址
url = IP_URL+ip
response =requests.get(url)
# print( dir(response) )
prov = response.json().get("pro")
isp = response.json().get("addr").split()[1]
return prov,isp
# 时间格式转换
def time_format(time_str):
# 处理带时区的时间字符串
struct_time = time.strptime(time_str, "%d/%b/%Y:%H:%M:%S")
return time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
# 192.168.10.1 - - [10/Mar/2025:20:47:24 +0800] \"GET / HTTP/1.1\" 200 24 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0\" \"-\"
def handler_log(log_str):
# 修复格式问题
log_str = log_str.replace('--[', '- - [')
log_str = log_str.replace('\"\"-\"', '""-"')
log_lst = log_str.split()
if len(log_lst) < 10:
raise ValueError("日志格式不完整")
ip = log_lst[0]
time_str = log_lst[3][1:] # 移除开头的[
status_code = log_lst[8]
flow = int(log_lst[9]) if len(log_lst) >= 10 else 0
prov, isp = resolv_ip(ip)
resukt_time = time_format(time_str)
return resukt_time, ip, prov, isp, flow
consum.py文件:
from kafka import KafkaConsumer
from multiprocessing import Process, current_process
import time
import lib
import pymysql
from config import *
def consume_kafka_partition(topic, group_id, partition):
"""
进程执行的函数,用于消费指定分区的Kafka消息
"""
consumer = KafkaConsumer(
group_id=group_id,
bootstrap_servers=['192.168.10.146:9092', '192.168.10.147:9092', '192.168.10.148:9092',],
auto_offset_reset='earliest', # 可按需调整偏移量重置策略
enable_auto_commit=True, # 自动提交偏移量
auto_commit_interval_ms=5000, # 每5秒提交一次偏移量
value_deserializer=lambda x: x.decode('utf-8') # 将消息值解码为UTF-8字符串 - (反序列化)
)
# 为消费者订阅的主题
consumer.subscribe(topic)
# 初始化数据库连接
conn = pymysql.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASSWD,
db=DB,
port=DB_PORT)
cursor = conn.cursor()
# 创建表(如果不存在)
create_table_sql = """
CREATE TABLE IF NOT EXISTS nginx_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
timestamp DATETIME,
ip VARCHAR(45),
province VARCHAR(45),
isp VARCHAR(45),
flow INT
)
"""
cursor.execute(create_table_sql)
conn.commit()
try:
for message in consumer:
log_str = message.value
try:
# 处理日志数据
resukt_time, ip, prov, isp, flow = lib.handler_log(log_str)
# 插入数据库
insert_sql = """
INSERT INTO nginx_logs (timestamp, ip, province, isp, flow)
VALUES (%s, %s, %s, %s, %s)
"""
cursor.execute(insert_sql, (resukt_time, ip, prov, isp, flow))
conn.commit()
print(f"成功插入数据: {ip} - {resukt_time}")
except Exception as e:
print(f"处理日志失败: {e}")
conn.rollback()
except Exception as e:
print(f"消费者异常: {e}")
finally:
# 关闭连接
cursor.close()
conn.close()
consumer.close()
if __name__ == "__main__":
topic = "nginxlog" # 根据实际情况修改要消费的主题名称
group_id = "message_group3" # 根据实际情况修改消费组名称 只要更改消费组的名称就可以重新拿取数据(所有的旧数据)
partitions = [0, 1, 2] # 定义要消费的分区列表,可按需调整
# 设置进程名称(可选)
def start_process(target, args):
p = Process(target=target, args=args)
p.name = f"Consumer-Partition-{args[2]}" # 设置进程名称
p.start()
return p
processes = []
for partition in partitions:
p = start_process(consume_kafka_partition, (topic, group_id, partition))
processes.append(p)
for p in processes:
p.join()
print("所有进程已结束,指定分区消费完成")
6.celery+redis
Celery为异步任务队列,Redis作为消息代理和缓存,实现日志的并行处理与加速。
- redis安装
yum install redis -y
- redis 配置文件修改 /etc/redis.conf
bind 0.0.0.0 #监听本机任意ip
- 启动服务
systemctl start redis
systemctl restart redis # 如果已经启动就restart重启
- redis详解
端口:6379
redis:key-value 存储系统,是跨平台的非关系型数据库。
redis支持的存储类型:
String: 字符串
Hash: 散列
List: 列表
Set: 集合
Sorted Set: 有序集合不同的数据类型对应不同的指令
可以做消息中间件,可以做消息队列,可以做缓存 -- memcache
redis持久化:
RDB 持久化是通过对 Redis 中的数据进行快照(snapshot)来实现的。在指定的时间间隔内,Redis 会将内存中的数据集快照写入磁盘上的一个临时文件,成功后再将这个临时文件替换为之前的 RDB 文件。
AOF 持久化是以日志的形式记录 Redis 服务器所执行的每一个写操作(如 SET、LPUSH 等命令)。这些写操作命令会按照执行的先后顺序追加到 AOF 文件的末尾。
小示例:
[root@kafka1 ~]# redis-cli
字符串类型的存储:
127.0.0.1:6379> set www "hello world" 键值对的方式存储
OK
127.0.0.1:6379> get www
"hello world"
127.0.0.1:6379> key *
(error) ERR unknown command 'key'
127.0.0.1:6379> keys *
1) "www"
127.0.0.1:6379> set www1 "xxx"
OK
127.0.0.1:6379> keys www*
1) "www1"
2) "www"
哈希类型:
127.0.0.1:6379> hmset root name "root" age 18
OK
127.0.0.1:6379> hgetall root
1) "name"
2) "root"
3) "age"
4) "18"
127.0.0.1:6379> hget root name
"root"
- python库安装
pip3 install celery -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
pip3 install redis
- celery配置
cd /opt
mkdir monitor/celery_app -p
在celery_app中
编辑配置文件 config.py
from celery.schedules import crontab
BROKER_URL = 'redis://192.168.10.146:6379/0' # Broker配置,使用Redis作为消息中间件
CELERY_RESULT_BACKEND = 'redis://192.168.10.146:6379/1' # BACKEND配置,这里使用redis
CELERY_RESULT_SERIALIZER = 'json' # 结果序列化方案
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任务过期时间
CELERY_TIMEZONE = 'Asia/Shanghai' # 时区配置
CELERY_IMPORTS = ( # 指定导入的任务模块,可以指定多个
'celery_app.task',
)
# 添加阈值配置
TRAFFIC_THRESHOLD = 1000
CELERYBEAT_SCHEDULE = {
'celery_app.task.monitor_traffic': {
'task': 'celery_app.task.monitor_traffic',
'schedule': crontab(minute='*/1'),
'args': ()
}
}
编辑__init__.py (双下划线开头,双下划线结尾)
# 初始化模块,运行包之前会先运行__int__.py
from celery import Celery
app = Celery('task')
app.config_from_object('celery_app.config')
编辑task.py
import sqlite3
from . import app
from .smtp_send import send_email
from .config import TRAFFIC_THRESHOLD
@app.task
def monitor_traffic():
print("Monitoring traffic...")
# 连接数据库
conn = sqlite3.connect('traffic.db')
cursor = conn.cursor()
# 查询最近一分钟的流量总和
cursor.execute("SELECT SUM(traffic) FROM traffic_data WHERE timestamp >= datetime('now', '-1 minute')")
total_traffic = cursor.fetchone()[0]
conn.close()
if total_traffic is not None and total_traffic > TRAFFIC_THRESHOLD:
content = f"流量已超过阈值,当前流量值为: {total_traffic}"
send_email(content)
print(content)
else:
print("流量未超过阈值")
编辑smtp-send.py
# smtplib 发邮件 email 构建邮件内容
import smtplib
from email.mime.text import MIMEText
from email.header import Header
from email.utils import formataddr
def send_email(content, sender=发送者邮箱, password=密码, receiver=接收者邮箱, subject='流量告警'):
# 邮件内容
mail_content = content
message = MIMEText(mail_content, 'plain', 'utf-8')
message['From'] = formataddr((str(Header('wcw', 'utf-8')), sender))
message['To'] = Header("lf", 'utf-8')
message['Subject'] = Header(subject, 'utf-8')
try:
# 使用SSL加密连接(以QQ邮箱为例)
smtpObj = smtplib.SMTP_SSL("smtp.qq.com", 465)
smtpObj.login(sender, password)
smtpObj.sendmail(sender, receiver, message.as_string())
print("邮件发送成功")
except smtplib.SMTPException as e:
print("邮件发送失败:", e)
- celery 启动beat (在monitor目录下执行)
celery -A celery_app beat
- celery 启动worker
celery -A celery_app worker -l info -c 4