From 61bc4af1a0ec1d8a5e8be51debbd51b1aa2fed08 Mon Sep 17 00:00:00 2001 From: Mukul Sharma Date: Thu, 17 Sep 2026 09:31:26 +0530 Subject: [PATCH] Serve every tool on deployshed.com instead of nip.io MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Harbor, Gitea, Argo CD, Jenkins, Vault, Grafana and vmui now answer on their deployshed.com names alone. Each was already serving both while the move was proved out; this removes the nip.io half. The dual-hostname workarounds go with it. Jenkins' secondaryingress existed only because its chart's primary ingress takes one hostName and a certificate could not span both names — the real domain moves onto the primary with jenkins-tls, which it already holds. Argo CD gets extraTls rather than ingress.tls, because the boolean hardcodes secretName argocd-server-tls and would request a second certificate for a name that already has a valid one in argocd-deployshed-tls. Harbor also changes in two ways beyond the hostname: - externalURL moves to https://harbor.infra.deployshed.com. Harbor hands this to docker clients in its own API responses and builds the push commands shown in its UI from it, so a stale value is what makes a correctly-configured registry still advertise the old address. - updateStrategy is now Recreate. Its jobservice and registry volumes are standard-rwo (ReadWriteOnce), and a RollingUpdate starts the new pod before the old one releases the disk, so the replacement hangs forever on Multi-Attach. The cluster was sitting in exactly that state, old pods serving while new ones stayed in ContainerCreating. The chart's own comment on this value recommends Recreate when RWM is unavailable. The cost is a brief outage during upgrades, which beats a rollout that cannot complete. The private registry CA is not removed yet. Apps deployed before this move recorded nip.io image references that only change when each is rebuilt, so the old hostname stays served by a standalone Ingress until then. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N --- .../argocd-admin-prd/custom-values.yaml | 35 ++++++----- .../contour/custom-values.yaml | 14 +++-- .../gitea/custom-values.yaml | 22 +++---- .../grafana/custom-values.yaml | 9 +-- .../harbor/custom-values.yaml | 62 ++++++++++++++----- .../jenkins/custom-values.yaml | 28 ++++----- .../vault/custom-values.yaml | 5 +- .../custom-values.yaml | 6 +- 8 files changed, 106 insertions(+), 75 deletions(-) diff --git a/helm-overrides/gke-toolshed-prd-usc1/argocd-admin-prd/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/argocd-admin-prd/custom-values.yaml index f4f1ef2..4bf936b 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/argocd-admin-prd/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/argocd-admin-prd/custom-values.yaml @@ -81,21 +81,23 @@ argo-cd: ingress: enabled: true ingressClassName: contour - hostname: "argocd.35.238.248.203.nip.io" - # The chart supports additional hostnames natively, so the new domain - # is served here rather than from a second Ingress object. - extraHosts: - - name: "argocd.infra.deployshed.com" - path: / - # No TLS yet, deliberately. This chart's ingress.tls is a boolean, not - # a host list: turning it on requests ONE certificate covering - # `hostname` plus every extraHost, and Let's Encrypt cannot issue for - # nip.io — so the request would fail and neither name would be served - # over TLS. There is no extraTls to scope it more narrowly. + hostname: "argocd.infra.deployshed.com" + # TLS via extraTls rather than the `tls: true` boolean, deliberately. # - # This one gets its certificate when nip.io is retired and `hostname` - # itself becomes the deployshed.com name. Until then ArgoCD is HTTP - # only, as it already was. + # The boolean hardcodes `secretName: argocd-server-tls` (see the + # chart's argocd-server/ingress.yaml). This deployment already holds a + # valid, issued certificate for this exact hostname in + # argocd-deployshed-tls, created by the standalone Ingress that served + # the real domain while nip.io was still on `hostname`. Flipping the + # boolean would ignore that and request a second certificate for the + # same name — a needless issuance and a gap while it is obtained. + # + # extraTls takes an explicit secretName, so the existing certificate is + # adopted as-is and the standalone Ingress can simply be deleted. + extraTls: + - hosts: + - argocd.infra.deployshed.com + secretName: argocd-deployshed-tls resources: requests: cpu: 50m @@ -129,7 +131,10 @@ argo-cd: params: server.insecure: true cm: - url: "http://argocd.35.238.248.203.nip.io" + # https now that the only hostname served carries a real certificate. + # This is what ArgoCD builds its own links from, so leaving it http + # would hand out plain-HTTP URLs for a TLS-only deployment. + url: "https://argocd.infra.deployshed.com" timeout.reconciliation: 3m timeout.reconciliation.jitter: 60s # No Ingress health override, unlike the homelab: there Contour sat diff --git a/helm-overrides/gke-toolshed-prd-usc1/contour/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/contour/custom-values.yaml index a215b64..7607edb 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/contour/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/contour/custom-values.yaml @@ -35,10 +35,16 @@ contour: type: LoadBalancer # The reserved address from Terraform (module.network's - # google_compute_address). Every hostname in this deployment is - # .35.238.248.203.nip.io, so this pin is what makes DNS work at - # all — an unpinned Service takes a fresh ephemeral IP and every - # hostname points at nothing. + # google_compute_address). Every hostname in this deployment — the + # deployshed.com records, including the two wildcards — resolves here, + # so this pin is what makes DNS work at all: an unpinned Service takes + # a fresh ephemeral IP and every hostname points at nothing. + # + # More load-bearing now, not less, than when hostnames were + # .35.238.248.203.nip.io. Those encoded the address, so a changed + # IP produced names that were merely wrong. Real DNS records point here + # until somebody edits them in Cloudflare, so a changed IP is an + # outage across every hostname at once. # # spec.loadBalancerIP is deprecated upstream (Kubernetes 1.24), and # GKE's replacement is the annotation diff --git a/helm-overrides/gke-toolshed-prd-usc1/gitea/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/gitea/custom-values.yaml index eac2d81..42b2d31 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/gitea/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/gitea/custom-values.yaml @@ -43,7 +43,7 @@ gitea: actions: ENABLED: true server: - ROOT_URL: http://gitea.35.238.248.203.nip.io/ + ROOT_URL: https://gitea.infra.deployshed.com/ service: # The homelab sat on a LAN; this Gitea is on a public IP. Open # registration would let anyone on the internet create an account. @@ -69,7 +69,7 @@ gitea: # The proper fix is a configurable webhook base URL in toolshed # pointing at toolshed-api.toolshed.svc.cluster.local:8080, after # which this entry should be removed. - ALLOWED_HOST_LIST: private,toolshed.35.238.248.203.nip.io + ALLOWED_HOST_LIST: private,console.deployshed.com admin: username: gitadmin # Created by hand with kubectl at bootstrap, because Vault and ESO @@ -82,22 +82,20 @@ gitea: # Contour does not exist yet at bootstrap — the Ingress just sits unused # until ArgoCD installs it. # - # Two hosts while the deployment moves onto its own domain. The nip.io one - # stays until everything that references it has been repointed — ROOT_URL - # below, and any git remote anyone has configured. + # One host. The nip.io name was served alongside this one while the + # deployment moved onto its own domain, and came out once everything + # referencing it had been repointed: ROOT_URL above, the webhook allow-list + # above that, and any git remote anyone had configured. ingress: enabled: true className: contour annotations: - # Issues the certificate named in tls below. Only the real domain is - # listed there: Let's Encrypt cannot issue for nip.io, and asking for - # one certificate spanning both names returns nothing for either. + # Issues the certificate named in tls below. This could only ever cover + # the real domain: Let's Encrypt cannot issue for nip.io, so while both + # names were served, asking for one certificate spanning them returned + # nothing for either. cert-manager.io/cluster-issuer: letsencrypt-prod hosts: - - host: gitea.35.238.248.203.nip.io - paths: - - path: / - pathType: Prefix - host: gitea.infra.deployshed.com paths: - path: / diff --git a/helm-overrides/gke-toolshed-prd-usc1/grafana/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/grafana/custom-values.yaml index b5aacb1..c917a3d 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/grafana/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/grafana/custom-values.yaml @@ -268,13 +268,14 @@ grafana: enabled: true ingressClassName: contour annotations: - # Only the real domain appears in tls below — Let's Encrypt cannot - # issue for nip.io, and one certificate spanning both would fail - # outright rather than covering the half it can serve. + # The real domain is now the only host, so this covers everything + # served. While nip.io was alongside it, only the real domain could + # appear in tls below — Let's Encrypt cannot issue for nip.io, and one + # certificate spanning both would have failed outright rather than + # covering the half it could serve. cert-manager.io/cluster-issuer: letsencrypt-prod path: / hosts: - - grafana.35.238.248.203.nip.io - grafana.infra.deployshed.com tls: - secretName: grafana-tls diff --git a/helm-overrides/gke-toolshed-prd-usc1/harbor/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/harbor/custom-values.yaml index 6f452c5..eca94bb 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/harbor/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/harbor/custom-values.yaml @@ -6,41 +6,69 @@ harbor: # The homelab runs Harbor on plain HTTP and works around containerd's # refusal to pull from an insecure registry by hand-editing # /etc/containerd/certs.d//hosts.toml on the node. GKE nodes are - # managed and replaced, so that edit cannot survive. Instead the node pool - # is told, at creation, to trust a private CA for exactly this hostname, - # fetching it from Secret Manager (see toolshed-gke-infra's gke-cluster - # module). cert-manager signs Harbor's certificate from that same CA. + # managed and replaced, so that edit cannot survive. # - # Let's Encrypt is not an option: nip.io is not on the public suffix list, - # so every *.nip.io certificate on the internet shares one rate limit. - # A private CA is fine for image pulls, which is what matters, but - # browsers will warn on the Harbor UI. That is expected, not a fault. + # That used to mean a private CA: the registry was a nip.io name, Let's + # Encrypt cannot issue for one (not on the public suffix list, and every + # *.nip.io certificate shares a single rate limit), so Terraform generated + # a CA, the node pool was told at creation to trust it for exactly that + # hostname, and cert-manager signed Harbor from it. It worked for pulls, + # which is what mattered, but every browser warned on the Harbor UI and + # every docker client that was not a node needed the CA mounted by hand. + # + # Owning a domain removes all of it. harbor.infra.deployshed.com takes an + # ordinary Let's Encrypt certificate that everything already trusts — the + # nodes, dockerd in a build pod, a laptop, a browser. What retires with it: + # registry-ca-clusterissuer.yaml, registry-ca-configmap.yaml, the CA mount + # in every dind pod, and eventually the node pool's own + # private_registry_access_config block in Terraform. + # + # The nip.io name is still served, by a standalone Ingress alongside this + # one, and still signed by the private CA. It has to be: apps deployed + # before the move recorded their image as harbor.35.238.248.203.nip.io/... + # in toolshed's database, and that reference only changes when each app is + # rebuilt. Retiring the old name before then breaks their next image pull. expose: type: ingress tls: enabled: true # secret, not the chart's "auto": auto generates its own self-signed - # certificate, which the nodes have no reason to trust. This one is - # signed by the CA they were told to trust. + # certificate, which nothing has any reason to trust. certSource: secret secret: - secretName: harbor-tls + secretName: harbor-deployshed-tls ingress: hosts: - core: "harbor.35.238.248.203.nip.io" + core: "harbor.infra.deployshed.com" className: contour annotations: # cert-manager's ingress-shim watches for this and creates the # Certificate itself, writing the result into the secret named # above. Nothing here ever touches a Certificate resource directly. - # The issuer is defined in devops-infra-argo-config-gcp's - # extra-manifests/registry-ca-clusterissuer.yaml. - cert-manager.io/cluster-issuer: registry-ca-issuer + cert-manager.io/cluster-issuer: letsencrypt-prod + + # The update strategy for the two deployments with persistent volumes, + # jobservice and registry. Recreate, not the chart's default RollingUpdate, + # because their volumes are standard-rwo — ReadWriteOnce — and a rolling + # update starts the replacement pod before the old one releases the disk. + # The new pod then waits on "Multi-Attach error for volume ... already used + # by pod" forever, and the rollout never converges: this cluster sat in + # exactly that state, old pods serving while new ones hung in + # ContainerCreating. The chart's own comment on this value says to set it + # this way when RWM volumes are unavailable. + # + # The cost is honest: Harbor is briefly down during an upgrade, because the + # old pod must stop before the new one starts. That beats an update that + # cannot complete at all. + updateStrategy: + type: Recreate # https, matching the ingress above. Harbor hands this URL to docker # clients in its own API responses, so a mismatch here breaks pushes in - # ways that look like registry errors rather than configuration. - externalURL: "https://harbor.35.238.248.203.nip.io" + # ways that look like registry errors rather than configuration — and it is + # what the "docker login / docker push" commands shown in Harbor's own UI + # are built from, which is where a stale value is noticed first. + externalURL: "https://harbor.infra.deployshed.com" # From Vault through External Secrets — see # secretstores/harbor-admin-credentials.yaml and Vault path diff --git a/helm-overrides/gke-toolshed-prd-usc1/jenkins/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/jenkins/custom-values.yaml index 2514572..91e5bfc 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/jenkins/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/jenkins/custom-values.yaml @@ -55,27 +55,23 @@ jenkins: userKey: jenkins-admin-user passwordKey: jenkins-admin-password - ingress: - enabled: true - hostName: "jenkins.35.238.248.203.nip.io" - ingressClassName: contour + # One hostname, on the primary ingress, with its certificate. + # # This chart's primary ingress supports exactly one hostName — no - # extraHosts like argo-cd. secondaryingress renders a whole second - # Ingress object at the same backend, which is the supported way to get - # a second hostname here. + # extraHosts like argo-cd — so while nip.io was also served, the real + # domain lived in a whole second Ingress object (secondaryingress). That + # was the only way to keep the certificate clean, since a certificate + # covering both names is impossible: Let's Encrypt cannot issue for + # nip.io. With nip.io gone there is one name, so the second object is + # gone with it and the certificate moves onto the primary. # - # paths must be set explicitly: the template renders zero routes if left - # at the chart's own default of [], unlike the primary ingress, and the - # hostname would then answer nothing at all. - # - # Being a separate Ingress object is what makes the certificate clean — - # it covers this hostname alone, with no nip.io name to drag in. - secondaryingress: + # controller.ingress.tls is a LIST here, not a boolean, taking an + # explicit secretName — so jenkins-tls, already issued for this exact + # hostname by the secondary ingress, is adopted rather than reissued. + ingress: enabled: true hostName: "jenkins.infra.deployshed.com" ingressClassName: contour - paths: - - / annotations: cert-manager.io/cluster-issuer: letsencrypt-prod tls: diff --git a/helm-overrides/gke-toolshed-prd-usc1/vault/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/vault/custom-values.yaml index 23abb60..e17b0bf 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/vault/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/vault/custom-values.yaml @@ -84,11 +84,10 @@ vault: enabled: true ingressClassName: contour annotations: - # Certificate for the real domain only; nip.io cannot have one. + # Certificate for the real domain, which is now the only one served. + # nip.io could never have had one. cert-manager.io/cluster-issuer: letsencrypt-prod hosts: - - host: "vault.35.238.248.203.nip.io" - paths: [] - host: "vault.infra.deployshed.com" paths: [] tls: diff --git a/helm-overrides/gke-toolshed-prd-usc1/victoria-metrics-single/custom-values.yaml b/helm-overrides/gke-toolshed-prd-usc1/victoria-metrics-single/custom-values.yaml index 7b389d9..dcb0a9e 100644 --- a/helm-overrides/gke-toolshed-prd-usc1/victoria-metrics-single/custom-values.yaml +++ b/helm-overrides/gke-toolshed-prd-usc1/victoria-metrics-single/custom-values.yaml @@ -56,12 +56,10 @@ victoria-metrics-single: enabled: true ingressClassName: contour annotations: - # Certificate for the real domain only; nip.io cannot have one. + # Certificate for the real domain, now the only one served. nip.io + # could never have had one. cert-manager.io/cluster-issuer: letsencrypt-prod hosts: - - name: vm.35.238.248.203.nip.io - path: ["/"] - port: http - name: vm.infra.deployshed.com path: ["/"] port: http