惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
大猫的无限游戏
大猫的无限游戏
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs
博客园 - 叶小钗
P
Privacy International News Feed
Jina AI
Jina AI
Apple Machine Learning Research
Apple Machine Learning Research
T
Threatpost
IT之家
IT之家
博客园 - 聂微东
Know Your Adversary
Know Your Adversary
Help Net Security
Help Net Security
罗磊的独立博客
I
Intezer
S
Schneier on Security
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
Cisco Talos Blog
Cisco Talos Blog
宝玉的分享
宝玉的分享
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
TaoSecurity Blog
TaoSecurity Blog
MyScale Blog
MyScale Blog
P
Privacy & Cybersecurity Law Blog
T
The Exploit Database - CXSecurity.com
PCI Perspectives
PCI Perspectives
Security Latest
Security Latest
H
Heimdal Security Blog
S
Secure Thoughts
Hacker News: Ask HN
Hacker News: Ask HN
Y
Y Combinator Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Microsoft Security Blog
Microsoft Security Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
SecWiki News
SecWiki News
The GitHub Blog
The GitHub Blog
A
Arctic Wolf
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
T
Threat Research - Cisco Blogs
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC

小松鼠的博客

记录一次线上k8s工作节点无法创建容器的问题排查思路与解决办法 记一次线上GoLang项目OOM排查过程 从LastPass转向拥抱开源KeePass的心路历程 故障定位与 AI 结合前后端编码实践 K8s云原生环境下文件描述符占用过高查询思路 2024年最新关闭火绒安全工具的开机自启方法 Kubernetes任务调度实践-Go语言实现Job和CronJob对比分析 离线更新k8s环境下的trivy漏洞库方法 使用Go语言接入Choerodon实现基于OAuth2的统一身份认证登录 在Vue2中自定义Switch组件并实现父子组件双向数据绑定 关于docker jdk1.8镜像中的GB18030-2022标准支持及验证 Go框架gin中的session存储gin-contrib-sessions和go-session 关于修改node_module中的源码问题记录 docker-compose网络和内网服务IP冲突问题 慎用存储过程:一条语句引发的数据库存储100%占用 Spring Boot中4种文件下载方法的实现 避坑-不能将specific类型的gitlab-runner改变为share类型 Docker compose中的MySQL主从复制模式和percona-toolkit工具使用 在minio中开启https访问以及使用rclone备份minio桶 在多机Docker环境下部署Choerodon的解决方案 Prometheus中Monitor添加对SpringBoot Actuator的Basic认证 在Nginx的容器镜像中隐藏Nginx的Server响应头 K8s中的两种nginx-ingress-controller及其区别 两个docker工具:runlike和whaler Grafana中的邮件报警和截图插件grafana-image-enderer K8s中externalName-service和services-without-selectors maven配置文件settings.xml中的一些概念总结 K8s中flexvolume插件驱动的安装 K8s中的coredns无法解析svc问题排查 K8s中使用Ingress访问请求体过大问题解决 关于k8s中对于SpringBoot应用TCP类型的就绪探针不准确的问题发现 K8s中的环境变量与应用程序的对应关系与操作 SpringMVC4升级为SpringBoot2实战 在Vmware中Ubuntu22.04的vm-tools和网络问题 修改k8s节点主机名并重新加入集群 离线安装Grafana插件 Spring Data Jpa 中使用CriteriaBuilder动态拼接SQL 在SpringBoot项目配置Liquibase数据库版本管理 记录Vue中父子组件传值的实战应用 实现单例模式的8种方法 三种常用的生产者消费者模式实现 使用两个线程交替打印0-100的奇偶数 关于部署于JBoss5中的Spring应用获取项目真实部署路径的问题 获取下一个完全对称日 通过短信验证码验证修改密码的解决方案 在Win10中使用Win+R快速启动软件 使用RSA加解密时注意Cipher.getInstance(String var0,Provider var1)提供的Provider是否正确 在RestEasy2.x中解决接口重复提交问题 几道简单的CTF题目思路 重温Spring---Spring事务控制与基于XML和注解的配置方法 重温Spring---Spring AOP基于XML和注解的配置 重温Spring---AOP动态代理和Spring AOP及其基本原理 重温Spring---Spring IOC基于XML和注解的配置和比较 在Windows10中安装MySQL5.7 Zip版本及常用配置 重温Spring---使用Spring IOC解决程序耦合 策略模式与责任链模式实战应用 Linux上直接打开war包修改文件 在Windows上运行两个微信的简单脚本 ThreadPoolExecutor的使用方法与分页查询数据实例 IDEA中Shelve Changes 和 Git Stash 通过resteasy发布RESTful接口 解决前端请求后台接口,后台报错Can not deserialize instance of java.util.ArrayList out of START_OBJECT token 使用VBA脚本汇总Excel文档 使用Jenkins+GitLab实现自动部署vue项目 Kubernetes:使用hostPath挂载nginx集群的配置文件和html 彻底搞定VirtualBox虚拟机的网络设定 在Docker中安装MySQL5.7并开启远程访问(附授权和修改密码方式) 利用git命令和java文件流 获取自己改动过的文件 浅谈Spring定时任务的使用(Scheduled注解) 在Spring项目简单配置Flyway(V4.2版本)数据库版本管理 解决Spring单元测试中因外键关联导致的失败integrity constraint violation:foreign key no action Redis安装与哨兵模式配置入门 关于Vue中使用Element-UI样式row-class-name失效的问题 Element-UI中实现可动态增加行列和可编辑单元格的表格 Windows系统查看端口占用、结束进程方法和命令 层次分析法(AHP)分析步骤与计算方法 源码分析之解决layui框架重载表格时额外参数不清空的问题 Spring Data Jpa 返回自定义对象(实体部分属性、多表联查) 如何将一个jar放到本地maven仓库中 关于SSM项目停止Tomcat时Log4j出现java.lang.NoClassDefFoundError: 获取el-table单元格值并根据该值对元素自定义样式渲染 解决Git每次push都要重新输入账号密码和HttpRequestException encountered的问题 解决前后端分离项目中Vue不带cookies的问题 SSM集成Shiro自定义权限过滤器不执行解决方案 SSM集成Shiro不进入自定义Realm的doGetAuthorizationInfo的解决方案 Vue+SSM中使用Token验证登录 Git拉代码推送代码提示密码错误如何修改 Git配置SSH Key(Git配置多个账户) 安装Tomcat服务器以及错误汇总(tomcat8.0、jdk8) 关于我
FileBeat收集nginx-ingress-controller日志
ycyin · 2024-08-01 · via 小松鼠的博客

