最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

云原生第三周--kubernetes组件详解|每日播报

来源:博客园

etcd组件

etcd简介:

etcd是CoreOS团队于2013年6月发起的开源项目,它的目标是构建一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法,etcd基于Go语言实现。


(相关资料图)

etcd具有下面这些属性:

完全复制:集群中的每个节点都可以使用完整的存档高可用性:Etcd可用于避免硬件的单点故障或网络问题一致性:每次读取都会返回跨多主机的最新写入简单:包括一个定义良好、面向用户的API(gRPC)安全:实现了带有可选的客户端证书身份验证的自动化TLS快速:每秒10000次写入的基准速度可靠:使用Raft算法实现了存储的合理分布Etcd的工作原理

etcd配置文件

root@192:~# cat /etc/systemd/system/etcd.service [Unit]Description=Etcd ServerAfter=network.targetAfter=network-online.targetWants=network-online.targetDocumentation=https://github.com/coreos[Service]Type=notifyWorkingDirectory=/var/lib/etcd  #数据保存目录ExecStart=/usr/bin/etcd \  #二进制文件路径  --name=etcd-192.168.110.180 \  #当前node 名称  --cert-file=/etc/kubernetes/ssl/etcd.pem \  --key-file=/etc/kubernetes/ssl/etcd-key.pem \  --peer-cert-file=/etc/kubernetes/ssl/etcd.pem \  --peer-key-file=/etc/kubernetes/ssl/etcd-key.pem \  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem \  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem \  --initial-advertise-peer-urls=https://192.168.110.180:2380 \#通告自己集群端口  --listen-peer-urls=https://192.168.110.180:2380 \ #集群之间通讯端口  --listen-client-urls=https://192.168.110.180:2379,http://127.0.0.1:2379 \#客户端访问地址  --advertise-client-urls=https://192.168.110.180:2379 \#通告自己的客户端端口  --initial-cluster-token=etcd-cluster-0 \#创建集群使用的token,一个集群内的节点保持一致  --initial-cluster=etcd-192.168.110.180=https://192.168.110.180:2380,etcd-192.168.110.181=https://192.168.110.181:2380,etcd-192.168.110.183=https://192.168.110.183:2380 \#集群所有节点信息  --initial-cluster-state=new \新建集群时为new,若是已经存在的集群为existing  --data-dir=/var/lib/etcd \数据目录路径  --wal-dir= \  --snapshot-count=50000 \  --auto-compaction-retention=1 \  --auto-compaction-mode=periodic \  --max-request-bytes=10485760 \  --quota-backend-bytes=8589934592Restart=alwaysRestartSec=15LimitNOFILE=65536OOMScoreAdjust=-999[Install]WantedBy=multi-user.target

etcd-选举

etcd 是一个分布式的k/V存储系统。核心使用了RAFT分布式一致性协议。一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服务器可能会崩溃或变得不可靠,它就不能和其他服务器达成一致状态。这样就需要一种Consensus协议,一致性协议是为了确保容错性,也就是即使系统中有一两个服务器当机,也不会影响其处理过程。为了以容错方式达成一致,我们不可能要求所有服务器100%都达成一致状态,只要超过半数的大多数服务器达成一致就可以了,假设有N台服务器,N/2 +1 就超过半数,代表大多数了。

节点角色:集群中每个节点只能处于 Leader、Follower 和 Candidate 三种状态的一种follower:追随者(Redis Cluster的Slave节点)candidate:候选节点,选举过程中。leader:主节点(Redis Cluster的Master节点)

节点启动后基于termID(任期ID)进行相互投票,termID是一个整数默认值为0,在Raft算法中,一个term代表leader的一段任期周期,每当一个节点成为leader时,就会进入一个新的term, 然后每个节点都会在自己的term ID上加1,以便与上一轮选举区分开来。选举过程

首次选举:1、各etcd节点启动后默认为 follower角色、默认termID为0、如果发现集群内没有leader,则会变成 candidate角色并进行选举 leader。2、candidate(候选节点)向其它候选节点发送投票信息(RequestVote),默认投票给自己。3、各候选节点相互收到另外的投票信息(如A收到BC的,B收到AC的,C收到AB的),然后对比日志是否比自己的更新,如果比自己的更新,则将自己的选票投给目的候选人,并回复一个包含自己最新日志信息的响应消息,如果C的日志更新,那么将会得到A、B、C的投票,则C全票当选,如果B挂了,得到A、C的投票,则C超过半票当选。4、C向其它节点发送自己leader心跳信息,以维护自己的身份(heartbeat-interval、默认100毫秒)。5、其它节点将角色切换为Follower并向leader同步数据。6、如果选举超时(election-timeout )、则重新选举,如果选出来两个leader,则超过集群总数半票的生效。后期选举:当一个follower节点在规定时间内未收到leader的消息时,它将转换为candidate状态,向其他节点发送投票请求(自己的term ID和日志更新记录),并等待其他节点的响应,如果该candidate的(日志更新记录最新),则会获多数投票,它将成为新的leader。新的leader将自己的termID +1 并通告至其它节点。如果旧的leader恢复了,发现已有新的leader,则加入到已有的leader中并将自己的term ID更新为和leader一致,在同一个任期内所有节点的term ID是一致的。

