前戏

老花:小白,我们今天来聊聊收集容器日志的那些事儿。

小白:好啊,老花。我听说有好几种方法可以收集容器日志,你能给我讲讲吗?

常见收集容器日志的方法

老花:当然可以。最常见的有两种方法:SidecarDaemonSet

  • Sidecar:俗称边车, 在每个需要收集日志的容器旁边运行一个Sidecar容器,专门负责日志收集。这种方法的好处是可以为每个应用提供定制化的日志收集配置,但缺点是资源消耗较大,因为每个应用都需要额外的容器。

  • DaemonSet:在Kubernetes集群的每个节点上运行一个Pod,负责收集该节点上所有容器的日志。优点是资源消耗较小,因为每个节点只需要一个Pod。缺点是配置不够灵活,因为所有容器共享同一个日志收集配置。

小白:我明白了,那我们通常用哪种方法呢?

老花:这取决于你的具体需求。如果你需要高度定制化的日志收集策略,可能会选择Sidecar。如果你更关心资源消耗,DaemonSet可能更适合。

日志收集系统优缺点

老花:不管是哪种收集方式, 都需要暴露日志路径给收集器。日志除了收集, 通常还有轮转、转换、存储和可视化查询等组件整合在一起, 组成强大日志收集系统。

我们来聊聊几个常见的日志收集系统:

采集、转换、扭转:

  • Loki:轻量级,易于水平扩展,但不支持全文搜索。
  • Fluentd/Fluent Bit:Fluent Bit 轻量级,插件丰富,Fluentd 插件丰富,支持多种数据源和目的地,灵活的配置,适合大规模部署。
  • Logstash:功能强大,但资源消耗大,配置复杂。
  • Filebeat:轻量级,易于配置,但功能不如 Logstash。
  • 网易和阿里开源的采集组件也很强大。

存储:

  • Elasticsearch/ OpenSearch,搜索能力强,但成本高;
  • ClickHouse(CK),查询速度快,成本较低。

套件:

  • ELK Stack(Elasticsearch, Logstash, Kibana):功能强大,但资源消耗大,配置复杂。
  • EFK Stack(Elasticsearch, Fluent, Kibana):功能强大,fluent-bit资源消耗低, 插件不如fluentd丰富,配置容易。
  • Loki on grafana:采集组件可以使用promtail或者fluent-bit, 不需要额外的存储, 还自带可视化界面 UI, 多租户, 读写分离, 但不支持全文搜索。
  • 各种采集器+ Kafka + ClickHouse +ClickVisual 等等。

小白:听起来每个系统都有它的优缺点,我们应该怎么选择呢?

老花:确实,选择哪个系统取决于你的具体需求,比如预算、资源、搜索需求等。

Loki 接入 MongoDB 分片集群日志最佳实践

小白:老花,我们公司正在用 MongoDB 分片集群,你觉得用 Loki 来收集日志怎么样?

老花:这是个好问题。Loki 可以很好地集成 MongoDB 分片集群的日志。我们可以部署 Promtail或者Fluent-bit 等采集器 作为 DaemonSet 来收集 MongoDB 的日志,然后将它们发送到 Loki

小白:那 Promtail或者Fluent-bit 怎么知道哪些日志文件需要收集呢?

老花:它们会使用 KubernetesAPI 来发现节点上的 Pods,并根据 Pod 的注解或者其他配置来决定是否收集该 Pod 的日志。

小白:那听起来不错,我们可以试试。

Loki Stack 部署

老花:我来帮你整理一下 Loki 的安装步骤,这里我们使用Fluent Bit作为采集器。我们仍然使用前文创建的mongodb-sharded 来部署helm应用。

准备工作

参考链接:

导入仓库:

$ helm repo add grafana https://grafana.github.io/helm-charts
$ helm repo update

导入镜像:

$ docker pull grafana/fluent-bit-plugin-loki:2.1.0-amd64
$ kind load docker-image grafana/fluent-bit-plugin-loki:2.1.0-amd64 --name mongodb-sharded

$ docker pull grafana/loki:2.6.1
$ kind load docker-image grafana/loki:2.6.1 --name mongodb-sharded

$ docker pull grafana/grafana:10.3.3
$ docker pull quay.io/kiwigrid/k8s-sidecar:1.19.2
$ kind load docker-image grafana/grafana:10.3.3 --name mongodb-sharded
$ kind load docker-image quay.io/kiwigrid/k8s-sidecar:1.19.2--name mongodb-sharded

安装 Fluent-bit

$ helm upgrade --install  fluent-bit grafana/fluent-bit --create-namespace --namespace=loki
$ kubectl get pods -n loki
NAME                               READY   STATUS    RESTARTS   AGE
fluent-bit-fluent-bit-loki-hll2q   1/1     Running   0          30s
fluent-bit-fluent-bit-loki-m4gz4   1/1     Running   0          30s
fluent-bit-fluent-bit-loki-zf4tp   1/1     Running   0          30s

