如何获取Pod标准输出(stdout)日志,使用Grafana Agent收集,最后上传到Grafana Loki

原创 吴就业 198 0 2024-03-26

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://wujiuye.com/article/eb6e2ee612a34c01b5a269537d2ebbd7

作者:吴就业
链接:https://wujiuye.com/article/eb6e2ee612a34c01b5a269537d2ebbd7
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。

本篇内容包括:

  1. 如何获取Pod的标准输出(stdout)日志
  2. 如何使用Grafana Agent收集日志(附配置案例讲解)
  3. 如何将日志上传Grafana Loki

如何获取Pod的标准输出(stdout)日志

我们通过kubectl logs命令可以查看某个pod输出到标准输出的日志(stdout),甚至还可以查看指定容器的日志。正常理解,我们将日志输出到标准输出是不存文件的,那么为什么还可以获取到标准输出的日志呢?其实我们输出到标准输出的日志也是会被存到某个文件的。

参考文献:https://loggie-io.github.io/docs/user-guide/use-in-kubernetes/general-usage/

如果我们的k8s集群使用的容器运行时的是dockershim,那么正常情况下我们可以在宿主机的/var/lib/docker/containers路径中找到对应容器的stdout的日志,默认为/var/lib/docker/containers/{containerId}/{containerId}-json.log。通过读取这个文件就能获取到容器的标准输出(stdout)。

但是,怎么知道这个容器是属于哪个Pod的呢?

我们还需要知道pod的namespace和name,才能获取关联的Deployment。

如果容器运行时是标准的CRI,那么kubelet会在宿主机/var/log/pods/目录下生成/var/log/pods/<namespace>_<pod_name>_<pod_id>/<container_name>/<num>.log日志文件。而如果运行时是dockershim,这个文件就会是一个软链文件,会链接到/var/lib/docker/containers/{containerId}/{containerId}-json.log。所以我们可以直接读取宿主机下的/var/log/pods/<namespace>_<pod_name>_<pod_id>/<container_name>/<num>.log文件。

