我正在尝试在 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]
}
官方文档说在使用 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 存储库。
基于此论坛的评论。解决方案是使用
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
}