云原生第三周--kubernetes组件详解(代码片段)

zhaoxiangyu-blog zhaoxiangyu-blog     2023-05-12     756

关键词:

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 Server
After=network.target
After=network-online.target
Wants=network-online.target
Documentation=https://github.com/coreos

[Service]
Type=notify
WorkingDirectory=/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=8589934592
Restart=always
RestartSec=15
LimitNOFILE=65536
OOMScoreAdjust=-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查看pod
ETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep namespaces查看namespace
ETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep deployment查看deployment
ETCDCTL_API=3 etcdctl get / --prefix --keys-only | grep calico查看calico

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

查询 get
ETCDCTL_API=3 etcdctl get /name

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

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

etcd进阶etcd数据watch机制
基于不断监看数据,发生变化就主动触发通知客户端,Etcd v3 的watch机制支持watch某个固定的key,也支持watch一个范围
ETCDCTL_API=3 etcdctl watch /data
ETCDCTL_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/ -p
cat etcd-backup.sh
#!/bin/bash
source /etc/profile
DATE=`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-proxy
4、停止ETCD集群
5、各ETCD节点恢复同一份备份数据
6、启动各节点并验证ETCD集群
7、启动kube-apiserver/controller-manager/scheduler/kubelet/kube-proxy
8、验证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 360000
kubectl exec -it net-test1 bash
yum install net-tools bind-utils
nslookup 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: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  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: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  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/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  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无法持久保存数据。

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


apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  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-server
cat /etc/exports
/data/k8sdata 192.168.110.0/24(rw,no_root_squash)
mkdir -p /data/k8asdata

搭建nfs存储yaml如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  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: v1
kind: Service
metadata:
  name: ng-deploy-80
spec:
  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。

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

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

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

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

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

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

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

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

PersistentVolumeClaim创建参数:

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

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

Volume静态存储卷示例
创建pv

apiVersion: v1
kind: PersistentVolume
metadata:
  name: myserver-myapp-static-pv
  namespace: myserver
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /data/k8sdata/myserver/myappdata
    server: 192.168.110.184   #存储地址

创建pvc

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myserver-myapp-static-pvc
  namespace: myserver
spec:
  volumeName: myserver-myapp-static-pv
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

创建pod和svc

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: myserver-myapp 
  name: myserver-myapp-deployment-name
  namespace: myserver
spec:
  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: Service
apiVersion: v1
metadata:
  labels:
    app: myserver-myapp-service
  name: myserver-myapp-service-name
  namespace: myserver
spec:
  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: v1
kind: Namespace
metadata:
  name: nfs
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - 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: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: nfs
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: nfs
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

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

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  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/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: nfs
spec:
  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: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: myserver-myapp-dynamic-pvc
  namespace: myserver
spec:
  storageClassName: managed-nfs-storage #调用的storageclass 名称
  accessModes:
    - ReadWriteMany #访问权限
  resources:
    requests:
      storage: 500Mi #空间大小

5创建svc

kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
  labels:
    app: myserver-myapp 
  name: myserver-myapp-deployment-name
  namespace: myserver
spec:
  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: Service
apiVersion: v1
metadata:
  labels:
    app: myserver-myapp-service
  name: myserver-myapp-service-name
  namespace: myserver
spec:
  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已经绑定

访问负载均衡入口 成功

云原生训练营模块六kubernetes控制平面组件:apiserver(代码片段)

...0、APIServer概念1、认证基于webhook的认证服务集成构建符合Kubernetes规范的认证服务1、开发认证服务2、配置apiserver2、鉴权Role与ClusterRole账户/组的管理3、准入准入控制准入控制插件4、限流计数器固定窗口算法漏斗算法令牌桶... 查看详情

云原生kubernetes系列第五篇kubeadmv1.20部署k8s集群架构(人生这道选择题,总会有遗憾)(代码片段)

...目实战第一篇】dockerfile+lnmp+workpress😜【云原生Kubernetes系列第一篇】深入理解容器集群管理系统Kubernetes(k8s)😜【云原生Kubernetes系列第二篇】Kubernetes(k8s) 查看详情

云原生kubernetes入门详细讲解

目录1、Kubernetes是什么2、Kubernetes不是什么3、Kubernetes架构与分层4、Kubernetes集群架构5、Kubernetes集群组件5.1、K8s在Master上的组件5.2、K8s在Node上的组件6、KubernetesAPI    在云原生技术与生态快速发展的今天,越来越多的IT厂商... 查看详情

云原生训练营模块五kubernetes控制平面组件:etcd(代码片段)

etcd----------Part1----------etcd概述etcd功能与场景服务注册与发现,消息发布与订阅Etcd的安装etcd工具练习Raft协议❤etcd基于Raft的一致性选举方法日志复制安全性失效处理wal日志----------Part2----------etcdv3存储,Watch以及过期机制... 查看详情

云原生训练营模块五kubernetes控制平面组件:etcd(代码片段)

etcd----------Part1----------etcd概述etcd功能与场景服务注册与发现,消息发布与订阅Etcd的安装etcd工具练习Raft协议❤etcd基于Raft的一致性选举方法日志复制安全性失效处理wal日志----------Part2----------etcdv3存储,Watch以及过期机制... 查看详情

云原生kubernetes基于minikube搭建第一个k8s集群

...搭建github下载发行版二进制包,手动部署每个组件,组成Kubernete 查看详情

详解kubernetes备份恢复利器velero-深入了解carina系列第三期

...re-tanzu/velero。Velero源于西班牙语,意思为帆船,非常符合Kubernetes社区的命名风格。利用velero用户可以安全的备份、恢复和迁移Kubernetes集群资源和持久卷。它的基本原理就是将集群的数据,例如集群资源和持久化数据卷备份到对... 查看详情

kuberntes云原生实战一高可用部署架构(代码片段)

大家好,我是飘渺。从今天开始我们将正式开始Kubernetes云原生实战系列,欢迎持续关注。Kubernets核心组件Kubernetes中组件众多,要完全介绍清楚估计要写上厚厚一本书,我们实战系列主要记住几个核心组件就行ÿ... 查看详情

kuberntes云原生实战一高可用部署架构(代码片段)

大家好,我是飘渺。从今天开始我们将正式开始Kubernetes云原生实战系列,欢迎持续关注。Kubernets核心组件Kubernetes中组件众多,要完全介绍清楚估计要写上厚厚一本书,我们实战系列主要记住几个核心组件就行ÿ... 查看详情

kubernetes云原生实战01kubernetes高可用部署架构

大家好,我是飘渺。从今天开始我们将正式开始Kubernetes云原生实战系列,欢迎持续关注。Kubernets核心组件Kubernetes中组件众多,要完全介绍清楚估计要写上厚厚一本书,我们实战系列主要记住几个核心组件就行,即两种节点,三... 查看详情

kubernetes云原生实战01kubernetes高可用部署架构

大家好,我是飘渺。从今天开始我们将正式开始Kubernetes云原生实战系列,欢迎持续关注。Kubernets核心组件Kubernetes中组件众多,要完全介绍清楚估计要写上厚厚一本书,我们实战系列主要记住几个核心组件就行,即两种节点,三... 查看详情

云原生训练营模块六kubernetes控制平面组件:apiserver(代码片段)

...0、APIServer概念1、认证基于webhook的认证服务集成构建符合Kubernetes规范的认证服务1、开发认证服务2、配置apiserver2、鉴权Role与ClusterRole账户/组的管理3、准入准入控制准入控制插件4、限流计数器固定窗口算法漏斗算法令牌桶... 查看详情

云原生(三十六)|kubernetes篇之harbor入门和安装(代码片段)

文章目录Harbor入门和安装一、入门1、简介2、核心组件3、安装二、docker使用1、基本配置2、镜像代理Harbor入门和安装一、入门1、简介Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器。作为一个企业级私有Registry服务器&#... 查看详情

云原生(三十一)|kubernetes篇之kubernetes平台基本预装资源(代码片段)

文章目录Kubernetes平台基本预装资源一、metrics-server二、ingress-nginx三、dashboard四、helm应用商店Kubernetes平台基本预装资源kubernetes平台安装完成后需要安装基本资源,本文适配kubernetes-v1.21.1版本一、metrics-serverGitHub-kubernetes-sigs/me... 查看详情

云原生|kubernetes篇kubernetes原理与安装(代码片段)

...://bbs.csdn.net/forums/lansonhttps://bbs.csdn.net/forums/lanson文章目录Kubernetes原理与安装一、集群原理1、master-node架构2、工作原理3、原理分解二、组件交互原理三、安装1、理解2、执行Kubernetes原理与安装一、集群原理1、master-node架构master和... 查看详情

云原生核心技术之——kubernetes(代码片段)

...大家介绍的便是云原生技术体系中的‘容器编排引擎——Kubernetes’。回顾:::hljs-center图云原生核心技术:::微服务化的服务与容器在轻量、敏捷 查看详情

云原生技术kubernates基础组件(第六集)(代码片段)

1.整体概述一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。1.1Master集群的控制平面,负责集群的决策(管理)ApiServer:资源操作的唯一入口,接收用户输入的命令,提... 查看详情

云原生devops:kubernetes编排工具(代码片段)

@TOCKubernetes是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。Kubernetes一个核心的特点就是能... 查看详情