콘텐츠로 이동

Kubernetes 애플리케이션의 RUM-APM-LOG 연동 분석


사용 사례 소개

기업의 가장 중요한 수익원은 비즈니스이며, 현재 대부분의 기업은 비즈니스를 해당 IT 시스템으로 운영합니다. 따라서 기업의 비즈니스 안정성을 보장하는 것은 결국 기업 내부의 IT 시스템을 어떻게 보장할 것인지로 귀결됩니다. 비즈니스 시스템에 이상이나 장애가 발생하면 일반적으로 비즈니스, 애플리케이션 개발, 운영 등 여러 부서의 동료가 함께 협력하여 문제를 조사해야 하며, 플랫폼 간, 부서 간, 전문 영역 간 등의 다양한 문제가 존재하여 조사에 시간과 노력이 많이 소모됩니다.

이 문제를 해결하기 위해 현재 업계에서는 비교적 성숙한 방식인 RUM + APM + LOG를 통해 전체 비즈니스 시스템의 프론트엔드/백엔드, 로그를 통합 모니터링하고, 세 가지 데이터를 핵심 필드로 연결하여 연동 분석을 구현함으로써 관련 작업자의 효율성을 높이고 시스템의 안정적인 운영을 보장합니다.

  • APM: 애플리케이션 성능 모니터링(APM)
  • RUM: 실제 사용자 모니터링(RUM)
  • LOG: 로그

이 문서는 세 가지 모니터링을 연결하는 방법과 Guance를 사용한 연동 분석 관점을 설명합니다. 데모로 사용된 애플리케이션은 RuoYi 권한 관리 시스템이며, 자세한 내용은 <0부터 1까지 Guance로 Spring Cloud 서비스의 관측 가능성 구축하기>를 참조하세요.

로그와 관련하여 이 문서에서는 DataKit의 Logfwd 수집기를 사용하여 비즈니스 Pod의 로그를 수집합니다. DataKit에서 Logfwd 수집기를 활성화하고, Pod에 Logfwd Sidecar를 추가하여 비즈니스 컨테이너의 로그를 수집하여 DataKit에 전달합니다. 비즈니스에서 Sidecar에 접근 가능하므로 로그 파일을 호스트에 저장할 필요가 없습니다. 자세한 사용법은 아래의 System 배포 모듈을 참조하세요. DataKit은 로그를 수신한 후 구성된 Pipeline을 사용하여 로그 파일을 파싱합니다.

사전 조건

계정 등록

Guance로 이동하여 계정을 등록하고, 등록된 계정/비밀번호로 로그인합니다.

image


DaemonSet 방식으로 DataKit 배포

OpenWay 주소의 token 획득

「관리」 - 「기본 설정」을 클릭하고 아래 그림의 token을 복사합니다.

image

「통합」 - 「DataKit」 - 「Kubernetes」를 클릭하여 최신 datakit.yaml 파일을 가져옵니다.

image

설치 실행

  • 위 단계의 datakit.yaml 파일을 사용하고, 위 그림에서 가져온 token으로 파일의 your-token을 교체합니다.
  • container 수집기, logfwd 수집기, ddtrace 수집기를 활성화합니다. 즉, DataKit 컨테이너에 container.conf, logfwdserver.conf, ddtrace.conf 파일을 마운트합니다.

