如何在销毁后从 Terraform 清理由 helm_release 资源留下并运行的 k8s 资源

Ber*_*one 2 kubernetes terraform kubernetes-helm terraform-cloud terraform-provider-helm

我在 Terraform 中使用 helm_release 资源时遇到问题。

我基本上部署了一个kube-prometheus-stack包含许多k8s资源并且运行顺利的。

当我尝试销毁(或删除)这部分时,问题出现了,因为卸载图表时 Helm 不会删除所有资源(这可能与某些垃圾收集规则有关,这些规则使它们在删除后保持正常运行)。这意味着我最终得到:

  • 图表已卸载
  • 资源仍在运行
  • 必须手动去那里删除所有内容,否则如果我重新创建该内容,我会得到大量重复项

我之前问过一个问题(现在我要结束了),涉及了解这是否是 Helm 的问题(实际上不是,根据设计,它会尽可能删除所有内容,我不确定图表中是否可以做一些事情,但是无论如何,我假设现在不会很快完成)现在我想问是否有人知道如何直接从 Terraform 管理它。

例如,当资源被销毁时,我可以使用某些东西kubectl delete在标记的资源(或者可能是整个命名空间)上运行命令吗?helm_release

注意:我没有添加任何代码,因为这与代码无关,但更多的是寻找一些钩子或黑客仅在销毁后运行清理。

ps:我还尝试在应用后利用 Terraform 云挂钩,但我更愿意根据 Terraform Cloud 来解决这个问题,无论如何,我没有设法创建对是否已被helm_release销毁的依赖关系。

Von*_*onC 5

如果您需要直接从 Terraform 解决此问题,您可以考虑使用在资源被销毁时触发的配置null_resource程序。 在本例中,配置程序在资源被销毁后调用本地可执行文件。local-exechelm_release
local-execkubectl

例如:

resource "helm_release" "example" {
  name       = "example"
  namespace  = "default"
  chart      = "stable/kube-prometheus-stack"
  # add your values here...
}

resource "null_resource" "cleanup" {
  triggers = {
    helm_release_id = helm_release.example.id
  }

  provisioner "local-exec" {
    when    = destroy
    command = "kubectl delete namespace ${helm_release.example.namespace}"
  }
}
Run Code Online (Sandbox Code Playgroud)

资源被销毁后,上面的脚本将kubectl delete namespace在命名空间上运行命令helm_release

请仔细测试:删除整个命名空间,而不仅仅是 Helm 图表创建的资源,这不是一个随意的操作!
如果命名空间中还有其他资源您不想删除,则需要修改命令kubectl以仅删除您想要的资源。

请注意,您需要kubectl在运行 Terraform 的计算机上进行配置,并且需要具有适当的权限才能删除 Kubernetes 集群中的资源。

此外,由于块中的依赖性,null_resource只有在创建之后才会创建它。因此,如果由于某种原因创建失败,则不会触发 及其配置器。helm_releasetriggershelm_releasenull_resource


不幸的是,我在 CI/CD 管道中使用 Terraform Cloud,因此我无法利用本地执行。但答案与我想要的很接近,因为我没有具体说明 Terraform Cloud 实际上是正确的。
你还有其他想法吗?

配置local-exec器确实不能在 Terraform Cloud 中使用,因为它不支持在运行 Terraform 的主机上运行任意命令。

Kubernetes Provider 生命周期管理

在这种情况下,另一种解决方案是使用Terraform 中的 Kubernetes 提供程序来管理遗留资源的生命周期。

例如,假设您的 Helm 图表留下了一个PersistentVolumeClaim资源。您可以使用 Terraform 中的 Kubernetes 提供程序来管理它:

provider "kubernetes" {
  # configuration for your Kubernetes cluster
}

resource "helm_release" "example" {
  name       = "example"
  namespace  = "default"
  chart      = "stable/kube-prometheus-stack"
  # add your values
}

data "kubernetes_persistent_volume_claim" "pvc" {
  metadata {
    name      = "my-pvc"
    namespace = helm_release.example.namespace
  }
}

