- 背景:有 3 台自建的 K8s 服务器,跑着核心业务。之前一直用阿里云 SLS 采集日志,但最近业务调整,希望使用AWS。
- 日志采集看起来是件小事,但真做起来才发现:采集器用什么?日志怎么传?告警怎么配?钉钉怎么通知?
- 经过一系列的踩坑、试错、解决各种报错,最终跑通了一条完整的链路:K8s → Fluent Bit → CloudWatch → SNS→ Lambda →钉钉告警。
AWS CloudWatch 日志接入
K8s 集群日志采集 + 钉钉告警 完整部署指南
1. 文档概述
1.1 目的
将自建 K8s 集群(3台服务器,containerd 运行时,rocky linux 9.7)的容器日志接入 AWS CloudWatch,并配置钉钉告警通知。
1.2 架构图
┌─────────────────────────────────────────────────────────────────────────────┐
│ K8s 集群 (3台服务器) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Fluent Bit (DaemonSet) │ │
│ │ - 采集 /var/log/containers/*.log │ │
│ │ - 解析 Kubernetes 元数据 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Amazon CloudWatch │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 日志组: /aws/containerinsights/{cluster-name}/application │ │
│ │ - 日志流按 Pod 自动命名 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 指标筛选器: ErrorCount │ │
│ │ - 筛选条件: ERROR │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 告警规则: K8s-Error-DingTalk │ │
│ │ - 条件: 5分钟内 ERROR > 5次 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ SNS 主题: K8s-DingTalk-Alarm │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Lambda: DingTalkNotifier │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 钉钉群机器人 │
└─────────────────────────────────────────────────────────────────────────────┘
2. 前置条件
2.1 环境信息
| 项目 | 值 |
|---|---|
| AWS 账号 ID | 登录AWS获取 |
| AWS 区域 | 按需选择 |
| K8s 版本 | v1.34.6 |
| 节点数量 | 3(1 control-plane + 2 worker) |
| 操作系统 | Rocky Linux 9.7 |
| 容器运行时 | containerd 2.2.3 |
| 日志路径 | /var/log/pods///*.log |
| 采集器 | Fluent Bit (aws-for-fluent-bit) |
2.2 所需权限
- AWS IAM 用户需具备以下权限:
CloudWatchAgentServerPolicyAmazonSNSFullAccessCloudWatchLogsFullAccessCloudWatchFullAccessAWSLambda_FullAccessIAMFullAccess(创建角色和策略)
2.3 前置准备
- AWS CLI 已安装并配置
- kubectl 已安装并可连接集群
- Helm 已安装(可选)
- 钉钉群机器人 Webhook 地址已准备
3. 第一步:AWS IAM 角色配置
3.1 创建 IAM 角色(在 AWS 控制台操作)
- 进入 IAM → 角色 → 创建角色
- 选择"AWS服务 " → "EC2"
- 附加策略:
CloudWatchAgentServerPolicy - 角色名称:
CloudWatchAgent-Role - 点击"创建角色"
3.2 绑定 IAM 角色到 EC2 实例
- 进入 EC2 控制台 → 实例
- 选中需要绑定的实例(你的 3 台 K8s 节点)
- 操作 → 安全 → 修改 IAM 角色
- 选择
CloudWatchAgent-Role - 点击"更新 IAM 角色"
3.3 验证 IAM 角色绑定成功
bash
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/CloudWatchAgent-Role | head -20
期望输出 :返回包含 AccessKeyId 和 SecretAccessKey 的 JSON
3.4 为 sms-sender 用户添加权限
bash
# 附加 SNS 权限
aws iam attach-user-policy \
--user-name sms-sender \
--policy-arn arn:aws:iam::aws:policy/AmazonSNSFullAccess
# 附加 CloudWatch 日志权限
aws iam attach-user-policy \
--user-name sms-sender \
--policy-arn arn:aws:iam::aws:policy/CloudWatchLogsFullAccess
# 附加 CloudWatch 告警权限
aws iam attach-user-policy \
--user-name sms-sender \
--policy-arn arn:aws:iam::aws:policy/CloudWatchFullAccess
# 附加 Lambda 权限
aws iam attach-user-policy \
--user-name sms-sender \
--policy-arn arn:aws:iam::aws:policy/AWSLambda_FullAccess
4. 第二步:部署 Fluent Bit 采集器
4.1 创建部署脚本
创建 deploy-fluentbit.sh:
bash
#!/bin/bash
# deploy-fluentbit.sh - 部署 Fluent Bit 日志采集器
# 在 K8s Master 节点执行
set -e
echo "=========================================="
echo "部署 Fluent Bit 日志采集器"
echo "=========================================="
# 设置变量
AWS_ACCOUNT_ID="997836553899"
REGION="ap-east-1"
ROLE_NAME="CloudWatchAgent-Role"
CLUSTER_NAME="k8s"
# 1. 创建命名空间
echo ">>> 创建命名空间 amazon-cloudwatch"
kubectl create namespace amazon-cloudwatch --dry-run=client -o yaml | kubectl apply -f -
# 2. 创建 ServiceAccount
echo ">>> 创建 ServiceAccount"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit-sa
namespace: amazon-cloudwatch
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::$AWS_ACCOUNT_ID:role/$ROLE_NAME
EOF
# 3. 创建 ConfigMap
echo ">>> 创建 Fluent Bit 配置"
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: amazon-cloudwatch
labels:
k8s-app: fluent-bit
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Log_Level info
Daemon off
Parsers_File parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser cri
Tag kube.*
Refresh_Interval 10
Mem_Buf_Limit 50MB
Skip_Long_Lines On
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
Keep_Log On
K8S-Logging.Parser On
K8S-Logging.Exclude Off
Annotations Off
Labels Off
[OUTPUT]
Name cloudwatch_logs
Match kube.*
region ap-east-1
log_group_name /aws/containerinsights/${CLUSTER_NAME}/application
log_stream_prefix ${CLUSTER_NAME}-
auto_create_group true
log_retention_days 30
parsers.conf: |
[PARSER]
Name cri
Format regex
Regex ^(?<time>[^ ]+) (?<stream>stdout|stderr) (?<logtag>[^ ]*) (?<message>.*)$
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L%z
EOF
# 4. 部署 DaemonSet
echo ">>> 部署 Fluent Bit DaemonSet"
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: amazon-cloudwatch
labels:
k8s-app: fluent-bit
spec:
selector:
matchLabels:
k8s-app: fluent-bit
template:
metadata:
labels:
k8s-app: fluent-bit
spec:
serviceAccountName: fluent-bit-sa
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: fluent-bit
image: public.ecr.aws/aws-observability/aws-for-fluent-bit:3
imagePullPolicy: Always
env:
- name: REGION
value: "${REGION}"
- name: CLUSTER_NAME
value: "${CLUSTER_NAME}"
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "100m"
volumeMounts:
- name: varlog
mountPath: /var/log
- name: dockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: fluent-bit-config
mountPath: /fluent-bit/etc/
- name: runlog
mountPath: /var/run
volumes:
- name: varlog
hostPath:
path: /var/log
- name: dockercontainers
hostPath:
path: /var/lib/docker/containers
- name: fluent-bit-config
configMap:
name: fluent-bit-config
- name: runlog
hostPath:
path: /var/run
tolerations:
- operator: Exists
EOF
echo "=========================================="
echo "部署完成!"
echo "=========================================="
echo ""
echo "查看状态:"
echo "kubectl get pods -n amazon-cloudwatch"
echo ""
echo "查看日志:"
echo "kubectl logs -n amazon-cloudwatch -l k8s-app=fluent-bit --tail=30"
4.2 执行部署
bash
chmod +x deploy-fluentbit.sh
./deploy-fluentbit.sh
4.3 验证部署
bash
# 查看 Pod 状态(3 个节点都应该 Running)
kubectl get pods -n amazon-cloudwatch
# 查看 Fluent Bit 日志
kubectl logs -n amazon-cloudwatch -l k8s-app=fluent-bit --tail=30
期望输出:
- 3 个 Pod 状态为
Running - 日志显示
[ info] [output:cloudwatch_logs]和[ info] [input:tail]
5. 第三步:创建 SNS 主题
5.1 创建 SNS 主题
bash
aws sns create-topic \
--name "K8s-DingTalk-Alarm" \
--region ap-east-1
输出示例:
json
{
"TopicArn": "arn:aws:sns:ap-east-1:997836553899:K8s-DingTalk-Alarm"
}
5.2 记录 Topic ARN
保存输出中的 TopicArn,后续步骤需要用到。
6. 第四步:创建 Lambda 函数(钉钉通知)
6.1 在 AWS 控制台创建 Lambda
- 进入 Lambda → 创建函数
- 选择"从头开始创作"
- 函数名称:
DingTalkNotifier - 运行时:
Python 3.9 - 点击"创建函数"
6.2 粘贴 Lambda 代码
python
import json
import urllib3
import os
http = urllib3.PoolManager()
# 从环境变量读取 Webhook 地址
WEBHOOK_URL = os.environ.get('WEBHOOK_URL')
def lambda_handler(event, context):
# 解析 SNS 消息
try:
sns_message = event['Records'][0]['Sns']['Message']
alarm_data = json.loads(sns_message)
except Exception as e:
print(f"解析消息失败: {e}")
return {"statusCode": 400, "body": "Invalid message"}
# 提取告警信息
alarm_name = alarm_data.get('AlarmName', '未知告警')
alarm_state = alarm_data.get('NewStateValue', '未知')
alarm_reason = alarm_data.get('NewStateReason', '无详细信息')
timestamp = alarm_data.get('StateChangeTime', '未知时间')
# 构建钉钉消息(包含 ERROR 关键字,满足关键词安全策略)
dingtalk_msg = {
"msgtype": "markdown",
"markdown": {
"title": "🚨 K8s 日志告警",
"text": f"""
### 🚨 K8s 日志告警
- **告警名称**: {alarm_name}
- **状态**: **{alarm_state}**
- **时间**: {timestamp}
- **详情**: {alarm_reason}
> 关键字: ERROR
"""
},
"at": {
"isAtAll": False
}
}
try:
response = http.request(
'POST',
WEBHOOK_URL,
body=json.dumps(dingtalk_msg).encode('utf-8'),
headers={'Content-Type': 'application/json'}
)
print(f"钉钉响应状态: {response.status}")
print(f"钉钉响应内容: {response.data}")
return {
"statusCode": response.status,
"body": json.dumps({"message": "Message sent to DingTalk"})
}
except Exception as e:
print(f"发送失败: {e}")
return {"statusCode": 500, "body": str(e)}
重要 :粘贴后必须点击 "Deploy" 按钮保存。
6.3 配置环境变量
- Lambda → 配置 → 环境变量
- 添加环境变量:
- 键 :
WEBHOOK_URL - 值:你的钉钉机器人 Webhook 地址
- 键 :
6.4 添加 SNS 触发器
- 点击"添加触发器"
- 选择 "SNS"
- 选择现有主题
K8s-DingTalk-Alarm - 点击"添加"
7. 第五步:创建告警规则
7.1 创建告警脚本
创建 create-alarm.sh:
bash
#!/bin/bash
# create-alarm.sh - 创建 ERROR 日志告警
LOG_GROUP="/aws/containerinsights/k8s/application"
REGION="ap-east-1"
SNS_TOPIC_ARN="arn:aws:sns:ap-east-1:997836553899:K8s-DingTalk-Alarm"
echo "=========================================="
echo "创建 CloudWatch 日志告警"
echo "=========================================="
# 1. 创建指标筛选器
echo ">>> 创建 ERROR 指标筛选器..."
aws logs put-metric-filter \
--log-group-name "$LOG_GROUP" \
--filter-name "ErrorCount" \
--filter-pattern "ERROR" \
--metric-transformations metricName=ErrorCount,metricNamespace=K8sLogs,metricValue=1 \
--region $REGION
if [ $? -eq 0 ]; then
echo "✅ 指标筛选器创建成功"
else
echo "❌ 指标筛选器创建失败"
fi
# 2. 创建告警
echo ">>> 创建告警规则..."
aws cloudwatch put-metric-alarm \
--alarm-name "K8s-Error-DingTalk" \
--alarm-description "当 5 分钟内 ERROR 日志超过 5 条时触发钉钉告警" \
--metric-name "ErrorCount" \
--namespace "K8sLogs" \
--statistic "Sum" \
--period 300 \
--evaluation-periods 1 \
--threshold 5 \
--comparison-operator "GreaterThanThreshold" \
--alarm-actions "$SNS_TOPIC_ARN" \
--region $REGION
if [ $? -eq 0 ]; then
echo "✅ 告警规则创建成功"
else
echo "❌ 告警规则创建失败"
fi
echo "=========================================="
echo "告警配置完成!"
echo "=========================================="
7.2 执行告警创建
bash
chmod +x create-alarm.sh
./create-alarm.sh
7.3 验证告警创建
bash
# 验证指标筛选器
aws logs describe-metric-filters \
--log-group-name "/aws/containerinsights/k8s/application" \
--filter-name-prefix "ErrorCount" \
--region ap-east-1
# 验证告警
aws cloudwatch describe-alarms \
--alarm-names "K8s-Error-DingTalk" \
--region ap-east-1
8. 第六步:测试告警
8.1 生成 ERROR 日志
bash
# 生成 6 条 ERROR 日志(超过阈值 5)
for i in {1..6}; do
kubectl run test-error-$i --image=busybox --restart=Never --rm -it -- sh -c "echo 'ERROR: test alarm message #$i'" 2>/dev/null || true
sleep 2
done
8.2 验证指标数据
bash
aws cloudwatch get-metric-statistics \
--namespace K8sLogs \
--metric-name ErrorCount \
--start-time "$(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 300 \
--statistics Sum \
--region ap-east-1
期望输出 :返回 Sum 值大于 0
8.3 验证钉钉收到告警
等待 3-5 分钟,钉钉群应收到告警消息:
🚨 K8s 日志告警
- 告警名称: K8s-Error-DingTalk
- 状态: ALARM
- 时间: 2026-07-01T03:12:10.404+0000
- 详情: Threshold Crossed: 1 datapoint [26.0 ...] was greater than the threshold (5.0).
关键字: ERROR
9. Logs Insights 查询指南
9.1 查看所有日志流(了解有哪些服务)
sql
SOURCE "/aws/containerinsights/k8s/application"
| stats count() by @logStream
| sort count desc
| limit 50
9.2 查看特定服务日志
sql
SOURCE "/aws/containerinsights/k8s/application"
| filter @logStream like /gateway/
| fields @timestamp, @message
| sort @timestamp desc
| limit 100
9.3 查看 ERROR 日志
sql
SOURCE "/aws/containerinsights/k8s/application"
| filter @message like /ERROR/
| fields @timestamp, @message, @logStream
| sort @timestamp desc
| limit 100
9.4 查看特定命名空间日志
sql
SOURCE "/aws/containerinsights/k8s/application"
| filter @logStream like /k8s/
| fields @timestamp, @message, @logStream
| sort @timestamp desc
| limit 100
10. 常见问题与解决方案
10.1 IAM 权限问题
| 错误信息 | 解决方案 |
|---|---|
User is not authorized to perform: SNS:CreateTopic |
添加 AmazonSNSFullAccess 策略到用户 |
User is not authorized to perform: logs:PutMetricFilter |
添加 CloudWatchLogsFullAccess 策略 |
User is not authorized to perform: cloudwatch:PutMetricAlarm |
添加 CloudWatchFullAccess 策略 |
User is not authorized to perform: cloudwatch:DescribeAlarms |
添加 CloudWatchFullAccess 或 cloudwatch:DescribeAlarms 权限 |
10.2 Fluent Bit 部署问题
| 问题 | 解决方案 |
|---|---|
ErrImagePull / ImagePullBackOff |
使用 public.ecr.aws/aws-observability/aws-for-fluent-bit:3 替代 amazon/cloudwatch-agent:1.300042.0 |
could not allocate key value pair |
Fluent Bit 5.x 配置语法更严格,使用简化配置(不带 @INCLUDE 的子配置文件) |
could not get meta for POD |
这是控制平面节点的警告,不影响业务 Pod,可以忽略 |
10.3 CloudWatch 查询问题
| 错误信息 | 解决方案 |
|---|---|
MalformedQueryException |
检查 Logs Insights 语法,使用 filter @logStream like /xxx/ 而非 filter @logStream = "xxx" |
A log group must be selected |
在 Logs Insights 中选择正确的日志组 |
kubernetes.container_name 字段为空 |
使用 @logStream 替代,日志流名称已包含 Pod 和容器信息 |
10.4 钉钉告警问题
| 问题 | 解决方案 |
|---|---|
| 钉钉收不到消息 | 1. 检查 Lambda 是否被触发(查看调用次数) 2. 检查 Lambda 环境变量 WEBHOOK_URL 是否正确 3. 检查钉钉机器人关键词是否包含 ERROR |
| Lambda 触发但钉钉无响应 | 查看 Lambda CloudWatch 日志,检查 urllib3 错误 |
| 告警不触发 | 1. 检查指标筛选器是否创建成功 2. 等待 3-5 分钟让数据聚合 3. 查看指标数据 get-metric-statistics |
11. 附录
11.1 环境变量汇总
| 变量 | 值 |
|---|---|
| AWS_ACCOUNT_ID | 997836553899 |
| AWS_REGION | ap-east-1 |
| CLUSTER_NAME | k8s |
| LOG_GROUP | /aws/containerinsights/k8s/application |
| SNS_TOPIC_ARN | arn:aws:sns:ap-east-1:997836553899:K8s-DingTalk-Alarm |
| IAM_ROLE | CloudWatchAgent-Role |
11.2 关键命令速查
bash
# 查看 Fluent Bit Pod
kubectl get pods -n amazon-cloudwatch
# 查看 Fluent Bit 日志
kubectl logs -n amazon-cloudwatch -l k8s-app=fluent-bit --tail=30
# 重启 Fluent Bit
kubectl rollout restart daemonset fluent-bit -n amazon-cloudwatch
# 查看告警状态
aws cloudwatch describe-alarms --alarm-names "K8s-Error-DingTalk" --region ap-east-1
# 查看指标数据
aws cloudwatch get-metric-statistics --namespace K8sLogs --metric-name ErrorCount --start-time "$(date -u -d '10 minutes ago' +%Y-%m-%dT%H:%M:%SZ)" --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --period 300 --statistics Sum --region ap-east-1
# 查看日志流
aws logs describe-log-streams --log-group-name "/aws/containerinsights/k8s/application" --region ap-east-1 --max-items 10
11.3 相关文档链接
12. 版本历史
| 版本 | 日期 | 作者 | 变更说明 |
|---|---|---|---|
| v1.0 | 2026-07-01 | - | 初始版本,基于实际部署经验整理 |
文档结束
如果有任何问题或需要补充的内容,请随时提出!📝