CoreDNS on Fargate, Datadog으로 모니터링하기

임시유저·2024년 1월 30일
0
post-thumbnail

EKS, Fargate, Datadog에 대한 기본적인 설명은 없습니다

논리적인 스토리 전개를 위해 실제 히스토리를 작성한것은 아닙니다. 일부 망상과 각색이 존재합니다.

사건의 발단

Karpenter를 사용하면 Node의 Lifecycle이 굉장히 짧아질 수 있습니다. 그래서 가능하면 크리티컬한 워크로드의 경우 karpenter가 제어하는 노드가 아닌 managed node 또는 fargate에서 실행되는 편이 마음이 편안합니다. 예를 들어 coredns, metric-server 등이 여기에 속합니다.
그리고 스스로가 스스로를 죽이는 행위를 방지하기 위해 karpenter 자체도 fargate에 올라가는것이 관리하기 좋습니다.

슉샥슉샥

데이터독을 이용하여 모니터링 하기 위한 구성은 간단합니다. datadog을 sidecar로 붙여주면 끝입니다. 하지만 fargate로 올라가는 워크로드가 얼마나 늘지, 줄지 그리고 누가 어떻게 생성할지는 알 수 없습니다.

Kyverno를 활용하도록 합니다!

Kyverno를 활용하면 admission controller를 활용하여 fargate에 생성되는 pod에 sidecar를 붙여넣는 행동을 할 수 있습니다. 여타 다른 custom admission controller와 같은 방식입니다. (datadog을 쓰신다면 datadog의 apm admission controller도 이와 같은 방식입니다)

우선 해당 datadog agent가 sidecar로 붙을 pod를 지정합시다. 우선 기본 대상인 coredns를 확인해봅니다.

        labels:
          eks.amazonaws.com/component: coredns
          k8s-app: kube-dns

이제 적당히 datadog agent를 sidecar로 붙이려면 이정도 느낌의 policy면 될것 같습니다.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-datadog-sidecar
spec:
  rules:
    - name: fargate-pods
      match:
        any:
          - resources:
              kinds:
                - Pod
              selector:
                matchLabels:
                  eks.amazonaws.com/component: coredns
      mutate:
        patchStrategicMerge:
          metadata:
            annotations:
              datadog-inject: "true"
          spec:
            shareProcessNamespace: true
            containers:
              - name: datadog-agent
                image: datadog/agent
                env:
                  - name: DD_API_KEY
                    value: "XXX"
                  - name: DD_SITE
                    value: "datadoghq.com"
                  - name: DD_EKS_FARGATE
                    value: "true"
                  - name: DD_CLUSTER_NAME
                    value: "CLUSTER_NAME"
                  - name: DD_CLUSTER_AGENT_ENABLED
                    value: "true"
                  - name: DD_CLUSTER_AGENT_AUTH_TOKEN
                    value: "AUTH_TOKEN"
                  - name: DD_ORCHESTRATOR_EXPLORER_ENABLED
                    value: "true"
                  - name: DD_KUBERNETES_KUBELET_NODENAME
                    valueFrom:
                      fieldRef:
                        apiVersion: v1
                        fieldPath: spec.nodeName
                resources:
                  requests:
                    memory: "256Mi"
                    cpu: "200m"
                  limits:
                    memory: "256Mi"
                    cpu: "200m"

적당히 필요한 환경변수와 토큰, API 등을 입력해줍니다. 이제 rollout/restart 를 통해 pod를 재배치 하면 잘 해결될것 같지만...

2024-07-07 07:77:77 UTC | PROCESS | ERROR | (pkg/workloadmeta/collectors/internal/kubemetadata/kubemetadata.go:75 in Start) | Could not initialise the communication with the cluster agent: temporary failure in clusterAgentClient, will retry later: cannot get a cluster agent endpoint for kubernetes service DATADOG_CLUSTER_AGENT, env DATADOG_CLUSTER_AGENT_SERVICE_HOST is empty

중간에 뭐 이런 요상한 에러가 뜨고, datadog에서 확인해봐도 뭔가 클러스터와 pod가 연결되는것 같지 않습니다.

sidecar로 붙은 datadog agent는 일부 데이터를 cluster agent로 보내서 처리합니다. 그런데 cluster agent의 URL은 어떻게 될까요?

별다른 설정이 없다면 datadog-cluster-agent.datadog 같은 형태일겁니다. 하지만 문제는 coredns입니다. 이러한 내부 도메인의 DNS를 처리하는 역할은 coredns가 담당하고 있습니다.

그리고 스스로가 스스로의 네임서버가 될 순 없으니 coredns의 dnspolicyDefault 입니다. 이로 인해 coredns의 sidecar는 내부 도메인을 사용하여 datadog cluster agent를 사용할 수 없습니다.

그렇다면 datadog cluster agent의 clusterIP로 호출하면 됩니다!

하지만 모든 클러스터의 datadog cluster agent의 cluster ip가 동일하다는 보장은 없습니다. 심지어 datadog을 operator pattern 또는 helm으로 설치한다면 svc의 clusterIP를 지정할수도 없습니다.

apiVersion: v1
kind: Service
metadata:
  name: {{ template "datadog.fullname" . }}-cluster-agent
  namespace: {{ .Release.Namespace }}
  labels:
{{ include "datadog.labels" . | indent 4 }}
spec:
  type: ClusterIP
  selector:
    app: {{ template "datadog.fullname" . }}-cluster-agent
  ports:
  - port: 5005
    name: agentport
    protocol: TCP
datadog service의 template는 이런 형태입니다. 수정할 여지를 안줍니다. 그렇다면 매 클러스터마다 자동생성되는, 그리고 고정되지도 않는 cluster IP를 매번 자동화하여 policy를 업데이트 하도록 구성해야 할까요?

