# 跨项目 PSC NEG 实现方案

## 问题描述

### 总体目标

希望实现一个跨 Project 的 API 访问架构：

```
A Project (入口层)
  ↓
Global Load Balancer
  ↓
PSC NEG (Consumer)
  ↓
B Project Service Attachment (Producer)
  ↓
Internal Load Balancer
  ↓
Backend Service (GKE / VM / MIG)
```

**核心目标**：让 A Project 的 GLB 通过 PSC 访问 B Project 的私有服务。

---

### 当前网络环境

网络结构包含两个 Project 和两种网络类型。

**A Project**
- 部署 Global Load Balancer
- 使用 Shared VPC
- 作为 PSC Consumer

```
A Project Shared VPC
└── GLB
    └── PSC NEG
```

**B Project**
- 有自己的 Private VPC
- 部署业务服务
- 作为 PSC Producer

```
B Project Private VPC
└── Service Attachment
    └── Internal Load Balancer
        └── Backend (GKE / VM)
```

---

### 想解决的问题

希望替换当前的跨 Project 访问方式。

**当前方案**（已实现）：

```
GLB
  ↓
NON_GCP_PRIVATE_IP_PORT NEG
  ↓
ILB IP
  ↓
Backend
```

特点：
- 直接访问 IP + Port
- Producer IP 暴露
- Producer 无法控制 consumer

**目标方案**：

```
GLB
  ↓
PSC NEG
  ↓
Service Attachment
  ↓
ILB
  ↓
Backend
```

特点：
- 访问 Service
- 不暴露 backend IP
- Producer 可以控制访问权限

---

### 核心问题

在实现这个架构时，主要在确认 PSC 的依赖关系和创建条件。

1. **PSC NEG 创建条件**
   - 是否需要 network
   - 是否需要 subnet
   - 是否必须在 Shared VPC

2. **Producer Service Attachment 条件**
   - 是否必须在 Shared VPC
   - 是否可以在 Private VPC
   - 是否必须有 PSC NAT subnet

3. **跨 Project 访问机制**

```
GLB (Shared VPC)
  ↓
PSC NEG
  ↓
B Project Private VPC Service Attachment
```

是否官方支持并可行。

4. **PSC 与当前方案对比**

NON_GCP_PRIVATE_IP_PORT NEG 和 PSC NEG 之间的区别：
- 网络依赖
- 安全性
- 架构设计

---

### 最终目标架构

```
GLB (A Project Shared VPC)
  ↓
Cloud Armor
  ↓
Backend Service
  ↓
PSC NEG
  ↓
Service Attachment (B Project)
  ↓
Internal Load Balancer
  ↓
Backend Service
```

| 能力                   | 目标 |
| ---------------------- | ---- |
| 跨 Project 访问        | ✔    |
| 不暴露 backend IP      | ✔    |
| Producer 控制 consumer | ✔    |
| 服务级别隔离           | ✔    |

---

### 需求总结

在 A Project 的 Shared VPC 中部署 GLB，并通过 PSC NEG 作为 Consumer，访问 B Project Private VPC 中通过 Service Attachment 发布的内部服务。

---

### PSC 核心机制

1. **Consumer 网络归属机制**：PSC NEG 需要绑定 Consumer 侧可用的 subnet，用于分配 PSC 接入 IP
2. **Producer 网络要求**：Service Attachment 必须绑定 ILB + PSC NAT subnet
3. **跨 VPC 通信机制**：Consumer VPC 与 Producer VPC 无需 Peering，PSC 会建立服务级私有连接

---

## 问题分析

你的方案本质是：**用 PSC（Private Service Connect）替换裸 IP NEG，实现跨 Project 的服务级访问隔离**。核心涉及三个层面：PSC NEG 创建条件、Producer Service Attachment 要求、以及跨 VPC 通信机制。

---

## 推荐画法

下面这 3 张图适合直接放在方案开头，分别回答 3 个不同问题：

1. **整体长什么样**：入口、控制点、转发链路在哪里
2. **先建什么再建什么**：Producer / Consumer 的资源依赖关系
3. **一次请求到底怎么走**：数据面流量和控制面审批是怎么分开的

---

## 图 1：整体架构总览

