Kubernetes 核心资源对象:DaemonSet、StatefulSet 与 Service
一、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-0、mysql-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 - externalTrafficPolicy:
Local保留源 IP,但可能分布不均 - 定期清理:删除不再使用的 Service,避免 Endpoint 泄漏
四、总结对比
| 特性 | DaemonSet | StatefulSet | Service |
|---|---|---|---|
| Pod 数量 | = 节点数 | = replicas | = Selector 匹配数 |
| Pod 标识 | 随机 | 固定序号 | 随机 |
| 存储 | 共享/无持久化 | 独立 PVC | 无状态 |
| 扩缩容 | 节点级 | 序号级 | 副本级 |
| 典型场景 | 日志、监控、网络 | 数据库、消息队列 | 无状态服务入口 |
更多推荐
所有评论(0)