Kubernetes クラスタログを同ノードの DataKit に報告するベストプラクティス¶
はじめに¶
Kubernetes クラスタにおいて、DaemonSet でデプロイした DataKit を使用してメトリクス、トレース、ログデータを収集する場合、Pod と DataKit の間のインタラクション性能を向上させる最善の方法は、その Pod が動作するノード上の DataKit でこれらのデータを収集することです。DaemonSet としてデプロイされた DataKit は、クラスタの各ノードに 1 つずつ起動されるため、トラフィックをそのノードの 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 token の設定¶
『Guance』にログインし、『管理』モジュールに移動し、下図の token を見つけ、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")
アプリケーションのデプロイ¶
マイクロサービスの作成¶
どの node ノードでログが出力されているかを確認しやすくするために、ログ出力コードでノードのサーバ名を出力するようにします。ノード 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"
}
}
]
}
]
トラフィックの検証¶
クラスタの master ノードにログインし、以下のコマンドを実行してログを生成します。
『Guance』にログインし、『ログ』モジュールに移動し、データソースから log_fwd_demo を検索し、ログを見つけてクリックし詳細を表示します。
host がログ出力のサーバ名と完全に一致していることが確認できます。複数回リクエストしても、報告される host は同じです。