附录:raft选举机制示例 非常清晰

thesecretlivesofdata.com/raft/

配置优化--max-request-bytes=10485760#request size limit 10M(请求的最大字节数默认1.5M,官方推荐不超过10B)--quota-backend-bytes=8589934592#storage size limit(磁盘存储空间大小限制,默认为2G,此值超过8G启动会有警告信息)

集群碎片整理ETCDCTL_API=3 /usr/local/bin/etcdctl defrag --cluster --endpoints=https://192.168.110.206 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/kubernetes/ssl/etcd.pem --key=/etc/kubernetes/ssl/etcd-key.pem

etcd 查看成员信息

etcd有多个不同的API访问版本,v1版本已经废弃,etcd v2 和 v3 本质上是共享同一套 raft 协议代码的两个独立的应用,接口不一样,存储不一样,数据互相隔离。也就是说如果从 Etcd v2 升级到 Etcd v3,原来v2 的数据还是只能用 v2 的接口访问,v3 的接口创建的数据也只能访问通过v3 的接口访问。

ETCDCTL_API=3 etcdctl --help使用v3版本 查看帮助

ETCDCTL_API=3 etcdctl --write-out=table member list --endpoints=https://192.168.110.180:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/kubernetes/ssl/etcd.pem --key=/etc/kubernetes/ssl/etcd-key.pem查看成员信息

验证节点心跳状态

for ip in ${NODE_IPS}; do ETCDCTL_API=3 etcdctl --endpoints=https://${ip}:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/kubernetes/ssl/etcd.pem --key=/etc/kubernetes/ssl/etcd-key.pem endpoint health; done

查看详细信息for ip in ${NODE_IPS}; do ETCDCTL_API=3 etcdctl --write-out=table endpoint status --endpoints=https://${ip}:2379 --cacert=/etc/kubernetes/ssl/ca.pem --cert=/etc/kubernetes/ssl/etcd.pem -- key=/etc/kubernetes/ssl/etcd-key.pem; done

查看etcd数据ETCDCTL_API=3 etcdctl get / --prefix --keys-only#以路径的方式查询所有key信息ETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep pod查看podETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep namespaces查看namespaceETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep deployment查看deploymentETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep calico查看calico

etcd增删改查添加 putETCDCTL_API=3 etcdctl put /name "lily"

查询 getETCDCTL_API=3 etcdctl get /name

改动数据 put直接覆盖ETCDCTL_API=3 etcdctl put /name "lusy"

删除数据 delETCDCTL_API=3 etcdctl del /name "lusy"

etcd进阶etcd数据watch机制基于不断监看数据,发生变化就主动触发通知客户端,Etcd v3 的watch机制支持watch某个固定的key,也支持watch一个范围ETCDCTL_API=3 etcdctl watch /dataETCDCTL_API=3 etcdctl put /data "test v1"ETCDCTL_API=3 etcdctl put /data "test v2"

etcd V3 API版本数据备份与恢复

WAL是write ahead log(预写日志)的缩写,顾名思义,也就是在执行真正的写操作之前先写一个日志,预写日志。wal: 存放预写式日志,最大的作用是记录了整个数据变化的全部历程。在etcd中,所有数据的修改在提交前,都要先写入到WAL中

V3备份数据命令

ETCDCTL_API=3 etcdctl snapshot save snapshot.db备分到WAL中

V3恢复数据命令

ETCDCTL_API=3 etcdctl snapshot restore snapshot.db --data-dir=/opt/etcd-v3bak#将数据恢复到一个新的不存在的目录中 如果目录存在就会报错!

自动备份数据

mkdir /data/etcd-backup-dir/ -pcat etcd-backup.sh#!/bin/bashsource /etc/profileDATE=`date +%Y-%m-%d_%H-%M-%S`ETCDCTL_API=3 etcdctl snapshot save /data/etcd-backup-dir/etcd-snapshot-${DATE}.db

etcd集群V3版本数据备份与恢复

./ezctl backup k8s-master1

备份目录在 clusters/[clusters-name]/backup查看备份文件覆盖文件删除掉两个pod 然后恢复

./ezctl restore k8s-cluster1

重新查看pod 又恢复回来了

集群也正常了

