1. Deployment为什么不适合部署有状态服务?

Deployment 在 Kubernetes 中是一种用于管理无状态应用的控制器。它不适合部署有状态服务的原因主要包括以下几点:

  1. 缺乏稳定的网络标识

    • Deployment 管理的 Pod 是无状态且不可区分的,它们没有固定的、稳定的网络标识(如 Pod 名称或 IP 地址)。当 Pod 被删除或重新调度时,其名称和 IP 会改变,这对于需要稳定标识的有状态服务来说是不合适的。

  2. 没有独立的存储卷绑定

    • Deployment 中的每个 Pod 通常共享相同的配置和存储策略。对于有状态服务来说,每个实例通常需要绑定到特定的持久化存储卷(Persistent Volume, PV),以确保数据在 Pod 重启后仍然保留。而 Deployment 无法为每个 Pod 提供这种独立的存储绑定机制。

  3. 启动顺序依赖

    • 有状态服务通常需要按照特定的顺序启动(例如主从架构中,主节点必须先于从节点启动)。Deployment 不支持控制 Pod 的启动顺序,而 StatefulSet 可以保证有序的启动和终止过程。

  4. 唯一性和可预测性

    • Deployment 管理的 Pod 是完全对等的,不能保证唯一性。而有状态服务通常要求每个实例具有唯一的身份,并且能够被其他服务或实例明确识别。StatefulSet 通过为每个 Pod 分配一个稳定的主机名和序号来满足这一需求。

  5. 滚动更新策略的局限性

    • Deployment 默认采用滚动更新策略,适用于无状态服务。但在有状态服务中,更新可能需要更加谨慎地处理,例如逐个更新实例并确保数据一致性。StatefulSet 提供了更可控的滚动更新方式,可以按序更新每个实例。


2. 什么是StatefulSet?解决了哪些问题?

StatefulSet 是 Kubernetes 中的一种控制器,专门用于管理有状态的应用。它为每个 Pod 提供了稳定的、唯一的网络标识和存储,并保证了 Pod 的启动顺序和终止顺序。StatefulSet 特别适合需要持久化数据、稳定标识以及有序操作的分布式系统,例如数据库集群(如 MySQL 主从架构)、分布式存储系统(如 ZooKeeper、Kafka)等。


StatefulSet 解决的问题

  1. 稳定的网络标识

    • 每个由 StatefulSet 管理的 Pod 都有一个稳定的主机名和 DNS 记录,格式为 <statefulset-name>-<ordinal>,例如 mysql-0, mysql-1

    • 这些名称是按序生成的,并且在 Pod 被删除或重新调度后保持不变,确保服务实例可以被其他组件准确识别。

  2. 独立的存储卷绑定

    • StatefulSet 为每个 Pod 分配独立的 PVC(Persistent Volume Claim),并绑定到特定的 PV 上。

    • 即使 Pod 被删除或重新创建,只要 PVC 不被删除,Pod 就能再次挂载相同的存储卷,从而保留数据。

  3. 有序的启动与终止

    • StatefulSet 保证了 Pod 的启动顺序,比如 mysql-0 必须先于 mysql-1 启动。

    • 在终止时,也是按照反向顺序进行,例如 mysql-1 先终止,然后才是 mysql-0

  4. 滚动更新的可控性

    • StatefulSet 支持滚动更新策略,但更新过程是按序进行的,确保在更新过程中系统的可用性和一致性。

    • 更新失败时,支持回滚机制。

  5. 唯一性保障

    • StatefulSet 保证每个 Pod 实例在整个生命周期内具有唯一的身份(通过序号标识),适用于需要明确区分每个实例的场景。


StatefulSet 的典型使用场景

  • 分布式数据库集群:如 MySQL 主从、PostgreSQL 流复制。

  • 消息中间件:如 Kafka、RabbitMQ 集群。

  • 协调服务:如 ZooKeeper、Etcd。


3. StatefulSet中如何保证Pod名称、存储、顺序稳定?

