一、DaemonSet 控制器

1.1 简介与核心作用

DaemonSet 是一种工作负载控制器,确保集群中每个(或特定)节点都运行一份指定的 Pod 副本。当新节点加入集群时,DaemonSet 自动在该节点部署 Pod;当节点被移除时,对应 Pod 会被垃圾回收。

典型应用场景:

场景 说明
日志收集 Fluentd、Filebeat 采集节点日志
监控代理 node-exporter、Prometheus 节点监控
网络插件 Calico、Flannel 的节点组件
存储插件 Ceph、GlusterFS 的节点客户端

1.2 工作原理

plaintext

┌─────────────────────────────────────────────────────────────────────┐
│                         DaemonSet 调度机制                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   传统 Deployment:                                                   │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  Scheduler → 任意节点(资源充足即可)                          │   │
│   │         ↓                                                    │   │
│   │  [Node-A] [Node-B] [Node-C] ← 副本均匀分布,节点数不固定       │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   DaemonSet:                                                         │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │  调度策略: 每个节点必须运行 1 个 Pod                           │   │
│   │         ↓                                                    │   │
│   │  [Node-A:Pod] [Node-B:Pod] [Node-C:Pod]                      │   │
│   │      ✓           ✓           ✓                               │   │
│   └─────────────────────────────────────────────────────────────┘   │
│                                                                      │
│   ⚠️ 默认情况: DaemonSet Pod 由 DaemonSet Controller 直接绑定节点  │
│      (不经过默认 Scheduler),除非设置 spec.template.nodeSelector   │
└─────────────────────────────────────────────────────────────────────┘

1.3 关键 YAML 配置

yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  # 滚动更新策略
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1    # 最多同时删除/创建 1 个 Pod
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      # 容忍污点(可选)
      tolerations:
      - key: "node-role.kubernetes.io/master"
        operator: "Exists"
        effect: "NoSchedule"
      containers:
      - name: node-exporter
        image: prom/node-exporter:v1.6.1
        ports:
        - containerPort: 9100
          hostPort: 9100          # 绑定主机端口
        resources:
          requests:
            cpu: "100m"
            memory: "64Mi"
          limits:
            cpu: "200m"
            memory: "128Mi"
        volumeMounts:
        - name: proc
          mountPath: /host/proc
          readOnly: true
      volumes:
      - name: proc
        hostPath:
          path: /proc

1.4 常用操作命令

bash

# 创建 DaemonSet
kubectl apply -f daemonset.yaml

# 查看 DaemonSet
kubectl get ds -n monitoring
kubectl describe ds node-exporter -n monitoring

# 查看各节点 Pod 分布
kubectl get pods -n monitoring -o wide | grep node-exporter

# 更新镜像
kubectl set image ds/node-exporter node-exporter=prom/node-exporter:v1.7.0

# 查看滚动更新状态
kubectl rollout status ds/node-exporter

# 回滚到上一版本
kubectl rollout undo ds/node-exporter

# 指定节点部署(使用 nodeSelector)
kubectl label node k8s-node1 disk-type=ssd
# 配合 nodeSelector 实现选择性部署

1.5 常见问题与排查

Q1: 新节点未自动部署 DaemonSet Pod?

bash

# 排查步骤
kubectl get nodes -l disk-type=ssd          # 检查节点标签
kubectl describe daemonset node-exporter   # 查看选择器匹配
kubectl get events --field-selector involvedObject.name=<node-name>
journalctl -u kubelet | grep daemon         # 检查 kubelet 日志

Q2: 某些节点不想部署 DaemonSet?

使用污点(Taints) + 容忍(Tolerations) 机制:

yaml

# 节点添加污点
kubectl taint node k8s-master node-role.kubernetes.io/master:NoSchedule

# DaemonSet 配置容忍(大部分系统 DaemonSet 已默认配置)
tolerations:
- key: "node-role.kubernetes.io/master"
  operator: "Exists"
  effect: "NoSchedule"

Q3: 滚动更新失败?

bash

