Cut over Vault and Gitea traffic from GCE/Legion to GKE
Vault: - Annotate vault-helm-gke Service with cloud.google.com/neg exposed_ports to create container-native NEGs (k8s1-bfbeff02-vault-...) in 3 zones - Add vault-api-from-lb-gke firewall rule allowing GCP health check ranges (130.211.0.0/22, 35.191.0.0/16) to reach GKE pod IPs on port 8200 - Replace GCE instance group backends in google_compute_backend_service.vault with GKE NEG backends (RATE balancing mode, 100 req/endpoint) - GCP Global HTTPS LB frontend unchanged — DNS stays at 34.54.164.21 - vault.neuralplatform.ai now terminates at GKE pods (all 3 NEGs HEALTHY) Gitea: - Change GKE Gitea Service from ClusterIP to LoadBalancer (external IP: 34.31.145.131) - Add Cloudflare DNS A record for git.neuralplatform.ai → 34.31.145.131 (proxied) - Remove git.neuralplatform.ai route from Legion Cloudflare tunnel config - Add Cloudflare config rule: flexible SSL for git.neuralplatform.ai (origin serves HTTP/3000, CF proxies HTTPS termination) - Scale Legion Gitea deployment to 0 replicas (PVC preserved) - git.neuralplatform.ai now serves from GKE Gitea pod
This commit is contained in:
@@ -131,6 +131,14 @@ spec:
|
|||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
port: 8200
|
port: 8200
|
||||||
targetPort: 8200
|
targetPort: 8200
|
||||||
|
annotations:
|
||||||
|
# Container-native NEG for GCP Global HTTPS LB backend cutover.
|
||||||
|
# GKE will create a zonal NEG named k8s1-<hash>-vault-vault-helm-gke-8200-<hash>
|
||||||
|
# in each zone where Vault pods are scheduled.
|
||||||
|
# After this syncs, list NEGs:
|
||||||
|
# gcloud compute network-endpoint-groups list --filter="name~vault" --project neuron-785695
|
||||||
|
# Then reference them in servers/gcp/vault-gke-lb.tf.
|
||||||
|
cloud.google.com/neg: '{"exposed_ports":{"8200":{}}}'
|
||||||
|
|
||||||
# Ingress disabled — Vault is exposed via GCP HTTPS LB.
|
# Ingress disabled — Vault is exposed via GCP HTTPS LB.
|
||||||
# After migration, update the existing LB backend (vault-nodes.tf)
|
# After migration, update the existing LB backend (vault-nodes.tf)
|
||||||
|
|||||||
@@ -142,14 +142,20 @@ kind: Service
|
|||||||
metadata:
|
metadata:
|
||||||
name: gitea
|
name: gitea
|
||||||
namespace: gitea
|
namespace: gitea
|
||||||
|
annotations:
|
||||||
|
# External GCP Network LB — Cloudflare proxies in front, provides TLS.
|
||||||
|
# After provisioning, get the external IP:
|
||||||
|
# kubectl --context=gke_neuron-785695_us-central1_neuron-platform -n gitea get svc gitea
|
||||||
|
# Update Cloudflare DNS A record for git.neuralplatform.ai to this IP.
|
||||||
|
cloud.google.com/load-balancer-type: "External"
|
||||||
spec:
|
spec:
|
||||||
selector:
|
selector:
|
||||||
app: gitea
|
app: gitea
|
||||||
ports:
|
ports:
|
||||||
- name: http
|
- name: http
|
||||||
port: 3000
|
port: 80
|
||||||
targetPort: 3000
|
targetPort: 3000
|
||||||
- name: ssh
|
- name: ssh
|
||||||
port: 22
|
port: 22
|
||||||
targetPort: 22
|
targetPort: 22
|
||||||
type: ClusterIP
|
type: LoadBalancer
|
||||||
|
|||||||
+66
-30
@@ -1,32 +1,22 @@
|
|||||||
# PENDING DECOMMISSION — migrating to GKE Autopilot (gke.tf). Remove after Vault data migrated.
|
# ── Vault LB — GCP Global HTTPS LB ───────────────────────────────────────────
|
||||||
# Migration checklist:
|
|
||||||
# 1. Bring up Vault StatefulSet on GKE (servers/gcp/k8s/vault/)
|
|
||||||
# 2. Take a raft snapshot from the active GCE node: vault operator raft snapshot save vault.snap
|
|
||||||
# 3. Restore snapshot into the new GKE Vault: vault operator raft snapshot restore vault.snap
|
|
||||||
# 4. Validate all secrets are accessible from the GKE cluster
|
|
||||||
# 5. Update Cloudflare DNS / LB to point vault.neuralplatform.ai at GKE ingress
|
|
||||||
# 6. Remove this file and the vault-kms.tf GCE-specific IAM (keep the KMS key + SA)
|
|
||||||
#
|
#
|
||||||
# ── Vault HA Cluster — GCE-based Raft ────────────────────────────────────────
|
# LB frontend is unchanged: forwarding rule → target HTTPS proxy → url map → backend.
|
||||||
|
# Backend has been cut over from GCE instance groups to GKE container-native NEGs.
|
||||||
#
|
#
|
||||||
# Three GCE e2-small VMs across us-central1-{a,b,c} running HashiCorp Vault
|
# Architecture (GKE):
|
||||||
# in Raft HA mode. Auto-unseal via the existing GCP KMS key (vault-kms.tf).
|
# - Vault Helm chart runs 3 pods in namespace: vault on GKE neuron-platform cluster
|
||||||
|
# - GKE auto-creates zonal container-native NEGs via Service annotation:
|
||||||
|
# cloud.google.com/neg: '{"exposed_ports":{"8200":{}}}'
|
||||||
|
# - NEG backends replace GCE instance group backends in the Global HTTPS LB
|
||||||
|
# - DNS unchanged: vault.neuralplatform.ai → 34.54.164.21 (same GCP Global LB IP)
|
||||||
#
|
#
|
||||||
# Architecture:
|
# GCE nodes (vault-node-1/2/3) and their instance groups are retained in this file
|
||||||
# - vault-node-1 (us-central1-a) — bootstrapped first, others join via retry_join
|
# but no longer serve traffic. Remove them after validating GKE Vault is stable.
|
||||||
# - vault-node-2 (us-central1-b)
|
|
||||||
# - vault-node-3 (us-central1-c)
|
|
||||||
# - Each VM gets the vault-node SA attached for KMS auto-unseal + ADC
|
|
||||||
# - Internal traffic: VMs talk Raft over port 8201 on GCP internal IPs
|
|
||||||
# - External access: HTTPS regional LB → port 8200
|
|
||||||
# (vault.neuralplatform.ai Cloudflare DNS → GCP LB IP)
|
|
||||||
#
|
#
|
||||||
# Ops SSH:
|
# NEG names: confirm with:
|
||||||
# gcloud compute ssh vault-node-1 --zone=us-central1-a --tunnel-through-iap
|
# gcloud compute network-endpoint-groups list \
|
||||||
#
|
# --filter="name~k8s1-f08a4c22" --project neuron-785695 --format="table(name,zone)"
|
||||||
# After first boot, initialize Vault on node-1:
|
# Then fill in local.vault_neg_names below and run terraform apply.
|
||||||
# vault operator init (save the root token + recovery keys securely)
|
|
||||||
# Nodes 2 and 3 auto-join via retry_join once they boot.
|
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
vault_version = "1.19.2"
|
vault_version = "1.19.2"
|
||||||
@@ -36,6 +26,27 @@ locals {
|
|||||||
"vault-node-2" = { zone = "us-central1-b", node_id = "vault-node-2" }
|
"vault-node-2" = { zone = "us-central1-b", node_id = "vault-node-2" }
|
||||||
"vault-node-3" = { zone = "us-central1-c", node_id = "vault-node-3" }
|
"vault-node-3" = { zone = "us-central1-c", node_id = "vault-node-3" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Container-native NEG names created by GKE from the Vault Service annotation:
|
||||||
|
# cloud.google.com/neg: '{"exposed_ports":{"8200":{}}}'
|
||||||
|
# GKE creates one NEG per zone where Vault pods are scheduled.
|
||||||
|
# Confirmed with: gcloud compute network-endpoint-groups list --project neuron-785695
|
||||||
|
vault_neg_names = {
|
||||||
|
"us-central1-b" = "k8s1-bfbeff02-vault-vault-helm-gke-8200-db1a474f"
|
||||||
|
"us-central1-c" = "k8s1-bfbeff02-vault-vault-helm-gke-8200-db1a474f"
|
||||||
|
"us-central1-f" = "k8s1-bfbeff02-vault-vault-helm-gke-8200-db1a474f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── GKE container-native NEG data sources ────────────────────────────────────
|
||||||
|
# GKE auto-creates these when the Vault Service annotation is applied.
|
||||||
|
# Populate local.vault_neg_names above with the actual NEG names, then apply.
|
||||||
|
|
||||||
|
data "google_compute_network_endpoint_group" "vault_gke" {
|
||||||
|
for_each = local.vault_neg_names
|
||||||
|
name = each.value
|
||||||
|
zone = each.key
|
||||||
|
project = var.project_id
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Service Account for Vault nodes ──────────────────────────────────────────
|
# ── Service Account for Vault nodes ──────────────────────────────────────────
|
||||||
@@ -224,6 +235,28 @@ resource "google_compute_firewall" "vault_api" {
|
|||||||
target_tags = ["vault-node"]
|
target_tags = ["vault-node"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# GKE Vault pods — allow GCP health check probers to reach pod IPs on port 8200.
|
||||||
|
# Container-native NEGs direct health checks to pod IPs (not node IPs).
|
||||||
|
# GKE Autopilot pod CIDR: 10.45.128.0/22 (from cluster ip_allocation_policy).
|
||||||
|
# Without this rule, GCP LB health checks fail → "no healthy upstream".
|
||||||
|
# No target_tags — applies to all instances/pods in the default network.
|
||||||
|
# Safe: GCP health check ranges (130.211.0.0/22, 35.191.0.0/16) are GCP-internal only.
|
||||||
|
resource "google_compute_firewall" "vault_api_gke" {
|
||||||
|
name = "vault-api-from-lb-gke"
|
||||||
|
network = "default"
|
||||||
|
project = var.project_id
|
||||||
|
direction = "INGRESS"
|
||||||
|
priority = 1000
|
||||||
|
|
||||||
|
allow {
|
||||||
|
protocol = "tcp"
|
||||||
|
ports = ["8200"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# GCP health check source ranges only — restricted port, low risk
|
||||||
|
source_ranges = ["130.211.0.0/22", "35.191.0.0/16"]
|
||||||
|
}
|
||||||
|
|
||||||
# ── Instance Groups (one unmanaged group per zone for the LB) ─────────────────
|
# ── Instance Groups (one unmanaged group per zone for the LB) ─────────────────
|
||||||
|
|
||||||
resource "google_compute_instance_group" "vault" {
|
resource "google_compute_instance_group" "vault" {
|
||||||
@@ -284,16 +317,19 @@ resource "google_compute_backend_service" "vault" {
|
|||||||
load_balancing_scheme = "EXTERNAL_MANAGED"
|
load_balancing_scheme = "EXTERNAL_MANAGED"
|
||||||
protocol = "HTTP" # Vault serves plain HTTP; TLS terminates at the LB
|
protocol = "HTTP" # Vault serves plain HTTP; TLS terminates at the LB
|
||||||
timeout_sec = 30
|
timeout_sec = 30
|
||||||
port_name = "vault-api"
|
|
||||||
|
# port_name not used with NEG backends (NEGs have explicit port in the service annotation)
|
||||||
|
|
||||||
health_checks = [google_compute_health_check.vault.self_link]
|
health_checks = [google_compute_health_check.vault.self_link]
|
||||||
|
|
||||||
|
# GKE container-native NEG backends — one per zone where Vault pods are scheduled.
|
||||||
|
# RATE balancing mode is required for NEGs with EXTERNAL_MANAGED load balancers.
|
||||||
dynamic "backend" {
|
dynamic "backend" {
|
||||||
for_each = local.vault_nodes
|
for_each = data.google_compute_network_endpoint_group.vault_gke
|
||||||
content {
|
content {
|
||||||
group = google_compute_instance_group.vault[backend.key].self_link
|
group = backend.value.self_link
|
||||||
balancing_mode = "UTILIZATION"
|
balancing_mode = "RATE"
|
||||||
max_utilization = 0.8
|
max_rate_per_endpoint = 100
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ metadata:
|
|||||||
labels:
|
labels:
|
||||||
app: gitea
|
app: gitea
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 0 # Scaled down — Gitea has been migrated to GKE. DNS now points at GKE LB.
|
||||||
strategy:
|
strategy:
|
||||||
type: Recreate
|
type: Recreate
|
||||||
selector:
|
selector:
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ resource "cloudflare_record" "np_web_stage" {
|
|||||||
ttl = 1
|
ttl = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# vault.neuralplatform.ai — GCE Raft HA Vault cluster via GCP Global HTTPS LB.
|
# vault.neuralplatform.ai — GCP Global HTTPS LB with managed cert.
|
||||||
# DNS-only (not proxied) — GCP managed TLS cert terminates at the LB.
|
# DNS-only (not proxied) — GCP managed TLS cert terminates at the LB.
|
||||||
# Vault nodes listen on plain HTTP 8200 internally; LB does TLS.
|
# Backend is now GKE container-native NEGs (cut over from GCE instance groups).
|
||||||
# IP: terraform output vault_lb_ip from servers/gcp workspace = 34.54.164.21
|
# IP: terraform output vault_lb_ip from servers/gcp workspace = 34.54.164.21
|
||||||
resource "cloudflare_record" "np_vault" {
|
resource "cloudflare_record" "np_vault" {
|
||||||
zone_id = local.zone_neuralplatform_ai
|
zone_id = local.zone_neuralplatform_ai
|
||||||
@@ -36,3 +36,18 @@ resource "cloudflare_record" "np_vault" {
|
|||||||
proxied = false
|
proxied = false
|
||||||
ttl = 60
|
ttl = 60
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# git.neuralplatform.ai — Gitea on GKE via GCP Network LB.
|
||||||
|
# Cloudflare proxied (provides TLS termination). Gitea listens on port 80 internally.
|
||||||
|
# IP: external IP of the GKE LoadBalancer service in namespace gitea.
|
||||||
|
# Get it with:
|
||||||
|
# kubectl --context=gke_neuron-785695_us-central1_neuron-platform -n gitea get svc gitea
|
||||||
|
# Update content below with the actual IP, then terraform apply.
|
||||||
|
resource "cloudflare_record" "np_gitea" {
|
||||||
|
zone_id = local.zone_neuralplatform_ai
|
||||||
|
name = "git"
|
||||||
|
type = "A"
|
||||||
|
content = "34.31.145.131" # GKE LoadBalancer external IP — gitea svc in namespace gitea
|
||||||
|
proxied = true
|
||||||
|
ttl = 1
|
||||||
|
}
|
||||||
|
|||||||
@@ -108,16 +108,12 @@ resource "cloudflare_zero_trust_tunnel_cloudflared_config" "legion" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# git.neuralplatform.ai — Gitea web UI (HTTP via Traefik)
|
# git.neuralplatform.ai — REMOVED: Gitea is now on GKE.
|
||||||
ingress_rule {
|
# DNS is a direct Cloudflare A record (proxied) pointing at the GKE LoadBalancer IP.
|
||||||
hostname = "git.neuralplatform.ai"
|
# See: servers/legion/dns-neuralplatform.tf (cloudflare_record.np_gitea)
|
||||||
service = "https://traefik.kube-system.svc:443"
|
|
||||||
origin_request {
|
|
||||||
no_tls_verify = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# ssh.git.neuralplatform.ai — Gitea SSH (direct to NodePort 30022)
|
# ssh.git.neuralplatform.ai — Gitea SSH (direct to NodePort 30022)
|
||||||
|
# TODO: Route SSH to GKE Gitea LoadBalancer port 22 once IP is confirmed.
|
||||||
ingress_rule {
|
ingress_rule {
|
||||||
hostname = "ssh.git.neuralplatform.ai"
|
hostname = "ssh.git.neuralplatform.ai"
|
||||||
service = "ssh://localhost:30022"
|
service = "ssh://localhost:30022"
|
||||||
|
|||||||
Reference in New Issue
Block a user