在 Kubernetes 中,StatefulSet 通过以下机制保证 Pod 名称、存储和启动/终止顺序的稳定性,特别适用于有状态应用的部署需求。


1. Pod 名称的稳定性

  • StatefulSet 创建的每个 Pod 都具有固定的、有序的主机名,格式为:<statefulset-name>-<ordinal>

    • 示例:如果 StatefulSet 名为 mysql,创建了 3 个副本,则 Pod 名为 mysql-0, mysql-1, mysql-2

  • 这些名称是按序生成且不会改变的,即使 Pod 被删除或重新调度,其名称也不会变化。

优势:

  • 其他服务可以通过固定 DNS 名(如 mysql-0.mysql, mysql-1.mysql)直接访问特定实例。

  • 适用于主从复制、分布式系统等需要唯一标识的场景。


2. 独立的持久化存储

  • 每个 Pod 可绑定一个独立的 PersistentVolumeClaim (PVC),确保数据在 Pod 重启或迁移后依然保留。

  • PVC 的命名格式为 <volume-claim-template-name>-<pod-name>,例如 data-mysql-0, data-mysql-1

spec:
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

优势:

  • 数据与 Pod 生命周期解耦,Pod 删除后 PVC 和 PV 仍存在,新 Pod 启动时可挂载原有数据。

  • 适用于数据库、日志服务等需要持久化数据的应用。


3. 有序的启动与终止

  • StatefulSet 保证 Pod 的启动和终止顺序:

    • 启动顺序0 -> 1 -> 2 ... N

    • 终止顺序N -> N-1 -> ... -> 0

应用场景:

  • 主从架构:主节点必须先于从节点启动,确保集群初始化正确。

  • ZooKeeper/Kafka:节点之间有依赖关系,需按序启动以完成选举或同步。

  • 分布式数据库:如 MongoDB 分片集群,需要有序加入集群。


4. 健康检查与滚动更新控制

  • StatefulSet 支持滚动更新策略,但更新是逐个进行的,并等待前一个 Pod 状态变为 Ready 后再继续下一个。

  • 更新失败时支持回滚。

配置示例:

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0

优势:

  • 更加可控的更新过程,避免所有 Pod 同时更新导致服务中断。

  • 支持金丝雀发布、灰度升级等高级部署模式。


5. 稳定的网络标识(DNS 解析)

  • Kubernetes 会自动为每个 Pod 创建稳定的 DNS 记录:

    • <pod-name>.<headless-service-name>.<namespace>.svc.cluster.local

    • 示例:mysql-0.mysql.default.svc.cluster.local

优势:

  • 即使 Pod 被重新调度,只要使用相同的 Headless Service,其他组件仍然可以准确访问对应实例。


总结

功能

StatefulSet 如何保障

Pod 名称稳定性

