Kubernetes 进阶

访问模式(accessModes) PV 回收策略(persistentVolumeReclaimPolicy) 无需管理员手动创建 PV,PVC 通过 storageClassName 指定 StorageClass,控制器自动创建匹配的 PV。 Ingress 是集群入口的 HTTP/HTTPS 路由规则,需要配合 Ingress Controller(如 Nginx Ingress)使用。Ingress Controller 本身作为 Deployment 运行在集群中,监听 Ingress 资源变化并动态配置反向代理。 Ingress YAML

分享

官方文档:https://kubernetes.io/zh-cn/docs/home/
适用版本:Kubernetes 1.29+(2026-05-07 核实)

存储

Volume 类型对比

类型 生命周期 说明 典型使用场景
emptyDir 与 Pod 相同 Pod 创建时分配,Pod 删除时数据清除;同一 Pod 内容器可共享 容器间临时文件共享、缓存、日志中转
hostPath 与 Node 相关 挂载宿主机目录或文件,Pod 删除后数据保留在节点上 访问宿主机日志、Docker socket;不推荐生产使用
configMap 与 ConfigMap 相同 将 ConfigMap 的数据挂载为文件 配置文件注入
secret 与 Secret 相同 将 Secret 数据挂载为文件(内存中的 tmpfs) 证书、密钥文件注入
persistentVolumeClaim 独立于 Pod 引用已有的 PVC,数据持久化存储 数据库、有状态应用

PV / PVC / StorageClass 三者关系

StorageClass(存储类)
    │
    │ 动态供应
    ▼
PersistentVolume(PV)── 静态绑定 ──► PersistentVolumeClaim(PVC)
                                              │
                                              │ 引用
                                              ▼
                                           Pod spec
  • PersistentVolume(PV):集群管理员预先创建(静态)或由 StorageClass 自动创建(动态)的存储资源,描述实际存储的位置和容量。
  • PersistentVolumeClaim(PVC):用户对存储的申请,声明所需容量和访问模式,Kubernetes 自动匹配合适的 PV 进行绑定。
  • StorageClass:存储的"模板",定义存储驱动(provisioner)和参数,支持动态按需创建 PV。

访问模式(accessModes)

模式 缩写 说明
ReadWriteOnce RWO 只能被单个 Node 以读写方式挂载
ReadOnlyMany ROX 可被多个 Node 以只读方式挂载
ReadWriteMany RWX 可被多个 Node 以读写方式挂载(需要共享文件系统,如 NFS)

PV 回收策略(persistentVolumeReclaimPolicy)

策略 说明
Retain PVC 删除后 PV 保留,需手动处理数据
Delete PVC 删除后 PV 及底层存储一并删除(动态供应的默认行为)
Recycle 已废弃,不推荐使用

动态供应(Dynamic Provisioning)

无需管理员手动创建 PV,PVC 通过 storageClassName 指定 StorageClass,控制器自动创建匹配的 PV。

# StorageClass 示例(使用 AWS EBS)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast
provisioner: ebs.csi.aws.com
parameters:
  type: gp3
reclaimPolicy: Delete
allowVolumeExpansion: true
# PVC 使用动态供应
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  storageClassName: fast
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
# Pod 引用 PVC
volumes:
  - name: data
    persistentVolumeClaim:
      claimName: my-pvc

网络

Ingress

Ingress 是集群入口的 HTTP/HTTPS 路由规则,需要配合 Ingress Controller(如 Nginx Ingress)使用。Ingress Controller 本身作为 Deployment 运行在集群中,监听 Ingress 资源变化并动态配置反向代理。

Ingress YAML 结构

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - example.com
      secretName: tls-secret    # 包含 tls.crt 和 tls.key 的 Secret
  rules:
    - host: example.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 80

spec.rules 字段

字段 类型 说明
host 字符串 域名,省略则匹配所有 Host
http.paths[].path 字符串 URL 路径前缀或精确匹配
http.paths[].pathType 字符串 Prefix(前缀)、Exact(精确)、ImplementationSpecific
http.paths[].backend.service.name 字符串 后端 Service 名称
http.paths[].backend.service.port.number 整数 后端 Service 端口

spec.tls 字段

字段 类型 说明
hosts 列表 TLS 生效的域名列表
secretName 字符串 存储 TLS 证书的 Secret 名称

NetworkPolicy

NetworkPolicy 限制 Pod 间的网络通信,默认 Pod 可以互相访问;创建 NetworkPolicy 后仅允许规则匹配的流量通过。需要 CNI 插件支持(如 Calico、Cilium)。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
  namespace: production
