nginx ingress controller 可通过 configmap 的 http-snippet 配置 log_format 与 map 白名单,结合 access_log 动态按域名分路写入日志,并需挂载日志卷、启用 logrotate 及验证生效。

在 Kubernetes 集群中,Nginx Ingress Controller 默认将所有访问日志统一写入标准输出(stdout),由容器运行时采集到日志系统(如 Loki、ES 或阿里云 SLS)。但生产环境中常需按域名(host)分离日志,便于审计、排查、容量分析或对接不同业务线的监控体系——这不是靠外部日志采集器“打标签”就能解决的,必须在 Nginx 层级实现原生分路。
核心思路:用 log_format + access_log 按 host 动态生成日志路径
Nginx 原生支持基于变量的动态日志路径。Ingress Controller 允许通过 ConfigMap 的 http-snippet 注入自定义 log_format 和 access_log 指令,关键在于利用 $host 或 $server_name 构建目录结构,避免硬编码或正则匹配开销。
- 在
nginx-configurationConfigMap 的http-snippet字段中定义格式:log_format by_host '$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $request_time'; - 为每个 server 块启用独立日志(需配合
server-snippet):access_log /var/log/nginx/access-$host.log by_host; - 确保日志路径可写:Ingress Controller Pod 必须挂载一个共享 emptyDir 或 hostPath 卷,路径需与
access_log中一致,例如:volumeMounts: - name: nginx-log mountPath: /var/log/nginx
安全与稳定性要点
直接拼接 $host 存在路径遍历风险(如恶意请求带 host: ..%2fetc%2fpasswd)。生产必须做白名单过滤:
- 使用
map指令预定义合法域名映射:map $host $log_path { default "default"; app1.example.com "app1"; api.example.com "api"; } - 再将
access_log改为:access_log /var/log/nginx/access-$log_path.log by_host; - 该
map定义需放在http-snippet中,且优先于server-snippet加载
日志落盘后的采集与归档
分存只是第一步,后续需保障日志不丢失、可轮转、易检索:
- 启用 logrotate:在容器启动脚本或 initContainer 中部署
logrotate配置,按大小(如 100MB)或时间(daily)切分,保留 7 天 - 避免日志堆积:Ingress Controller 默认不自动轮转,
access_log指向文件后,若无外部管理,单文件会无限增长 - 采集端适配:Loki 可通过
pipeline_stages提取filename标签;SLS 需配置多 Logstore 或使用 topic 分隔,对应$log_path值
验证是否生效
不依赖日志平台,直接检查 Pod 内部状态:
- 进入 Ingress Controller Pod:
kubectl exec -it -n ingress-nginx deploy/nginx-ingress-controller -- sh - 确认日志文件已生成:
ls -l /var/log/nginx/access-*.log - 触发请求并实时观察:
tail -f /var/log/nginx/access-app1.log,看是否仅出现对应域名的记录 - 检查 nginx.conf 是否注入成功:
grep -A5 "access_log.*app1" /etc/nginx/nginx.conf











