Files

315 lines
7.1 KiB
Markdown
Raw Permalink Normal View History

# Networking: Ingress, TLS & Service Discovery
**Ingress Controller:** `nginx-ingress` (Nginx)
**Load Balancer:** Cilium LB-IPAM (eBPF-based)
**TLS CA:** homelab-ca (self-signed, 10-year)
**Namespace:** `ingress-nginx`
## When to Use
- **Public HTTPS endpoints** — External access via TLS
- **Hostname-based routing** — Multiple services on same IP
- **TLS termination** — Offload encryption/decryption
- **Service discovery** — Internal DNS (CoreDNS)
## Quick Start
**1. Create Ingress rule:**
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: myapp-ns
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod" # or homelab-ca
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.riotpiao.com
secretName: myapp-tls
rules:
- host: myapp.riotpiao.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 8080
```
**2. Deploy:**
```bash
kubectl apply -f ingress.yaml
# Wait for cert issuance
kubectl get certificate -n myapp-ns
# Should show "Ready" after ~30s
```
**3. Test from client:**
```bash
# Add to /etc/hosts (or use WireGuard)
192.168.1.160 myapp.riotpiao.com
# Access
curl https://myapp.riotpiao.com
```
## Configuration
| Key | Value |
|-----|-------|
| Ingress class | `nginx` |
| Load balancer type | `LoadBalancer` (Cilium LB-IPAM) |
| TLS issuer | `homelab-ca` (ClusterIssuer) |
| TLS cert lifetime | 90 days (auto-renewed by cert-manager) |
| DNS | CoreDNS (in-cluster), external via `/etc/hosts` or DuckDNS |
## Common Patterns
**Ingress with path-based routing:**
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api
namespace: default
spec:
ingressClassName: nginx
tls:
- hosts:
- api.riotpiao.com
secretName: api-tls
rules:
- host: api.riotpiao.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: users-svc
port:
number: 3000
- path: /orders
pathType: Prefix
backend:
service:
name: orders-svc
port:
number: 3001
```
**Ingress with basic auth:**
```bash
# Generate htpasswd
htpasswd -c auth admin
# → prompted for password
# Create Secret
kubectl create secret generic basic-auth --from-file=auth -n default
# Create Ingress
```
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: protected
namespace: default
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required'
spec:
ingressClassName: nginx
rules:
- host: protected.riotpiao.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: app-svc
port:
number: 8080
```
**Internal DNS (CoreDNS rewrite):**
```yaml
# k8s/coredns/coredns-configmap.yaml
# Rewrite:
# - grafana.riotpiao.com → grafana.logging (cluster-internal)
# - prometheus.riotpiao.com → prometheus-kube-prom-prometheus.monitoring
#
# Allows pods to use external URLs but resolve to internal Services
```
**Fixed LoadBalancer IP (Cilium LB-IPAM):**
```yaml
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
annotations:
io.cilium/lb-ipam-ips: "192.168.1.160" # fixed IP
spec:
type: LoadBalancer
selector:
app: nginx-ingress
ports:
- port: 443
targetPort: 443
protocol: TCP
```
## TLS Certificate Management
**Automatic renewal (cert-manager):**
```yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: myapp-cert
namespace: myapp-ns
spec:
secretName: myapp-tls
duration: 2160h # 90 days
renewBefore: 360h # renew 15 days before expiry
commonName: myapp.riotpiao.com
dnsNames:
- myapp.riotpiao.com
issuerRef:
name: homelab-ca
kind: ClusterIssuer
```
**Check certificate status:**
```bash
# List certs
k get certificate -A
# View cert details
k describe certificate -n myapp-ns myapp-cert
# View TLS Secret
k get secret -n myapp-ns myapp-tls -o json | jq '.data."tls.crt"' | base64 -d | openssl x509 -text
# Check expiry date
k get secret -n myapp-ns myapp-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -enddate
```
## Service Discovery
**Cluster-internal DNS:**
```bash
# From any pod, resolve via CoreDNS
nslookup grafana.logging.svc.cluster.local # full FQDN
nslookup grafana.logging # short form (same namespace)
nslookup grafana # if in logging namespace
# Resolved to ClusterIP (internal only)
```
**External DNS (WireGuard VPN or port-forward):**
```bash
# Option 1: WireGuard tunnel
# Client connects to 10.6.0.1 (WireGuard server on talos-cp-1)
# All traffic tunneled to cluster
# Option 2: Port-forward from jump box
make pf-grafana # localhost:3000 → grafana.logging:3000
# Option 3: Add to /etc/hosts (on home network)
192.168.1.160 grafana.riotpiao.com
```
## Monitoring
**Grafana dashboard:** `svc-nginx-ingress`
**Key metrics:**
- `nginx_requests_total` — total requests
- `nginx_request_duration_seconds` — latency histogram
- `nginx_ingress_upstream_requests_total{status=~"5.."}` — backend errors
- `nginx_ssl_expire_time_seconds` — cert expiry countdown
**Alert on cert expiry:**
```yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cert-expiry
namespace: ingress-nginx
spec:
groups:
- name: cert-expiry
rules:
- alert: CertificateExpiringSoon
expr: nginx_ssl_expire_time_seconds < 86400 * 14 # < 14 days
annotations:
summary: "Certificate {{ $labels.host }} expires in {{ $value | humanizeDuration }}"
```
## Troubleshooting
**Certificate stuck in "Pending":**
```bash
# Check cert-manager logs
k logs -n cert-manager -f deploy/cert-manager
# Verify ClusterIssuer exists
k get clusterissuer
# Check ACME order (if using LetsEncrypt)
k describe certificate -n myapp-ns myapp-cert
```
**Ingress not exposing service (503 error):**
```bash
# Verify Service exists and has endpoints
k get svc -n myapp-ns
k get endpoints -n myapp-ns myapp-svc
# Check if pods are ready
k get pods -n myapp-ns
# Test pod directly (port-forward)
k port-forward -n myapp-ns pod/myapp-0 8080:8080
curl http://localhost:8080
```
**DNS resolution fails from pod:**
```bash
# Test from pod
k run -it --rm debug --image=busybox:1.28 --restart=Never -- \
nslookup grafana.logging.svc.cluster.local
# If fails, CoreDNS may be unhealthy
k get pods -n kube-system -l k8s-app=kube-dns
k logs -n kube-system -l k8s-app=kube-dns
```
**TLS handshake error (cert not trusted):**
```bash
# Verify TLS cert Secret exists
k get secret -n myapp-ns myapp-tls
# Verify cert is correctly signed by homelab-ca
k get secret -n myapp-ns myapp-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text | grep -A 5 "Issuer:"
# If cert is self-signed (homelab-ca), add to client's trusted roots
# Or bypass cert verification (dev only):
curl -k https://myapp.riotpiao.com
```
See `/TROUBLESHOOTING.md` for full incident guide.