固定命名规则(<name>-<ordinal>

存储独立性

每个 Pod 绑定独立 PVC

启动顺序

严格按序启动(0 → 1 → 2)

终止顺序

逆序终止(2 → 1 → 0)

滚动更新控制

串行更新,确保前一个 Pod Ready 再更新下一个

网络标识稳定性

固定 DNS 记录


适用场景

  • 分布式数据库(MySQL 主从、PostgreSQL 流复制)

  • 消息中间件(Kafka、RabbitMQ 集群)

  • 协调服务(ZooKeeper、Etcd)

  • 文件存储系统(Ceph、GlusterFS)

因此,StatefulSet 是 Kubernetes 中管理有状态服务的最佳实践,通过上述机制实现了 Pod 名称、存储和顺序的稳定性,满足了对数据一致性、服务发现和部署顺序的高要求。


4. StatefulSet启动和删除Pod的顺序是怎样的?

StatefulSet 在 Kubernetes 中管理 Pod 的启动和删除顺序时,遵循严格的 有序控制机制,确保有状态应用在部署、扩缩容或终止过程中保持一致性。


1. 启动 Pod 的顺序

  • StatefulSet 会按照 序号递增的顺序 启动 Pod:

    • 第一个 Pod(<statefulset-name>-0)必须成功启动并进入 Ready 状态后,才会启动下一个 Pod(<statefulset-name>-1)。

    • 直到达到期望的副本数。

示例:

如果 StatefulSet 名为 mysql,设置副本数为 3,则启动顺序为:

mysql-0 → mysql-1 → mysql-2

原因:

  • 适用于主从架构、分布式数据库等需要先启动主节点再启动从节点的场景。

  • 确保每个实例启动时依赖的前序服务已就绪。


2. 删除 Pod 的顺序

  • 当缩减副本数或删除 StatefulSet 时,Kubernetes 会按照 序号递减的顺序 终止 Pod:

    • 最后一个 Pod(<statefulset-name>-N)会被首先终止,然后依次向前推进。

示例:

对于 mysql StatefulSet,当前副本数为 3,在缩减到 1 时,删除顺序为:

mysql-2 → mysql-1 → mysql-0

原因:

  • 避免破坏核心服务(如主节点)在前序操作中被提前终止。

  • 保证数据一致性,尤其在使用持久化存储的情况下。


3. 为什么需要有序控制?

  • 有状态服务依赖拓扑结构:例如 ZooKeeper、Etcd、Kafka 等系统要求节点之间存在明确的选举顺序或依赖关系。

  • 数据一致性保障:Pod 被删除后,其绑定的 PVC 数据不会自动清除,重新创建 Pod 时仍可挂载原有数据。

  • 滚动更新安全控制:StatefulSet 支持滚动更新策略,但每次只更新一个 Pod,并等待前一个更新完成后再继续。

总结

操作类型

执行顺序

启动 Pod

<name>-0<name>-1 → ...

删除 Pod

<name>-N<name>-(N-1) → ...

StatefulSet 的这种有序控制机制是其区别于 Deployment 的关键特性之一,特别适合需要稳定身份标识、独立存储以及严格启动/终止顺序的有状态服务。


5. StatefulSet必须依赖哪种类型的Volume?为什么?

StatefulSet 必须依赖具有稳定存储能力的 Volume 类型,通常推荐使用 PersistentVolume(PV) + PersistentVolumeClaim(PVC) 的组合。


1. 为什么 StatefulSet 需要特定类型的 Volume?

StatefulSet 是 Kubernetes 中用于管理有状态应用的控制器,其核心特性包括:

  • 每个 Pod 具有稳定的、唯一的网络标识和存储。

  • Pod 被删除或重新调度后,数据必须保留并能被新 Pod 挂载。

  • 数据与 Pod 生命周期解耦。

因此,StatefulSet 必须使用能够提供以下特性的 Volume:

特性

描述

持久化存储

数据不会因 Pod 删除而丢失。

唯一绑定

每个 Pod 对应一个独立的 PVC,确保数据隔离。

可迁移性

支持跨节点挂载,保证 Pod 调度到其他节点时仍能访问原数据。


6. 创建一个StatefulSet部署3个副本的Nginx,并挂载持久卷

---
# Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: http
  clusterIP: None
  selector:
    app: nginx

---
# StatefulSet definition
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  serviceName: nginx
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
          name: http
        volumeMounts:
        - name: www-data
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www-data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi


7. 如何为StatefulSet创建对应的Headless Service?作用是什么?

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: http
  clusterIP: None   # 关键字段:设置为 None 表示这是一个 Headless Service
  selector:
    app: nginx      # 必须与 StatefulSet 中的 Pod 标签选择器匹配

Headless Service 的作用

1. 稳定的网络标识

  • 每个 Pod 被分配一个固定的主机名,格式为 <statefulset-name>-<ordinal>

    • 例如:nginx-0, nginx-1, nginx-2

  • 提供稳定的 DNS 记录:

    • <pod-name>.<service-name>.<namespace>.svc.cluster.local

    • 例如:nginx-0.nginx.default.svc.cluster.local

2. Pod 发现机制

  • 其他服务可以通过 DNS 查询获取所有 Pod 的 IP 地址列表。

  • 可用于构建主从架构、分布式系统(如 ZooKeeper、MySQL 主从等)。

3. 支持有序部署与访问

  • StatefulSet 启动顺序依赖 Headless Service 提供的稳定网络拓扑。

  • 适用于需要按序启动、唯一身份识别的有状态应用。

4. 不进行负载均衡

  • 与普通 Service 不同,Headless Service 不做负载均衡或流量代理,客户端需自行处理多实例访问逻辑。


8. 使用busybox访问StatefulSet中每个Pod的主机名,观察其DNS规则

使用 BusyBox 测试 DNS 解析

你可以通过临时 Pod 或 YAML 文件运行 BusyBox 并测试 DNS 解析。

方法一:使用临时 Pod 进入交互模式(推荐)

kubectl run -i --tty --image=busybox dns-test --restart=Never -- sh

进入容器后,使用 nslookupdig(如果安装)查询每个 Pod 的 DNS 地址:

/ # nslookup nginx-0.nginx.default.svc.cluster.local
/ # nslookup nginx-1.nginx.default.svc.cluster.local
/ # nslookup nginx-2.nginx.default.svc.cluster.local
你将看到类似如下输出,表示解析成功:

Name:      nginx-0.nginx.default.svc.cluster.local
Address 1: 172.17.0.5

方法二:使用 YAML 文件定义并执行命令

你也可以创建一个带有 BusyBox 的 Pod 来批量测试:

# busybox-dns-test.yaml
apiVersion: v1
kind: Pod
metadata:
  name: busybox-dns
spec:
  containers:
  - name: busybox
    image: busybox
    command:
      - sh
      - -c
      - |
          echo "Testing DNS for nginx-0"
          nslookup nginx-0.nginx.default.svc.cluster.local
          echo "Testing DNS for nginx-1"
          nslookup nginx-1.nginx.default.svc.cluster.local
          echo "Testing DNS for nginx-2"
          nslookup nginx-2.nginx.default.svc.cluster.local
  restartPolicy: Never


9. StatefulSet如何扩容?扩容后原有Pod会重建吗?

在 Kubernetes 中,StatefulSet 的扩容操作是安全且有序的。扩容后的行为与 Deployment 不同,它遵循 StatefulSet 的核心机制,确保每个新增 Pod 具有稳定的身份标识和独立的存储卷。


1. 如何对 StatefulSet 进行扩容?

你可以通过修改 replicas 字段来实现扩容。

示例命令:

kubectl scale statefulset nginx --replicas=5

或者修改 YAML 文件并重新应用:

spec:
  replicas: 5

然后应用更改:

kubectl apply -f statefulset.yaml

2. 扩容后原有 Pod 会重建吗?

不会重建原有 Pod,这是 StatefulSet 的一个重要特性。

  • 扩容只会创建新的 Pod(如 nginx-3, nginx-4

  • 原有的 Pod(如 nginx-0, nginx-1, nginx-2)保持不变

  • 新增 Pod 按照序号递增顺序依次启动,并绑定新 PVC

原因:

  • StatefulSet 的每个 Pod 是唯一且稳定的

  • Pod 名称、网络标识、存储卷在整个生命周期中保持不变

  • 扩容只是增加新的 Pod 实例,不影响已有服务


10. StatefulSet中的Pod如果删除,数据是否还在?

在 Kubernetes 的 StatefulSet 中,如果某个 Pod 被删除,其数据是否保留取决于所使用的存储类型。以下是详细说明和行为规范:


StatefulSet 存储机制回顾

  • StatefulSet 通常使用 PersistentVolumeClaim (PVC) 为每个 Pod 提供独立的存储卷。

  • 每个 Pod 对应一个 PVC,命名格式为:

<volume-claim-template-name>-<pod-name>
  • PVC 绑定到 PersistentVolume(PV),数据持久化在底层存储中(如 NFS、hostPath、云厂商磁盘等)。

删除 Pod 后的数据行为

情况

数据是否保留

原因

Pod 被删除

保留

PVC 不会被自动删除,只要 PVC 存在,绑定的 PV 和数据依然保留

PVC 被手动删除

取决于 PV 的回收策略(Retain/Delete)

默认策略下数据可能被清除

StatefulSet 被删除

PVC 默认不删除

Kubernetes v1.23+ 开始默认保留 PVC


🔍 回收策略(Reclaim Policy)

PersistentVolume 的回收策略决定了当 PVC 被删除后,PV 如何处理:

策略

行为

推荐场景

Retain

PVC 删除后 PV 进入 Released 状态,数据保留在底层存储中

生产环境推荐,防止误删数据

Delete

PVC 删除时自动删除 PV 和底层存储(如云盘)

云平台动态供给场景

Recycle(已弃用)

清空 PV 数据后恢复为 Available 状态

不推荐使用


11. 如何升级StatefulSet中的应用版本?会滚动更新吗?

在 Kubernetes 中,升级 StatefulSet 中的应用版本是支持滚动更新的。StatefulSet 的滚动更新机制与 Deployment 类似,但遵循其独特的有序更新策略,确保每个 Pod 在前一个 Pod 更新完成并变为 Ready 后才继续下一个。


1. 升级方式:修改镜像版本

你可以通过 kubectl set image 命令或直接修改 YAML 文件来更新容器镜像。

方法一:使用命令行升级

kubectl set image statefulset/nginx nginx=nginx:1.21

其中:

  • statefulset/nginx 是你要更新的 StatefulSet;

  • nginx 是容器名;

  • nginx:1.21 是新版本镜像。

方法二:修改 YAML 文件并应用

# 修改 containers.image 字段
spec:
  replicas: 3
  serviceName: nginx
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21   # ←← 修改此字段为新版本
        ports:
        - containerPort: 80

然后应用更改:

kubectl apply -f statefulset.yaml

2. 滚动更新行为说明

StatefulSet 支持滚动更新,并且默认配置下会按序更新,即:

  • 从最后一个 Pod 开始逐个终止并重建

  • 每次只更新一个 Pod

  • 等待前一个 Pod Ready 后才会继续更新下一个

3. 查看滚动更新状态

kubectl rollout status statefulset/nginx

输出示例:

Waiting for 3 pods to be ready...
Pod nginx-0 successfully rolled out
Pod nginx-1 successfully rolled out
Pod nginx-2 successfully rolled out

你也可以查看当前副本集信息:

kubectl get statefulset

4. 回滚操作

如果发现新版本存在问题,可以回滚到上一个版本:

kubectl rollout undo statefulset/nginx

指定特定版本回滚:

kubectl rollout undo statefulset/nginx --to-revision=<revision-number>

5. 更新策略配置(RollingUpdate)

StatefulSet 默认使用 RollingUpdate 策略,你可以在 YAML 中显式配置更新行为:

spec:
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      partition: 0


12. StatefulSet能否和ConfigMap/Secret结合使用?

是的,StatefulSet 可以与 ConfigMap 和 Secret 结合使用。这是 Kubernetes 中一种常见的做法,用于为每个 Pod 提供配置信息或敏感数据。


一、结合方式

StatefulSet 的 Pod 模板中可以像普通 Pod 一样定义 volumesvolumeMounts,将 ConfigMap 或 Secret 挂载为文件作为环境变量注入

1. 挂载为 Volume(推荐)

spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/conf.d
  volumes:
  - name: config-volume
    configMap:
      name: nginx-config

2. 注入为环境变量

env:
- name: CONFIG_KEY
  valueFrom:
    configMapKeyRef:
      name: app-config
      key: someKey

Secret 的用法与 ConfigMap 类似,只需替换 configMap 字段为 secret

volumes:
- name: secret-volume
  secret:
    secretName: app-secret


或者作为环境变量:

env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password


13. StatefulSet中的存储如何持久化?PVC是否会回收?

一、StatefulSet 如何实现存储持久化?

StatefulSet 为每个 Pod 提供独立且稳定的存储卷绑定机制,确保即使 Pod 被删除或重新调度,其数据仍能保留。

1. 使用 volumeClaimTemplates 自动生成 PVC

StatefulSet 支持通过 volumeClaimTemplates 为每个 Pod 自动创建 PVC:

spec:
  replicas: 3
  serviceName: nginx
  volumeClaimTemplates:
  - metadata:
      name: www-data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
  • 每个 Pod 对应一个 PVC,命名格式为:<claim-name>-<pod-name>,例如:

    • www-data-nginx-0

    • www-data-nginx-1

    • www-data-nginx-2

2. PVC 绑定到 PV 实现持久化

PVC 会自动绑定到合适的 PV(由 StorageClass 动态供给或手动创建),从而将数据写入底层存储系统中(如 NFS、hostPath、云盘等)。

  • 数据与 Pod 生命周期解耦,Pod 删除后数据仍保留在 PV 中;

  • 新建 Pod 启动时可挂载原有 PVC,继续使用旧数据。


二、PVC 是否会被回收?如何控制?

1. 默认情况下,PVC 不会被自动删除

当以下操作发生时,PVC 的状态变化如下:

操作

PVC 行为

删除 Pod

PVC 保持不变,数据仍然保留

缩容 StatefulSet(减少副本数)

被缩容的 Pod 会被删除,但其对应的 PVC 仍然存在

删除 StatefulSet

PVC 默认不会被删除,除非设置了 --cascade=foreground

手动删除 PVC

可触发 PV 的回收策略(Retain/Delete)

2. PV 的回收策略决定数据是否保留

PersistentVolume 具有以下三种回收策略:

回收策略

行为

推荐场景

Retain

PVC 删除后,PV 状态变为 Released,数据需手动清理

生产环境推荐,防止误删数据

Delete

PVC 删除时,PV 和底层存储(如云盘)也会被自动删除

适用于云平台动态供给

Recycle(已弃用)

清空 PV 数据后恢复为 Available 状态

不推荐使用

设置方式:

spec:
  persistentVolumeReclaimPolicy: Retain


三、完整的资源生命周期管理流程

删除顺序建议(避免数据丢失)

先删除 Pod

kubectl delete pod nginx-0

再删除 PVC(谨慎操作)

kubectl delete pvc www-data-nginx-0


查看 PV 状态是否变为 Released

kubectl get pv


最后手动删除 PV(仅限 Retain 策略)

kubectl delete pv pv-nginx-0


14. 使用StatefulSet部署一个带状态的Redis集群(主从模式),如果不了解主机中如何部署过Redis主从,可先跳过


15. 如何彻底删除StatefulSet及其存储资源?

一、删除 StatefulSet

1. 删除 StatefulSet 资源(默认不会删除 PVC)

kubectl delete statefulset <statefulset-name>


二、删除 PVC(PersistentVolumeClaim)

由于 PVC 不会随 StatefulSet 自动删除,你需要手动删除每个 PVC。

查看所有 PVC:

kubectl get pvc

手动删除 PVC:

kubectl delete pvc www-data-nginx-0
kubectl delete pvc www-data-nginx-1
kubectl delete pvc www-data-nginx-2

三、检查 PV 回收策略并清理 PV

1. 查看 PV 的回收策略:

kubectl get pv -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.persistentVolumeReclaimPolicy}{"\n"}{end}'

2. 如果 PV 策略为 Retain(推荐用于生产环境)

  • PVC 删除后,PV 会进入 Released 状态。

  • 数据仍保留在底层存储中,需手动清理数据和删除 PV

删除 PV:
kubectl delete pv pv-nginx-0
kubectl delete pv pv-nginx-1
kubectl delete pv pv-nginx-2

四、可选:使用标签统一管理资源(推荐做法)

你可以给 PVC/PV 添加标签,便于批量删除。

示例:添加标签

metadata:
  labels:
    app: nginx

然后批量删除:

kubectl delete pvc -l app=nginx
kubectl delete pv -l app=nginx

以他人的幸福为幸福,以他人的享乐为享乐。