# Kubernetes Network

> Read [`../../references/kubernetes.md`](../../references/kubernetes.md) before changing network resources. It defines the kubectl-only boundary, identity labels, name defaults, and ownership loop.

- Skill: `zeabur/kubernetes-network` (Agent Skill)
- Install (CLI): `npx skillmds@latest add zeabur/kubernetes-network`
- Raw SKILL.md: https://api.skillmd.com/api/skills/zeabur/kubernetes-network/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: zeabur (https://skillmd.com/u/zeabur)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/zeabur/kubernetes-network

---


Read [`../../references/kubernetes.md`](../../references/kubernetes.md) before changing network resources. It defines the kubectl-only boundary, identity labels, name defaults, and ownership loop.

## Resolve the traffic contract

Collect the protocol, target container port, requested public hostname or NodePort, path behavior, and TLS requirement. Inventory the controller, Service, selected Pods, EndpointSlices, and Ingresses before choosing a name or selector:

```sh
kubectl -n "$NS" get svc,ingress -l "zeabur_service_id=$ZEABUR_SERVICE_ID"
kubectl -n "$NS" get endpointslice -l "kubernetes.io/service-name=$SERVICE_NAME" -o wide
kubectl -n "$NS" get pods -l "zeabur_service_id=$ZEABUR_SERVICE_ID" -o wide
```

A Service selector is a traffic contract. Copy the selected workload's stable labels, normally `zeabur_type=user-service,zeabur_service_id=<service-id>`, and avoid deployment-specific labels when the Service must survive a rollout. Name the service `service-<service-id>` unless an existing resource supplies the canonical name.

Complete this step only when at least one Ready endpoint is known, or the user has explicitly requested creation before the workload is healthy.

## Internal Service

Create or update a `ClusterIP` Service with named ports. A named port avoids silently coupling the Service to a container port number.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-<service-id>
  namespace: environment-<environment-id>
  labels:
    zeabur_type: user-service
    zeabur_project_id: <project-id>
    zeabur_service_id: <service-id>
    zeabur_environment_id: <environment-id>
spec:
  type: ClusterIP
  selector:
    zeabur_type: user-service
    zeabur_service_id: <service-id>
  ports:
    - name: http
      protocol: TCP
      port: 8080
      targetPort: http
```

Kubernetes-native DNS is `service-<service-id>.$NS.svc.cluster.local`. The backend configures Pods with the search domain `$NS.zeabur.internal`, so `service-<service-id>.zeabur.internal` is a compatibility alias only when that custom DNS zone resolves in the target cluster. Verify both names from an existing service Pod; use the Kubernetes-native name when the alias is absent.

```sh
kubectl -n "$NS" exec "$POD" -- getent hosts "$SERVICE_NAME.$NS.svc.cluster.local"
kubectl -n "$NS" exec "$POD" -- getent hosts "$SERVICE_NAME.zeabur.internal"
```

## NodePort

Use `NodePort` only for direct node-address reachability. It does not create a hostname, provide TLS termination, or bypass node firewalls. Change an existing Service with a declarative fragment that preserves all ports and selectors; for a new Service set `type: NodePort` and omit `nodePort` so Kubernetes allocates a valid port.

```yaml
apiVersion: v1
kind: Service
metadata:
  name: service-<service-id>
  namespace: environment-<environment-id>
spec:
  type: NodePort
  ports:
    - name: tcp
      protocol: TCP
      port: 5432
      targetPort: 5432
```

After applying, obtain the allocated port and routable node address from cluster state. Test with the requested protocol from an authorized client; `kubectl port-forward` proves the Service/endpoints but not node firewall or load-balancer reachability.

```sh
kubectl -n "$NS" get service "$SERVICE_NAME" \
  -o custom-columns=NAME:.metadata.name,TYPE:.spec.type,PORT:.spec.ports[*].port,NODEPORT:.spec.ports[*].nodePort
kubectl get nodes -o wide
```

## Domain, Ingress, and TLS

A domain route is an `Ingress` rule: host, prefix path `/`, backend `service-<service-id>`, and the requested Service port. Select an existing ingress class or require the user to choose one; an Ingress without a controller has no external effect.

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: domain-<domain-id>
  namespace: environment-<environment-id>
  labels:
    zeabur_type: user-service
    zeabur_service_id: <service-id>
    zeabur_environment_id: <environment-id>
spec:
  ingressClassName: <class>
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: service-<service-id>
                port:
                  number: 8080
```

For TLS, reference an existing Secret or, only after confirming the `cert-manager.io` API exists, create a `Certificate` whose `secretName` is referenced by `spec.tls`. Wait for `Certificate.status.conditions` before declaring HTTPS ready.

Ingress establishes cluster routing. Public DNS is complete only when the authoritative zone points to the ingress controller address. Detect a configured `external-dns` controller before using its annotations; otherwise report the exact `status.loadBalancer.ingress` hostname/IP and the required A, AAAA, or CNAME record for the DNS owner to create.

```sh
kubectl get ingressclass
kubectl -n "$NS" get ingress "$INGRESS_NAME" -o wide
kubectl -n "$NS" describe ingress "$INGRESS_NAME"
kubectl -n "$NS" get certificate "$CERTIFICATE_NAME" 2>/dev/null
```

## Completion

Verify the full traffic chain in order: Ready Pod, EndpointSlice target, Service port, Ingress address, then DNS and HTTPS from an external client when requested. Report the canonical Service DNS name, custom `zeabur.internal` lookup result, exposed ports, ingress class/address, and outstanding DNS or certificate prerequisite. Do not equate an admitted Ingress with a live DNS record or a working TLS certificate.