这张图适合用在评审页第一页，重点表达“谁是入口、谁暴露服务、谁控制访问”。

```mermaid
flowchart LR
    Client["External Client"] --> DNS["DNS / Public Domain"]
    DNS --> GLB["Global HTTPS Load Balancer"]
    GLB --> Armor["Cloud Armor / TLS Policy"]
    Armor --> BS["Backend Service"]
    BS --> PSCNEG["PSC NEG<br/>Consumer Endpoint Group"]

    subgraph Consumer["A Project (Service Project / Shared VPC)"]
        GLB
        Armor
        BS
        PSCNEG
        SubnetA["Consumer Subnet<br/>普通子网，仅用于分配 PSC 接入 IP"]
    end

    subgraph GoogleBackbone["Google Internal Backbone"]
        Tunnel["PSC Service-Level Private Connectivity"]
    end

    subgraph Producer["B Project (Producer / Private VPC)"]
        SA["Service Attachment<br/>访问控制入口"]
        PSCNAT["PSC NAT Subnet<br/>purpose=PRIVATE_SERVICE_CONNECT"]
        ILB["Internal Load Balancer"]
        App["Backend Service<br/>GKE / VM / MIG"]
    end

    PSCNEG --> Tunnel --> SA
    SubnetA -.-> PSCNEG
    PSCNAT -.-> SA
    SA --> ILB --> App
```

### 这张图要传达的重点

- **入口在 A Project**：公网只打到 GLB，不直接看到 B Project 的服务
- **服务发布在 B Project**：Producer 通过 Service Attachment 暴露服务，不暴露真实 Backend IP
- **连接是服务级的，不是网络级的**：A 和 B 不需要 VPC Peering，也不需要暴露整段网段
- **权限控制在 Producer 手里**：是否允许 Consumer 接入，由 Service Attachment 决定

---

## 图 2：资源创建顺序图

这张图适合回答“为什么要先建 B，再建 A”，也适合用在实施步骤前面。

```mermaid
sequenceDiagram
    participant ProdNet as B Project VPC
    participant ProdSvc as Service Attachment
    participant ConsNet as A Project Shared VPC
    participant ConsNEG as PSC NEG
    participant GLB as Global HTTPS LB

    Note over ProdNet,ProdSvc: Phase 1 - Producer 先准备可发布服务
    ProdNet->>ProdNet: 创建 Internal Load Balancer
    ProdNet->>ProdNet: 创建 PSC NAT Subnet
    ProdNet->>ProdSvc: 创建 Service Attachment\n绑定 ILB + NAT Subnet
    ProdSvc-->>ProdSvc: 配置 accept list / manual approval

    Note over ConsNet,ConsNEG: Phase 2 - Consumer 创建接入端点
    ConsNet->>ConsNEG: 在 Shared VPC 子网中创建 PSC NEG\n指向 Service Attachment
    ConsNEG-->>ProdSvc: 发起 PSC 连接请求
    ProdSvc-->>ConsNEG: 自动接受或等待手动审批

    Note over GLB,ConsNEG: Phase 3 - 暴露公网入口
    GLB->>ConsNEG: Backend Service 绑定 PSC NEG
    GLB->>GLB: 绑定 URL Map / HTTPS Proxy / Forwarding Rule
    GLB-->>GLB: 可选绑定 Cloud Armor / TLS Policy
```

### 这张图要传达的重点

- **Producer 是前置依赖**：没有 Service Attachment，Consumer 侧没法创建完整接入链路
- **PSC NEG 不是独立服务**：它本质上是 GLB 后端引用的 Consumer 接入对象
- **审批动作发生在 Producer 侧**：`ACCEPT_MANUAL` 时，B Project 才是最终放行方

---

## 图 3：一次请求的完整生命周期

这张图比“架构图”更具体，适合解释流量到底走了什么路径。

```mermaid
flowchart TD
    R1["1. Client 发起 HTTPS 请求"] --> R2["2. Global HTTPS LB 终止 TLS"]
    R2 --> R3["3. Cloud Armor 执行访问控制"]
    R3 --> R4["4. Backend Service 选择 PSC NEG"]
    R4 --> R5["5. PSC NEG 命中 Consumer VPC 内的接入 IP"]
    R5 --> R6["6. Google Backbone 通过 PSC 通道转发到 B Project"]
    R6 --> R7["7. Service Attachment 校验 Consumer 是否被允许"]
    R7 --> R8["8. Producer 侧使用 PSC NAT Subnet 做 SNAT"]
    R8 --> R9["9. Internal Load Balancer 转发到 GKE / VM / MIG"]
    R9 --> R10["10. Backend 返回响应，沿原路径回到 Client"]
```

