k8s-yaml
一,yaml 文件
#基本语法:
1,大小写敏感
2.使用缩进表示层级关系。
3.缩进时不允许使用Tab键,只允许使用空格。(可以通过编译器将tab键设置成2个空格或者4个空格)
4.缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。(使用一个空格缩进,都使用一个。使用二个空格缩进,都使用二个),建议还是2个空格,这样好看些。
5.# 表示注释,从这个字符一直到行尾,都会被解析器忽略。
6.破折号后面跟一个空格(a dash and space)表示列表
7.用冒号和空格表示键值对 key: value
值的写法
1. k: v:字面直接来写;2. 字符串默认不用加上单引号或者双引 号;
"":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思 name: "idig8 \n com":输出;idig8换行com
'':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据 name: 'idig8\n com':输出;idig8\n com①对象、Map(属性和值)(键值对)
#yaml 格式
map,散列表 字典
使用冒号(:)表示键值对,同一缩进的所有键值对属于一个map
①对象、Map(属性和值)(键值对)
---是分隔符,是可选的,在单一文件中,可用连续三个连字号---区分多个文件
有两个键:friends,他们对应的值分别是:lastName: zhangsan 和 age: 20
---
friends
lastName: zhangsan
age: 20
对应json
{
"friends"::{
"lastname":"zhangshan",
"age":20
}
}
②数组(List、Set)
用- 值表示数组中的一个元素
pets:
- pig
- cat
- dog
{
"pets": ["pig","cat","dog"]
}
数组List和Map的混合
person:
lastname: liming
age: 34
boss: false
birth: 2019/08/03
maps: [k1: v1,k2: v2]
lists:
- lizi
- zhaoliu
dog:
name: 小狗
age: 10
json 格式
{
"person": {
"lastname": "liming",
"age": 34,
"boss": false,
"birth": '2019/08/03',
"maps": {
"k1": 'v1' ,
"k2": 12
} ,
"lists": [
"lishi",
"xholiu"
],
"dog": {
"name": '小狗',
"age": 10
}
}
}
缩进相同的都是同级元素,缩进比上一个元素长就是上一个元素的子元素
- apple1:
color: red
- apple2:
color: green
相同样式:
-
apple1:
color: red
-
apple2:
color: green
json:
[
{
"apple1": null,
"color": "red"
},
{
"apple2": null,
"olor": "green"
}
]
YAML 的纯量(Scalar)
null
~ / null / Null / NULL 还有空字符都被解析成 null 类型,最标准的写法是 ~。
bool
最新标准里 y / Y / yes / Yes / YES / n / N / no / No / NO / true / True / TRUE / false / False / FALSE / on / On / ON / off / Off / OFF 全部都会被解析成正确的 bool 类型,为了兼容性比较好建议用 true 和 false。
int
float
str
参考网站
http://blog.harrisonxi.com/2018/07/YAML%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95%E6%89%8B%E5%86%8C%E4%BB%A5%E5%8F%8A%E5%92%8CJSON%E7%9A%84%E5%AF%B9%E7%85%A7.html
k8s yaml 语法格式:
大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tal键,只允许使用空格
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
”#” 表示注释,从这个字符一直到行尾,都会被解析器忽略
1,k8syaml
#Map 指的是字典,即一个Key:Value 的键值对信息
---
apiVersion: v1
kind: Pod
--- 为可选的分隔符 ,当需要在一个文件中定义多个结构的时候需要使用
表示有两个键apiVersion和kind,分别对应的值为v1和Pod
##Maps的value既能够对应字符串也能够对应一个Maps:
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
metadata这个KEY对应的值为一个Maps,而嵌套的labels这个KEY的值又是一个Map
#List即列表 数组
args:
- beijing
- shanghai
- shenzhen
- guangzhou
# Lists的子项也可以是Maps,Maps的子项也可以是List:
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports: 8080
#创建Pod
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports:
- containerPort: 5000
定义了一个普通的Pod文件:
apiVersion:此处值是v1,这个版本号需要根据安装的Kubernetes版本和资源类型进行变化,记住不是写死的。
kind:此处创建的是Pod,根据实际情况,此处资源类型可以是Deployment、Job、Ingress、Service等。
metadata:包含Pod的一些meta信息,比如名称、namespace、标签等信息。
spe:包括一些container,storage,volume以及其他Kubernetes需要的参数,以及诸如是否在容器失败时重新启动容器的属性。可在特定Kubernetes API找到完整的Kubernetes Pod的属性
###########
#Deployment的概念 ,目的是让Kubernetes去管理一组Pod的副本,也就是副本集 ,这样就能够保证一定数量的副本一直可用,不会因为某一个Pod挂掉导致整个服务挂掉
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: kube100-site
spec:
replicas: 2
template:
metadata:
labels:
app: web
spec:
containers:
- name: front-end
image: nginx
ports:
- containerPort: 80
- name: flaskapp-demo
image: jcdemo/flaskapp
ports:
- containerPort: 5000
yaml格式的pod定义文件
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,Pod
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #必选,Pod所属的命名空间
labels: #自定义标签
- name: string #自定义标签名字
annotations: #自定义注释列表
- name: string
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
imagePullPolicy: [Always | Never | IfNotPresent] #获取镜像的策略 Alawys表示下载镜像 IfnotPresent表示优先使用本地镜像,否则下载镜像,Nerver表示仅使用本地镜像
command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] #容器的启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷配置
- name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean #是否为只读模式
ports: #需要暴露的端口库号列表
- name: string #端口号名称
containerPort: int #容器需要监听的端口号
hostPort: int #容器所在主机需要监听的端口号,默认与Container相同
protocol: string #端口协议,支持TCP和UDP,默认TCP
env: #容器运行前需设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量的值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests: #资源请求的设置
cpu: string #Cpu请求,容器启动的初始可用数量
memory: string #内存清楚,容器启动的初始可用数量
livenessProbe: #对Pod内个容器健康检查的设置,当探测无响应几次后将自动重启该容器,检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
exec: #对Pod容器内检查方式设置为exec方式
command: [string] #exec方式需要制定的命令或脚本
httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged:false
restartPolicy: [Always | Never | OnFailure] #Pod的重启策略,Always表示一旦不管以何种方式终止运行,kubelet都将重启,OnFailure表示只有Pod以非0退出码退出才重启,Nerver表示不再重启该Pod
nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
- name: string
hostNetwork:false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes: #在该pod上定义共享存储卷列表
- name: string #共享存储卷名称 (volumes类型有很多种)
emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,将被用于同期中mount的目录
secret: #类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
2,kubernetes中的对象
Kubernetes 对象 是持久化的条⽬。Kubernetes 使⽤这些条⽬去表示整个集群的状态。特别
地,它们描述了如下信息:
什么容器化应⽤在运⾏(以及在哪个 Node 上)
可以被应⽤使⽤的资源
关于应⽤如何表现的策略,⽐如重启策略、升级策略,以及容错策略
与 Kubernetes 对象⼯作 —— 是否创建、修改,或者删除 —— 需要使⽤ Kubernetes API。当使⼝时,⽐如,CLI 会使⽤必要的 Kubernetes API 调⽤,也可以在程序中直接使⽤ Kubernetes AP
对象 Spec 与状态
每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置:对象 spec 和 对象 status。spec 必须提供,它描述了对象的 期望状态—— 希望对象所具有的特征。status 描述了对象的 实际状态,它是由 Kubernetes 系统提供和更新。在任何时刻,Kubernetes 控制平⾯⼀直处于活跃状态,管理着对象的实际状态以与我们所期望的状态相匹
Kubernetes对象也被称为资源,就是Kubernetes集群中的各种可持久化实体,如Deployment,Pod,Service等。
从Kubernetes集群的角度,任何Kubernetes对象都有如下两个侧面:
spec,表示Kubernetes对象期望的状态,往往通过YAML配置文件给出
status,表示Kubernetes对象在当前集群中实际的状态,往往通过资源的Controller控制
事实上,Kubernetes集群的状态,就是通过大量Kubernetes对象的status表示出来的。
首先通过YAML文件定义Kubernetes对象的期望状态,然后调用Kubernetes API,将YAML文件作为参数交给Kubernetes集群,Kubernetes集群根据YAML文件创建必要的Kubernetes对象,以满足spec的期望。此后,Kubernetes的Master通过各种Controllers保证Kubernetes对象的status与spec一致。
1. Kubernetes Workload对象
1)Deployment
一个Deployment就是Kubernetes中运行着的一个应用,可能由一个或多个Pods组成,其中的每个Pod就是一个应用副本。
通过Deployment,一个应用可以被安装部署多次,每次部署都在Nodes上生成一定数量的应用实例。即一个应用可以有多个应用实例,每个应用实例有一个唯一的名字。
通过Deployment,可以实现应用的滚动升级,逐步替换容器镜像。
2)Pod
Pod是Kubernetes集群负责创建、管理的,可部署的、可水平扩展的、可创建副本的最小计算单元。
一个Pod可以被视为一个灵活调度的逻辑主机,拥有唯一的IP和hostname。但是其IP和hostname对Kubernetes集群外部是不可见的。
Pod的IP在集群内部是唯一的,所以集群中的Pod对任何其他Pod都是可见的,无论这些Pods在哪个Node上。登录到任意一个Node上,都可以ssh访问任何一个Pod(通过Pod的IP,无需Pod的端口)。
通常,一个Pod只包含一个容器实例。Pod的hostname就是Pod内部的容器实例的hostname。
个别情况下,一个Pod也可以包含若干个联系密切的容器实例。这些容器实例彼此不同,但是作为一个整体代表一个应用。Pod中的容器实例,共享上下文环境(如Linux namespaces, cgroups等)、网络(共用Pod的hostname,IP和对外开放的端口)、存储。因此,在Pod内部,容器之间彼此通过localhost即可互相访问。但是不同Pod的容器实例之间的访问,只能通过Pod的IP和端口,以及Pod的端口映射到容器实例的端口实现。容器实例之间共享存储volumes作为Pod的组成部分,可以被mount到应用的文件系统
通常,用户不应该直接创建Pod,而是通过Controllers创建Pod(有多个Controllers可以创建Pod),如通过创建Deployment创建Pod。
pod
当创建 Kubernetes 对象时,必须提供对象的 spec,用来描述该对象的期望状态,以及关于对象的一些基本信息(例如,名称)。当使用 Kubernetes API 创建对象时(或者直接创建,或者基于kubectl),API 请求必须在请求体中包含 JSON 格式的信息。更常用的是,需要在 .yaml 文件中为 kubectl 提供这些信息。 kubectl 在执行 API 请求时,将这些信息转换成 JSON 格式。查看已经部署好的pod的资源定义格式
pod 配置格式
当创建 Kubernetes 对象时,必须提供对象的 spec,用来描述该对象的期望状态,以及关于对象的一些基本信息(例如,名称)。当使用 Kubernetes API 创建对象时(或者直接创建,或者基于kubectl),API 请求必须在请求体中包含 JSON 格式的信息。更常用的是,需要在 .yaml 文件中为 kubectl 提供这些信息。 kubectl 在执行 API 请求时,将这些信息转换成 JSON 格式。查看已经部署好的pod的资源定义格式:
kubectl get pod myapp-848b5b879b-5f69p -o yaml
创建资源的方法:
apiserver在定义资源时,仅接收JSON格式的资源定义;
yaml格式提供配置清单,apiservere可自动将其转为json格式,而后再提交;
大部分资源的配置清单格式都由5个一级字段组成:
apiVersion: group/version 指明api资源属于哪个群组和版本,同一个组可以有多个版本
$ kubectl api-versions
kind: 资源类别,标记创建的资源类型,k8s主要支持以下资源类别
Pod,ReplicaSet,Deployment,StatefulSet,DaemonSet,Job,Cronjob
metadata:元数据,主要提供以下字段
name:同一类别下的name是唯一的
namespace:对应的对象属于哪个名称空间
labels:标签,每一个资源都可以有标签,标签是一种键值数据
annotations:资源注解
每个的资源引用方式(selflink):
/api/GROUP/VERSION/namespace/NAMESPACE/TYPE/NAME
spec: 定义目标资源的期望状态(disired state),资源对象中最重要的字段
status: 显示资源的当前状态(current state),本字段由kubernetes进行维护
kubectl explain pods
apiVersion,kind等定义的键值都是<string>,而metadata和spec看到是一个<Object>,当看到存在<Object>的提示,说明该字段可以存在多个二级字段,那么可以使用如下命令继续查看二级字段的定义方式
kubectl explain pods.metadata
kubectl explain pods.spec
二级字段下,每一种字段都有对应的键值类型,常用类型大致如下:
<[]string>:表示是一个字串列表,也就是字串类型的数组
<Object>:表示是可以嵌套的字段
<map[string]string>:表示是一个由键值组成映射
<[]Object>:表示是一个对象列表
<[]Object> -required-:required表示该字段是一个必选的字段
vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name:myapp
image: ikubernetes/myapp:v1
- name: busybox
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
kubectl create -f pod-demo.yaml
kubectl get pods
kubectl describe pods pod-demo #获取pod详细信息
kubectl logs pod-demo myapp
kubectl exec -it pod-demo -c myapp -- /bin/sh
kubectl explain pods.spec.containers
如果Pod不提供command或args使用Container,则使用Docker镜像中的cmd或者ENTRYPOINT。
如果Pod提供command但不提供args,则仅使用提供 command的。将忽略Docker镜像中定义EntryPoint和Cmd。
如果Pod中仅提供args,则args将作为参数提供给Docker镜像中EntryPoint。
如果提供了command和args,则Docker镜像中的ENTRYPOINT和CMD都将不会生效,Pod中的args将作为参数给command运行
- Service
Service是一个抽象访问层,能够将一组Pods以一定的Policy暴露出来响应服务请求,同时实现请求在多个Pods之间的负载均衡。通过YAML/JSON定义,将拥有指定Labe的Pods归入到一个Service。
因为Pod是短暂的、动态产生和消亡的,所以不能直接暴露出来。Service的CLUSTER-IP是虚拟的IP(Virtual IP, VIP),只要服务存在,该IP就不变。因而可以将Service的CLUSTER-IP暴露给请求。
Service的Port也是一个抽象端口,通过该端口的请求被映射到对应的Endpoint,转发给Pod中的容器实例进行处理。
Service的Endpoints通常是一组Pods的访问信息,包括Pod的IP及Pod中容器的端口,以<pod_IP>:
Service的4个类型(type):
ClusterIP(默认),服务只有一个集群内部可访问的CLUSTER-IP,只能供集群内部访问
NodePort,通过<Node_IP>:
LoadBalancer,通过外部云提供者的负载均衡器,将服务直接暴露给集群外部
ExternalName,一个集群外部的独立服务,通过--external-name属性被声明到Kubernetes集群中
Service的类型不同,有的Service只暴露给Kubernetes集群内部的其他应用;有的Service还能够暴露给Kubernetes集群外部的应用。
2.Kubernetes Metadata对象
-
Name
唯一标志 -
Namespace
为Name设置了一个有效的范围,仅适合有大量用户的Kubernetes集群。
Namespace用于区分用户,Name只需要在Namespace中唯一即可。
但是,Namespace不用于区分Kubernetes集群中的资源,通过Label区分Kubernetes集群中的资源。
Kubernetes集群中初始默认的3个Namespace:
default,未指明Namespace的任何用户创建的Kubernetes对象
kube-system,Kubernetes系统创建的对象
kube-public,所有用户(包括未授权用户)都可读的Kubernetes对象
此外,不属于任何Namespace的Kubernetes对象:
Namespace本身不属于任何Namespace
Node
persistentVolume
3) UID
唯一标志
- Label
Label是一组键值对,附加到Kubernetes对象上的标志性信息(identifying information)。
相对于用户,Label用以区分Kubernetes资源(通过贴标签),用于后续的分组和排序。相对于Kubernetes系统,Label没有任何意义。
key=value
key:只能使用 字母 数字 _ - . (只能以字母数字开头,不能超过63给字符)
value: 可以为空 只能使用 字母 数字开头
kubectl get pods --show-labels #查看pod标签
kubectl get pods -l app #过滤包含app的标签
kubectl get pods -L app
kubectl label pods pod-demo release=canary #给pod-demo打上标签
kubectl get pods -l app --show-labels
kubectl label pods pod-demo release=stable --overwrite #修改标签
kubectl get pods -l release
kubectl get pods -l release,app
标签选择器
等值关系标签选择器:=, == , != (kubectl get pods -l app=test,app=dev)
集合关系标签选择器: KEY in (v1,v2,v3), KEY notin (v1,v2,v3) !KEY (kubectl get pods -l "app in (test,dev)")
许多资源支持内嵌字段
matchLabels: 直接给定建值
matchExpressions: 基于给定的表达式来定义使用标签选择器,{key:"KEY",operator:"OPERATOR",values:[V1,V2,....]}
操作符: in notin:Values字段的值必须是非空列表 Exists NotExists: Values字段的值必须是空列表
节点选择器
kubectl explain pod.spec
nodeSelector可以限定pod创建在哪个节点上,举个例子,给节点k8s-node01打上标签disktype=ssd,让pod-demo指定创建在k8s-node01上
(1)给k8s-node01节点打标签
[root@k8s-master mainfests]# kubectl label nodes k8s-node01 disktype=ssd
(2)修改yaml文件,增加标签选择器
[root@k8s-master mainfests]# cat pod-demo.yaml
(3)重新创建pod-demo,可以看到固定调度在k8s-node01节点上
[root@k8s-master mainfests]# kubectl delete -f pod-demo.yaml
- Annotation
Annotation是一组键值对,附加到Kubernetes对象上的非标志性信息(Non-identifying information)。
Annotation相对于用户,用以补充说明Kubernetes资源(通过贴标签)。Annotation相对于Kubernetes系统,没有任何意义
3.Kubernetes Storage对象
- Volume
Pod级别的存储
- PersistentVolume/PersistentVolumeClaim
集群级别的存储
1.kubernetes中的资源对象
资源对象名称 缩写
componentstatuses cs
daemonsets ds
deployments
events ev
endpoints ep
horizontalpodautoscalers hpa
ingress ing
jobs
limitranges limits
nodes no
namespaces ns
pods po
persistentvolumes pv
persistentvolumeclaims pvc
resourcequotas quota
secrets
serviceaccounts
services svc
POD
Kubernetes为每个Pod都分配了唯一的IP地址,称之为Pod IP,一个Pod里的多个容器共享Pod IP地址。Kubernetes要求底层网络支持集群内任意两个Pod之间的TCP/IP直接通信,这通常采用虚拟二层网络技术来实现,例如Flannel、Open vSwitch等。因此,在Kubernetes里,一个Pod里的容器与另外主机上的Pod容器能够直接通信。
Pod有两种类型:普通的Pod和静态Pod(Static Pod),静态Pod不存放在etcd存储里,而是存放在某个具体的Node上的一个具体文件中,并且只在此Node上启动运行。普通的Pod一旦被创建,就会被存储到etcd中,随后会被Kubernetes Master调度到某个具体的Node上并进行绑定(Binding),该Node上的kubelet进程会将其实例化成一组相关的Docker容器并启动起来。当Pod里的某个容器停止时,Kubernetes会自动检测到这个问题并且重新启动这个Pod(重启Pod里的所有容器);如果Pod所在的Node宕机,则会将这个Node上的所有Pod重新调度到其他节点上运行
pod 定义文件
apiVersion: v1
kind: Pod
metadata:
name: myweb #pod名称
labels: #资源对象的标签
name: myweb
spec: #pod包含的容器组定义
containers:
- name: myweb #名称为mywwb,镜像为kubeguide/tomcat-app: v1的容器
image: kubeguide/tomcat-app: v1
ports: #容器的8080端口启动容器进程 pod ip+ 端口组成了Endpoint,一个pod里可以存在多个
- containerPort: 8080
env: #注入的环境变量
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
Pod Volume,Pod Volume有一些扩展,比如可以用分布式文件系统GlusterFS等实现后端存储功能;Pod Volume是定义在Pod之上,然后被各个容器挂载到自己的文件系统中的
Event概念,Event是一个事件的记录,记录了事件的最早产生时间、最后重现时间、重复次数、发起者、类型,以及导致此事件的原因等众多信息。Event通常会关联到某个具体的资源对象上,是排查故障的重要参考信息,当我们发现某个Pod迟迟无法创建时,可以用kubectl describe pod xxx来查看它的描述信息,用来定位问题的原因
每个Pod都可以对其能使用的服务器上的计算资源设置限额,当前可以设置限额的计算资源有CPU和Memory两种,其中CPU的资源单位为CPU(Core)的数量,是一个绝对值。
对于容器来说一个CPU的配额已经是相当大的资源配额了,所以在Kubernetes里,通常以千分之一的CPU配额为最小单位,用m来表示。通常一个容器的CPU配额被定义为100-300m,即占用0.1-0.3个CPU。与CPU配额类似,Memory配额也是一个绝对值,它的单位是内存字节数。
对计算资源进行配额限定需要设定以下两个参数:
Requests:该资源的最小申请量,系统必须满足要求。
Limits:该资源最大允许使用的量,不能超过这个使用限制,当容器试图使用超过这个量的资源时,可能会被Kubernetes Kill并重启。
通常我们应该把Requests设置为一个比较小的数值,满足容器平时的工作负载情况下的资源需求,而把Limits设置为峰值负载情况下资源占用的最大量
spec:
containers:
- name: db
image: mysql
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
#最小0.25个CPU及64MB内存,最大0.5个CPU及128MB内存
标签 label
某个资源对象定义一个Label,就相当于给它打了一个标签,随后可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象,Kubernetes通过这种方式实现了类似SQL的简单又通用的对象查询机制
Label Selector的表达式有两种:基于等式的(Equality-based)和基于集合的(Set-based)。下面是基于等式的匹配例子。
name=redis-slave:匹配所有标签为name=redis-slave的资源对象。
env != production:匹配所有标签env不等于production的资源对象。
name in (redis-master, redis-slave):匹配所有标签为name=redis-master或者name=redis-slave的资源对象。
name not in (php-frontend):匹配所有标签name不等于php-frontend的资源对象。
还可以通过多个Label Selector表达式的组合实现复杂的条件选择,多个表达式之间用“,”进行分隔即可,几个条件之间是“AND”的关系,即同时满足多个条件
name=redis-slave, env!=production
name not in (php-frontend), env!=production
apiVersion: v1
kind: Pod
metadata:
name: myweb
labels:
app: myweb
role: backend
env: testing
#RC和Service在spec中定义Selector与Pod进行关联:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 1
selector:
app: myweb
template:
#Deployment、ReplicaSet、DaemonSet和Job则可以在Selector中使用基于集合的筛选条件:
selector:
matchLabels:
app: myweb
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
- {key: environment, operator: NotIn, values: [dev]}
matchLabels用于定义一组Label,与直接写在Selector中作用相同;matchExpressions用于定义一组基于集合的筛选条件,可用的条件运算符包括:In、NotIn、Exists和DoesNotExist。
如果同时设置了matchLabels和matchExpressions,则两组条件为“AND”关系,即所有条件需要同时满足才能完成Selector的筛选。
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“定向调度”的特性。
Replication Controller
RC的作用是声明Pod的副本数量在任意时刻都符合某个预期值,所以RC的定义包括如下几个部分。
Pod期待的副本数量(replicas)。
用于筛选目标Pod的Label Selector。
当Pod的副本数量小于预期数量时,用于创建新Pod的Pod模板(template)。
下面是一个完整的RC定义的例子,即确保拥有tier=frontend标签的这个Pod(运行Tomcat容器)在整个Kubernetes集群中始终有三个副本:
apiVersion: v1
kind: ReplicationController
metadata:
name: frontend
spec:
replicas: 3
selector:
tier: frontend
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
containers:
- name: tomcat-demo
image: tomcat
imagePullPolicy: IfNotPresent
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 80
当我们定义了一个RC并提交到Kubernetes集群中后,Master节点上的Controller Manager组件就得到通知,定期巡检系统中当前存活的目标Pod,并确保目标Pod实例的数量刚好等于此RC的期望值。如果有过多的Pod副本在运行,系统就会停掉多余的Pod;如果运行的Pod副本少于期望值,即如果某个Pod挂掉,系统就会自动创建新的Pod以保证数量等于期望值。
通过RC,Kubernetes实现了用户应用集群的高可用性,并且大大减少了运维人员在传统IT环境中需要完成的许多手工运维工作(如主机监控脚本、应用监控脚本、故障恢复脚本等)
通过修改RC的副本数量,可以实现Pod的动态缩放(Scaling)功能。
kubectl scale rc redis-slave --replicas=3
Replica Set
由于Replication Controller与Kubernetes代码中的模块Replication Controller同名,同时这个词也无法准确表达它的意思,所以从Kubernetes v1.2开始,它就升级成了另外一个新的对象——Replica Set,官方解释为“下一代的RC”。它与RC当前存在的唯一区别是:Replica Set支持基于集合的Label selector(Set-based selector),而RC只支持基于等式的Label selector(equality-based selector),所以Replica Set的功能更强大。
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: frontend
spec:
selector:
matchLabels:
tier: frontend
matchExpressions:
- {key: tier, operator: In, values: [frontend]}
template:
Replica Set很少单独使用,它主要被Deployment这个更高层的资源对象所使用,从而形成一整套Pod创建、删除、更新的编排机制。
RC和RS的特性与作用如下:
在大多情况下,我们通过定义一个RC实现Pod的创建过程及副本数量的自动控制。
RC里包括完整的Pod定义模板。
RC通过Label Selector机制实现对Pod副本的自动控制。
通过改变RC里的Pod副本数量,可以实现Pod的扩容或缩容功能。
通过改变RC里Pod模板中的镜像版本,可以实现Pod的滚动升级功能
Deployment
Deployment相对于RC的最大区别是我们可以随时知道当前Pod“部署”的进度。一个Pod的创建、调度、绑定节点及在目标Node上启动对应的容器这一完整过程需要一定的时间,所以我们期待系统启动N个Pod副本的目标状态,实际上是一个连续变化的“部署过程”导致的最终状态。
Deployment的典型使用场景有以下几个:
创建一个Deployment对象来生成对应的Replica Set并完成Pod副本的创建过程。
检查Deployment的状态来看部署动作是否完成(Pod副本的数量是否达到预期的值)。
更新Deployment以创建新的Pod(比如镜像升级)。
如果当前Deployment不稳定,则回滚到一个早先的Deployment版本。
暂停Deployment以便于一次性修改多个Pod Template Spec的配置项,之后再恢复Deployment,进行新的发布。
扩展Deployment以应对高负载。
查看Deployment的状态,以此作为发布是否成功的指标。
清理不再需要的旧版本ReplicaSet。
Deployment的定义与Replica Set的定义类似,只是API声明与Kind类型不同。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
--------------------------------
apiVersion: v1
kind: ReplicaSet
metadata:
name: nginx-repset
Deployment定义的一个完整例子
apiVersion: extensions/v1beta1
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
通过命令kubectl get deployment来查看Deployment的信息
ESIRED::Pod副本数量的期望值,即Deployment里定义的Replica。
CURRENT:当前Replica的值,如果小于DESIRED的期望值,会创建新的Pod,直到达成DESIRED为止。
UP-TO-DATE:最新版本的Pod的副本数量,用于指示在滚动升级的过程中,有多少个Pod副本已经成功升级。
AVAILABLE:当前集群中可用的Pod副本数量,即集群中当前存活的Pod数量。
Pod的管理对象,除了RC、ReplicaSet、Deployment,还有DaemonSet、StatefulSet、Job等,分别用于不同的应用场景
Horizontal Pod Autoscaler
HPA与RC、Deployment一样,也属于Kubernetes资源对象。通过追踪分析RC或RS控制的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数。
HPA有以下两种方式作为Pod负载的度量指标:
CPUUtilizationPercentage
应用程序自定义的度量指标,比如服务在每秒内的相应的请求数(TPS或QPS)。
CPUUtilizationPercentage是一个算术平均值,即目标Pod所有副本自带的CPU利用率的平均值。一个Pod自身的CPU利用率是该Pod当前CPU的使用量除以它的Pod Request的值,比如我们定义一个Pod的Pod Request为0.4,而当前Pod的CPU使用量为0.2,则它的CPU使用率为50%,这样我们就可以算出来一个RC或RS控制的所有Pod副本的CPU利用率的算术平均值了。如果某一时刻CPUUtilizationPercentage的值超过80%,则意味着当前的Pod副本数很可能不足以支撑接下来更多的请求,需要进行动态扩容,而当请求高峰时段过去后,Pod的CPU利用率又会降下来,此时对应的Pod副本数应该自动减少到一个合理的水平
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
namespace: default
spec:
maxReplicas: 10
minReplicas: 2
scaleTargetRef:
kind: Deployment
name: php-apache
targetCPUUtilizationPercentage: 90
通过HPA控制php-apache的Pod副本,当Pod副本的CPUUtilizationPercentage的值超过90%时,会进行自动扩容增加Pod副本的数量,扩容或缩容时Pod的副本数量要介于2-10之间。
除了通过yaml文件来定义HPA对象之外,还可以通过命令的方式创建:
kubectl autoscale deployment php-apache --cpu-percent=90 --min=1 --max=10
StatefulSet
Pod的管理对象RC、Deployment、DaemonSet和Job都是面向无状态的服务,但实际中有很多服务是有状态的,比如Mysql集群、MongoDB集群、ZooKeeper集群等,可以使用StatefulSet来管理有状态的服务。
StatefulSet有如下一些特性:
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).$(headless service name)
比如一个3节点的kafka的StatefulSet集群,对应的Headless Service的名字为kafka,StatefulSet的名字为kafka,则StatefulSet里面的3个Pod的DNS名称分别为kafka-0.kafka、kafka-1.kafka、kafka-3.kafka,这些DNS名称可以直接在集群的配置文件中固定下来。
Service(服务)
Service其实就是我们经常提起的微服务架构中的一个“微服务”,Pod、RC等资源对象其实都是为它作“嫁衣”的。Pod、RC或RS与Service的逻辑关系如下图所示
Kubernetes的Service定义了一个服务的访问入口地址,前端的应用(Pod)通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,Service与其后端Pod副本集群之间则是通过Label Selector来实现“无缝对接”的。而RC的作用实际上是保证Service的服务能力和服务质量始终处于预期的标准。
通过分析、识别并建模系统中的所有服务为微服务——Kubernetes Service,最终我们的系统由多个提供不同业务能力而又彼此独立的微服务单元所组成,服务之间通过TCP/IP进行通信,从而形成了强大而又灵活的弹性集群,拥有了强大的分布式能力、弹性扩展能力、容错能力。因此,我们的系统架构也变得简单和直观许多。
既然每个Pod都会被分配一个单独的IP地址,而且每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,多个Pod副本组成了一个集群来提供服务,那么客户端如何来访问它们呢?一般的做法是部署一个负载均衡器(软件或硬件),但这样无疑增加了运维的工作量。在Kubernetes集群里使用了Service(服务),它提供了一个虚拟的IP地址(Cluster IP)和端口号,Kubernetes集群里的任何服务都可以通过Cluster IP+端口的方式来访问此服务,至于访问请求最后会被转发到哪个Pod,则由运行在每个Node上的kube-proxy负责。kube-proxy进程其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并在内部实现服务的负载均衡与会话保持机制
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
selector:
tier: frontend
#定义了一个名为“tomcat-service”的Service,它的服务端口为8080,拥有“tier=frontend”这个Label的所有Pod实例
很多服务都存在多个端口的问题,通常一个端口提供业务服务,另外一个端口提供管理服务,比如Mycat、Codis等常见中间件。Kubernetes Service支持多个Endpoint,要求每个Endpoint定义一个名字来区分,下面是tomcat多端口的Service定义样例
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
ports:
- port: 8080
name: service-port
- port: 8005
name: shutdown-port
selector:
tier: frontend
Kubernetes的服务发现机制
ubernetes通过Add-On增值包的方式引入了DNS系统,把服务名作为DNS域名,这样程序就可以直接使用服务名来建立连接
.外部系统访问Service的问题
Node IP:Node节点的IP地址,即物理网卡的IP地址。
Pod IP:Pod的IP地址,即docker容器的IP地址,此为虚拟IP地址。
Cluster IP:Service的IP地址,此为虚拟IP地址。
外部访问Kubernetes集群里的某个节点或者服务时,必须要通过Node IP进行通信。
Pod IP是Docker Engine根据docker0网桥的IP地址段进行分配的一个虚拟二层网络IP地址,Pod与Pod之间的访问就是通过这个虚拟二层网络进行通信的,而真实的TCP/IP流量则是通过Node IP所在的物理网卡流出的。
Service的Cluster IP具有以下特点:
Cluster IP仅仅作用于Service这个对象,并由Kubernetes管理和分配IP地址。
Cluster IP是一个虚拟地址,无法被ping。
Cluster IP只能结合Service Port组成一个具体的通信端口,供Kubernetes集群内部访问,单独的Cluster IP不具备TCP/IP通信的基础,并且外部如果要访问这个通信端口,需要做一些额外的工作。
Node IP、Pod IP和Cluster IP之间的通信,采用的是Kubernetes自己设计的一种特殊的路由规则,与我们熟悉的IP路由有很大的区别。
我们的应用如果想让外部访问,最常用的作法是使用NodePort方式
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31002
selector:
tier: frontend
NodePort的实现方式是在Kubernetes集群里的每个Node上为需要外部访问的Service开启一个对应的TCP监听端口,外部系统只要用任意一个Node的IP地址+具体的NodePort端口号即可访问此服务。
NodePort还没有完全解决外部访问Service的所有问题,比如负载均衡问题,常用的做法是在Kubernetes集群之外部署一个负载均衡器
Load balancer组件独立于Kubernetes集群之外,可以是一个硬件负载均衡器,也可以是软件方式实现,例如HAProxy或者Nginx。这种方式,无疑是增加了运维的工作量及出错的概率。
于是Kubernetes提供了自动化的解决方案,如果我们使用谷歌的GCE公有云,那么只需要将type: NodePort改成type: LoadBalancer,此时Kubernetes会自动创建一个对应的Load balancer实例并返回它的IP地址供外部客户端使用
Volume(存储卷
Volume是Pod中能够被多个容器访问的共享目录。Volume定义在Pod上,被一个Pod里的多个容器挂载到具体的文件目录下,当容器终止或者重启时,Volume中的数据也不会丢失。Kubernetes支持多种类型的Volume,例如GlusterFS、Ceph等分布式文件系统。
除了可以让一个Pod里的多个容器共享文件、让容器的数据写到宿主机的磁盘上或者写文件到网络存储中,Kubernetes还提供了容器配置文件集中化定义与管理,通过ConfigMap对象来实现。
Kubernetes支持多种Volume类型,下面我们一一进行介绍。
1.emptyDir
emptyDir是在Pod分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,它是Kubernetes自动分配的一个目录,当Pod从Node上移除时,emptyDir中的数据也会被永久删除。
emptyDir的用途如下:
临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保存。
长时间任务的中间过程CheckPoint的临时保存目录。
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)。
emptyDir的定义如下:
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
volumes:
- name: datavol
emptyDir: {}
containers:
- name: tomcat-demo
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
imagePullPolicy: IfNotPresent
2.hostPath
使用hostPath挂载宿主机上的文件或目录,主要用于以下几个方面:
容器应用程序生成的日志文件需要永久保存时,可以使用宿主机的文件系统存储。
需要访问宿主机上Docker引擎内部数据时,可以定义hostPath的宿主机目录为docker的数据存储目录,使容器内部应用可以直接访问docker的数据文件。
使用hostPath时,需要注意以下几点:
在不同的Node上的Pod挂载的是本地宿主机的目录,如果要想让不同的Node挂载相同的目录,则可以使用网络存储或分布式文件存储。
如果使用了资源配额管理,则Kubernetes无法将其在宿主机上使用的资源纳入管理。
hostPath的定义如下:
volumes:
- name: "persistent-storage"
hostPath:
path: "/data"
3.gcePersistentDisk
使用这种类型的Volume表示使用谷歌公有云提供的永久磁盘(Persistent Disk,PD)存放数据,使用gcePersistentDisk有以下一些限制条件:
Node需要是谷歌GCE云主机。
这些云主机需要与PD存在于相同的GCE项目和Zone中。
通过gcloud命令创建一个PD:
gcloud compute disks create --size=500GB --zone=us-centrall-a my-data-disk
定义gcePersistentDisk类型的Volume的示例如下:
volumes:
- name: test-volume
gcPersistentDisk:
pdName: my-data-disk
fsType: ext4
5.NFS
使用NFS网络文件系统提供的共享目录存储数据时,我们需要在系统中部署一个NFS Server。
定义NFS类型的Volume的示例如下:
volumes:
- name: nfs-volume
nfs:
server: nfs-server.localhost
path: "/"
6.其他类型的Volume
iscsi:使用iSCSI存储设备上的目录挂载到Pod中。
flocker:使用Flocker来管理存储卷。
glusterfs:使用GlusterFS网络文件系统的目录挂载到Pod中。
rbd:使用Ceph块设备共享存储(Rados Block Device)挂载到Pod中。
gitRepo:通过挂载一个空目录,并从GIT库clone一个git repository以供Pod使用。
secret:一个secret volume用于为Pod提供加密的信息,可以将定义在Kubernetes中的secret直接挂载为文件让Pod访问。Secret volume是通过tmfs(内存文件系统)实现的,所以这种类型的volume不会持久化。
Persistent Volume
上面提到的Volume是定义在Pod上的,属于“计算资源”的一部分,而实际上,“网络存储”是相对独立于“计算资源”而存在的一种实体资源。比如在使用云主机的情况下,我们通常会先创建一个网络存储,然后从中划出一个“网盘”并挂载到云主机上。Persistent Volume(简称PV)和与之相关联的Persistent Volume Claim(简称PVC)实现了类似的功能。
PV与Volume的区别如下:
PV只能是网络存储,不属于任何Node,但可以在每个Node上访问。
PV并不是定义在Pod上的,而是独立于Pod之外定义。
下面是NFS类型PV的yaml定义内容,声明了需要5G的存储空间:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somepath
server: 172.17.0.2
PV的accessModes属性有以下类型:
ReadWriteOnce:读写权限、并且只能被单个Node挂载。
ReadOnlyMany:只读权限、允许被多个Node挂载。
ReadWriteMany:读写权限、允许被多个Node挂载。
如果Pod想申请使用PV资源,则首先需要定义一个PersistentVolumeClaim(PVC)对象
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
然后在Pod的volume定义中引用上述PVC即可
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
PV是有状态的对象,它有以下几种状态:
Available:空闲状态。
Bound:已经绑定到某个PVC上。
Released:对应的PVC已经删除,但资源还没有被集群收回。
Failed:PV自动回收失败。
Namespace(命名空间)
通过将Kubernetes集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组,便于不同的分组在共享使用整个集群的资源的同时还能被分别管理。
Kubernetes集群在启动后,会创建一个名为“default”的Namespace,通过kubectl可以查看到:
kubectl get namespaces
如果不特别指明Namespace,则用户创建的Pod、RC、RS、Service都奖被系统创建到这个默认的名为default的Namespace中。
下面是Namespace的定义示例:
apiVersion: v1
kind: Namespace
metadata:
name: development
定义一个Pod,并指定它属于哪个Namespace:
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: development
spec:
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
使用kubectl get命令查看Pod状态信息时,需要加上--namespace参数,指定查看哪个namespace下的资源对象,不加这个参数则默认查看 default下的资源对象。
kubectl get pods --namespace=development
当我们给每个租户创建一个Namespace来实现多租户的资源隔离时,还能结合Kubernetes的资源配额管理,限定不同租户能占用的资源,例如CPU使用量、内存使用量等
Annotation
Annotation与Label类似,也使用key/value键值对的形式进行定义。不同的是Label具有严格的命名规则,它定义的是Kubernetes对象的元数据(Metadata),并且用于Label Selector。而Annotation则是用户任意定义的“附加”信息,以便于外部工具进行查找。通常Kubernetes的模块会通过Annotation的方式标记资源对象的一些特殊信息。
使用Annotation来记录的信息如下:
build信息、release信息、Docker镜像信息等,例如时间戳、release id号、PR号、镜像bash值、docker registry地址等。
日志库、监控库、分析库等资源库的地址信息。
程序调试工具信息,例如工具名称、版本号等。
团队的联系信息,例如电话号码、负责人名称、网址等
docker info 查看默认运行时 runtime
Kubernetes 集群状态异常排错
https://zhuanlan.zhihu.com/p/34722886