콘텐츠로 이동

Kubernetes 클러스터 로그를 동일 노드의 DataKit에 보고하는 모범 사례


개요

Kubernetes 클러스터에서 DaemonSet으로 배포된 DataKit을 사용하여 메트릭, 트레이스, 로그 데이터를 수집할 때, Pod과 DataKit 간의 상호 작용 성능을 향상시키는 가장 좋은 방법은 해당 Pod이 위치한 노드의 DataKit이 이러한 데이터를 수집하도록 하는 것입니다. DaemonSet으로 배포된 DataKit은 클러스터의 각 노드마다 하나의 DataKit이 시작되므로, 트래픽을 해당 노드의 DataKit으로 라우팅하기만 하면 동일 노드에서의 데이터 수집을 실현할 수 있습니다.

이미지

Kubernetes의 Service 리소스에는 "externalTrafficPolicy"라는 필드가 있으며, Cluster 또는 Local 정책을 설정할 수 있습니다. 이 외부 정책을 사용하려면 Service 유형이 NodePort 또는 LoadBalancer여야 합니다.

  1. Cluster: 트래픽을 다른 노드의 Pod으로 전달할 수 있습니다. 이것이 기본 모드입니다.
  2. Local: 트래픽을 로컬 노드의 Pod으로만 전송합니다.

기본 Cluster 모드에서는 Kube-proxy가 요청 트래픽을 수신하면 전달 시 SNAT(source network address translation)를 수행하여 소스 IP를 노드의 IP로 변경합니다. 이는 요청이 원래 경로로 반환되도록 보장하기 위함입니다. Local 모드로 변경하면 Kube-proxy는 요청 전달 시 소스 IP를 유지하고, 로컬 노드의 Pod으로만 전달하며, 절대 노드를 넘어 전달하지 않습니다.

설치 및 배포

DataKit 배포

1.1.1 배포 파일 다운로드

Guance』에 로그인하여 『통합』 모듈을 클릭하고, 왼쪽 상단의 『DataKit』을 선택한 후 『Kubernetes』를 선택하여 datakit.yaml을 다운로드합니다.

1.1.2 토큰 구성

Guance』에 로그인하여 『관리』 모듈로 이동한 후, 아래 그림의 토큰을 찾아 datakit.yaml 파일의 ENV_DATAWAY 환경 변수 value 값에서 <your-token>을 교체합니다.

        - name: ENV_DATAWAY
          value: https://openway.guance.com?token=<your-token>

이미지

1.1.3 글로벌 태그 설정

datakit.yaml 파일의 ENV_GLOBAL_HOST_TAGS 환경 변수 값 끝에 cluster_name_k8s=k8s-istio를 추가합니다. 여기서 k8s-istio는 글로벌 태그입니다.

        - name: ENV_GLOBAL_HOST_TAGS
          value: host=__datakit_hostname,host_ip=__datakit_ip,cluster_name_k8s=k8s-prod
1.1.4 네임스페이스 설정

DataKit이 선거 시 서로 다른 클러스터를 구분하기 위해 ENV_NAMESPACE 환경 변수를 설정해야 하며, 서로 다른 클러스터의 값은 동일할 수 없습니다. datakit.yaml 파일의 환경 변수 부분에 다음 내용을 추가합니다.

        - name: ENV_NAMESPACE
          value: guance-k8s
1.1.5 수집기 활성화

본 예제에서는 logfwd를 사용하여 로그를 수집하므로 logfwd를 활성화하고 pipeline을 마운트해야 합니다.

        volumeMounts:
        # 다음은 새로 추가된 내용입니다
        - mountPath: /usr/local/datakit/conf.d/log/logfwdserver.conf
          name: datakit-conf
          subPath: logfwdserver.conf 
        - mountPath: /usr/local/datakit/pipeline/pod-logging-demo.p
          name: datakit-conf
          subPath: pod-logging-demo.p