2024年8月1日大约 4 分钟云原生k8snginx-ingress


在云原生环境下使用nginx-ingress-controller作为网关服务,我们希望能监控网关流量,重点监控访问者的IP和访问的服务。目前使用比较多的两种基于 NGINX 的 Ingress 控制器实现:一种是nginxinc/kubernetes-ingress,另一种是kubernetes/ingress-nginx,我们使用的是nginxinc/kubernetes-ingress,它是nginx社区维护的一个版本。开始准备通过metric暴露nginx-ingress-controller的监控指标,使用Prometheus进行采集,发现nginx社区维护的这个开源版kubernetes-ingress可收集的监控指标非常少。我们需要的信息其实Nginx都有打印日志,所以,我们决定采集nginx-ingress-controller的日志,最终收集到ES中进行存储,后期就可以使用Kibana查询这些日志,甚至对这些日志进行分析。

涉及组件

nginxinc/kubernetes-ingress Helm部署
helm.sh/chart版本:nginx-ingress-0.10.4 ,镜像:deploy.bocloud.k8s/nginx/nginx-ingress:1.12.4

filebeat:7.13.4 Helm部署
elasticsearch:v 7.8.0 集群

实现

首先,我们要让nginx-ingress-controller打印出json日志,使用官方提供的log-format配置实现

│ apiVersion: v1                                                                                                                                                                                  
│ data:                                                                                                                                                                                           
│   log-format: '{"time": "$time_iso8601", "remote_addr": "$remote_addr", "x_forwarded_for":                                                                                                      
│     "$proxy_add_x_forwarded_for", "remote_user": "$remote_user", "bytes_sent": $bytes_sent,                                                                                                     
│     "request_time": $request_time, "status": $status, "vhost": "$host", "request_proto":                                                                                                        
│     "$server_protocol", "path": "$uri", "request_query": "$args", "request_length":                                                                                                             
│     $request_length, "duration": $request_time,"method": "$request_method", "http_referrer":                                                                                                    
│     "$http_referer", "http_user_agent": "$http_user_agent"}'                                                                                                                                    
│   log-format-escaping: default                                                                                                                                                                  
│   server-tokens: "false"                                                                                                                                                                        
│ kind: ConfigMap                                                                                                                                                                                 
│ metadata:                                                                                                                                                                                       
│   annotations:                                                                                                                                                                                  
│     meta.helm.sh/release-name: my-nginx-ingress                                                                                                                                                
│     meta.helm.sh/release-namespace: nginx-ingress                                                                                                                                               
│   labels:                                                                                                                                                                                       
│     app.kubernetes.io/instance: my-nginx-ingress                                                                                                                                               
│     app.kubernetes.io/managed-by: Helm                                                                                                                                                          
│     app.kubernetes.io/name: my-nginx-ingress-nginx-ingress                                                                                                                                     
│     helm.sh/chart: nginx-ingress-0.10.4                                                                                                                                                         
│   name: my-nginx-ingress-nginx-ingress                                                                                                                                                         
│   namespace: nginx-ingress                                                                                                                                                                       

然后,我们发现nginx-ingress的日志并没有打印到.log文件中,而是被重定向到标准输出中。