# 检查 Pod 状态
kubectl get pods -n monitoring -l app=node-exporter
kubectl logs <pod-name> -n monitoring

# 手动重启滚动更新
kubectl rollout restart ds/node-exporter -n monitoring

1.6 最佳实践

  • 资源限制:务必设置 CPU/内存 limits,避免单节点资源耗尽
  • 污点容忍:生产环境合理配置 tolerations,确保核心 DaemonSet 可调度
  • 健康检查:配置 livenessProbe,及时发现并重启异常 Pod
  • 滚动策略:使用 maxUnavailable: 1 控制更新节奏,避免服务中断
  • 使用 hostPort:如需暴露端口,优先使用 hostPort 而非 HostNetwork

二、StatefulSet 控制器

2.1 简介与核心作用

StatefulSet 是 Kubernetes 专门为有状态应用设计的控制器,提供以下核心能力:

特性 说明
稳定网络标识 Pod 名称、主机名固定,如 mysql-0mysql-1
有序部署/扩缩容 Pod 按序号顺序创建/删除
有序滚动更新 从序号最大的 Pod 开始更新
持久化存储 每个 Pod 绑定独立的 PVC,数据独立

典型应用场景:

  • 数据库:MySQL、PostgreSQL、MongoDB
  • 消息队列:Kafka、RabbitMQ
  • 分布式存储:Ceph、GlusterFS
  • 有状态微服务:需要固定身份的服务

2.2 工作原理

plaintext

┌─────────────────────────────────────────────────────────────────────┐
│                    StatefulSet Pod 命名与部署顺序                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  StatefulSet: mysql (replicas=3)                                   │
│                                                                      │
│  创建顺序: mysql-0 → mysql-1 → mysql-2 (按序号)                     │
│  ┌───────────┐  ┌───────────┐  ┌───────────┐                       │
│  │  mysql-0  │  │  mysql-1  │  │  mysql-2  │                       │
│  │ (Ready)   │  │ (Pending) │  │ (Pending) │                       │
│  └───────────┘  └───────────┘  └───────────┘                       │
│       ↓                                                            │
│  扩缩容顺序: mysql-2 → mysql-1 → mysql-0 (反向)                     │
│                                                                      │
│  Headless Service: mysql-headless                                  │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  DNS 记录 (无 ClusterIP,由客户端自己解析):                       │ │
│  │    mysql-0.mysql-headless.default.svc.cluster.local           │ │
│  │    mysql-1.mysql-headless.default.svc.cluster.local           │ │
│  │    mysql-2.mysql-headless.default.svc.cluster.local           │ │
│  └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

2.3 关键 YAML 配置

yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
  namespace: database
spec:
  serviceName: mysql-headless   # 必须匹配 Headless Service 名称
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  # 持久化存储模板
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "nfs-storage"
      resources:
        requests:
          storage: 20Gi
  template:
    metadata:
      labels:
        app: mysql
    spec:
      # 初始化容器(可选)
      initContainers:
      - name: init-mysql
        image: mysql:8.0
        command: ["bash", "-c", "echo 'init'"]
      containers:
      - name: mysql
        image: mysql:8.0
        ports:
        - containerPort: 3306
          name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "root123"
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        resources:
          requests:
            cpu: "500m"
            memory: "512Mi"
  # 滚动更新策略
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 1    # 序号 >= partition 的 Pod 才更新(用于金丝雀)

对应的 Headless Service:

yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
  namespace: database
spec:
  clusterIP: None      # Headless 关键配置
  selector:
    app: mysql
  ports:
  - port: 3306
    targetPort: 3306

2.4 常用操作命令

bash

# 创建 StatefulSet
kubectl apply -f statefulset.yaml

# 查看 StatefulSet
kubectl get statefulset -n database
kubectl describe statefulset mysql -n database

# 查看 Pod(观察序号)
kubectl get pods -n database -l app=mysql

# 查看 PVC(每个 Pod 对应独立 PVC)
kubectl get pvc -n database
kubectl get pvc -l app=mysql -n database