resource "kubernetes_persistent_volume_claim" "pvc" {
  depends_on = [helm_release.example]

  metadata {
    name      = data.kubernetes_persistent_volume_claim.pvc.metadata.0.name
    namespace = data.kubernetes_persistent_volume_claim.pvc.metadata.0.namespace
  }

  spec {
    access_modes = data.kubernetes_persistent_volume_claim.pvc.spec.0.access_modes
    resources {
      requests = {
        storage = data.kubernetes_persistent_volume_claim.pvc.spec.0.resources.0.requests["storage"]
      }
    }

    volume_name = data.kubernetes_persistent_volume_claim.pvc.spec.0.volume_name
  }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,kubernetes_persistent_volume_claim当 Terraform 堆栈被销毁时,资源将删除 PVC。

您必须对留下的每种类型的资源执行此操作,因此可能有点乏味,但这是一种选择。

作业或脚本的 Kubernetes 提供者

另一种方法是使用 Kubernetes 提供程序调用 Kubernetes 作业或清理遗留资源的脚本:

provider "kubernetes" {
  # configuration for your Kubernetes cluster goes here
}

resource "helm_release" "example" {
  name       = "example"
  namespace  = "default"
  chart      = "stable/kube-prometheus-stack"
  # add your values here...
}

resource "kubernetes_job" "cleanup" {
  metadata {
    name      = "cleanup-job"
    namespace = helm_release.example.namespace
  }

  spec {
    template {
      metadata {}
      spec {
        container {
          name    = "cleanup"
          image   = "appropriate/curl" # or any image that has kubectl or equivalent tool
          command = ["sh", "-c", "kubectl delete ..."] # replace ... with the actual cleanup commands
        }
        
        restart_policy = "Never"
      }
    }

    backoff_limit = 4
  }

  depends_on = [helm_release.example]
}
Run Code Online (Sandbox Code Playgroud)

在第二个示例中,创建资源kubernetes_job时会触发该资源,并运行清理脚本。helm_release清理脚本可以删除 Helm 图表留下的任何资源。

请记住,在这两种情况下,都需要正确配置 Kubernetes 提供程序,并且 Kubernetes 集群权限必须允许您尝试执行的操作。


关于第二个例子,OP询问是否可以在资源被销毁kubernetes_job时自动触发helm_release

不幸的是,Terraform 的内置资源和提供程序不提供仅在另一个资源被破坏时执行某些操作的直接方法。块provisioner是实现此目的的一种方法,但正如我们所讨论的,它不适合 Terraform Cloud,并且不能直接与 Kubernetes 提供程序一起使用。

作为间接解决方案,您可以创建一个 Kubernetes 作业,配置为在启动后立即删除资源,然后在作业配置中使用对的引用depends_onhelm_release这样,每当创建 Helm 版本时,该作业也会启动。当您运行时terraform destroy,Helm 版本将被销毁,作业将再次启动,从而清理资源。

然而,这种方法并不完美,因为它还会在资源首次创建时运行作业,而不仅仅是在资源被销毁时运行。

为了解决这个问题,您可以编写清理脚本,使其具有幂等性,并且如果在不需要时(即创建 Helm 版本时)运行该脚本,则不会失败或导致任何负面影响。
例如,您的脚本可以在尝试删除它们之前首先检查它应该清理的资源是否确实存在:

provider "kubernetes" {
  # configuration for your Kubernetes cluster goes here
}

resource "helm_release" "example" {
  name       = "example"
  namespace  = "default"
  chart      = "stable/kube-prometheus-stack"
  # add your values here...
}

resource "kubernetes_job" "cleanup" {
  depends_on = [helm_release.example]

  metadata {
    name      = "cleanup-job"
    namespace = helm_release.example.namespace
  }

  spec {
    template {
      metadata {}
      spec {
        container {
          name    = "cleanup"
          image   = "appropriate/curl" # or any image that has kubectl or equivalent tool
          command = ["sh", "-c", 
                     "if kubectl get <resource> <name>; then kubectl delete <resource> <name>; fi"]
                     # replace <resource> and <name> with the actual resource and name
        }

        restart_policy = "Never"
      }
    }

    backoff_limit = 4
  }
}
Run Code Online (Sandbox Code Playgroud)

在此示例中,该命令在尝试删除特定 Kubernetes 资源之前检查该资源是否存在。这样,无论 Helm 版本正在创建还是销毁,作业都可以安全地运行,并且只有在资源存在时才会进行清理。

请务必将<resource>和替换<name>为您想要检查并可能删除的实际资源和资源名称。