DaemonSet可以给Pod挂载/var/log/pods/这个路径,然后遍历**/*.log文件,通过log文件的父级目录能够获取文件所属的Pod的namespace和pod的名称,通过log文件名能够获取容器名。

我们可以做个实验验证:写个DaemonSet,并给Pod挂载/var/log/pods/宿主机目录,然后运行/bin/sh (或 /bin/bash)进入容器终端,查看/var/log/pods/目录。

附DaemonSet:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: curl-daemonset
spec:
  selector:
    matchLabels:
      app: curl-daemonset
  template:
    metadata:
      labels:
        app: curl-daemonset
    spec:
      containers:
      - name: curl-container
        image: curlimages/curl:latest
        command: ["sleep", "infinity"]
        volumeMounts:
        - name: pod-logs
          mountPath: /var/log/pods/
      volumes:
      - name: pod-logs
        hostPath:
          path: /var/log/pods/

进入Pod的/var/log/pods目录执行ls -la命令输出如下:

截屏2024-03-19 19.00.25

使用tree命令查看输出如下:

截屏2024-03-19 19.01.15

如何使用Grafana Agent收集日志(附配置案例讲解)

Grafana Agent是一个开源的Agent项目,集成了指标、日志、链接追踪的收集上报能力,支持将指标上报到Prometheus或Grafana Mimir,支持将日志收集到Grafana Loki,支持将tracing收集到Grafana Tempo。

Grafana Agent的配置项基本兼容Prometheus的配置。很多Grafana Agent没给出的配置项解析,需要我们去查看Prometheus的配置项文档:https://prometheus.io/docs/prometheus/2.45/configuration/configuration/。

关于配置Grafana Agent如何收集日志并将其发送到Grafana Loki,详细的可以阅读官方文档。官方文档给了很多例子,唯独没给出收集K8s的Pod标准输出的例子。这篇文章的目的是降低上手门槛,通过给出收集K8s的Pod标准输出的配置案例,并解释为什么这样配置,让读者能够快速上手。

收集k8s的Pod标准输出我们需要借助DaemonSet,在每个Node上部署一个Grafana Agent Pod,这个Pod用来收集日志。

部署Grafana Agent的DaemonSet如下:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: grafana-agent
spec:
  selector:
    matchLabels:
      app: grafana-agent
  template:
    metadata:
      labels:
        app: grafana-agent
    spec:
      serviceAccountName: grafana-agent
      containers:
      - name: grafana-agent
        image: grafana/agent:latest
        command:
          - grafana-agent
          - -config.file=/etc/agent/agent.yaml
        env:
        - name: HOSTNAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        volumeMounts:
          - name: config-volume
            mountPath: /etc/agent
          - name: pod-logs-volume
            mountPath: /var/log/pods
          - name: store-otherdata-volume
            mountPath: /tmp
      volumes:
      - name: config-volume
        configMap:
          name: grafana-agent-config
      - name: pod-logs-volume
        hostPath:
          path: /var/log/pods
      - name: store-otherdata-volume
        hostPath:
          path: /tmp

关于挂盘部分:

关于环境变量:

由于grafana agent的日志收集模块需要读取一些k8s资源,因此还需要配置rbac赋予权限。

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: grafana-agent
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: grafana-agent
rules:
- apiGroups:
  - ""
  resources:
  - nodes
  - nodes/proxy
  - nodes/metrics
  - services
  - endpoints
  - pods
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: grafana-agent
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: grafana-agent
subjects:
- kind: ServiceAccount
  name: grafana-agent
  namespace: default

最后就是配置文件了。

apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-agent-config
data:
  agent.yaml: |
    server:
      log_level: debug ## 这个是配置grafana agent本身的日记输出级别,测试验证阶段我们设置为debug
    
    logs:
      configs:
      - name: default
        clients:
        - url: https://<username>:<password>@logs-prod-006.grafana.net/loki/api/v1/push
        positions: 
          filename: /tmp/logs/positions.yaml
        scrape_configs:
        - job_name: kubernetes-pods
          pipeline_stages:
          - cri: {}
          kubernetes_sd_configs:
          - role: pod
          relabel_configs:
          - source_labels:
            - __meta_kubernetes_pod_node_name
            target_label: __host__
          - action: replace
            source_labels:
            - __meta_kubernetes_namespace
            target_label: namespace
          - action: replace
            source_labels:
            - __meta_kubernetes_pod_name
            target_label: pod
          - action: replace
            source_labels:
            - __meta_kubernetes_pod_container_name
            target_label: container
          - action: replace
            source_labels:
            - __meta_kubernetes_pod_host_ip
            target_label: host_ip
          - action: replace
            target_label: __path__
            source_labels:
            - __meta_kubernetes_pod_uid
            - __meta_kubernetes_pod_container_name
            separator: /
            replacement: "/var/log/pods/*$1/*.log"
          

配置项说明:

截屏2024-03-26 11.33.12

  • dockershim的日志格式: {"log":"level=info ts=2019-04-30T02:12:41.844179Z caller=filetargetmanager.go:180 msg=\"Adding target\"\n","stream":"stderr","time":"2019-04-30T02:12:41.8443515Z"}
  • cri的日志格式: 2019-01-01T01:00:00.000000001Z stderr P some log message

relabel_configs配置的解释:

其中最主要的是这个配置:

- action: replace
   target_label: __path__
   source_labels:
   - __meta_kubernetes_pod_uid
   - __meta_kubernetes_pod_container_name
   separator: /
   replacement: "/var/log/pods/*$1/*.log"

通过替换__path__标签来指定日志文件的路径。separator: /定义使用/分隔符将__meta_kubernetes_pod_uid__meta_kubernetes_pod_container_name标签的值连接起来。/var/log/pods/*$1/*.log中的$1会被{__meta_kubernetes_pod_uid}/{__meta_kubernetes_pod_container_name}替换。所以是匹配/var/log/pods目录下的*{__meta_kubernetes_pod_uid}/{__meta_kubernetes_pod_container_name}/*.log日记文件。

其它是自定义标签,例如这个:

- action: replace
  source_labels:
  - __meta_kubernetes_pod_host_ip
  target_label: host_ip

意思是我们自定义了host_ip这个标签,将grafana agent提供的元标签__meta_kubernetes_pod_host_ip输出为目标标签host_ip

grafana-agent提供的这些元标签,只是提供值给我们使用,并不会把这些元标签都上报。例如,如果我们不配置__meta_kubernetes_pod_host_ip的目标标签为host_ip,收集的日记并不存在__meta_kubernetes_pod_host_ip这个标签。

如何将日志上传Grafana Loki

将日志上传到Grafana Cloud,其实我们只需要修改配置文件中log_configs模块的clients.url即可。这个url填什么呢?

假如你已经注册了Grafana Cloud,有一个Grafana Cloud的账号。

现在可以进入My Account页面,然后找到Loki产品,点击Details按钮。

截屏2024-03-26 11.56.48

进入产品实例页面后,找到Sending Logs

截屏2024-03-26 12.01.27

我们可以直接拿到url,然后其中的<Your Grafana.com API Token>,可点击Generate now按钮生成一个Token,这个Token就是密码了。

最后,如何查看收集的日志?

在Loki产品实例页面,找到Using Grafana with Logs

截屏2024-03-26 11.57.51

这里可以看到我们的数据源名称。这里是grafanacloud-wujiuye-logs

回到My Account页面,然乎找到Grafana产品,点击Launch按钮进入Grafana控制台。

截屏2024-03-26 12.04.06

点击explore菜单,数据源选择grafanacloud-wujiuye-logs。然后就可以查看收集的日志了。

截屏2024-03-26 12.07.59

#云原生

声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

文章推荐

云原生专栏第一阶段完结,第二阶段开始

由于方向的错误,我们第一阶段开发的云原生PaaS平台这个项目也迎来了终结。但我们换了个方向继续做云原生PaaS平台。

使用Grafana Agent收集Pod的cpu和内存指标,以及标准输出日志的完整案例

通常指标和日志收集这两者是一起的,可观测即离不开指标,也离不开日记。当两者都需要的时候,就没必要部署两个DaemonSet了。本篇将两者结合成一个完整的案例,大家可以直接拿去部署使用。

如何获取Pod的CPU和内存指标,使用Grafana Agent收集指标,上传到Prometheus

本篇是作者在云原生PaaS平台项目中实战可观测能力做的技术调研,将关键技术知识点讲透,涉及:如何获取Pod的cpu和内存指标、使用Grafana Agent收集指标、上传到Prometheus。

Kubernetes可观测之Metrics API,什么是Metrics API?

不禁感叹,k8s这个底座设计的太牛了,我们不仅可以通过CRD + 控制器做扩展,还可以自定义APIService去做扩展。k8s,牛啊!

通过预测Pod的request实现超卖,如果是java感觉容易大面积重启?

超卖其实就是赌徒思想,堵的就是概率。一个节点上的个别Pod异常突增的概率都很小,同时很多Pod一起异常突增的概率更小。

k8s 自定义调度器禁用了某个评分插件,对默认调度器是否有影响?

不会影响到默认调度器,只会影响到scheduler-plugins调度器本身。默认调度器不会因为自定义调度器的配置而改变。