### 这张图要传达的重点

- **TLS / WAF / 公网治理** 都在 A Project 入口层完成
- **PSC 负责私有跨项目服务接入**，不是传统路由互通
- **B Project 只接收来自 Service Attachment 放行后的流量**
- **Producer 看到的是 PSC NAT 后的来源，不是 Consumer 原始工作负载 IP**

---

## 角色与职责表

这个表适合放在图后面，帮助第一次接触 PSC 的人快速建立组件心智模型。

| 组件 | 所在位置 | 作用 | 是否必须 |
| --- | --- | --- | --- |
| Global HTTPS Load Balancer | A Project | 公网入口、TLS 终止、域名承载 | 是 |
| Cloud Armor | A Project | WAF、IP/地理/速率防护 | 推荐 |
| Backend Service | A Project | 承接 GLB 后端配置，挂接 PSC NEG | 是 |
| PSC NEG | A Project | 把 Service Attachment 暴露为 GLB 可引用的后端对象 | 是 |
| Consumer Subnet | A Project Shared VPC | 给 PSC NEG 分配接入 IP | 是 |
| Service Attachment | B Project | Producer 对外发布内部服务，并控制 Consumer 接入 | 是 |
| PSC NAT Subnet | B Project Private VPC | 给 Producer 侧 PSC 转换分配地址 | 是 |
| Internal Load Balancer | B Project | 把进入 Producer 的请求转给实际后端 | 是 |
| GKE / VM / MIG Backend | B Project | 真正承载业务流量 | 是 |

---

## 架构全景图

```mermaid
graph TD
    subgraph A_Project [A Project - Shared VPC]
        GLB["GLB\nGlobal HTTPS LB"]
        CA["Cloud Armor"]
        BS_A["Backend Service"]
        PSC_NEG["PSC NEG\nNetwork Endpoint Group\ntype: PRIVATE_SERVICE_CONNECT"]
    end

    subgraph B_Project [B Project - Private VPC]
        SA["Service Attachment\n绑定 PSC NAT subnet"]
        ILB["Internal Load Balancer\nInternal L4/L7"]
        GKE["Backend\nGKE / VM / MIG"]
        PSC_SUBNET["PSC NAT Subnet\npurpose: PRIVATE_SERVICE_CONNECT"]
    end

    Client["外部 Client"] --> GLB
    GLB --> CA
    CA --> BS_A
    BS_A --> PSC_NEG
    PSC_NEG -->|"PSC 自动建立跨 VPC 通道"| SA
    SA --> ILB
    ILB --> GKE
    PSC_SUBNET -.->|"NAT 地址池\n隔离两侧 IP"| SA
```

---

## 实现步骤

### Step 1：B Project - 准备 PSC Producer

#### 1.1 创建 PSC NAT Subnet（必须）

```bash
gcloud compute networks subnets create psc-nat-subnet \
  --project=b-project \
  --network=private-vpc \
  --region=asia-east1 \
  --range=10.100.0.0/28 \
  --purpose=PRIVATE_SERVICE_CONNECT
```

> 注意：`purpose=PRIVATE_SERVICE_CONNECT` 是 Producer 侧的硬性要求，该 subnet 专用于 PSC NAT 地址转换，不能复用普通 subnet。

#### 1.2 确认 ILB 已存在

```bash
# 确认 B Project 的 ILB forwarding rule 名称
gcloud compute forwarding-rules list \
  --project=b-project \
  --filter="loadBalancingScheme=INTERNAL"
```

#### 1.3 创建 Service Attachment

```bash
gcloud compute service-attachments create my-service-attachment \
  --project=b-project \
  --region=asia-east1 \
  --producer-forwarding-rule=my-ilb-forwarding-rule \
  --connection-preference=ACCEPT_MANUAL \
  --consumer-accept-list=a-project-id=100 \
  --nat-subnets=psc-nat-subnet
```

