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의 dnspolicy
는 Default
입니다. 이로 인해 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을 들어가서 확인해보면 원하는대로 클러스터와 컨테이너가 잘 결합한 모습이 보입니다.
[참고]
cluster agent와의 연결이 영향을 미치는 datadog상의 기능은
입니다
사실 하고싶은 말은 fargate에 kyverno를 사용하여 sidecar injection을 구현하는것은 아닙니다. 이 부분은 다른 여러 방식으로 mutating webhook을 만들어서 구성할수도 있습니다. 오히려 kyverno로 sidecar inject를 하는것은 kyverno의 원래 역할을 벗어난다고 느끼실 수도 있습니다.
종종 잘 만들어진 패키지를 그대로 사용하는것에 익숙해져서 다른 생각을 하지 못하는 경우가 있습니다.
예를 들어 helm에 묶여있는 서비스가 아닌 별도 관리형의 고전된 clusterIP를 가지는 서비스를 만드는것이 아닌,
kyverno의 자동화에 사로잡혀 datadog svc의 admission event를 받아서 kyverno policy를 수정하고 sidecar를 수정하는 방식은 다소 경직된 사고라고 생각합니다.
이번 포스팅을 통해서 뇌가 조금이라도 말랑말랑 해지셨으면 성공한 포스팅이 된것 같네요!
- 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로 슉슉샥 할 수 있겠죠?
{"computeType":"Fargate","podLabels":{"compute-type":"Fargate"}}
이런식으로 configuration values를 수정하여 pod label 기준으로 통일된 fargate selector를 구성할 수 있겠습니다.
현재는 datadog에서 apm agent를 admission controller로 inject하는것과 유사하게,
fargate (사실 특정 조건을 만족하는 pod)에 datadog agent sidecar를 inject 하는 기능을 지원하기 시작했습니다.
dns policy문제로 인해 coredns는 별도의 env를 오버라이드 해줘야 하긴 하지만 이 글이 작성되던 시점보다는 훨씬 간단하게 (kyverno 없이) 구성하는것이 가능합니다!