当etcd集群宕机数量超过集群总节点数一半以上的时候(如总数为三台宕机两台),就会导致整合集群宕机,后期需要重新恢复数据,则恢复流程如下:1、恢复服务器系统2、重新部署ETCD集群3、停止kube-apiserver/controller-manager/scheduler/kubelet/kube-proxy4、停止ETCD集群5、各ETCD节点恢复同一份备份数据6、启动各节点并验证ETCD集群7、启动kube-apiserver/controller-manager/scheduler/kubelet/kube-proxy8、验证k8s master状态及pod数据

coredns

CoreDNS 是一个灵活可扩展的 DNS 服务器,可以作为 Kubernetes 集群 DNS。 与 Kubernetes 一样,CoreDNS 项目由 CNCF 托管。

coredns插件配置

errors:错误信息标准输出。health:在CoreDNS的 http://localhost:8080/health 端口提供 CoreDNS 服务的健康报告。ready:监听8181端口,当coredns的插件都已就绪时,访问该接口会返回 200OK。kubernetes:CoreDNS 将基于 kubernetes service name进行 DNS 查询并返回查询记录给客户端.prometheus:CoreDNS 的度量指标数据以 Prometheus 的key-value的格式在http://localhost:9153/metrics URI上提供。forward: 不是Kubernetes 集群内的其它任何域名查询都将转发到 预定义的目的server,如 (/etc/resolv.conf或IP(如8.8.8.8)).cache:启用 service解析缓存,单位为秒。loop:检测域名解析是否有死循环,如coredns转发给内网DNS服务器,而内网DNS服务器又转发给coredns,如果发现解析是死循环,则强制中止 CoreDNS 进程(kubernetes会重建)。reload:检测corefile是否更改,在重新编辑configmap 配置后,默认2分钟后会优雅的自动加载。loadbalance:轮训DNS域名解析, 如果一个域名存在多个记录则轮训解析。

coredns域名解析pod优先从coredns查找可解析域名,如没有则转到powerdns查找,还找不到最后从223.6.6.6查找。

示例kubectl run net-test1 --image=centos:7.9.2009 sleep 360000kubectl exec -it net-test1 bashyum install net-tools bind-utilsnslookup kubernetes

k8s设计理念

API设计原则

  • 所有API应该是声明式的。
  • API对象是彼此互补而且可组合的。
  • 高层API以操作意图为基础设计。
  • 低层API根据高层API的控制需要设计。
  • 尽量避免简单封装,不要有在外部API无法显式知道的内部隐藏的机制。
  • API操作复杂度与对象数量成正比。
  • API对象状态不能依赖于网络连接状态。
  • 尽量避免让操作机制依赖于全局状态,因为在分布式系统中要保证全局状态的同步是非常困难的。

kubernetes 内置API及资源对象查看k8s api资源对象命令如下:curl --cacert /etc/kubernetes/ssl/ca.pem -H "Authorization: Bearer TOKEN" https://127.0.0.1:6443

资源对象类别简介

kubernetes 资源对象详解及示例

yaml详解

yaml文件:为了方便后期管理,通过使用yaml文件通过API管理资源对象。yaml必需字段:

  1. apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  2. kind - 想要创建的对象的类型
  3. metadata - 定义识别对象唯一性的数据,包括一个 name 名称 、可选的 namespace
  4. spec:定义资源对象的详细规范信息(统一的label标签、容器名称、镜像、端口映射等) ##期望状态
  5. status(Pod创建完成后k8s自动生成status状态)##实际状态

pod简介

  1. pod是k8s中的最小单元。
  2. 一个pod中可以运行一个容器,也可以运行多个容器。多容器可以是不同的文件系统。
  3. 运行多个容器的话,这些容器是一起被调度的。
  4. Pod的生命周期是短暂的,不会自愈,是用完就销毁的实体。
  5. 一般我们是通过 Controller 来创建和管理 pod

示例:创建一个php/nginx多容器pod

其中php容器添加command语句是为了方式容器因自动关闭而报错

进入pod时如果有多容器,需要用-c指定想要进入的容器,不添加默认进入最先创建的容器

Job

Job 会创建一个或者多个 Pod,并将继续重试 Pod 的执行,直到指定数量的 Pod 成功终止。 随着 Pod 成功结束,Job 跟踪记录成功完成的 Pod 个数。 当数量达到指定的成功个数阈值时,任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pod。 挂起 Job 的操作会删除 Job 的所有活跃 Pod,直到 Job 被再次恢复执行。一种简单的使用场景下,你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时,Job 对象会启动一个新的 Pod。

创建一个job该job生成实时时间并输出到容器中/cache/data.log中,然后将/cache目录挂载到宿主机/tmp/jobdata中。

kubectl apply -f 1.job.yaml

如上图 job在worker01生成一个pod,因此容器中的/cahce/就被挂载到worker01的/tmp/jobdata

Cronjob

