https://kubernetes.io/zh/docs/concepts/workloads/controllers/
资源管理核心概念
k8s的设计理念-分层架构:
http://docs.kubernetes.org.cn/251.html
#k8s分层架构
云原生生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
接口层:kubectl命令行工具、客户端SDK以及集群联邦
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
核心层:Kubernetes API和执行环境
容器运行时、容器网络接口、容器存储接口、镜像仓库、云供应商、身份供应商
#三个接口标准:
container runtime interface CRI,容器运行时(run、rkt)
container storage interface CSI,容器存储接口
container network interface CNI,网络接口
#docker info
Default Runtime: runc
如何管理:
kubectl、API、SDK、UI
管理对象:
资源对象:Pod、ReplicaSet、ReplicationController、Deployment(运行无状态服务)、StatefulSet(运行有状态服务)、DaemonSet(自动运行在每个节点的进程)、Job(单次任务,对数据归类并输出时)、CronJob(周期性任务)、HorizontalPodAutoscaling(HPA对pod进行弹性伸缩)、Node(运行容器的虚拟机或物理机)、Namespace、Service、Ingress、Label、CustomResourceDefinition
存储对象:Volume、PersistentVolume、Secret(密钥)、ConfigMap(把配置与镜像解耦)
(直接挂nfs不太安全,都能看到)
策略对象:SecurityContext、ResourceQuota、LimitRange
身份对象:ServiceAccount(多账户)、Role、ClusterRole
API:对象–是K8s集群中的管理操作单元
https://kubernetes.io/zh/docs/concepts/workloads/controllers/deployment/
k8s的设计理念-API设计原则:
https://www.kubernetes.org.cn/kubernetes%E8%AE%BE%E8%AE%A1%E7%90%86%E5%BF%B5
1.所有API应该是声明式的。
2.API对象是彼此互补而且可组合的。
3.高层API以操作意图为基础设计。
4.低层API根据高层API的控制需要设计。
5.尽量避免简单封装,不要有在外部API无法显式知道的内部隐藏的机制。
6.API操作复杂度与对象数量成正比。
7.API对象状态不能依赖于网络连接状态。
8.尽量避免让操作机制依赖于全局状态,因为在分布式系统中要保证全局状态的同步是非常困难的。
k8s的几个重要概念:
对象(用k8s是和什么打交道):K8S声明式API
yaml文件(怎么打交道):调用声明式API
https://kubernetes.io/zh/docs/concepts/workloads/pods/
(1)概述:
1.pod是k8s中的最小单元
2.一个pod中可以运行一个容器,也可以运行多个容器
3.运行多个容器的话,这些容器是一起被调度的
4.Pod的生命周期是短暂的,不会自愈,是用完就销毁的实体
5.一般我们是通过Controller来创建和管理pod的
(2)Controller副本控制器:维持副本个数(Rc,Rs和Deployment)
Replication Controller#第一代pod副本控制器(支持选择器slector = !=)
https://kubernetes.io/zh/docs/concepts/workloads/controllers/replicationcontroller/
https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/labels/
ReplicaSet#第二代pod副本控制器(支持选择器slector、in notin)
https://kubernetes.io/zh/docs/concepts/workloads/controllers/replicaset/
Deployment#第三代pod控制器(支持选择器slector、in notin,滚动升级、回滚)(目前使用)
https://kubernetes.io/zh/docs/concepts/workloads/controllers/deployment/
案例case1:
rc:只支持=、!=
selector:
app: ng-rc-80
rs:还支持in、notin
selector:
matchExpressions:
- {key: app, operator: In, values: [ng-rs-80,ng-rs-81]}
deployment:还可实现版本升级/滚动升级或回滚
[root@k8s-master1 case1]#vim deployment.yml
image: nginx:1.16.1
[root@k8s-master1 case1]#kubectl apply -f deployment.yml
[root@k8s-master1 case1]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d76cf6966-gr8mr 1/1 Running 0 2m2s
nginx-deployment-5d76cf6966-p6j8v 1/1 Running 0 2m2s
[root@k8s-master1 case1]#vim deployment.yml
image: nginx:1.17.1
[root@k8s-master1 case1]#kubectl apply -f deployment.yml
deployment.apps/nginx-deployment configured
[root@k8s-master1 case1]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d76cf6966-gr8mr 1/1 Running 0 2m59s
nginx-deployment-5d76cf6966-p6j8v 1/1 Running 0 2m59s
nginx-deployment-8cbfb8fb-lh4sv 0/1 Terminating 0 37s
nginx-deployment-95ffc4469-dwrtr 0/1 ContainerCreating 0 2s
[root@k8s-master1 case1]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-95ffc4469-dwrtr 1/1 Running 0 62s
nginx-deployment-95ffc4469-r4d9n 1/1 Running 0 43s
(3)Pod生命周期:
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/
初始化容器->启动前操作->就绪探针->存活探针->删除pod操作
(4)pod状态
新建–>调度–>部署启动–>结束
第一阶段:
pending:
#正在创建Pod但是pod中的容器还没有全部被创建完成,处于此状态的pod应该检查pod依赖的存储是否有权限挂载、镜像是否可以下载、调度是否正常等。
Failed
#Pod中有容器启动失败而导致pod工作异常。
Unknown
#由于某种原因无法获得pod的当前状态,通常是由于与pod所在的node节点通信错误。
Succeeded
#Pod中的所有容器都被成功终止即pod里所有的containers均已terminated。
第二阶段:
Unschedulable:
#Pod不能被调度,kube-scheduler没有匹配到合适的node节点
PodScheduled
#pod正处于调度中,在kube-scheduler刚开始调度的时候,还没有将pod分配到指定的node,在筛选出合适的节点后就会更新etcd数据,将pod分配到指定的node
Initialized
#所有pod中的初始化容器已经完成了
ImagePu11BackOff:
#Pod所在的node节点下载镜像失败
Running
#Pod内部的容器已经被创建并且启动。
Ready
#表示pod中的容器已经可以提供访问服务
Error: #pod启动过程中发生错误
NodeLost: #Pod 所在节点失联
Unkown: #Pod 所在节点失联或其它未知异常
Waiting: #Pod 等待启动
pending: #Pod 等待被调度
Terminating: #Pod 正在被销毁
CrashLoopBackoff: #Pod,但是kubelet正在将它重启
InvalidImageName: #node节点无法解析镜像名称导致的镜像无法下载
Image InspectError: #无法校验镜像,镜像不完整导致
ErrImageNeverPul1:#策略禁止拉取镜像,镜像中心权限是私有等
ImagePu11Backoff: #镜像拉取失败,但是正在重新拉取
Registryunavailab1e: #镜像服务器不可用,网络原因或harbor宕机
ErrImagePu11:#镜像拉取出错,超时或下载被强制终止
CreateContainerconfigError: #不能创建kubelet使用的容器配置
CreatecontainerError: #创建容器失败,
Prestartcontainer: #执行prestart hook报错,pod hook(钩子)是由Kubernetes管理的kubelet发起的,当容器中的进程启动前或者容器中的进程终止之前运行,比如容器创建完成后里面的服务启动之前可以检查一下依赖的其它服务是否启动,或者容器退出之前可以把容器中的服务先通过命令停止。
PoststartHookError: #执行poststart hook 报错
RunContainerError: #pod运行失败,容器中没有初始化PTD为1的守护进程等
ContainersNotInitialized: #pod没有初始化完毕
ContainersNotReady: #pod没有准备完毕
ContainerCreating: #pod正在创建中
PodInitializing: #pod正在初始化中
DockerDaemonNotReady: #node节点decker服务没有启动
NetworkpluginNotReady: #网络插件还没有完全启动
(5)pod调度过程
见k8s实战案例2.1.3:kube-scheduler
(6)pod探针
https://kubernetes.io/zh/docs/concepts/workloads/pods/pod-lifecycle/
1/探针简介:
探针是由kubelet 对容器执行的定期诊断,以保证Pod的状态始终处于运行状态,要执行诊断,kubelet 调用由容器实现的Handler(处理程序),有三种类型的处理程序:
ExecAction #在容器内执行指定命令,如果命令退出时返回码为0则认为诊断成功。
TCPSocketAction #对指定端口上的容器的IP地址进行TCP检查,如果端口打开,则诊断被认为是成功的。
HTTPGetAction #对指定的端口和路径上的容器的IP地址执行HTTPGet请求,如果响应的状态码大于等于200且小于400,则诊断被认为是成功的。
每次探测都将获得以下三种结果之一:
成功:容器通过了诊断。
失败:容器未通过诊断。
未知:诊断失败,因此不会采取任何行动。
2/livenessprobe和readinessprobe探针
基于探针实现对pod的状态检测
探针类型:
livenessProbe:存活探针
检测应用发生故障时使用,不能提供服务、超时等检测失败时重启pod
readinessProbe:就绪探针
检测pod启动之后应用是否就绪,是否可以提供服务检测成功,pod才开始接收流量
livenessProbe和readinessProbe的对比:
相同点:配置参数一样
不同点:
livenessProbe连续探测失败会重启、重建pod,readinessProbe不会
livenessProbe连续检测失败后会将容器置于CrashLoopBackOff且不可用,readinessProbe不会
readinessProbe连续探测失败会从service的endpointd中删除该Pod,livenessProbe不具备此功能,但是会将容器挂起livenessProbe
livenessProbe用户控制是否重启pod,readinessProbe用于控制pod是否添加至service
建议:两个探针都配置
面试题:如何保证k8s服务的可用性?
通过存活探针对出现异常时重启pod;通过就绪探针对pod进行状态检查,失败后从service删掉,从而保证用户访问始终正常;再通过日志和监控进一步定位什么原因导致的异常
重启时先把pod地址从service删掉,正常启动后再加到service,使允许访问
3/探针配置:
https://kubernetes.io/zh/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
探针有很多配置字段,可以使用这些字段精确的控制存活和就绪检测的行为:
initialpelaySeconds:120
#初始化延迟时间,告诉kubelet在执行第一次探测前应该等待多少秒,默认是0秒,最小值是0
periodseconds:60
#探测周期间隔时间,指定了kubelet应该每多少秒秒执行一次存活探测,默认是10秒。最小值是1
timeoutSeconds:5
#单次探测超时时间,探测的超时后等待多少秒,默认值是1秒,最小值是1。
successThreshold:1
#从失败转为成功的重试次数,探测器在失败后,被视为成功的最小连续成功数,默认值是1,存活探测的这个值必须是1,最小值是1。
failureThreshold:3
#从成功转为失败的重试次数,当Pod启动了并且探测到失败,Kubernetes的重试次数,存活探测情况下的放弃就意味着重新启动容器,就绪探测情况下的放弃Pod会被打上未就绪的标签,默认值是3,最小值是1。
HTTP探测器可以在httpGet上配置额外的字段:
host:
#连接使用的主机名,默认是pod的IP,也可以在HTTP头中设置“Host”来代替。
scheme: http
#用于设置连接主机的方式(HTTP还是HTTPS),默认是HTTP
path: /monitor/index.html
#访问HTTP服务的路径。
httpHeaders:
#请求中自定义的HTTP头,HTTP头字段允许重复。
port: 80
#访问容器的端口号或者端口名,如果数字必须在1~65535之间。
4\示例:
#1、HTTP探针示例:
#(1)探针url正确时,显示均正常
[root@k8s-master1 tanzhen]#vim nginx.yaml #存活探针正确url
livenessProbe:
httpGet:
path: /index.html
[root@k8s-master1 tanzhen]#vim nginx.yaml #就绪探针正确url
readinessProbe:
httpGet:
path: /index.html
[root@k8s-master1 tanzhen]#kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment configured
service/ng-deploy-80 configured
[root@k8s-master1 tanzhen]#kubectl get pod #0重启
NAME READY STATUS RESTARTS AGE
nginx-deployment-57bf88c9db-5xhr7 1/1 Running 0 15s
[root@k8s-master1 tanzhen]#kubectl get ep #展示ENDPOINTS值
NAME ENDPOINTS AGE
ng-deploy-80 10.200.36.95:80 20s
[root@k8s-master1 tanzhen]#curl http://192.168.150.161:40012/index.html #可正常访问
#(2)存活探针错误url
[root@k8s-master1 tanzhen]#vim nginx.yaml
livenessProbe:
httpGet:
path: /index1.html
[root@k8s-master1 tanzhen]#kubectl get pod #livenessProbe控制重启,连续检测失败后状态是CrashLoopBackOff
NAME READY STATUS RESTARTS AGE
nginx-deployment-6f655b87d7-qh6b8 1/1 Running 0 13s
[root@k8s-master1 tanzhen]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6f655b87d7-qh6b8 1/1 Running 1 24s
[root@k8s-master1 tanzhen]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6f655b87d7-qh6b8 1/1 Running 2 40s
[root@k8s-master1 tanzhen]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-6f655b87d7-qh6b8 0/1 CrashLoopBackOff 3 66s
[root@k8s-master1 tanzhen]#kubectl get ep #无法访问但依旧展示ENDPOINTS值,只控制重启不控制是否允许访问
NAME ENDPOINTS AGE
ng-deploy-80 10.200.36.96:80 3m20s
#(3)就绪探针错误url
[root@k8s-master1 tanzhen]#vim nginx.yaml
readinessProbe:
httpGet:
path: /index1.html
[root@k8s-master1 tanzhen]#kubectl apply -f nginx.yaml
deployment.apps/nginx-deployment created
service/ng-deploy-80 created
[root@k8s-master1 tanzhen]#kubectl get pod #检测失败但状态是Running
NAME READY STATUS RESTARTS AGE
nginx-deployment-d44c95b47-8rg9x 0/1 Running 0 18s
[root@k8s-master1 tanzhen]#kubectl get ep #不能访问,uir错误时会删除ENDPOINTS值,保证不会把请求转发给请求失败的容器
NAME ENDPOINTS AGE
ng-deploy-80 11s
#2、TCP探针示例:
#(1)探针端口正确时,显示均正常
[root@k8s-master1 tanzhen]#vim tcp.yaml #存活探针正确端口
livenessProbe:
tcpSocket:
port: 80
[root@k8s-master1 tanzhen]#vim tcp.yaml #就绪探针正确端口
readinessProbe:
tcpSocket:
port: 80
[root@k8s-master1 tanzhen]#kubectl apply -f tcp.yaml
deployment.apps/nginx-deployment configured
service/ng-deploy-80 unchanged
[root@k8s-master1 tanzhen]#kubectl get pod #0重启
NAME READY STATUS RESTARTS AGE
nginx-deployment-7bd48778d5-pfjx7 1/1 Running 0 20s
[root@k8s-master1 tanzhen]#kubectl get ep #展示ENDPOINTS值
NAME ENDPOINTS AGE
ng-deploy-80 10.200.36.100:80 36m
[root@k8s-master1 tanzhen]#curl http://192.168.150.161:40012/index.html #正常访问
#(2)存活探针错误端口
[root@k8s-master1 tanzhen]#vim tcp.yaml
livenessProbe:
tcpSocket:
port: 8080
[root@k8s-master1 tanzhen]#kubectl apply -f tcp.yaml
deployment.apps/nginx-deployment created
service/ng-deploy-80 created 38s
[root@k8s-master1 tanzhen]#kubectl get pod #多次重启,连续检测失败后状态是CrashLoopBackOff
NAME READY STATUS RESTARTS AGE
nginx-deployment-7bb4b6bd5c-46dvq 0/1 CrashLoopBackOff 3 62s
[root@k8s-master1 tanzhen]#kubectl get ep #访问异常但仍旧展示ENDPOINTS值
NAME ENDPOINTS AGE
ng-deploy-80 10.200.36.109:80 2m17s
#(3)就绪探针错误端口
[root@k8s-master1 tanzhen]#vim tcp.yaml
readinessProbe:
tcpSocket:
port: 8080
[root@k8s-master1 tanzhen]#kubectl apply -f tcp.yaml
deployment.apps/nginx-deployment created
service/ng-deploy-80 created
[root@k8s-master1 tanzhen]#kubectl get pod #0重启
NAME READY STATUS RESTARTS AGE
nginx-deployment-86c8bb75f5-mm4mm 0/1 Running 0 15s
[root@k8s-master1 tanzhen]#kubectl get ep #不能访问,错误端口时会把ENDPOINTS值删除
NAME ENDPOINTS AGE
kubernetes 192.168.150.151:6443 25d
ng-deploy-80 11s
#(4)一般写两个
[root@k8s-master1 tanzhen]#vim tcp.yaml
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
[root@k8s-master1 tanzhen]#kubectl apply -f tcp.yaml
deployment.apps/nginx-deployment created
service/ng-deploy-80 created
[root@k8s-master1 tanzhen]#kubectl get pod #livenessProbe控制重启
NAME READY STATUS RESTARTS AGE
nginx-deployment-585c997f4c-72j9j 1/1 Running 0 8s
[root@k8s-master1 tanzhen]#kubectl get ep #readinessProbe控制访问
NAME ENDPOINTS AGE
kubernetes 192.168.150.151:6443 26d
ng-deploy-80 10.200.36.106:80 11s
#3、ExecAction探针:可以基于指定的命令对Pod进行特定的状态检查
#(1)两个探针配置正确时
[root@k8s-master1 tanzhen]#vim redis.yaml
readinessProbe:
exec:
command:
- /usr/local/bin/redis-cli
- quit
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
livenessProbe:
exec:
command:
- /usr/local/bin/redis-cli
- quit
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
[root@k8s-master1 tanzhen]#kubectl apply -f redis.yaml
deployment.apps/redis-deployment created
service/redis-deploy-6379 created
[root@k8s-master1 tanzhen]#kubectl get pod
NAME READY STATUS RESTARTS AGE
redis-deployment-775c65c67c-vbx9d 1/1 Running 0 51s
[root@k8s-master1 tanzhen]#kubectl get ep
NAME ENDPOINTS AGE
kubernetes 192.168.150.151:6443 26d
redis-deploy-6379 10.200.36.107:6379 56s
#(2)两个探针配置错误时
[root@k8s-master1 tanzhen]#vim redis.yaml
readinessProbe:
exec:
command:
- /usr/local/bin/redis-cli11111
- quit
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
livenessProbe:
exec:
command:
- /usr/local/bin/redis-cli1111
- quit
initialDelaySeconds: 5
periodSeconds: 3
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
[root@k8s-master1 tanzhen]#kubectl apply -f redis.yaml
deployment.apps/redis-deployment created
service/redis-deploy-6379 created
[root@k8s-master1 tanzhen]#kubectl get pod #重启指定次数后仍检测失败,则状态置为CrashLoopBackOff
NAME READY STATUS RESTARTS AGE
redis-deployment-987b6f585-fxkg9 0/1 CrashLoopBackOff 5 2m54s
[root@k8s-master1 tanzhen]#kubectl get ep #该pod从service删除
NAME ENDPOINTS AGE
kubernetes 192.168.150.151:6443 26d
redis-deploy-6379 30s
(7)pod重启策略
k8s在Pod出现异常的时候会自动将Pod重启以恢复Pod中的服务。
restartPolicy:
Always:当容器异常时,k8s自动重启该容器,ReplicationController/Replicaset/Deployment。
onFailure:当容器失败时(容器停止运行且退出码不为0),k8s自动重启该容器。
Never:不论容器运行状态如何都不会重启该容器,Job或cronJob。
(8)镜像拉取策略
https://kubernetes.io/zh/docs/concepts/configuration/overview/
imagePu11Policy: IfNotPresent #node节点没有此镜像就去指定的镜像仓库拉取,node有就使用node本地镜像。
imagePullPolicy: Always #每次重建pod都会重新拉取镜像
imagePu11Policy: Never #从不到镜像中心拉取镜像,只使用本地镜像
Why: pod重建后ip变了,pod之间再次直接访问会有问题
What:解耦了服务和应用。
How:声明一个service对象
一般常用的service有两种:
k8s集群内的service:selector指定pod,自动创建Endpoints
k8s集群外的service:手动创建Endpoints,指定外部服务的ip、端口和协议
kube-proxy和service的关系:
kube-proxy---watch-->k8s-apiserver
kube-proxy监听着k8s-apiserver,一旦service资源发生变化(调k8s-api修改service信息),kube-proxy就会生成对应的负载调度的调整,这样就保证service的最新状态。
#service案例case2:
[root@k8s-master1 case2]#kubectl apply -f 1-deploy_node.yml
deployment.apps/nginx-deployment created
[root@k8s-master1 case2]#kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-deployment-5d76cf6966-nz9cn 1/1 Running 0 4s
[root@k8s-master1 case2]#kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 11d
#默认的ClusterIP类型:
[root@k8s-master1 case2]#vim 2-svc_service.yml
ports:
- name: http
port: 88
targetPort: 80
protocol: TCP
type: ClusterIP
[root@k8s-master1 case2]#kubectl apply -f 2-svc_service.yml
service/ng-deploy-80 created
[root@k8s-master1 case2]#kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 11d
ng-deploy-80 ClusterIP 10.100.229.10 <none> 88/TCP 2s
[root@k8s-master1 case2]#curl 10.100.229.10:88 #正常访问
#使用NodePort类型:
[root@k8s-master1 case2]#kubectl delete -f 2-svc_service.yml
[root@k8s-master1 case2]#vim 3-svc_NodePort.yml
ports:
- name: http
port: 90
targetPort: 80
nodePort: 30012
protocol: TCP
type: NodePort
[root@k8s-master1 case2]#kubectl apply -f 3-svc_NodePort.yml
service/ng-deploy-80 created
[root@k8s-master1 case2]#kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.100.0.1 <none> 443/TCP 11d
ng-deploy-80 NodePort 10.100.125.157 <none> 90:30012/TCP 6s
[root@k8s-master1 case2]#curl 10.100.125.157:90 #正常访问
#配置haproxy进行访问(不带端口访问,公网访问或域名访问)
[root@k8s-ha1 ~]#vim /etc/haproxy/haproxy.cfg
listen magedu-nginx-80
bind 192.168.150.189:80
mode tcp
server k8s1 192.168.150.151:30012 check inter 3s fall 3 rise 5
#server k8s2 192.168.150.152:30012 check inter 3s fall 3 rise 5
#server k8s3 192.168.150.153:30012 check inter 3s fall 3 rise 5
[root@k8s-ha1 ~]#systemctl restart haproxy
!!!重启报错!!!
https://kubernetes.io/zh/docs/concepts/storage/volumes/
Why:数据和镜像解耦,以及容器间的数据共享
What: k8s抽象出的一个对象,用来保存数据,做存储用
常用的几种卷:
emptyDir:本地临时卷(放日志,一般不用)
hostPath:本地卷(不能被数据共享)
nfs等:共享卷
configmap:主要用于配置文件变更和定义环境变量
(1)emptyDir
当Pod被分配给节点时,首先创建 emptyDir卷,并且只要该Pod在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。
Pod中的容器可以读取和写入emptyDir卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除Pod时,emptyDir中的数据将被永久删除。
#宿主机临时路径:/var/lib/kubelet/pods/$ID/volumes/kubernetes.io~empty-dir/cache-volume/$FILE
case3案例:
[root@k8s-master1 case3]#vim deploy_empty.yml
volumeMounts:
- mountPath: /cache
name: cache-volume-n56 #把卷cache-volume-n56挂载到目录/cache
volumes: #先定义再调用
- name: cache-volume-n56
emptyDir: {}
[root@k8s-master1 case3]#kubectl apply -f deploy_empty.yml
[root@k8s-master1 case3]#kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-7d4dcd7f7d-m96zz 1/1 Running 0 9m15s 10.200.36.77 192.168.150.161 <none> <none>
#测试
[root@k8s-master1 case3]#kubectl exec -it nginx-deployment-7d4dcd7f7d-m96zz bash
root@nginx-deployment-7d4dcd7f7d-m96zz:/# cd cache/
root@nginx-deployment-7d4dcd7f7d-m96zz:/cache# echo "123">> n56.txt
root@nginx-deployment-7d4dcd7f7d-m96zz:/cache# cat n56.txt
123
root@nginx-deployment-7d4dcd7f7d-m96zz:/cache# exit
[root@k8s-node1 ~]#cat /var/lib/kubelet/pods/84a807c9-55a8-4b8e-9502-bd033a146a7c/volumes/kubernetes.io~empty-dir/cache-volume-n56/n56.txt
123
[root@k8s-node1 ~]#echo "456">> /var/lib/kubelet/pods/84a807c9-55a8-4b8e-9502-bd033a146a7c/volumes/kubernetes.io~empty-dir/cache-volume-n56/n56.txt
[root@k8s-node1 ~]#cat /var/lib/kubelet/pods/84a807c9-55a8-4b8e-9502-bd033a146a7c/volumes/kubernetes.io~empty-dir/cache-volume-n56/n56.txt
123
456
[root@k8s-master1 case3]#kubectl exec -it nginx-deployment-7d4dcd7f7d-m96zz bash
root@nginx-deployment-7d4dcd7f7d-m96zz:/# cat /cache/n56.txt
123
456
#pod删除后宿主机数据也被删除,不做保留
root@nginx-deployment-7d4dcd7f7d-m96zz:/# rm -rf /cache/n56.txt
[root@k8s-node1 ~]#ls /var/lib/kubelet/pods/84a807c9-55a8-4b8e-9502-bd033a146a7c/volumes/kubernetes.io~empty-dir/cache-volume-n56/
[root@k8s-master1 case3]#kubectl delete -f deploy_empty.yml
deployment.apps "nginx-deployment" deleted
[root@k8s-node1 ~]#ls /var/lib/kubelet/pods/84a807c9-55a8-4b8e-9502-bd033a146a7c/volumes/kubernetes.io~empty-dir/cache-volume-n56/
ls: cannot access '/var/lib/kubelet/pods/84a807c9-55a8-4b8e-9502-bd033a146a7c/volumes/kubernetes.io~empty-dir/cache-volume-n56/': No such file or directory
(2)hostPath
hostPath卷将主机节点的文件系统中的文件或目录挂载到集群中,pod删除的时候,卷不会被删除
hostPath:(不常用,因为再次运行该pod时,如果该pod绑定了其他node则数据需要迁移才会展示)
case4案例:
[root@k8s-master1 case4]#vim deploy_hostPath.yml
volumeMounts:
- mountPath: /data/n56
name: cache-n56-volume
volumes:
- name: cache-n56-volume
hostPath:
path: /opt/n56
[root@k8s-master1 case4]#kubectl apply -f deploy_hostPath.yml
deployment.apps/nginx-deployment created
[root@k8s-master1 case4]#kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-56b8bf9f66-xn4cr 1/1 Running 0 6s 10.200.36.81 192.168.150.161 <none> <none>
[root@k8s-master1 case4]#kubectl exec -it nginx-deployment-56b8bf9f66-xn4cr bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-deployment-56b8bf9f66-xn4cr:/# echo "1111">>/data/n56/1.txt
root@nginx-deployment-56b8bf9f66-xn4cr:/# cat /data/n56/1.txt
1111
#宿主机验证
[root@k8s-node1 ~]#cat /opt/n56/1.txt
1111
#删除pod数据不丢失
[root@k8s-master1 case4]#kubectl delete -f deploy_hostPath.yml
deployment.apps "nginx-deployment" deleted
[root@k8s-node1 ~]#cat /opt/n56/1.txt
1111
(3)nfs等共享存储卷
nfs卷允许将现有的NFS(网络文件系统)共享挂载到您的容器中。不像emptyDir,当删除Pod时,nfs卷的内容被保留,卷仅仅是被卸载。这意味着NFS卷可以预填充数据,并且可以在pod之间“切换”数据。NFS可以被多个写入者同时挂载。
创建多个pod测试挂载同一个NFS
创建多个pod测试每个pod挂载多个NFS
case5案例:
#1、准备nfs(复用haproxy机器)
[root@k8s-ha1 ~]#apt install nfs-server
[root@k8s-ha1 ~]#mkdir /data/magedu/n56 -p
[root@k8s-ha1 ~]#mkdir /data/magedu/js
#将目录共享出去
[root@k8s-ha1 ~]#vim /etc/exports
/data/magedu *(rw,no_root_squash)
[root@k8s-ha1 ~]#systemctl restart nfs-server.service
[root@k8s-ha1 ~]#systemctl enable nfs-server.service
#测试挂载没问题
[root@k8s-master1 case5]#showmount -e 192.168.150.159
Export list for 192.168.150.159:
/data/magedu *
#2、配置挂载nfs共享存储
[root@k8s-master1 case5]#kubectl explain deployment.spec.template.spec.volumes.nfs
[root@k8s-master1 case5]#vim deploy_nfs.yml
volumeMounts:
- mountPath: /usr/share/nginx/html/mysite
name: my-nfs-volume
- mountPath: /usr/share/nginx/html/js
name: my-nfs-js
volumes:
- name: my-nfs-volume
nfs:
server: 192.168.150.159
path: /data/magedu/n56
- name: my-nfs-js
nfs:
server: 192.168.150.159
path: /data/magedu/js
[root@k8s-master1 case5]#kubectl apply -f deploy_nfs.yml
deployment.apps/nginx-deployment created
service/ng-deploy-80 created
[root@k8s-master1 case5]#kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-5d66576b45-q9brx 1/1 Running 0 24m 10.200.36.82 192.168.150.161 <none> <none>
[root@k8s-master1 case5]#kubectl exec -it nginx-deployment-5d66576b45-q9brx bash
root@nginx-deployment-5d66576b45-q9brx:/# df
192.168.150.159:/data/magedu/js 125764864 8582400 117182464 7% /usr/share/nginx/html/js
192.168.150.159:/data/magedu/n56 125764864 8582400 117182464 7% /usr/share/nginx/html/mysite
root@nginx-deployment-5d66576b45-q9brx:/# cd /usr/share/nginx/html/mysite/ #上传数据测试
root@nginx-deployment-5d66576b45-q9brx:/usr/share/nginx/html/mysite# ll
#或通过挂载本地上传数据
[root@k8s-master1 case5]#mount -t nfs 192.168.150.159:/data/magedu/n56 /mnt
[root@k8s-master1 case5]#cd /mnt/
[root@k8s-master1 mnt]#ls
ee87cf318f3ac192ce9399da8f5c39a.jpg
#验证访问网页
#验证nfs数据
[root@k8s-ha1 ~]#ls /data/magedu/n56/
ee87cf318f3ac192ce9399da8f5c39a.jpg
#重新启动新pod后数据不丢失,可直接使用
#3、宿主机挂载位置
[root@k8s-node1 ~]#df
192.168.150.159:/data/magedu/js 125764864 8590848 117174016 7% /var/lib/kubelet/pods/394719ff-6b9d-4941-87af-bfb04a03ff04/volumes/kubernetes.io~nfs/my-nfs-js
192.168.150.159:/data/magedu/n56 125764864 8590848 117174016 7% /var/lib/kubelet/pods/394719ff-6b9d-4941-87af-bfb04a03ff04/volumes/kubernetes.io~nfs/my-nfs-volume
(4)configmap
Why:配置信息和镜像解耦
What:将配置信息放到configmap对象中,然后在pod的对象中导入configmap对象,实现导入配置的操作
How:声明一个ConfigMap的对象,作为Volume挂载到pod中
configmap配置文件详解:
声明容器(先声明再挂载)
修改配置文件,一个pod所有容器都将重建
tomcat和nginx的挂载点可以不同
示例:
configmap环境变量示例:不常用
configmap环境变量示例:常用,直接指定
系统级环境变量,可直接使用
配置变更的几种方式
直接把服务的配置文件(nginx.conf)放到镜像
configmap:把配置和镜像解耦
配置中心:阿波罗(要求足够的性能和高可用)
定义环境变量的几种方法
(1)dockerfile
(2)configmap
(3)yaml
(4)配置文件
case6案例:
[root@k8s-master1 case6]#kubectl explain configmap #如何写configmap
[root@k8s-master1 case6]#vim deploy_configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
default: |
server {
listen 80;
server_name www.mysite.com;
index index.html;
location / {
root /data/nginx/html;
if (!-e $request_filename) {
rewrite ^/(.*) /index.html last;
}
}
}
#1、修改为不挂载看效果
[root@k8s-master1 case6]#vim deploy_configmap.yml
# - name: nginx-config
# mountPath: /etc/nginx/conf.d
[root@k8s-master1 case6]#kubectl apply -f deploy_configmap.yml
configmap/nginx-config unchanged
deployment.apps/nginx-deployment configured
service/ng-deploy-80 unchanged
[root@k8s-master1 case6]#kubectl get pod
nginx-deployment-84b6d76b5-q66ph 2/2 Running 0 107s
#验证
root@nginx-deployment-84b6d76b5-q66ph:/# ls /etc/nginx/conf.d/
default.conf
#2、改回挂载后继续操作
[root@k8s-master1 case6]#vim deploy_configmap.yml
- name: nginx-config
mountPath: /etc/nginx/conf.d
[root@k8s-master1 case6]#kubectl apply -f deploy_configmap.yml
#(1)nginx验证
[root@k8s-master1 case6]#vim deploy_configmap.yml
kind: ConfigMap
server_name www.mysite.com;
kind: Deployment
- name: nginx-config
mountPath: /etc/nginx/conf.d
#登录dashboard验证
root@nginx-deployment-789d799b8c-nfn9z:/# ls /etc/nginx/conf.d/
mysite.conf
root@nginx-deployment-789d799b8c-nfn9z:/# cat /etc/nginx/conf.d/mysite.conf
server {
listen 80;
server_name www.mysite.com;
index index.html;
location / {
root /data/nginx/html;
if (!-e $request_filename) {
rewrite ^/(.*) /index.html last;
}
}
}
#配置haproxy和vip
[root@k8s-ha1 ~]#vim /etc/keepalived/keepalived.conf
virtual_ipaddress {
192.168.150.189 dev eth0 label eth0:1
[root@k8s-ha1 ~]#vim /etc/haproxy/haproxy.cfg
listen magedu-nginx-80
bind 192.168.150.189:80
mode tcp
server k8s1 192.168.150.151:30019 check inter 3s fall 3 rise 5
#server k8s1 192.168.150.152:30019 check inter 3s fall 3 rise 5
#server k8s1 192.168.150.153:30019 check inter 3s fall 3 rise 5
[root@k8s-ha1 ~]#systemctl restart keepalived.service
[root@k8s-ha1 ~]#systemctl restart haproxy.service
#配置本地浏览器域名访问
C:\Windows\System32\drivers\etc\hosts
192.168.150.189 www.mysite.com
#dashboard登录后自定义页面展示信息
root@nginx-deployment-789d799b8c-j4bnv:/# cd /data/nginx/html/
root@nginx-deployment-789d799b8c-j4bnv:/data/nginx/html# ls
root@nginx-deployment-789d799b8c-j4bnv:/data/nginx/html# echo "my app"> index.html
#访问网址验证:http://www.mysite.com/
#(2)tomcat验证
[root@k8s-master1 case6]#vim deploy_configmap.yml
kind: Deployment
- name: ng-deploy-8080
image: tomcat
ports:
- containerPort: 8080
volumeMounts:
- name: nginx-config
mountPath: /data
#登录dashboard验证
root@nginx-deployment-789d799b8c-j4bnv:/usr/local/tomcat# cat /data/mysite.conf
server {
listen 80;
server_name www.mysite.com;
index index.html;
location / {
root /data/nginx/html;
if (!-e $request_filename) {
rewrite ^/(.*) /index.html last;
}
}
}
#3、通过configmap配置环境变量示例
[root@k8s-master1 case6]#vim deploy_configmapenv.yml
env:
- name: "magedu"
value: "n56"
[root@k8s-master1 case6]#kubectl apply -f deploy_configmapenv.yml
configmap/nginx-config configured
deployment.apps/nginx-deployment configured
#dashboard登录后验证
root@nginx-deployment-76dcbcb567-d6blh:/# env #系统级环境变量,可直接使用
magedu=n56
root@nginx-deployment-76dcbcb567-d6blh:/# echo $magedu
n56
https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/
https://www.kubernetes.org.cn/statefulset
Why:为了解决有状态服务的问题
What:它所管理的Pod拥有固定的Pod名称,主机名,启停顺序
How:创建一个statefulset类型的pod,并指定serviceName,创建headless类型的svc
#与deployment相对应,deployment运行无状态服务:nginx、微服务等
#Statefulset解决有状态服务,服务本身有集群同步关系,主从关系等,如mysql主从,zookeeper集群,Elasticsearch集群等
#后期演示
https://kubernetes.io/zh/docs/concepts/workloads/controllers/daemonset/
DaemonSet在当前集群中每个节点运行同一个pod,当有新的节点加入集群时也会为新的节点配置相同的pod,
当节点从集群中移除时其pod也会被kubernetes回收,但是删除DaemonSet将删除其创建的所有的pod。
#可用于网络组件calico/flanne、日志收集、监控zabbix等
#在每个节点上运行相同任务的pod
case7案例:(官网案例,以日志收集为例)
#daemonset的主进程
[root@k8s-master1 case7]#kubectl get daemonset -A
NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-system calico-node 5 5 2 5 2 kubernetes.io/os=linux 20d
#日志路径
[root@k8s-node1 ~]#find /var/lib/docker -name *.log
#简单看daemonset效果,在每个节点都起一个日志收集功能的pod
[root@k8s-master1 case7]#vim daemonset.yaml
kind: DaemonSet
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
[root@k8s-master1 case7]#kubectl apply -f daemonset.yaml
daemonset.apps/fluentd-elasticsearch created
[root@k8s-master1 case7]#kubectl get pod -A
kube-system fluentd-elasticsearch-jdkgd 1/1 Running 0 39s
kube-system fluentd-elasticsearch-lj6qv 1/1 Running 0 39s
#PV/PVC官网介绍
https://kubernetes.io/docs/concepts/storage/persistent-volumes/
#官网案例:stateful结合pv/pvc实现 WordPress 和 MySQL的部署
https://kubernetes.io/docs/tutorials/stateful-application/mysql-wordpress-persistent-volume/
#k8s综合介绍
http://docs.kubernetes.org.cn/429.html
https://www.kubernetes.org.cn/pvpvcstorageclass
默认情况下容器中的磁盘文件是非持久化的,对于运行在容器中的应用来说面临两个问题:
第一:当容器挂掉,kubelet将重启它时,文件将会丢失;
第二:Pod中同时运行多个容器,当容器之间需要共享文件时
kubernetes 从1.0版本开始支持PersistentVolume和PersistentVolumeClaim。
persistentVolume(PV)
是集群中由管理员配置的一段网络存储,它类似于node,node是集群中的一个节点资源,pv是集群中的一个存储资源
PV是对底层网络存储的抽象,即将网络存储定义为一种存储资源,将一个整体的存储资源拆分成多份后给不同的业务使用。
PV是一个全局资源即不隶属于任何namespace,包含存储的类型,存储的大小和访问模式等,
它的生命周期独立于pod,例如当使用它的Pod销毁时对pv没有影响。
PV是Volume之类的卷插件,但是具有独立于使用PV的任何单个pod的生命周期,此API对象包含存储实现的细节,即NFS、iSCSI或特定于云供应商的存储系统。
PersistentVolumeClaim(PVC)
pvc是用户存储的请求,它类似于pod,Pod消耗节点node资源,PVC消耗存储pv资源,
就像pod可以请求特定级别的资源(CPU和内存),PVC是namespace中的资源,可以设置特定的空间大小和访问模式。
PVC是对pv资源的申请调用,就像pod消费node节点资源一样,pod是通过PVC将数据保存至PV,PV在保存至存储。
动态创建:etcd保留pvc和pod的对应关系,管理员预先创建存储类
手动创建:共享存储时几个pod创建一个pvc
华为 联想 浪潮
戴尔 EMC NetAPP
ceph glusterfs
nfs
1、管理员预先创建存储类
2、用户创建pvc
3、用户创建后通知k8s根据存储类创建pv
4、k8s获取存储类信息
5、k8s基于存储类创建pv
6、用户创建使用pvc的pod
7、pod使用pvc存储数据
8、pvc使用pv存储数据
#pv的yaml文件格式及含义详解
[root@k8s-master1 ~]#kubectl explain persistentvolume
[root@k8s-master1 ~]#kubectl explain PersistentVolume.spec.capacity #当前pv空间大小
[root@k8s-master1 ~]#kubectl explain PersistentVolume.spec.accessModes #访问模式
ReadWriteOnce--PV只能被单个节点以读写权限挂载,RWO
ReadOnlyMany--PV可以被多个节点挂载但是权限是只读的,ROX
ReadWriteMany--PV可以被多个节点是读写方式挂载使用,RWX
[root@k8s-master1 ~]#kubectl explain PersistentVolume.spec.persistentVolumeReclaimPolicy #删除机制(慎重)
Retain--删除PV后保持原装,最后需要管理员手动删除(常用)
Recycle--空间回收,及删除存储卷上的所有数据(包括目录和隐藏文件),目前仅支持NFS和hostpath
Delete--自动删除存储卷
[root@k8s-master1 ~]#kubectl explain PersistentVolume.spec.volumeMode #卷类型
#定义pv使用的文件系统是块设备还是文件系统,默认为文件系统
[root@k8s-master1 ~]#kubectl explain PersistentVolume.spec.mountOptions
ro #等
#附加的挂载选项列表,实现更精细的权限控制
#pvc的yaml文件格式及含义详解
[root@k8s-master1 ~]#kubectl explain PersistentVolumeClaim
[root@k8s-master1 ~]#kubectl explain PersistentVolumeClaim.spec.accessModes #
ReadWriteOnce--PV只能被单个节点以读写权限挂载,RWO
ReadOnlyMany--PV可以被多个节点挂载但是权限是只读的,ROX
ReadWriteMany--PV可以被多个节点是读写方式挂载使用,RWX
[root@k8s-master1 ~]#kubectl explain PersistentVolumeClaim.spec.resources #创建pvc空间大小
[root@k8s-master1 ~]#kubectl explain PersistentVolumeClaim.spec.selector #标签选择器,指定要绑定的pv
matchLabels #基于标签名称匹配
matchExpressions #基于正则表达式匹配
[root@k8s-master1 ~]#kubectl explain PersistentVolumeClaim.spec.volumeName #要绑定的pv名称
[root@k8s-master1 ~]#kubectl explain PersistentVolumeClaim.spec.volumeMode #卷类型
#定义pvc使用的文件系统是块设备还是文件系统,默认为文件系统
见博客:https://editor.csdn.net/md/?articleId=120701762