如何在Terraform中运行kubectl apply命令

30

我已经开发了一个terraform脚本,在GKE上创建k8集群。

在成功创建集群后,我有一组yaml文件需要应用到k8集群中。

我如何在我的terraform脚本中调用下面的命令?

kubectl create <.yaml>
10个回答

56
你可以使用 Terraform 的第三方提供商 kubectl。请按照这里的安装说明进行操作:Kubectl Terraform Provider。然后,只需定义一个指向你的 YAML 文件的kubectl_manifest 就行了。
# Get your cluster-info
data "google_container_cluster" "my_cluster" {
  name     = "my-cluster"
  location = "us-east1-a"
}

# Same parameters as kubernetes provider
provider "kubectl" {
  load_config_file       = false
  host                   = "https://${data.google_container_cluster.my_cluster.endpoint}"
  token                  = "${data.google_container_cluster.my_cluster.access_token}"
  cluster_ca_certificate = "${base64decode(data.google_container_cluster.my_cluster.master_auth.0.cluster_ca_certificate)}"
}

resource "kubectl_manifest" "my_service" {
    yaml_body = file("${path.module}/my_service.yaml")
}

这种方法的最大优点在于一切都是动态获取的,不依赖于任何本地配置文件(如果您在CI/CD服务器上运行Terraform或管理多集群环境,则非常重要)。

多对象清单文件

kubectl提供程序还提供了数据源,可以轻松处理具有多个对象的文件。来自文档kubectl_filename_list:

data "kubectl_filename_list" "manifests" {
    pattern = "./manifests/*.yaml"
}

resource "kubectl_manifest" "test" {
    count = length(data.kubectl_filename_list.manifests.matches)
    yaml_body = file(element(data.kubectl_filename_list.manifests.matches, count.index))
}

额外加分点: 您可以将您的yaml文件进行模板化处理。我在多资源自动缩放器 yaml 文件中插入集群名称的方法如下:

resource "kubectl_manifest" "autoscaler" {
  yaml_body = templatefile("${path.module}/autoscaler.yaml", {cluster_name = var.cluster_name })
}

2
我想在 Terraform Cloud 中使用这个,但 kubectl 提供者未发布到注册表中,因此它失败了。我该如何克服这个问题? - Shawlz
1
当前版本使用 token = data.google_client_config.default.access_token - 请参阅 https://www.terraform.io/docs/providers/google/d/client_config.html。 - rantoniuk
1
@Shawlz 只需添加提供程序定义:terraform { required_version = ">= 0.13.3" required_providers { kubectl = { source = "gavinbunney/kubectl" version = ">= 1.7.0" } } } - rantoniuk
这似乎只适用于单个对象清单。如果按“---”拆分时有多个对象事件,则无法工作。 - Christopher Markieta
2
@ChristopherMarkieta,提供程序可以正确处理多个对象,但您需要先使用kubectl_filename_list数据将它们拆分。请参考上面的示例。我还更新了链接。 - david_g
请注意,kubectl提供程序不支持List资源。 - Shinebayar G

15

有几种方法可以实现您想要做的事情。

您可以使用 Terraform 资源 template_filenull_resource
请注意,我正在使用 trigger 来运行 kubectl 命令,每次修改模板时都会运行它(您可能需要将 create 替换为 apply)。

data "template_file" "your_template" {
  template = "${file("${path.module}/templates/<.yaml>")}"
}

resource "null_resource" "your_deployment" {
  triggers = {
    manifest_sha1 = "${sha1("${data.template_file.your_template.rendered}")}"
  }

  provisioner "local-exec" {
    command = "kubectl create -f -<<EOF\n${data.template_file.your_template.rendered}\nEOF"
  }
}