CronJob 创建基于时隔重复调度的 Job。CronJob 用于执行排期操作,例如备份、生成报告等。 一个 CronJob 对象就像 Unix 系统上的 crontab(cron table)文件中的一行。 它用 Cron 格式进行编写, 并周期性地在给定的调度时间执行 Job。kubectl apply -f 2.cronjob.yaml

Replication Controller/ReplicaSet/Deployment

ReplicationController第一代副本控制器,确保在任何时候都有特定数量的 Pod 副本处于运行状态。 换句话说,ReplicationController 确保一个 Pod 或一组同类的 Pod 总是可用的。ReplicaSet第二代副本控制器,目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。与ReplicationController区别是其中selector 选项还支持in notin的用法。Deployments第三代副本控制器,目前最推荐使用。为 Pod 和 ReplicaSet 提供声明式的更新能力。你负责描述 Deployment 中的 目标状态,而 Deployment 控制器(Controller) 以受控速率更改实际状态, 使其变为期望状态。你可以定义 Deployment 以创建新的 ReplicaSet,或删除现有 Deployment, 并通过新的 Deployment 收养其资源。

创建Deployment的yaml

测试Deployment滚动更新特性,更改yaml中nginx镜像

重新执行yaml文件

从上面两图可以看出 deployment优先创建一个新镜像pod,等其running后创建第二个新pod时将第一个旧pod关闭,如此可以保证至少有2个pod正常运行,以保证服务稳定。

最后结果滚动替换pod成功。

deployment滚动更新机制Deployment 可确保在更新时仅关闭一定数量的 Pod。默认情况下,它确保至少所需 Pod 的 75% 处于运行状态(最大不可用比例为 25%)。

Deployment 还确保仅所创建 Pod 数量只可能比期望 Pod 数高一点点。 默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 125%(最大峰值 25%)。例如,如果仔细查看上述 Deployment ,将看到它首先创建了一个新的 Pod,然后删除旧的 Pod, 并创建了新的 Pod。它不会杀死旧 Pod,直到有足够数量的新 Pod 已经出现。 在足够数量的旧 Pod 被杀死前并没有创建新 Pod。它确保至少 3 个 Pod 可用, 同时最多总共 4 个 Pod 可用。 当 Deployment 设置为 4 个副本时,Pod 的个数会介于 3 和 5 之间。

service

Kubernetes 中 Service 是 将运行在一个或一组 Pod 上的网络应用程序公开为网络服务的方法。 它的一个关键目标是让你无需修改现有应用程序就能使用不熟悉的服务发现机制。 你可以在 Pod 中运行代码,无需顾虑这是为云原生世界设计的代码,还是为已容器化的老应用程序设计的代码。 你可以使用 Service 让一组 Pod 在网络上可用,让客户端能够与其交互。

ervice API 是 Kubernetes 的组成部分,它是一种抽象,帮助你通过网络暴露 Pod 组合。 每个 Service 对象定义一个逻辑组的端点(通常这些端点是 Pod)以及如何才能访问这些 Pod 的策略。

Service服务类型

Kubernetes ServiceTypes 允许指定你所需要的 Service 类型。

Type 的取值以及行为如下:

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是你没有为服务显式指定 type 时使用的默认值。 你可以使用 Ingress 或者 Gateway API 向公众暴露服务。
  • NodePort:通过每个节点上的 IP 和静态端口(NodePort)暴露服务。 为了让节点端口可用,Kubernetes 设置了集群 IP 地址,这等同于你请求 type: ClusterIP 的服务。
  • LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
  • ExternalName:通过返回 CNAME 记录和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。

clusterIP集群模式

apiVersion: v1kind: Servicemetadata:  name: ng-deploy-80spec:  ports:  - name: http    port: 80   ##暴露给服务的端口    targetPort: 80  ##容器的端口    protocol: TCP  type: ClusterIP ##指定服务类型为ClusterIP  selector: ##标签选择器,必须指定pod资源本身的标签    app: ng-deploy-80

生成的svc如下,可以看到endpoints有两个ip,这是发现并加入了2个拥有app=ng-deploy-80相同标签的pod。

进入pod内部curl 集群ip 可以获取pod信息

nodeport方式

apiVersion: v1kind: Servicemetadata:  name: ng-deploy-80spec:  ports:  - name: http    port: 81    targetPort: 80    nodePort: 30012    #与cluster相比,增加了svc服务端口,该地址为集群外部访问内部pod时使用的端口。    protocol: TCP  type: NodePort    #svc改为nodeport方式  selector:    app: ng-deploy-80

如图可见,由于selector匹配成功,svc绑定了两2个endpoint;同时暴露了宿主机外网端口30012,集群外可以通过访问宿主机30012端口,访问到svc的cluster ip,再由svc的endpoint找到后端pod ip,以达到访问pod内容的效果。