# 扩容
kubectl scale statefulset mysql --replicas=5 -n database

# 删除 StatefulSet(默认保留 PVC)
kubectl delete statefulset mysql -n database

# 删除 StatefulSet 并清理 PVC
kubectl delete statefulset mysql -n database --cascade=orphan
kubectl delete pvc --all -n database

# 滚动更新
kubectl rollout status statefulset/mysql -n database
kubectl rollout undo statefulset/mysql -n database

2.5 常见问题与排查

Q1: Pod 一直处于 Pending 状态?

bash

# 检查 PVC 绑定状态
kubectl get pvc -n database
kubectl describe pvc data-mysql-0 -n database

# 常见原因:StorageClass 不存在、PV 不足、存储插件异常
kubectl get storageclass
kubectl get pv | grep mysql

Q2: Pod 无法正常启动,陷入 CrashLoopBackOff?

bash

# 查看日志
kubectl logs mysql-0 -n database -p    # 上次失败日志
kubectl describe pod mysql-0 -n database

# 检查挂载路径权限
kubectl exec mysql-0 -n database -- ls -la /var/lib/mysql

Q3: 如何实现有序的优雅删除?

StatefulSet 按序号从大到小删除 Pod,Pod 删除前会等待后续 Pod 完全 Terminated:

bash

# 查看 Pod 终止进度
kubectl get pods -n database -w

# 手动触发优雅删除(不影响原有行为)
kubectl delete pod mysql-2 -n database

2.6 最佳实践

  • 始终配合 Headless Service:确保 Pod 可通过 DNS 直接访问
  • 使用 volumeClaimTemplates:为每个 Pod 分配独立存储,实现数据隔离
  • 合理设置 replicas:生产环境建议 ≥ 3 实现高可用
  • 配置 readinessProbe:确保流量仅发送到就绪的 Pod
  • 滚动更新注意 partition:通过 partition 实现金丝雀发布
  • 数据备份:StatefulSet 数据持久化,需配合定期备份策略

三、Service 资源对象  

3.1 简介与核心作用

Service 是 Kubernetes 核心的服务发现与负载均衡资源,为一组 Pod 提供稳定的虚拟 IP(ClusterIP)和 DNS 名称,屏蔽后端 Pod 的动态变化。

核心能力:

类型 说明
ClusterIP 集群内部访问,分配虚拟 IP
NodePort 通过节点端口暴露服务
LoadBalancer 借助云厂商 LB 暴露服务
ExternalName CNAME 映射外部域名

3.2 工作原理

plaintext

┌─────────────────────────────────────────────────────────────────────┐
│                         Service 负载均衡机制                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│   Service: my-app (ClusterIP: 10.96.45.123)                         │
│   ┌─────────────────────────────────────────────────────────────┐   │
│   │                                                              │   │
│   │   ┌─────────────┐   kube-proxy (iptables/ipvs)              │   │
│   │   │   Client    │──────────────┐                            │   │
│   │   └─────────────┘              │                            │   │
│   │                                 ▼                            │   │
│   │   ┌─────────────────────────────────────────────┐           │   │
│   │   │         iptables/IPVS 规则                    │           │   │
│   │   │   10.96.45.123:80 → Pod-0:80 (33%)          │           │   │
│   │   │   10.96.45.123:80 → Pod-1:80 (33%)          │           │   │
│   │   │   10.96.45.123:80 → Pod-2:80 (33%)          │           │   │
│   │   └─────────────────────────────────────────────┘           │   │
│   │                                 │                            │   │
│   └─────────────────────────────────┼────────────────────────────┘   │
│                                     │                                 │
│   ┌─────────────────────────────────┼────────────────────────────┐   │
│   │   ┌─────────┐  ┌─────────┐  ┌─────────┐                       │   │
│   │   │ Pod-0  │  │ Pod-1  │  │ Pod-2  │                       │   │
│   │   │ nginx  │  │ nginx  │  │ nginx  │   ←  Endpoint 列表     │   │
│   │   └─────────┘  └─────────┘  └─────────┘                       │   │
│   └───────────────────────────────────────────────────────────────┘   │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