| 参数                    | 说明                                                             |
| ----------------------- | ---------------------------------------------------------------- |
| `connection-preference` | `ACCEPT_MANUAL`：Producer 手动审批；`ACCEPT_AUTOMATIC`：自动接受 |
| `consumer-accept-list`  | 指定允许的 Consumer Project ID，实现访问控制                     |
| `nat-subnets`           | 绑定上一步创建的 PSC NAT subnet                                  |

---

### Step 2：A Project - 创建 PSC NEG（Consumer）

#### 2.1 创建 PSC NEG

```bash
gcloud compute network-endpoint-groups create psc-neg \
  --project=a-project \
  --region=asia-east1 \
  --network-endpoint-type=PRIVATE_SERVICE_CONNECT \
  --psc-target-service=projects/b-project/regions/asia-east1/serviceAttachments/my-service-attachment \
  --network=shared-vpc-network \
  --subnetwork=shared-vpc-subnet
```

> 注意：PSC NEG 需要指定 `network` 和 `subnetwork`，用于分配 Consumer 侧的 PSC 接入 IP（该 IP 仅在 Consumer VPC 内可路由）。

## 🔍 问题分析

你发现了一个很有意思的现象：**删掉 `--network` 后反而创建成功了**。这背后的原因确实和 Shared VPC 的 IAM 授权粒度有关。

---

## ✅ 核心原因

Shared VPC 的 IAM 授权有两种粒度：

| 授权粒度             | 命令                | 效果                                      |
| -------------------- | ------------------- | ----------------------------------------- |
| **整个网络级别**     | 绑定到 Host Project | Service Project 可用所有 subnet           |
| **单独 Subnet 级别** | 绑定到具体 subnet   | Service Project **只能用被授权的 subnet** |

你的环境是 **subnet 级别授权**，所以：

```
--network=shared-vpc-network   ← GCP 尝试验证整个 network 的访问权限 → 权限不足 → 失败
--subnetwork=shared-vpc-subnet ← GCP 只验证该 subnet 的访问权限     → 有权限   → 成功
```

---

## 📊 权限验证路径差异

```mermaid
graph TD
    subgraph 失败路径 [指定 --network 的验证路径]
        CMD_A["gcloud ... --network=shared-vpc-network"] 
        CHECK_A["GCP 检查:\nprojects/host-project/global/networks/shared-vpc-network\n的 compute.networkUser 权限"]
        RESULT_A["❌ 失败\nIAM 只绑定在 subnet 级别\n没有 network 级别权限"]
        CMD_A --> CHECK_A --> RESULT_A
    end

    subgraph 成功路径 [只指定 --subnetwork 的验证路径]
        CMD_B["gcloud ... --subnetwork=shared-vpc-subnet"]
        CHECK_B["GCP 检查:\nprojects/host-project/regions/xxx/subnetworks/shared-vpc-subnet\n的 compute.networkUser 权限"]
        RESULT_B["✅ 成功\n该 subnet 上有授权"]
        CMD_B --> CHECK_B --> RESULT_B
    end
```

---

## 🔑 Shared VPC IAM 授权粒度原理

```bash
# 网络级别授权（你的环境没有这个）
gcloud projects add-iam-policy-binding host-project \
  --member=serviceAccount:a-project@... \
  --role=roles/compute.networkUser
# 效果：A Project 可以使用 Host Project 所有 subnet

# Subnet 级别授权（你的环境是这个）
gcloud compute networks subnets add-iam-policy-binding shared-vpc-subnet \
  --project=host-project \
  --region=asia-east1 \
  --member=serviceAccount:a-project@... \
  --role=roles/compute.networkUser
# 效果：A Project 只能使用这一个 subnet
```

当你指定 `--network` 时，GCP 在 API 层面会做 **network 级别的权限校验**，你没有这个级别的授权，所以失败。只指定 `--subnetwork` 时，GCP **只校验该 subnet 的权限**，授权存在，所以成功。

---

## 一句话总结

> 你的 Shared VPC IAM 是 **subnet 级别精细授权**，不是 network 级别授权。指定 `--network` 触发了 network 级别的权限检查，而你只有 subnet 级别的权限，所以只写 `--subnetwork` 才能绕过 network 级别检查直接通过。这是 Shared VPC **最小权限原则**的典型配置。

