本章概述
- kubernetes资源管理核心概念
- Kubernetes API
- kubernetes资源对象
7.1 kubernetes资源管理核心概念
K8S的设计理念-分层架构
备注:无状态服务(应用)和有状态服务(应用)
无状态:无状态服务不会在本地存储持久化数据,多个服务实例对于同一个用户请求的响应结果是完全一致的。这种多服务实例之间是没有依赖关系,比如web应用,在k8s控制器 中动态启停无状态服务的pod并不会对其它的pod产生影响。
有状态:有状态服务需要在本地存储持久化数据,典型的是分布式数据库的应用,分布式节点实例之间有依赖的拓扑关系.比如主从关系.,如果K8S停止分布式集群中任一实例pod,就可能会导致数据丢失或者集群的crash。
CRI:Container Runtime Interface 容器运行时接口:实现容器运行,在K8S和runtime(运行时)之间传递命令和参数
CRI:Container Network Interface 容器网络接口:创建容器时分配地址,删除容器时回收地址,给容器创建网络规则,保证从外网可以访问容器
CSI:Container Storage Interface 容器存储接口:提供对接第三方存储的接口,只有符合接口标准的存储才可以在k8S集群使用
K8S的设计理念-API设计原则
注意:API设计原则主要是对K8S进行二次开发时需要注意,运维只需了解即可
https://www.kubernetes.org.cn/kubernetes%e8%ae%be%e8%ae%a1%e7%90%86%e5%bf%b5
所有API应该是声明式的。
API对象是彼此互补而且可组合的。
高层API以操作意图为基础设计。
低层API根据高层API的控制需要设计。
尽量避免简单封装,不要有在外部API无法显式知道的内部隐藏的机制。
API操作复杂度与对象数量成正比。
API对象状态不能依赖于网络连接状态。
尽量避免让操作机制依赖于全局状态,因为在分布式系统中要保证全局状态的同步是非常困难的。
备注:API声明式和API命令式
声明式:只看目标结果,对过程如何并不关心。如创建pod时,只需在yaml文件指定关键字(如容器名称、镜像、端口等),拉起pod的操作则由k8s自己进行
命令式:不仅定义了结果,过程也需要定义,告诉机器每一步如何操作,要实现什么结果。每一步都需要指定(如容器名称、镜像、端口、创建容器的命令等)
7.2 Kubernetes API
k8s API分为两种:内置API和自定义资源
内置API: 部署好kubernetes集群后自带的API接口
查看内置API命令:kubectl api-resources
自定义资源:CRD(Custom ResourceDefinition),部署kubernetes之后通过安装其它组件等方式扩展出来的API。
比如之前安装的velero(K8S备份服务)自带的api:
7.2.1 内置API简介
API命名格式:
查询内置api命令:
curl -k --cacert /etc/kubernetes/ssl/ca.pem -H "Authorization: Bearer TOKEN" https://127.0.0.1:6443
注意:访问时需要认证,因此需要指定证书以及TOKEN
如何获取TOKEN
在安装dashboard时,创建过一个admin-user账号,这里使用admin-user账号的TOKEN来认证
查找admin用户的secrets
kubectl get secrets -A |grep admin
根据查找出的secrests名称查找TOKEN
kubectl describe secrets admin-user-token-xsbjs -n kubernetes-dashboard
因此查询api的命令为
注意:如果证书到期无法通过token访问,可以通过添加-k选项,忽略对证书的验证
curl -k --cacert /etc/kubernetes/ssl/ca.pem -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ii05ZmctQVVKcEdaLXpuN2pCUHg4WXloNEJYbVNPR1JpVEVhQTNmTUw1SkkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXhzYmpzIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI2ZjQ4YTJjYi1hMjg0LTRmMTQtODkyYS00ODBhYzY3MjU0MTAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.mp9524YErPetxDUtyTY1EAVUd9cjSKw-IJdQ-JYwPV57AaEcWK-8gdZNgZeDH9IoI_Y--BdDeHBZ-KIRabxdjHrJwO2XlJ7Ygd6jaw9qTvdBzu2E43vZ1AdN2V71UPwiZmoux3pwsn2nwooTAC2yf_uvvyYBEsmWwsSOjCiB48mw1k9fdIMZ8Qtn8sMXH-m-j1JeSQvAbLrGhqMFhb8mFozSEoMpQVqTWaOBQ42Ulv-fYMUULtpgv9G1pmAEbp24HognAee-4i28diYndlxcoDZdLK9bZwpvEKl-3mhODEnqIv0XM8fXswGLM5p5wOcK5BQUZKKKk53jhxclYCRZVw" https://127.0.0.1:6443
如何查看K8S中API接口具体内容:只需在查看API的命令后加上API路径即可
查看连接状态:
查看k8s版本
api简略路径图:
7.3 kubernetes资源对象
资源对象简介
资源对象内容如下:
资源类型 | 资源名称 |
---|---|
资源对象 | Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling、Node、Namespace、Service、Ingress、Label、CustomResourceDefinition |
存储对象 | Volume、PersistentVolume、PersistemVolumeClaim、Secret、ConfigMap |
策略对象 | SecurityContext、ResourceQuota、LimitRange |
身份对象 | ServiceAccount、Role、ClusterRole |
kubernetes 资源对象操作命令:
https://kubernetes.io/zh/docs/concepts/workloads/controllers/deployment/
查看命令输出结果:
kubectl cluster-info #查看集群信息
kubectl top node/pod #查看node/pod的CPU、内存使用情况;该命令需要安装指标收集服务才可以使用,如果没有安装会报错
kubectl cordon x.x.x.x
目前集群情况
给172.31.7.113打上不可被调度的标签,此时再创建新的容器将不会往113上调度,已经创建好的容器不受影响
kubectl uncordon x.x.x.x #取消不可被调度的标签
kubectl config #显示配置内容
kubernetes 的几个重要概念:
资源对象:kubernetes基于声明式API,和资源对象进行交互。
yaml文件:为了方便后期管理,通过使用yaml文件通过API管理资源对象。
yaml必需字段:
1. apiVersion - 创建该对象所使用的 Kubernetes API 的版本
2. kind - 想要创建的对象的类型
3. metadata - 定义识别对象唯一性的数据,包括一个 name 名称 、可选的 namespace
4. spec:定义资源对象的详细规范信息(统一的label标签、容器名称、镜像、端口映射等)
5. status(Pod创建完成后k8s自动生成status状态,该字段不用在yaml文件中写明)
查看资源对象状态(这里以deployment为例)
yaml文件及必需字段:
每个API对象都有3大类属性:元数据metadata、规范spec和状态status。
spce和status的区别:
spec是期望状态
status是实际状态
7.3.1 Pod
pod说明:
1. pod是k8s中的最小单元
2. 一个pod中可以运行一个容器,也可以运行多个容器
3. 运行多个容器的话,这些容器是一起被调度的
4. Pod的声明周期是短暂的,不会自愈,是用完就销毁的实体
5. 一般我们通过controller来创建和管理pod
备注:由于pod类型的容器没有自愈功能,因此一般情况下我们不会创建pod类型的资源
示例:
vim pod-test-case.yaml
apiVersion: v1
kind: Pod
metadata: #注意:这里没有指定namespace,pod会在默认namespace(default)创建
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.20.2-alpine
ports:
- containerPort: 80
根据yaml文件创建pod资源:
kubectl apply -f pod-test-case.yaml
删除pod,pod删除后,不会自动恢复
7.3.2 Job和cronJob
job:任务,只执行一次就结束,不再执行。如:项目部署时,数据库初始化,执行一次后不再执行该任务。由于每个项目需求不同,每次数据库初始化的内容也不一样,因此不能在打镜像时把初始化的动作加进去
cornjob:周期任务,周期性执行任务,执行完任务之后容器会退出,下一次任务时间到达时容器会再次启动。如:数据备份任务、数据同步任务等
官网链接:
job:https://kubernetes.io/docs/concepts/workloads/controllers/job/
cronjob:https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/
示例1:job
vim 1.job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: job-mysql-init
namespace: linux66
spec:
template:
spec:
containers:
- name: job-mysql-init-container
image: centos:7.9.2009 #由于没有初始化任务,这里指定镜像为centos镜像
command: ["/bin/sh"]
args: ["-c", "echo data init job at `date +%Y-%m-%d_%H-%M-%S` >> /cache/data.log"]
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume #为了便于查看容器内是否写入了数据,指定挂载路径,把宿主机的/tmp/jobdata挂载到容器的/cache。
hostPath: #指定挂载类型,hostPath类型是指当容器删除后,数据仍然存在
path: /tmp/jobdata
restartPolicy: Never #指定重启策略,永不重启
根据yaml文件创建pod并查看
kubectl apply -f 1.job.yaml
当job类型的容器执行完任务之后,容器退出,容器状态会变成completed
查看宿主机挂载路径下的文件,查看是否有信息输出:
容器被调度到172.31.7.113节点,在该节点上查看/tmp/jobdata目录是否有文件生成
可以看到有日志文件生成(容器内时间默认为格林尼治时间,与北京时间差8小时)
示例2:cronjob
vim 2.cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: cronjob-mysql-databackup
namespace: linux66
spec:
schedule: "* * * * *" #指定运行周期为1分钟执行一次
jobTemplate:
spec:
template:
spec:
containers:
- name: cronjob-mysql-databackup-pod
image: centos:7.9.2009
#imagePullPolicy: IfNotPresent
command: ["/bin/sh"]
args: ["-c", "echo mysql databackup cronjob at `date +%Y-%m-%d_%H-%M-%S` >> /cache/data.log"]
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume #为了便于查看容器内是否写入了数据,指定挂载路径,把宿主机的/tmp/jobdata挂载到容器的/cache。
hostPath:
path: /tmp/cronjobdata
restartPolicy: OnFailure #指定重启策略,容器正常启动或退出时不会重启,当容器启动或退出一场时进行重启
根据yaml创建pod
查看容器状态为completed,容器被调度172.31.7.113上
在172.31.7.113上查看日志,日志中有周期性的日志生成(每隔1分钟生成1条)
cronjob类型的容器不会记录所有的执行容器,只会保留与当前时间最接近的3个容器,在此之前的容器会被删掉,只保留最近3个
7.3.3 RC/RS副本控制器
副本控制器,用于控制容器的副本数,根据yaml文件创建容器时,yaml文件中指定有几个副本,就会创建几个容器。当容器数少于或多于副本数时,控制器会相应地的自动创建或删除容器,确保容器数和yaml文件中指定的副本数保持一致。副本控制器类似于进程管理器,但不是监控单个节点上的单个进程,而是监控跨多个节点的多个 Pod。
到目前为止,K8S迭代了三个版本的副本控制器,分别是Replication Controller、ReplicaSet和Deployment控制器,前两种控制器目前很少单独使用,常用的是Deployment控制器,因此我们只需要记住Deployment控制器的用法即可,前两种控制器只做了解。
7.3.3.1 第一代pod副本控制器:Replication Controller
Replication Controller:副本控制器(selector = !=) ,selector只支持等于(严格匹配)和不等于(取反)这两种操作符进行匹配
官网链接:https://kubernetes.io/zh/docs/concepts/workloads/controllers/replicationcontroller/
https://kubernetes.io/zh/docs/concepts/overview/working-with-objects/labels/
示例:
vim rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: ng-rc #指定控制器名称
spec:
replicas: 2 #指定容器副本数为2
selector:
app: ng-rc-80
template:
metadata:
labels:
app: ng-rc-80
spec:
containers:
- name: ng-rc-80
image: nginx
ports:
- containerPort: 80
根据yaml文件创建容器
kubectl apply -f rc.yaml
查看rc控制器,控制器名称为yaml文件定义的ng-rc
kubectl get rc -A
查看replication controller控制器创建的pod,pod名称为rc名称加随机字符串
手动删除一个pod,rc控制器会再创建出一个pod,保证容器数量是两个
7.3.3.2 第二代pod副本控制器:ReplicaSet
ReplicaSet:副本控制器,和副本控制器的区别是:对选择器的支持(selector在第一代的基础上还支持in notin)
官网链接:https://kubernetes.io/zh/docs/concepts/workloads/controllers/replicaset/
示例:
vim rs.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend #指定控制器名称
spec:
replicas: 2 #指定容器副本数
selector:
matchLabels: #标签匹配
app: ng-rs-80
#matchExpressions: #支持正则表达式匹配
# - {key: app, operator: In, values: [ng-rs-80,ng-rs-81]} #列表匹配,即value值ng-rs-80和ng-rs-81只要匹配到其中一个就表示匹配成功。会出现两个容器标签都是ng-rs-80或ng-rs-81的情况,做不到精确匹配,因此正则表达式匹配的方式并不常用,常用的的还是matchLabels(标签匹配)的匹配方式
template:
metadata:
labels:
app: ng-rs-80
spec:
containers:
- name: ng-rs-80
image: nginx
ports:
- containerPort: 80
根据yaml文件创建容器
kubectl apply -f rs.yaml
查看pod控制器
查看pod
手动删除pod,replicaset控制器会创建新的pod
使用yaml文件中正则表达式的写法来创建容器(这种写法并不常用,了解即可)
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: frontend #指定控制器名称
spec:
replicas: 2 #指定容器副本数
selector:
#matchLabels: #标签匹配
#app: ng-rs-80
matchExpressions: #支持正则表达式匹配
- {key: app, operator: In, values: [ng-rs-80,ng-rs-81]} #列表匹配,即value值ng-rs-80和ng-rs-81只要匹配到其中一个就表示匹配成功。会出现两个容器标签都是ng-rs-80或ng-rs-81的情况,做不到精确匹配,因此正则表达式匹配的方式并不常用,常用的的还是matchLabels(标签匹配)的匹配方式
template:
metadata:
labels:
app: ng-rs-80
spec:
containers:
- name: ng-rs-80
image: nginx
ports:
- containerPort: 80
根据yaml文件创建容器
kubectl apply -f rs.yaml
查看pod控制器
查看pod
手动删除pod,replicaset控制器会创建新的pod
7.3.3.3 第三代pod副本控制器:Deployment
Deployment:比rs更高一级的控制器,除了有rs的功能之外,还有很多高级功能,,比如说最重要的:滚动升级、回滚等。
官网链接:https://kubernetes.io/zh/docs/concepts/workloads/controllers/deployment/
Deployment控制器并不会直接管理容器副本数,而是会创建一个replicaset控制器,由replicaset控制器来管理容器副本数。
Deployment控制器本身则用于pod的升级和回滚。
滚动升级过程:
(1)在进行升级时,deployment控制器在现有情况基础上,新创建一个replicaset控制器(建设新建replicaset控制器名称为rs02)
(2)由新建的replicaset控制器rs02新建一组新的容器(新建容器数量为容器副本数的25%,容器数不足一个按照一个算),容器创建完成后,把旧的一组容器删除,这样就完成了第一组容器的升级
创建新的一组容器pod1-v2,回收旧的一组容器pod1-v1:
最终状态只保留新的容器pod1-v2:
(3)然后进行第二组容器的升级
创建新的一组容器pod2-v2,回收旧的一组容器pod2-v1:
最终状态只保留新的容器pod2-v2:
(4)以此类推,直到容器数量满足我们指定的副本数,最终完成所有容器的升级
注意:在升级过程中,新建的容器一旦创建成功,K8S就会把外部请求转发给新建的pod,新建的pod会接收到外部请求的流量。
示例:
vim deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment #指定控制器名称
spec:
replicas: 2 #指定容器副本数
selector:
#app: ng-deploy-80 #rc
matchLabels: #rs or deployment
app: ng-deploy-80
#matchExpressions:
# - {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
template:
metadata:
labels:
app: ng-deploy-80
spec:
containers:
- name: ng-deploy-80
image: nginx
ports:
- containerPort: 80
根据yaml创建容器
kubectl apply -f deployment.yaml
查看pod
注意:pod名称为nginx-deployment-674ccf5c5f-8qlgn,其中nginx-deployment为yaml文件中指定的deployment的名称,674ccf5c5f是指deployment创建的replicaset名称,而8qlgn 才是容器名称。同一个控制器创建的容器名称中deployment和replicaset名称(即nginx-deployment-674ccf5c5f-)相同,只有容器名称(8qlgn和qjc2p)是不一样的。
模拟滚动升级过程:
更改yaml文件中nginx的镜像版本
vim deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment #指定控制器名称
spec:
replicas: 2 #指定容器副本数
selector:
#app: ng-deploy-80 #rc
matchLabels: #rs or deployment
app: ng-deploy-80
#matchExpressions:
# - {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
template:
metadata:
labels:
app: ng-deploy-80
spec:
containers:
- name: ng-deploy-80
image: nginx:1.16.0
ports:
- containerPort: 80
更改完成后,重新加载yaml文件
kubectl apply -f deployment.yml
查看pod,发现新的容器(nginx-deployment-55fdf878b9-7x5x8)被创建,而且容器名称中replicaset名称和容器名称(即55fdf878b9-7x5x8)发生了变化,说明容器是由新的replicaset控制创建的,而旧的容器(nginx-deployment-674ccf5c5f-8qlgn和nginx-deployment-674ccf5c5f-qjc2p)还没有被回收,此时就是在做滚动升级的动作,先新建容器,容器创建完成后再把旧的容器回收
等待新的容器创建完成后,旧的容器被回收,只保留新的容器(nginx-deployment-55fdf878b9-7x5x8和nginx-deployment-55fdf878b9-bvfnm)
回滚:
使用命令:
kubectl rollout undo deployment <控制器名称>
示例:(yaml文件中指定的控制器名称为nginx-deployment)
执行以下命令进行回滚:
kubectl rollout undo deployment nginx-deployment
查看pod,发现旧的容器正在被创建,容器名称为nginx-deployment-674ccf5c5f-slmsg(升级之前的replicaset控制器名称为674ccf5c5f,这里控制器名称相同,说明正在回滚到以前版本)
注意:回滚时控制器名称(nginx-deployment-674ccf5c5f)不变,但容器名称(slmsg)会随机生成
回滚完成:
7.3.4 service
7.3.4.1 service介绍
由于pod重建之后ip就变了,因此pod之间使用pod的IP直接访问会出现无法访问的问题,而service则解耦了服务和应用,service的实现方式就是通过label标签动态匹配后端endpoint。
注意:service只会将流量转发给同一个namespace的pod
kube-proxy(维护k8s网络规则的组件)监听着k8s-apiserver,一旦service资源发生变化(如调用k8s-api修改service信息),kube-proxy就会生成对应的负载调度的调整,这样就保证service的最新状态。
kube-proxy有三种调度模型:
userspace:k8s1.1之前
iptables:1.2-k8s1.11之前
ipvs:k8s 1.11之后,如果没有开启ipvs,则自动降级为iptables
7.3.4.2 service类型
1、ClusterIP类型
ClusterIP:用于内部服务基于service name的访问,如果创建容器时没有指定,默认网络类型就是ClusterIP。
ClusterIP类型访问流程:(只能在k8s集群内部进行通信)
在k8s集群内部,A服务要调用B服务下的pod,但A服务不知道B服务下的三个pod地址,这时我们给B服务创建一个service,service通过标签(label)发现B服务下的三个pod,A服务请求service,service再把请求转发给B服务下的三个pod,这样就完成了,两个服务之间的通信。
2、NodePort类型
NodePort:用于kubernetes集群以外的服务主动访问运行在kubernetes集群内部的服务。
NodePort类型访问流程:(可以和k8s集群外部通信)
客户端请求k8s集群内部A服务下,通过负载均衡把请求转发给宿主机暴露的nodeport(负载均衡上配置凡是请求A服务的流量都转发给宿主机的该端口),每个宿主机都会监听nodeport端口,收到请求后把请求转发给k8s集群内部的A服务的service,service通过label匹配A服务下的pod,把请求转发给A服务下的pod,A服务pod处理请求时需要调用B服务,再把请求转发给B服务的service(ClusterIP类型),service把请求转发给B服务下的pod处理,这样就完成了流量的转发。
注意:
(1)NodePort类型会在宿主机暴露端口用以接收k8s集群外部的流量请求,每个宿主机(包括master和node节点)都会监听暴露的nodeport端口
(2)k8s集群内部每个服务都会有一个service接收请求服务的流量,然后转发给服务下的pod
3、LoadBalancer类型
LoadBalancer:用于公有云环境的服务暴露。
4、ExternalName类型(该类型不常用)
ExternalName:用于将k8s集群外部的服务映射至k8s集群内部访问,从而让集群内部的pod能够通过固定的servicename访问集群外部的服务,有时候也用于将不同namespace之间的pod通过ExternalName进行访问。
注意:这里只对前两种进行举例,后两种在讲解ingress时进行举例
示例1:ClusterIP类型
1、先创建一个pod
vim 1-deploy_node.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
#matchLabels: #rs or deployment
# app: ng-deploy3-80
matchExpressions: #通过正则表达式匹配的方式进行匹配
- {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
template:
metadata:
labels:
app: ng-deploy-80
spec:
containers:
- name: ng-deploy-80
image: nginx:1.17.5
ports:
- containerPort: 80
根据yaml文件创建容器
kubectl apply -f 1-deploy_node.yml
查看pod
2、创建service
vim 2-svc_service.yml
apiVersion: v1
kind: Service
metadata:
name: ng-deploy-80 #指定service名称
spec:
ports:
- name: http
port: 80 #指定service端口,该端口可以和容器端口不一致,设置为其他端口
targetPort: 80 #容器服务端口,容器为nginx服务,端口为80
protocol: TCP #指定协议类型
type: ClusterIP #指定service类型
selector:
app: ng-deploy-80 #指定service匹配的pod标签,service通过该标签匹配到哪个pod,就把请求转发给哪个pod。(该标签要和service转发的pod标签保持一致,即在1-deploy_node.yml文件中红色字体部分。)
查看service
查看service后端的pod
该地址就是根据1-deploy_node.yml文件创建的pod地址,说明service通过label匹配到了后端的pod
在集群内其他容器内访问service地址,就可以把请求转发给service label匹配到的pod。
在net-test1容器内,使用curl命令请求service name:ng-deploy-80,可以到收到nginx的返回。
示例2:NodePort类型
1、先创建一个pod
vim 1-deploy_node.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
#matchLabels: #rs or deployment
# app: ng-deploy3-80
matchExpressions: #通过正则表达式匹配的方式进行匹配
- {key: app, operator: In, values: [ng-deploy-80,ng-rs-81]}
template:
metadata:
labels:
app: ng-deploy-80
spec:
containers:
- name: ng-deploy-80
image: nginx:1.17.5
ports:
- containerPort: 80
根据yaml文件创建容器
kubectl apply -f 1-deploy_node.yml
查看pod
2、创建service
vim 3-svc_NodePort.yml
apiVersion: v1
kind: Service
metadata:
name: ng-deploy-80
spec:
ports:
- name: http
port: 81 #指定service端口为81
targetPort: 80 #指定容器服务端口为80
nodePort: 30012 #指定nodePort端口(端口范围为部署k8s集群时指定的端口范围),如果不指定,k8s会随机分配端口
protocol: TCP
type: NodePort #指定service类型
selector:
app: ng-deploy-80
查看service,将宿主机的30012转发给service的81端口
查看service后端的pod
查看pod所在宿主机为172.31.7.113
访问宿主机的30012端口,即172.31.7.113:30012,可以正常访问nginx,这样就实现了在外部访问k8s集群内部服务
转发流程:
当访问宿主机暴露的30012端口时(每个宿主机都会监听nodeport端口),会转发给service的81端口,然后serivce再转发给容器的80端口
注意:这里存在本机转发和跨主机转发的情况
宿主机接收到请求,把请求转发给service,
如果service在本机,将会直接转发,这是本机转发,性能损耗较小;
如果service不在本机,在其他宿主机,其他宿主机在接收到请求后,会先把请求转发给service所在宿主机,然后再由宿主机转发给service,这是跨主机转发,这样的方式会有一定的性能损耗。
7.3.5 volume存储卷
7.3.5.1 volume存储卷简介
Volume将容器中的指定数据和容器解耦,并将数据存储到指定的位置,不同的存储卷功能不一样,如果是基于网络存储的存储卷可以可实现容器间的数据共享和持久化。(即容器删除后,数据不会丢失,拉起新容器时只要指定相应地存储卷,就可以读取到数据)
静态存储卷需要在使用前手动创建PV和PVC,然后绑定至pod使用。
常用的几种卷:
emptyDir:本地临时卷,数据存储在本地,容器删除,存储卷也会被删除,数据会丢失,使用场景:同一个pod中运行多个容器,这种类型的存储卷不常用
hostPath:本地存储卷,数据存储在本地,容器删除,存储卷不会被删除,数据会保存下来。但是数据保留在本地,如果容器被调度到其他主机,将无法读取数据。使用场景:在特定的主机上运行特定的容器,容器不会被调度到其他主机,这种类型的存储卷也不常用。
configmap: 配置文件,容器启动时挂载该类型存储卷,容器可以直接获取到配置文件。
Secret:是一种包含少量敏感信息例如密码、令牌或密钥的对象
nfs等:网络存储卷,常用的存储类型
官网链接:https://kubernetes.io/zh/docs/concepts/storage/volumes/
7.3.5.1 emptyDir
当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在,正如卷的名字所述,它最初是空的,Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir 中的数据将被永久删除。
存储卷所在文件路径:/var/lib/kubelet/pods/$ID/volumes/kubernetes.io~empty-dir/cache-volume/$FILE
示例:
vim deploy_emptyDir.yml
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 #指定将存储卷挂载到容器内的/cache目录
name: cache-volume #指定挂载的存储卷名称,通过该名称找到存储卷并将其挂载到容器内的/cache目录
volumes: #声明一个存储卷
- name: cache-volume
emptyDir: {} #指定存储卷为emptyDir类型
根据yaml文件创建容器
kubectl apply -f deploy_emptyDir.yml
查看容器,在宿主机172.31.7.111上
在宿主机上172.31.7.111上根据存储卷名称cache-volume查找存储卷
find /var/lib -name cache-volume
在容器内/cache目录下创建一个文件,然后到宿主机存储卷上查看是否存在
在容器内创建文件
在宿主机172.31.7.111上的存储卷内查看是否存在该文件
ls /var/lib/kubelet/pods/06196ccf-ade2-40eb-8c97-daaa30277bc2/volumes/kubernetes.io~empty-dir/cache-volume
注意:该存储卷只在172.31.7.111上存在,在其他节点上不存在,并且一旦容器删除,存储卷也会被删除
删除容器
kubectl delete -f deploy_emptyDir.yml
在172.31.7.111上查看存储卷,存储卷已经不存在
ls /var/lib/kubelet/pods/06196ccf-ade2-40eb-8c97-daaa30277bc2/volumes/kubernetes.io~empty-dir/cache-volume
7.3.5.2 hostPath
hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中,pod删除的时候,卷不会被删除
hostPath类型的存储卷和emptyDir类型的存储卷用法基本一致,只不过pod删除后,存储卷会被保留下来,但是该存储卷只能被本机上的其他pod使用,不能跨主机使用,存在局限性。
举例:
vim deploy_hostPath.yml
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 #指定将存储卷挂载到容器内的/cache目录
name: cache-volume #指定挂载的存储卷名称,通过该名称找到存储卷并将其挂载到容器内的/cache目录
volumes:
- name: cache-volume
hostPath: #指定存储卷类型为hostPath
path: /tmp/linux66 #指定挂载的存储卷为/tmp/linux66,该目录不用手动创建,会自动创建
根据yaml文件创建容器
kubectl apply -f deploy_hostPath.yml
查看容器,容器在宿主机172.31.7.113上
在容器/cache目录下写入文件,查看宿主机172.31.7.113上/tmp/linux66目录下是否存在该文件
在容器内创建文件
在172.31.7.113上查看/tmp/linux66目录,存在该文件
删除容器,查看存储卷是否存在
删除容器
kubectl delete -f deploy_hostPath.yml
宿主机172.31.7.113上文件仍然存在
注意:hostPath类型的存储卷只存在于某个节点,不能跨主机访问,如果容器被调度到其他节点上,将无法访问该存储卷,存在局限性,因此该类型存储卷不常用。
7.3.5.3 NFS等共享存储
nfs 卷允许将现有的 NFS(网络文件系统)共享挂载到您的容器中。不像 emptyDir,当删除 Pod 时,nfs 卷的内容被保留,卷仅仅是被卸载。这意味着 NFS 卷可以预填充数据,并且可以在 pod 之间“切换”数据。NFS 可以被多个写入者同时挂载。
示例1:挂载单个nfs
1、配置存储服务器:在172.31.7.109节点上安装nfs作为nfs存储
(1)yum安装nfs服务
yum -y install nfs-utils.x86_64
(2)创建共享目录/data/k8sdata
mkdir -p /data/k8sdata/linux66
(3)配置nfs配置文件,配置共享目录,并赋予读写权限
vim /etc/exports #配置格式为:共享目录 可以访问共享目录的主机网段/IP(权限)
/data/k8sdata 172.31.0.0/21(rw,no_root_squash) #注意*和()之间没有空格
这里需要注意:如果nfs存储不在k8s集群内,而是在k8s集群外部时,配置nfs配置文件/etc/exports中可以访问共享目录的网段或IP时,一定要配置容器所在宿主机的网段或IP,而不能只写容器所在网段和IP(如果只写容器ip,容器将无法挂载nfs存储)。这是因为k8s集群内容器访问外部服务时,会将容器ip转换为宿主机ip地址访问外部服务地址,如果只配置容器ip地址,宿主机ip就无权nfs存储导致容器无法挂载nfs存储。
重启nfs服务,使配置生效,并且设置为开机自启动
systemctl restart nfs-server
systemctl enable nfs-server
systemctl enable rpcbind
(4)在其他节点(以172.31.7.101为例)查看能否看到nfs共享目录
在172.31.7.101节点上查看
showmount -e 172.31.7.109
注意:如果k8s集群是在Ubuntu系统上部署的,在挂载nfs存储前,要在node节点上需要安装nfs-common安装包,如果不安装这个包,可能会出现容器无法挂载nfs存储或者挂载上以后访问nfs存储很慢的问题(命令为apt install nfs-common)
另外,如果指定的共享目录是/data/k8sdata,那么/data/k8sdata都可以被挂载
2、通过yaml文件创建容器
vim deploy_nfs.yml
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: #指定存储卷类型为nfs
server: 172.31.7.109 #指定nfs存储地址
path: /data/k8sdata/linux66 #指定nfs存储卷路径
---
apiVersion: v1
kind: Service #为了便于测试,为容器创建service,并暴露端口可以通过外部访问
metadata:
name: ng-deploy-80
spec:
ports:
- name: http
port: 81 #指定service端口为81
targetPort: 80 #指定容器服务端口为80
nodePort: 30016 #指定暴露的宿主机端口
protocol: TCP
type: NodePort #指定service类型为NodePort类型
selector:
app: ng-deploy-80
根据yaml文件创建容器
kubectl apply -f deploy_nfs.yml
进入容器查看挂载点
由于在yaml文件中配置了通过deployment创建nginx容器,并且使用了nodePort类型的service,暴露宿主机端口30016
此时我们访问宿主机的30016端口,可以正常访问到k8s集群内的nginx服务
在yaml文件中挂载nfs存储的目录为/usr/share/nginx/html/mysite,该目录为nginx的配置文件目录。我们可以通过在nfs存储中存放文件,测试在nginx提供的web页面访问该文件
进入172.31.7.109(nfs存储)节点,在存储卷目录下存放一个图片
cd /data/k8sdata/linux66
随便上传一个图片(比如说1.jpg)放在该目录下
访问nginx服务,即http://172.31.7.113:30016/mysite/1.jpg
示例2:挂载多个存储
1、配置存储服务器,由于要挂载多个存储,因此除了172.31.7.109之外,在172.31.7.110上配置nfs存储服务,作为第二个存储。
在节点172.31.7.110上
(1)yum安装nfs服务
yum -y install nfs-utils.x86_64
(2)创建共享目录/data/magedu
mkdir -p /data/magedu
(3)配置nfs配置文件,配置共享目录,并赋予读写权限
vim /etc/exports #配置格式为:共享目录 可以访问共享目录的主机网段/IP(权限)
/data/magedu 172.31.0.0/21(rw,no_root_squash) #注意*和()之间没有空格
重启nfs服务,使配置生效,并且设置为开机自启动
systemctl restart nfs-server
systemctl enable nfs-server
systemctl enable rpcbind
(4)在其他节点(以172.31.7.101为例)查看能否看到nfs共享目录
在172.31.7.101节点上查看
showmount -e 172.31.7.110
2、根据yaml创建容器
vim deploy_nfs2.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-site2
spec:
replicas: 1
selector:
matchLabels:
app: ng-deploy-81
template:
metadata:
labels:
app: ng-deploy-81
spec:
containers:
- name: ng-deploy-81
image: nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html/mysite #指定第一个存储卷挂载容器内的目录
name: my-nfs-volume
- mountPath: /usr/share/nginx/html/magedu #指定第二个存储卷挂载容器内的目录
name: magedu-statics-volume
volumes:
- name: my-nfs-volume #指定第一个nfs存储
nfs:
server: 172.31.7.109 #指定nfs存储地址
path: /data/k8sdata/linux66 #指定nfs存储路径
- name: magedu-statics-volume #指定第二个nfs存储
nfs:
server: 172.31.7.110 #指定nfs存储地址
path: /data/magedu #指定nfs存储路径
---
apiVersion: v1
kind: Service
metadata:
name: ng-deploy-81 #指定service名称
spec:
ports:
- name: http
port: 80 #指定service端口
targetPort: 80 #指定容器nginx服务的端口
nodePort: 30017 #指定宿主机暴露的端口
protocol: TCP
根据yaml文件创建容器
kubectl apply -f deploy_nfs2.yml
进入容器查看,有两个挂载点,说明挂载成功
注意:这种挂载方式挂载的nfs存储,无法对nfs存储使用空间大小进行限制,用户可以无限制的使用nfs存储空间,直至空间用完。要想对存储卷空间进行限制,可以使用pv或者pvc进行限制。
7.3.6 PV/PVC
PV:PersistentVolume
PVC :PersistentVolumeClaim
用于实现pod和storage的解耦,这样我们修改storage的时候不需要修改pod。
与NFS的区别:可以在PV和PVC层面实现实现对存储服务器的空间分配、存储的访问权限管理等。
kubernetes 从1.0版本开始支持PersistentVolume和PersistentVolumeClaim。
官网链接https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/
PV用于和硬件对接,PV定义了一些接口标准,凡是要和k8s对接、为k8s提供存储的硬件,都要符合这些标准。
pod无法直接挂载PV,而是封装了一个PVC,专门用于pod的挂载,但是PVC无法存储数据,因此把PV和PVC关联起来进行绑定,pv用于提供硬件存储能力来存储数据,而pvc则用于pod挂载
7.3.6.1 PV/PVC简介
PersistentVolume(PV):
是集群中已经由kubernetes管理员配置的一个网络存储,集群中的一个全局资源,即不隶属于任何namespace,PV的数据最终存储在硬件存储,pod不能直接挂载PV,PV需要绑定给PVC并最终由pod挂载PVC使用,PV其支持NFS、Ceph、商业存储或云提供商的特定的存储,可以自定义PV的类型(是块存储还是文件存储)、存储空间大小、访问模式等,PV的生命周期独立于Pod,即当使用PV的Pod被删除时可以对PV中的数据没有影响。
PersistentVolumeClaim(PVC):
是pod对存储的请求, pod挂载PVC并将数据存储在PVC,而PVC需要绑定到PV才能使用,另外PVC在创建的时候要指定namespace,即pod要和PVC运行在同一个namespace,可以对PVC设置特定的空间大小和访问模式,使用PVC的pod在删除时对PVC中的数据没有影响。
pv/pvc总结:
PV是对底层网络存储的抽象,即将网络存储定义为一种存储资源,将一个整体的存储资源拆分成多后给不同的业务使用。
PVC是对PV资源的申请调用,pod是通过PVC将数据保存至PV,PV再把数据保存至真正的硬件存储。
7.3.6.2 PersistentVloume参数
以下为PV常用参数
kubectl explain PersistentVolume #查看PV字段信息
Capacity: #当前PV空间大小,kubectl explain PersistentVolume.spec.capacity
accessModes :访问模式,#kubectl explain PersistentVolume.spec.accessModes
ReadWriteOnce – PV只能被单个节点以读写权限挂载,RWO
ReadOnlyMany – PV以可以被多个节点挂载但是权限是只读的,ROX
ReadWriteMany – PV可以被多个节点是读写方式挂载使用,RWX
persistentVolumeReclaimPolicy #PV删除/回收机制,即删除存储卷的时候,已经创建好的存储卷可以通过以下命令进行删除操作:
kubectl explain PersistentVolume.spec.persistentVolumeReclaimPolicy #查看pv回收策略
Retain – 删除PV后,硬件存储中的数据保存下来,最后需要管理员手动删除,为常用的回收策略
Recycle – 空间回收,即删除存储卷上的所有数据(包括目录和隐藏文件),目前仅支持NFS和hostPath
Delete – 自动删除存储卷
volumeMode #卷类型,定义存储卷使用的文件系统是块设备还是文件系统,默认为文件系统,通过命令查看:kubectl explain PersistentVolume.spec.volumeMode
mountOptions #附加的挂载选项列表,实现更精细的权限控制,如设置为只读(ro)权限。
官方提供的基于各后端存储创建的PV支持的访问模式:
访问链接:https://kubernetes.io/zh/docs/concepts/storage/persistent-volumes/
7.3.6.3 PersistentVolumeClaim参数
以下为PVC常用参数:
kubectl explain PersistentVolumeClaim. #通过命令查看PVC字段信息
accessModes :PVC 访问模式,#kubectl explain PersistentVolumeClaim.spec.volumeMode
ReadWriteOnce – PVC只能被单个节点以读写权限挂载,RWO
ReadOnlyMany – PVC以可以被多个节点挂载但是权限是只读的,ROX
ReadWriteMany – PVC可以被多个节点是读写方式挂载使用,RWX
resources: #定义PVC创建存储卷的空间大小,需要注意的是:指定的pvc大小要小于等于pv的大小。如pv大小为5G,pvc的大小要小于等于5G。
selector: #标签选择器,选择要绑定的PV
matchLabels #匹配标签名称
matchExpressions #基于正则表达式匹配
volumeName #要绑定的PV名称
注意:PVC和PV绑定,可以通过标签选择器(selector)进行绑定,也可以通过指定名称绑定(volumeName)进行绑定
volumeMode #卷类型,定义PVC使用的文件系统是块设备还是文件系统,默认为文件系统
7.3.6.4 Volume存储类型
static:静态存储卷 ,需要在使用前手动创建PV、然后创建PVC并绑定到PV,然后挂载至pod使用,适用于PV和PVC相对比较固定的业务场景,即pod不经常变动,比如:多个nginx访问同一个存储,将数据写入到同一个位置,位置不发生变动(如果数据写入位置发生变动,将数据写入到其他存储,将无法读取数据)
dynamin:动态存储卷,先创建一个存储类storageclass(storageclass会指定好后端存储类型),后期pod在使用PVC的时候可以通过存储类动态创建PVC,适用于有状态服务集群如MySQL一主多从、zookeeper集群等。
动态存储卷官网链接:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/
静态存储卷示例
1、准备nfs存储
在172.31.7.109节点上(该节点已经安装nfs服务)创建共享目录
mkdir /data/k8sdata/myserver/myappdata -p
修改nfs配置文件,指定共享存储以及权限
vim /etc/exports
/data/k8sdata/myserver/myappdata *(rw,no_root_squash)
重启服务,使配置生效
systemctl restart nfs-server && systemctl enable nfs-server
2、通过yaml文件创建PV
vim 1-myapp-persistentvolume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: myserver-myapp-static-pv #指定pv名称
spec:
capacity: #指定pv大小
storage: 10Gi
accessModes: #指定pv的访问模式
- ReadWriteOnce
nfs:
path: /data/k8sdata/myserver/myappdata #指定nfs的路径
server: 172.31.7.109 #指定nfs服务器地址
注意:pv属于全局资源,不需要指定namespace。
根据yaml文件创建pv
kubectl apply -f 1-myapp-persistentvolume.yaml
查看pv
kubectl get pv
注意:pv状态必须时available状态才可以使用
3、创建pvc
vim 2-myapp-persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myserver-myapp-static-pvc #指定pvc名称
namespace: myserver #指定pvc的namespace,pvc要和业务容器在同一个namespace,否则容器将无法访问pvc,导致容器无法挂载存储
spec:
volumeName: myserver-myapp-static-pv #pvc通过指定pv名称和pv进行绑定
accessModes: #指定访问模式
- ReadWriteOnce
resources:
requests: #指定pvc大小,该值小于等于pv的大小
storage: 10Gi
注意:requests是指请求容量的最小值,即分配给pvc的空间最小是10G。由于这里pvc的resources没有添加limit做资源限制,因此可以使用超过10G的存储容量,要想通过pvc做资源限制,必须添加limit字段进行资源限制。
根据yaml文件创建pvc
kubectl apply -f 2-myapp-persistentvolumeclaim.yaml
查看pvc,查看时需要指定namespace
状态为bound,表示pv和pvc已经绑定完成
4、根据yaml文件创建容器
vim 3-myapp-webserver.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
labels:
app: myserver-myapp
name: myserver-myapp-deployment-name
namespace: myserver #指定容器namespace,pvc和容器namespace要保持一致
spec:
replicas: 3 #指定容器副本数
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 #指定挂载的存储卷名称,根据名称匹配存储卷,把该存储卷挂载到容器的/usr/share/nginx/html/statics目录
volumes: #指定存储类型为pvc
- name: statics-datadir #指定存储卷名称
persistentVolumeClaim:
claimName: myserver-myapp-static-pvc #指定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 #指定宿主机暴露端口为30080
selector:
app: myserver-myapp-frontend
根据yaml创建容器
kubectl apply -f 3-myapp-webserver.yaml
查看创建的3个容器,分别在172.31.7.111/112/113上
kubectl get pod -n myserver -o wide
5、分别进入3个容器查看挂载点,3个容器均挂载成功
kubectl exec -it myserver-myapp-deployment-name-fb44b4447-b2qfn -n myserver bash
df -Th
kubectl exec -it myserver-myapp-deployment-name-fb44b4447-gfvrb bash
df -Th
6、从宿主机向容器的挂载路径下传入文件
kubectl cp /root/1.jpg myserver-myapp-deployment-name-7c855dc86d-p9dvw:usr/share/nginx/html/statics/2.jpg -n myserver
注意:从宿主机向容器内传入文件时,命令中容器的路径usr/share/nginx/html/statics前不能带“/”。
7、在nfs存储(172.31.7.109)上查看是否存在文件
通过浏览器访问172.31.7.111:30080/statics/2.jpg,可以正常访问
通过浏览器访问172.31.7.112:30080/statics/2.jpg,也可以正常访问
动态存储卷示例
官网介绍:https://kubernetes.io/zh/docs/concepts/storage/storage-classes/
github项目:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
举例:
1、创建账号,进行授权
vim 1-rbac.yaml
apiVersion: v1
kind: Namespace #创建nfs namespace,用于保留nfs的驱动程序
metadata:
name: nfs
---
apiVersion: v1
kind: ServiceAccount #创建账号,负责pv创建时k8s内部的认证
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
根据yaml文件创建账号
kubectl apply -f 1-rbac.yaml
2、创建存储类storageclass
vim 2-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
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:
- noresvport #告知NFS客户端在重新建立网络连接时,使用新的传输控制协议源端口
- noatime #访问文件时不更新文件inode中的时间戳,高并发环境可提高性能
parameters:
mountOptions: "vers=4.1,noresvport,noatime"
archiveOnDelete: "true" #删除pod时保留pod数据,默认为false时为不保留数据
注意:创建storageclass时,如果在挂载选项(mountOptions)中指定NFS版本为4.x(即ver=4.x),创建容器时可能会挂载不上,因此需要把该选项删掉(下方代码块中红色字体部分要删掉)或者把版本改为3
mountOptions:
- ver=4.1
- noresvport
- noatime
根据yaml文件创建存储类
kubectl apply -f 2-storageclass.yaml
查看存储类
kubectl get storageclasses.storage -n myserver
3.创建NFS provisioner:
vim 3-nfs-provisioner.yaml
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 #pod更新策略,更新版本时新建pod
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-hangzhou.aliyuncs.com/docker_registry01/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 #值的内容要和storageclass的provisioner字段的值保持一致,会通过该字段调用storageclass创建pv
- name: NFS_SERVER #指定nfs服务器地址
value: 172.31.7.109
- name: NFS_PATH #指定nfs存储卷路径
value: /data/volumes
volumes: #声明一个存储卷
- name: nfs-client-root
nfs: #指定存储卷为nfs类型
server: 172.31.7.109 #指定nfs存储的地址
path: /data/volumes #指定nfs存储的挂载路径
在nfs存储节点(172.31.7.109)上创建共享目录
mkdir -p /data/volumes
修改nfs配置文件,指定共享目录,并重启服务,使配置生效
修改nfs配置文件
vim /etc/exports
/data/volumes *(rw,no_root_squash)
重启nfs服务
systemctl restart nfs-server
根据yaml文件创建NFS provisioner
kubectl apply -f 3-nfs-provisioner.yaml
4.创建PVC:
vim 4-create-pvc.yaml
# Test 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 #空间大小
根据yaml创建pvc
kubectl apply -f 4-create-pvc.yaml
查看pvc和pv
kubectl get pvc -n myserver (pvc名称为myserver-myapp-dynamic-pvc)
注意:pvc的状态为bound,说明pvc正常
kubectl get pv (pv名称为pvc-5551214e-c6bb-410a-9a00-9ec970e72f0a)
注意:这时pv的名字虽然显示为pvc,实际上是pv
此时在nfs存储节点(172.31.7.109)上查看pv,该pv存在于/data/volumes目录下
ls -lh /data/volumes/
注意:如果要删除pvc,要先删除调用pvc的pod,否则将无法删除pvc
5.创建web服务:
vim 5-myapp-webserver.yaml
kind: Deployment
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 #指定调用的pvc,要和下方pvc名称保持一致
volumes: #指定pvc
- name: statics-datadir #指定pvc名称
persistentVolumeClaim:
claimName: myserver-myapp-dynamic-pvc #指定调用的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
根据yaml文件创建业务容器
kubectl apply -f 5-myapp-webserver.yaml
查看pod,在宿主机172.31.7.113上
进入容器,查看挂载点
kubectl exec -it myserver-myapp-deployment-name-7c855dc86d-p9dvw bash -n myserver
df -Th
6、从宿主机向容器挂载目录(/usr/share/nginx/html/statics/)下传入图片,然后通过浏览器访问
向容器内传入图片3.jpg
kubectl cp /root/1.jpg myserver-myapp-deployment-name-7c855dc86d-p9dvw:usr/share/nginx/html/statics/3.jpg -n myserver
注意:从宿主机向容器内传入文件时,命令中容器的路径usr/share/nginx/html/statics前不能带“/”。
在浏览器访问172.31.7.113:30080/statics/3.jpg
动态存储卷调用流程:
(1)在创建容器的yaml文件中指定了调用的storageclass,因此当容器被创建时会调用storageclass关联pvc
(2)storageclass的yaml文件中指定nfs-provisioner作为nfs提供者
(3)nfs-provisioner的yaml文件中指定nfs服务器地址和存储卷的路径,它会连接到nfs创建pv
(4)创建pvc,pvc的yaml文件指定了调用的storageclass名称,会和已经创建的pv进行绑定
这样就完成了整个调度过程。
7.3.7 Configmap
Configmap配置信息和镜像解耦, 实现方式为将配置信息放到configmap对象中,然后在pod的中作为Volume挂载到pod中,从而实现导入配置的目的。
使用场景:
通过Configmap给pod定义全局环境变量
通过Configmap给pod传递命令行参数,如mysql -u -p中的账户名密码可以通过Configmap传递。
通过Configmap给pod中的容器服务提供配置文件,配置文件以挂载到容器的形式使用。
注意事项:
Configmap需要在pod使用它之前创建。
pod只能使用位于同一个namespace的Configmap,及Configmap不能跨namespace使用。
通常用于非安全加密的配置场景。
Configmap通常是小于1MB的配置。
示例:(这里配置文件使用configmap挂载,代码文件使用nfs存储卷挂载)
1、编辑yml文件
vim 1-deploy_configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config #指定configmap的名称,创建容器调用configmap时通过指定该名称来调用
data: #配置文件内容
default: | #指定配置文件为default,一个configmap可以有个多个配置,通过指定该项来调用不同的配置。
server {
listen 80;
server_name www.mysite.com; #指定站点域名为www.mysite.com
index index.html index.php index.htm;
location / {
root /data/nginx/html; #指定www.mysite.com的默认代码文件目录
if (!-e $request_filename) { #如果请求文件不存在,转发到index.html
rewrite ^/(.*) /index.html last;
}
}
}
---
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:1.20.0
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config #指定configmap在容器中挂载nginx配置文件路径
mountPath: /etc/nginx/conf.d/mysite
- name: nginx-mysite-config #指定nfs存储卷在容器中挂载代码文件路径
mountPath: /data/nginx/html
volumes:
- name: nginx-config
configMap:
name: nginx-config #指定configmap名称调用configmap的配置文件
items:
- key: default #指定调用default配置项的配置文件
path: mysite.conf #指定挂载路径,是指把configmap中的default配置挂载到mysite.conf文件中,而mysite.conf文件会挂载到/etc/nginx/conf.d/mysite目录下
- name: nginx-mysite-config #容器中存放代码文件的目录使用nfs存储卷进行挂载
nfs:
server: 172.31.7.109 #指定nfs服务器地址
path: /data/k8sdata/linux66 #指定存储卷路径
---
apiVersion: v1
kind: Service
metadata:
name: ng-deploy-80
spec:
ports:
- name: http
port: 81
targetPort: 80
nodePort: 30019 #指定宿主机暴露端口为30019
protocol: TCP
type: NodePort
selector:
app: ng-deploy-80
注意:yaml文件中的挂载关系:
(1)配置文件使用configmap挂载,configmap中的default配置会被挂载到mysite.conf文件中,而mysite.conf文件最终会被挂载到nginx的配置文件目录下(即/etc/nginx/conf.d/mysite目录),最终nginx的配置文件为/etc/nginx/conf.d/mysite/mysite.conf
(2)存放代码文件的目录使用nfs共享存储挂载,即将nfs存储上的/data/k8sdata/linux66挂载到容器内的/data/nginx/html,将代码文件存放到nfs存储/data/k8sdata/linux66目录下,访问www.mysite.com时,可以直接访问代码文件。
2、根据yaml文件创建容器
kubectl apply -f 1-deploy_configmap.yml
查看pod
3、进入容器查看配置文件已经挂载成功
4、由于/etc/nginx/conf.d/mysite/mysite.conf配置不在默认配置文件包括的范围(nginx包括的配置文件为/etc/nginx/conf.d/*.conf),该文件不会被加载,因此需要更改nginx的配置文件,把该文件加入nginx配置文件包含的范围内,然后重新加载nginx服务。
注意:这里为了便于实验,直接在容器内修改配置文件,这种修改方式并不正规。一般不会采用这种方式修改,而是通过重新打镜像的方式修改。
更改内容如下:
更改完之后重载nginx服务
nginx -s reload
5、在容器中查看/data/nginx/html,已经存在1.jpg图片
6、配置haproxy负载均衡,绑定VIP地址172.31.7.189,并监听三个node节点的30019端口(该端口为宿主机暴露端口,可以在外部访问通过该端口k8s集群内部服务)
listen magedu-linux66-nginx-80
bind 172.31.7.189:80 #vip地址
mode tcp
server node1 172.31.7.111:30019 check inter 3s fall 3 rise 1
server node2 172.31.7.112:30019 check inter 3s fall 3 rise 1
server node3 172.31.7.113:30019 check inter 3s fall 3 rise 1
监听三个node节点的30019端口,无论容器被调度到哪个node上,都可以访问。
在本机把域名www.mysite.com和 VIP地址172.31.7.189进行绑定
在浏览器访问域名www.mysite.com/1,jpg
小知识:
通过浏览器访问nginx静态文件,访问成功时,第一次访问会返回200,为什么再次访问会返回304?
这是因为浏览器对静态文件会打上Etag值,当客户端请求静态文件时,为了能够快速响应客户端请求,浏览器会对比静态文件的Etag值,如果值一样,说明请求的是同一个文件,会直接把客户端缓存的静态文件返回,而不会再去请求服务端,这时返回给客户端的就是304
示例:通过configmap传递环境变量
1、编辑yml文件
vim 2-deploy_configmap_env.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config #指定configmap名称,引用该configmap要指定该名称
data:
username: "user1" #指定环境变量,后面可以引用
password: "12345678" #指定环境变量,后面可以引用
---
#apiVersion: extensions/v1beta1
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
env:
- name: magedu
value: linux66
- name: MY_USERNAME #指定环境变量
valueFrom: #指定环境变量的值来源于configmap
configMapKeyRef:
name: nginx-config #指定configmap名称
key: username #指定configmap中的key
- name: MY_PASSWORD #指定环境变量
valueFrom: #指定环境变量的值来源于configmap
configMapKeyRef:
name: nginx-config #指定configmap名称
key: password #指定configmap中的key
ports:
- containerPort: 80
2、根据yaml文件创建容器
kubectl apply -f 2-deploy_configmap_env.yml
查看容器
3、进入容器查看环境变量,存在这两个环境变量
7.3.8 Secret
7.3.8.1 secret简介
Secret 的功能类似于 ConfigMap给pod提供额外的配置信息,但是Secret是一种包含少量敏感信息例如密码、令牌或密钥的对象。
Secret 的名称必须是合法的DNS子域名。
每个Secret的大小最多为1MiB,主要是为了避免用户创建非常大的Secret进而导致API服务器和kubelet内存耗尽,不过创建很多小的Secret也可能耗尽内存,可以使用资源配额来约束每个名字空间中Secret的个数。
在通过yaml文件创建secret时,可以设置data或stringData字段,data和stringData字段都是可选的,data字段中所有键值都必须是base64编码的字符串,如果不希望执行这种 base64字符串的转换操作,也可以选择设置stringData字段,其中可以使用任何非加密的字符串作为其取值。
Pod 可以用三种方式的任意一种来使用 Secret:
作为挂载到一个或多个容器上的卷中的文件(crt文件、key文件)。
作为容器的环境变量。
由 kubelet 在为 Pod 拉取镜像时使用(如镜像仓库的认证)。
7.3.8.2 secret类型
Kubernetes默认支持多种不同类型的secret,用于一不同的使用场景,不同类型的secret的配置参数也不一样。
Secret类型 | 使用场景 |
---|---|
Opaque | 用户定义的任意数据 |
kubernetes.io/service-account-token | ServiceAccount 令牌 |
kubernetes.io/dockercfg ~/.dockercfg | 文件的序列化形式 |
kubernetes.io/dockerconfigjson | ~/.docker/config.json 文件的序列化形式(镜像仓库认证) |
kubernetes.io/basic-auth | 用于基本身份认证的凭据 |
kubernetes.io/ssh-auth | 用于 SSH 身份认证的凭据 |
kubernetes.io/tls | 用于 TLS 环境,保存crt证书和key证书 |
bootstrap.kubernetes.io/token | 启动引导令牌数据 |
备注:常用的类型有三种,分别是:Opaque、kubernetes.io/dockerconfigjson和kubernetes.io/tls
类型1:secret类型-Opaque格式
1、Opaque格式-data类型数据:
base64加密方式:
echo admin | base64
echo 123456 | base64
举例:
vim 1-secret-Opaque-data.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret-data
namespace: myserver
type: Opaque
data: #指定secret为data类型,账号和密码必须使用base64加密
user: YWRtaW4K
password: MTIzNDU2Cg==
创建secret
kubectl apply -f 1-secret-Opaque-data.yaml
验证secret:
kubectl get secrets mysecret-data -n myserver -o yaml
以json格式显示的内容更加详细
2、Opaque格式stringData类型数据:
举例:
vim 2-secret-Opaque-stringData.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret-stringdata
namespace: myserver
type: Opaque
stringData: #指定secret为stringData类型,账号密码使用明文
user: 'admin'
password: '123456'
创建secret
kubectl apply -f 2-secret-Opaque-stringData.yaml
验证secret:(以json格式显示)
kubectl get secrets mysecret-stringdata -n myserver -o yaml
3、调用挂载secret
举例:
vim 3-secret-Opaque-mount.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myserver-myapp-app1-deployment
namespace: myserver
spec:
replicas: 1
selector:
matchLabels:
app: myserver-myapp-app1
template:
metadata:
labels:
app: myserver-myapp-app1
spec:
containers:
- name: myserver-myapp-app1
image: tomcat:7.0.94-alpine
ports:
- containerPort: 8080
volumeMounts:
- mountPath: /data/myserver/auth #指定secret挂载的路径
name: myserver-auth-secret #指定挂载的secret名称
volumes:
- name: myserver-auth-secret #指定secret名称,可以通过该名称调用secret
secret:
secretName: mysecret-data #指定调用data类型(base64加密)的secret
---
apiVersion: v1
kind: Service
metadata:
name: myserver-myapp-app1
namespace: myserver
spec:
ports:
- name: http
port: 8080
targetPort: 8080
nodePort: 30018
protocol: TCP
type: NodePort
selector:
app: myserver-myapp-app1
根据yaml文件创建容器
kubectl apply -f 3-secret-Opaque-mount.yaml
查看pod
进入容器查看secret挂载内容
可以看到在挂载目录下分别生成user和password两个文件,用户和密码信息分别存在文件中
注意:user和password信息虽然挂载之前使用base64加密,但是在挂载之后会对密文进行解密,因此在容器内看到的user和password是明文
在容器所在宿主机(172.31.7.111)可以看到挂载的secret文件
在172.31.7.111上查看secret文件
在etcd(172.31.7.106)上也可以查到secret文件
etcdctl get / --keys-only --prefix | grep mysecret
etcdctl get /registry/secrets/myserver/mysecret-data
调度流程:
(1)创建一个secret,然后发给api-server
(2)api-server把secret写入etcd
(3)当某服务要使用secret时,node节点上的kubelet通过api-server从etcd获取到secret
(4)然后再把secret挂载到容器内使用该secret
类型2:Secret类型-kubernetes.io/tls
1、自签名证书:
(1)创建存放证书的目录
mkdir certs
cd certs/
(2)创建ca证书
openssl req -x509 -sha256 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3560 -nodes -subj '/CN=www.ca.com'
(3)生成csr文件
openssl req -new -newkey rsa:4096 -keyout server.key -out server.csr -nodes -subj '/CN=www.mysite.com'
(4)创建自签名证书
openssl x509 -req -sha256 -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt
(5)查看创建的自签名证书
(6)创建tls类型的secret
kubectl create secret tls myserver-tls-key --cert=./server.crt --key=./server.key -n myserver
查看创建的tls类型的secret
2、创建web服务,引用tls证书
注意:这里为了方便演示,同时使用了configmap挂载为nginx的配置文件
vim 4-secret-tls.yaml
apiVersion: v1
kind: ConfigMap #创建configmap,挂载nginx配置我呢见
metadata:
name: nginx-config #指定configmap的名称
namespace: myserver
data: #指定configmap的配置内容
default: |
server {
listen 80;
server_name www.mysite.com;
listen 443 ssl;
ssl_certificate /etc/nginx/conf.d/certs/tls.crt; #指定ssl证书公钥路径
ssl_certificate_key /etc/nginx/conf.d/certs/tls.key; #指定ssl证书私钥路径
location / {
root /usr/share/nginx/html;
index index.html;
if ($scheme = http ){ #如果加条件判断,会导致死循环
rewrite / https://www.mysite.com permanent; #把访问http的请求转发给https
}
if (!-e $request_filename) {
rewrite ^/(.*) /index.html last;
}
}
}
---
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: myserver-myapp-frontend-deployment
namespace: myserver
spec:
replicas: 1
selector:
matchLabels:
app: myserver-myapp-frontend
template:
metadata:
labels:
app: myserver-myapp-frontend
spec:
containers:
- name: myserver-myapp-frontend
image: nginx:1.20.2-alpine
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config #指定configmap在容器内的挂载路径
mountPath: /etc/nginx/conf.d/myserver
- name: myserver-tls-key #指定secret在容器内挂载路径,该路径即为nginx配置中ssl证书的路径,key值会作为文件名(即tls.crt和tls.key)
mountPath: /etc/nginx/conf.d/certs
- name: nginx-mysite-config
mountPath: /usr/share/nginx/html
volumes:
- name: nginx-config
configMap: #指定挂载类型为configmap
name: nginx-config #指定configmap名称
items:
- key: default
path: mysite.conf
- name: myserver-tls-key
secret: #指定挂载类型为secret
secretName: myserver-tls-key #指定secret名称,其他服务根据该名称挂载secret
- name: nginx-mysite-config
nfs: #指定挂载类型为nfs
server: 172.31.7.109 #指定nfs服务器地址
path: /data/k8sdata/linux66 #指定nfs挂载路径
---
apiVersion: v1
kind: Service
metadata:
name: myserver-myapp-frontend
namespace: myserver
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30018 #指定http协议暴露宿主机端口
protocol: TCP
- name: htts
port: 443
targetPort: 443
nodePort: 30019 #指定https协议暴露宿主机端口
protocol: TCP
selector:
app: myserver-myapp-frontend
注意:挂载关系为:(1)nginx配置文件使用configmap挂载提供;(2)https协议中的ssl证书使用tls类型的secret挂载提供;(3)nginx代码文件使用nfs存储卷挂载提供。
根据yaml文件创建容器
kubectl apply -f 4-secret-tls.yaml
进入容器查看secret,证书已经挂载成功
查看配置文件,已经挂载成功
查看代码文件挂载路径,已经挂载成功,并且该目录下存在一个图片1.jpg
由于/etc/nginx/conf.d/myserver/nginx.conf不在主配置文件包含范围内,不会被加载,配置不会生效,因此需要修改nginx配置,把/etc/nginx/conf.d/myserver/nginx.conf包含进去
注意:这里为了方便实验,直接在nginx容器内更改nginx配置文件,这种方式并不正规。在实际环境中不会采用这种方法,而是通过打镜像的方式进行修改
重新加载nginx
nginx -s reload
查看容器内443端口已经被监听,说明配置文件/etc/nginx/conf.d/myserver/nginx.conf已经被加载
3、配置负载均衡(这里通过haproxy负载三个node节点,通过nodeport暴露的端口,可以从外部访问k8s集群内部的服务)
listen magedu-linux66-nginx-80
bind 172.31.7.189:80 #绑定vip的80端口
mode tcp
server node1 172.31.7.111:30018 check inter 3s fall 3 rise 1
server node2 172.31.7.112:30018 check inter 3s fall 3 rise 1
server node3 172.31.7.113:30018 check inter 3s fall 3 rise 1
listen magedu-linux66-nginx-443
bind 172.31.7.189:443 #绑定vip的443端口
mode tcp
server node1 172.31.7.111:30019 check inter 3s fall 3 rise 1
server node2 172.31.7.112:30019 check inter 3s fall 3 rise 1
server node3 172.31.7.113:30019 check inter 3s fall 3 rise 1
注意:这里的vip地址172.31.7.189是通过已经部署的keepalived设置的。
4、在本机hosts文件中绑定www.mysite.com和172.31.7.189
在浏览器访问https://www.mysite.com/1.jpg
类型3:Secret类型-kubernetes.io/dockerconfigjson
存储docker registry的认证信息,在下载镜像的时候使用,这样每一个node节点就可以不登录也可以下载私有级别的镜像。
可以通过两种方式创建:
方式一:通过命令创建
kubectl create secret docker-registry keyName \
--docker-server=registry.myserver.com \
--docker-username=USER\
--docker-password=PASSWORD
方式二:通过docker认证文件创建:
docker login registry.cn-hangzhou.aliyuncs.com
kubectl create secret generic aliyun-registry-image-pull-key \
--from-file=.dockerconfigjson=/root/.docker/config.json \
--type=kubernetes.io/dockerconfigjson \
-n myserver
示例:(以第2种方法进行举例)
1、登录私有镜像仓库(可以在阿里云创建)
docker login registry.cn-hangzhou.aliyuncs.com
登录成功后会在用户家目录生成认证文件
ls -lh /root/.docker/config.json
2、创建secret
kubectl create secret generic aliyun-registry-image-pull-key \
--from-file=.dockerconfigjson=/root/.docker/config.json \
--type=kubernetes.io/dockerconfigjson \
-n myserver
查看secret
查看secret详细内容
3、创建pod,引用secret
vim 5-secret-imagePull.yaml
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: myserver-myapp-frontend-deployment
namespace: myserver
spec:
replicas: 1
selector:
matchLabels:
app: myserver-myapp-frontend
template:
metadata:
labels:
app: myserver-myapp-frontend
spec:
containers:
- name: myserver-myapp-frontend
image: registry.cn-hangzhou.aliyuncs.com/docker_image01/nginx:1.16.1-alpine-perl
ports:
- containerPort: 80
imagePullSecrets: #指定镜像拉取的secret
- name: aliyun-registry-image-pull-key
---
apiVersion: v1
kind: Service
metadata:
name: myserver-myapp-frontend
namespace: myserver
spec:
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30018
protocol: TCP
type: NodePort
selector:
app: myserver-myapp-frontend
根据yaml创建容器
kubectl apply -f 5-secret-imagePull.yaml
查看容器状态,容器正在创建
查看容器日志,镜像已经拉取成功,说明已经secret已经被引用
kubectl describe pod myserver-myapp-frontend-deployment-64d99987df-4w4l2 -n myserver
容器已经被拉起,并且运行在宿主机172.31.7.111上
在172.31.7.111上查看本地镜像,镜像已经被拉取到本地
注意:secret是由node节点的kebelet通过api-server从etcd获取,而镜像则是由docker去拉取的。
调度过程:
(1)dockerconfigjson类型的secret创建完成后发给api-server
(2)api-server把secret写入etcd
(3)当存在创建容器的任务,kubelet获取到创建容器的任务,把任务交给runtime(运行时)docker
(4)docker连接镜像仓库(yaml文件中指定的是阿里云镜像仓库)拉取镜像,拉取镜像时根据yaml指定的secret,调用已经创建好的secret通过镜像仓库认证
(5)kubelet通过api-server从etcd获取secret发给docker进行镜像仓库认证
(6)认证通过后,把镜像拉取到本地创建容器
7.3.9 Statefulset
官网链接:https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/
Statefulset为了解决有状态服务的集群部署、集群之间的数据同步问题(MySQL主从等)
Statefulset所管理的Pod拥有唯一且固定的Pod名称(即使pod被删除,重建后pod名称仍然不会变化)
Statefulset按照顺序对pod进行启停、伸缩和回收(创建pod是从前到后的顺序进行创建:如果要创建10个pod,pod1创建完成后,pod2才会被创建,以此类推;pod回收是从后向前的顺序回收:如果有1-10个pod,会先对pod10进行回收,然后回收pod9,以此类推)
Headless Services(无头服务,即service不配置ip地址,而是把请求的service域名直接解析为pod IP)
以mysql主从为例
创建过程如下:
(1)申请创建statefulset
(2)apiserver创建statefulset控制器
(3)statefulset控制器创建mysql副本数
(4)创建的mysql副本会自动加上序号用以区分三个pod
(5)每个pod挂载pvc和pv用以持久化数据,每个pod挂载的存储可以不相同(如mysql1使用nfs存储,mysql2使用ceph,mysql3使用NAS),但三个mysql存储中的数据是一致的。
注意:statefulset除了可以部署有状态服务,也可以部署无状态服务
示例:(这里以nginx镜像为例)
vim 1-Statefulset.yaml
---
apiVersion: apps/v1
kind: StatefulSet #指定控制器类型
metadata:
name: myserver-myapp #指定控制器名称
namespace: myserver
spec:
replicas: 3 #指定创建pod副本数
serviceName: "myserver-myapp-service"
selector:
matchLabels:
app: myserver-myapp-frontend
template:
metadata:
labels:
app: myserver-myapp-frontend
spec:
containers:
- name: myserver-myapp-frontend
image: nginx:1.20.2-alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: myserver-myapp-service #指定service名称
namespace: myserver
spec:
clusterIP: None #statefulset类型的控制器,service没有地址,会把service域名解析为pod地址
ports:
- name: http
port: 80
selector:
app: myserver-myapp-frontend
根据yaml文件创建pod
kubectl apply -f 1-Statefulset.yaml
验证两点:
第一点,pod会按照顺序创建或回收
第二点,service本身没有ip,解析service名称,得到的地址是service后端的podIP。
1、查看创建过程,发现pod是按照顺序(即先pod0,pod1,pod2)创建的。
注意:deployment控制器创建的pod名称是deployment名称+replicaset名称+随机字串,而statefulset控制器创建的pod名称是statefulset名称加上序号。
删除pod,查看删除过程,发现是从后向前删除(即pod2,pod1,pod0)
查看pod,ip地址分别为10.200.107.241,10.200.36.102,10.200.169.153
查看创建的service,没有clusterIP,其名称为myserver-myapp-service
kubectl get svc -n myserver
查看service的endpoint地址,是三个pod的地址
kubectl get ep -n myserver
2、在default namespace进入容器解析myserver namespace下的service域名进行验证,发现解析到的ip地址是pod ip(dns以轮询方式把service域名解析到三个podIP)。
注意:由于在不同的namespace,因此在default namespace解析myserver namespace的容器需要加入容器service所在的namespace
kubectl exec -it net-test1 bash -n default
ping myser-myapp-service.myserver
注意:这里返回的三个pod全称为:
myserver-myapp-0.myserver-myapp-service.myserver.svc.magedu.local
myserver-myapp-1.myserver-myapp-service.myserver.svc.magedu.local
myserver-myapp-2.myserver-myapp-service.myserver.svc.magedu.local
statefulset控制器创建的pod,删除后自动重建,可以看到pod名称不变
由于pod名称是固定且唯一的,我们可以通过指定pod域名来固定访问某个pod,如mysql主从,我们可以通过指定pod域名来指定mysql主节点和mysql从节点,这样就可以在k8s集群中部署有状态服务了。
注意:重新创建的pod ip地址虽然发生变化,但是pod名称不会变化,那么pod域名也不会变化,因此解析pod的域名,仍然可以解析到新建pod的ip地址,这样就能保证pod重建后我们也能访问到固定的pod容器
查看重建后的pod ip
在default namespace的net-test1容器中解析myapp-0的域名,ip地址就是重建后的pod ip。
7.3.10 Daemonset
DaemonSet 在当前集群中每个节点运行同一个pod,当有新的节点加入集群时也会为新的节点配置相同的pod,当节点从集群中移除时其pod也会被kubernetes回收,删除DaemonSet 控制器将删除其创建的所有的pod。
可以为加入集群的节点安装一些基础组件,如:网络组件、日志收集客户端、监控客户端等
官网链接:https://kubernetes.io/zh/docs/concepts/workloads/controllers/daemonset/
示例1:在每个node上创建一个nginx容器直接web服务的请求
场景:外部访问集群内部pod(如nginx服务)时,会先访问暴露的nodeport,然后转发给nginx前端的service,由service转发给后端的nginx容器,这样一来,如果负责转发的service和node在同一台机器则直接转发,如果不在同一台机器,就需要跨主机转发,这样存在性能损耗。此时我们只需在每个node上创建nginx容器,那么就可以直接处理请求,减少跨主机转发,降低性能损耗。
1、编辑yaml文件
vim 1-DaemonSet-webserverp.yaml
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: myserver-myapp
namespace: myserver
spec:
selector:
matchLabels:
app: myserver-myapp-frontend
template:
metadata:
labels:
app: myserver-myapp-frontend
spec:
tolerations: #创建污点,增加容忍度,忽略master节点不可调度的标签,即在master节点也创建该容器
# this toleration is to have the daemonset runnable on master nodes
# remove it if your masters can't run pods
- key: node-role.kubernetes.io/master #指定key为master节点
operator: Exists #存在
effect: NoSchedule #不可调度的标签
hostNetwork: true #是否使用宿主机网络,这里选择可以使用,减少网络开销,如果使用宿主机网络,注意端口冲突。
hostPID: true #是否使用宿主机的PID,可以选择可以使用
containers:
- name: myserver-myapp-frontend
image: nginx:1.20.2-alpine
ports:
- containerPort: 80 #指定容器的端口,由于使用宿主机网络,该端口会在宿主机直接监听,因此不能和宿主机上的端口冲突
---
apiVersion: v1
kind: Service #如果pod使用hostnetwork模式直接使用宿主机网络,则可以不使用service,service可以不用配置
metadata:
name: myserver-myapp-frontend
namespace: myserver
spec:
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30018
protocol: TCP
type: NodePort
selector:
app: myserver-myapp-frontend
2、创建pod
kubectl apply -f 1-DaemonSet-webserverp.yaml
3、查看pod,master和node节点上均部署nginx,并且暴露80端口
此时,可以直接访问master和node节点中任一节点的80端口,来访问nginx服务
示例2:日志收集:在每个node上部署一个日志收集客户端flu
1、编写yaml文件
vim 2-DaemonSet-fluentd.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations: #创建污点,增加容忍度,忽略master节点不可调度的标签,即在master节点也创建该容器
# this toleration is to have the daemonset runnable on master nodes
# remove it if your masters can't run pods
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log #指定挂载到容器内的目录,可以在fluentd配置中指定该目录为日志收集目录
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers #指定挂载到容器内的目录,可以在fluentd配置中指定该目录为日志收集目录
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog #把宿主机的日志目录挂载到fluentd容器,使fluentd客户端可以收集日志
hostPath:
path: /var/log
- name: varlibdockercontainers #把容器在宿主机默认生成的的日志目录挂载到fluentd容器内,使fluentd客户端可以收集日志
hostPath:
path: /var/lib/docker/containers
2、查看pod,master和node节点上都创建了fluentd pod
举例3:为每个node节点部署监控客户端
1、编写yaml文件
vim 3-DaemonSet-prometheus.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
labels:
k8s-app: node-exporter
spec:
selector:
matchLabels:
k8s-app: node-exporter
template:
metadata:
labels:
k8s-app: node-exporter
spec:
tolerations: #创建污点,增加容忍度,忽略master节点不可调度的标签,即在master节点也创建该容器
- effect: NoSchedule
key: node-role.kubernetes.io/master
containers:
- image: prom/node-exporter:v1.3.1
imagePullPolicy: IfNotPresent
name: prometheus-node-exporter
ports:
- containerPort: 9100 #指定容器端口
hostPort: 9100
protocol: TCP
name: metrics
volumeMounts:
- mountPath: /host/proc
name: proc
- mountPath: /host/sys
name: sys
- mountPath: /host
name: rootfs
args:
- --path.procfs=/host/proc
- --path.sysfs=/host/sys
- --path.rootfs=/host
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
- name: rootfs
hostPath:
path: /
hostNetwork: true #使用宿主机网络
hostPID: true #使用宿主机PID
2、创建监控专用namespace “monitoring”
kubectl create ns monitoring
3、创建pod
kubectl apply -f 3-DaemonSet-prometheus.yaml
4、查看pod
5、访问容器9100端口(该端口为prometheus监控数据端口,可用来测试访问后端pod)
日志收集方式:
1、原生方式:容业务器日志输出到物理机,在物理机收集容器日志
2、daemonset方式:容器日志输出到物理机,在物理机通过daemonset部署监控容器来收集业务容器日志
3、sidecar方式:在业务容器所在pod中创建监控容器来收集业务容器日志
文章评论