참고: DataKit 버전에 따라 구성이 다를 수 있으므로 최신 버전을 기준으로 하세요. 이 yaml은 이번 배포의 전체 구성이며, 이후 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:
      - guance.com
    resources:
      - datakits
    verbs:
      - get
      - list
  - 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
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
      - env:
        - name: HOST_IP
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: ENV_K8S_NODE_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: spec.nodeName
        - name: ENV_DATAWAY
          value: https://openway.guance.com?token=XXXXXX
        - name: ENV_GLOBAL_HOST_TAGS
          value: host=__datakit_hostname,host_ip=__datakit_ip,cluster_name_k8s=k8s-prod
        - name: ENV_DEFAULT_ENABLED_INPUTS
          value: cpu,disk,diskio,mem,swap,system,hostobject,net,host_processes,container,statsd,ebpf,rum
        - name: ENV_ENABLE_ELECTION
          value: enable
        - name: ENV_GLOBAL_ENV_TAGS
          value: cluster_name_k8s=k8s-prod
        - name: ENV_HTTP_LISTEN
          value: 0.0.0.0:9529
        - name: ENV_NAMESPACE
          value: guance-k8s-demo
        #- name: ENV_LOG_LEVEL
        #  value: debug
        #- name: ENV_K8S_CLUSTER_NAME
        #  value: k8s-prod
        image: pubrepo.guance.com/datakit/datakit:1.4.10
        imagePullPolicy: Always
        name: datakit
        ports:
        - containerPort: 9529
          hostPort: 9529
          name: port
          protocol: TCP
        securityContext:
          privileged: true
        volumeMounts:
        - mountPath: /var/run
          name: run
        - mountPath: /var/lib
          name: lib
        - mountPath: /var/log
          name: log
        #- mountPath: /var/run/containerd/containerd.sock
        #  name: containerd-socket
        #  readOnly: true
        - mountPath: /usr/local/datakit/conf.d/container/container.conf
          name: datakit-conf
          subPath: container.conf
        - mountPath: /usr/local/datakit/conf.d/log/logfwdserver.conf
          name: datakit-conf
          subPath: logfwdserver.conf
        - mountPath: /usr/local/datakit/conf.d/ddtrace/ddtrace.conf
          name: datakit-conf
          subPath: ddtrace.conf
        - 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

        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
          name: run
        - hostPath:
            path: /var/lib
          name: lib
        - hostPath:
            path: /var/log
          name: log

        - 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:
  #### container
  container.conf: |-
    [inputs.container]
      docker_endpoint = "unix:///var/run/docker.sock"
      containerd_address = "/var/run/containerd/containerd.sock"

      enable_container_metric = true
      enable_k8s_metric = true
      enable_pod_metric = true

      ## Containers logs to include and exclude, default collect all containers. Globs accepted.
      container_include_log = []
      container_exclude_log = ["image:*"]
      #container_exclude_log = ["image:pubrepo.guance.com/datakit/logfwd*", "image:pubrepo.guance.com/datakit/datakit*"]

      exclude_pause_container = true

      ## Removes ANSI escape codes from text strings
      logging_remove_ansi_escape_codes = false

      kubernetes_url = "https://kubernetes.default:443"

      ## Authorization level:
      ##   bearer_token -> bearer_token_string -> TLS
      ## Use bearer token for authorization. ('bearer_token' takes priority)
      ## linux at:   /run/secrets/kubernetes.io/serviceaccount/token
      ## windows at: C:\var\run\secrets\kubernetes.io\serviceaccount\token
      bearer_token = "/run/secrets/kubernetes.io/serviceaccount/token"
      # bearer_token_string = "<your-token-string>"

      [inputs.container.tags]
        # some_tag = "some_value"
        # more_tag = "some_other_value"

  #### ddtrace
  ddtrace.conf: |-
    [[inputs.ddtrace]]
      endpoints = ["/v0.3/traces", "/v0.4/traces", "/v0.5/traces"]
      # ignore_resources = []
      customer_tags = ["node_ip"]
      [inputs.ddtrace.close_resource]
        "*" = ["PUT /nacos/*","GET /nacos/*","POST /nacos/*"]
      ## tags is ddtrace configed key value pairs
      # [inputs.ddtrace.tags]
        # some_tag = "some_value"
        # more_tag = "some_other_value"

  #### logfwdserver
  logfwdserver.conf: |-
    [inputs.logfwdserver]
      ## logfwd 接收端监听地址和端口
      address = "0.0.0.0:9531"

      [inputs.logfwdserver.tags]
      # some_tag = "some_value"
      # more_tag = "some_other_value"

Kubernetes 클러스터마다 DaemonSet으로 배포된 DataKit의 선출을 구분하기 위해 ENV_NAMESPACE 환경 변수를 추가해야 하며, 동일한 token 아래에서 값이 중복될 수 없습니다.

동일한 token 아래에서 다른 Kubernetes 클러스터를 구분하기 위해 전역 태그를 추가해야 하며, 값은 cluster_name_k8s=k8s-prod입니다.

자세한 내용은 <다중 Kubernetes 클러스터 메트릭 수집 모범 사례>를 참조하세요.

명령어 실행

$ cd /usr/local/k8s/
$ kubectl apply -f  datakit.yaml
$ kubectl get pod -n datakit

image

DataKit 설치가 완료되면 Linux 호스트의 일반적인 플러그인이 기본적으로 활성화됩니다. 「Guance」 - 「시나리오」 - 「인프라 Linux 호스트 모니터링 뷰」에서 확인할 수 있습니다.

