分类 Kubernetes 下的文章

K8s 流量入口选 Ingress Controller 是搭集群必经的题。本文对比 4 个主流选项。

一句话

  • Nginx Ingress:稳,文档全,默认选项
  • Traefik:现代,配置友好,中小规模
  • APISIX:性能强,插件多,国产
  • Istio Gateway:要 service mesh 就选

对比

维度 Nginx Traefik APISIX Istio
性能 最高
配置易用 中(annotation 多)
插件 极多
WebSocket / gRPC 支持 支持 支持 支持
动态配置 需 reload
社区
学习曲线

Nginx Ingress:默认选

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx --create-namespace \
  --set controller.replicaCount=3 \
  --set controller.resources.requests.cpu=200m \
  --set controller.resources.requests.memory=256Mi

优点:
- 最广泛使用,遇到问题 Google 都有答案
- annotations 几乎覆盖 nginx.conf 所有指令
- 生产稳定性最佳

缺点:
- 改 backend 要 reload nginx(短暂中断)
- annotations 写多了乱

适合:中大规模、保守业务。

Traefik:现代友好

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: api
spec:
  routes:
  - match: Host(`api.example.com`)
    kind: Rule
    services:
    - name: api
      port: 8080
    middlewares:
    - name: rate-limit

优点:
- 配置语法清晰(IngressRoute CRD)
- 自动发现 Docker / K8s 服务
- 自带 Let's Encrypt 证书签发

缺点:
- 性能比 Nginx 略低
- 生产场景没 Nginx 久经考验

适合:小到中规模、新项目。

APISIX:性能党

基于 OpenResty (Nginx + Lua),性能极致。

helm install apisix apisix/apisix \
  --namespace apisix --create-namespace \
  --set gateway.type=LoadBalancer

优点:
- 性能最高(QPS 比 Nginx Ingress 高 30%+)
- 插件市场丰富(限流、鉴权、灰度、AB 测试)
- 全动态配置,零 reload
- Dashboard UI 友好

缺点:
- 国内文档好、国外社区相对小
- 故障排查需要懂一些 Lua

适合:高 QPS、网关有复杂逻辑(流量染色、灰度发布)。

Istio Gateway:上 mesh 就用

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: api-gw
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
    - "*.example.com"

只在你已经/准备用 Istio service mesh 时才考虑,单纯做入口太重。

选型决策树

要 service mesh?
  └ Yes → Istio Gateway
  └ No → 业务规模?
          └ 小 → Traefik
          └ 中大 → 网关有复杂逻辑(限流/鉴权/灰度)?
                    └ Yes → APISIX
                    └ No → Nginx Ingress

教训:选 Ingress Controller 主要看团队熟悉度——你能 5 分钟定位故障的那个,就是最好的。

默认 K8s 任意 Pod 间互通,零安全。NetworkPolicy 是命名空间级别的 L3/L4 防火墙,本文给一套生产可用的策略集。

前提

NetworkPolicy 是声明式,但真正生效要 CNI 支持

  • Calico ✅
  • Cilium ✅
  • WeaveNet ✅
  • Flannel ❌(默认不支持,要加 Canal)
kubectl get pods -n kube-system | grep -E "calico|cilium"  # 确认 CNI

第一条:默认拒绝

每个 namespace 都先加一条 default deny:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

应用后整个 ns 所有 Pod 收发都断。然后按需放开。

放通同 namespace 内部

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: production
spec:
  podSelector: {}
  ingress:
  - from:
    - podSelector: {}

放通 DNS

90% 的应用都要 DNS,必须放:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: kube-system
    ports:
    - protocol: UDP
      port: 53
    - protocol: TCP
      port: 53

跨 namespace 调用(前端 → 后端)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-frontend
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: api
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend
      podSelector:
        matchLabels:
          app: web
    ports:
    - protocol: TCP
      port: 8080

注意 ingress 是作用在被访问方——这条 policy 部署在 backend ns。

调试技巧

策略加错了 Pod 突然连不上,怎么定位?

# 1. 看哪些 policy 作用在这个 Pod
kubectl get networkpolicy -A -o jsonpath='{.items[*].spec.podSelector}' | grep <label>

# 2. 用 cilium hubble 看实际流量
hubble observe --from-pod production/api --to-pod production/db

# 3. 临时关掉所有 policy 看是否就通了
kubectl delete networkpolicy --all -n production  # 危险

常见坑

  1. labels 改了 policy 失效:podSelector 是按 label 匹配,重命名了 label 要同步改
  2. namespaceSelector 需要 ns 有对应 label:默认 ns 没 label,K8s 1.22+ 自动加 kubernetes.io/metadata.name
  3. Egress 拒绝把 healthcheck 也封了:kubelet 探测来自 host network,需要放通节点 IP
  4. CNI 不支持 Egress:老版本 Calico 默认只支持 Ingress

教训:NetworkPolicy 先在 staging 跑两周,盯监控有没有断流,再推生产。

K8s 所有状态都在 etcd 里。etcd 坏了集群就死了,但 etcd 备份恢复其实不难。本文是完整流程。

备份

ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%F-%H%M).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

校验:

ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-xxx.db -w table

自动化(crontab)

0 2 * * * /usr/local/bin/etcd-backup.sh

脚本:

#!/bin/bash
BACKUP_DIR=/backup/etcd
mkdir -p $BACKUP_DIR
FILE=$BACKUP_DIR/etcd-$(date +%F-%H%M).db
ETCDCTL_API=3 etcdctl snapshot save $FILE \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 保留 7 天
find $BACKUP_DIR -name "etcd-*.db" -mtime +7 -delete

# rsync 异地
rsync -av $FILE backup@<backup-server>:/etcd-backups/

恢复(单节点 etcd)

# 1. 停 kube-apiserver 和 etcd
systemctl stop kube-apiserver
mv /etc/kubernetes/manifests/etcd.yaml /tmp/   # 如果是 kubeadm

# 2. 清理 etcd 数据
mv /var/lib/etcd /var/lib/etcd.bak

# 3. 恢复
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-xxx.db \
  --data-dir /var/lib/etcd \
  --name <node-name> \
  --initial-cluster <node-name>=https://<node-ip>:2380 \
  --initial-advertise-peer-urls https://<node-ip>:2380

# 4. 起服务
mv /tmp/etcd.yaml /etc/kubernetes/manifests/
systemctl start kube-apiserver

恢复(HA etcd 集群)

3 节点 etcd 恢复要在所有节点同时做:

# 在每个节点:
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-xxx.db \
  --data-dir /var/lib/etcd \
  --name <本节点名> \
  --initial-cluster node1=https://<n1ip>:2380,node2=https://<n2ip>:2380,node3=https://<n3ip>:2380 \
  --initial-advertise-peer-urls https://<本节点ip>:2380 \
  --initial-cluster-token <token>

# 同时启动所有 etcd

--initial-cluster-token 必须三个节点都一样且和原集群不同(避免脑裂)。

配合 Velero 做应用级备份

etcd 备份只保了 K8s 对象,PV 数据没保。生产再加 Velero:

velero install \
  --provider aws --bucket k8s-backup \
  --backup-location-config region=us-east-1 \
  --use-volume-snapshots=true

velero backup create daily-backup --include-namespaces production

Velero + etcd 双备份,定期演练恢复,是 K8s 生产标配。

教训:备份脚本写好后至少做一次完整恢复演练,没演练过的备份等同于没有。