下内容整理自这本书的读书笔记:《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第4版),各大书店有售
一、无状态的服务
1.pod
1)基本概念
2)配置文件样例
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
app: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
3)命令行例子
kubectl get pod -o wide
kubectl describe pod
里面的IP是POD的IP
kubectl describe pod frontend-5cb785b459-6cn6w
里面有个container可以查看pod里各个容器的情况:包括容器name、image,服务端口等,加上ip就可以获取EndPoint
kubectl get endpoints
kubectl edit pod frontend-5cb785b459-6cn6w
执行pod内容器的命令
kubectl exec -it frontend-5cb785b459-6cn6w -c tomcat-demo -- mkdir /tmp/abc
kubectl exec -it frontend-5cb785b459-6cn6w -c tomcat-demo -- bash
会出现shell提示符,可以使用env,ls等命令
也可以在相应的node节点执行以下命令,进入到docker中的容器
sudo docker exec -it 5e45393d667c bash
2.label
1)基本概念
一个Label是一个key=value的键值对,其中key与value由用户自己指定。Label可以被附加到各种资源对象上,例如Node、Pod、Service、RC等
YAML中的标签定义格式:
labels:
key: value
如
labels:
app: myweb
给某个资源对象定义一个Label,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,当前有两种Label Selector表达式:基于等式的(Equality-based)和基于集合的(Set-based)
基于等式的操作符:=,!=
基于集合的操作符:in,notin,exists,doesnotexist
matchLabels用于定义一组Label,与直接写在Selector中的作用相同
selector:
app: mysql
与下面的表达式等价
selector:
matchLabels
app: mysql
matchExpressions用于定义一组基于集合的筛选条件,可用的条件运算符包括in,notin,exists,doesnotexist。
selector:
matchExpressions:
- {key: name1, operator: In , values:[v1,v2]}
- {key: name2, operator: In , values:[v3,v4]}
可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式之间用“,”进行分隔即可,几个条件之间是“AND”的关系,即同时满足多个条件
如果同时设置了matchLabels和matchExpressions,则两组条件为AND关系
Label Selector在Kubernetes中的重要使用场景如下。
◎ kube-controller进程通过在资源对象RC上定义的Label Selector来筛选要监控的Pod副本数量,使Pod副本数量始终符合预期设定的全自动控制流程。
◎ kube-proxy进程通过Service的Label Selector来选择对应的Pod,自动建立每个Service到对应Pod的请求转发路由表,从而实现Service的智能负载均衡机制。
◎ 通过对某些Node定义特定的Label,并且在Pod定义文件中使用NodeSelector这种标签调度策略,kube-scheduler进程可以实现Pod定向调度的特性。
3.Replication Controller/Replica Set
注:Replica Set与Deployment这资源对象逐步替代了之前RC的作用
1)基本概念
定义了一个期望的场景,即声明某种Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分。
◎ Pod期待的副本数量。
◎ 用于筛选目标Pod的Label Selector。
◎ 当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板(template)。
在定义了一个RC并将其提交到Kubernetes集群中后,Master上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保目标Pod实例的数量刚好等于此RC的期望值,如果有过多的Pod副本在运行,系统就会停掉一些Pod,否则系统会再自动创建一些Pod。可以说,通过RC,Kubernetes实现了用户应用集群的高可用性
Node 2上的Pod 2意外终止,则根据RC定义的replicas数量2,Kubernetes将会自动创建并启动一个新的Pod,以保证在整个集群中始终有两个redis-slave Pod运行。
在运行时,我们可以通过修改RC的副本数量,来实现Pod的动态缩放(Scaling),这可以通过执行kubectl scale命令来一键完成
kubectl scale rc redis-slave --replicas=3
Replication Controller由于与Kubernetes代码中的模块Replication Controller同名,同时“Replication Controller”无法准确表达它的本意,所以在Kubernetes 1.2中,升级为另外一个新概念—Replica Set,官方解释其为“下一代的RC”。Replica Set与RC当前的唯一区别是,Replica Sets支持基于集合的Label selector(Set based selector),而RC只支持基于等式的Label Selector(equality-based selector),这使得Replica Set的功能更强。
2)配置文件样例
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
spec:
replicas: 1
selector:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
上述的ReplicationController,可以替换为ReplicaSet关键字
3.Deployment
1)基本概念
Deployment在内部使用了Replica Set来实现目的,无论从Deployment的作用与目的、YAML定义,还是从它的具体命令行操作来看,我们都可以把它看作RC的一次升级,两者的相似度超过90%
Deployment的典型使用场景有以下几个。
◎ 创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建。
◎ 检查Deployment的状态来看部署动作是否完成(Pod副本数量是否达到预期的值)。
◎ 更新Deployment以创建新的Pod(比如镜像升级)。
◎ 如果当前Deployment不稳定,则回滚到一个早先的Deployment版本。
◎ 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项,之后再恢复Deployment,进行
新的发布。
◎ 扩展Deployment以应对高负载。
◎ 查看Deployment的状态,以此作为发布是否成功的指标。
◎ 清理不再需要的旧版本ReplicaSets。
2)配置文件样例
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
3)命令行例子
创建deployment
kubectl create -f frontend-deployment.yaml
查看Deployment的信息
kubectl get deploy -o wide
查看相应的Replica Set
kubectl get rs -o wide
kubectl describe rs frontend-5cb785b459
查看相应的Pod
kubectl get pod -o wide
kubectl describe pod frontend-5cb785b459-7trhb
注意这个pod的名称前缀,与相应的deploy和rs的名称是相同的
要是pod还处在Pulling image ...,还可以到相应的node上查看images的pull状态
sudo docker images
sudo docker pull tomcat
直到出现Running
基于deploy的伸缩
kubectl scale deploy frontend --replicas=2
5.Horizontal Pod Autoscaler
1)基本概念
通过手工执行kubectl scale命令,我们可以实现Pod扩容或缩容。如果仅仅到此为止,显然不符合谷歌对Kubernetes的定位目标—自动化、智能化。在谷歌看来,分布式系统要能够根据当前负载的变化自动触发水平扩容或缩容,因为这一过程可能是频繁发生的、不可预料的,所以手动控制的方式是不现实的。
HPA有以下两种方式作为Pod负载的度量指标。
◎ CPUUtilizationPercentage。
◎ 应用程序自定义的度量指标,比如服务在每秒内的相应请求数(TPS或QPS)。
2)配置文件样例
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: frontend
namespace: default
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
kind: Deployment
name: frontend
targetCPUUtilizationPercentage: 90
3)命令行例子
命令行格式:kubectl autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU] [options]
kubectl autoscale deploy frontend --min=1 --max=10 --cpu-percent=90
二、有状态的服务
6.StatefulSet
1)基本概念
在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet和Job都面向无状态的服务。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群,例如MySQL集群、MongoDB集群、Akka集群、ZooKeeper集群等,这些应用集群有4个共同点。
(1)每个节点都有固定的身份ID,通过这个ID,集群中的成员可以相互发现并通信。
(2)集群的规模是比较固定的,集群规模不能随意变动。
(3)集群中的每个节点都是有状态的,通常会持久化数据到永久存储中。
(4)如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损。
StatefulSet,StatefulSet从本质上来说,可以看作Deployment/RC的一个特殊变种,它有如下特性。
◎ StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群内的其他成员。假设StatefulSet的名称为kafka,那么第1个Pod叫kafka-0,第2个叫kafka-1,以此类推。
◎ StatefulSet控制的Pod副本的启停顺序是受控的,操作第n个Pod时,前n-1个Pod已经是运行且准备好的状态。
◎ StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(为了保证数据的安全)。
StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless Service配合使用,即在每个StatefulSet定义中都要声明它属于哪个Headless Service。Headless Service与普通Service的关键区别在于,它没有Cluster IP,如果解析Headless Service的DNS域名,则返回的是该Service对应的全部Pod的Endpoint列表。StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod实例都创建了一个DNS域名,这个域名的格式为:
$(podname).$(HeadlessServicename)
比如一个3节点的Kafka的StatefulSet集群对应的Headless Service的名称为kafka,StatefulSet的名称为kafka,则StatefulSet里的3个Pod的DNS名称分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称可以直接在集群的配置文件中固定下来。
7.Service
1)基本概念
Kubernetes里的每个Service其实就是我们经常提起的微服务架构中的一个微服务,下图显示了Pod、RC与Service的逻辑关系:Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现无缝对接的。RC的作用实际上是保证Service的服务能力和服务质量始终符合预期标准。
2)配置文件样例
deploymentYAML文件
#nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-web1
spec:
replicas: 1
selector:
matchLabels:
app: nginx-web1
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
metadata:
labels:
app: nginx-web1
tier: frontend
spec:
containers:
- name: nginx-web1
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
单端口svc版本
#nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-web1-svc-s
spec:
ports:
- port: 80
selector:
app: nginx-web1
tier: frontend
在spec.ports的定义中,targetPort属性用来确定提供该服务的容器所暴露(EXPOSE)的端口号,即具体业务进程在容器内的targetPort上提供TCP/IP接入;port属性则定义了Service的虚端口。前面定义Tomcat服务时没有指定targetPort,则默认targetPort与port相同,如下面的作用与上述是等价的
ports:
- port: 80
targetPort: 80
(targetPort与port的缩进要相同)
多端口版本
#nginx-service-multiple-ports.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-web1-svc-m
spec:
ports:
- port: 81
name: service-port
targetPort: 80
- port: 82
name: shutdown-port
targetPort: 80
selector:
app: nginx-web1
tier: frontend
nodePort svc版本
#nginx-service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-web1-svc-n
spec:
type: NodePort
ports:
- port: 83
targetPort: 80
nodePort: 31002
selector:
app: nginx-web1
tier: frontend
3)命令行例子
kubectl create -f nginx-deployment.yaml
kubectl create -f nginx-service.yaml
kubectl create -f nginx-service-multiple-ports.yaml
kubectl create -f nginx-service-nodeport.yaml
kubectl get svc -o wide
kubectl get pod -o wide
以下命令在master或者node上都可以执行
#Pod IP:port
curl 10.244.36.73:80
#单端口版本ClusterIP类型Svc,用 ClusterIP:port
curl 10.105.151.82:80
#多端口版本ClusterIP类型Svc,用 ClusterIP:port
curl 10.106.206.123:81
curl 10.106.206.123:82
#NodePort类型Svc,用 ClusterIP:port或者NodeIP:EXPOSE_PORT
curl 10.100.25.130:83
curl 10.1.13.26:31002
或者在浏览器里浏览:http://10.1.13.26:31002
可以查看监听端口
sudo netstat -tlp|grep 31002
查看svc详情
kubectl describe svc nginx-web1-svc-m
可以看出svc的ip:port与pod的endpoints之间的对应关系
8.Job(批处理任务)
1)基本概念
批处理任务通常并行(或者串行)启动多个计算进程去处理一批工作项(work item),在处理完成后,整个批处理任务结束。
Job是一种特殊的Pod副本自动控制器,同时Job控制Pod副本与RC等控制器的工作机制有以下重要差别。
(1)Job所控制的Pod副本是短暂运行的,可以将其视为一组Docker容器,其中的每个Docker容器都仅仅运行一次。当Job控制的所有Pod副本都运行结束时,对应的Job也就结束了。Kubernetes在1.5版本之后又提供了类似crontab的定时任务——CronJob,解决了某些批处理任务需要定时反复执行的问题。
(2)Job所控制的Pod副本的工作模式能够多实例并行计算,以TensorFlow框架为例,可以将一个机器学习的计算任务分布到10台机器上,在每台机器上都运行一个worker执行计算任务,这很适合通过Job生成10个Pod副本同时启动运算。
9.Volume(存储卷)
1)基本概念
Volume(存储卷)是Pod中能够被多个容器访问的共享目录。
Kubernetes中的Volume被定义在Pod上,然后被一个Pod里的多个容器挂载到具体的文件目录下
Kubernetes中的Volume与Pod的生命周期相同,但与容器的生命周期不相关,当容器终止或者重启时,Volume中的数据也不会丢失
Kubernetes支持多种类型的Volume,例如GlusterFS、Ceph等先进的分布式文件系统
通过以下命令可以查看系统支持得存储卷类型
kubectl explain pods.spec.volumes
临时存储:emptyDir
半持久化存储:hostPath
持久化存储:pvc,pv,nfs
分布式存储: glusterfs,rbd,cephfs,云存储(EBS,等)
各种类型的存储卷配置方法参见下面的例子
1) emptyDir
volumes:
- name: tmp
emptyDir: {}
2) hostPath
volumes:
- name: data
hostPath:
path: "/data"
2) nfs
volumes:
- name: nfs
nfs:
server: 10.1.1.1
path: "/data"
2)配置文件样例
#volume-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-volume
spec:
replicas: 1
selector:
matchLabels:
app: web-volume
template:
metadata:
labels:
app: web-volume
spec:
volumes:
- name: data
hostPath:
path: "/data"
- name: tmp
emptyDir: {}
containers:
- name: web-volume
image: nginx
volumeMounts:
- mountPath: /test/data
name: data
- mountPath: /test/tmp
name: tmp
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
3)命令行例子
kubectl explain pods.spec.volumes
kubectl create -f volume-deploy.yaml
kubectl get pod
kubectl exec -it web-volume-869968c6b-bqqvw -- bash
ls看看/test有没有两个子目录
9.Persistent Volume(持久化 存储卷)
1)基本概念
PV可以被理解成Kubernetes集群中的某个网络存储对应的一块存储,它与Volume类似,但有以下区别。
◎ PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。
◎ PV并不是被定义在Pod上的,而是独立于Pod之外定义的。
◎ PV目前支持的类型包括:gcePersistentDisk、AWSElasticBlockStore、AzureFile、AzureDisk、FC(Fibre Channel)、Flocker、NFS、iSCSI、RBD(Rados Block Device)、CephFS、Cinder、GlusterFS、VsphereVolume、Quobyte Volumes、VMware Photon、Portworx Volumes、ScaleIO Volumes和HostPath(仅供单机测试)。
◎ 需要定义一个PersistentVolumeClaim对象, PV 和 PVC 的 storageClassName 字段必须一样。
◎ 创建Pod是引用和mount
比较重要的是PV的accessModes属性,目前有以下类型。
◎ ReadWriteOnce:读写权限,并且只能被单个Node挂载。
◎ ReadOnlyMany:只读权限,允许被多个Node挂载。
◎ ReadWriteMany:读写权限,允许被多个Node挂载。
2)配置文件样例
#pv.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
name: pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/pv"
#pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
#pv-pod.yaml
kind: Pod
apiVersion: v1
metadata:
name: pv-pod
spec:
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: pv-claim
containers:
- name: pv-container
image: nginx
ports:
- containerPort: 80
name: "http-server"
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: pv-storage
#pv-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: pv-web
spec:
replicas: 1
selector:
matchLabels:
app: pv-web
template:
metadata:
labels:
app: pv-web
spec:
volumes:
- name: pv-storage
persistentVolumeClaim:
claimName: pv-claim
containers:
- name: pv-web
image: nginx
volumeMounts:
- mountPath: /data
name: pv-storage
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
3)命令行例子
kubectl create -f pv.yaml
kubectl create -f pvc.yaml
kubectl create -f pv-pod.yaml
kubectl get pv
kubectl get pvc
#对应的deploy挂载的卷
kubectl exec -it pv-pod bash
ls /usr/share/nginx/html
#对应的deploy挂载的卷
kubectl exec -it pv-web-66c4dc975c-q2mjp -- bash
ls /data
在node上/data/pv目录下touch文件,回到容器中看看是否有这个文件
10.Namespace
1)基本概念
Namespace(命名空间)是Kubernetes系统中的另一个非常重要的概念,Namespace在很多情况下用于实现多租户的资源隔离。Namespace通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
2)配置文件样例
#ns.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-ns1
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: web-ns1
template:
metadata:
labels:
app: web-ns1
spec:
containers:
- name: web-ns1
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
3)命令行例子
kubectl get ns
kubectl get pod -n dev
11.Annotation
Annotation(注解)与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。Annotation则是用户任意定义的附加信息,以便于外部工具查找。在很多时候,Kubernetes的模块自身会通过Annotation标记资源对象的一些特殊信息
12.ConfigMap
Docker通过将程序、依赖库、数据及配置文件“打包固化”到一个不变的镜像文件中的做法,解决了应用的部署的难题,但这同时带来了棘手的问题,即配置文件中的参数在运行期如何修改的问题。我们不可能在启动Docker容器后再修改容器里的配置文件,然后用新的配置文件重启容器里的用户主进程。为了解决这个问题,Docker提供了两种方式:
◎ 在运行时通过容器的环境变量来传递参数;
◎ 通过Docker Volume将容器外的配置文件映射到容器内。
这两种方式都有其优势和缺点,
◎ 在目标主机上先创建好对应的配置文件,然后才能映射到容器里。
◎ 在分布式情况下变得更为严重,写入(修改)多台服务器上的某个指定文件,并确保这些文件保持一致
Kubernetes使用ConfigMap资源对象:把所有的配置项都当作key-value字符串,比如password=123456、user=root,这些配置项可以作为Map表中的一个项,整个Map的数据可以被持久化存储在Kubernetes的Etcd数据库中,然后提供API以方便Kubernetes相关组件或客户应用CRUD操作这些数据。
2)配置文件样例
#cm-appvars.yaml
appvars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cm-appvars
data:
apploglevel: info
appdatadir: /var/data
#test-pod-envfrom-and-use-envvar.yaml
apiVersion: v1
kind: Pod
metadata:
name: cm-test-pod
spec:
containers:
- name: cm-test
image: nginx
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- configMapRef:
name: cm-appvars
env:
- name: APPLOGLEVEL
valueFrom:
configMapKeyRef:
name: cm-appvars
key: apploglevel
- name: APPDATADIR
valueFrom:
configMapKeyRef:
name: cm-appvars
key: appdatadir
restartPolicy: Never
3)命令行例子
kubectl create -f cm-appvars.yaml
kubectl create -f test-pod-envfrom-and-use-envvar.yaml
kubectl log cm-test-pod
就可以看到cm-appvars.yaml中定义的环境变量