#### 2.2 将 PSC NEG 加入 Backend Service

```bash
# 创建 Backend Service（如不存在）
gcloud compute backend-services create psc-backend-service \
  --project=a-project \
  --global \
  --protocol=HTTPS

# 添加 PSC NEG 作为 Backend
gcloud compute backend-services add-backend psc-backend-service \
  --project=a-project \
  --global \
  --network-endpoint-group=psc-neg \
  --network-endpoint-group-region=asia-east1
```

#### 2.3 绑定 Cloud Armor（可选但推荐）

```bash
gcloud compute backend-services update psc-backend-service \
  --project=a-project \
  --global \
  --security-policy=my-cloud-armor-policy
```

---

### Step 3：GLB 串联

```bash
# URL Map 指向 Backend Service
gcloud compute url-maps create psc-url-map \
  --project=a-project \
  --default-service=psc-backend-service

# HTTPS Proxy
gcloud compute target-https-proxies create psc-https-proxy \
  --project=a-project \
  --url-map=psc-url-map \
  --ssl-certificates=my-ssl-cert

# Forwarding Rule（GLB 入口）
gcloud compute forwarding-rules create psc-glb-rule \
  --project=a-project \
  --global \
  --target-https-proxy=psc-https-proxy \
  --ports=443
```

---

## PSC NEG vs NON_GCP_PRIVATE_IP_PORT NEG 对比

| 维度                       | `NON_GCP_PRIVATE_IP_PORT` NEG | PSC NEG                 |
| -------------------------- | ----------------------------- | ----------------------- |
| 访问目标                   | 裸 IP + Port                  | Service Attachment URI  |
| Backend IP 是否暴露        | 是                            | 否                      |
| Producer 访问控制          | 无                            | 支持 allowlist/手动审批 |
| 网络依赖                   | 需要 VPC Peering 或共享路由   | PSC 自动建立隔离通道    |
| 跨 Project 支持            | 需要额外网络打通              | 原生支持                |
| 服务级别隔离               | 否                            | 是                      |
| Consumer 侧需要 subnet     | 否                            | 是，分配接入 IP 用      |
| Producer 侧需要 PSC subnet | -                             | 必须                    |

---

## 三大核心机制澄清

### 1. PSC NEG 的网络归属

PSC NEG 创建时**需要指定 network + subnetwork**，原因是 GCP 需要在 Consumer VPC 内分配一个私有 IP 作为 PSC 接入点（Forwarding Rule IP）。这个 IP 仅用于 Consumer 内部路由，**不是** Backend 的真实 IP。

### 2. Producer 必须有 PSC NAT Subnet

Service Attachment 强制绑定 `purpose=PRIVATE_SERVICE_CONNECT` 的 subnet，这个 subnet 的 IP 用于做 **SNAT**，使 Consumer 的流量进入 Producer VPC 时源 IP 被替换，实现双向隔离。

### 3. 跨 VPC 通信机制

```
Consumer VPC (A Project Shared VPC)
    └── PSC NEG IP (e.g. 10.0.1.5)  ← Consumer 侧可见
            ↓ [GCP PSC 内部隧道，无需 VPC Peering]
Producer VPC (B Project Private VPC)
    └── PSC NAT IP (e.g. 10.100.0.3) ← SNAT 后进入 Producer
            ↓
    ILB → Backend
```

两个 VPC **完全不需要 Peering**，PSC 通过 Google 内部网络建立单向连接通道，IP 空间可以重叠。

---

## 注意事项

1. **区域一致性**：PSC NEG、Service Attachment、ILB 必须在**同一 Region**
2. **GLB 是全球资源，但 PSC NEG 是区域资源**，Backend Service 绑定时注意 `--region` vs `--global` 区别
3. **`ACCEPT_MANUAL` 模式下**，Producer 需要在 Service Attachment 上手动 approve Consumer 的连接请求：

    ```bash
    gcloud compute service-attachments describe my-service-attachment \  --project=b-project --region=asia-east1# 查看 connectedEndpoints 状态，手动 approve
    ```