[root@Ubuntu ~]$ kubectl exec -it my-nginx-ingress-nginx-ingress-68f94c8866-99p4d -- ls -l /var/log/nginx
total 0
lrwxrwxrwx 1 root root 11 Mar 17  2022 access.log -> /dev/stdout
lrwxrwxrwx 1 root root 11 Mar 17  2022 error.log -> /dev/stderr

在宿主机的/var/log目录下存放了所有容器的标准输出日志。

Note:/var/log/containers/.log is normally a symlink to /var/log/pods//*/.log

所以,我们只需要让Filebeat收集/var/log目录下对应容器的日志就可以了。这里我们使用了flexVolume来挂载文件,也可以使用其他方式。
如下是Filebeat helm包的values.yaml文件部分关键内容:

daemonset:
 enabled: true
 extraVolumeMounts:
 - mountPath: /var/log/pods
   name: pods
   readOnly: true
 - mountPath: /var/log/containers
   name: containers
   readOnly: true
 extraVolumes:
 - flexVolume:
     driver: mydriver/hostpath
     options:
       driver.root: /var/log/containers
   name: containers
 - flexVolume:
     driver: mydriver/hostpath
     options:
       driver.root: /var/log/pods
   name: pods
 filebeatConfig:
   filebeat.yml: |
     filebeat.inputs:
     - type: container
       id: my-nginx-ingress
       paths:
         - /var/log/containers/my-nginx-ingress-nginx-ingress-*.log
       processors:
       - replace:
           fields:
           - field: "log.file.path"
             pattern: "/var/log/containers/my-nginx-ingress-nginx-ingress-"
             replacement: "/opt/applog/cluster-demo/nginx-ingress/my-nginx-ingress/nginx-ingress-"
           ignore_missing: false
           fail_on_error: true
       - add_fields:
           target: kubernetes
           fields:
             namespace: cluster-ingress-nginx
             labels:
               app: nginx-ingress-controller
       - decode_json_fields:
           fields: ["message"]
           process_array: false
           max_depth: 1
           target: ""
           overwrite_keys: false
           add_error_key: true
       - drop_fields:
           when:
             and:  
             - has_fields: ['message']
             - equals:
                 stream: 'stdout'
           fields: ["message"]
           ignore_missing: false

配置中包含日志文件的挂载,这里不再赘述,主要看看filebeat.yml文件的配置
首先使用filebeat-input-container收集nginx-ingress-controller的log文件
最后配置了4个processors,分别是replaceadd_fieldsdecode_json_fieldsdrop_fields

replace和add_fields: 由于我们的业务日志大多是指定目录规则存储在宿主机的/opt/applog目录下,通过filebeat的autodiscover收集(如下配置)后发送到kafka,然后logstash取出数据进行处理,在处理时我们需要根据路径和字段名取出对应的值进行逻辑处理。所以对于这种特殊的/var/log/目录下的日志,logstash无法处理,我们需要根据规则replace替换log.file.path以及添加一些我们需要的字段。

filebeat.autodiscover:
  providers:
    - type: kubernetes
      hints.enabled: true
      hints.default_config.enabled: false
      hints.default_config:
        type: log
        paths:
          - /opt/applog/${data.kubernetes.namespace}/${data.kubernetes.labels.app}/${data.kubernetes.pod.name}/**/*.log
          - /opt/applog/${data.kubernetes.namespace}/${data.kubernetes.labels.app}/${data.kubernetes.pod.name}/**/*.json
        ignore_older: 48h
        clean_inactive: 72h

decode_json_fields和drop_fields:nginx-ingress-controller打印出json日志到/var/log/containers目录文件下后格式如下

{
  "log": {
    "time": "2024-08-01T02:52:15+00:00",
    "status": 304,
    "vhost": "app.com",
    "request_proto": "HTTP/1.1",
    "request_length": 945,
    "duration": 0.209,
    "method": "GET",
    "path": "/user/get",
    // 省略其它字段
  },
  "stream": "stdout",
  "time": "2024-08-01T02:52:15.053975954Z"
}

filebeat收集时将这个json结构中的log字段下的内容放到message字段下(猜测是type: container干的?这里不去追究),这在elasticsearch中使用kibana查询时不太友好(不能解析为“可用字段”就不能根据我们需要的字段筛选比如:vhost)。我们希望将message中的json字段解码出来方便搜索和筛选,就要用到decode_json_fields,解码完成后将message字段删除使用drop_fields。

20240117添加:前面nginx日志的log-format只对access_log生效,而error_log还是会以之前的格式输出,导致错误日志经过filebeat的处理后最重要的message字段被drop_fields掉了,所以可以通过and判断的方式来删除message字段,标准输出的stream是stdout,而错误日志的输出stream是stderr

       - drop_fields:
           when:
             and:  
             - has_fields: ['message']
             - equals:
                 stream: 'stdout'
           fields: ["message"]
           ignore_missing: false

这样就实现了stream是stdout就没有message字段,而错误日志的输出stream是stderr,保留message字段。