apiVersion: v1
kind: ConfigMap
metadata:
  name: datakit-conf
  namespace: datakit
data:
    #  ...   다음은 새로 추가된 내용입니다
    #### logfwdserver
    logfwdserver.conf: |-
      [inputs.logfwdserver]
        ## logfwd 수신 측 리스닝 주소 및 포트
        address = "0.0.0.0:9531"

        [inputs.logfwdserver.tags]
        # some_tag = "some_value"
        # more_tag = "some_other_value"
    pod-logging-demo.p: |-
        #로그 스타일
        grok(_, "%{TIMESTAMP_ISO8601:time} %{NOTSPACE:thread_name} %{LOGLEVEL:status}%{SPACE}%{NOTSPACE:class_name} - \\[%{NOTSPACE:method_name},%{NUMBER:line}\\] -  - %{GREEDYDATA:msg}")
        default_time(time,"Asia/Shanghai")
1.1.6 DataKit 배포
kubectl apply -f datakit.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: datakit
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: datakit
rules:
- apiGroups:
  - rbac.authorization.k8s.io
  resources:
  - clusterroles
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes
  - nodes/proxy
  - namespaces
  - pods
  - pods/log
  - events
  - services
  - endpoints
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - apps
  resources:
  - deployments
  - daemonsets
  - statefulsets
  - replicasets
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - batch
  resources:
  - jobs
  - cronjobs
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - metrics.k8s.io
  resources:
  - pods
  - nodes
  verbs:
  - get
  - list
- nonResourceURLs: ["/metrics"]
  verbs: ["get"]

---

apiVersion: v1
kind: ServiceAccount
metadata:
  name: datakit
  namespace: datakit

---

apiVersion: v1
kind: Service
metadata:
  name: datakit-service
  namespace: datakit
spec:
  selector:
    app: daemonset-datakit
  ports:
    - protocol: TCP
      port: 9529
      targetPort: 9529

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: datakit
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: datakit
subjects:
- kind: ServiceAccount
  name: datakit
  namespace: datakit

---

apiVersion: apps/v1
kind: DaemonSet
metadata:
  labels:
    app: daemonset-datakit
  name: datakit
  namespace: datakit
spec:
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: daemonset-datakit
  template:
    metadata:
      labels:
        app: daemonset-datakit
      annotations:
        datakit/logs: |
          [
            {
              "disable": true
            }
          ]

    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - env:
        - name: HOST_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        - name: ENV_DATAWAY
          value: https://openway.guance.com?token=<your-token> # 여기에 dataway 실제 주소를 입력하세요
        - name: ENV_GLOBAL_HOST_TAGS
          value: host=__datakit_hostname,host_ip=__datakit_ip
        - name: ENV_DEFAULT_ENABLED_INPUTS
          value: cpu,disk,diskio,mem,swap,system,hostobject,net,host_processes,container
        - name: ENV_ENABLE_ELECTION
          value: enable
        - name: ENV_LOG
          value: stdout
        - name: ENV_HTTP_LISTEN
          value: 0.0.0.0:9529
        - name: ENV_K8S_CLUSTER_NAME
          value: k8s-prod
        - name: ENV_NAMESPACE
          value: guance-k8s
        image: pubrepo.guance.com/datakit/datakit:1.2.16
        imagePullPolicy: Always
        name: datakit
        ports:
        - containerPort: 9529
          hostPort: 9529
          name: port
          protocol: TCP
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /var/run/docker.sock
          name: docker-socket
          readOnly: true
        - mountPath: /var/run/containerd/containerd.sock
          name: containerd-socket
          readOnly: true
        - mountPath: /usr/local/datakit/conf.d/log/logfwdserver.conf
          name: datakit-conf
          subPath: logfwdserver.conf 
        - mountPath: /usr/local/datakit/pipeline/pod-logging-demo.p
          name: datakit-conf
          subPath: pod-logging-demo.p
        - mountPath: /host/proc
          name: proc
          readOnly: true
        - mountPath: /host/dev
          name: dev
          readOnly: true
        - mountPath: /host/sys
          name: sys
          readOnly: true
        - mountPath: /rootfs
          name: rootfs
        - mountPath: /sys/kernel/debug
          name: debugfs
        #- mountPath: /usr/local/datakit/conf.d/db/mysql.conf
        #  name: datakit-conf
        #  subPath: mysql.conf
        #  readOnly: true
        #- mountPath: /usr/local/datakit/conf.d/db/redis.conf
        #  name: datakit-conf
        #  subPath: redis.conf
        #  readOnly: true
        workingDir: /usr/local/datakit
      hostIPC: true
      hostPID: true
      restartPolicy: Always
      serviceAccount: datakit
      serviceAccountName: datakit
      tolerations:
      - operator: Exists
      volumes:
      - configMap:
          name: datakit-conf
        name: datakit-conf
      - hostPath:
          path: /var/run/docker.sock
        name: docker-socket
      - hostPath:
          path: /var/run/containerd/containerd.sock
        name: containerd-socket
      - hostPath:
          path: /proc
          type: ""
        name: proc
      - hostPath:
          path: /dev
          type: ""
        name: dev
      - hostPath:
          path: /sys
          type: ""
        name: sys
      - hostPath:
          path: /
          type: ""
        name: rootfs
      - hostPath:
          path: /sys/kernel/debug
          type: ""
        name: debugfs
  updateStrategy:
    rollingUpdate:
      maxUnavailable: 1
    type: RollingUpdate

