如何避免 ClusterIssuer 在 Terraform 计划和应用中对 helm cert-manager CRD 的依赖? (在“cert-manager.io”组中找不到“ClusterIssuer”)

问题描述 投票:0回答:2

我正在尝试在 Terraform 中创建一个模块,以在 Kubernetes 集群中创建基本资源,这意味着

cert-manager
ingress-nginx
(作为入口控制器)和
ClusterIssuer
用于证书。按照这个确切的顺序。

前两个我使用

helm_release
资源和
cluster_issuer
通过
kubernetes_manifest
安装。

我收到以下错误,经过一些Google搜索后,我发现这是因为

cert-manager
安装了
ClusterIssuer
所需的CRD,但在
terraform plan
阶段,因为它们尚未安装,所以清单无法检测到
ClusterIssuer

然后,我想知道是否有一种方法可以绕过这个问题,但仍然在相同的配置中创建所有内容,只有一个

terraform apply

注意:我尝试使用 dependent_on 参数,并包含一个

time_sleep
块,但它没有用,因为计划中没有安装任何内容,这就是它失败的地方

| Error: Failed to determine GroupVersionResource for manifest
│ 
│   with module.k8s_base.kubernetes_manifest.cluster_issuer,
│   on ../../modules/k8s_base/main.tf line 37, in resource "kubernetes_manifest" "cluster_issuer":
│   37: resource "kubernetes_manifest" "cluster_issuer" {
│ 
│ no matches for kind "ClusterIssuer" in group "cert-manager.io"
resource "helm_release" "cert_manager" {
  chart      = "cert-manager"
  repository = "https://charts.jetstack.io"
  name       = "cert-manager"

  create_namespace = var.cert_manager_create_namespace
  namespace        = var.cert_manager_namespace

  set {
    name  = "installCRDs"
    value = "true"
  }
}

resource "helm_release" "ingress_nginx" {
  name = "ingress-nginx"

  repository = "https://kubernetes.github.io/ingress-nginx"
  chart      = "ingress-nginx"

  create_namespace = var.ingress_nginx_create_namespace
  namespace        = var.ingress_nginx_namespace

  wait = true

  depends_on = [
    helm_release.cert_manager
  ]
}

resource "time_sleep" "wait" {
  create_duration = "60s"

  depends_on = [helm_release.ingress_nginx]
}

resource "kubernetes_manifest" "cluster_issuer" {
  manifest = {
    "apiVersion" = "cert-manager.io/v1"
    "kind"       = "ClusterIssuer"
    "metadata" = {
      "name" = var.cluster_issuer_name
    }
    "spec" = {
      "acme" = {
        "email" = var.cluster_issuer_email
        "privateKeySecretRef" = {
          "name" = var.cluster_issuer_private_key_secret_name
        }
        "server" = var.cluster_issuer_server
        "solvers" = [
          {
            "http01" = {
              "ingress" = {
                "class" = "nginx"
              }
            }
          }
        ]
      }
    }
  }
  depends_on = [helm_release.cert_manager, helm_release.ingress_nginx, time_sleep.wait]
}
kubernetes terraform kubernetes-helm
2个回答
2
投票

官方文档说在使用 Helm Chart 安装之前使用

kubectl apply
,使其成为一个两步过程。 使用 Terraform,这将使其成为一个 3 步过程,因为您必须应用目标部分来创建集群,以便您可以访问 kubeconfig 凭据,然后运行 kubectl apply 命令来安装 CRD,最后再次运行 terraform apply安装 Helm Chart 和 IaC 的其余部分。这更不理想。

我会按照上面的评论使用 kubectl_manifest 资源中的

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.crds.yaml
,但这是不可能的,因为这不会链接到单个 yaml 文件,但其中很多文件将无法跟上更改。不幸的是,Helm Chart 没有“kubectl_apply”terraform 资源**来依赖于首先安装的 CRD。

尽管有这些古怪之处,还是有一个解决方案,那就是使用 helm_release 资源两次。它需要为证书颁发者创建一个模块并引用自定义 helm 图表。考虑到创建它以满足自定义需求需要花费大量的精力,它并不理想,但一旦创建,它就是一个可重用的模块化解决方案。

#
# Cert-manager
# main.tf
#
resource "helm_release" "cert_manager" {
  name             = "cert-manager"
  repository       = "https://charts.jetstack.io"
  chart            = "cert-manager"
  version          = var.cert_manager_chart_version
  namespace        = var.cert_manager_namespace
  create_namespace = true

  set {
    name  = "installCRDs"
    value = true
  }

}

自定义图表参考:

#
# cert-issuer.tf
#
# Cert Issuer using Helm
resource "helm_release" "cert_issuer" {
  name       = "cert-issuer"
  repository = path.module
  chart      = "cert-issuer"
  namespace  = var.namespace

  set {
    name  = "fullnameOverride"
    value = local.issuer_name
  }

  set {
    name  = "privateKeySecretRef"
    value = local.issuer_name
  }

  set {
    name  = "ingressClass"
    value = var.ingress_class
  }

  set {
    name  = "acmeEmail"
    value = var.cert_manager_email
  }

  set {
    name  = "acmeServer"
    value = var.acme_server
  }

  depends_on = [helm_release.cert_manager]
}

你可以看到上面使用的

helm_release
是在本地引用自己作为存储库,这需要你有一个自定义的helm图表,如下所示:

# ./cluster-issuer/cluster-issuer.yaml

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
 name: {{ include "cert-issuer.fullname" . }}
 namespace: {{ .Release.Namespace }}
spec:
 acme:
   # The ACME server URL
   server: {{ .Values.acmeServer }}
   email: {{ .Values.acmeEmail }}
   # Name of a secret used to store the ACME account private key
   privateKeySecretRef:
     name: {{ .Values.privateKeySecretRef }}
   # Enable the HTTP-01 challenge provider
   solvers:
   - http01:
       ingress:
         class: {{ .Values.ingressClass }}

出于某种原因,这避免了 terraform 用来引发错误的依赖项检查,并且可以很好地将其安装在单个

apply

通过创建纯图表,不使用values.yaml 值,可以进一步简化这一点。

** 注意,我认为另一种解决方法是在创建集群后可以使用“local-exec”或“remote-exec”等配置程序来直接运行 CRd 的 kubectl apply 命令,但我还没有测试过这还没有。它还仍然需要您的配置环境安装了 kubectl 并正确配置了 .kubeconfig,从而创建依赖关系树。

此外,这当然不是完全有效的代码。有关要使用或分叉的模块的完整示例,请参阅此 github 存储库


0
投票

基于此论坛的评论。解决方案是使用

kubectl_manifest
而不是
kubernetes_manifest

terraform {
  required_providers {
    kubectl = {
      source  = "gavinbunney/kubectl"
      version = "1.14.0"
    }
  }
}

resource "helm_release" "cert_manager" {
  name             = "cert-manager"
  namespace        = var.namespace
  chart            = "cert-manager"
  repository       = "https://charts.jetstack.io"
  version          = var.cert_manager_version
  create_namespace = true
  set {
    name  = "crds.enabled"
    value = true
  }

  set {
    name  = "extraArgs"
    value = "{--dns01-recursive-nameservers-only,--dns01-recursive-nameservers=${var.dns_server}}"
  }
}

resource "kubectl_manifest" "clusterissuer" {
  depends_on = [helm_release.cert_manager]

  yaml_body = <<YAML
  apiVersion: cert-manager.io/v1
  kind: ClusterIssuer
  metadata:
    name: ${var.cluster_issuer_name}
  spec:
    acme:
      server: ${var.acme_server}
      email: ${var.email}
      privateKeySecretRef:
        name: ${var.cluster_issuer_secret_name}
      solvers:
        - dns01:
            azureDNS:
              clientID: ${var.service_principal_client_id}
              clientSecretSecretRef:
                name: ${local.azure_dns_secret_name}
                key: client-secret
              subscriptionID: ${var.dns_zone_subscription_id}
              tenantID: ${var.dns_zone_tenant_id}
              resourceGroupName: ${var.dns_zone_resource_group}
              hostedZoneName: ${var.dns_zone_name}
              environment: AzurePublicCloud
  YAML
}
© www.soinside.com 2019 - 2024. All rights reserved.