下图是将nodeip地址由haproxy代理,外网可以访问haproxy虚拟ip来访问到pod内容。

nodeport模式外网访问pod过程

外网(global)先通过防火墙访问slb,然后由slb将请求转到宿主机外网端口(node1,node2,node3),从而找到集群svc的clusterip,然后由svc根据自己的selector;将请求转给匹配的pod ip(endpoint),从而访问到pod。

volume存储卷

为了保证数据的持久性,必须保证数据在外部存储在docker容器中,为了实现数据的持久性存储,在宿主机和容器内做映射,可以保证在容器的生命周期结束,数据依旧可以实现持久性存储。但是在k8s中,由于pod分布在各个不同的节点之上,并不能实现不同节点之间持久性数据的共享,并且,在节点故障时,可能会导致数据的永久性丢失。为此,k8s就引入了外部存储卷的功能。

常用的几种卷:

  • Secret:是一种包含少量敏感信息例如密码、令牌或密钥的对象
  • configmap: 配置文件
  • emptyDir:本地临时卷
  • hostPath:本地存储卷
  • nfs等:网络存储卷

emptyDir

一个emptyDir 第一次创建是在一个pod被指定到具体node的时候,并且会一直存在在pod的生命周期当中,正如它的名字一样,它初始化是一个空的目录,pod中的容器都可以读写这个目录,这个目录可以被挂在到各个容器相同或者不相同的的路径下。当一个pod因为任何原因被移除的时候,这些数据会被永久删除。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod.

emptyDir 磁盘的作用:(1)普通空间,基于磁盘的数据存储(2)作为从崩溃中恢复的备份点(3)存储那些那些需要长久保存的数据,例web服务中的数据

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-deploymentspec:  replicas: 1  selector:    matchLabels: #rs or deployment      app: ng-deploy-80  template:    metadata:      labels:        app: ng-deploy-80    spec:      containers:      - name: ng-deploy-80        image: nginx        ports:        - containerPort: 80        volumeMounts:        - mountPath: /cache #容器内部卷地址          name: cache-volume      volumes:      - name: cache-volume        emptyDir: {}

在存储卷里创建1.txt文件

在pod所在宿主机可以找到该文件

删除pod后 宿主机该文件也消失了,证明emptydir无法持久保存数据。

hostPathhostPath宿主机路径,就是把pod所在的宿主机之上的脱离pod中的容器名称空间的之外的宿主机的文件系统的某一目录和pod建立关联关系,在pod删除时,存储数据不会丢失。

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-deploymentspec:  replicas: 1  selector:    matchLabels:      app: ng-deploy-80  template:    metadata:      labels:        app: ng-deploy-80    spec:      containers:      - name: ng-deploy-80        image: nginx        ports:        - containerPort: 80        volumeMounts:        - mountPath: /cache   ##容器存储卷位置          name: cache-volume      volumes:      - name: cache-volume        hostPath:          path: /data/kubernetes  #宿主机存储地址 对应容器存储

创建pod在容器中存储卷里写入文件,而后删除pod

在宿主机存储位置依然可以看到之前写入的文件,可见hostpath模式有数据持久化的能力。

nfs共享存储卷

nfs使的我们可以挂在已经存在的共享到的我们的Pod中,和emptyDir不同的是,emptyDir会被删除当我们的Pod被删除的时候,但是nfs不会被删除,仅仅是解除挂在状态而已,这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递.并且,nfs可以同时被多个pod挂在并进行读写

注意:必须先报纸NFS服务器正常运行在我们进行挂在nfs的时候

(1)在harobr02节点上安装nfs,并配置nfs服务

apt install nfs-servercat /etc/exports/data/k8sdata 192.168.110.0/24(rw,no_root_squash)mkdir -p /data/k8asdata

搭建nfs存储yaml如下:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-deploymentspec:  replicas: 1  selector:    matchLabels:      app: ng-deploy-80  template:    metadata:      labels:        app: ng-deploy-80    spec:      containers:      - name: ng-deploy-80        image: nginx        ports:        - containerPort: 80        volumeMounts:        - mountPath: /usr/share/nginx/html/mysite          name: my-nfs-volume      volumes:      - name: my-nfs-volume        nfs:          server: 192.168.110.184          path: /data/k8sdata---apiVersion: v1kind: Servicemetadata:  name: ng-deploy-80spec:  ports:  - name: http    port: 81    targetPort: 80    nodePort: 30016    protocol: TCP  type: NodePort  selector:    app: ng-deploy-80

创建yaml文件后,在nfs存储中创建1.txt文件

之后在任意node节点访问svc开放的30016端口。可以看到共享的文件1.txt

pv pvc