---

apiVersion: v1
kind: ConfigMap
metadata:
  name: datakit-conf
  namespace: datakit
data:
    #mysql.conf: |-
    #  [inputs.mysql]
    #  ...
    #redis.conf: |-
    #  [inputs.redis]
    #  ...
    #### logfwdserver
    logfwdserver.conf: |-
      [inputs.logfwdserver]
        ## logfwd 수신 측 리스닝 주소 및 포트
        address = "0.0.0.0:9531"

        [inputs.logfwdserver.tags]
        # some_tag = "some_value"
        # more_tag = "some_other_value"
    pod-logging-demo.p: |-
        #로그 스타일
        grok(_, "%{TIMESTAMP_ISO8601:time} %{NOTSPACE:thread_name} %{LOGLEVEL:status}%{SPACE}%{NOTSPACE:class_name} - \\[%{NOTSPACE:method_name},%{NUMBER:line}\\] -  - %{GREEDYDATA:msg}")
        default_time(time,"Asia/Shanghai")

애플리케이션 배포

마이크로서비스 작성

로그가 어떤 노드에서 출력되는지 쉽게 확인하기 위해, 코드를 작성하여 로그를 출력할 때 노드 서버 이름을 출력합니다. 노드 IP는 환경 변수 HOST_IP에서 가져오고, 노드 서버 이름은 환경 변수 HOST_NAME에서 가져옵니다. 전체 프로젝트는 datakit-springboot-demo를 참조하세요.

이미지

이미지

이미지 생성

Dockerfile 작성

FROM openjdk:8u292

RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ENV jar service-demo-1.0-SNAPSHOT.jar

ENV workdir /data/app/
RUN mkdir -p ${workdir}
COPY ${jar} ${workdir}
WORKDIR ${workdir}
ENTRYPOINT ["sh", "-ec", "exec java ${JAVA_OPTS} -jar ${jar} ${PARAMS} "]

프로젝트의 Jar와 Dockerfile을 같은 디렉터리에 배치합니다. 다음 명령을 실행하여 이미지를 생성하고 프라이빗 레지스트리에 업로드합니다.

docker build -t 172.16.0.238/df-demo/service-demo:v1  .
docker push 172.16.0.238/df-demo/service-demo:v1

배포 파일 작성