安装 Loki

$ helm upgrade --install loki grafana/loki-stack --set fluent-bit.enabled=false,promtail.enabled=false,grafana.enabled=true,grafana.service.type=NodePort --create-namespace --namespace=loki

$ kubectl get pods -n loki
NAME                               READY   STATUS    RESTARTS   AGE
fluent-bit-fluent-bit-loki-hll2q   1/1     Running   0          18m
fluent-bit-fluent-bit-loki-m4gz4   1/1     Running   0          18m
fluent-bit-fluent-bit-loki-zf4tp   1/1     Running   0          18m
loki-0                             1/1     Running   0          16m
loki-grafana-66f56c89f7-gl5zh      2/2     Running   0          7m48s

Loki Stack 问题排查

fluent-bit 写入报错

查看日志:

$ kubectl  -n loki logs -f fluent-bit-fluent-bit-loki-hll2q
level=warn caller=client.go:288 id=0 component=client host=fluent-bit-loki:3100 msg="error sending batch, will retry" status=-1 error="Post \"http://fluent-bit-loki:3100/api/prom/push\": context deadline exceeded"

$ kubectl -n loki get svc
$ kubectl -n loki get svc
NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
loki              ClusterIP   10.0.52.138   <none>        3100/TCP   25m
loki-grafana      ClusterIP   10.0.82.18    <none>        80/TCP     16m
loki-headless     ClusterIP   None          <none>        3100/TCP   25m
loki-memberlist   ClusterIP   None          <none>        7946/TCP   25m

显然, 这里的service有问题, 我们需要改成loki:

$ kubectl edit cm fluent-bit-fluent-bit-loki -n loki
[Output]
    Name grafana-loki
    Match *
    Url http://loki:3100/api/prom/push
    TenantID ""
    BatchWait 1
    BatchSize 1048576

发现报错依然存在, 显然这个镜像是不支持热加载的, 现在我们手动重启pod或者也可以在开始安装的时候直接指定配置:

helm upgrade --install  fluent-bit grafana/fluent-bit --create-namespace --namespace=loki --set loki.serviceName="loki"

grafana dashboard 访问

kind是部署在docker中的, 我们先难道容器ip:

 $ docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED      STATUS      PORTS                                                 NAMES
d75686260f85   kindest/node:v1.25.3   "/usr/local/bin/entr…"   8 days ago   Up 8 days                                                         mongodb-sharded-worker3
8627b5cd41f9   kindest/node:v1.25.3   "/usr/local/bin/entr…"   8 days ago   Up 8 days   0.0.0.0:31000->31000/tcp, 127.0.0.1:38823->6443/tcp   mongodb-sharded-control-plane
b0a00cb36381   kindest/node:v1.25.3   "/usr/local/bin/entr…"   8 days ago   Up 8 days                                                         mongodb-sharded-worker
aff9e82d00be   kindest/node:v1.25.3   "/usr/local/bin/entr…"   8 days ago   Up 8 days

$ docker inspect 8627b5cd41f9 |grep IPAddress
            "SecondaryIPAddresses": null,
            "IPAddress": "",
                    "IPAddress": "172.18.0.4",

这说明在docker中的30776暴露了出来:

$ kubectl  -n loki get svc
NAME              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
loki              ClusterIP   10.0.52.138   <none>        3100/TCP       49m
loki-grafana      NodePort    10.0.82.18    <none>        80:30776/TCP   40m
loki-headless     ClusterIP   None          <none>        3100/TCP       49m
loki-memberlist   ClusterIP   None          <none>        7946/TCP       49m

理论上172.18.0.4:30776是可以通的。

为了让我们的虚拟机可以在主机上访问, 我们翻看下之前暴露的端口:

$ cat cluster.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 31000  # 将主机 31000 端口映射到容器的 31000 端口
    hostPort: 31000
    listenAddress: "0.0.0.0" # Optional, defaults to "0.0.0.0"
    protocol: tcp # Optional, defaults to tcp

因此, 我们要把grafanaNodePort改成31000:

$ kubectl edit svc loki-grafana  -n loki

恭喜你, 可以在浏览器打开31000端口了~

下面获取登录密码:

$ kubectl get  secret loki-grafana  -n loki -ojson | jq .data
{
  "admin-password": "SWhZcldQaTBkUkdjZ1EyTzVFTml3anZUY085WElSb09aWHQxTjZIMA==",
  "admin-user": "YWRtaW4=",
  "ldap-toml": ""
}

$ kubectl get  secret loki-grafana  -n loki -ojson | jq -r '.data."admin-password"' | base64 -d

然后我们就可以登录grafana页面了~

添加datasource:http://loki:3100, 点击explore就可以查询了:

小尾巴

小白: 太好了, 那我们开始查询容器日志吧!

老花: 休息会儿, 让我下期帮你介绍下loki语法哈~ 在此之前, 你可以使用这个模板这个模板摸索哦~