PersistentVolume(PV)是集群中已由管理员配置的一段网络存储。 集群中的资源就像一个节点是一个集群资源。 PV是诸如卷之类的卷插件,但是具有独立于使用PV的任何单个pod的生命周期。 该API对象捕获存储的实现细节,即NFS,iSCSI或云提供商特定的存储系统。

PersistentVolumeClaim(PVC)是用户存储的请求。PVC的使用逻辑:在pod中定义一个存储卷(该存储卷类型为PVC),定义的时候直接指定大小,pvc必须与对应的pv建立关系,pvc会根据定义去pv申请,而pv是由存储空间创建出来的。pv和pvc是kubernetes抽象出来的一种存储资源。

虽然PersistentVolumeClaims允许用户使用抽象存储资源,但是常见的需求是,用户需要根据不同的需求去创建PV,用于不同的场景。而此时需要集群管理员提供不同需求的PV,而不仅仅是PV的大小和访问模式,但又不需要用户了解这些卷的实现细节。 对于这样的需求,此时可以采用StorageClass资源。这个在前面就已经提到过此方案。

PV是集群中的资源。 PVC是对这些资源的请求,也是对资源的索赔检查。 PV和PVC之间的相互作用遵循这个生命周期:

Provisioning(配置)---> Binding(绑定)--->Using(使用)---> Releasing(释放) ---> Recycling(回收)Provisioning这里有两种PV的提供方式:静态或者动态

静态-->直接固定存储空间:    集群管理员创建一些 PV。它们携带可供集群用户使用的真实存储的详细信息。 它们存在于Kubernetes API中,可用于消费。

动态-->通过存储类进行动态创建存储空间:    当管理员创建的静态 PV 都不匹配用户的 PVC 时,集群可能会尝试动态地为 PVC 配置卷。此配置基于 StorageClasses:PVC 必须请求存储类,并且管理员必须已创建并配置该类才能进行动态配置。 要求该类的声明有效地为自己禁用动态配置。

Binding在动态配置的情况下,用户创建或已经创建了具有特定数量的存储请求和特定访问模式的PersistentVolumeClaim。 主机中的控制回路监视新的PVC,找到匹配的PV(如果可能),并将 PVC 和 PV 绑定在一起。 如果为新的PVC动态配置PV,则循环将始终将该PV绑定到PVC。 否则,用户总是至少得到他们要求的内容,但是卷可能超出了要求。 一旦绑定,PersistentVolumeClaim绑定是排他的,不管用于绑定它们的模式。

如果匹配的卷不存在,PVC将保持无限期。 随着匹配卷变得可用,PVC将被绑定。 例如,提供许多50Gi PV的集群将不匹配要求100Gi的PVC。 当集群中添加100Gi PV时,可以绑定PVC。

UsingPod使用PVC作为卷。 集群检查声明以找到绑定的卷并挂载该卷的卷。 对于支持多种访问模式的卷,用户在将其声明用作pod中的卷时指定所需的模式。一旦用户有声明并且该声明被绑定,绑定的PV属于用户,只要他们需要它。 用户通过在其Pod的卷块中包含PersistentVolumeClaim来安排Pods并访问其声明的PV。

Releasing当用户完成卷时,他们可以从允许资源回收的API中删除PVC对象。 当声明被删除时,卷被认为是“释放的”,但是它还不能用于另一个声明。 以前的索赔人的数据仍然保留在必须根据政策处理的卷上.

ReclaimingPersistentVolume的回收策略告诉集群在释放其声明后,该卷应该如何处理。 目前,卷可以是保留,回收或删除。 保留可以手动回收资源。 对于那些支持它的卷插件,删除将从Kubernetes中删除PersistentVolume对象,以及删除外部基础架构(如AWS EBS,GCE PD,Azure Disk或Cinder卷)中关联的存储资产。 动态配置的卷始终被删除

Recycling如果受适当的卷插件支持,回收将对卷执行基本的擦除(rm -rf / thevolume / *),并使其再次可用于新的声明。

PersistentVolume参数kubectl explain PersistentVolumeCapacity: #当前PV空间大小,kubectl explain PersistentVolume.spec.capacityaccessModes :访问模式,#kubectl explain PersistentVolume.spec.accessModes

  • ReadWriteOnce – PV只能被单个节点以读写权限挂载,RWO
  • ReadOnlyMany – PV以可以被多个节点挂载但是权限是只读的,ROX
  • ReadWriteMany – PV可以被多个节点是读写方式挂载使用,RWXpersistentVolumeReclaimPolicy #删除机制即删除存储卷卷时候,已经创建好的存储卷由以下删除操作:kubectl explain PersistentVolume.spec.persistentVolumeReclaimPolicy

Retain – 删除PV后保持原装,最后需要管理员手动删除Recycle – 空间回收,及删除存储卷上的所有数据(包括目录和隐藏文件),目前仅支持NFS和hostPathDelete – 自动删除存储卷