但是也许最好的方法是使用Kubernetes提供程序
有两种配置方式:

  • 默认情况下,您的清单将在当前上下文中部署(kubectl config current-context
  • 第二种方式是静态定义TLS证书凭据:
provider "kubernetes" {
  host = "https://104.196.242.174"

  client_certificate     = "${file("~/.kube/client-cert.pem")}"
  client_key             = "${file("~/.kube/client-key.pem")}"
  cluster_ca_certificate = "${file("~/.kube/cluster-ca-cert.pem")}"
}

完成上述步骤后,您可以轻松创建自己的部署。对于基本Pod,只需执行以下操作:

resource "kubernetes_pod" "hello_world" {
  metadata {
    name = "hello-world"
  }

  spec {
    container {
      image = "my_account/hello-world:1.0.0"
      name  = "hello-world"
    }

    image_pull_secrets  {
      name = "docker-hub"
    }
  }
}

使用null_resource和local exec对我无效。 exit status 1. Output: << was unexpected at this time. - christopher clark
克里斯托弗,我需要看看你的代码才能告诉你问题出在哪里。 - Ricardo Jover
我的代码就是你的代码。唯一的区别是我填写了命名的yaml文件,而且该文件肯定存在并且工作正常。data "template_file" "your_template" { template = "${file("${path.module}/templates/ingress.yaml")}" } - christopher clark
我可以在terraform和本地都轻松运行kubectl apply -f filepath/templates/ingress.yml。问题出现在EOF和渲染文件业务方面。 - christopher clark
谢谢,将解释器更改为bash解决了问题,对于任何在Windows上的人,您需要。 `command =“cat << EOF \ n $ {template} \ nEOF | kubectl apply-f-” interpreter = [“bash”]` - christopher clark
显示剩余2条评论

2
创建多个 Terraform 资源时,通常最好使用命名的资源实例而不是使用列表 (count)。如果源文件被更新,Kubernetes 资源的顺序发生变化,可能会导致 Terraform 因索引更改而删除/创建资源。以下代码通过连接 kindmetadata.name 字段来创建键:
data "kubectl_file_documents" "myk8s" {
  content = file("./myk8s.yaml")
}

resource "kubectl_manifest" "myk8s" {
  # Create a map of { "kind--name" => raw_yaml }
  for_each  = {
    for value in [
      for v in data.kubectl_file_documents.myk8s.documents : [yamldecode(v), v]
    ] : "${value.0["kind"]}--${value.0["metadata"]["name"]}" => value.1
  }
  yaml_body = each.value
}

在未来,您可能希望使用Hashicorp官方的kubernetes_manifest资源(从2.5.0版本开始处于beta版,存在错误)代替。
resource "kubernetes_manifest" "default" {
  for_each = {
    for value in [
      for yaml in split(
        "\n---\n",
        "\n${replace(file("./myk8s.yaml"), "/(?m)^---[[:blank:]]*(#.*)?$/", "---")}\n"
      ) :
      yamldecode(yaml)
      if trimspace(replace(yaml, "/(?m)(^[[:blank:]]*(#.*)?$)+/", "")) != ""
    ] : "${value["kind"]}--${value["metadata"]["name"]}" => value
  }
  manifest = each.value
}

在 Kubernetes 提供程序 2.11.0 版本中,使用 Kubernetes 清单对我有效。 - FatalFlaw

1
如果远程URL托管了YAML文件,并且该文件包含多个配置/对象,则可以采取以下措施。
resource "null_resource" "controller_rancher_installation" {

  provisioner "local-exec" {
    command = <<EOT
      echo "Downloading rancher config"
      curl -L https://some-url.yaml -o rancherconfig.yaml
    EOT
  }
}

data "kubectl_path_documents" "rancher_manifests" {
    pattern = "./rancherconfig.yaml"
    depends_on = [null_resource.controller_rancher_installation]
}

resource "kubectl_manifest" "spot_cluster_controller" {
    count     = length(data.kubectl_path_documents.spot_controller_manifests.documents)
    yaml_body = element(data.kubectl_path_documents.spot_controller_manifests.documents, count.index)
}

首先需要下载它,然后再应用。这是基于观察得出的结论:

  1. pattern = "./rancherconfig.yaml" 不支持远程url,只能使用本地文件。
  2. "kubectl_manifest" 默认只应用yaml文件中的第一个配置/对象。

如果您将kubectl包含在eks模块中,则这将无法正常工作。 - Arsen

1

为2022年的观众添加一些现有答案的信息。给定的问题缺少关于这些terraform和kubectl命令需要在哪里执行的信息。

情况1:开发人员使用的是不属于Google Cloud Platform的本地系统 在这种情况下,当您使用null_resource来执行命令时,您的命令将在本地计算机上而不是Google Cloud上运行。

情况2:开发人员使用的是Google Cloud平台的一部分的系统,例如Google控制台终端或Google Cloud Code

在这种情况下,当您使用null_resource来执行命令时,您的命令将在一个临时环境中运行,该环境由Google Cloud主动管理。

从这里开始,可以使用terraform执行kubectl命令。话虽如此,下一个问题是这样做是否是一个好方法?不是。

GCP不是免费资源。如果您正在构建/使用工具,则重要的是针对正确的资源使用正确的工具。在DevOps操作中,有两个核心领域。一个是设置基础架构,其中Terraform最有效,下一个是管理基础架构,其中Ansible最有效。出于同样的原因,GCP积极提供对这些的支持。


0

你也可以将helm提供程序与itscontained图表一起使用。例如,Tekton仪表板可以像这样安装,在Yuri的Yaml拆分表达式的基础上构建:

data "http" "tekton_dashboard_install" {
  url = "https://storage.googleapis.com/tekton-releases/dashboard/previous/v0.26.0/tekton-dashboard-release.yaml"

  request_headers = {
    Accept = "application/octet-stream"
  }
}

locals {
  tekton_dashboard_manifests = [
    for yaml in split(
      "\n---\n",
      "\n${replace(data.http.tekton_dashboard_install.body, "/(?m)^---[[:blank:]]*(#.*)?$/", "---")}\n"
    ) :
    yamldecode(yaml)
    if trimspace(replace(yaml, "/(?m)(^[[:blank:]]*(#.*)?$)+/", "")) != ""
  ]
}

resource "helm_release" "tekton_dashboard" {
  name       = "tekton_dashboard"
  repository = "https://charts.itscontained.io"
  chart      = "raw"
  version    = "0.2.5"
  namespace  =  "tekton-pipelines"

  values = [
    yamlencode({ resources = local.tekton_dashboard_manifests })
  ]
}

一些 YAML 文件(例如 tekton 核心文件)带有命名空间定义,必须首先进行过滤。在解析 YAML 后这很容易实现。
此解决方案避免了使用 kubernetes_manifest 资源时出现的“应用后提供程序产生不一致结果”的问题以及随之而来的 hacky 解决方法。

0

4
如果你在示例中包含问题所包含的命令,而不是直接复制文档中的示例,那么这将是一个更好的示例。同时,使用 terraform fmt 命令使其格式化。 - ydaetskcoR

0

这里的答案都很好。一个建议是,随着您的需求从初始清单发展,您可能需要查看是否可以从清单创建helm图表(或者可能已经存在),并使用terraform helm提供程序来设置环境的值。

https://tech.paulcz.net/blog/getting-started-with-helm/

你会注意到使用Helm提供程序的优点是,它易于通过terraform环境覆盖和管理值的更改,而不是将其嵌入清单中。

https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release


0
我建议始终使用kubectl_path_documents。
如果yaml文件通过---分割后包含多个对象事件,你必须使用kubectl_path_documents,这不仅用于加载多个yaml文件,还用于处理一个yaml中通过---分割的多个对象。
data "kubectl_path_documents" "metrics_server_yaml" { // 某种方式,pattern只支持在本地文件目录中搜索,而不支持远程URL。 pattern = "${path.module}/yamls/metrics-server.yaml" }
resource "kubectl_manifest" "metrics_server_manifests" { count = length(data.kubectl_path_documents.metrics_server_yaml.documents) yaml_body = element(data.kubectl_path_documents.metrics_server_yaml.documents, count.index) }
如果你需要加载https URL的yaml文件,你可以在一个null_resource中运行"curl"命令。

-12

最好的方法是使用Terraform的Kubernetes提供程序


5
如果您提供一个简单的实例,而不仅仅是链接到 Kubernetes 提供者文档,那么这个答案会更好。 - ydaetskcoR
4
目前的 Kubernetes Provider 版本无法应用 .yaml 文件。请参考 https://github.com/terraform-providers/terraform-provider-kubernetes/issues/141。 - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接