zoukankan      html  css  js  c++  java
  • Kubernetes 实战——有状态应用(StatefulSet)

    一、简介

    • 有状态实例:新实例和旧实例需要有相同的名称、网络标识和状态
    • 无状态实例:可随时被替换

    1. ReplicaSet 和有状态 Pod

    ReplicaSet 通过 Pod 模板创建多个 Pod 副本,这些副本除了名字和 IP 地址不同,没有其他差异。若 Pod 模板指定了 PVC,则其创建的所有 Pod 共享相同的 PVC 和 PV

    集群应用可能要求实例具有唯一的网络标识。可针对每个实例创建一个独立的 Service 来提供稳定的网络地址(因为服务 IP 固定)。但 Pod 无法获取该 IP,不能在别的 Pod 里通过 IP 自行注册

    2. 了解 StatefulSet

    • 每一个实例不可替代,都拥有稳定的名字(从零开始的顺序索引)和状态(独立的数据卷)
    • 有状态的 Pod 有时需要通过其主机名来定位。因为彼此状态不同,通常希望操作的是指定的那个
      • 一个 StatefulSet 常要求创建一个用来记录每个 Pod 网络标记的 headless Service。通过该 Service,每个 Pod 将拥有独立的 DNS 记录,这样集群中的 Pod 或客户端可以通过主机名来定位
      • 如一个 default 命名空间,名为 foo 的服务,它的一个 Pod 名为 a-0,就可以通过a-0.foo.default.svc.cluster.local来定位该 Pod
      • 也可以通过 DNS 服务查找域名foo.default.svc.cluster.local对应的所有 SRV 记录,获取一个 StatefulSet 所有 Pod 的信息
    • 当 StatefulSet 管理的 Pod 消失后,会重启一个标识完全一致的 Pod 替换(不一定在同一个节点)
    • 扩容用下一个索引值,缩容先删除最高索引值,扩/缩容都是逐步进行的K8s 保证两个拥有相同标记和绑定相同 PVC 的有状态 Pod 不会同时运行
      • 若有不健康实例,则不允许做缩容操作(避免一次删除两个)
      • 缩容只删除 Pod,保留创建的持久卷声明(PVC 被删除后,与之绑定的 PV 也会被回收或删除),需要手动删除。再扩容会重新挂载上

    3. 专属存储

    • 有状态的 Pod 存储必须是持久的,且与 Pod 解耦。即 StatefulSet 的 Pod 需要关联到不同的持久卷声明,且与独立的持久卷对应
    • 持久卷可以预先创建,也可以由持久卷的动态供应机制实时创建

    卷声明模板

    StatefulSet 可以有一个或多个卷声明模板,会在创建 Pod 前创建持久卷声明,并绑定到 Pod 实例上

    二、使用 StatefulSet

    1. 创建

    ① 容器准备

    docker.io/luksa/kubia-pet

    • POST 请求将 body 中的数据存储到 /var/data/kubia.txt
    • GET 请求返回主机名和存储的数据

    ② 手动创建存储卷

    apiVersion: v1
    kind: List
    items:
    - apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: pv-a  # 持久卷名称 pv-a、pv-b、pv-c
      spec:
        capacity:
          storage: 1Mi  # 持久卷大小
        accessModes:
          - ReadWriteOnce
        persistentVolumeReclaimPolicy: Recycle  # 卷被声明释放后,空间被回收再利用
        nfs:  # 卷使用 nfs 持久磁盘。见 https://www.cnblogs.com/lb477/p/14713883.html
          server: 192.168.11.210
          path: "/nfs/pv-a"
    ...
    

    ③ 创建控制 Service

    apiVersion: v1
    kind: Service
    metadata:
      name: kubia
    spec:
      clusterIP: None  # StatefulSet 的控制 Service 必须是 headless 模式
      selector:
        app: kubia
      ports:
      - name: http
        port: 80
    

    ④ 创建 StatefulSet

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: kubia
    spec:
      selector:
        matchLabels:
          app: kubia
      serviceName: kubia
      replicas: 2
      template:
        metadata:
          labels:
            app: kubia
        spec:
          containers:
          - name: kubia
            image: luksa/kubia-pet
            ports:
            - name: http
              containerPort: 8080
            volumeMounts:
            - name: data
              mountPath: /var/data  # Pod 中的容器会把 pvc 数据卷嵌入指定目录
      volumeClaimTemplates:  # 创建持久卷声明的模板,会为每个 Pod 创建并关联一个 pvc
      - metadata:
          name: data
        spec:
          resources:
            requests:
              storage: 1Mi
          accessModes:
          - ReadWriteOnce
    

    ⑤ 查看创建结果

    $ kubectl get pod -w
    NAME      READY   STATUS              RESTARTS   AGE
    kubia-0   0/1     ContainerCreating   0          35s
    kubia-0   1/1     Running             0          53s
    kubia-1   0/1     Pending             0          0s
    kubia-1   0/1     ContainerCreating   0          3s
    kubia-1   1/1     Running             0          20s
    $ kubectl get pv
    NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                  STORAGECLASS   REASON   AGE
    pv-a   1Mi        RWO            Recycle          Bound       default/data-kubia-0                           18m
    pv-b   1Mi        RWO            Recycle          Bound       default/data-kubia-1                           18m
    pv-c   1Mi        RWO            Recycle          Available                                                  18m
    $ kubectl get pvc
    NAME           STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    data-kubia-0   Bound    pv-a     1Mi        RWO                           2m3s
    data-kubia-1   Bound    pv-b     1Mi        RWO                           70s
    

    2. 测试

    • 直连 Pod 来访问:借助另一个 Pod,在其内部运行 curl 命令或使用端口转发
    • 通过 API 服务器与 Pod 通信:API 服务器可通过代理直接连接到指定 Pod:可通过访问<apiServerHost>:<port>/api/v1/namespaces/default/pods/kubia-0/proxy/<path>请求 Pod,但 API 服务器有安全保障,需要在每次请求中添加授权令牌。因此可使用 kubectl 代理和 API 服务器代理与 Pod 通信:
    $ kubectl proxy
    Starting to serve on 127.0.0.1:8001
    $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
    You've hit kubia-0
    Data stored on this pod: No data posted yet
    

    测试

    # 1. 应用的状态独立
    $ curl -X POST -d "Hello kubia-0" localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
    Data stored on pod kubia-0
    $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
    You've hit kubia-0
    Data stored on this pod: Hello kubia-0
    $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-1/proxy/
    You've hit kubia-1
    Data stored on this pod: No data posted yet
    
    # 2. 重新启动一个完全相同的 Pod(新的 Pod 可能被调度到其他节点)
    $ kubectl delete pod kubia-0
    pod "kubia-0" deleted
    $ kubectl get pod
    NAME      READY   STATUS              RESTARTS   AGE
    kubia-0   0/1     ContainerCreating   0          1s
    kubia-1   1/1     Running             0          106m
    $ curl localhost:8001/api/v1/namespaces/default/pods/kubia-0/proxy/
    You've hit kubia-0
    Data stored on this pod: Hello kubia-0
    

    暴露 StatefulSet 的 Pod

    # 一个常规的 ClusterIP Service,只能在集群内部访问
    apiVersion: v1
    kind: Service
    metadata:
      name: kubia-public
    spec:
      selector:
        app: kubia
      ports:
      - port: 80
        targetPort: 8080
    
    $ curl localhost:8001/api/v1/namespaces/default/services/kubia-public/proxy/
    You've hit kubia-1 / 0
    

    3. 发现伙伴节点

    SRV 记录指向提供指定服务的服务器的主机名和端口号

    获取 StatefulSet 里的所有 Pod 信息

    # 运行一个名为 srvlookup 的一次性 Pod,关联控制台并在终止后立即删除
    $ kubectl run -it srvlookup --image=tutum/dnsutils --rm --restart=Never -- dig SRV kubia.default.svc.cluster.local
    ;; ANSWER SECTION:
    kubia.default.svc.cluster.local. 30 IN	SRV	0 50 80 kubia-0.kubia.default.svc.cluster.local.
    kubia.default.svc.cluster.local. 30 IN	SRV	0 50 80 kubia-1.kubia.default.svc.cluster.local.
    ;; ADDITIONAL SECTION:
    kubia-0.kubia.default.svc.cluster.local. 30 IN A 10.244.0.15
    kubia-1.kubia.default.svc.cluster.local. 30 IN A 10.244.0.16
    ...
    # 返回的 SRV 记录顺序随机
    

    让节点返回所有集群节点的数据

    4. 处理节点失效

    可通过关闭节点的 eth0 网络接口模拟节点的网络断开

    • 当一个节点失效,运行在该节点上的 Kubelet 服务就无法与 K8s API 服务器通信,即无法汇报节点及其 Pod 的状态
    • StatefulSet 在明确知道一个 Pod 不再运行之前,不会创建一个替换的 Pod
    • 一段时间后,该节点状态变为 NotReady,Pod 状态变为 Unknown
      • 若节点恢复,汇报状态后 Pod 会被重新标记为 Running
      • 若 Pod 的 Unknown 状态持续几分钟(可配置)后,主节点就会将 Pod 从节点驱逐(删除 Pod 资源)
        • 若此时 describe Pod,可看到其状态为 Terminating,即已经被标记为删除。但由于节点不能通信,该 Pod 仍会一直运行
      • 可强制删除:kubectl delete pod kubia-0 --force --grace-period 0(除非确定节点不再运行,否则不要强制删除有状态的 Pod)
  • 相关阅读:
    POJ 1015 Jury Compromise【DP】
    POJ 1661 Help Jimmy【DP】
    HDU 1074 Doing Homework【状态压缩DP】
    HDU 1024 Max Sum Plus Plus【DP,最大m子段和】
    占坑补题。。最近占的坑有点多。。。
    Codeforces 659F Polycarp and Hay【BFS】
    Codeforces 659E New Reform【DFS】
    Codeforces 659D Bicycle Race【计算几何】
    廖大python实战项目第四天
    廖大python实战项目第三天
  • 原文地址:https://www.cnblogs.com/lb477/p/14898850.html
Copyright © 2011-2022 走看看