volumeMode #卷类型,kubectl explain PersistentVolume.spec.volumeMode定义存储卷使用的文件系统是块设备还是文件系统,默认为文件系统mountOptions #附加的挂载选项列表,实现更精细的权限控制ro等

PersistentVolumeClaim创建参数:

accessModes:PVC 访问模式,#kubectl explain PersistentVolumeClaim.spec.volumeMode- ReadWriteOnce – PVC只能被单个节点以读写权限挂载,RWO- ReadOnlyMany – PVC以可以被多个节点挂载但是权限是只读的,ROX- ReadWriteMany – PVC可以被多个节点是读写方式挂载使用,RWXresources: #定义PVC创建存储卷的空间大小selector: #标签选择器,选择要绑定的PVmatchLabels #匹配标签名称matchExpressions #基于正则表达式匹配volumeName #要绑定的PV名称volumeMode #卷类型定义PVC使用的文件系统是块设备还是文件系统,默认为文件系统

Volume-存储卷类型static:静态存储卷 ,需要在使用前手动创建PV、然后创建PVC并绑定到PV,然后挂载至pod使用,适用于PV和PVC相对比较固定的业务场景。dynamin:动态存储卷,先创建一个存储类storageclass,后期pod在使用PVC的时候可以通过存储类动态创建PVC,适用于有状态服务集群如MySQL一主多从、zookeeper集群等。

Volume静态存储卷示例创建pv

apiVersion: v1kind: PersistentVolumemetadata:  name: myserver-myapp-static-pv  namespace: myserverspec:  capacity:    storage: 10Gi  accessModes:    - ReadWriteOnce  nfs:    path: /data/k8sdata/myserver/myappdata    server: 192.168.110.184   #存储地址

创建pvc

apiVersion: v1kind: PersistentVolumeClaimmetadata:  name: myserver-myapp-static-pvc  namespace: myserverspec:  volumeName: myserver-myapp-static-pv  accessModes:    - ReadWriteOnce  resources:    requests:      storage: 10Gi

创建pod和svc

kind: Deployment#apiVersion: extensions/v1beta1apiVersion: apps/v1metadata:  labels:    app: myserver-myapp   name: myserver-myapp-deployment-name  namespace: myserverspec:  replicas: 1   selector:    matchLabels:      app: myserver-myapp-frontend  template:    metadata:      labels:        app: myserver-myapp-frontend    spec:      containers:        - name: myserver-myapp-container          image: nginx:1.20.0           #imagePullPolicy: Always          volumeMounts:          - mountPath: "/usr/share/nginx/html/statics"            name: statics-datadir      volumes:        - name: statics-datadir          persistentVolumeClaim:            claimName: myserver-myapp-static-pvc ---kind: ServiceapiVersion: v1metadata:  labels:    app: myserver-myapp-service  name: myserver-myapp-service-name  namespace: myserverspec:  type: NodePort  ports:  - name: http    port: 80    targetPort: 80    nodePort: 30080  selector:    app: myserver-myapp-frontend

执行创建命令

kubectl apply -f 1-myapp-persistentvolume.yaml kubectl apply -f 2-myapp-persistentvolumeclaim.yaml kubectl apply -f 3-myapp-webserver.yaml 

查看pv与pvc绑定状态

可以看到pv已经和pvc绑定查看svc和pod状态

修改haproxy 后端端口为30080 ,访问成功

Volume动态存储卷 StorageClass

在pv和pvc使用过程中存在的问题,在pvc申请存储空间时,未必就有现成的pv符合pvc申请的需求,上面nfs在做pvc可以成功的因素是因为我们做了指定的需求处理。那么当PVC申请的存储空间不一定有满足PVC要求的PV事,又该如何处理呢?为此,Kubernetes为管理员提供了描述存储"class(类)"的方法(StorageClass)。举个例子,在存储系统中划分一个1TB的存储空间提供给Kubernetes使用,当用户需要一个10G的PVC时,会立即通过restful发送请求,从而让存储空间创建一个10G的image,之后在我们的集群中定义成10G的PV供给给当前的PVC作为挂载使用。在此之前我们的存储系统必须支持restful接口,比如ceph分布式存储,而glusterfs则需要借助第三方接口完成这样的请求。如图:

使用示例:1 创建rbac

