k8s是一个容器编排工具。它是集群架构,有master和node节点组成。master有api server,controller manager,scheduler调度器。node作为工作节点有kubelet,kube proxy和容器引擎如docker。
# api server
提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制;
# controller manager
负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
各个不同的controller管理不同的资源,如replication controller管理deployment、statefulSet,daemonSet的生命周期;namespace controller管理namespace资源。
# scheduler
调度器,负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上;亲和性、污点容忍、指定。
# etcd
分布式的键值对数据库,存储数据,可以持久化。
# kubelet
直接跟容器引擎(docker)交互(CRI容器运行接口)实现pod里面容器的生命周期管理
# kube proxy
负责为Service提供cluster内部的服务发现和负载均衡;写入规则至iptables或者ipvs。
# Container runtime
负责镜像管理以及Pod和容器的真正运行(CRI);docker或者rkt容器引擎等
---
# kube-dns
负责为整个集群提供DNS服务
# dashboard
B/S访问体系
# ingress controller
官方四层代理,它可以实现7层代理,可以根据主机名,域名进行代理
# federation
集群联邦,跨集群中心多k8s统一管理功能
# prometheus
提供k8s集群的监控能力
更多细节可以参考Kubernetes中文手册。
流程
客户的启动容器等请求会先发给master节点,master节点有个scheduler调度器会分析node节点资源(cpu、内存)的可用状态,找到最佳适配的node来启动用户请求的容器。可见,Master节点主要还是负责管理和控制,Node节点是工作负载节点。
优点
滚动更新;弹性伸缩;负载均衡;适合微服务;轻量级;开源;
Pod
Pod是Kubernetes中最基本的调度单元,一般说来,一个pod里只放一个容器,可以包含多个容器。
Pod逻辑上表示某种应用的一个实例,也就是容器的更上面的一层载体。它主要是无需关注容器引擎的具体实现是什么。docker或者其他如LXC 容器,rkt。k8s只管pod就行。一组相同功能的pod组成一个service对外提供访问。
pause容器:
pod总会有一个“根容器”pause容器,它的存在可以使pod里面的容器之间可以进行共享,一是共享网络栈(localhost访问即可),二是共享存储卷。
Replication Controller,RC
Replication Controller,RC定义了副本数和创建容器的模板,
可以对pod以及里面的容器进行扩容,回滚,更新。保证按照预期的情况运行。
Replica Set,RS
Replica Set和Deployment,跟rc没有身本质区别,
唯一区别就是支持集合式的selector,对label支持集合的选取。
rc只支持等式的选取
Deployment
Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新。
Deployment本身并不负责pod创建。它建立rs,由rs来创建。但是Deployments支持滚动更新。
虽然ReplicaSets可以独立使用,但是今天它主要被 Deployments 作为协调pod创建,删除和更新的机制。rc不支持rolling-update滚动更新。
即rs比rc多一个支持集合式的selector功能。label可以使用in,不仅仅是=
deployment比又rs多支持rolling-update滚动更新的功能(创建多个rs),还支持版本记录、回滚、暂停升级等高级特性。
滚动更新:Deployment会创建多个rs,并且保存最近的历史几个rs。见下图。
HPA
Kubernetes有一个HPA(Horizontal Pod Autoscaler)的资源,可以实现基于CPU使用率的Pod自动伸缩的功能
StatefulSet
StatefulSet:用于有状态服务,它管理所有有状态的服务,比如MySQL、MongoDB集群等。而RC、Deployment、DaemonSet都是面向无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。
StatefulSet特点:
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。
稳定的网络标识符,即Pod重新调度后其PodName和HostName不变。
有序部署,有序扩展,基于init containers来实现。
重点
StatefulSet本质上是Deployment的一种变体,它为了解决有状态服务的问题。
它所管理的Pod拥有固定的Pod名称,启停顺序。
在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。
在Deployment中,与之对应的服务是service,
而在StatefulSet中与之对应的headless service,headless service,即无头服务,
headless service与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
DaemonSet
确保全部或者某些node(服务器)上运行一个pod的副本。跟屁虫的角色,比如日志收集,运行监控Prometheus。
Job
job,执行一次的pod。一些脚本的程序,比如备份脚本。cron job,定时执行。
Service
对外提供服务的角色。k8s的核心。
服务发现根据selector下选择服务条件的lable来进行联系。Service有ip:port。均衡负载(RR,轮询)内部已经实现好分配到某个pod执行。
k8s认为所有的pod都在一个直接连通的扁平化的网络空间之中,每一个pod都有唯一ip地址。因此k8s的网络模型的名字叫做IP-perPod模型。
网络通信方式
同一个pod里面的多个容器:lo本地回环接口
各个pod之间:
在同一个node,则直接使用docker0或者cni0网桥进行转发。
不在同一个node节点,则使用覆盖网络overlay network或者underlay
pod和service:各个节点的iptables规则/lvs
外网访问pod:通过service的nodeport或者云服务的负载均衡、或者更上一层的ingress、与主机同局域网的一个地址hostNetwork,利用宿主机的端口hostPort 等共5种方法
pod访问外网,SNAT转发
flannel
这个方案是flannel这个网络规划服务。让不同node为docker容器创建一个全集群唯一的虚拟ip地址。然后建立一个覆盖网络overlay network,通过这个覆盖网络将内容和虚拟ip信息包装起来,发送到另外一个node上。
保证唯一和如何通信,etcd有着重要作用:一是、存储管理flannel可分配的IP地址段资源。二是、监控etcd中每个pod的实际地址,并在内存中建立维护pod节点路由表。 k8s中所有的内容都抽象为资源,资源实例化之后,叫做对象。
一般k8s资源可以分为3类:一是,命名空间级别;二是,集群级别;三是,元数据型。
命名空间级别:
工作负载型资源对象(workload):pod,rc,ReplicaSet,Deployment,StatefulSet,DaemonSet,Job,Cronjob ...
服务发现及均衡资源对象:Service,Ingress ...
配置与存储资源对象:Volume(存储卷),CSI(容器存储接口,可以扩展各种各样的第三方存储卷),ConfigMap(当做配置中心来使用资源类型),Secret(保存敏感数据),DownwardAPI(把外部环境的信息输出给容器)
集群级资源:
Namespace,Node,Role,ClusterRole,RoleBinding,ClusterRoleBinding
元数据型资源:
HPA,PodTemplate(pod模板),LimitRange(资源限制)
在k8s中,一般使用yaml格式的文件来创建符合我们预期期望的pod,这样的yaml文件我们一般称为资源清单。这些资源清单类型可以为Namespace、Pod、Service、Deployment等。
apiVersion: v1 #API版本
kind: Pod #资源类型必须大写
metadata: #元数据对象
name: pod-demo #如Pod名称
namespace: default #命名空间默认default
labels: #标签
app: myapp
spec: #详细对象
containers: #容器列表
- name: test1 #容器名称
image: qsm:latest #镜像
探测:在Pod的声明周期中保证里面的容器能够正常的提供服务。有些情况下:pod里面的容器存在,但是程序进程已经死亡了。常见就是Jenkins成功,但是服务因为某些原因没起来。
puas、init C、start、readiness、liveness、stop
1、首先是pod里面会创建puase根容器,共享网络栈和存储卷
2、然后执行初始化容器(init Containter),可有可无,主要初始化一些mainC所需要的东西,比如提前获取加密文件;或者检测其他服务是否正常启动等。
特点:一是总是运行到成功完成为止,失败会遵循MainC的重启策略。二是先来后到原则,前一个initC完全成功之后才会启动下一个initC。
3、接下来主容器(main Containter)运行,运行过程有4个点,
一个是start,刚开始运行容器的时候执行的命令。
一个是stop,容器结束的时候执行的命令。
中间有就绪检测readiness(检测成功完成之后,才能提供对外服务。不成功,即使pod的状态为running,但是ready显示0/1。)
中间有生存检测liveness(过程中,容器不能正常运行,会执行重启或者删除等操作)。
init Containter实例
apiVersion: v1
kind: Pod
metadata:
name: qsm-pod
labels:
app: myapp
spec:
containers:
- name: qsm-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
- name: init-mydb
image: busybox
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
上面的2个初始化容器是说检测到myservice和mydb才会成功
kind: Service
apiVersion: v1
metadata:
name: myservice
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
name: mydb
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9377
运行成功之后,Pod(qsm-pod)才会成功。原因是myservice和mydb对应的ip会写入coredns里面。
探针的三种类型
1、命令:指定在容器内执行的命令,返回0,则认为诊断成功
2、tcp:对ip+port进行检测,端口打开就任务诊断成功
3、http:执行一个http的get请求,返回码大于等于200,小于400认为诊断成功。
探测的2种方案(方式)
在Kubernetes上下文中就绪探针和存活探针被称作健康检查
1、就绪探针:经过短暂的延时,进行探测,检测成功完成之后,才能提供对外服务。不成功,即使pod的状态为running,但是ready显示0/1。svc也不会让其提供服务。
2、生存探针:经过短暂的延时,定时的进行检测程序是否还活着;若容器不能正常运行,会执行重启或者删除等操作,主要是看重启策略如何设置的。
注意:存活探针探测失败会导致pod重新启动,所以配置初始探测延迟initialDelaySeconds十分重要,要确保在应用准备之后探针才启动。否则,应用将无限重启!
举例
apiVersion: v1
kind: Pod
metadata:
name: readiness-httpget-pod
namespace: default
spec:
containers:
- name: readiness-httpget-container
image: busybox
imagePullPolicy: IfNotPresent
readinessProbe:
httpGet:
port: 8080
path: /health
initialDelaySeconds: 10 #探测延时时长,第一次探测前等待10秒,默认为0
periodSeconds: 3 #每3秒执行一次liveness探测,默认值10秒,最小1秒
livenessProbe:
exec:
command: ["test","-e","/tmp/healthy"]
initialDelaySeconds: 5 #探测延时时长,第一次探测前等待5秒,默认为0
periodSeconds: 5 #每5秒执行一次liveness探测,默认值10秒,最小1秒
timeoutSeconds: 2 #超长时长,默认为1s,最小值也为1s
failureThreshold: 3 #处于成功状态时,探测操作至少连续多少次的失败才被视为检测不通过,默3次,最小为1
tcp端口检测时使用
tcpSocket:
port: 8080
启动和退出
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
namespace: default
spec:
containers:
- name: lifecycle-demo-container
image: kone.com/library/nginx
imagePullPolicy: IfNotPresent
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo test postStart > /tmp/lifecycle"]
prestop:
httpGet:
host: qsm.com
psth: /waring
port: 8080
scheme: HTTP
k8s有很多的控制器,用于控制pod的具体状态和行为。也就是创建和维持Pod的模板。
控制器类型
1、deployment:适合无状态的服务部署。replicationController和ReplicaSet(支持集合式的selector)。deployment又比rs多了滚动更新等功能。
2、StatefullSet:适合有状态的服务部署
3、DaemonSet:一次部署,所有(或某些)的node节点都会部署,例如一些典型的应用场景:在每个Node上运行日志收集和监控logstash Prometheus Node Exporter
4、Job:一次性的执行任务
5、Cronjob:周期性的执行任务
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3 #指定副本数为3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx1
image: nginx:1.15.4
ports:
- containerPort: 80
集合式的Selector
1、基于等式的Selector
上面Deployment就是基于等式的Selector。也就是基于等式,如等于,不等于
app=nginx 选择所有Label中key为app,value为nginx的对象。
env!=dev 选择所有Label中key为env,value不等于dev的对象。
2、基于集合式的Selector
基于集合的Selector通过集合操作的表达式来进行筛选。也就是有in
name in (redis-master, redis-slave) 选择所有Label中key为name,并且value为redis-master或redis-slave的对象。
env not in (dev) 选择所有Label中key为env,并且value不为dev的对象。
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: deamonset-example
labels:
app: daemonset
spec:
selector:
matchLabels:
name: deamonset-example
template:
metadata:
labels:
name: deamonset-example
spec:
containers:
- name: daemonset-example
image: qsm/myapp:v3
apiVersion: batch/v1
kind: Job
metadata:
name: myjob
spec:
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["echo", "hello k8s job! "]
restartPolicy: Never
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: Cronjob-example
labels:
app: mycronjob
spec:
schedule: "*/2 * * * *"
jobTemplate:
metadata:
labels:
app: mycronjob-jobs
spec:
parallelism: 2 #job控制器的spec.parallelism表示同时运行的Pod对象数,默认1
template:
spec:
containers:
- name: myjob
image: nginx
command: ["/bin/sh","-c", "sleep 120"]
restartPolicy: OnFailure
Service,可以看作是一组提供相同服务的Pod对外的访问接口。因为pod的ip会动态变化,而且外部无法访问,所以需要service作为服务发现,可以对一组pod,通过Label Selector来发现这些pod。比如,RR这一种负载均衡策略。
node上有个组件叫kube-proxy,它负责和ApiServer进行通信,kube-proxy一旦发现service背后的pod地址发生变化,kube-proxy就会把pod地址反映到iptables 或者ipvs中。所以service的管理是靠kube-proxy来实现的。
常见的servic定义如下:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
labels:
app: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- name: http
port: 80
protocol: TCP
targetPort: 81
spec.ports.port: 80表明此Service将会监听80端口,并将所有监听到的请求转发给其管理的Pod。spec.ports.targetPort: 81表明此Service监听到的80端口的请求都会被转发给其管理的Pod的81端口,此字段可以省略,省略后其值会被设置为spec.ports.port的值。
Service的代理模式
有三种,分别是使用Userspace,iptables模型, ipvs模型。目前最新版本默认为ipvs。
以上不论哪种,kube-proxy都通过watch的方式监控着kube-APIServer写入etcd中关于Pod的最新状态信息,它一旦检查到一个Pod资源被删除了 或 新建,它将立即将这些变化,反应再iptables 或 ipvs规则中,以便iptables和ipvs在调度Clinet Pod请求到Server Pod时,不会出现Server Pod不存在的情况。
K8S系列:port、nodePort、targetPort区别
port:service暴露在clusterIp上的端口,clusterIp:port是提供给集群内部客户访问service的入口
nodePort:使用nodeIP:nodePort是提供给集群外部客户访问service的入口。因此前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。
targetPort:从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。即pod的端口。
Service的类型
在Serive定义时,我们需要指定spec.type
字段,这个字段拥有四种类型:
1、ClusterIP。默认值。给这个Service分配一个Cluster IP,它是Kubernetes系统自动分配的虚拟IP,因此只能在集群内部访问。
2、NodePort。在ClusterIP的基础上,将Service通过指定的Node上的端口暴露给外部。通过此方法,访问任意一个NodeIP:nodePort都将路由到ClusterIP,从而成功获得该服务。
3、LoadBalancer。在NodePort的基础上,借助 cloud provider 创建一个外部的负载均衡器,并将请求转发到 :NodePort。此模式只能在云服务器(AWS等)上使用。
4、ExternalName。将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定)。需要 kube-dns 版本在 1.7 以上。
ClusterIP例子:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: default
labels:
app: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- name: http
port: 80
protocol: TCP
targetPort: 81
NodePort例子
不指定nodePort参数,则会随机创建一个node对应的nodePort。外面就可以根据nodeIP:nodePort访问。而内部依然可以继续使用clusterIP:port访问。
apiVersion: v1
kind: Service
metadata:
name: nginx-service-nodeport
spec:
type: NodePort
selector:
app: nginx
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 80
- name: https
port: 8443
protocol: TCP
targetPort: 443
LoadBalancer例子:
在nodeport的基础上,是外部引用一个云服务的负责均衡器。收费。
ExternalName例子:
apiVersion: v1
kind: Service
metadata:
name: qsm-test-externalname
namespace: default
spec:
type: ExternalName
externalName: www.baidu.com
将外部服务地址在内部就通过service的qsm-test-externalname.default.svc.cluster.local来访问,相当在NDS服务有一个CName别名记录。主要是因为外部地址可能会变,这边只需要修改ExternalName类型的service就可以了。
有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP
)的值为 "None"
来创建 Headless
Service。
headless service做dns解析是直接解析到pod的,而servcie是解析到ClusterIP的。参考
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
release: canary
clusterIP: "None" #headless的clusterIP值为None
ports:
- port: 80
targetPort: 80
Ingress,是一个面向对外访问的API对象。可以实现7层代理,比如使用域名,使用http/https等。
为什么要使用ingress
动态配置服务:
如果按照传统方式, 当新增加一个服务时, 我们可能需要在流量入口加一个反向代理指向我们新的k8s服务.
而如果用了Ingress, 只需要配置好这个服务, 当服务启动时, 会自动注册到Ingress的中, 不需要额外的操作.
减少不必要的端口暴露:
配置k8s时, 第一步是要关闭防火墙, 原因是k8s的很多服务会以NodePort方式映射出去, 这样就相当于给宿主机打了很多孔, 既不安全也不优雅.
而Ingress可以避免这个问题, 除了Ingress自身服务可能需要映射出去, 其他服务都不要用NodePort方式。
目前对外暴露服务有五种方式:分别是NodePort、LoadBalancer、Ingress,hostNetwork和hostPort。
nodePort 方式当服务巨大的时候,端口管理将是灾难性的。
LoadBalancer 更适合结合云提供商的 LB 来使用,但是需要费用。
Ingress 是比较推荐的暴露服务的方式。相当于service的service。
hostNetwork与主机同局域网的一个地址,随时会变,
hostPort利用宿主机的端口
Ingress和Ingress Contoller和原理
Ingress是k8s资源清单对象。指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。
通俗的来说就是把将自定义的域名和路径写在Ingress上,然后访问service。
Ingress Contoller是一个pod服务,封装了一个web前端负载均衡器,同时在其基础上实现了动态感知Ingress,并根据Ingress的定义动态生成前端web负载均衡器的配置文件,比如Nginx Ingress Controller本质上就是一个Nginx,只不过它能根据Ingress资源的定义动态生成 Nginx 的配置文件,然后动态 Reload。
通俗来说,就是一个pod,与api service通信,只要有Ingress有改变,就将规则转化成Nginx的配置文件并使之生效;
因此,原理就是:
ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化,
然后读取它,按照ingress中自定义的规则,规则写明了哪个域名对应哪个service,生成一段nginx配置,
再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,
然后reload一下使配置生效。
以此达到域名分配置和动态更新的问题。
使用步骤:
1、正常使用的时候,需要先安装ingress-controller,先去下载一个mandatory.yaml。然后kubectl apply -f mandatory.yaml
就相当于安装了一个nginx-ingress-controller的一个pod,它去与apiservice交互,去感知Ingress资源的变化。
2、由于这个Nginx也需要对外提供暴露,所有有2种方式可选,第一个是创建service使用nodeport,一个就是hostPort(直接使用宿主机的端口)。
3、接下来可以正常的创建Service和Deployment。
4、最后一步就是编写Ingress的一个yaml文件。
配置:nginx-ingress-controller对外暴露使用第一种,创建service使用nodeport
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30080
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
nodePort: 30443
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
配置一个Ingress:主要是域名加路径,和指向哪个service
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo/qsm1
backend:
serviceName: service1
servicePort: 80
- path: /foo/qsm2
backend:
serviceName: service2
servicePort: 80
- host: bar.foo.com
http:
paths:
- path: /bar
backend:
serviceName: service3
servicePort: 80
configMap就是相当一个配置中心,允许您将配置构件与映像内容解耦,以保持容器化应用程序的可移植性。
创建方式有很多,现在使用ConfigMap资源清单来创建。然后kubectl create -f myconfigmap.yaml
执行。其他Deployment/Pod创建的时候可以使用环境变量ENV或者存储卷Volume使用。
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.how: very
---
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
namespace: default
data:
log_level: INFO
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 1
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: my-nginx
image: nginx:1.9
ports:
- containerPort: 8081
volumeMounts: --就是这一段使用configMap配置
- name: config-qsm
mountPath: /etc/nginx/conf.d --将配置文件挂载到哪里
volumes:
- name: config-qsm --指定config使用configMap
configMap:
name: special-config
它是用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。
有三种类型,service Accoun,pod与apiservice连接的时候需要用用的。默认就有;Opaque,用于base64 编码格式存秘钥,密码等;kubernetes.io/dockerconfigjson:用来存储私有docker registry的认证信息。
先创建一个下面的Secret的yaml文件,然后kubectl create -f ./secret.yaml
,使用它,可以使用env和Volume
apiVersion: v1
kind: Secret
metadata:
name: qsmsecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
apiVersion: v1
kind: Pod
metadata:
name: secret1-pod
spec:
containers:
- name: secret1
image: busybox
command: [ "/bin/sh", "-c", "env" ]
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: qsmsecret
key: username
- name: PASSWORD
valueFrom:
secretKeyRef:
name: qsmsecret
key: password
volume是k8s数据卷,常见的数据卷有3种类型,即EmptyDir,HostDir,NFS。kubenetes中的volume与Pod的生命周期相同,用于数据持久化。
可以在同一 Pod 内的不同容器之间共享工作过程中产生的文件。实际上在宿主机上还是有一个临时的文件夹与之对应。
apiVersion: v1
kind: Pod
metadata:
labels:
name: test-emptypath
role: master
name: test-emptypath
spec:
containers:
- name: test-emptypath
image: registry:5000/back_demon:1.0
volumeMounts:
- name: qsm-emptyDir-storage
mountPath: /home/laizy/test/
command:
- /run.sh
volumes:
- name: qsm-emptyDir-storage
emptyDir: {}
HostDir属性的volume使得对应的容器能够访问当前宿主机上的指定目录。
apiVersion: v1
kind: Pod
metadata:
labels:
name: qsm-hostpath
role: master
name: test-hostpath
spec:
containers:
- name: qsm-hostpath
image: registry:5000/back_demon:1.0
volumeMounts:
- name: qsm-ssl-certs
mountPath: /home/laizy/test/cert
readOnly: true
command:
- /run.sh
volumes:
- name: qsm-ssl-certs
hostPath:
path: /etc/ssl/certs
支持NFS文件系统,即可以网络存储。
apiVersion: apps/v1
kind: Deployment
metadata:
name: mydeploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: mynginx
template:
metadata:
name: web
labels:
app: mynginx
spec:
containers:
- name: mycontainer
image: qsm/nginx:v1
ports:
- containerPort: 80
volumeMounts:
- name: qsm-nfs
mountPath: /usr/share/nginx/html
volumes:
- name: qsm-nfs
nfs:
server: 192.168.254.3 #nfs服务器地址
path: /share #nfs服务器共享目录
PV可以理解为集群中,某个网络存储对应的一个存储,它有设置一些内存大小,访问模式等限制指标。而PVC只是声明需要那些存储的指标即可。k8s会自动将符合条件pv与该pvc一一绑定。
PV和PVC是一一对应关系,当有PV被某个PVC所占用时,会显示banding,其它PVC不能再使用绑定过的PV。PVC若没有找到合适的PV时,则会处于pending状态。
PVC一旦绑定PV,就相当于是一个存储卷,此时PVC可以被多个Pod所使用。(PVC支不支持被多个Pod访问,取决于访问模型accessMode的定义)。
PV是属于集群级别的,不能定义在名称空间中。PVC时属于名称空间级别的。
在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败.而且通过 PVC 请求到一定的存储空间也很有可能不足以满足应用对于存储设备的各种需求
而且不同的应用程序对于存储性能的要求可能也不尽相同,比如读写速度、并发性能等,为了解决这一问题,Kubernetes 又为我们引入了一个新的资源对象:StorageClass,通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。
简单而言对资源按照某些标准进行分类,好让pvc能知道申请合适的pv
PV状态
Available:空闲的资源,未绑定给PVC
Bound:绑定给了某个PVC
Released:PVC已经删除了,但是PV还没有被集群回收
Failed:PV在自动回收中失败了
PV访问模式
ReadWriteOnce – RWO - 一node读写
ReadOnlyMany – ROX - 多node只读
ReadWriteMany – RWX - 多node读写
回收策略
默认是Retain保留,保留生成的数据。
可以改为recycle回收,删除生成的数据,回收pv
delete,删除,pvc解除绑定后,pv也就自动删除。
Retain 管理员回收:kubectl delete pv pv-name 创建:kubectl apply -f pv-name.yaml ;
Retain策略 在删除pvc后PV变为Released不可用状态, 若想重新被使用,需要管理员删除pv,重新创建pv,删除pv并不会删除存储的资源,只是删除pv对象而已;若想保留数据,请使用该Retain,
Recycle策略 – 删除pvc自动清除PV中的数据,效果相当于执行 rm -rf /thevolume/*. 删除pvc时.pv的状态由Bound变为Available.此时可重新被pvc申请绑定
Delete – 删除存储上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等,NFS不支持delete策略
使用nfs例子:首先创建PV,在创建PVC,deployment种使用Volume挂载。
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
namespace: qsm-pv-test
spec:
capacity:
storage: 100Mi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
nfs:
# FIXME: use the right IP
server: 10.130.44.20
path: "/test/mysql-nfs01"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: qsm-nfs-pvc
namespace: qsm-pv-test
spec:
accessModes:
- ReadWriteMany
storageClassName: slow
resources:
requests:
storage: 90Mi
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: qsm-nginx
namespace: qsm-pv-test
spec:
replicas: 1
template:
metadata:
labels:
name: qsm-nginx
spec:
containers:
- name: qsm-nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- name: qsm-pvc-volume
mountPath: "/usr/share/nginx/html"
volumes:
- name: qsm-pvc-volume
persistentVolumeClaim:
claimName: qsm-nfs-pvc
应用程序存在有状态和无状态两种类别,因为无状态类没有特殊性,deployment、replicaset和daemonset等常用于管理无状态应用。而有状态的,比如存储数据的数据库等。
在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。
与pvc一起使用的例子
apiVersion: v1
kind: Service÷
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx" #声明它属于哪个Headless Service.
replicas: 3 # by default is 1
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: k8s.gcr.io/nginx-slim:0.8
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates: #可看作pvc的模板
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "gluster-heketi" #存储类名,改为集群中已存在的
resources:
requests:
storage: 1Gi
Statefulset名称为web 三个Pod副本: web-0,web-1,web-2,volumeClaimTemplates名称为:www,那么自动创建出来的PVC名称为www-web[0-2],为每个Pod创建一个PVC
规律总结:
匹配Pod name(网络标识)的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2。
StatefulSet为每个Pod副本创建了一个DNS域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非Pod IP,因为当Pod所在Node发生故障时,Pod会被飘移到其它Node上,Pod IP会发生变化,但是Pod域名不会有变化。
StatefulSet使用Headless服务来控制Pod的域名,这个域名的FQDN为:$(service name).$(namespace).svc.cluster.local,其中,“cluster.local”指的是集群的域名。
根据volumeClaimTemplates,为每个Pod创建一个pvc,pvc的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的volumeMounts.name=www, Pod name=web-[0-2],因此创建出来的PVC是www-web-0、www-web-1、www-web-2。
删除Pod不会删除其pvc,手动删除pvc将自动释放pv。
关于Cluster Domain、headless service名称、StatefulSet 名称如何影响StatefulSet的Pod的DNS域名的示例:
Cluster Domain | Service (ns/name) | StatefulSet (ns/name) | StatefulSet Domain | Pod DNS | Pod Hostname |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0…N-1}.nginx.default.svc.cluster.local | web-{0…N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0…N-1}.nginx.foo.svc.cluster.local | web-{0…N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0…N-1}.nginx.foo.svc.kube.local | web-{0…N-1} |
StatefulSet使用场景:
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现。
稳定的网络标识符,即Pod重新调度后其PodName和HostName不变。可以利用headless service一起创建服务。
有序部署,有序扩展,基于init containers来实现。
有序收缩。
1、为什么需要 headless service 无头服务?
在用Deployment时,每一个Pod名称是没有顺序的。无头服务,它可以给每个Pod一个唯一的名称 ,重建后也一样。
2、为什么需要volumeClaimTemplate?
Deployment中的Pod template里定义的存储卷,是所有副本集共用一个存储卷,数据是相同的。而statefulset中每个Pod都要自已的专有存储卷,它会为每个Pod生成不同的pvc,并绑定pv, 从而实现各pod有专用存储。
Scheduler调度步骤
节点预选(Predicate):排除完全不满足条件的节点,如内存大小,端口等条件不满足。
节点优先级排序(Priority):根据优先级选出最佳节点。
固定节点调度
指定NodeName和使用NodeSelector调度是最简单的,可以将Pod调度到期望的节点上。
sepc.nodename有值,不经过调度器调度。即将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配。
spec:
nodeSelector:
cloudnil.com/role: dev #指定调度节点为带有label标记为:cloudnil.com/role=dev的node节点
Pod.spec.nodeSelector。通过kubernetes的label-selector机制进行节点选择,由scheduler调度策略MatchNodeSelector进行label匹配,调度pod到目标节点,该匹配规则是强制约束
pec:
odeSelector:
qsm.com/role: dev #指定调度节点为带有label标记为:qsm.com/role=dev的node节点
节点亲和性
硬亲和性required: 必须满足亲和性
软亲和性 preferred:能满足最好,不满足也没关系
Pod亲和性
硬亲和性 required 、软亲和性 preferred。
Pod对象间亲和性,将一些Pod对象组织在相近的位置(同一节点、机架、区域、地区)
Pod对象间反亲和性,将一些Pod在运行位置上隔开
污点和容忍
污点 taints 是定义在节点上的键值型属性数据,用于让节点拒绝将Pod调度运行于其上,除非Pod有接纳节点污点的容忍度容忍度。这是主动权在node上。而亲和性主动权在pod上,在node上配置stain键值对。正式master节点设置了污点,所以新的pod都是不会被调度到主节点而是去工作节点node上创建。这样是一道面试题。
设置污点kubectl taint node1 node2 node-type=production:NoShedule
effect定义排斥等级:
NoSchedule,不能容忍,但仅影响调度过程,已调度上去的pod不受影响,仅对新增加的pod生效。
PreferNoSchedule,柔性约束,节点现存Pod不受影响,如果实在是没有符合的节点,也可以调度上来
NoExecute,不能容忍,当污点变动时,Pod对象会被驱逐
容忍tolerations 是定义在Pod上的键值属性数据,用于配置可容忍的污点,且调度器将Pod调度至其能容忍该节点污点的节点上或没有污点的节点上。在pod的yaml文件种配置。
设置容忍
piVersion: v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 3
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
tolerations:
- key: "node-type"
operator: "Equal"
value: "production":
effect: "NoExecute"
tolerationSeconds: 3600
以上笔记均是先导知识。后续进阶版笔记,待读完《Kubernetes权威指南 (从Docker到Kubernetes实践全接触) 》之后补齐。
【暂完】
【正在去BAT的路上修行!!!】