3.3 关键 YAML 配置

ClusterIP Service:

yaml

apiVersion: v1
kind: Service
metadata:
  name: my-app
  namespace: default
  labels:
    app: my-app
spec:
  type: ClusterIP
  selector:
    app: my-app        # 匹配目标 Pod 标签
  ports:
  - name: http
    port: 80            # Service 端口
    targetPort: 8080    # Pod 端口
    protocol: TCP
  - name: https
    port: 443
    targetPort: 8443
    protocol: TCP

NodePort Service:

yaml

apiVersion: v1
kind: Service
metadata:
  name: my-app-nodeport
spec:
  type: NodePort
  selector:
    app: my-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    nodePort: 30080     # 可选,指定节点端口(30000-32767)

LoadBalancer Service(需云厂商支持):

yaml

apiVersion: v1
kind: Service
metadata:
  name: my-app-lb
  annotations:
    # 云厂商注解(如阿里云)
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-id: "lb-xxx"
spec:
  type: LoadBalancer
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 8080
  # 保留源 IP
  externalTrafficPolicy: Local

Headless Service(用于 StatefulSet):

yaml

apiVersion: v1
kind: Service
metadata:
  name: mysql-headless
spec:
  clusterIP: None      # 核心配置:无 ClusterIP
  selector:
    app: mysql
  ports:
  - port: 3306

3.4 常用操作命令

bash

# 创建 Service
kubectl apply -f service.yaml

# 查看 Service
kubectl get svc -A                    # 查看所有命名空间
kubectl get svc my-app -o wide

# 查看 Service 详情(含 Endpoints)
kubectl describe svc my-app

# 测试 Service 访问
kubectl run test --rm -it --image=busybox -- wget -O- http://my-app

# 端口转发(开发调试)
kubectl port-forward svc/my-app 8080:80

# 暴露已有 Deployment
kubectl expose deployment my-app --type=ClusterIP --port=80

# 编辑 Service
kubectl edit svc my-app

# 删除 Service
kubectl delete svc my-app

3.5 常见问题与排查

Q1: Service 无法访问,Endpoints 为空?

bash

# 检查 Endpoints
kubectl get endpoints my-app
kubectl describe svc my-app

# 常见原因:Selector 不匹配
# 验证 Pod 标签
kubectl get pods --show-labels | grep my-app

# 修复示例
kubectl label pods <pod-name> app=my-app --overwrite

Q2: ExternalName Service 不生效?

yaml

# 检查 CNAME 解析
kubectl run dns-test --rm -it --image=busybox -- nslookup mysql.external.com

Q3: NodePort/LoadBalancer 无法外部访问?

bash

# 检查防火墙
firewall-cmd --list-ports | grep 30080

# 检查节点监听
ss -tlnp | grep 30080

# 检查 kube-proxy 模式
kubectl configmap kube-proxy -n kube-system -o yaml | grep mode

# 建议:生产环境使用 IPVS 模式提升性能

3.6 最佳实践

  • 使用标签选择器:避免使用 selector: {} 空选择器(会匹配所有 Pod)
  • 命名端口:为端口定义 name,便于 Ingress、多端口 Service 管理
  • 健康检查:配合 Pod readinessProbe,确保流量仅发送到就绪 Pod
  • Session Affinity:如需会话保持,使用 sessionAffinity: ClientIP
  • externalTrafficPolicyLocal 保留源 IP,但可能分布不均
  • 定期清理:删除不再使用的 Service,避免 Endpoint 泄漏

四、总结对比

特性 DaemonSet StatefulSet Service
Pod 数量 = 节点数 = replicas = Selector 匹配数
Pod 标识 随机 固定序号 随机
存储 共享/无持久化 独立 PVC 无状态
扩缩容 节点级 序号级 副本级
典型场景 日志、监控、网络 数据库、消息队列 无状态服务入口

更多推荐