수집기 이름 설명
cpu 호스트의 CPU 사용량 수집
disk 디스크 점유율 수집
diskio 호스트의 디스크 IO 수집
mem 호스트의 메모리 사용량 수집
swap Swap 메모리 사용량 수집
system 호스트 운영 체제 부하 수집
net 호스트 네트워크 트래픽 수집
host_process 호스트에서 상주(10분 이상 활성)하는 프로세스 목록 수집
hostobject 호스트 기본 정보 수집(운영 체제 정보, 하드웨어 정보 등)
kubernetes Kubernetes 클러스터 메트릭 수집
container 호스트의 컨테이너 객체 및 컨테이너 로그 수집

「인프라」 모듈을 클릭하여 DataKit이 설치된 모든 호스트 목록을 확인합니다.

image

「호스트 이름」을 클릭하면 해당 호스트의 상세 시스템 정보와 통합 실행 상태(해당 호스트에 설치된 모든 플러그인)를 확인할 수 있습니다.

image

image

애플리케이션 예제 배포

예제 설명

Web 계층은 게이트웨이를 통해 백엔드의 Auth 및 System 서비스에 액세스합니다. Web은 Vue로 개발되었고, 백엔드는 Java로 개발되었습니다.
예제에서는 Statsd를 사용하여 JVM을 수집합니다. 예제에서 사용된 이미지 저장소는 172.16.0.215:5000입니다. 예제에서는 ddtrace를 사용하여 Java 애플리케이션의 JVM 메트릭을 수집하며, 예제에서 사용된 Nacos, Redis, MySQL의 내부 IP는 172.16.0.230입니다.

image

Web 배포 파일 작성

Web 애플리케이션의 콘텐츠를 /usr/local/k8s/dist 디렉터리에 복사합니다.

image

/usr/local/k8s/DockerfileWeb 파일을 새로 만듭니다.

$ vim /usr/local/k8s/DockerfileWeb

내용은 다음과 같습니다.

FROM nginx:1.21.0
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

COPY nginx.conf /etc/nginx/nginx.conf
RUN mkdir -p /data/nginx/web/dist/
COPY ./dist/ /data/nginx/web/dist/
WORKDIR /etc/nginx
CMD ["nginx","-g","daemon off;"]
EXPOSE 80
EXPOSE 443

/usr/local/k8s/nginx.conf 파일을 새로 만듭니다. 내용은 다음과 같습니다.