apiVersion: v1kind: Namespacemetadata:  name: nfs---apiVersion: v1kind: ServiceAccountmetadata:  name: nfs-client-provisioner  # replace with namespace where provisioner is deployed  namespace: nfs---kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: nfs-client-provisioner-runnerrules:  - apiGroups: [""]    resources: ["nodes"]    verbs: ["get", "list", "watch"]  - apiGroups: [""]    resources: ["persistentvolumes"]    verbs: ["get", "list", "watch", "create", "delete"]  - apiGroups: [""]    resources: ["persistentvolumeclaims"]    verbs: ["get", "list", "watch", "update"]  - apiGroups: ["storage.k8s.io"]    resources: ["storageclasses"]    verbs: ["get", "list", "watch"]  - apiGroups: [""]    resources: ["events"]    verbs: ["create", "update", "patch"]---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: run-nfs-client-provisionersubjects:  - kind: ServiceAccount    name: nfs-client-provisioner    # replace with namespace where provisioner is deployed    namespace: nfsroleRef:  kind: ClusterRole  name: nfs-client-provisioner-runner  apiGroup: rbac.authorization.k8s.io---kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata:  name: leader-locking-nfs-client-provisioner  # replace with namespace where provisioner is deployed  namespace: nfsrules:  - apiGroups: [""]    resources: ["endpoints"]    verbs: ["get", "list", "watch", "create", "update", "patch"]---kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:  name: leader-locking-nfs-client-provisioner  # replace with namespace where provisioner is deployed  namespace: nfssubjects:  - kind: ServiceAccount    name: nfs-client-provisioner    # replace with namespace where provisioner is deployed    namespace: nfsroleRef:  kind: Role  name: leader-locking-nfs-client-provisioner  apiGroup: rbac.authorization.k8s.io

2 创建存储类 用于自动创建pv

apiVersion: storage.k8s.io/v1kind: StorageClassmetadata:  name: managed-nfs-storage  annotations:    storageclass.kubernetes.io/is-default-class: "true" #将该类设定为默认类,之后创建pvc后会自动创建pv匹配provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment"s env PROVISIONER_NAME"reclaimPolicy: Retain #PV的删除策略,默认为delete,删除PV后立即删除NFS server的数据mountOptions:  #- vers=4.1 #containerd有部分参数异常  #- noresvport #告知NFS客户端在重新建立网络连接时,使用新的传输控制协议源端口  - noatime #访问文件时不更新文件inode中的时间戳,高并发环境可提高性能parameters:  #mountOptions: "vers=4.1,noresvport,noatime"  archiveOnDelete: "true"  #删除pod时保留pod数据,默认为false时为不保留数据 

3 创建deployment (pod)

apiVersion: apps/v1kind: Deploymentmetadata:  name: nfs-client-provisioner  labels:    app: nfs-client-provisioner  # replace with namespace where provisioner is deployed  namespace: nfsspec:  replicas: 1  strategy: #部署策略    type: Recreate  selector:    matchLabels:      app: nfs-client-provisioner  template:    metadata:      labels:        app: nfs-client-provisioner    spec:      serviceAccountName: nfs-client-provisioner      containers:        - name: nfs-client-provisioner          #image: k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2           image: registry.cn-qingdao.aliyuncs.com/zhangshijie/nfs-subdir-external-provisioner:v4.0.2           volumeMounts:            - name: nfs-client-root              mountPath: /persistentvolumes          env:            - name: PROVISIONER_NAME              value: k8s-sigs.io/nfs-subdir-external-provisioner            - name: NFS_SERVER              value: 192.168.110.184            - name: NFS_PATH              value: /data/volumes      volumes:        - name: nfs-client-root          nfs:            server: 192.168.110.184            path: /data/volumes

4 创建绑定类的pvc

kind: PersistentVolumeClaimapiVersion: v1metadata:  name: myserver-myapp-dynamic-pvc  namespace: myserverspec:  storageClassName: managed-nfs-storage #调用的storageclass 名称  accessModes:    - ReadWriteMany #访问权限  resources:    requests:      storage: 500Mi #空间大小

5创建svc

kind: Deployment#apiVersion: extensions/v1beta1apiVersion: apps/v1metadata:  labels:    app: myserver-myapp   name: myserver-myapp-deployment-name  namespace: myserverspec:  replicas: 1   selector:    matchLabels:      app: myserver-myapp-frontend  template:    metadata:      labels:        app: myserver-myapp-frontend    spec:      containers:        - name: myserver-myapp-container          image: nginx:1.20.0           #imagePullPolicy: Always          volumeMounts:          - mountPath: "/usr/share/nginx/html/statics"            name: statics-datadir      volumes:        - name: statics-datadir          persistentVolumeClaim:            claimName: myserver-myapp-dynamic-pvc ---kind: ServiceapiVersion: v1metadata:  labels:    app: myserver-myapp-service  name: myserver-myapp-service-name  namespace: myserverspec:  type: NodePort  ports:  - name: http    port: 80    targetPort: 80    nodePort: 30080  selector:    app: myserver-myapp-frontend

在nfs服务器上创建/data/volumes目录 用于存储

查看创建的pv pvc pod

上图看可以看到 为匹配pvc由storageclass 两个自动创建的pv

pvc已经绑定

访问负载均衡入口 成功

关键词: