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)
Kubernetes 实战——有状态应用(StatefulSet)的更多相关文章
- Kubernetes实战总结 - 自定义Prometheus
一.概述 首先Prometheus整体监控结构略微复杂,一个个部署并不简单.另外监控Kubernetes就需要访问内部数据,必定需要进行认证.鉴权.准入控制, 那么这一整套下来将变得难上加难,而且还需 ...
- 新书推荐《再也不踩坑的Kubernetes实战指南》
<再也不踩坑的Kubernetes实战指南>终于出版啦.目前可以在京东.天猫购买,京东自营和当当网预计一个星期左右上架. 本书贴合生产环境经验,解决在初次使用或者是构建集群中的痛点,帮 ...
- kubernetes实战(二十六):kubeadm 安装 高可用 k8s v1.16.x dashboard 2.x
1.基本配置 基本配置.内核升级.基本服务安装参考https://www.cnblogs.com/dukuan/p/10278637.html,或者参考<再也不踩坑的Kubernetes实战指南 ...
- kubernetes实战(二十七):CentOS 8 二进制 高可用 安装 k8s 1.16.x
1. 基本说明 本文章将演示CentOS 8二进制方式安装高可用k8s 1.16.x,相对于其他版本,二进制安装方式并无太大区别.CentOS 8相对于CentOS 7操作更加方便,比如一些服务的关闭 ...
- kubernetes实战(二十八):Kubernetes一键式资源管理平台Ratel安装及使用
1. Ratel是什么? Ratel是一个Kubernetes资源平台,基于管理Kubernetes的资源开发,可以管理Kubernetes的Deployment.DaemonSet.Stateful ...
- kubernetes实战(二十九):Kubernetes RBAC实现不同用户在不同Namespace的不同权限
1.基本说明 在生产环境使用k8s以后,大部分应用都实现了高可用,不仅降低了维护成本,也简化了很多应用的部署成本,但是同时也带来了诸多问题.比如开发可能需要查看自己的应用状态.连接信息.日志.执行命令 ...
- kubernetes实战(三十):CentOS 8 二进制 高可用 安装 k8s 1.17.x
1. 基本说明 本文章将演示CentOS 8二进制方式安装高可用k8s 1.17.x,相对于其他版本,二进制安装方式并无太大区别. 2. 基本环境配置 主机信息 192.168.1.19 k8s-ma ...
- Kubernetes实战总结 - 阿里云ECS自建K8S集群
一.概述 详情参考阿里云说明:https://help.aliyun.com/document_detail/98886.html?spm=a2c4g.11186623.6.1078.323b1c9b ...
- kubernetes实战之consul篇及consul在windows下搭建consul简单测试环境
consul是一款服务发现中间件,1.12版本后增加servicemesh功能.consul是分布式的,可扩展的,高可用的根据官方文档介绍,目前已知最大的consul集群有5000个节点,consul ...
随机推荐
- 『动善时』JMeter基础 — 8、JMeter主要元件介绍
目录 1.测试计划(Test Plan) 2.线程组 3.取样器(sampler) 4.逻辑控制器(Logic Controller) 5.配置元件(Config Element) 6.定时器(Tim ...
- java8中的日期和时间API
一.背景 jdk 1.8 之前, Java 时间使用java.util.Date 和 java.util.Calendar 类. Date today = new Date(); System.out ...
- 自动化测试面试官:登录或注册时有验证码怎么处理?OCR图像识别技术大揭秘!
本节大纲 读取cookie实现免登陆 pytesseract+tesseract-ocr实现图像识别 Pillow库对验证码截图 API接口实现图像识别 今天的这个技术点,为什么要给大家分享一下呢? ...
- Linux 内核调度器源码分析 - 初始化
导语 上篇系列文 混部之殇-论云原生资源隔离技术之CPU隔离(一) 介绍了云原生混部场景中CPU资源隔离核心技术:内核调度器,本系列文章<Linux内核调度器源码分析>将从源码的角度剖析内 ...
- C++基于armadillo im2col的实现
最近学习CNN,需要用到im2col这个函数,无奈网上没有多少使用armadillo的例子,而且armadillo库中似乎也没有这个函数,因此自己写了. im2col的原理网上一大把,我懒得写了. 1 ...
- JMeter四种参数化方式
JMeter参数化是指把固定的数据动态化,这样更贴合实际的模拟用户请求,比如模拟多个不同账号.JMeter一共有四种参数化方式,分别是: CSV Data Set Config Function He ...
- 优启通-PE启动盘制作工具 原版Win7系统安装超详细教程!!!!!
https://www.jianshu.com/p/cd4abc9889b6 前期准备 原版Win7系统ISO映像文件 PE启动U盘或系统光盘(本教程以纯净无捆绑的优启通PE为示例) 优启通v3.3下 ...
- Zookeeper——Docker下安装部署
单节点安装 一. 环境说明 docker: 18.09.9-ce zookeeper: 3.5.6 二. 拉取 zookeeper 镜像 拉取镜像 docker pull zookeeper 默认是摘 ...
- git cherry-pick(不同分支的提交合并)
git cherry-pick可以选择某一个分支中的一个或几个commit(s)来进行操作.例如,假设我们有个稳定版本的分支,叫v2.0,另外还有个开发版本的分支v3.0,我们不能直接把两个分支合并, ...
- DOCKER学习_013:Dockerfile配置指令ENTRYPOINT详解
前面已经介绍了一些Dockerfile的一些指令,对于ENTRYPOINT和CMD也有介绍实验 一 ENTRYPOINT和CMD配置使用 ENTRYPOINT相当于CMD,是配置容器后的一个指令,但是 ...