Load-Balancing

Google Cloud External Load Balancer 的後端服務隨機失敗並出現 502 伺服器錯誤

  • June 1, 2021

我有以下外部Google云負載均衡器配置:

負載均衡器示意圖

  • GlobalNetworkEndpointGroupToClusterByIp是 Internet NEG,類型INTERNET_IP_PORT指向 Kubernetes 集群的 IP。
  • GlobalNetworkEndpointGroupToManagedS3是 Internet NEG,類型INTERNET_FQDN_PORT指向由 Yandex S3 服務管理。

由於某種原因,某些後端服務無法工作,當我嘗試連接它們時,它們會響應 HTML 頁面顯示502 Server Error

錯誤:伺服器錯誤

伺服器遇到臨時錯誤,無法完成您的請求。

請在 30 秒後重試。

在失敗的後端服務日誌中總是有以下錯誤:

jsonPayload: {
 cacheId: "GRU-c0ee45d8"
 @type: "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
 statusDetails: "failed_to_pick_backend"
}

對後端服務的請求在 1 毫秒內失敗(如日誌中所述),因此它們似乎甚至沒有嘗試連接到我的 Kubernetes 集群的 IP 或託管 S3 並立即失敗。

在發布此問題時,S3 和 Imgproxy 後端服務狀況良好,但其他服務無法正常工作:

正常執行時間狀態

如果我重新部署所有內容,其他一些服務可能會失敗,例如:

  • API 和 Docs 會起作用,其他會失敗
  • API、Docs、FPS 和 Imgproxy 將工作,S3 將失敗
  • S3 會工作,其他人會失敗

所以這絕對是隨機的,我不明白為什麼會發生。如果我足夠幸運,重新部署後所有後端服務都會執行良好。也有可能它們都不起作用。

Kubernetes 集群可以正常工作,它接受連接,託管 S3 也可以正常工作。它看起來像一個錯誤,但我在 Google 中找不到任何關於此的內容。

這是我的 Terraform 配置的外觀:

resource "google_compute_global_network_endpoint_group" "kubernetes-cluster" {
 name                  = "kubernetes-cluster-${var.ENVIRONMENT_NAME}"
 network_endpoint_type = "INTERNET_IP_PORT"

 depends_on = [
   module.kubernetes-resources
 ]
}

resource "google_compute_global_network_endpoint" "kubernetes-cluster" {
 global_network_endpoint_group = google_compute_global_network_endpoint_group.kubernetes-cluster.name
 port                          = 80
 ip_address                    = yandex_vpc_address.kubernetes.external_ipv4_address.0.address
}

resource "google_compute_global_network_endpoint_group" "s3" {
 name                  = "s3-${var.ENVIRONMENT_NAME}"
 network_endpoint_type = "INTERNET_FQDN_PORT"
}

resource "google_compute_global_network_endpoint" "s3" {
 global_network_endpoint_group = google_compute_global_network_endpoint_group.s3.name
 port                          = 443
 fqdn                          = trimprefix(local.s3.endpoint, "https://")
}

resource "google_compute_backend_service" "s3" {
 name = "s3-${var.ENVIRONMENT_NAME}"

 backend {
   group = google_compute_global_network_endpoint_group.s3.self_link
 }

 custom_request_headers = [
   "Host:${google_compute_global_network_endpoint.s3.fqdn}"
 ]

 cdn_policy {
   cache_key_policy {
     include_host         = true
     include_protocol     = false
     include_query_string = false
   }
 }

 enable_cdn            = true
 load_balancing_scheme = "EXTERNAL"

 log_config {
   enable      = true
   sample_rate = 1.0
 }

 port_name   = "https"
 protocol    = "HTTPS"
 timeout_sec = 60
}

resource "google_compute_backend_service" "imgproxy" {
 name = "imgproxy-${var.ENVIRONMENT_NAME}"

 backend {
   group = google_compute_global_network_endpoint_group.kubernetes-cluster.self_link
 }

 cdn_policy {
   cache_key_policy {
     include_host         = true
     include_protocol     = false
     include_query_string = false
   }
 }

 enable_cdn            = true
 load_balancing_scheme = "EXTERNAL"

 log_config {
   enable      = true
   sample_rate = 1.0
 }

 port_name   = "http"
 protocol    = "HTTP"
 timeout_sec = 60
}

resource "google_compute_backend_service" "api" {
 name = "api-${var.ENVIRONMENT_NAME}"

 custom_request_headers = [
   "Access-Control-Allow-Origin:${var.ALLOWED_CORS_ORIGIN}"
 ]

 backend {
   group = google_compute_global_network_endpoint_group.kubernetes-cluster.self_link
 }

 load_balancing_scheme = "EXTERNAL"

 log_config {
   enable      = true
   sample_rate = 1.0
 }

 port_name   = "http"
 protocol    = "HTTP"
 timeout_sec = 60
}