4. **健康检查**：GLB 的 Health Check 流量也会经过 PSC 通道，需确保 B Project ILB 的健康检查端口对外可达
5. **Shared VPC 权限**：A Project 若使用 Shared VPC，PSC NEG 的 subnet 需要在 Host Project 的网络中，确认 Service Project 有 `compute.networkUser` 角色

---

## 为什么需要 Subnet

## 问题分析

你有两个疑问：

1. PSC NEG 为什么必须指定 Shared VPC 的 network/subnet？
2. Consumer 侧的 subnet 是否也需要 `purpose=PRIVATE_SERVICE_CONNECT`？

---

## PSC 连接建立机制

```mermaid
graph TD
    subgraph A_Project [A Project - Shared VPC]
        SUBNET["普通 Subnet\ne.g. 10.0.1.0/24\npurpose=PRIVATE 默认即可"]
        PSC_IP["PSC 接入 IP\ne.g. 10.0.1.88\n从该 subnet 分配"]
        PSC_NEG["PSC NEG\n持有这个接入 IP"]
        BS["Backend Service"]
        GLB["GLB"]
    end

    subgraph B_Project [B Project - Private VPC]
        SA["Service Attachment"]
        PSC_NAT_SUBNET["PSC NAT Subnet\npurpose=PRIVATE_SERVICE_CONNECT\n仅 Producer 需要"]
        ILB["ILB"]
    end

    GLB --> BS --> PSC_NEG
    PSC_NEG -->|"GCP 内部通道"| SA
    PSC_NAT_SUBNET -.->|"提供 SNAT 地址"| SA
    SA --> ILB
    SUBNET -.->|"分配接入 IP"| PSC_IP
    PSC_IP -.->|"绑定到"| PSC_NEG
```

---

## 回答问题 1：为什么 PSC NEG 要指定 network/subnet？

**本质原因：GCP 需要在 Consumer VPC 内分配一个真实的私有 IP，作为 PSC 的本地接入端点。**

创建 PSC NEG 时，GCP 背后做了这件事：

```
你指定的 subnet (10.0.1.0/24)
        ↓
GCP 自动从中分配一个 IP，例如 10.0.1.88
        ↓
这个 IP 成为 PSC Forwarding Rule 的地址
        ↓
GLB → Backend Service → 流量发往 10.0.1.88
        ↓
GCP 识别这是 PSC 端点，转发到 B Project Service Attachment
```

**你必须给 GCP 指定从哪个 subnet 里分配这个 IP**，所以 network + subnet 是必填的。

---

## 回答问题 2：Consumer 的 subnet 需要 `PRIVATE_SERVICE_CONNECT` purpose 吗？

**不需要。Consumer 侧用普通 subnet 即可。**

| 侧                        | Subnet Purpose 要求            | 原因                             |
| ------------------------- | ------------------------------ | -------------------------------- |
| **Producer**（B Project） | 必须 `PRIVATE_SERVICE_CONNECT` | 用于 SNAT，隔离 Consumer 真实 IP |
| **Consumer**（A Project） | 普通 subnet 即可               | 仅用于分配 PSC 接入 IP           |

```bash
# Consumer 侧 subnet 就是普通 subnet，无需特殊 purpose
gcloud compute networks subnets create consumer-subnet \
  --project=a-project \
  --network=shared-vpc-network \
  --region=asia-east1 \
  --range=10.0.1.0/24
# purpose 默认就是 PRIVATE，不需要加任何额外参数
```

```bash
# PSC NEG 直接用这个普通 subnet 即可
gcloud compute network-endpoint-groups create psc-neg \
  --project=a-project \
  --region=asia-east1 \
  --network-endpoint-type=PRIVATE_SERVICE_CONNECT \
  --psc-target-service=projects/b-project/regions/asia-east1/serviceAttachments/my-service-attachment \
  --network=shared-vpc-network \
  --subnetwork=consumer-subnet   # 普通 subnet 即可
```

---

## 关于 Shared VPC 的补充说明

你说"不理解为什么一定要在 Shared VPC 里创建"——其实**不是强制必须在 Shared VPC**，而是因为你的 A Project 本身就使用 Shared VPC，所以 PSC NEG 只能用 A Project 能访问到的网络，即 Shared VPC 的 subnet。