만약 이렇게 생각하셨다면 helm 같은 패키지 관리자에 너무 익숙해져 버린겁니다.

우리는 종종 편안함에 익숙해져 커스터마이징 하는것을 종종 잊곤 합니다. helm에서 만들어준 svc를 그대로 쓸 필요 없습니다! 그냥 하나 더 만들면 되죠

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/managed-by: manually
  name: self-managed-datadog-cluster-agent
  namespace: datadog
spec:
  clusterIP: 172.20.177.177
  internalTrafficPolicy: Cluster
  ipFamilyPolicy: SingleStack
  ports:
    - port: 5005
      protocol: TCP
      targetPort: 5005
  selector:
    agent.datadoghq.com/component: cluster-agent
    agent.datadoghq.com/name: datadog
  type: ClusterIP

요런 느낌으로 자체 관리형 데이터독 서비스를 하나 더 만듭시다. 이러면 이 clusterIP는 거의 왠만하면 괜찮습니다. (이것마저도 100% 완전히 괜찮게 할 수 있겠지만 여기서는 다루지 않습니다)

이제 policy를 수정하여 datadog cluster agent의 endpoint를 명시해줍시다.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-datadog-sidecar
spec:
  rules:
    - name: fargate-pods
      match:
        any:
          - resources:
              kinds:
                - Pod
              selector:
                matchLabels:
                  eks.amazonaws.com/component: coredns
      mutate:
        patchStrategicMerge:
          metadata:
            annotations:
              datadog-inject: "true"
          spec:
            shareProcessNamespace: true
            containers:
              - name: datadog-agent
                image: datadog/agent
                env:
                  - name: DD_API_KEY
                    value: "XXX"
                  - name: DD_SITE
                    value: "datadoghq.com"
                  - name: DD_EKS_FARGATE
                    value: "true"
                  - name: DD_CLUSTER_NAME
                    value: "CLUSTER_NAME"
                  - name: DD_CLUSTER_AGENT_ENABLED
                    value: "true"
                  - name: DD_CLUSTER_AGENT_AUTH_TOKEN
                    value: "AUTH_TOKEN"
                  - name: DD_ORCHESTRATOR_EXPLORER_ENABLED
                    value: "true"
#! 요놈을 추가합니다 ------------------------------------------
                  - name: DD_CLUSTER_AGENT_URL
                    value: https://172.20.177.177:5005
# ---------------------------------------------------------
                  - name: DD_KUBERNETES_KUBELET_NODENAME
                    valueFrom:
                      fieldRef:
                        apiVersion: v1
                        fieldPath: spec.nodeName
                resources:
                  requests:
                    memory: "256Mi"
                    cpu: "200m"
                  limits:
                    memory: "256Mi"
                    cpu: "200m"

이러면 적어도 클러스터를 새로 만들때마다 datadog cluster IP를 확인하고 policy를 생성하고 sidecar inject를 하는 수동 작업을 할 필요는 없을겁니다.

이제 datadog을 들어가서 확인해보면 원하는대로 클러스터와 컨테이너가 잘 결합한 모습이 보입니다.
chart

17:4x 기준으로 해당 수정을 변경한 결과

[참고]
cluster agent와의 연결이 영향을 미치는 datadog상의 기능은

입니다

사실...

사실 하고싶은 말은 fargate에 kyverno를 사용하여 sidecar injection을 구현하는것은 아닙니다. 이 부분은 다른 여러 방식으로 mutating webhook을 만들어서 구성할수도 있습니다. 오히려 kyverno로 sidecar inject를 하는것은 kyverno의 원래 역할을 벗어난다고 느끼실 수도 있습니다.

종종 잘 만들어진 패키지를 그대로 사용하는것에 익숙해져서 다른 생각을 하지 못하는 경우가 있습니다.

예를 들어 helm에 묶여있는 서비스가 아닌 별도 관리형의 고전된 clusterIP를 가지는 서비스를 만드는것이 아닌,
kyverno의 자동화에 사로잡혀 datadog svc의 admission event를 받아서 kyverno policy를 수정하고 sidecar를 수정하는 방식은 다소 경직된 사고라고 생각합니다.

이번 포스팅을 통해서 뇌가 조금이라도 말랑말랑 해지셨으면 성공한 포스팅이 된것 같네요!

누락된 부분

  1. datadog agent를 sidecar로 쓰려면 추가적인 clusterrole이 필요합니다.
- apiGroups:
  - ""
  resources:
  - nodes
  - namespaces
  - endpoints
  verbs:
  - get
  - list
- apiGroups:
    - ""
  resources:
    - nodes/metrics
    - nodes/spec
    - nodes/stats
    - nodes/proxy
    - nodes/pods
    - nodes/healthz
  verbs:
    - get         

대충 이런 느낌인데, 이것도 kyverno로 슉슉샥 할 수 있겠죠?

  1. eks addon으로 설치된 coredns에 pod label을 설정할 수 있습니다.
{"computeType":"Fargate","podLabels":{"compute-type":"Fargate"}}

이런식으로 configuration values를 수정하여 pod label 기준으로 통일된 fargate selector를 구성할 수 있겠습니다.

추가 내용 (2024년 언젠가)

현재는 datadog에서 apm agent를 admission controller로 inject하는것과 유사하게,
fargate (사실 특정 조건을 만족하는 pod)에 datadog agent sidecar를 inject 하는 기능을 지원하기 시작했습니다.

dns policy문제로 인해 coredns는 별도의 env를 오버라이드 해줘야 하긴 하지만 이 글이 작성되던 시점보다는 훨씬 간단하게 (kyverno 없이) 구성하는것이 가능합니다!

profile
멍총이 엔지니어

0개의 댓글