Terraform을 사용해 Talos 위에 Cilium을 포함한 Kubernetes를 배포하려고 하는데 장애가 발생합니다

Talos에서 Cilium을 커스텀 CNI로 사용하고자 할 때, Helm 차트를 Terraform으로 배포하는 도중에 kube-proxy 역할을 넘겨받고, 경우에 따라 kubeadm 인증서를 갱신하기도 하기 때문에 발생하는 것 같은데… 이 과정에서 Terraform은 Helm 배포가 완료되기를 기다리는 반면, kubectl은 일시적으로 작동하지 않더라구요. Helm 차트는 정상적으로 설치된 것처럼 보이지만, helm list 명령어로는 확인할 수 없는 일종의 ‘ghost install’ 상태가 되고, Terraform은 에러를 반환합니다.

│ Error: installation failed                                                                                        
│                                                                                                                   
│   with helm_release.cilium,                                                                                       
│   on helm.tf line 15, in resource "helm_release" "cilium":                                                        
│   15: resource "helm_release" "cilium" {                                                                          
│                                                                                                                   
│ cannot re-use a name that is still in use                                                                        

Talos 클러스터는 부트스트랩 직후 CNI가 없는 상태에서 시작되고, 이 시점에 Helm을 이용해 Cilium을 설치하려 하면 네트워크 자체가 아직 구성되지 않았기 때문에 문제가 발생하네요.

추가적으로, Talos는 기본적으로 kube-proxy를 사용하지 않으며, Cilium이 그 역할을 대체하려다 보니 설치 시점에서 클러스터 내부 통신이 잠시 중단되고 kubectl 명령도 실패하게 됩니다. 그 결과, Helm은 release 정보를 제대로 생성하지 못하고, Cilium은 설치된 것처럼 보이지만 helm list에는 나타나지 않는 상태가 되는 것입니다.

resource "null_resource" "wait" {
  depends_on = [local_file.kubeconfig, talos_machine_bootstrap.control_node]
  triggers = {
    kubeconfig_content = local_file.kubeconfig.content
  }

  provisioner "local-exec" {
    environment = {
      KUBECONFIG = "${var.config_output_path}/kubeconfig"
    }
    command = "${path.module}/scripts/wait-for-cluster-ready.bash"
  }
}

resource "helm_release" "cilium" {
  depends_on = [null_resource.wait]
  name = "cilium"
  repository = "https://helm.cilium.io/"
  chart = "cilium"
  version = "1.17.5"
  namespace = "kube-system"
  create_namespace = false

  # Avoid a race condition issue where helm provider errors with;
  # "cannot re-use a name that is still in use"
  atomic = true
  timeout = 500

  # Contact the kubeapi by using a Talos VIP to avoid  the chicken&egg problem
  # of trying to replace kube-proxy with no CNI installed.
  set = [
    {
      name = "kubeProxyReplacement"
      value = true
    },
    {
      name = "k8sServiceHost"
      value = var.cluster_endpoint_domain
    },
    {
      name = "k8sServicePort"
      value = 6443
    },
    {
      name = "securityContext.capabilities.ciliumAgent"
      value = "{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}"
    },
    {
      name = "securityContext.capabilities.cleanCiliumState"
      value = "{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}"
    }
  ]
}

resource "kubernetes_namespace" "ceph_csi_cephfs" {
  # This is only for the reverse dependency graph created on destroy.
  # To avoid the API going down before this namespace is deleted.
  depends_on = [talos_machine_bootstrap.control_node]
  metadata {
    labels = {
      "pod-security.kubernetes.io/enforce" = "privileged"
    }

    name = "ceph-csi-cephfs"
  }
}

resource "helm_release" "cephfs_csi" {
  depends_on = [
    null_resource.wait,
    kubernetes_namespace.ceph_csi_cephfs,
    helm_release.cilium
  ]
  name = "ceph-csi-cephfs"
  repository = "https://ceph.github.io/csi-charts"
  chart = "ceph-csi-cephfs"
  version = "3.14.0"
  namespace = "ceph-csi-cephfs"
  create_namespace = false

  # Avoid a race condition issue where helm provider errors with;
  # "cannot re-use a name that is still in use"
  atomic = true
  timeout = 500

  # Set value overrides.
  set = [
    {
      name = "secret.create"
      value = true
    },
    {
      name = "secret.userID"
      value = var.cephfs_csi.userid
    },
    {
      name = "secret.userKey"
      value = var.cephfs_csi.userkey
    },
    {
      name = "storageClass.clusterID"
      value = var.cephfs_csi.clusterid
    },
    {
      name = "storageClass.create"
      value = true
    },
    {
      name = "storageClass.fsName"
      value = var.cephfs_csi.fsname
    },
    {
      name = "storageClass.mounter"
      value = "kernel"
    },
    {
      name = "nodeplugin.podSecurityContext.runAsUser"
      value = 0
    },
    {
      name = "nodeplugin.podSecurityContext.runAsGroup"
      value = 0
    },
    {
      name = "provisioner.podSecurityContext.runAsUser"
      value = 0
    },
    {
      name = "provisioner.podSecurityContext.runAsGroup"
      value = 0
    },
    {
      name = "csiConfig[0].clusterID"
      value = var.cephfs_csi.clusterid
    },
    {
      name = "csiConfig[0].cephFS.subvolumeGroup"
      value = var.cephfs_csi.subvolumegroup
    },

    # These should force the Pods to restart when userkey value changes.
    # Mostly for troubleshooting, switching between ceph users.
    {
      name = "provisioner.podAnnotations.checksum\\/secret"
      value = sha256(var.cephfs_csi.userkey)
    },
    {
      name = "nodeplugin.podAnnotations.checksum\\/secret"
      value = sha256(var.cephfs_csi.userkey)
    }
  ]
  set_list = [
    {
      name = "csiConfig[0].monitors"
      value = var.cephfs_csi.monitors
    },
    {
      name = "storageClass.mountOptions"
      value = [
        "ms_mode=secure"
      ]
    }
  ]
}

이 경우 var.cluster_endpoint_domain은 Talos 클러스터의 VIP(가상 IP)로, 정상적인 컨트롤 노드 중 하나로 라우팅되도록 설정되어 있고, Terraform의 Helm provider에서 발생하는 제한 사항에 부딪힌 것 같다는 의심이 들기는 하네요.

전문가분들의 조언 부탁드립니다.

1 Like

[CloudBro AI Bot Response]
네, Talos에서 Cilium을 커스텀 CNI로 사용하면서 Terraform의 Helm provider로 배포할 때 겪는 문제는 꽤 흔한 이슈입니다. 특히 Talos는 부트스트랩 직후 네트워크가 아직 준비되지 않은 상태에서 Helm을 통해 Cilium을 설치하려고 하면, 네트워크가 구성되기 전까지 kube-apiserver와의 통신이 불안정해지고, 이로 인해 Helm release 정보가 정상적으로 기록되지 않아 ‘ghost install’ 현상이 발생할 수 있습니다. 이 과정에서 Terraform의 Helm provider는 Helm release 상태를 제대로 추적하지 못해 “cannot re-use a name that is still in use”와 같은 에러를 내뱉게 됩니다.

핵심 원인 정리

  • Talos 부트스트랩 직후 CNI가 없어서 kube-apiserver와의 네트워크가 불안정함
  • Helm provider는 kube-apiserver와의 통신이 정상이어야만 release 정보를 관리할 수 있음
  • Cilium이 kube-proxy 역할까지 대체할 때, 클러스터 네트워크가 잠시 중단될 수 있음
  • Helm release 정보가 etcd에 정상적으로 기록되지 않아 ghost install 상태 발생

해결 방법 및 우회 방법

  1. Helm 대신 Manifest 직접 적용
  2. CNI(특히 Cilium) 배포만큼은 Helm provider 대신 kubectl apply로 manifest를 직접 적용하는 방식을 고려해보세요. 예를 들어, null_resource에서 local-exec로 Cilium manifest를 적용한 뒤, 네트워크가 정상화된 후 Helm provider를 사용하는 방식입니다.
resource "null_resource" "cilium_install" {
  depends_on = [null_resource.wait]
  provisioner "local-exec" {
    environment = {
      KUBECONFIG = "${var.config_output_path}/kubeconfig"
    }
    command = "kubectl apply -f https://raw.githubusercontent.com/cilium/cilium/v1.17.5/install/kubernetes/quick-install.yaml"
  }
}
  1. Helm Release Resource의 Lifecycle 관리
  2. 이미 ghost install 상태가 발생했다면, Helm release 정보를 수동으로 삭제하거나, terraform state rm 명령어로 state에서 해당 리소스를 제거한 뒤 재시도해야 할 수 있습니다.
terraform state rm helm_release.cilium
  1. 네트워크 안정성 확인 후 Helm 배포
  2. 네트워크가 완전히 안정화될 때까지 기다렸다가 Helm provider를 실행하도록 wait-for-cluster-ready 스크립트의 조건을 좀 더 엄격하게 하세요. 예를 들어, kubectl get nodes가 정상적으로 반환될 때까지 반복적으로 체크하는 로직을 추가하면 좋습니다.
  3. Helm provider의 atomic, timeout 옵션 활용
  4. 이미 사용 중이지만, 네트워크가 불안정할 경우에는 atomic 옵션이 오히려 롤백을 유발할 수 있으니, 상황에 따라 atomic = false로 변경 후, 수동으로 상태를 확인하는 것도 방법입니다.

참고 링크


결론

Talos에서 Cilium을 Helm provider로 바로 설치하는 것은 네트워크 부트스트랩 특성상 여러 문제가 발생할 수 있습니다. 가장 안전한 방법은 Cilium 설치만큼은 manifest 직접 적용(혹은 Talos의 MachineConfig에 CNI manifest를 직접 넣는 방식)으로 네트워크를 먼저 안정화시키고, 그 이후 Helm provider를 사용하는 것입니다. 이미 ghost install이 발생했다면 Helm release 정보와 Terraform state를 정리한 뒤 재시도해야 합니다.

추가적으로, Talos와 Cilium의 버전 호환성, 그리고 cluster endpoint(VIP) 설정이 올바른지 다시 한 번 검토해보시는 것도 좋겠습니다. 필요하다면 Talos Slack이나 CNCF Cilium 채널에서 실제 사례를 공유해도 많은 도움을 받을 수 있습니다.

혹시 추가적으로 궁금한 점 있으시면 언제든 질문 남겨주세요!