demo-service.yaml 배포 파일을 작성하고, Service 리소스 파일에 externalTrafficPolicy: Local을 추가하여 외부 정책의 Local 모드를 활성화합니다. HOST_IP 및 HOST_NAME 환경 변수를 추가하여 IP 및 서버 이름을 출력합니다.

 kubectl  apply -f demo-service.yaml

logfwd 사용에 대한 자세한 내용은 Pod 로그 수집 모범 사례를 참조하세요. logfwd에서 DataKit을 지정하는 환경 변수에 DataKit Service의 도메인 이름 datakit-service.datakit.svc.cluster.local을 사용합니다.

        - name: LOGFWD_DATAKIT_HOST
          value: "datakit-service.datakit.svc.cluster.local"

demo-service.yaml의 전체 내용은 다음과 같습니다.

apiVersion: v1
kind: Service
metadata:
  name: guance-service
  labels:
    app: guance-service
spec:
  selector:
    app: guance-pod
  externalTrafficPolicy: Local
  ports:
    - protocol: TCP
      port: 8090
      targetPort: 8090
      nodePort: 30090
  type: NodePort  
---

apiVersion: apps/v1
kind: Deployment
metadata:
  name: guance-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: guance-pod
  template:
    metadata:
      labels:
        app: guance-pod
      annotations:          
    spec:
      containers:
      - name: guance-demo-container
        image: 172.16.0.238/df-demo/service-demo:v1
        env:
        - name: HOST_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: HOST_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName  
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: DD_AGENT_HOST
          value: $(HOST_IP)    
        - name: JAVA_OPTS
          value: |-
            -javaagent:/usr/dd-java-agent/agent/dd-java-agent.jar -Ddd.service.name=k8s-guance-service  -Ddd.tags=container_host:$(PODE_NAME)  -Ddd.tags=node_ip:$(DD_AGENT_HOST) -Ddd.env=dev -Ddd.agent.port=9529                   
        ports:
        - containerPort: 8090
          protocol: TCP
        volumeMounts:
        - mountPath: /data/app/logs
          name: varlog 
      - name: logfwd
        image: pubrepo.guance.com/datakit/logfwd:1.2.12
        env:
        - name: LOGFWD_DATAKIT_HOST
          value: "datakit-service.datakit.svc.cluster.local"
        - name: LOGFWD_DATAKIT_PORT
          value: "9531"
        - name: LOGFWD_ANNOTATION_DATAKIT_LOGS
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.annotations['datakit/logs']
        - name: LOGFWD_POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: LOGFWD_POD_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        volumeMounts:
        - mountPath: /var/log
          name: varlog 
        - mountPath: /opt/logfwd/config
          name: logfwd-config
          subPath: config          
      restartPolicy: Always
      volumes:
      - name: varlog
        emptyDir: {} 
      - configMap:
          name: guance-logfwd-conf
        name: logfwd-config 

---

apiVersion: v1
kind: ConfigMap
metadata:
  name: guance-logfwd-conf
data:
  config: |
    [
        {            
            "loggings": [
                {
                    "logfiles": ["/var/log/log.log"],
                    "source": "log_fwd_demo",                    
                    "pipeline": "pod-logging-demo.p",
                    "multiline_match": "^\\d{4}-\\d{2}-\\d{2}",
                    "tags": {
                        "flag": "tag1"
                    }
                }
            ]
        }
    ]

트래픽 검증

클러스터의 마스터 노드에 로그인하여 다음 명령을 실행하여 로그를 생성합니다.

이미지

Guance』에 로그인하여 『로그』 모듈로 이동한 후, 데이터 소스에서 log_fwd_demo를 검색하여 로그를 찾고 클릭하여 상세 정보를 확인합니다.

이미지

host가 로그에 출력된 서버 이름과 완전히 일치하며, 여러 번 요청해도 보고된 host가 동일한 것을 확인할 수 있습니다.

이미지

문서 평가

이 페이지가 도움이 되었나요?