EFK는 elasticsearch, fluentd, kibana를 묶어 표현한 단어
각 노드의 파드들이 가지는 로그를 취합해 검색하여 시각화하는 EFK의 대한 예제
우선 namespace를 정의
#elasticsearch-namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
name: kube-logging
역시 원하는데로 설정하여 사용하여도 좋으나 이후 나올 코드들의 네임 스페이스도 변경해야 함
elasticsearch의 경우 persistentvolume에 로그를 저장하여 사용하기 때문에 persistentvolume을 정의
#persistentvolume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: data
namespace: kube-logging
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: data
hostPath:
path: /mnt/disk/vol1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
위의 persistentvolume과 연결할 storageclass 정의
#storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: data
namespace: kube-logging
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
실행 후 터미널에서 아래 명령어를 실행해 권한을 변경
chmod 777 /mnt/disk/vol1
뒤의 /mnt/disk/vol1 은 pv에서 설정한 루트
elasticsearch의 특성 상 종료되더라도 로그기록을 유지한채로 다시 시작되어야 하기 때문에 statefulset을 사용
#elasticsearch-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace: kube-logging
spec:
serviceName: elasticsearch-svc
replicas: 1
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch-svc
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
ports:
- containerPort: 9200
name: rest
protocol: TCP
- containerPort: 9300
name: inter-node
protocol: TCP
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
env:
- name: cluster.name
value: k8s-logs
- name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: discovery.seed_hosts
value: "es-cluster-0.elasticsearch"
- name: cluster.initial_master_nodes
value: "es-cluster-0"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
initContainers:
- name: fix-permissions
image: busybox
command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
securityContext:
privileged: true
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
- name: increase-vm-max-map
image: busybox
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
- name: increase-fd-ulimit
image: busybox
command: ["sh", "-c", "ulimit -n 65536"]
securityContext:
privileged: true
volumeClaimTemplates:
- metadata:
name: data
namespace: kube-logging
labels:
app: elasticsearch
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: data
resources:
requests:
storage: 5Gi
elasticsearch의 service 정의
kind: Service
apiVersion: v1
metadata:
name: elasticsearch-svc
namespace: kube-logging
labels:
app: elasticsearch
spec:
#type: ClusterIP
selector:
app: elasticsearch
clusterIP: None
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node
헤드리스 서비스로 구성하였지만 연결 문제가 존재할 경우 clusterIP: None을 주석처리 하고 type: ClusterIP의 주석처리를 해제하여 내부 IP를 할당해 사용
하지만 스테이트풀셋은 헤드리스 서비스를 기본으로 사용하기 때문에 가급적 유지하길 바람
위의 사항까지 잘 구현했다면 pv와 pvc의 정보를 확인하여 status가 bound 상태인지 확인
만약 바운드상태가 아니라면 kubectl delete -f <yaml file> #ex: kubectl delete -f elasticsearch-statefulset.yaml의 명령어를 적용해 지우고 네임 스페이스와 엘라스틱서치부터 구현한 후 다시 시도
이제 각 노드에 배포해 로그를 가져올 fluentd를 정의
하나의 yaml에 serviceaccount, clusterrole, clusterrolebinding, daemonset을 모두 정의하였으니 잘 살펴야 함
#위에서 부터 serviceaccount, clusterrole, clusterrolebinding, daemonset 정의
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: kube-logging
labels:
app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd
labels:
app: fluentd
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: fluentd
namespace: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-logging
labels:
app: fluentd
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
serviceAccount: fluentd
serviceAccountName: fluentd
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch-svc.kube-logging.svc.cluster.local" # elasticsearch의 svc name과 네임스페이스를 잘 맞춰서 적용
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value: "http"
- name: FLUENTD_SYSTEMD_CONF
value: disable
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
# - name: fluentd-extra-config
# configMap: # 로그 레코드를 처리하는 방법을 지정한 구성 파일 매핑
# name: "fluentd-hands-on-config"
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch-svc.kube-logging.svc.cluster.local" # elasticsearch의 svc name과 네임스페이스를 잘 맞춰서 적용
따로 떼어놓은 부분은 사소한 실수로 연결 오류를 만들 수 있으니 주의해서 보길 바람
해당 fluentd yaml로 모든 로그를 가져와 검색이 가능하지만 만약 특정 필터를 적용해 선택적으로 로그를 가져오고 싶다면 주석처리한 volume부분을 해제하고 아래의 configmap.yaml을 적용
apiVersion: v1
kind: ConfigMap
metadata:
name: fluentd-hands-on-config
namespace: kube-system
data: # 플루언트 구성이 data 요소에 들어감
fluentd-hands-on.conf: |
<match kubernetes.**istio**> # kubernetes로 시작하고 태그 이름 중에 istio라는 문자열이 있는지 찾음
@type rewrite_tag_filter # 태그 이름을 변경한 후 플루언티드 라우팅 엔진으로 로그 레코드를 재전송
<rule>
key log # 로그 레코드에서 log 필드를 찾도록 key 필드를 log로 설정
pattern ^(.*)$ # 모든 문자열과 일치
tag istio.${tag} # 태그 앞에 istio 문자열을 붙임
</rule>
</match>
<match kubernetes.**hands-on**>
@type rewrite_tag_filter
<rule>
key log
pattern ^(.*)$
tag spring-boot.${tag} # spring-boot 태그 붙임
</rule>
</match>
<match spring-boot.**>
@type rewrite_tag_filter
<rule> # log 필드가 타임스탬프로 시작하는지 확인
key log
pattern /^\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3}.*/
tag parse.${tag}
</rule>
<rule> # log 필드가 타임스탬프로 시작하지 않으면 스택 추적 로그 레코드로 처리
key log
pattern /^.*/
tag check.exception.${tag}
</rule>
</match>
<match check.exception.spring-boot.**>
@type detect_exceptions # 여러 줄로 된 예외를 스택 추적을 포함하는 한 줄의 로그 레코드로 취합하고자 사용
languages java
remove_tag_prefix check # 무한 루프를 제거하기 위해 check 접두어 제거
message log
multiline_flush_interval 5
</match>
<filter parse.spring-boot.**> # 로그 레코드르 재전송하지 않으며, 로그 레코드 태그와 일치하는 구성 파일의 다음 요소로 전달
@type parser
key_name log
time_key time
time_format %Y-%m-%d %H:%M:%S.%N
reserve_data true
# Sample log message: 2019-08-10 11:27:20.497 DEBUG [product-composite,677435df95da585a22d6d6a62db84082,59f32cef360398a4,true] 1 --- [or-http-epoll-1] s.m.u.h.GlobalControllerExceptionHandler : Returning HTTP status: 404 NOT_FOUND for path: /product-composite/13, message: Product Id: 13 not found
# <time> <spring.level> <log> 등을 추출
format /^(?<time>\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3})\s+(?<spring.level>[^\s]+)\s+(\[(?<spring.service>[^,]*),(?<spring.trace>[^,]*),(?<spring.span>[^,]*),[^\]]*\])\s+(?<spring.pid>\d+)\s+---\s+\[\s*(?<spring.thread>[^\]]+)\]\s+(?<spring.class>[^\s]+)\s*:\s+(?<log>.*)$/
</filter>
주석이 달린 부분을 잘 확인하여 수정 후 사용 요망
시각화를 위한 kibana의 deployment와 service정의
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: kube-logging
labels:
app: kibana
spec:
ports:
- port: 5601
selector:
app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: kube-logging
labels:
app: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:7.2.0
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_HOSTS
value: 'http://elasticsearch-svc:9200'
ports:
- containerPort: 5601
만약 연결문제로 위에서 nodeport로 진행할 경우 해당부분을 http://<CLUSTER-IP>:<node ports>로 진행
진행이 끝났다면 port-forward을 통해 연결
이때 사용되는 파드는 kibana의 파드이므로 이름을 알아와야 함
kubectl port-forward -n kube-logging kibana-675bc55bbb-fm6nk 5601:5601
여기서 5601은 키바나의 서비스에서 정의한 port번호
http://localhost:5601/을 통해 키바나에 접속가능
use Elasticsearch data -> connect to youre elasticsearch index
정상적으로 설정되었다면 그림처럼 로그들이 나오겠지만 로그가 안나온다면 volume과의 연결이 안되었을 가능성이 큼
인덱스 이름을 정해주고 다음으로 넘어가면 time filter를 설정
memory오류가 나타난다면 아래의 yaml 파일을 적용
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
rbac.authorization.k8s.io/aggregate-to-admin: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: system:aggregated-metrics-reader
rules:
- apiGroups:
- metrics.k8s.io
resources:
- pods
- nodes
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
rules:
- apiGroups:
- ""
resources:
- pods
- nodes
- nodes/stats
- namespaces
- configmaps
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server-auth-reader
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: extension-apiserver-authentication-reader
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: metrics-server:system:auth-delegator
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: metrics-server
name: system:metrics-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:metrics-server
subjects:
- kind: ServiceAccount
name: metrics-server
namespace: kube-system
---
apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
ports:
- name: https
port: 443
protocol: TCP
targetPort: https
selector:
k8s-app: metrics-server
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
strategy:
rollingUpdate:
maxUnavailable: 0
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls=true ## 추가부분
- --kubelet-preferred-address-types=InternalIP ## 추가부분
image: k8s.gcr.io/metrics-server/metrics-server:v0.5.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /livez
port: https
scheme: HTTPS
periodSeconds: 10
name: metrics-server
ports:
- containerPort: 4443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /readyz
port: https
scheme: HTTPS
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 200Mi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /tmp
name: tmp-dir
nodeSelector:
kubernetes.io/os: linux
hostNetwork: true
priorityClassName: system-cluster-critical
serviceAccountName: metrics-server
volumes:
- emptyDir: {}
name: tmp-dir
---
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
labels:
k8s-app: metrics-server
name: v1beta1.metrics.k8s.io
spec:
group: metrics.k8s.io
groupPriorityMinimum: 100
insecureSkipTLSVerify: true
service:
name: metrics-server
namespace: kube-system
version: v1beta1
versionPriority: 100
137줄과 138줄의 코드를 추가하여 적용하는 것으로 해결 가능
'Kubernetes' 카테고리의 다른 글
Kubesphere Tower Federation (0) | 2024.05.23 |
---|---|
Kubefed (0) | 2024.05.23 |
Kubernetes Prometheus (0) | 2024.05.23 |
Kubesphere Federation (0) | 2024.05.23 |
Kubekey를 사용한 Kubernetes, Kubesphere 배포 (0) | 2024.05.23 |