spec:
  podSelector:
    matchLabels:
      role: api             # 该策略作用于带此标签的 Pod
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              role: frontend  # 只允许 frontend Pod 访问
      ports:
        - protocol: TCP
          port: 8080
  egress:
    - to:
        - podSelector:
            matchLabels:
              role: database
      ports:
        - protocol: TCP
          port: 5432

调度

nodeSelector / nodeAffinity / podAffinity / podAntiAffinity 对比

特性 描述 灵活性
nodeSelector 简单键值对标签匹配,强制要求 Pod 调度到带有指定标签的节点
nodeAffinity 节点亲和性,支持 InNotInExists 等操作符,可区分硬性(requiredDuringScheduling)和软性(preferredDuringScheduling)要求
podAffinity Pod 亲和性,要求调度到与指定 Pod 相同的节点/拓扑域(如同一区域),常用于低延迟场景
podAntiAffinity Pod 反亲和性,要求调度到与指定 Pod 不同的节点/拓扑域,常用于高可用(避免单点)
# nodeAffinity 示例
affinity:
  nodeAffinity:
    requiredDuringSchedulingIgnoredDuringExecution:
      nodeSelectorTerms:
        - matchExpressions:
            - key: topology.kubernetes.io/zone
              operator: In
              values: ["us-east-1a", "us-east-1b"]
  podAntiAffinity:
    preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchLabels:
              app: my-app
          topologyKey: kubernetes.io/hostname  # 尽量分散到不同节点

Taints 与 Tolerations

Taint 给节点打上"污点",阻止没有对应 Toleration 的 Pod 调度到该节点。

# 给节点添加 Taint
kubectl taint nodes node1 key=value:NoSchedule

# 删除 Taint
kubectl taint nodes node1 key=value:NoSchedule-

Taint 效果(effect):

效果 说明
NoSchedule 不允许调度新 Pod(已运行的 Pod 不受影响)
PreferNoSchedule 尽量不调度,但没有其他节点时仍可调度
NoExecute 不允许调度,且驱逐已运行的不容忍 Pod

Toleration 允许 Pod 容忍节点的污点:

tolerations:
  - key: "key"
    operator: "Equal"
    value: "value"
    effect: "NoSchedule"
  - key: "node.kubernetes.io/not-ready"
    operator: "Exists"
    effect: "NoExecute"
    tolerationSeconds: 300   # 最多容忍 300 秒后驱逐

PriorityClass

定义 Pod 的调度优先级,高优先级 Pod 在资源不足时可抢占低优先级 Pod。

apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000000          # 数值越大优先级越高
globalDefault: false
description: "用于关键生产服务"
# Pod 使用 PriorityClass
spec:
  priorityClassName: high-priority

自动扩缩容

HorizontalPodAutoscaler(HPA)

HPA 根据 CPU、内存或自定义指标自动调整 Deployment/StatefulSet 的副本数。需要集群安装 metrics-server。

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-app
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70    # CPU 使用率超过 70% 时扩容
    - type: Resource
      resource:
        name: memory
        target:
          type: AverageValue
          averageValue: 512Mi

HPA spec 核心字段

字段 类型 默认值 说明
scaleTargetRef 对象 必填 指定被扩缩容的目标资源
minReplicas 整数 1 最小副本数,HPA 不会将副本降至此值以下
maxReplicas 整数 必填 最大副本数
metrics 列表 必填 扩缩容指标列表
behavior.scaleUp 对象 扩容行为配置(稳定窗口、步长)
behavior.scaleDown 对象 缩容行为配置(稳定窗口默认 5 分钟,防止频繁缩容)

metrics 类型

类型 说明
Resource 基于 CPU/内存使用率或绝对值
ContainerResource 针对 Pod 内特定容器的资源指标
Pods Pod 级别自定义指标(如每秒请求数)
Object 集群对象的指标(如 Ingress 的 QPS)
External 外部系统指标(如消息队列积压量)

StatefulSet

与 Deployment 的区别

