Search

kubent에서 사라지지 않는 삭제 객체 처리하기

kubent에서 지적하는 삭제될 API 조치를 했으나 여전히 조치해야할 항목으로 남아 있는 위양성(false positive)의 일부 경우를 해결했다.

kubent

kube-no-trouble
doitintl
kubent를 쿠버네티스 클러스터를 업그레이드 하기 전에 조치하기 위해 진단 툴로써 사용한다. 이 CLI 툴은 현재 컨텍스트에서 조치해야할 즉, 상위 버전에서 삭제될 deprecated API의 GVR을 알려준다:
sudo sh -c "$(curl -sSL https://git.io/install-kubent)" # 또는 brew ❯ brew install kubent
Shell
복사
설치하고 업그레이드 할 대상 버전(v1.25.16)으로 보고서를 만들 수 있다:
❯ kubent --target-version v1.25.16 3:37PM INF >>> Kube No Trouble `kubent` <<< 3:37PM INF version 0.7.2 (git sha 25eb8a3757d1db39a04e94bb97a3f099fb5c9fb6) 3:37PM INF Initializing collectors and retrieving data 3:37PM INF Target K8s version is 1.25.16 3:37PM INF Retrieved xxx resources from collector name=Cluster 3:37PM WRN failed to discover supported resources for autoscaling/v2beta1: the server could not find the requested resource 3:37PM WRN failed to discover supported resources for autoscaling/v2beta1: the server could not find the requested resource 3:37PM INF Retrieved xxx resources from collector name="Helm v3" 3:37PM INF Loaded ruleset name=custom.rego.tmpl 3:37PM INF Loaded ruleset name=deprecated-1-16.rego 3:37PM INF Loaded ruleset name=deprecated-1-22.rego 3:37PM INF Loaded ruleset name=deprecated-1-25.rego 3:37PM INF Loaded ruleset name=deprecated-1-26.rego 3:37PM INF Loaded ruleset name=deprecated-1-27.rego 3:37PM INF Loaded ruleset name=deprecated-1-29.rego 3:37PM INF Loaded ruleset name=deprecated-future.rego __________________________________________________________________________________________ >>> Deprecated APIs removed in 1.25 <<< ------------------------------------------------------------------------------------------ KIND NAMESPACE NAME API_VERSION REPLACE_WITH (SINCE) HorizontalPodAutoscaler <undefined> a-hpa autoscaling/v2beta1 autoscaling/v2 (1.23.0) HorizontalPodAutoscaler a-namespace a-hpa autoscaling/v2beta1 autoscaling/v2 (1.23.0)
Shell
복사
이처럼 업그레이드 할 버전에선 GVK autoscaling/v2beta1 HPA가 사라진다고 알려주며 해당하는 객체 목록(a-hpa)을 보여준다:
네임스페이스가 없는 hpa? 이상하다.
문제 정의를 간단하게 하기 위해 실제 보고서의 일부를 생략했다.
문제는 a-hpa 를 autoscaling/v2에 호환되도록 수정하고 EKS 클러스터도 1.25로 업그레이드 했는데도 같은 결과가 나온다는 것이다.

객체 살펴보기

위에서 말한대로 a-hpa 객체는 이미 apiVersion=autoscaling/v2이다:
# ❯ k get hpa a-hpa -oyaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: labels: app.kubernetes.io/instance: a-hpa app.kubernetes.io/managed-by: Helm app.kubernetes.io/name: a-hpa app.kubernetes.io/version: 1.16.0 argocd.argoproj.io/instance: a-hpa helm.sh/chart: a-hpa-0.1.0 name: a-hpa namespace: a-hpa ...
YAML
복사
하지만 좀 더 살펴보니 이런 어노테이션에 autoscaling/v2beta1 apiVersion이 있는 것을 발견했다:
# ❯ k get hpa a-hpa -oyaml | yq '.metadata.annotations."kubectl.kubernetes.io/last-applied-configuration"' | jq { "apiVersion": "autoscaling/v2beta1", "kind": "HorizontalPodAutoscaler", "metadata": { "annotations": {}, "labels": { "app.kubernetes.io/instance": "a-hpa", "app.kubernetes.io/managed-by": "Helm", "app.kubernetes.io/name": "a-hpa", "app.kubernetes.io/version": "1.16.0", "argocd.argoproj.io/instance": "a-hpa", "helm.sh/chart": "a-hpa-0.1.0" }, "name": "a-hpa", "namespace": "a-hpa" }, "spec": { "maxReplicas": 5, "metrics": [ { "resource": { "name": "cpu", "targetAverageUtilization": 70 }, "type": "Resource" } ], "minReplicas": 1, "scaleTargetRef": { "apiVersion": "apps/v1", "kind": "Deployment", "name": "a-hpa" } } }
YAML
복사
어노테이션 안에 있는 매니페스트를 kubent가 감지한 것처럼 보인다.

