feat: add development kubernetes mtls overlay
This commit is contained in:
parent
2124523cef
commit
4f49473cdd
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -22,10 +22,13 @@ jobs:
|
|||||||
deployment:
|
deployment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
|
PROXY_POOL_GATEWAY_USERNAME: ci-gateway-user
|
||||||
PROXY_POOL_GATEWAY_PASSWORD: ci-gateway-password
|
PROXY_POOL_GATEWAY_PASSWORD: ci-gateway-password
|
||||||
PROXY_POOL_EXTRACT_TOKEN: ci-extract-token
|
PROXY_POOL_EXTRACT_TOKEN: ci-extract-token
|
||||||
PROXY_POOL_ADMIN_TOKEN: ci-admin-token
|
PROXY_POOL_ADMIN_TOKEN: ci-admin-token
|
||||||
PROXY_POOL_CONFIG_FINGERPRINT_KEY: ci-config-fingerprint-key-at-least-32-bytes
|
PROXY_POOL_CONFIG_FINGERPRINT_KEY: ci-config-fingerprint-key-at-least-32-bytes
|
||||||
|
PROXY_POOL_POSTGRES_URL: postgres://ci:ci@postgres.example:5432/proxy_pool?sslmode=verify-full
|
||||||
|
PROXY_POOL_REDIS_URL: rediss://:ci@redis.example:6379/0
|
||||||
PROVIDER_A_TOKEN: ci-provider-a-token
|
PROVIDER_A_TOKEN: ci-provider-a-token
|
||||||
PROVIDER_B_TOKEN: ci-provider-b-token
|
PROVIDER_B_TOKEN: ci-provider-b-token
|
||||||
steps:
|
steps:
|
||||||
@ -34,6 +37,10 @@ jobs:
|
|||||||
run: docker compose -f deploy/docker-compose.yml config --quiet
|
run: docker compose -f deploy/docker-compose.yml config --quiet
|
||||||
- name: Render Kubernetes base
|
- name: Render Kubernetes base
|
||||||
run: kubectl kustomize deploy/kubernetes/base > /dev/null
|
run: kubectl kustomize deploy/kubernetes/base > /dev/null
|
||||||
|
- name: Render Kubernetes development mTLS overlay
|
||||||
|
run: kubectl kustomize deploy/kubernetes/overlays/development-mtls > /dev/null
|
||||||
|
- name: Validate development mTLS configuration
|
||||||
|
run: go run ./deploy/tools/configcheck deploy/kubernetes/overlays/development-mtls/config.yaml
|
||||||
|
|
||||||
test:
|
test:
|
||||||
strategy:
|
strategy:
|
||||||
|
|||||||
@ -164,7 +164,7 @@ go run ./deploy/tools/configcheck deploy/config/local.yaml
|
|||||||
```
|
```
|
||||||
|
|
||||||
`package-docs.ps1` 默认生成被忽略的 `dist/proxy-pool-docs-v1.0.zip`;包内包含 README、
|
`package-docs.ps1` 默认生成被忽略的 `dist/proxy-pool-docs-v1.0.zip`;包内包含 README、
|
||||||
`docs/`、图表、OpenAPI 和 Proto 契约,并以 `manifest.json` 记录 Git revision、文件大小和
|
`docs/`、图表、OpenAPI、Proto 契约和部署手册,并以 `manifest.json` 记录 Git revision、文件大小和
|
||||||
SHA-256。可使用 `-Version vMAJOR.MINOR[.PATCH]` 与 `-OutputPath OUTPUT.zip` 生成指定交付物。
|
SHA-256。可使用 `-Version vMAJOR.MINOR[.PATCH]` 与 `-OutputPath OUTPUT.zip` 生成指定交付物。
|
||||||
`benchmark-gateway.ps1` 固定执行一次 100k 索引调度、Routing Round Robin 和 Snapshot Apply
|
`benchmark-gateway.ps1` 固定执行一次 100k 索引调度、Routing Round Robin 和 Snapshot Apply
|
||||||
微基准,并把版本与原始输出写入 `dist/gateway-benchmarks.txt`;它用于回归比较,不构成
|
微基准,并把版本与原始输出写入 `dist/gateway-benchmarks.txt`;它用于回归比较,不构成
|
||||||
|
|||||||
@ -14,9 +14,11 @@ SPIFFE URI 证书,私钥只写入被忽略的 `deploy/.control-plane-tls/`。
|
|||||||
docker compose -f deploy/docker-compose.yml up -d --build
|
docker compose -f deploy/docker-compose.yml up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
Kubernetes Base 仍保持 `controlPlane.enabled: false`,并保留 `checker.yaml` 作为
|
Kubernetes Base 仍保持 `controlPlane.enabled: false`。`kubernetes/overlays/development-mtls`
|
||||||
环境 Overlay 模板。生产 Overlay 必须为每个弹性 Gateway/Checker 工作负载配置唯一
|
启用单副本 Controller/Gateway/Checker mTLS 拓扑,挂载三组外部 TLS Secret,并固定
|
||||||
身份与证书轮换,不能复用 Compose 的固定开发证书。
|
`gateway-a`、`checker-a` 身份。具体创建 Secret 和部署步骤见
|
||||||
|
[development mTLS overlay](kubernetes/overlays/development-mtls/README.md)。生产 Overlay
|
||||||
|
必须为每个弹性 Gateway/Checker 工作负载配置唯一身份与证书轮换,不能复用固定开发证书。
|
||||||
|
|
||||||
当前可执行验证:
|
当前可执行验证:
|
||||||
|
|
||||||
@ -24,6 +26,7 @@ Kubernetes Base 仍保持 `controlPlane.enabled: false`,并保留 `checker.yam
|
|||||||
docker compose -f deploy/docker-compose.yml config --quiet
|
docker compose -f deploy/docker-compose.yml config --quiet
|
||||||
docker compose -f deploy/docker-compose.test.yml config --quiet
|
docker compose -f deploy/docker-compose.test.yml config --quiet
|
||||||
kubectl kustomize deploy/kubernetes/base | Out-Null
|
kubectl kustomize deploy/kubernetes/base | Out-Null
|
||||||
|
kubectl kustomize deploy/kubernetes/overlays/development-mtls | Out-Null
|
||||||
go run ./deploy/tools/configcheck deploy/config/local.yaml
|
go run ./deploy/tools/configcheck deploy/config/local.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,8 @@ func TestCIValidatesProtocolAndDeploymentAssets(t *testing.T) {
|
|||||||
"./scripts/verify-proto.ps1",
|
"./scripts/verify-proto.ps1",
|
||||||
"docker compose -f deploy/docker-compose.yml config --quiet",
|
"docker compose -f deploy/docker-compose.yml config --quiet",
|
||||||
"kubectl kustomize deploy/kubernetes/base",
|
"kubectl kustomize deploy/kubernetes/base",
|
||||||
|
"kubectl kustomize deploy/kubernetes/overlays/development-mtls",
|
||||||
|
"go run ./deploy/tools/configcheck deploy/kubernetes/overlays/development-mtls/config.yaml",
|
||||||
} {
|
} {
|
||||||
if !strings.Contains(workflow, required) {
|
if !strings.Contains(workflow, required) {
|
||||||
t.Errorf("ci workflow does not validate %q", required)
|
t.Errorf("ci workflow does not validate %q", required)
|
||||||
|
|||||||
56
deploy/kubernetes/overlays/development-mtls/README.md
Normal file
56
deploy/kubernetes/overlays/development-mtls/README.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# Kubernetes development mTLS overlay
|
||||||
|
|
||||||
|
该 Overlay 在 base 资源之上启用 Controller 控制面 mTLS,并启动一个固定身份的 Gateway
|
||||||
|
和 Checker。它只用于开发或预发布的单副本控制面验证,不是生产弹性身份方案。
|
||||||
|
|
||||||
|
## Prepare certificates
|
||||||
|
|
||||||
|
先生成未提交的开发证书。输出目录必须为空;生成器的 `controller` 证书包含
|
||||||
|
`DNS:controller`,Overlay 中的客户端也只拨号 `controller:8443`。
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
./scripts/generate-local-controlplane-certs.ps1
|
||||||
|
kubectl apply -f deploy/kubernetes/base/namespace.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
将生成的三组证书写入 Kubernetes Secret。下面的命令可重复执行,只更新对应 Secret:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
kubectl -n proxy-pool create secret generic proxy-pool-controlplane-server-tls `
|
||||||
|
--from-file=tls.crt=deploy/.control-plane-tls/controller/tls.crt `
|
||||||
|
--from-file=tls.key=deploy/.control-plane-tls/controller/tls.key `
|
||||||
|
--from-file=ca.crt=deploy/.control-plane-tls/controller/ca.crt `
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
kubectl -n proxy-pool create secret generic proxy-pool-controlplane-gateway-tls `
|
||||||
|
--from-file=tls.crt=deploy/.control-plane-tls/gateway-a/tls.crt `
|
||||||
|
--from-file=tls.key=deploy/.control-plane-tls/gateway-a/tls.key `
|
||||||
|
--from-file=ca.crt=deploy/.control-plane-tls/gateway-a/ca.crt `
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
kubectl -n proxy-pool create secret generic proxy-pool-controlplane-checker-tls `
|
||||||
|
--from-file=tls.crt=deploy/.control-plane-tls/checker-a/tls.crt `
|
||||||
|
--from-file=tls.key=deploy/.control-plane-tls/checker-a/tls.key `
|
||||||
|
--from-file=ca.crt=deploy/.control-plane-tls/checker-a/ca.crt `
|
||||||
|
--dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
```
|
||||||
|
|
||||||
|
另行从私密配置系统创建 `proxy-pool-secrets`。可参考 base 目录的
|
||||||
|
`secret.example.yaml`,但不要把实际数据库、Redis、Provider 或 API 凭据写入 Git。
|
||||||
|
|
||||||
|
## Deploy
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
kubectl apply -k deploy/kubernetes/overlays/development-mtls
|
||||||
|
kubectl -n proxy-pool rollout status deployment/proxy-controller --timeout=5m
|
||||||
|
kubectl -n proxy-pool rollout status deployment/proxy-gateway --timeout=5m
|
||||||
|
kubectl -n proxy-pool rollout status deployment/proxy-checker --timeout=5m
|
||||||
|
```
|
||||||
|
|
||||||
|
Overlay 的 Gateway 固定为 `worker_id=gateway-a`,Checker 固定为
|
||||||
|
`checker_id=checker-a`,并把 Gateway HPA 限制为 `minReplicas=maxReplicas=1`。
|
||||||
|
因此不得在此 Overlay 上增加副本数或放宽 HPA;重复使用同一 Worker 身份会破坏
|
||||||
|
session、ownership 和证书角色边界。
|
||||||
|
|
||||||
|
生产环境需要由工作负载身份系统为每个副本签发独立、可轮换的证书,并将该副本的
|
||||||
|
身份注入 `worker_id`、`instance_id`、Gateway Client TLS 或 Checker Client TLS。
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: checker-control-plane
|
||||||
|
namespace: proxy-pool
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels: {app.kubernetes.io/name: proxy-checker}
|
||||||
|
policyTypes: [Ingress, Egress]
|
||||||
|
ingress:
|
||||||
|
- from:
|
||||||
|
- namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: monitoring}}
|
||||||
|
ports: [{port: 9090, protocol: TCP}]
|
||||||
|
egress:
|
||||||
|
- to:
|
||||||
|
- namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: kube-system}}
|
||||||
|
ports: [{port: 53, protocol: UDP}, {port: 53, protocol: TCP}]
|
||||||
|
- to:
|
||||||
|
- podSelector: {matchLabels: {app.kubernetes.io/name: proxy-controller}}
|
||||||
|
ports: [{port: 8443, protocol: TCP}]
|
||||||
|
- to:
|
||||||
|
- ipBlock:
|
||||||
|
cidr: 0.0.0.0/0
|
||||||
|
except: [10.0.0.0/8, 100.64.0.0/10, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16]
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: proxy-checker
|
||||||
|
namespace: proxy-pool
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: checker
|
||||||
|
env:
|
||||||
|
- {name: PROXY_POOL_CONTROL_PLANE_ADDRESS, value: controller:8443}
|
||||||
|
- {name: PROXY_POOL_CHECKER_ID, value: checker-a}
|
||||||
|
- {name: PROXY_POOL_CHECKER_INSTANCE_ID, value: checker-a-1}
|
||||||
|
- {name: PROXY_POOL_CHECKER_MAX_IN_FLIGHT, value: "200"}
|
||||||
|
volumeMounts:
|
||||||
|
- name: controlplane-checker-tls
|
||||||
|
mountPath: /run/proxy-pool-tls/client
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: controlplane-checker-tls
|
||||||
|
secret:
|
||||||
|
secretName: proxy-pool-controlplane-checker-tls
|
||||||
65
deploy/kubernetes/overlays/development-mtls/checker.yaml
Normal file
65
deploy/kubernetes/overlays/development-mtls/checker.yaml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: proxy-checker
|
||||||
|
namespace: proxy-pool
|
||||||
|
labels: {app.kubernetes.io/name: proxy-checker, app.kubernetes.io/part-of: proxy-pool}
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
minReadySeconds: 5
|
||||||
|
revisionHistoryLimit: 3
|
||||||
|
strategy:
|
||||||
|
type: RollingUpdate
|
||||||
|
rollingUpdate: {maxUnavailable: 1, maxSurge: 1}
|
||||||
|
selector:
|
||||||
|
matchLabels: {app.kubernetes.io/name: proxy-checker}
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels: {app.kubernetes.io/name: proxy-checker, app.kubernetes.io/part-of: proxy-pool}
|
||||||
|
annotations: {prometheus.io/scrape: "true", prometheus.io/port: "9090", prometheus.io/path: /metrics}
|
||||||
|
spec:
|
||||||
|
serviceAccountName: proxy-pool
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
terminationGracePeriodSeconds: 45
|
||||||
|
securityContext: {runAsNonRoot: true, seccompProfile: {type: RuntimeDefault}}
|
||||||
|
topologySpreadConstraints:
|
||||||
|
- maxSkew: 1
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
whenUnsatisfiable: ScheduleAnyway
|
||||||
|
labelSelector: {matchLabels: {app.kubernetes.io/name: proxy-checker}}
|
||||||
|
containers:
|
||||||
|
- name: checker
|
||||||
|
image: REGISTRY/proxy-pool:VERSION
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command: [proxy-checker]
|
||||||
|
env:
|
||||||
|
- {name: PROXY_POOL_CONFIG, value: /etc/proxy-pool/config.yaml}
|
||||||
|
envFrom:
|
||||||
|
- secretRef: {name: proxy-pool-secrets}
|
||||||
|
ports:
|
||||||
|
- {name: metrics, containerPort: 9090}
|
||||||
|
readinessProbe:
|
||||||
|
httpGet: {path: /readyz, port: metrics}
|
||||||
|
periodSeconds: 5
|
||||||
|
timeoutSeconds: 2
|
||||||
|
livenessProbe:
|
||||||
|
httpGet: {path: /livez, port: metrics}
|
||||||
|
periodSeconds: 10
|
||||||
|
timeoutSeconds: 2
|
||||||
|
lifecycle:
|
||||||
|
preStop: {exec: {command: [sh, -c, "sleep 3"]}}
|
||||||
|
resources:
|
||||||
|
requests: {cpu: "1", memory: 512Mi}
|
||||||
|
limits: {cpu: "2", memory: 1Gi}
|
||||||
|
securityContext:
|
||||||
|
allowPrivilegeEscalation: false
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
capabilities: {drop: [ALL]}
|
||||||
|
volumeMounts:
|
||||||
|
- {name: config, mountPath: /etc/proxy-pool, readOnly: true}
|
||||||
|
- {name: tmp, mountPath: /tmp}
|
||||||
|
volumes:
|
||||||
|
- name: config
|
||||||
|
configMap: {name: proxy-pool-config}
|
||||||
|
- name: tmp
|
||||||
|
emptyDir: {sizeLimit: 64Mi}
|
||||||
216
deploy/kubernetes/overlays/development-mtls/config.yaml
Normal file
216
deploy/kubernetes/overlays/development-mtls/config.yaml
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
version: 1
|
||||||
|
|
||||||
|
security:
|
||||||
|
requireProtectionOnPublicListen: true
|
||||||
|
|
||||||
|
gateway:
|
||||||
|
enabled: true
|
||||||
|
listen: 0.0.0.0:8080
|
||||||
|
access:
|
||||||
|
allowCIDRs: [0.0.0.0/0]
|
||||||
|
trustedProxies: []
|
||||||
|
auth:
|
||||||
|
mode: usernamePassword
|
||||||
|
username: "${PROXY_POOL_GATEWAY_USERNAME}"
|
||||||
|
password: "${PROXY_POOL_GATEWAY_PASSWORD}"
|
||||||
|
limits:
|
||||||
|
maxConcurrentConnections: 20000
|
||||||
|
requestsPerMinutePerClient: 60000
|
||||||
|
transport:
|
||||||
|
maxIdleConns: 20000
|
||||||
|
maxIdleConnsPerHost: 32
|
||||||
|
maxConnsPerHost: 32
|
||||||
|
tunnelBufferBytes: 32768
|
||||||
|
retry:
|
||||||
|
maxAttempts: 2
|
||||||
|
retryMethods: [GET, HEAD]
|
||||||
|
destinationPolicy:
|
||||||
|
denyPrivateNetworks: true
|
||||||
|
denyLoopback: true
|
||||||
|
denyLinkLocal: true
|
||||||
|
denyCIDRs: [169.254.169.254/32]
|
||||||
|
|
||||||
|
distribution:
|
||||||
|
enabled: true
|
||||||
|
listen: 0.0.0.0:8081
|
||||||
|
access:
|
||||||
|
allowCIDRs: [10.0.0.0/8]
|
||||||
|
trustedProxies: [10.0.0.0/8]
|
||||||
|
auth:
|
||||||
|
mode: apiKey
|
||||||
|
header: X-API-Key
|
||||||
|
token: "${PROXY_POOL_EXTRACT_TOKEN}"
|
||||||
|
limits:
|
||||||
|
requestsPerMinute: 6000
|
||||||
|
requestsPerMinutePerClient: 600
|
||||||
|
clientIdentification:
|
||||||
|
mode: sourceIP
|
||||||
|
extraction:
|
||||||
|
fulfillment: partial
|
||||||
|
maxCountPerRequest: 100
|
||||||
|
minRemainingTTL: 30s
|
||||||
|
maxHealthCheckAge: 30s
|
||||||
|
reserveForGateway: 1000
|
||||||
|
idempotencyTTL: 5m
|
||||||
|
|
||||||
|
admin:
|
||||||
|
enabled: true
|
||||||
|
listen: 0.0.0.0:8082
|
||||||
|
access:
|
||||||
|
allowCIDRs: [10.0.0.0/8]
|
||||||
|
auth:
|
||||||
|
mode: apiKey
|
||||||
|
header: X-Admin-Key
|
||||||
|
token: "${PROXY_POOL_ADMIN_TOKEN}"
|
||||||
|
|
||||||
|
controlPlane:
|
||||||
|
enabled: true
|
||||||
|
listen: 0.0.0.0:8443
|
||||||
|
protocolVersion: 1
|
||||||
|
heartbeatInterval: 10s
|
||||||
|
sessionTTL: 30s
|
||||||
|
maxStaleAge: 30s
|
||||||
|
maxMessageBytes: 4194304
|
||||||
|
maxRuntimeCounters: 100000
|
||||||
|
maxConcurrentStreams: 1000
|
||||||
|
tls:
|
||||||
|
mode: mtls
|
||||||
|
certFile: /run/proxy-pool-tls/server/tls.crt
|
||||||
|
keyFile: /run/proxy-pool-tls/server/tls.key
|
||||||
|
clientCAFile: /run/proxy-pool-tls/server/ca.crt
|
||||||
|
trustDomain: proxy-pool.local
|
||||||
|
environment: development
|
||||||
|
gatewayTLS:
|
||||||
|
certFile: /run/proxy-pool-tls/client/tls.crt
|
||||||
|
keyFile: /run/proxy-pool-tls/client/tls.key
|
||||||
|
serverCAFile: /run/proxy-pool-tls/client/ca.crt
|
||||||
|
checkerTLS:
|
||||||
|
certFile: /run/proxy-pool-tls/client/tls.crt
|
||||||
|
keyFile: /run/proxy-pool-tls/client/tls.key
|
||||||
|
serverCAFile: /run/proxy-pool-tls/client/ca.crt
|
||||||
|
|
||||||
|
metrics:
|
||||||
|
enabled: true
|
||||||
|
listen: 0.0.0.0:9090
|
||||||
|
|
||||||
|
storage:
|
||||||
|
postgresURL: "${PROXY_POOL_POSTGRES_URL}"
|
||||||
|
redisURL: "${PROXY_POOL_REDIS_URL}"
|
||||||
|
|
||||||
|
routing:
|
||||||
|
- name: gateway-default
|
||||||
|
enabled: true
|
||||||
|
purpose: gateway
|
||||||
|
upstreams: [provider-a, provider-b]
|
||||||
|
strategy:
|
||||||
|
type: sequential
|
||||||
|
switchAfterEmptyFetch: 5
|
||||||
|
endBehavior: stayLast
|
||||||
|
onUnavailable:
|
||||||
|
action: reject
|
||||||
|
- name: extract-default
|
||||||
|
enabled: true
|
||||||
|
purpose: extract
|
||||||
|
upstreams: [provider-a, provider-b]
|
||||||
|
strategy:
|
||||||
|
type: sequential
|
||||||
|
switchAfterEmptyFetch: 5
|
||||||
|
endBehavior: stayLast
|
||||||
|
onUnavailable:
|
||||||
|
action: reject
|
||||||
|
|
||||||
|
upstreams:
|
||||||
|
provider-a:
|
||||||
|
enabled: true
|
||||||
|
exposure: [gateway, extract]
|
||||||
|
provider:
|
||||||
|
billingMode: fetch
|
||||||
|
protocols: [http]
|
||||||
|
api:
|
||||||
|
url: https://provider-a.invalid/api/proxies
|
||||||
|
method: GET
|
||||||
|
auth:
|
||||||
|
type: apiKey
|
||||||
|
location: header
|
||||||
|
name: Authorization
|
||||||
|
value: "${PROVIDER_A_TOKEN}"
|
||||||
|
template: '{{ . }}'
|
||||||
|
proxyAuth:
|
||||||
|
type: response
|
||||||
|
pool:
|
||||||
|
maxSize: 5000
|
||||||
|
shrinkDelay: 30s
|
||||||
|
capacity:
|
||||||
|
maxConcurrencyPerProxy: 20
|
||||||
|
refill:
|
||||||
|
reconcileInterval: 1s
|
||||||
|
minimumAvailableSlots: 8000
|
||||||
|
targetAvailableSlots: 12000
|
||||||
|
lifecycle:
|
||||||
|
ttl: 5m
|
||||||
|
allocationSafetyMargin: 20s
|
||||||
|
fetch:
|
||||||
|
estimatedIPsPerCall: 100
|
||||||
|
requestInterval: 1s
|
||||||
|
timeout: 3s
|
||||||
|
maxAttempts: 3
|
||||||
|
maxInFlight: 1
|
||||||
|
maxTotal: 100000
|
||||||
|
maxResponseBytes: 4194304
|
||||||
|
templateTimeout: 100ms
|
||||||
|
retry: {initial: 500ms, max: 30s, jitter: 20}
|
||||||
|
check:
|
||||||
|
interval: 30s
|
||||||
|
jitter: 20
|
||||||
|
maxInFlight: 200
|
||||||
|
timeout: 3s
|
||||||
|
maxAttempts: 2
|
||||||
|
maxConsecutiveFailures: 3
|
||||||
|
urls: [https://example.com/]
|
||||||
|
provider-b:
|
||||||
|
enabled: true
|
||||||
|
exposure: [gateway, extract]
|
||||||
|
provider:
|
||||||
|
billingMode: fetch
|
||||||
|
protocols: [http]
|
||||||
|
api:
|
||||||
|
url: https://provider-b.invalid/api/proxies
|
||||||
|
method: GET
|
||||||
|
auth:
|
||||||
|
type: apiKey
|
||||||
|
location: header
|
||||||
|
name: Authorization
|
||||||
|
value: "${PROVIDER_B_TOKEN}"
|
||||||
|
template: '{{ . }}'
|
||||||
|
proxyAuth:
|
||||||
|
type: response
|
||||||
|
pool:
|
||||||
|
maxSize: 5000
|
||||||
|
shrinkDelay: 30s
|
||||||
|
capacity:
|
||||||
|
maxConcurrencyPerProxy: 20
|
||||||
|
refill:
|
||||||
|
reconcileInterval: 1s
|
||||||
|
minimumAvailableSlots: 8000
|
||||||
|
targetAvailableSlots: 12000
|
||||||
|
lifecycle:
|
||||||
|
ttl: 5m
|
||||||
|
allocationSafetyMargin: 20s
|
||||||
|
fetch:
|
||||||
|
estimatedIPsPerCall: 100
|
||||||
|
requestInterval: 1s
|
||||||
|
timeout: 3s
|
||||||
|
maxAttempts: 3
|
||||||
|
maxInFlight: 1
|
||||||
|
maxTotal: 100000
|
||||||
|
maxResponseBytes: 4194304
|
||||||
|
templateTimeout: 100ms
|
||||||
|
retry: {initial: 500ms, max: 30s, jitter: 20}
|
||||||
|
check:
|
||||||
|
interval: 30s
|
||||||
|
jitter: 20
|
||||||
|
maxInFlight: 200
|
||||||
|
timeout: 3s
|
||||||
|
maxAttempts: 2
|
||||||
|
maxConsecutiveFailures: 3
|
||||||
|
urls: [https://example.com/]
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: proxy-controller
|
||||||
|
namespace: proxy-pool
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: controller
|
||||||
|
volumeMounts:
|
||||||
|
- name: controlplane-server-tls
|
||||||
|
mountPath: /run/proxy-pool-tls/server
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: controlplane-server-tls
|
||||||
|
secret:
|
||||||
|
secretName: proxy-pool-controlplane-server-tls
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: controller
|
||||||
|
namespace: proxy-pool
|
||||||
|
labels: {app.kubernetes.io/name: proxy-controller}
|
||||||
|
spec:
|
||||||
|
type: ClusterIP
|
||||||
|
selector: {app.kubernetes.io/name: proxy-controller}
|
||||||
|
ports:
|
||||||
|
- {name: control, port: 8443, targetPort: control}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
apiVersion: autoscaling/v2
|
||||||
|
kind: HorizontalPodAutoscaler
|
||||||
|
metadata:
|
||||||
|
name: proxy-gateway
|
||||||
|
namespace: proxy-pool
|
||||||
|
spec:
|
||||||
|
minReplicas: 1
|
||||||
|
maxReplicas: 1
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: proxy-gateway
|
||||||
|
namespace: proxy-pool
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: gateway
|
||||||
|
env:
|
||||||
|
- {name: PROXY_POOL_CONTROL_PLANE_ADDRESS, value: controller:8443}
|
||||||
|
- {name: PROXY_POOL_CLUSTER_ID, value: kubernetes-development}
|
||||||
|
- {name: PROXY_POOL_WORKER_ID, value: gateway-a}
|
||||||
|
- {name: PROXY_POOL_INSTANCE_ID, value: gateway-a-1}
|
||||||
|
- {name: PROXY_POOL_ZONE, value: kubernetes-development}
|
||||||
|
volumeMounts:
|
||||||
|
- name: controlplane-gateway-tls
|
||||||
|
mountPath: /run/proxy-pool-tls/client
|
||||||
|
readOnly: true
|
||||||
|
volumes:
|
||||||
|
- name: controlplane-gateway-tls
|
||||||
|
secret:
|
||||||
|
secretName: proxy-pool-controlplane-gateway-tls
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- ../../base
|
||||||
|
- checker.yaml
|
||||||
|
- controller-service.yaml
|
||||||
|
- checker-networkpolicy.yaml
|
||||||
|
configMapGenerator:
|
||||||
|
- name: proxy-pool-config
|
||||||
|
namespace: proxy-pool
|
||||||
|
behavior: replace
|
||||||
|
files:
|
||||||
|
- config.yaml
|
||||||
|
generatorOptions:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
patches:
|
||||||
|
- path: controller-patch.yaml
|
||||||
|
- path: gateway-patch.yaml
|
||||||
|
- path: checker-patch.yaml
|
||||||
|
- path: gateway-hpa-patch.yaml
|
||||||
39
deploy/kubernetes_overlay_test.go
Normal file
39
deploy/kubernetes_overlay_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package deploy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKubernetesDevelopmentMTLSOverlayRendersStaticWorkerIdentities(t *testing.T) {
|
||||||
|
kubectl, err := exec.LookPath("kubectl")
|
||||||
|
if err != nil {
|
||||||
|
t.Skip("kubectl is required to render the Kubernetes development mTLS overlay")
|
||||||
|
}
|
||||||
|
command := exec.Command(kubectl, "kustomize", "kubernetes/overlays/development-mtls")
|
||||||
|
command.Dir = "."
|
||||||
|
payload, err := command.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("render development mTLS overlay: %v\n%s", err, payload)
|
||||||
|
}
|
||||||
|
rendered := string(payload)
|
||||||
|
for _, required := range []string{
|
||||||
|
"mode: mtls",
|
||||||
|
"trustDomain: proxy-pool.local",
|
||||||
|
"value: controller:8443",
|
||||||
|
"value: gateway-a",
|
||||||
|
"value: checker-a",
|
||||||
|
"secretName: proxy-pool-controlplane-server-tls",
|
||||||
|
"secretName: proxy-pool-controlplane-gateway-tls",
|
||||||
|
"secretName: proxy-pool-controlplane-checker-tls",
|
||||||
|
"name: proxy-checker",
|
||||||
|
"name: checker-control-plane",
|
||||||
|
"minReplicas: 1",
|
||||||
|
"maxReplicas: 1",
|
||||||
|
} {
|
||||||
|
if !strings.Contains(rendered, required) {
|
||||||
|
t.Errorf("development mTLS overlay does not render %q", required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -374,7 +374,9 @@ OpenAPI 结构契约由 Go 测试在双平台 CI 执行。
|
|||||||
Gateway 热路径。`platform/logging` 以 JSON `slog` 输出进程级致命错误,字段、URL
|
Gateway 热路径。`platform/logging` 以 JSON `slog` 输出进程级致命错误,字段、URL
|
||||||
用户信息、查询 Secret 和错误对象均经过脱敏,且不在请求热路径逐条写日志。
|
用户信息、查询 Secret 和错误对象均经过脱敏,且不在请求热路径逐条写日志。
|
||||||
CI 另有独立 Deployment job,在占位凭据下渲染 Compose,并使用 `kubectl kustomize`
|
CI 另有独立 Deployment job,在占位凭据下渲染 Compose,并使用 `kubectl kustomize`
|
||||||
渲染 Kubernetes base;它不启动容器、不访问真实存储或密钥。
|
渲染 Kubernetes base 和单副本 development mTLS Overlay;它不启动容器、不访问真实存储或密钥。
|
||||||
|
development Overlay 已挂载独立 Controller/Gateway/Checker TLS Secret、启用 Checker 和最小
|
||||||
|
网络策略,但固定 `gateway-a`、`checker-a` 身份且 HPA 锁为单副本,不能代替生产证书轮换。
|
||||||
|
|
||||||
## Task 14: Documentation, Examples, and Diagrams
|
## Task 14: Documentation, Examples, and Diagrams
|
||||||
|
|
||||||
@ -386,7 +388,7 @@ CI 另有独立 Deployment job,在占位凭据下渲染 Compose,并使用 `k
|
|||||||
- [x] Provide at least 30 Mermaid architecture, flow, sequence, state, and failure diagrams.
|
- [x] Provide at least 30 Mermaid architecture, flow, sequence, state, and failure diagrams.
|
||||||
- [x] Generate `proxy-pool-docs-v1.0.zip` from versioned documentation assets.
|
- [x] Generate `proxy-pool-docs-v1.0.zip` from versioned documentation assets.
|
||||||
|
|
||||||
`scripts/package-docs.ps1` 会将 README、`docs/`、图表、OpenAPI 与 Proto 契约复制至临时目录,
|
`scripts/package-docs.ps1` 会将 README、`docs/`、图表、OpenAPI、Proto 契约与部署手册复制至临时目录,
|
||||||
生成包含 Git revision、文件大小和 SHA-256 的 `manifest.json`,再以临时 ZIP 原子替换目标。
|
生成包含 Git revision、文件大小和 SHA-256 的 `manifest.json`,再以临时 ZIP 原子替换目标。
|
||||||
默认输出为被 Git 忽略的 `dist/proxy-pool-docs-v1.0.zip`;Go 回归测试实际执行脚本并校验归档内容。
|
默认输出为被 Git 忽略的 `dist/proxy-pool-docs-v1.0.zip`;Go 回归测试实际执行脚本并校验归档内容。
|
||||||
|
|
||||||
|
|||||||
@ -76,6 +76,8 @@ func TestPackageDocsCreatesTraceableArchive(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, required := range []string{
|
for _, required := range []string{
|
||||||
"README.md",
|
"README.md",
|
||||||
|
"deploy/README.md",
|
||||||
|
"deploy/kubernetes/overlays/development-mtls/README.md",
|
||||||
"docs/design/architecture.md",
|
"docs/design/architecture.md",
|
||||||
"docs/operations/runbook.md",
|
"docs/operations/runbook.md",
|
||||||
"diagrams/README.md",
|
"diagrams/README.md",
|
||||||
|
|||||||
@ -103,8 +103,10 @@ Windows 环境为 `CGO_ENABLED=0` 且没有 C 编译器,`go test -race` 在本
|
|||||||
CI 已配置 Linux race job。PostgreSQL 18 和 Redis 8.2 的隔离 Adapter fixture
|
CI 已配置 Linux race job。PostgreSQL 18 和 Redis 8.2 的隔离 Adapter fixture
|
||||||
已经运行;本地 Compose 已声明 Controller、两个独立 Gateway 身份和一个 Checker 的
|
已经运行;本地 Compose 已声明 Controller、两个独立 Gateway 身份和一个 Checker 的
|
||||||
mTLS 运行拓扑,并由配置、证书、命令装配和部署契约测试覆盖。Kubernetes base 刻意不直接
|
mTLS 运行拓扑,并由配置、证书、命令装配和部署契约测试覆盖。Kubernetes base 刻意不直接
|
||||||
启用 Checker,因为生产环境必须通过 Overlay 为每个弹性 Worker 注入唯一身份与证书;
|
启用 Checker;`development-mtls` Overlay 已为单副本 Controller、Gateway 和 Checker
|
||||||
该 Overlay 和容器端到端启动仍待具备镜像网络的环境验证。
|
提供固定开发身份、mTLS Secret 挂载、最小出站 NetworkPolicy 与渲染/配置契约验证。
|
||||||
|
生产环境仍必须通过独立 Overlay 为每个弹性 Worker 注入唯一、可轮换的身份与证书,且
|
||||||
|
容器端到端启动仍待具备镜像网络的环境验证。
|
||||||
|
|
||||||
## 3. 后续实现范围
|
## 3. 后续实现范围
|
||||||
|
|
||||||
@ -116,8 +118,9 @@ mTLS 运行拓扑,并由配置、证书、命令装配和部署契约测试覆
|
|||||||
与 p99 告警;仍需代表性环境的流量压测和容量报告。
|
与 p99 告警;仍需代表性环境的流量压测和容量报告。
|
||||||
3. Provider 长期凭据回收与累计额度的生产运营策略仍需结合实际供应商合同、失效时间和
|
3. Provider 长期凭据回收与累计额度的生产运营策略仍需结合实际供应商合同、失效时间和
|
||||||
密钥平台完成演练。
|
密钥平台完成演练。
|
||||||
4. Kubernetes 需要按工作负载身份系统提供 mTLS Overlay、证书轮换、唯一 Worker ID 和
|
4. Kubernetes 的开发 mTLS Overlay 已覆盖单副本固定身份和 Checker 出站
|
||||||
Checker 出站 NetworkPolicy;base 清单不假定或伪造这些生产环境能力。
|
NetworkPolicy;生产环境仍需要按工作负载身份系统提供证书轮换、每副本唯一 Worker ID
|
||||||
|
与可弹性扩缩容的身份注入,base 清单不假定或伪造这些能力。
|
||||||
5. Redis/PostgreSQL 故障转移、控制面断线恢复、Snapshot 回滚、Drain 回收和 Extract
|
5. Redis/PostgreSQL 故障转移、控制面断线恢复、Snapshot 回滚、Drain 回收和 Extract
|
||||||
库存不足的完整故障演练。
|
库存不足的完整故障演练。
|
||||||
6. 10k 稳态、100k 峰值、CONNECT 活跃连接和建连速率的代表性集群负载测试。
|
6. 10k 稳态、100k 峰值、CONNECT 活跃连接和建连速率的代表性集群负载测试。
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| ARCH-001 | 数据面 Worker 与控制面 Controller 分离 | 1-70 | 包、协议和部署拓扑已分离;Controller 已运行 Worker Register/Watch/ACK/Runtime/Outcome 与 Checker Observation gRPC,并发布 Proxy/Gateway Routing/按引用去重凭据完整快照;Checker 任务流已具备有界领取、租约栅栏和任务期凭据契约。Gateway 已将快照编译为同版本动态 View,并由独立进程维护控制面会话。`proxy-checker` 与 Redis BASIC/EGRESS/TARGET 共享任务运行态、`proxy-loadgen` 有界 HTTP/CONNECT/Extract 校验场景均已实现 |
|
| ARCH-001 | 数据面 Worker 与控制面 Controller 分离 | 1-70 | 包、协议和部署拓扑已分离;Controller 已运行 Worker Register/Watch/ACK/Runtime/Outcome 与 Checker Observation gRPC,并发布 Proxy/Gateway Routing/按引用去重凭据完整快照;Checker 任务流已具备有界领取、租约栅栏和任务期凭据契约。Gateway 已将快照编译为同版本动态 View,并由独立进程维护控制面会话。`proxy-checker` 与 Redis BASIC/EGRESS/TARGET 共享任务运行态、`proxy-loadgen` 有界 HTTP/CONNECT/Extract 校验场景均已实现 |
|
||||||
| ARCH-002 | 热路径只做认证、本地路由和网络转发 | 1-70, 380-430 | Gateway bootstrap 集成测试验证启动期控制面会话与快照就绪,HTTP 请求只走本地 Snapshot/Dispatch;Outcome 仅写入有界非阻塞本地队列,代表性性能剖析待完成 |
|
| ARCH-002 | 热路径只做认证、本地路由和网络转发 | 1-70, 380-430 | Gateway bootstrap 集成测试验证启动期控制面会话与快照就绪,HTTP 请求只走本地 Snapshot/Dispatch;Outcome 仅写入有界非阻塞本地队列,代表性性能剖析待完成 |
|
||||||
| ARCH-003 | Gateway、Distribution、Admin、Metrics 独立入口 | 8904-8958 | Controller 命令已装配 Distribution/Admin/Metrics 三个独立监听及联动停机;Gateway 命令已装配代理与 Metrics 监听。本地 Compose 已声明 Controller/Gateway/Checker mTLS 拓扑;Kubernetes 的工作负载身份 Overlay 仍待环境实现 |
|
| ARCH-003 | Gateway、Distribution、Admin、Metrics 独立入口 | 8904-8958 | Controller 命令已装配 Distribution/Admin/Metrics 三个独立监听及联动停机;Gateway 命令已装配代理与 Metrics 监听。本地 Compose 已声明 Controller/Gateway/Checker mTLS 拓扑;Kubernetes `development-mtls` Overlay 已通过渲染与配置契约验证单副本固定身份链路,生产工作负载身份仍待环境实现 |
|
||||||
| ARCH-004 | Controller 集中 Provider 获取与切换 | 1403-1580 | Redis Leader、动态 Provider Supervisor 与 Bootstrap 生产装配已完成;静态配置与 Admin 管理态共同决定 Provider 启停,停用策略以 Upstream revision 栅栏驱动既有 ownership Drain;Routing 切换会向本进程 Worker 快照流广播完整刷新,多副本按定时刷新与权威 HMAC 指纹/revision 栅栏收敛并拒绝旧配置换主 |
|
| ARCH-004 | Controller 集中 Provider 获取与切换 | 1403-1580 | Redis Leader、动态 Provider Supervisor 与 Bootstrap 生产装配已完成;静态配置与 Admin 管理态共同决定 Provider 启停,停用策略以 Upstream revision 栅栏驱动既有 ownership Drain;Routing 切换会向本进程 Worker 快照流广播完整刷新,多副本按定时刷新与权威 HMAC 指纹/revision 栅栏收敛并拒绝旧配置换主 |
|
||||||
| ARCH-005 | 100k QPS 峰值使用多 Worker 集群 | 当前会话 | 未验证设计目标;待代表性集群负载报告 |
|
| ARCH-005 | 100k QPS 峰值使用多 Worker 集群 | 当前会话 | 未验证设计目标;待代表性集群负载报告 |
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,9 @@ $sources = @(
|
|||||||
"docs",
|
"docs",
|
||||||
"diagrams",
|
"diagrams",
|
||||||
"api/openapi",
|
"api/openapi",
|
||||||
"api/proto"
|
"api/proto",
|
||||||
|
"deploy/README.md",
|
||||||
|
"deploy/kubernetes/overlays/development-mtls/README.md"
|
||||||
)
|
)
|
||||||
$stagingRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("proxy-pool-docs-" + [Guid]::NewGuid().ToString("N"))
|
$stagingRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("proxy-pool-docs-" + [Guid]::NewGuid().ToString("N"))
|
||||||
$temporaryArchive = ""
|
$temporaryArchive = ""
|
||||||
|
|||||||
@ -75,8 +75,8 @@
|
|||||||
与 PostgreSQL 18 的隔离 Adapter fixture 已运行。Docker Desktop 缺少镜像 HTTPS
|
与 PostgreSQL 18 的隔离 Adapter fixture 已运行。Docker Desktop 缺少镜像 HTTPS
|
||||||
代理配置,未能在本机完成 Compose 容器端到端启动。
|
代理配置,未能在本机完成 Compose 容器端到端启动。
|
||||||
- Controller、Gateway、Checker、Loadgen、Provider Fleet、WorkerControlPlane、业务指标和
|
- Controller、Gateway、Checker、Loadgen、Provider Fleet、WorkerControlPlane、业务指标和
|
||||||
Redis 故障边界均有实现与定向测试。真实容器故障转移、Kubernetes 工作负载身份 Overlay
|
Redis 故障边界均有实现与定向测试。Kubernetes `development-mtls` Overlay 已静态验证
|
||||||
与代表性集群压测仍属于后续验证范围。
|
单副本固定身份链路;真实容器故障转移、生产工作负载身份与代表性集群压测仍属于后续验证范围。
|
||||||
- `implementation-plan.md` 当前按 74 个验收项统计;验收勾选数不等同于生产就绪度。
|
- `implementation-plan.md` 当前按 75 个验收项统计;验收勾选数不等同于生产就绪度。
|
||||||
Compose 本地 mTLS 控制面运行链已实现并通过配置、证书、单元与构建验证;受本机 Docker
|
Compose 本地 mTLS 控制面运行链已实现并通过配置、证书、单元与构建验证;受本机 Docker
|
||||||
Desktop 镜像 HTTPS 代理缺失影响,容器端到端启动仍待具备镜像网络的环境复核。
|
Desktop 镜像 HTTPS 代理缺失影响,容器端到端启动仍待具备镜像网络的环境复核。
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user