resource "google_compute_backend_service" "front" {
 name = "front-${var.ENVIRONMENT_NAME}"

 backend {
   group = google_compute_global_network_endpoint_group.kubernetes-cluster.self_link
 }

 cdn_policy {
   cache_key_policy {
     include_host         = true
     include_protocol     = false
     include_query_string = true
   }
 }

 enable_cdn            = true
 load_balancing_scheme = "EXTERNAL"

 log_config {
   enable      = true
   sample_rate = 1.0
 }

 port_name   = "http"
 protocol    = "HTTP"
 timeout_sec = 60
}

resource "google_compute_url_map" "default" {
 name            = "default-${var.ENVIRONMENT_NAME}"
 default_service = google_compute_backend_service.front.self_link

 host_rule {
   hosts = [
     local.hosts.api,
     local.hosts.fps
   ]
   path_matcher = "api"

 }

 host_rule {
   hosts = [
     local.hosts.s3
   ]
   path_matcher = "s3"
 }

 host_rule {
   hosts = [
     local.hosts.imgproxy
   ]
   path_matcher = "imgproxy"
 }

 path_matcher {
   default_service = google_compute_backend_service.api.self_link
   name            = "api"
 }

 path_matcher {
   default_service = google_compute_backend_service.s3.self_link
   name            = "s3"
 }

 path_matcher {
   default_service = google_compute_backend_service.imgproxy.self_link
   name            = "imgproxy"
 }

 test {
   host    = local.hosts.docs
   path    = "/"
   service = google_compute_backend_service.front.self_link
 }

 test {
   host    = local.hosts.api
   path    = "/"
   service = google_compute_backend_service.api.self_link
 }

 test {
   host    = local.hosts.fps
   path    = "/"
   service = google_compute_backend_service.api.self_link
 }

 test {
   host    = local.hosts.s3
   path    = "/"
   service = google_compute_backend_service.s3.self_link
 }

 test {
   host    = local.hosts.imgproxy
   path    = "/"
   service = google_compute_backend_service.imgproxy.self_link
 }
}

# See: https://github.com/hashicorp/terraform-provider-google/issues/5356
resource "random_id" "managed-certificate-name" {
 byte_length = 4
 prefix      = "default-${var.ENVIRONMENT_NAME}-"

 keepers = {
   domains = join(",", values(local.hosts))
 }
}

resource "google_compute_managed_ssl_certificate" "default" {
 name = random_id.managed-certificate-name.hex

 lifecycle {
   create_before_destroy = true
 }

 managed {
   domains = values(local.hosts)
 }
}

resource "google_compute_ssl_policy" "default" {
 name    = "default-${var.ENVIRONMENT_NAME}"
 profile = "MODERN"
}

resource "google_compute_target_https_proxy" "default" {
 name       = "default-${var.ENVIRONMENT_NAME}"
 url_map    = google_compute_url_map.default.self_link
 ssl_policy = google_compute_ssl_policy.default.self_link
 ssl_certificates = [
   google_compute_managed_ssl_certificate.default.self_link
 ]
}

resource "google_compute_global_forwarding_rule" "default" {
 name                  = "default-${var.ENVIRONMENT_NAME}"
 load_balancing_scheme = "EXTERNAL"
 port_range            = "443-443"
 target                = google_compute_target_https_proxy.default.self_link
}

**UPD。**我發現重新創建 NEG 將解決這個問題:

  1. 等到 Terraform 完成部署。
  2. 通過具有相同配置的 Google Cloud Platform Console NEG 創建。
  3. 編輯後端服務以使用新創建的 NEG。
  4. 有用!

但這絕對是 hack,似乎沒有辦法用 Terraform 自動化它。我會繼續調查這個問題。

很高興聽到您的問題已得到解決,我了解您已通過 GCP 控制台手動創建 NEG 並隨後編輯後端服務而不是使用 Terraform 來實現它。這個問題最可能的原因似乎是競速條件,即在 Terraform 中,我們通常在鏈中定義資源,因此定義的每個資源都依賴於另一個資源。通常在通過 Terraform 定義資源時,後端服務創建和 NE 附件依賴於 NEG 創建。

因此,在 Terraform 中創建後端服務時,我們必須將其定義為依賴(元參數)

$$ 1 $$NE 連接(即後端服務應僅在 NE 連接後執行)。 $$ 1 $$ https://www.terraform.io/docs/language/meta-arguments/depends_on.html 希望這能澄清你的疑問。

引用自:https://serverfault.com/questions/1058992