어노테이션 kubectl.kubernetes.io/last-applied-configuration

이 문제(?)는 이미 kubent 커뮤니티에서 꽤 알려진 문제이다. 특히 kubectl의 경우 last-applied-configuration 어노테이션의 내용은 객체의 desired state를 나타낼 유일한 수단이고 이게 있다고 알리는게 즉, 보고서에 표시하는게 맞다고 이야기 한다(따라서 문제는 아니라는 입장이다).
결국 kubectl로 적용했던 매니페스트에서 drift 된 것이 원인이라고 할 수 있다.

이 잡듯 뒤지기

__________________________________________________________________________________________ >>> Deprecated APIs removed in 1.25 <<< ------------------------------------------------------------------------------------------ KIND NAMESPACE NAME API_VERSION REPLACE_WITH (SINCE) HorizontalPodAutoscaler <undefined> a-hpa autoscaling/v2beta1 autoscaling/v2 (1.23.0)
Shell
복사
그러나 여전히 네임스페이스 미상의 a-hpa 가 남아 있었다. a-hpa 의 어노테이션이 아닌 다른 어딘가의 어노테이션 또는 무언가로 존재하는거 아닐까 하는 의심이 들었다. 클러스터 전체의 매니페스트로부터 찾기 위해 다음 명령을 실행했다:
# 모든 namespaced 객체 매니페스트에서 last-applied 필터 ❯ k api-resources --verbs=list --namespaced -oname \ | xargs -I{} kubectl get {} -A -oyaml \ | grep last-applied # 모든 cluster-scoped 객체 매니페스트에서 last-applied 필터 ❯ k api-resources --verbs=list --namespaced=false -oname \ | xargs -I{} kubectl get {} -A -oyaml \ | grep last-applied # 모든 crd 매니페스트에서 last-applied 필터 ❯ k get crd -oyaml | grep last-applied # 모든 cr 매니페스트에서 laste-applied 필터 ❯ k get crd -oname | awk -F/ '{print $2}' \ | xargs -I{} kubectl get {} -oyaml \ | grep last-applied
Shell
복사
결과는 나오지 않았다. 검색 키워드에서 agent인 kubectl은 일부러 뺐다. 혹시 다른 apply하는 agent도 있을까봐. 아무튼 어떻게 찾으려 해도 결과가 나오진 않았다.

로그 보기, 코드 보기

