我无法使用数据库的私有 IP 从 GKE 集群访问运行 Postgres 的 Cloud SQL 实例。我找到的所有文档都建议使用启用 VPC 的集群来实现这一点,但我仍然无法访问数据库。
具体来说,我可以从集群中的节点访问数据库,但除非我使用主机的网络运行 docker 容器,否则我无法从节点上的容器内访问数据库。这让我相信我对 GCP VPC 和 Kubernetes 的网络组件如何相互交互存在误解。
私有云平台
我的 VPC 有一个子网,其中包含两个次要范围:
IP 范围:10.0.0.0/16
次要射程 - 吊舱:10.1.0.0/16
次要范围 - 服务:10.2.0.0/16
这是使用以下 Terraform 配置创建的:
resource "google_compute_subnetwork" "cluster" {
ip_cidr_range = "10.0.0.0/16"
name = "cluster"
network = google_compute_network.vpc.self_link
secondary_ip_range {
ip_cidr_range = "10.1.0.0/16"
range_name = "pods"
}
secondary_ip_range {
ip_cidr_range = "10.2.0.0/16"
range_name = "services"
}
}
数据库
我的云 SQL 数据库运行的是 Postgres 11,并配置为仅允许通过私有 IP 进行连接。我已与一组全局计算地址建立了对等连接,以允许从我的 VPC 访问云 SQL 实例。在这种情况下,我最终得到了以下值:
私人服务连接IP范围:172.26.0.0/16
数据库私有IP:172.26.0.3
这些资源是通过以下 Terraform 配置提供的:
resource "google_compute_global_address" "db_private_ip" {
provider = "google-beta"
name = "db-private-ip"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = google_compute_network.vpc.self_link
}
resource "google_service_networking_connection" "db_vpc_connection" {
network = google_compute_network.vpc.self_link
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.db_private_ip.name]
}
resource "google_sql_database_instance" "db" {
depends_on = [google_service_networking_connection.db_vpc_connection]
database_version = "POSTGRES_11"
settings {
availability_type = "ZONAL"
tier = "db-f1-micro"
ip_configuration {
ipv4_enabled = false
private_network = google_compute_network.vpc.self_link
}
}
}
簇
我的 GKE 集群配置为 VPC 原生集群,并使用cluster
VPC 子网中的次要范围。以下是一些相关的集群信息:
主版本:1.14.8-gke.17
网络:我的 VPC
子网:簇
VPC 原生:已启用
Pod 地址范围:10.1.0.0/16
服务地址范围:10.2.0.0/16
该集群是使用以下 Terraform 配置创建的:
resource "google_container_cluster" "primary" {
location = var.gcp_region
min_master_version = data.google_container_engine_versions.latest_patch.latest_master_version
name = "my-cluster"
network = google_compute_network.vpc.self_link
subnetwork = google_compute_subnetwork.cluster.self_link
# We can't create a cluster with no node pool defined, but we want to only use
# separately managed node pools. So we create the smallest possible default
# node pool and immediately delete it.
remove_default_node_pool = true
initial_node_count = 1
ip_allocation_policy {
use_ip_aliases = true
cluster_secondary_range_name = "pods"
services_secondary_range_name = "services"
}
master_auth {
username = ""
password = ""
client_certificate_config {
issue_client_certificate = false
}
}
}
连接尝试
我尝试从许多不同的环境连接到数据库以试图找出问题所在。
独立实例
我在我的 VPC 中启动了一个新的 Ubuntu 计算 VM,并且能够使用和连接到nping
数据库psql
。
从节点上的容器
通过kubectl attach
在我的集群中使用 pod 或通过 SSH 进入一个节点并运行我自己的 docker 命令,我发现所有到达数据库的数据包都无法成功。
# SSH-ing and running a docker container.
docker run -it ubuntu /bin/bash -c 'apt update && apt install -y nmap && nping --tcp -p 5432 172.26.0.3'
从具有主机网络的节点上的容器
如果我重复上述命令但使用主机的网络,我就可以连接到数据库。
docker run -it --net host ubuntu /bin/bash -c 'apt update && apt install -y nmap && nping --tcp -p 5432 172.26.0.3'
有什么建议吗?
鉴于大多数关于通过私有 IP 从 GKE 连接到 Cloud SQL 实例的问题在他们将集群配置为 VPC 原生时得到解决,我认为我的问题出在我的网络配置中。我很感激任何建议,我很乐意提供任何其他信息。谢谢。
相关问题
使用 GKE 中的私有 IP 连接到 Cloud SQL Postgres 时出现问题
更新 2019-12-05
将命令从相关问题通过将上面链接的配置导入 Terraform(称为 MVP 配置),我能够使用私有 IP 连接到 Postgres 实例,因此我现在认为问题在于我的配置中。我仍然无法确定我的基础设施中究竟哪一部分与 MVP 配置不同。
我的下一次尝试可能是增强 MVP 配置以使用单独配置的节点池而不是默认节点池,看看是否能解释我所看到的行为。
答案1
对于我来说,这是一个完全可用的版本:
- 使用 GCP GKE VPC 原生集群
- 与私有 GCP PGSQL 实例配合使用
resource "google_compute_subnetwork" "gke-subnet" {
name = "gke-subnet"
region = var.region
network = google_compute_network.vpc.name
ip_cidr_range = "10.10.0.0/16"
}
resource "google_container_cluster" "the_cluster" {
provider = google-beta
name = "gke"
project = var.project_id
# single-zone cluster
location = var.zone
# we need 1.17.6+ to use NEGs
# https://cloud.google.com/kubernetes-engine/docs/concepts/ingress
# min_master_version = data.google_container_engine_versions.default.latest_master_version
min_master_version = "1.17.12-gke.2502"
remove_default_node_pool = true
initial_node_count = 1
# Create a VPC-native GKE cluster instead of route-based cluster
network = google_compute_network.vpc.name
subnetwork = google_compute_subnetwork.gke-subnet.name
networking_mode = "VPC_NATIVE"
ip_allocation_policy {
cluster_ipv4_cidr_block = "/20"
services_ipv4_cidr_block = "/20"
}
master_auth {
username = var.gke_username
password = var.gke_password
client_certificate_config {
issue_client_certificate = false
}
}
}
# Separately Managed Node Pool
resource "google_container_node_pool" "the_cluster_nodes" {
name = "node-pool"
project = var.project_id
# single-zone cluster
location = var.zone
cluster = google_container_cluster.the_cluster.name
node_count = var.gke_num_nodes
node_config {
oauth_scopes = [
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring",
# needed for Container Image pulling
"https://www.googleapis.com/auth/devstorage.read_only"
]
machine_type = "g1-small"
tags = [ "${data.google_project.project.name}-gke" ]
metadata = {
disable-legacy-endpoints = "true"
}
}
}
答案2
通过私有连接进行通信时,Cloud SQL 实例必须遵守特定的网络要求。其中之一是您的 CloudSQL 和 GKE 实例位于同一区域和 VPC 网络中。[1]
关于“我无法从节点上的容器内访问数据库”,这是否意味着您的数据库和容器位于不同的网络中?如果是这样,您无法使用 Cloud VPN 隧道、基于实例的 VPN 或 Cloud 互连从另一个网络访问其私有 IP 地址上的 Cloud SQL 实例。
[1]https://cloud.google.com/sql/docs/mysql/private-ip#network_requirements。
答案3
我已经使用以下 terraform VPC 设置完成了这项工作,我可以看到与 OP 设置的主要区别是,我正在定义一个顶级网络(见下文 {“google_compute_network” “gke-sql-vpc-impl”}),而不是 OP 使用“google_compute_network.vpc.self_link”
variable "public-subnet-cidr" {
default = "10.1.0.0/24"
}
resource "google_compute_network" "gke-sql-vpc-impl" {
name = "${var.network}"
auto_create_subnetworks = "false"
}
resource "google_compute_global_address" "mysql-private-ip-address-impl" {
name = "mysql-private-ip-address"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = "${google_compute_network.gke-sql-vpc-impl.name}"
}
resource "google_service_networking_connection" "private-mysql-vpc-connection-impl" {
network = "${google_compute_network.gke-sql-vpc-impl.self_link}"
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = ["${google_compute_global_address.mysql-private-ip-address-impl.name}"]
}
resource "google_compute_subnetwork" "public-subnet-impl" {
name = "${var.network}-public-subnet"
ip_cidr_range = "${var.public-subnet-cidr}"
network = "${var.network}"
depends_on = ["google_compute_network.gke-sql-vpc-impl"]
region = "${var.region}"
secondary_ip_range {
ip_cidr_range = "10.2.0.0/16"
range_name = "pods"
}
secondary_ip_range {
ip_cidr_range = "10.3.0.0/16"
range_name = "services"
}
}
使用此 VPC,我可以使用“mysql-private-ip-address-impl”上方的私有 IP 从 pod 连接到 Cloud SQL 实例。此外,我还为 tcp 设置了防火墙规则,并将 Cloud SQL 实例数据库端口标记为集群节点。
resource "google_container_cluster" "primary" {
name = "${var.cluster_name}"
location = "${var.zone}"
remove_default_node_pool = false
initial_node_count = "${var.node_count_simple}"
network = "${google_compute_network.gke-sql-vpc-impl.name}"
subnetwork = "${google_compute_subnetwork.public-subnet-impl.name}"
ip_allocation_policy {
cluster_secondary_range_name = "pods"
services_secondary_range_name = "services"
}
node_config {
machine_type = "${var.pool_machine_type}"
preemptible = true
oauth_scopes = [
"https://www.googleapis.com/auth/compute",
"https://www.googleapis.com/auth/devstorage.read_only",
"https://www.googleapis.com/auth/logging.write",
"https://www.googleapis.com/auth/monitoring"
]
tags = ["default-nodeport-http", "default-nodeport-https", "default-firewall-mysql"]
}
master_auth {
username = ""
password = ""
client_certificate_config {
issue_client_certificate = false
}
}
}
答案4
容器在网络方面的工作方式与虚拟机(主机和客户机)相同。virtualbox 的示例展示了不同的网络类型https://www.nakivo.com/blog/virtualbox-network-setting-guide/它们在其他网络场景(例如容器化)中非常有用。我相信,GKE 可以内部网络因此您应该在节点上使用 iptables。例如,在 GCP 中,当创建为所有其他虚拟机提供 Internet 访问的 NAT 实例时,此内容用于 NAT 网关。
此外,节点应与 CloudSQL 位于同一区域,否则使用私有 IP 将不起作用。PS 如果您考虑将来在 CloudSQL 实例中强制使用 SSL,请不要这样做,除非您想永久失去私有连接。我刚刚向 GCP 支持提交了一张票,因为我认为这是一个 Bug。