特性 Deployment StatefulSet
Pod 名称 随机后缀(如 app-abc12 有序且固定(如 app-0app-1
部署/删除顺序 并行,无序 有序(0, 1, 2...),删除时反序
网络标识 Pod IP 变化 稳定 DNS 名称(通过 Headless Service)如 app-0.my-svc
存储 共享或无状态 每个 Pod 有独立的 PVC(volumeClaimTemplates)

适用场景

  • 数据库:MySQL、PostgreSQL(需要主从固定角色)
  • 消息队列:Kafka(Broker ID 与 Pod 序号绑定)
  • 分布式协调:ZooKeeper、etcd(需要固定网络标识)
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: "mysql"          # 必须指定 Headless Service 名称
  replicas: 3
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:8.0
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql-secret
                  key: password
          volumeMounts:
            - name: data
              mountPath: /var/lib/mysql
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: fast
        resources:
          requests:
            storage: 20Gi

ConfigMap/Secret 热更新

Volume 挂载方式(自动更新)

以 Volume 方式挂载的 ConfigMap,Kubernetes 会在 ConfigMap 内容变更后自动同步到容器内的文件(默认同步周期约为 1 分钟,取决于 kubelet 的 --sync-frequency)。

volumeMounts:
  - name: config
    mountPath: /etc/app/config.yaml
    subPath: config.yaml    # 注意:使用 subPath 时不会自动更新
volumes:
  - name: config
    configMap:
      name: my-config

注意:使用 subPath 挂载单个文件时,自动更新失效,需重启 Pod。

环境变量方式(不自动更新)

通过 env.valueFrom.configMapKeyRefenvFrom 注入的环境变量,在 ConfigMap 更新后不会刷新。必须重启 Pod 才能生效。

强制触发 Pod 重启的常用方式

# 方式一:滚动重启 Deployment(零停机)
kubectl rollout restart deployment/my-app

# 方式二:修改 Deployment 的 annotation 触发更新
kubectl patch deployment my-app -p \
  '{"spec":{"template":{"metadata":{"annotations":{"restarted-at":"2026-04-11"}}}}}'

RBAC(基于角色的访问控制)

核心对象关系

ServiceAccount(身份)
    │
    │ RoleBinding / ClusterRoleBinding
    ▼
Role(命名空间级权限)/ ClusterRole(集群级权限)
对象 作用范围 说明
ServiceAccount Namespace Pod 的身份标识,用于与 API Server 交互
Role Namespace 定义在特定命名空间内的权限规则
ClusterRole 集群 定义集群范围的权限,或跨命名空间复用
RoleBinding Namespace 将 Role 或 ClusterRole 绑定到 ServiceAccount/User/Group
ClusterRoleBinding 集群 将 ClusterRole 绑定到集群范围的主体
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: production

---
# Role:允许读取特定命名空间的 Pod 和 ConfigMap
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["pods", "configmaps"]
    verbs: ["get", "list", "watch"]

---
# RoleBinding:将 Role 绑定到 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: production
subjects:
  - kind: ServiceAccount
    name: app-sa
    namespace: production
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

最小权限原则

  • 每个应用创建专用 ServiceAccount,不使用 default
  • 优先使用 Role + RoleBinding 而非 ClusterRole + ClusterRoleBinding
  • verbs 只授予必要的操作:getlistwatch(只读),避免授予 * 通配符
  • 定期审查并清理不再使用的 Role

Helm

Helm 是 Kubernetes 的包管理工具,通过 Chart 封装一组 Kubernetes 资源模板,支持版本管理和参数化配置。

常用命令

helm install

helm install <release-name> <chart> [flags]
参数 说明
<release-name> 发布名称,集群内唯一
<chart> Chart 名称(repo/chart)或本地路径
-n / --namespace 部署到指定命名空间
-f / --values 指定自定义 values 文件
--set 命令行覆盖单个值,如 --set replicas=3
--version 指定 Chart 版本
--dry-run 模拟安装,输出渲染后的 YAML
--create-namespace 命名空间不存在时自动创建

helm upgrade

helm upgrade <release-name> <chart> [flags]
参数 说明
--install release 不存在时自动安装(等同于 install + upgrade 二合一)
--atomic 升级失败时自动回滚
--wait 等待所有资源就绪后再返回
--timeout 等待超时时间,默认 5m0s
--reuse-values 复用上次的 values,只覆盖本次指定的参数

helm rollback

helm rollback <release-name> [revision]
参数 说明
[revision] 目标版本号,省略则回滚到上一版本
--wait 等待回滚完成

helm uninstall

helm uninstall <release-name> [-n namespace]
参数 说明
--keep-history 保留 release 历史记录(默认删除)

其他常用命令

# 添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

# 查看已安装的 release
helm list -A

# 查看 release 历史
helm history my-release

# 查看渲染后的 YAML(不实际部署)
helm template my-release ingress-nginx/ingress-nginx -f values.yaml

# 查看 Chart 的默认 values
helm show values ingress-nginx/ingress-nginx

values.yaml 覆盖

优先级从低到高:Chart 默认 values → -f values.yaml--set

# 使用自定义 values 文件安装
helm install ingress-nginx ingress-nginx/ingress-nginx \
  -n ingress-nginx \
  --create-namespace \
  -f custom-values.yaml

# 多个 values 文件叠加(后者覆盖前者)
helm install my-app ./my-chart -f base.yaml -f prod.yaml

# 命令行覆盖嵌套字段
helm install my-app ./my-chart --set image.tag=1.2.3 --set service.type=LoadBalancer

常用 Chart

Chart 仓库 用途
ingress-nginx/ingress-nginx https://kubernetes.github.io/ingress-nginx Nginx Ingress Controller
cert-manager/cert-manager https://charts.jetstack.io 自动申请/续签 TLS 证书(支持 Let's Encrypt)
prometheus-community/kube-prometheus-stack https://prometheus-community.github.io/helm-charts Prometheus + Grafana 监控套件

踩坑与注意事项

PVC 数据残留问题

删除 StatefulSet 时,volumeClaimTemplates 创建的 PVC 不会自动删除(设计如此,防止数据误删)。

# 查看残留 PVC
kubectl get pvc -n <namespace>

# 手动删除 PVC(确认数据已备份)
kubectl delete pvc data-mysql-0 data-mysql-1 data-mysql-2

如果 PV 的回收策略是 Retain,删除 PVC 后 PV 仍保留,需要手动删除或重新绑定。

ConfigMap 更新后 Pod 不重启

环境变量方式注入的 ConfigMap 不会自动刷新,必须触发 Pod 重启。推荐做法:

  1. 在 CI/CD 流水线中将 ConfigMap 的 hash 作为 Deployment annotation,ConfigMap 变更时 annotation 值变化,自动触发滚动更新:
    annotations:
      checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
    
  2. 或使用 kubectl rollout restart deployment/<name> 手动触发。

Ingress 404 排查步骤

  1. 确认 Ingress Controller 正常运行
    kubectl get pods -n ingress-nginx
    kubectl logs -n ingress-nginx <ingress-nginx-pod>
    
  2. 确认 Ingress 资源已创建
    kubectl get ingress -A
    kubectl describe ingress <name> -n <namespace>
    
  3. 确认 ingressClassName 匹配:Ingress 的 spec.ingressClassName 必须与 Controller 的 class 一致(通常为 nginx)。
  4. 确认后端 Service 和 Pod 正常
    kubectl get svc,endpoints -n <namespace>
    
    Endpoints 列表为空说明 selector 未匹配到 Pod。
  5. 确认路径规则pathType: Prefix/api 能匹配 /api/v1pathType: Exact 则只能精确匹配。
  6. 检查 annotationnginx.ingress.kubernetes.io/rewrite-target 配置不当会导致路径重写错误。

其他常见坑

  • HPA 不生效:检查 metrics-server 是否安装,kubectl top pods 能否正常输出。Deployment 必须设置 resources.requests.cpu,否则 HPA 无法计算利用率百分比。
  • StatefulSet 滚动更新卡住:某个 Pod 一直 Pending 或 CrashLoopBackOff 会阻塞后续 Pod 的更新(有序策略)。用 kubectl rollout status statefulset/<name> 监控,出现问题需先修复该 Pod。
  • Helm release 状态为 failed:执行 helm rollbackhelm uninstall 后重新安装。--atomic 参数可以在失败时自动回滚,适合 CI/CD 场景。
  • Secret 更新后 Volume 挂载不刷新:虽然理论上 Secret Volume 也会自动同步,但使用 subPath 挂载时同样不会更新。避免对频繁变更的 Secret 使用 subPath

最佳实践

HPA 与 VPA 不要同时作用于同一资源维度:HPA 基于 CPU/内存自动扩缩副本数,VPA 自动调整单 Pod 的 resource requests;两者同时控制 CPU 会互相干扰。可以让 HPA 管 CPU 水平扩展,VPA 只管内存的垂直调整(updateMode: "Initial" 避免重建 Pod)。

StatefulSet 使用 podManagementPolicy: Parallel 加速批量操作:默认有序(OrderedReady)策略在扩缩容时依次等待每个 Pod ready,并发场景下改为 Parallel 可大幅缩短时间(注意应用本身需支持并发启动)。

Helm Chart 用 --atomic 防止残留失败 release:CI/CD 中加 --atomic 标志,升级失败时自动回滚到上一个成功版本,避免留下 failed 状态的 release 阻塞后续部署。

网络策略(NetworkPolicy)默认拒绝,按需开放:新命名空间默认无网络策略,所有 Pod 互通。生产环境先应用 deny-all 策略,再逐步开放需要的端口和方向,减少横向移动风险。

PodDisruptionBudget 保障滚动更新期间最小可用副本minAvailable: 1 确保即使在节点维护或滚动更新时,始终至少有 1 个副本处于 Ready 状态对外服务。


常见陷阱

陷阱:HPA 不生效,副本数不变

现象: 负载升高但 kubectl get hpa 显示 <unknown>/50%,副本数不增加。
原因: metrics-server 未安装或 Pod 未设置 resources.requests.cpu,HPA 无法获取利用率百分比。
解决: 安装 metrics-server(kubectl top pods 可用),确保 Deployment 的 Pod spec 中有 resources.requests.cpu

陷阱:StatefulSet 滚动更新卡在某个 Pod

现象: kubectl rollout status statefulset/<name> 长时间等待,某个 Pod 一直 CrashLoopBackOff。
原因: 有序策略下,前一个 Pod 未 Ready 则后续 Pod 不会更新,问题 Pod 阻塞整个滚动更新。
解决: kubectl describe pod <stucked-pod> 查看错误原因修复应用问题,或 kubectl rollout undo statefulset/<name> 回滚。

陷阱:Helm upgrade 后旧版本 ConfigMap/Secret 残留

现象: Helm upgrade 后应用读取到旧配置,kubectl describe pod 显示挂载的仍是旧 ConfigMap。
原因: Helm 不重启未变更的 Deployment,即使 ConfigMap 已更新,已运行的 Pod 不会自动感知变化。
解决: 在 Deployment 的 Pod template annotations 中加入 ConfigMap 的 checksum,ConfigMap 变更时 annotation 变化触发滚动重启:

annotations:
  checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}

参见

Kubernetes基础
Docker高级指南
GitHub Actions完全指南
Prometheus与Grafana监控

阅读更多

Web 安全基础

1. HTML 转义(服务端渲染必须): 2. CSP(Content Security Policy): 3. HttpOnly Cookie:防止 JS 读取会话 Cookie: 4. 前端框架防护: 攻击者在第三方网站构造一个表单,诱导已登录用户提交,浏览器会自动携带目标站的 Cookie。 触发条件: 1. 用户已登录目标网站(Cookie 有效) 2. 目标 API 仅凭 Cookie 识别用户身份 3. 请求来源未验证 1. CSRF Token(推荐): 2. SameSite Cookie: 3. 验证 Origin/Referer 头:

By yellowdog

HTTP 协议深度指南

HTTP(HyperText Transfer Protocol)是 Web 的基础传输协议,基于 TCP/IP,采用请求/响应模型。 相关文档:Web安全基础(/web-an-quan-ji-chu/) FastAPI完全指南(/fastapi-wan-quan-zhi-nan/) Nginx完全指南(/nginx-wan-quan-zhi-nan/) 幂等性:多次执行相同请求,服务器状态结果相同。PUT /users/1 多次执行结果一致;POST /users 每次创建新资源,非幂等。 浏览器直接从本地缓存读取,不向服务器发送请求。 缓存命中时,状

By yellowdog

系统设计基础

SLA 对照表: 选择建议:无状态服务(Web 层、API 层)优先水平扩展;数据库初期垂直扩展,达到瓶颈后考虑分库分表或读写分离。 缓存穿透(查询不存在的 key,每次都打到 DB): 缓存击穿(热点 key 过期,瞬间大量请求打到 DB): 缓存雪崩(大量 key 同时过期,或缓存服务宕机): 令牌桶 Python 实现: Redis 实现分布式限流(滑动窗口): URL 命名规则: Cursor 分页响应格式: 雪花算法结构(64 bit): 定义:分布式系统不能同时满足以下三个特性: 在分布式环境中 P 是必须保证的,所以实际是 CP vs AP

By yellowdog

算法思路与模板

二分查找要求序列有序,每次将搜索范围缩减一半,时间复杂度 O(log n)。 两个指针从两端向中间收缩,常用于有序数组。 滑动窗口维护一个满足条件的区间 left, right,right 不断向右扩张,条件不满足时收缩 left。 滑动窗口通用框架: 1. 确定"子问题":原问题可以分解为哪些规模更小的同类问题 2. 定义 dpi 或 dpij 的含义,要足够清晰 3. 推导状态转移方程 4. 确定初始状态(边界条件) 5. 确定计算顺序(确保依赖的子问题先计算) 每件物品最多选一次。dpj = 容量为 j 时的最大价值,逆序遍历容量防止重复选取。 每

By yellowdog