3. VPC Flow Logs采集与解析实战

VPC Flow Logs 是 AWS 网络可观测性体系中最基础、也最容易被低估的数据源。它记录的是弹性网卡(ENI)层面的 IP 流量元数据——不是包内容,而是“谁在什么时间、通过哪个接口、以什么协议、向谁发了多少字节、结果是被接受还是被拒绝”。理解这一点非常关键:Flow Logs 回答的是“连接级事实”,而不是“应用级语义”。因此它最适合用来做安全排查、连通性验证、异常流量发现和成本归因,而不是直接判断某个 HTTP 请求返回了 500。

采集前必须先明确三个决策:采集范围、目标存储、字段格式。采集范围可以是 VPC、子网或单个 ENI;目标可以是 CloudWatch Logs 或 S3。二者定位不同:CloudWatch Logs 适合近实时查询与告警,保留期短、按摄取与存储计费;S3 适合长期归档与 Athena 批量分析,成本低、可对接分区与生命周期策略。字段格式则决定后续能查什么——默认格式只含版本、账户、接口、源/目的地址与端口、协议、包数、字节数、起止时间、动作、日志状态;自定义格式可追加 tcp-flags、pkt-srcaddr、flow-direction、traffic-path、region、az-id、sublocation-type 等字段,用于区分同区域/跨区域、入站/出站、以及流量是否经过中间设备。

建议:安全与排障场景优先启用自定义格式,至少加入 tcp-flags、flow-direction、traffic-path、pkt-srcaddr、pkt-dstaddr。这些字段能把“被拒绝”进一步细化为“被安全组拒绝”还是“被 NACL 拒绝”,也能识别经过 NAT 或网关负载均衡器后的真实源地址。

下面这张图展示了从 ENI 产生流记录,到 CloudWatch Logs 与 S3 两条消费路径,再到 Logs Insights 与 Athena 两类查询引擎的完整数据流关系。图中同时标注了关键字段在两条路径中的落点,便于对照理解。

弹性网卡 ENI eni-0a1b2c3d 子网 10.0.1.0/24 实例 i-0f9e8d7c Flow Log 记录 version account-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status + 自定义字段 CloudWatch Logs 日志组 /aws/vpc/flowlogs 保留期 7~30 天 近实时查询与告警 S3 存储桶 AWSLogs/.../vpcflowlogs/ 按年月日分区 长期归档与批量分析 Logs Insights fields / filter / stats 近实时聚合 适合排障与告警 Athena CREATE TABLE + SQL 分区裁剪 适合批量与趋势分析 关键字段落点:action=ACCEPT/REJECT 判断放行结果;log-status=NODATA/SKIPDATA 表示无流量或采集跳过; tcp-flags 区分 SYN/RST;flow-direction 区分 ingress/egress;traffic-path 标识是否经过中间设备。

字段解析是实战的核心。以一条典型记录为例:

2 123456789012 eni-0a1b2c3d 10.0.1.15 203.0.113.88 49152 443 6 12 840 1620000000 1620000060 ACCEPT OK

逐字段读:版本 2;账户 123456789012;接口 eni-0a1b2c3d;源地址 10.0.1.15(内网实例);目的地址 203.0.113.88(外部服务);源端口 49152(客户端临时端口);目的端口 443(HTTPS);协议 6(TCP);12 个包、840 字节;起止时间戳;动作 ACCEPT;日志状态 OK。若把 action 换成 REJECT,则说明流量到达了 ENI 但被安全组或 NACL 拒绝——此时要结合 flow-direction 判断是入站被拒还是出站被拒,再对照安全组规则定位。

在 CloudWatch Logs Insights 中,查询语法围绕 fields、filter、stats、sort、limit 展开。例如要找出最近一小时内被拒绝次数最多的目的端口:

fields @timestamp, dstport, action
| filter action = "REJECT"
| stats count(*) as rejectCount by dstport
| sort rejectCount desc
| limit 20

若要排查某台实例的出站异常,可先按源地址过滤,再按目的地址聚合字节数:

fields @timestamp, srcaddr, dstaddr, dstport, bytes
| filter srcaddr = "10.0.1.15" and action = "ACCEPT"
| stats sum(bytes) as totalBytes by dstaddr, dstport
| sort totalBytes desc

在 S3 + Athena 路径上,第一步是建表。Flow Logs 以空格分隔,需用 ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ',并指定 LOCATION 指向 S3 前缀。若启用了分区,还需 MSCK REPAIR TABLE 或 ALTER TABLE ADD PARTITION 让 Athena 识别分区,否则会全量扫描,成本和延迟都会显著上升。

CREATE EXTERNAL TABLE vpc_flow_logs (
  version int, account_id string, interface_id string,
  srcaddr string, dstaddr string, srcport int, dstport int,
  protocol bigint, packets bigint, bytes bigint,
  start_time bigint, end_time bigint, action string, log_status string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ' '
LOCATION 's3://my-flowlogs-bucket/AWSLogs/123456789012/vpcflowlogs/ap-southeast-1/';

建表后即可用标准 SQL 做趋势分析。例如统计每天被拒绝的独立源地址数量,用于识别扫描行为:

SELECT date_trunc('day', from_unixtime(start_time)) AS day,
       count(DISTINCT srcaddr) AS distinct_src,
       count(*) AS reject_records
FROM vpc_flow_logs
WHERE action = 'REJECT'
GROUP BY 1
ORDER BY 1 DESC;

实际排障中,有几类异常模式值得优先关注。第一类是 log-status 为 NODATA:表示采集窗口内该 ENI 没有流量,可能是实例已停止、安全组全封或采集配置未生效。第二类是大量 REJECT 集中在少数目的端口,常见于端口扫描或错误的安全组规则。第三类是 bytes 与 packets 比例异常,例如包数很多但字节数极小,可能是 SYN 重传或探测流量。第四类是同一对地址在短时间内反复出现 ACCEPT 与 REJECT 交替,通常意味着规则顺序或状态跟踪存在问题。

注意:Flow Logs 不采集以下流量——DHCP、实例元数据 169.254.169.254、Amazon DNS、Windows 激活、以及被安全组或 NACL 拒绝的流量在部分配置下可能不记录。因此“没有记录”不等于“没有流量”,必须结合 log-status 和采集范围综合判断。

最后给出一个可直接套用的排查顺序:先确认采集范围是否覆盖目标 ENI,再确认 log-status 是否为 OK,然后按 action 分流——ACCEPT 看字节与端口分布,REJECT 看方向与规则匹配。CloudWatch Logs Insights 适合分钟级定位,Athena 适合天级趋势与跨账户汇总。两条路径共用同一份字段语义,掌握字段含义后,切换查询引擎只是语法差异,判断逻辑完全一致。