Kubernetes 클러스터 로그를 동일 노드의 DataKit에 보고하는 모범 사례¶
개요¶
Kubernetes 클러스터에서 DaemonSet으로 배포된 DataKit을 사용하여 메트릭, 트레이스, 로그 데이터를 수집할 때, Pod과 DataKit 간의 상호 작용 성능을 향상시키는 가장 좋은 방법은 해당 Pod이 위치한 노드의 DataKit이 이러한 데이터를 수집하도록 하는 것입니다. DaemonSet으로 배포된 DataKit은 클러스터의 각 노드마다 하나의 DataKit이 시작되므로, 트래픽을 해당 노드의 DataKit으로 라우팅하기만 하면 동일 노드에서의 데이터 수집을 실현할 수 있습니다.
Kubernetes의 Service 리소스에는 "externalTrafficPolicy"라는 필드가 있으며, Cluster 또는 Local 정책을 설정할 수 있습니다. 이 외부 정책을 사용하려면 Service 유형이 NodePort 또는 LoadBalancer여야 합니다.
- Cluster: 트래픽을 다른 노드의 Pod으로 전달할 수 있습니다. 이것이 기본 모드입니다.
- 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>을 교체합니다.
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 파일의 환경 변수 부분에 다음 내용을 추가합니다.
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 배포¶
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 및 서버 이름을 출력합니다.
logfwd 사용에 대한 자세한 내용은 Pod 로그 수집 모범 사례를 참조하세요. logfwd에서 DataKit을 지정하는 환경 변수에 DataKit Service의 도메인 이름 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가 동일한 것을 확인할 수 있습니다.






