MinIO 的“将事件发布至 Webhook”功能简介
MinIO 的 “将事件发布至 Webhook” 功能,简单来说就是 对象存储里的事件通知机制。它的作用是让 MinIO 在特定操作发生时,自动通知你的外部系统(例如服务端 API、微服务、自动化流程等)。
MinIO 的“将事件发布至 Webhook”功能,简单来说就是对象存储里的事件通知机制。它的作用是让 MinIO 在特定操作发生时,自动通知你的外部系统(例如服务端 API、微服务、自动化流程等)。
1. 基本概念
事件(Event):对象存储中的操作,如:
s3:ObjectCreated:*→ 对象创建(上传/复制)s3:ObjectRemoved:*→ 对象删除s3:ObjectAccessed:*→ 对象被访问(取决于配置)
Webhook:HTTP 接口(URL),MinIO 将事件信息以 POST 请求的方式发送给它。
用途:实现实时响应、自动化处理和系统联动。
2. 作用场景
自动处理上传文件
- 用户上传文件到 MinIO → Webhook 收到事件 → 后端服务自动处理文件(如转码、生成缩略图)。
实时同步
- 上传/删除对象时,触发 Webhook 将操作信息同步到数据库或其他存储系统。
告警与监控
- 对特定操作(如删除敏感文件)触发 Webhook → 通知运维系统或发送 Slack/钉钉告警。
事件驱动架构(EDA)
- MinIO 作为事件源,将对象操作事件推送到微服务或消息队列,实现事件驱动设计。
3. 工作流程示例
用户上传文件 myphoto.jpg │ ▼ MinIO 触发事件 s3:ObjectCreated:Put │ ▼ POST 请求发送到 Webhook URL │ ▼ Web 服务收到 JSON payload │ ▼ 自动处理(转码、入库、推送通知等)Webhook payload 示例:
{"version":"1","deploymentid":"7e137b2a-f438-44cb-91dc-58148d945542","time":"2026-05-20T03:55:23.858364371Z","event":"","trigger":"incoming","api":{"name":"GetObject","bucket":"bucket","object":"repo.go","status":"OK","statusCode":200,"rx":0,"tx":68,"txHeaders":524,"timeToFirstByte":"1443873ns","timeToFirstByteInNS":"1443873","timeToResponse":"1503092ns","timeToResponseInNS":"1503092"},"remotehost":"222.173.127.34","requestID":"18B129D54B57D454","userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0","requestPath":"/bucket/e35f6b7b-0661-11f1-85ea-0242ac150003/repo.go","requestHost":"部署域名","requestQuery":{"X-Amz-Algorithm":"AWS4-HMAC-SHA256","X-Amz-Credential":"minioadmin/20260520/r/s3/aws4_request","X-Amz-Date":"20260520T035518Z","X-Amz-Expires":"86400","X-Amz-Signature":"640853ac793171f59c002b8bd3052d73016866df484c5c32c0fc1d89b7487d03","X-Amz-SignedHeaders":"host","response-content-disposition":"attachment"},"requestHeader":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip, deflate, br, zstd","Accept-Language":"zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Connection":"keep-alive","Cookie":"Hm_lvt_3f8ed946d171f676297a0","Sec-Ch-Ua":"\"Chromium\";v=\"148\", \"Microsoft Edge\";v=\"148\", \"Not/A)Brand\";v=\"99\"","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"Windows\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 Edg/148.0.0.0","X-Amz-Signature-Age":"5858"},"responseHeader":{"Accept-Ranges":"bytes","Content-Disposition":"attachment","Content-Length":"68","Content-Type":"application/octet-stream","ETag":"b338e1f20d5aa78824bb8e53d1cbd8fe","Last-Modified":"Tue, 10 Feb 2026 09:23:12 GMT","Server":"MinIO","Strict-Transport-Security":"max-age=31536000; includeSubDomains","Vary":"Origin,Accept-Encoding","X-Amz-Id-2":"cd12db15ea11a38dd85b2ac1cd0e7672a49d94ae8535078abcb6d6bd756cbab2","X-Amz-Request-Id":"18B129D54B57D454","X-Content-Type-Options":"nosniff","X-Ratelimit-Limit":"26413","X-Ratelimit-Remaining":"26413","X-Xss-Protection":"1; mode=block"},"tags":{"GetObject":"name=repo.go,pool=1,set=1"},"accessKey":"accessKey"}4. 配置方式
在 MinIO 里,事件通知可通过以下方式配置:控制台 UI
minio 日志配置
部署接收日志服务
services: minio-webhook: image: registry.cn-qingdao.aliyuncs.com/lys_ns/app:minio-webhook container_name: minio-webhook ports: - "8080:8080" volumes: - ./app:/app - ./logs:/var/log/minio-webhook restart: unless-stoppedapp.py
import json import logging import os from datetime import datetime, timezone from flask import Flask, request LOG_DIR = os.getenv("WEBHOOK_LOG_DIR", "/var/log/minio-webhook") logging.basicConfig(level=logging.INFO) logger = logging.getLogger("minio-webhook") class DailyLogHandler(logging.FileHandler): def __init__(self, log_dir): self.log_dir = log_dir os.makedirs(log_dir, exist_ok=True) super().__init__(self._filename()) def _filename(self): return os.path.join(self.log_dir, f"events-{datetime.now():%Y-%m-%d}.log") def emit(self, record): if self.baseFilename != self._filename(): self.close() self.baseFilename = self._filename() self.stream = self._open() super().emit(record) fh = DailyLogHandler(LOG_DIR) fh.setFormatter(logging.Formatter("%(asctime)s %(message)s")) logger.addHandler(fh) app = Flask(__name__) @app.route("/minio-webhook", methods=["POST"]) def minio_webhook(): event = request.json log_entry = json.dumps({ "time": datetime.now(timezone.utc).isoformat(), "requestPath": event.get("requestPath"), "remotehost": event.get("remotehost"), }) logger.info("%s", log_entry) return "OK", 200 if __name__ == "__main__": app.run(host="0.0.0.0", port=8080)