/usr/local/k8s/nginx.conf
events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    #tcp_nopush     on;

    client_max_body_size  50m;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    server {
        listen       80;
        server_name  localhost;

        location / {
            root  /data/nginx/web/dist;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

            location /prod-api/{
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://172.16.0.229:30001/;
        }

        location /nginx_status{
            stub_status;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

/usr/local/k8s/web-deployment.yaml 파일을 새로 만듭니다. 파일 내용은 다음과 같습니다.

/usr/local/k8s/web-deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-service
  labels:
    app: web-service
spec:
  selector:
    app: web-service
  ports:
    - protocol: TCP
      port: 80
      nodePort: 30000
      targetPort: 80
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-service
  labels:
    app: web-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-service
  template:
    metadata:
      labels:
        app: web-service
    spec:
      containers:
      - env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name

        name: web-service
        image: 47.96.6.150:5000/df-demo/demo-web:v1
        #command: ["sh","-c"]
        ports:
        - containerPort: 80
          protocol: TCP

dd-java-agent 이미지

java -jar 방식으로 사용자의 jar를 시작할 때는 -javaagent:/usr/local/datakit/data/dd-java-agent.jar를 사용해야 합니다. 사용자의 이미지에 이 jar가 반드시 존재하는 것은 아니므로, 사용자의 비즈니스 이미지를 변경하지 않기 위해 dd-java-agent.jar를 포함하는 이미지를 만든 다음, Init 컨테이너 방식으로 비즈니스 컨테이너보다 먼저 시작하여 공유 스토리지 방식으로 dd-java-agent.jar를 제공합니다.

Guance에서 이 이미지를 이미 제공하고 있습니다.

pubrepo.guance.com/datakit-operator/dd-lib-java-init

이 예제에서는 Sidecar 방식을 사용합니다. jar를 이미지에 직접 포함하려면 dd-java-agent를 다운로드하고, Dockerfile에서 아래 스크립트를 참조하여 jar를 이미지에 포함시킨 후, 배포 yaml의 -javaagent에서 사용하는 jar를 포함시킨 jar로 변경하면 됩니다.

FROM openjdk:8u292

ENV workdir /data/app/
RUN mkdir -p ${workdir}

COPY  dd-java-agent.jar ${workdir}  # 여기서 dd-java-agent를 이미지에 포함시킵니다.

Gateway 배포 파일 작성

/usr/local/k8s/DockerfileGateway를 새로 만듭니다.

$ vim /usr/local/k8s/DockerfileGateway

파일 내용은 다음과 같습니다.

FROM openjdk:8u292
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ENV jar demo-gateway.jar
ENV workdir /data/app/
RUN mkdir -p ${workdir}
COPY ${jar} ${workdir}
WORKDIR ${workdir}
ENTRYPOINT ["sh", "-ec", "exec java ${JAVA_OPTS} -jar ${jar} ${PARAMS} 2>&1 > /dev/null"]

/usr/local/k8s/gateway-deployment.yaml 파일을 새로 만듭니다. 파일 내용은 다음과 같습니다.

/usr/local/k8s/gateway-deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: gateway-service
  labels:
    app: gateway-service
spec:
  selector:
    app: gateway-service
  ports:
    - protocol: TCP
      port: 9299
      nodePort: 30001
      targetPort: 9299
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: gateway-service
  labels:
    app: gateway-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: gateway-service
  template:
    metadata:
      labels:
        app: gateway-service
    spec:
      containers:
      - env:
        - name: DD_AGENT_HOST
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: NACOS_IP
          value: "172.16.0.230"
        - name: JAVA_OPTS
          value: |-
            -javaagent:/usr/dd-java-agent/agent/dd-java-agent.jar -Ddd.service.name=demo-k8s-gateway  -Ddd.tags=container_host:$(POD_NAME),node_ip:$(DD_AGENT_HOST) -Ddd.service.mapping=redis:redisk8s -Ddd.env=dev -Ddd.agent.port=9529
        - name: PARAMS
          value: "--spring.redis.host=$(NACOS_IP) --spring.nacos.ip=$(NACOS_IP)"
        name: gateway-service
        image: 47.96.6.150:5000/df-demo/demo-gateway:v1
        #command: ["sh","-c"]
        ports:
        - containerPort: 9299
          protocol: TCP
        volumeMounts:
        - mountPath: /usr/dd-java-agent/agent
          name: ddagent
      initContainers:
      - command:
        - sh
        - -c
        - set -ex;mkdir -p /ddtrace/agent;cp -r /datadog-init/* /ddtrace/agent;
        image: pubrepo.guance.com/datakit-operator/dd-lib-java-init
        imagePullPolicy: Always
        name: ddtrace-agent-sidecar
        volumeMounts:
        - mountPath: /ddtrace/agent
          name: ddagent
      restartPolicy: Always
      volumes:
      - emptyDir: {}
        name: ddagent

Auth 배포 파일 작성

/usr/local/k8s/DockerfileAuth를 새로 만듭니다.

$ vim  /usr/local/k8s/DockerfileAuth

파일 내용은 다음과 같습니다.

FROM openjdk:8u292
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ENV jar demo-auth.jar
ENV workdir /data/app/
RUN mkdir -p ${workdir}
COPY ${jar} ${workdir}
WORKDIR ${workdir}
ENTRYPOINT ["sh", "-ec", "exec java ${JAVA_OPTS} -jar ${jar} ${PARAMS} 2>&1 > /dev/null"]

/usr/local/k8s/auth-deployment.yaml 파일을 새로 만듭니다. 파일 내용은 다음과 같습니다.

/usr/local/k8s/auth-deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: auth-service
  labels:
    app: auth-service
spec:
  selector:
    app: auth-service
  ports:
    - protocol: TCP
      port: 9200
      targetPort: 9200
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth-service
  labels:
    app: auth-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: auth-service
  template:
    metadata:
      labels:
        app: auth-service
    spec:
      containers:
      - env:
        - name: DD_AGENT_HOST
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: NACOS_IP
          value: "172.16.0.230"
        - name: JAVA_OPTS
          value: |-
            -javaagent:/usr/dd-java-agent/agent/dd-java-agent.jar -Ddd.service.name=demo-k8s-auth  -Ddd.tags=container_host:$(POD_NAME),node_ip:$(DD_AGENT_HOST) -Ddd.service.mapping=redis:redisk8s -Ddd.env=dev -Ddd.agent.port=9529 
        - name: PARAMS
          value: "--spring.redis.host=$(NACOS_IP) --spring.nacos.ip=$(NACOS_IP)"
        name: auth-service
        image: 47.96.6.150:5000/df-demo/demo-auth:v1
        #command: ["sh","-c"]
        ports:
        - containerPort: 9200
          protocol: TCP
        volumeMounts:
        - mountPath: /usr/dd-java-agent/agent
          name: ddagent
      initContainers:
      - command:
        - sh
        - -c
        - set -ex;mkdir -p /ddtrace/agent;cp -r /datadog-init/* /ddtrace/agent;
        image: pubrepo.guance.com/datakit-operator/dd-lib-java-init
        imagePullPolicy: Always
        name: ddtrace-agent-sidecar
        volumeMounts:
        - mountPath: /ddtrace/agent
          name: ddagent
      restartPolicy: Always
      volumes:
      - emptyDir: {}
        name: ddagent

System 배포 파일 작성

/usr/local/k8s/DockerfileSystem을 새로 만듭니다.

$ vim /usr/local/k8s/DockerfileSystem

파일 내용은 다음과 같습니다.

FROM openjdk:8u292
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
ENV jar demo-modules-system.jar
ENV workdir /data/app/
RUN mkdir -p ${workdir}
COPY ${jar} ${workdir}
WORKDIR ${workdir}

ENTRYPOINT ["sh", "-ec", "exec java ${JAVA_OPTS}   -jar ${jar} ${PARAMS}  2>&1 > /dev/null"]

/usr/local/k8s/system-deployment.yaml 파일을 새로 만듭니다. Pod에서는 3개의 이미지(172.16.0.238/df-ruoyi/demo-system:v1, pubrepo.guance.com/datakit/logfwd:1.2.7, pubrepo.guance.com/datakit-operator/dd-lib-java-init)를 사용합니다.
dd-lib-java-init는 dd-java-agent.jar 파일을 system-container 비즈니스 컨테이너에 제공하고, logfwd는 비즈니스 컨테이너의 로그 파일을 수집합니다. logfwd의 구성 파일은 ConfigMap을 통해 컨테이너에 마운트되며, 구성 파일에서 수집할 로그 파일 위치, source 이름 등을 지정합니다.

system-deployment.yaml의 전체 내용은 다음과 같습니다.

system-deployment.yaml
apiVersion: v1
kind: Service
metadata:
  name: system-service
spec:
  selector:
    app: system-pod
  ports:
    - protocol: TCP
      port: 9201
      #nodePort: 30001
      targetPort: 9201
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: system-deployment
  #labels:
  #  app: system-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: system-pod
  template:
    metadata:
      labels:
        app: system-pod
    spec:          
      containers:
      - name: system-container      
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: DD_AGENT_HOST
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: NACOS_IP
          value: "172.16.0.229"
        - name: DB_IP
          value: "172.16.0.230"
        - name: JAVA_OPTS
          value: |-
            -javaagent:/usr/dd-java-agent/agent/dd-java-agent.jar -Ddd.service.name=demo-k8s-system  -Ddd.tags=container_host:$(POD_NAME),node_ip:$(DD_AGENT_HOST) -Ddd.service.mapping=mysql:mysql-k8s,redis:redisk8s -Ddd.env=dev -Ddd.agent.port=9529 
        - name: PARAMS
          value: "--spring.redis.host=$(DB_IP) --spring.nacos.ip=$(NACOS_IP) --spring.db.ip=$(DB_IP)"
        image: 172.16.0.238/df-ruoyi/demo-system:v1
        #command: ["sh","-c"]
        ports:
        - containerPort: 9201
          protocol: TCP
        volumeMounts:
        - name: ddagent
          mountPath: /usr/dd-java-agent/agent
        - name: varlog
          mountPath: /data/app/logs/ruoyi-system
        resources:
          limits: 
            memory: 512Mi
          requests:
            memory: 256Mi
      - name: logfwd
        image: pubrepo.guance.com/datakit/logfwd:1.2.7
        env:
        - name: LOGFWD_DATAKIT_HOST
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: status.hostIP
        - name: LOGFWD_DATAKIT_PORT
          value: "9531"
        - name: LOGFWD_LOGFWD_ANNOTATION_DATAKIT_LOG_CONFIGS
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.annotations['datakit/log']
        - 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               
      initContainers:
      - name: ddtrace-agent-sidecar
        command:
        - sh
        - -c
        - set -ex;mkdir -p /ddtrace/agent;cp -r /datadog-init/* /ddtrace/agent;
        image: pubrepo.guance.com/datakit-operator/dd-lib-java-init
        imagePullPolicy: Always
        volumeMounts:
        - mountPath: /ddtrace/agent
          name: ddagent
      restartPolicy: Always
      volumes:
      - name: varlog
        emptyDir: {} 
      - name: ddagent
        emptyDir: {} 
      - configMap:
          name: logfwd-conf
        name: logfwd-config 

---

apiVersion: v1
kind: ConfigMap
metadata:
  name: logfwd-conf
data:
  config: |
    [
        {            
            "loggings": [
                {
                    "logfiles": ["/var/log/info.log","/var/log/error.log"],
                    "source": "k8s-log-system",                   
                    "multiline_match": "^\\d{4}-\\d{2}-\\d{2}"
                }
            ]
        }
    ] 

또한 system-deployment.yaml 파일에서는 환경 변수를 사용하여 DataKit 및 logfwd 포트를 지정합니다.

환경 변수 설명:

  • LOGFWD_DATAKIT_HOST: DataKit 주소
  • LOGFWD_DATAKIT_PORT: logfwd 포트

logfwd-conf 매개변수 설명:

  • logfiles: 로그 파일 목록.
  • ignore: 파일 경로 필터, glob 규칙 사용, 조건 중 하나라도 일치하면 해당 파일을 수집하지 않음.
  • source: 데이터 소스.
  • service: 추가 태그, 비어 있으면 기본적으로 $source 사용.
  • pipeline: Pipeline 사용 시 스크립트 경로 정의.
  • character_encoding: 인코딩 선택.
  • multiline_match: 여러 줄 일치.
  • remove_ansi_escape_codes: ANSI 이스케이프 코드(예: 표준 출력의 텍스트 색상 등)를 삭제할지 여부, 값은 true 또는 false.

링크 데이터에 node_ip 태그 추가

datakit.yaml에 ConfigMap 추가:

ddtrace.conf: |-
  [[inputs.ddtrace]]
    endpoints = ["/v0.3/traces", "/v0.4/traces", "/v0.5/traces"]
    # ignore_resources = []
    customer_tags = ["node_ip"]

volumeMounts 아래에 추가:

- mountPath: /usr/local/datakit/conf.d/ddtrace/ddtrace.conf
  name: datakit-conf
  subPath: ddtrace.conf

실제 사용자 모니터링(RUM)

애플리케이션 생성

「Guance 플랫폼」에 로그인하여 「RUM」 - 「애플리케이션 생성」 - 「Web 유형 선택」 - 「동기 로드」를 선택하고, 애플리케이션 이름에 web-k8s-demo를 입력합니다.

image

프론트엔드 RUM 모니터링 활성화

DataKit에서 RUM 수집기를 활성화하려면 ENV_DEFAULT_ENABLED_INPUTS 환경 변수 값 끝에 rum을 추가합니다.

- name: ENV_DEFAULT_ENABLED_INPUTS
  value: cpu,disk,diskio,mem,swap,system,hostobject,net,host_processes,container,statsd,ebpf,rum

RUM에서 사용하는 DataKit 주소는 고객의 네트워크에서 접근 가능한 주소여야 하므로, DataKit의 구성 파일 /usr/local/datakit/conf.d/datakit.conflisten="0.0.0.0:9529"를 수정해야 합니다.
이 예제에서 사용된 DataKit은 DaemonSet 방식으로 배포되었으며, 기본 구성이 이미 수정되었습니다. 실제 프로덕션 환경에서는 RUM에 사용되는 DataKit을 별도로 배포하는 것이 좋습니다.

/usr/local/k8s/dist/index.html 파일을 수정하고, head에 다음 내용을 추가합니다.

<script src="https://static.guance.com/browser-sdk/v2/dataflux-rum.js" type="text/javascript"></script>
<script>
  window.DATAFLUX_RUM &&
    window.DATAFLUX_RUM.init({
      applicationId: 'appid_ddxxxxxxxxxxxxxxxxxx5',
      datakitOrigin: 'http://172.16.0.230:9529',
      env: 'production',
      version: '1.0.0',
      trackInteractions: true,
      traceType: 'ddtrace',
      allowedTracingOrigins: ["http://8.136.207.182:30000","http://8.136.193.105:30000","http://8.136.204.98:30000"]
    })
</script>
매개변수 설명:

  • applicationId: 애플리케이션 ID.

  • datakitOrigin: 사용자가 접근 가능한 DataKit 주소 또는 도메인 이름입니다. 여기서 172.16.0.230은 k8s의 node1 IP 주소입니다.

  • env: 필수, 애플리케이션이 속한 환경으로, test 또는 product 또는 다른 필드입니다.

  • version: 필수, 애플리케이션의 버전 번호입니다.

  • allowedTracingOrigins: RUM과 APM을 연결합니다. 백엔드 서버 주소 또는 도메인 이름을 구성합니다. 이 예제에서는 프론트엔드와 백엔드 액세스 주소가 모두 http://8.136.193.105:30000/이므로 구성 시 30000 포트를 추가해야 합니다.

  • trackInteractions: 사용자 행동 통계입니다. 예: 버튼 클릭, 정보 제출 등의 동작.

  • traceType: 선택 사항이며 기본값은 ddtrace입니다. 현재 ddtrace, zipkin, skywalking_v3, jaeger, zipkin_single_header, w3c_traceparent의 6가지 유형을 지원합니다.

RUM에 대한 자세한 내용은 <Web 애플리케이션 모니터링(RUM) 모범 사례>를 참조하세요.

애플리케이션 성능 모니터링(APM)

ddtrace 활성화

자세한 내용은 <링크 데이터에 node_ip 태그 추가>를 참조하세요.

Java 애플리케이션 ddtrace 연결

system 이미지 파일 DockerfileSystem에서 jar를 시작하는 명령은 다음과 같습니다.

exec java ${JAVA_OPTS}   -jar ${jar}

환경 변수 JAVA_OPTS는 배포 파일 system-deployment.yaml에서 다음과 같이 정의됩니다.

- name: JAVA_OPTS
  value: |-
    -javaagent:/usr/dd-java-agent/agent/dd-java-agent.jar -Ddd.service.name=demo-k8s-system  -Ddd.tags=container_host:$(POD_NAME) -Ddd.service.mapping=mysql:mysql-k8s,redis:redisk8s -Ddd.env=dev -Ddd.agent.port=9529

JAVA_OPTS 상세 설명:

-Ddd.env: 애플리케이션의 환경 유형, 선택 사항
-Ddd.tags: 사용자 정의 태그, 여러 개는 쉼표로 구분, 선택 사항
-Ddd.service.name: JVM 데이터 소스의 애플리케이션 이름, 필수
-Ddd.agent.host=localhost    DataKit 주소, 선택 사항
-Ddd.agent.port=9529         DataKit 포트, 필수
-Ddd.version: 버전, 선택 사항
-Ddd.jmxfetch.check-period: 수집 빈도(밀리초), 기본값 1500, 선택 사항
-Ddd.jmxfetch.statsd.host=127.0.0.1 statsd 수집기의 연결 주소로 DataKit 주소와 동일, 선택 사항
-Ddd.jmxfetch.statsd.port=8125: DataKit의 statsd 수집기 UDP 연결 포트, 기본값 8125, 선택 사항
-Ddd.trace.health.metrics.statsd.host=127.0.0.1: 자체 메트릭 데이터 수집 전송 주소로 DataKit 주소와 동일, 선택 사항
-Ddd.trace.health.metrics.statsd.port=8125: 자체 메트릭 데이터 수집 전송 포트, 선택 사항
-Ddd.service.mapping: 애플리케이션이 호출하는 redis, mysql 등의 별칭, 선택 사항

참고: JAVA_OPTS에는 링크 데이터를 보고할 DataKit 주소가 지정되어 있지 않습니다. 대신 yaml에서 환경 변수 DD_AGENT_HOST를 정의하여 링크 데이터를 보고할 DataKit 주소를 지정합니다. Kubernetes 클러스터에서 링크 데이터 보고 원칙은 POD 링크 데이터를 동일한 호스트에 배포된 DataKit에 보고하는 것입니다. 자세한 구성은 system-deployment.yaml을 참조하세요.

- name: DD_AGENT_HOST
  valueFrom:
    fieldRef:
      apiVersion: v1
      fieldPath: status.hostIP

교차 출처 요청 화이트리스트 설정

response.headers.add('Access-Control-Allow-Headers','x-datadog-parent-id,x-datadog-sampled,x-datadog-sampling-priority,x-datadog-trace-id')

로그

logback.xml 구성

logback.xml을 수정하여 traceId, spanId, service를 로그에 출력하여 링크와 연결합니다.

image

로그 수집 활성화

Kubernetes 로그 수집은 datakit의 logfwd 수집기를 사용하는 것이 좋습니다.

    logfwdserver.conf: |-
      [inputs.logfwdserver]
        ## logfwd 수신 측 수신 주소 및 포트
        address = "0.0.0.0:9531"

        [inputs.logfwdserver.tags]
        # some_tag = "some_value"
        # more_tag = "some_other_value"

volumeMounts 아래에 추가:

- mountPath: /usr/local/datakit/conf.d/log/logfwdserver.conf
  name: datakit-conf
  subPath: logfwdserver.conf

로그 파싱 Pipeline

Pipeline을 사용하여 System 시스템에서 생성된 로그를 파싱하고, 키 정보(예: traceID)를 태그로 잘라내어 링크와 연결할 수 있도록 합니다.

「로그」 모듈을 클릭하고 「Pipelines」로 이동한 후, Pipeline을 새로 만듭니다. 필터에서 System 모듈이 로그를 수집할 때 설정한 Source: k8s-log-system을 선택하고, 다음 내용을 입력한 후 테스트에 성공하면 「저장」을 클릭합니다.

#2022-08-09 13:39:57.392 [http-nio-9201-exec-4] INFO  c.r.s.c.SysUserController - [list,70] - demo-k8s-system 1241118275256671447 9052729774571622516 - 查询用户列表开始

grok(_, "%{TIMESTAMP_ISO8601:time} %{NOTSPACE:thread_name} %{LOGLEVEL:status}%{SPACE}%{NOTSPACE:class_name} - \\[%{NOTSPACE:method_name},%{NUMBER:line}\\] - %{DATA:service_name} %{DATA:trace_id} %{DATA:span_id} - %{GREEDYDATA:msg}")

default_time(time,"Asia/Shanghai")

image

애플리케이션 배포

이미지 빌드 및 Harbor 저장소에 업로드

$ cd /usr/local/k8s/
$ docker build -t 172.16.0.215:5000/df-demo/demo-web:v1 -f DockerfileWeb .
$ docker push 172.16.0.215:5000/df-demo/demo-web:v1

$ docker build -t 172.16.0.215:5000/df-demo/demo-gateway:v1 -f DockerfileGateway .
$ docker push 172.16.0.215:5000/df-demo/demo-gateway:v1

$ docker build -t 172.16.0.215:5000/df-demo/demo-auth:v1 -f DockerfileAuth .
$ docker push 172.16.0.215:5000/df-demo/demo-auth:v1

$ docker build -t 172.16.0.215:5000/df-demo/demo-system:v1 -f DockerfileSystem .
$ docker push 172.16.0.215:5000/df-demo/demo-system:v1

배포

$ cd /usr/local/k8s/
$ kubectl apply -f web-deployment.yaml
$ kubectl apply -f gateway-deployment.yaml
$ kubectl apply -f auth-deployment.yaml
$ kubectl apply -f system-deployment.yaml

image

링크 분석

RUM APM 연동

Web 애플리케이션에 접속하여 「시스템 관리」 - 「사용자 관리」를 클릭합니다. 그러면 사용자 목록 조회 요청인 list가 트리거됩니다. dataflux-rum.js는 trace-id를 생성하여 header에 저장하며, list 인터페이스에 해당하는 trace-id가 2772508174716324531인 것을 확인할 수 있습니다. 요청이 백엔드의 list 인터페이스를 호출하면, 백엔드의 ddtrace가 trace-id를 읽어 자체 trace 데이터에 기록합니다. logback.xml%X{dd.trace_id}가 추가되어 trace_id가 로그와 함께 출력되므로 RUM, APM, 로그의 연동이 구현됩니다.

image

「RUM」 - 「ruoyi-k8s-web」 - 「탐색기」 - 「view」를 클릭합니다. 이전 작업은 사용자 관리 목록 조회였으므로 목록에서 /system/user를 클릭합니다.

image

「Fetch/XHR」을 클릭합니다.

image

위 그림의 prod-api/system/user/list를 클릭합니다. prod-api는 nginx가 추가한 전달 요청이고, /system/user/list는 백엔드의 API 인터페이스입니다. 들어가면 구체적인 요청 및 소요 시간을 확인할 수 있습니다.

image

image

image

로그 분석

「로그」 모듈을 클릭하고 「전체 소스」를 선택합니다. 기본적으로 최근 15분간의 로그를 조회합니다. 프론트엔드에서 생성된 trace_id 2772508174716324531을 「검색창」에 입력하고 Enter 키를 눌러 검색합니다.

image

「APM」 - 「링크 필터 상자」를 클릭하고 trace_id:704229736283371775를 입력한 후 Enter 키를 눌러 링크의 호출 상황을 검색합니다.

image

「SysUserController.list」를 클릭하여 상세 정보를 확인합니다.

image

문서 평가

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