云原生第三周--kubernetes组件详解
Posted zhaoxiangyu-blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了云原生第三周--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 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必需字段:
- apiVersion - 创建该对象所使用的 Kubernetes API 的版本
- kind - 想要创建的对象的类型
- metadata - 定义识别对象唯一性的数据,包括一个 name 名称 、可选的 namespace
- spec:定义资源对象的详细规范信息(统一的label标签、容器名称、镜像、端口映射等) ##期望状态
- status(Pod创建完成后k8s自动生成status状态)##实际状态
pod简介
- pod是k8s中的最小单元。
- 一个pod中可以运行一个容器,也可以运行多个容器。多容器可以是不同的文件系统。
- 运行多个容器的话,这些容器是一起被调度的。
- Pod的生命周期是短暂的,不会自愈,是用完就销毁的实体。
- 一般我们是通过 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 operator 第1部分:简介
什么是Operator?
Kubernetes平台是围绕控制器的软件设计模式构建的,该控制器是管理两个实体之间数据流的软件组件。在Kubernetes中,控制器监视在一个资源中发现的声明状态的更改,然后通过创建或更改其他下游资源来响应状态更改请求。由于控制器对帐过程连续发生,因此此过程称为“主动对帐”。如图1所示。
创建部署时,可以观察到此行为的一个示例。创建新的部署资源后,将向部署控制器通知资源更改,并通过创建新的副本集做出反应。反过来,副本集控制器对副本集资源做出反应,并导致创建一个或多个Pod。稍后,如果您要修改部署的图像属性,则部署控制器将使用新的图像属性创建新的副本集,同时逐步淘汰旧的副本集。尽管对下游资源采取的操作根据资源而有所不同,但其他控制器的行为类似。
像其他控制器一样,操作员也要注意Kubernetes资源的修改。但是,与Kubernetes平台概念(如部署,状态集和服务(在许多类型的软件中通用))不同,操作员将特定于软件的知识体现在控制器中。考虑一个复杂的工作负载,例如集群数据库,其中需要按照该软件独有的精确顺序来组织常见的操作活动。
实践中的Operator
让我们考虑一个例子。也许升级数据库需要先启动数据格式的先决条件步骤,然后再启动最新版本的容器软件,并且所有吊舱都需要在数据迁移之前停止。或者,可能需要按特定顺序启动Pod,以确保共识算法可以识别所有群集成员。操作员负责协调这些活动,同时利用最终用户可以编辑的资源模型中的声明性或所需状态。
将声明的状态与特定于实现的活动分开,使用户可以在没有特定于软件的知识的情况下控制软件的实例。知识被编码到操作员提供的控制器中。同时,另一软件的操作特性以其自己的方式是独特的,因此具有自己的运算符。
规模化Operator
如果单独部署operator,他们将消耗很少的资源。实际上,我们通过使用Kube Builder SDK和golang语言生成控制器来进行一些分析。然后,我们分析了生成的控制器的实际CPU和内存使用情况,以及对生成的资源请求和限制进行自省。下表中汇总了此信息:
这些数目是针对单个控制器容器的,集群中容器的总数由以下各项确定:
软件包中特定于软件的运算符的数量(Redis的一个运算符,Postgres的一个运算符)。
单个运算符的唯一实例数。为了隔离起见,Redis运算符可能安装在一个命名空间中,而Redis运算符实例的另一个实例存在于另一个命名空间中。
上面的指标是针对每个Pod的,但是出于冗余的考虑,每个操作员部署可能会部署3次。
如果我们要计划由10个名称空间隔离的10个运算符,并且冗余为3,这将导致以下资源消耗:
我们可以对这些数据进行一些重要的观察:
在上述规模下,一个以上的内核将专门用于保持空闲操作员的运行。
除了实际的资源消耗外,operator还计入集群的资源配额。
您选择安装哪些操作程序,以及在什么作用范围内(例如名称空间或群集范围)进行大规模安装。
我们可以无服务器吗?
当然,许多操作员实例的资源利用率可能会影响集群资源需求,但是它是否非常适合无服务器?现实情况是,许多控制器的需求并不恒定,尤其是当单个操作员实例的范围已限于特定的名称空间时。
Kubernetes资源修改事件通常源于两个用户修改单个资源以及通过机器驱动或批处理作业。单个用户可能会强烈地操纵资源一段时间,然后一段时间不会。例如,您可以创建一个Redis集群,然后在根据自己的特定需求微调该集群时编辑各个参数,但是在此之后,您将继续编辑应用程序的其他部分。对于机器驱动的作业,其中一些按计划运行,而另一些则由源更改事件驱动,这些事件通常在工作日左右聚集。
单一资源或资源种类上的活动集群趋向于倾向于无服务器模型。在此模型中,容器进程仅在工作到达时才保持活动状态,但是可以在活动停止的时间段内停止这些容器。
请继续关注有关现有operator部署和新设计模式的更多帖子
随着operator继续在Kubernetes生态系统中获得关注,并且自定义控制器变得越来越普遍,这些容器流程的资源需求值得注意。在本系列的第2部分中,我们将考虑一些既适用于现有operator部署又适用于利用Knative提供无服务器功能的新设计模式的特定技术方法。
原文:https://www.ibm.com/cloud/blog/new-builders/being-frugal-with-kubernetes-operators
本文:http://jiagoushi.pro/node/873
讨论:请加入知识星球【首席架构师圈】或者微信圈子【首席架构师圈】
仙翁小号
如果想进一步讨论,请加仙翁小号【intelligenttimes】,注明你希望加入的群:架构,云计算,大数据,数据科学,物联网,人工智能,安全,全栈开发,DevOps,数字化,产品转型。
微信圈子
如果想和志趣相投的同好交流,请关注仙翁的微信圈子【首席架构师圈】。
如果想向大咖提问,近距离接触,或者获得私密分享,请加入知识星球【首席架构师圈】
以上是关于云原生第三周--kubernetes组件详解的主要内容,如果未能解决你的问题,请参考以下文章
详解kubernetes备份恢复利器 Velero - 深入了解Carina系列第三期
云原生 Kubernetes基于 Minikube 搭建第一个k8s集群
Kubernetes云原生实战01 Kubernetes高可用部署架构