❯ kubent --target-version v1.25.16 -l debug 9:20AM INF >>> Kube No Trouble `kubent` <<< 9:20AM INF version 0.7.2 (git sha 25eb8a3757d1db39a04e94bb97a3f099fb5c9fb6) 9:20AM INF Initializing collectors and retrieving data 9:20AM INF Target K8s version is 1.25.16 9:20AM DBG Retrieving: daemonsets.v1.apps 9:20AM DBG Retrieving: deployments.v1.apps 9:20AM DBG Retrieving: replicasets.v1.apps 9:20AM DBG Retrieving: statefulsets.v1.apps 9:20AM DBG Retrieving: networkpolicies.v1.networking.k8s.io 9:20AM DBG Retrieving: podsecuritypolicies.v1beta1.policy ... 9:20AM INF Retrieved xxx resources from collector name=Cluster 9:20AM DBG retrieved: /a-release (/v1, Kind=Service) 9:20AM DBG retrieved: /a-release (autoscaling/v2beta1, Kind=HorizontalPodAutoscaler) 9:20AM WRN failed to discover supported resources for autoscaling/v2beta1: the server could not find the requested resource # 문제의 리소스 9:20AM DBG retrieved: a-namespace/a-release (rbac.authorization.k8s.io/v1, Kind=RoleBinding) 9:20AM DBG retrieved: /a-release (apps/v1, Kind=Deployment) ... 9:20AM INF Retrieved xxx resources from collector name="Helm v3" 9:20AM INF Loaded ruleset name=custom.rego.tmpl 9:20AM INF Loaded ruleset name=deprecated-1-16.rego 9:20AM INF Loaded ruleset name=deprecated-1-22.rego 9:20AM INF Loaded ruleset name=deprecated-1-25.rego 9:20AM INF Loaded ruleset name=deprecated-1-26.rego 9:20AM INF Loaded ruleset name=deprecated-1-27.rego 9:20AM INF Loaded ruleset name=deprecated-1-29.rego 9:20AM INF Loaded ruleset name=deprecated-future.rego __________________________________________________________________________________________ >>> Deprecated APIs removed in 1.25 <<< ------------------------------------------------------------------------------------------ KIND NAMESPACE NAME API_VERSION REPLACE_WITH (SINCE) HorizontalPodAutoscaler <undefined> a-hpa autoscaling/v2beta1 autoscaling/v2 (1.23.0)
Shell
복사
kubent -l 옵션 플래그로 디버그 로그를 켰다. 문제의 warning 로그 근처를 보니 /a-release 라는 것에서 무언갈 찾고 있었다(a-release 는 설명을 위해 재구성한 이름이고 실제론 a-hpa 와 같은 이름이어서 이때까진 무엇이 문젠지 정확히 몰랐다. 아래 비슷한 DBG 로그 후에 INF 로그를 보면 helm collector의 결과라는 것을 알 수 있다).
failed to discover supported resources 라는 키워드로 kubent 코드에서 검색했다. 이 함수와 패키지 파일을 보면 helm collector가 수집한 매니페스트를 검사했다는 걸 알 수 있다(이 때 알았다!). helm?
혹시나하고 검색해보니 helm release가 있었고 그 hpa 매니페스트는 autoscaling/v2beta1로 쓰여 있었다:
❯ helm ls NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION a-release a-namespace 16 2021-07-15 11:23:13.835795 +0900 KST deployed a-release-0.1.0 1.16.0 ... ❯ helm get manifest a-release | yq 'select(.kind=="HorizontalPodAutoscaler")' # Source: a-releas/templates/hpa.yaml apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: name: a-hpa labels: helm.sh/chart: a-release-0.1.0 app.kubernetes.io/name: a-release app.kubernetes.io/instance: a-release app.kubernetes.io/version: "1.16.0" app.kubernetes.io/managed-by: Helm spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: a-deploy minReplicas: 1 maxReplicas: 10 metrics: - type: Resource resource: name: cpu targetAverageUtilization: 70
Shell
복사
아마 과거에 이 객체는 helm으로 만들어졌다가 현재 회사에서 gitops로 사용하는 argocd로 소유권이 넘어간거 같다. helm은 소유권을 잃고 release의 관련 객체는 stale 된 채로 남아 있었다.
또 객체를 보면 네임스페이스가 선언되어 있지 않다. 아마 helm upgrade --install a-release -n a-namespace ... 으로, 명령적으로 설치했을 것이다.
어쨌든 실제 객체(current state) 입장에선 위양성이기 때문에 helm release를 삭제했다. 같은 이름의 객체는 그대로 두고 오래된 매니페스트만 삭제하기 위해, helm 메타데이터인 release secret을 삭제했다:
❯ k get secrets -l "owner=helm,name=a-release" NAME TYPE DATA AGE sh.helm.release.v1.a-release.v1 helm.sh/release.v1 1 705d ❯ k delete secret sh.helm.release.v1.a-release.v1
Shell
복사
이제 더 이상 kubent 보고서에 autoscaling/v2beta1 객체가 나오지 않는다.

습득 교훈

kubent는 kubectl apply 했을 경우 리소스에 생기는 kubectl.kubernetes.io/last-applied-configuration 어노테이션의 매니페스트를 감지한다.
kubent는 helm release의 매니페스트를 감지한다.
위 경우 둘 다 보안 문제와 관련 있다고 생각된다. 클러스터 객체 상태를 변경하는 여러 agents에 대한 감시가 필요하다.
워크로드 관리를 위한 배포 방법(=~agent)은 한가지로 통일하는 것이 좋다.
gpt가 잘 풀지 못하는 문제였다. 위 같은 컨텍스트가 나에게도 없으니 프롬프트로 줄 수 없었고, 같이 헤맸다.
로그는 자세히 보고 코드는 편하게 보자.
로그: debug 옵션만 켜도 많은 정보가 나온다.
코드: 의외로 간단한 방법이지만 선뜻 손이 안갔다. 적절한 시점에 디버깅하기 좋은 방법이다.