```
如果 A Project 用的是独立 VPC  → 指定自己的 VPC subnet
如果 A Project 用的是 Shared VPC → 指定 Host Project 的 subnet
```

> 注意：**Shared VPC 下的权限注意**：PSC NEG 创建在 Service Project（A Project），但 subnet 属于 Host Project，需要确认 A Project 的 Service Account 有 Host Project 网络的 `compute.networkUser` 角色，否则创建会报权限错误。

---

## 总结

| 问题                                             | 结论                                                |
| ------------------------------------------------ | --------------------------------------------------- |
| 为什么要指定 subnet？                            | GCP 需要从该 subnet 分配一个本地 IP 作为 PSC 接入点 |
| Consumer subnet 需要特殊 purpose 吗？            | **不需要**，普通 subnet 即可                        |
| `PRIVATE_SERVICE_CONNECT` purpose 只在哪侧需要？ | **仅 Producer 侧**的 NAT subnet 需要                |

---

## 如何切换到其他方式

## 问题分析

你说的完全正确。PSC Consumer 侧有 **两种接入方式**，PSC NEG 只是其中一种。另一种就是直接创建 **PSC Forwarding Rule**（也叫 Consumer Endpoint）。

---

## 两种方案对比

```mermaid
graph TD
    subgraph 方案A [方案 A - PSC NEG]
        GLB_A["GLB"] --> BS_A["Backend Service"]
        BS_A --> NEG["PSC NEG"]
        NEG -->|PSC 通道| SA_A["Service Attachment"]
    end

    subgraph 方案B [方案 B - PSC Forwarding Rule]
        VM["VM / GKE Pod\n内部访问"] -->|直接访问 PSC IP| FR["PSC Forwarding Rule\nConsumer Endpoint"]
        FR -->|PSC 通道| SA_B["Service Attachment"]
    end
```

---

## 两种方式核心区别

| 维度           | PSC NEG                     | PSC Forwarding Rule     |
| -------------- | --------------------------- | ----------------------- |
| 适用场景       | **GLB 作为入口**            | **内部服务直接访问**    |
| 能否挂载到 GLB | 可以                        | **不能**                |
| 访问方式       | GLB → Backend Service → NEG | 直接访问分配的私有 IP   |
| 创建复杂度     | 稍高                        | 简单                    |
| DNS 集成       | 通过 GLB 域名               | 可配合 Cloud DNS 私有域 |

---

## PSC Forwarding Rule 创建方式

```bash
# 第一步：在 Consumer subnet 预留一个静态内部 IP
gcloud compute addresses create psc-consumer-ip \
  --project=a-project \
  --region=asia-east1 \
  --subnet=consumer-subnet \
  --address-type=INTERNAL

# 第二步：创建 PSC Forwarding Rule 指向 Service Attachment
gcloud compute forwarding-rules create psc-consumer-endpoint \
  --project=a-project \
  --region=asia-east1 \
  --network=shared-vpc-network \
  --address=psc-consumer-ip \
  --target-service-attachment=projects/b-project/regions/asia-east1/serviceAttachments/my-service-attachment
```

创建成功后，A Project 内的任何资源（VM、GKE Pod）都可以直接通过 `psc-consumer-ip` 访问 B Project 的服务。

---

## 关键限制

**PSC Forwarding Rule 无法作为 GLB 的 Backend。**

所以如果你的目标架构是：

```
外部流量 → GLB → PSC → B Project
```

PSC Forwarding Rule **不满足需求**，必须用 PSC NEG。

PSC Forwarding Rule 适合的场景是：

```
A Project 内部服务（Kong DP / VM / GKE）→ PSC IP → B Project
```

---

## 结论

你的目标架构（GLB 作入口）只有 PSC NEG 这一条路。如果 PSC NEG 创建失败，需要先排查失败原因：

```bash
# 确认 subnet 存在且可用
gcloud compute networks subnets describe consumer-subnet \
  --project=a-project \
  --region=asia-east1

# 确认 Service Attachment 状态
gcloud compute service-attachments describe my-service-attachment \
  --project=b-project \
  --region=asia-east1
```

常见失败原因：

1. **Service Attachment 未 approve** Consumer Project
2. **subnet 不在同一 region**
3. **Shared VPC 权限不足**（`compute.networkUser` 缺失）

---
