kubernetes入门
简介
-
Docker发布Docker Swarm,架构优秀,但是功能简单,不能完全符合市场需求
-
Google发布Go语言开发的开源Kubernetes项目,参考Borg系统开发,技术成熟,功能强大,使用简单
-
Kubernetes简称k8s,架构如下图所示:
组件介绍
-
API-Server:整个集群的核心,基本上所有的组件都需要和API-Server进行交互,也是外界访问Kubernetes的唯一入口
-
Etcd:键值对分布式数据库,集群中所有的重要信息都存储在其中
-
Schedular:调度器,负责调度Pod到那个节点上运行
-
Controller:控制器,主要是对Pod进行控制
-
Kubelet:从节点上负责与主节点上的API-Server进行交互的组件,同时负责与本节点的容器进行通信
-
Kube-Proxy:用来转发service中定义的服务,实现外部可以访问集群内的服务
-
Flanner:一种覆盖网络,实现集群内Pod之间的通信
-
DNS:域名解析服务(高版本默认安装)
-
Dashboard:基于网页的Kubernetes用户界面,用于实时查看集群的内部状态
-
Helm:服务安装工具,类似Centos上的yum,python中的pip,方便高效
-
EFK:日志查看工具
控制器
-
Replication Controller:用于控制副本的个数,是的副本个数始终等于期望的个数。
-
Replication Set:是Replication Controller的升级版本,包含Replication Controller的全部功能,新增基于Label selector的集合操作。
-
Deployment:新增组件,通过控制Replication Set的方式控制副本数,主要是新增升级和回滚功能。升级的大概流程:创建一个新的RS,控制老的RS杀死一个副本,控制新的RS创建一个副本,依次循环,直到所有的老副本都变成新副本,然后杀死老的RS。回滚也是相同的流程,可以把回滚理解成升级的目标版本是老版本。
-
StatefulSet:主要是处理有状态服务,目前是Kubernetes的短板。无状态:如流水线上的工人可以随时离开一段时间,回来后可以继续无缝连接工作;有状态:部门开发经理离开一段之后,再回来工作可能就不能跟上节奏了,因为项目的状态在实时更新,对比服务,最明显的例子就是数据库服务。
-
Daemon Set:保证每个节点上都运行一个Pod,主要适用于日志采集和性能监控服务
-
Job:保证任务执行一次且成功结束,相比于手动执行任务,Job的优势是如果任务异常结束,会被重新执行,直到成功结束
-
Corn Job:周期性定时运行任务
Flanner网络
Flannel实质上是一种“覆盖网络(overlay network)”,在现有的网络上实现另外一层网络,主要是将需要发送的报文通过UDP的方式封装,然后通过真实的网卡传输,在目的端解封得到传输的报文,最后根据传输的报文将报文发送给指定IP的Pod。需要注意的是:这里传输的数据是TCP/TCMP/UDP等格式的报文。宏观上理解是:在应用层上定义一种私有协议,实现多主机上的点对点传输,只不过这种私有协议是TCP/TCMP/UDP等。
处理流程:数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡(桥接方式),这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。Flannel通过Etcd服务维护了一张节点间的路由表,详细记录了各节点子网网段。源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由到达目标容器。flannel0虚拟网卡的作用就是UDP封装和解封,在容器层面感知不到,就像直接Socket通信一样。控制容器的的IP地址,是通过设置容器的启动参数--bip实现。
Pod
-
Pod是Kubernetes中能够创建和部署的最小单元,一个Pod中可能包含一个或多个容器,属于同一个Pod的所有容器共享一个IP,且数据卷也是共享,彼此之间通过localhost就可以通信。Pod可以分为两种:自主式Pod和控制器管理的Pod。
-
所有的Pod在启动时会默认创建一个pasue的容器,然后里面的其他容器共享pause的网络协议栈,达到IP相同的目的
-
Kubernetes为什么不将容器当做调度的最小单元?
-
Pod需要和ApiServer进行通信,包括交互探测到的容器健康状态,如果直接将具体的容器当做调度单元管理,那么需要针对具体的容器类型进行开发,非常麻烦。启用pod之后,直接在Pod里面进行差异化开发,对ApiServer统一接口。基本思想就是增加一层,解决差异化。
-
大多数时候,一个业务的建立需要多个容器联合完成,将彼此管理的多个容器一起调度更加合适
-
-
Pod的状态
-
Pending:Pod已被Kubernetes系统接受,但尚未创建一个或多个Container
-
Running:Pod已绑定到节点,并且已创建所有Container
-
Succeeded:Pod中的所有容器都已成功终止,并且不会重新启动
-
Failed:Pod中的所有容器都已终止,并且至少有一个Container已终止失败
-
Unknown:由于某种原因,无法获得Pod的状态
资源
-
空间名称级别
-
工作负载型:Pod,RC,RS,Deployment,StatefulSet,DaemonSet,Job,CronJob
-
服务发现型:Service,Ingress
-
存储型:Volume,CSI(容器存储统一接口,标准)
-
特殊型存储卷:ConfigMap(当配置中心使用的资源类型),Secret(保存敏感数据),DownwardAPI(外部信息输入给容器)
-
-
集群级别
-
Namespace,Node,Role,ClusterRole,RoleBinding,ClusterRoleBinding
-
-
元数据型
-
HPA,PodTemplate,LimitRange
-
理解成有一定指标的东西
-
- Kubernetes中所有的内容都可以抽象为资源,资源实例化之后(理解为被执行),叫做对象
yaml格式
YAML语言的设计目标是方便人类读写。它实质上是一种通用的数据串行化格式。它的基本语法规则如下:
-
大小写敏感
-
使用缩进表示层级关系
-
缩进时不允许使用Tab键,只允许使用空格
-
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
-
#
表示注释,从这个字符一直到行尾,都会被解析器忽略
YAML支持的数据结构有三种:
-
对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
-
数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
-
纯量(scalars):单个的、不可再分的值
对象:对象的一组键值对,使用冒号结构表示
animal: pets hash: { name: Steve, foo: bar }
数组:一组连词线开头的行,构成一个数组
animal: - Cat - Dog - Goldfish animal: [Cat, Dog]
纯量:纯量是最基本的、不可再分的值。以下数据类型都属于 JavaScript 的纯量:
-
字符串
-
布尔值
-
整数
-
浮点数
-
Null
-
时间
-
日期
number: 12.30 isSet: true iso8601: 2001-12-14t21:59:43.10-05:00 date: 1976-07-31
字符串:字符串是最常见,也是最复杂的一种数据类型。
-
字符串默认不使用引号表示
-
如果字符串之中包含空格或特殊字符,需要放在引号之中
-
单引号和双引号都可以使用,双引号不会对特殊字符转义
-
单引号之中如果还有单引号,必须连续使用两个单引号转义
-
字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格
-
多行字符串可以使用
|
保留换行符,也可以使用>
折叠换行 -
+
表示保留文字块末尾的换行,-
表示删除字符串末尾的换行
str: 这是一行字符串 str: '内容: 字符串' str: 'labor''s day' this: | Foo Bar that: > Foo Bar #{ this: 'Foo\nBar\n', that: 'Foo Bar\n' } str: 这是一段 多行 字符串 #{ str: '这是一段 多行 字符串' } s1: | Foo s2: |+ Foo s3: |- Foo #{ s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo' }
yaml常用字段
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 # 一个简单的Pod定义 apiVersion: v1 kind: Pod metadata: name: my-pod labels: name: my-pod version: v1 spec: containers: - name: one image: nginx ports: - containerPort: 80 - name: two image: jcdemo/flaskapp ports: - containerPort: 5000
Pod生命周期
-
启动pause容器,每个Pod默认具有,主要是初始化网络和数据卷
-
pause串行启动初始化容器(initC),只到所有的初始化容器都成功执行
-
pause并行启动主容器(mainC),启动开始钩子(post start),启动探针(liveness probe,readness probe)
-
在主容器结束之前,调用结束钩子(pre stop)
为什么分离出初始化容器,而不将所有的功能都封装在主容器中?
-
容器功能越单一越好,越适合调度
-
权限管理,更加安全。容器只具备需要访问的安全,其他都不应该具有
-
执行共性操作,如果一个Pod里面包含多个容器,都需要使用同一份代码,那么可以在初始容器里面将代码下载好,然后主容器共用
探针和钩子
-
livenessProbe:探测容器是否活着
-
httpGet:http请求方式,如果返回的HTTP状态码在200和399之间,则认为程序正常,否则异常
-
exec:命令方式,在用户容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则异常
-
tcpSocket:访问端口方式,如果能够建立成功则认为程序正常,否则异常
-
-
readnessProbe:探测容器是否准备就绪
-
httpGet:http请求方式,如果返回的HTTP状态码在200和399之间,则认为程序正常,否则异常
-
exec:命令方式,在用户容器内执行一次命令,如果命令执行的退出码为0,则认为程序正常,否则异常
-
tcpSocket:访问端口方式,如果能够建立成功则认为程序正常,否则异常
-
-
lifecycle
-
postStart 启动后执行
-
preStop 终止前执行
-
执行方式和探针相同
-
-
例子
apiVersion: v1 kind: Pod metadata: name: readiness-httpget-pod namespace: default labels: name: myapp spec: containers: - name: readiness-httpget image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 imagePullPolicy: IfNotPresent readinessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3
--- apiVersion: v1 kind: Pod metadata: name: liveness-httpget-pod namespace: default labels: name: myapp spec: containers: - name: livess-httpget image: ikubernetes/myapp:v1 ports: - name: http containerPort: 80 imagePullPolicy: IfNotPresent livenessProbe: httpGet: port: http path: /index.html initialDelaySeconds: 1 periodSeconds: 3
--- apiVersion: v1 kind: Pod metadata: name: lifecycle-poststart namespace: default labels: name: myapp tier: appfront spec: containers: - name: lifecycle-poststart-pod image: nginx imagePullPolicy: IfNotPresent lifecycle: postStart: exec: command: ["/bin/sh","-c","echo Home+Page >> /usr/share/message"] preStop: exec: command: ["/usr/sbin/nginx","-s","quit"]
最简单的Pod例子
apiVersion: v1 kind: Pod metadata: name: my-pod labels: name: my-pod spec: containers: - name: busybox image: busybox:latest
Deployment的升级和回退
-
升级:如更新image:
kubectl set image mydeploy mynginx=docker.io/nginx:1.15
-
--record=true:记录每次操作的命令,并与history查看
-
回退:
kubectl rollout history deployment mydeploy
apiVersion: v1 kind: Deployment metadata: name: my-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: busybox image: busybox:latest
DaemonSet升级策略
-
通过
.spec.updateStrategy.type
指定 -
OnDelete: 该策略表示当更新了DaemonSet的模板后,只有手动删除旧的DaemonSet Pod才会创建新的DaemonSet Pod
-
RollingUpdate: 该策略表示当更新DaemonSet模板后会自动删除旧的DaemonSet Pod并创建新的DaemonSetPod
-
要使用DaemonSet的滚动升级,需要
.spec.updateStrategy.type
设置为RollingUpdate
apiVersion: v1 kind: DaemonSet metadata: name: my-DaemonSet spec: selector: matchLabels: name: myapp template: metadata: labels: name: myappt spec: containers: - name: busybox image: busybox:latest
Job
-
spec.template格式同Pod
-
RestartPolicy仅支持Never或OnFailure
-
单个Pod时,默认Pod成功运行后Job即结束
apiVersion: batch/v1 kind: Job metadata: name: pi spec: backoffLimit: 6 # 最多失败6次 completions: 1 # 有一个Job成功运行 parallelism: 1 # 一次性运行pod的个数 template: metadata: name: pi spec: containers: - name: pi image: perl command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never
CornJob
-
CornJob通过控制JJob实现功能
-
schedule格式:(与crontab格式相同)
-
*****
:从左到右依次是分钟,小时,每个月的第几天,月,星期几 -
每15分钟:
0,15,30,45****
-
每隔30分钟运行一次,但仅在每月的第一天运行:
0,30
*1
** -
每天3-5,17-20每隔30分钟:
*/30 [3-5],[17-20] * * *
-
apiVersion: batch/v1beta1 kind: CronJob metadata: name: hello spec: schedule: "*/1 * * * *" jobTemplate: spec: template: spec: containers: - name: busybox image: busybox:latest args: - /bin/sh - -c - date; echo Hello from the Kubernetes cluster restartPolicy: OnFailure
注意:Job和CornJob没有selector,deployment,DaemonSet有selector
Service
Service类型
-
ClusterIP:默认方式,仅集群内部可以访问
-
NodePort:service的port映射到集群内每个节点的一个相同端口,外部使用节点IP和端口就可以访问
-
LoadBalancer:使用外部云服务上的负载均衡服务,要交钱。也可以自己搭建一个负载均衡,如下如
-
ExternalName:将service接收到的请求转发到另外的域名(有可能是在集群内部,有可能是在集群外部)
-
无头服务:也是一种service,只有service名称,没有ClusterIP,可以通过访问名称的方式访问后端Pod,但是没有负载均衡的功能。名称类似:
my-service.default.svc.cluster.local.
-
一个简单的service例子
apiVersion: v1 kind: Service metadata: name: my-nginx labels: app: nginx spec: type: ClusterIP ports: - port: 80 targetPort: 80 selector: app: my-pod
Proxy模式
-
userspace模式,缺点kube-proxy的压力很大,已经放弃
-
iptables模式:使用iptables进行路由转发
-
ipvs模式:架构和iptables一样,不同的是ipvs的转发效率更高。需要注意的是:使用ipvs要事先进行配置。否则默认使用iptables模式
StatefulSet
-
StatefulSet是deployment的变种,具有deployment的基本属性,如保存Pod的数量
-
StatefulSet是Kubernetes提供的管理有状态应用的负载管理控制器API。在Pods管理的基础上,保证Pods的顺序和一致性。与Deployment一样,StatefulSet也是使用容器的Spec来创建Pod,与之不同StatefulSet创建的Pods在生命周期中会保持持久的标记
-
特点:
-
具有固定的网络标记(主机名),是不是通过Pod的IP,使用的是无头服务
-
具有持久化存储,使用pvc实现
-
需要按顺序部署和扩展,第一个Pod启动完成后,才能启动第二个;如果序号为2的Pod死亡,会重新起一个Pod,名称和原来一样(无头服务通过名称解析IP,保证新起的Pod是第二个Pod)
-
需要按顺序终止及删除,,删除的顺序和创建时相反,删除时也是上一个Pod删除成功后,才会删除下一个Pod
-
需要按顺序滚动更新,更新时也是上一个Pod更新成功后,才会更新下一个Pod
-
-
demo
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: serviceName: "nginx" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: docker.io/nginx ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: ["ReadWriteOnce"] volumeMode: Filesystem resources: requests: storage: 50Mi storageClassName: local-storage
Ingress
Ingress的优势
-
service提供的是4层代理,Ingress提供的是7层代理(根据域名代理)
-
Ingress对外提供统一的IP和端口,减少对外暴露的端口
-
Ingess提供负载均衡
Ingress的大致流程
-
创建一个Ingress控制器,可以使用nginx搭建
-
创建Ingress
-
Ingress控制器会实时监测Ingrss,将Ingress的配置自动添加到Ingress控制器(nginx的配置文件)
Ingress流程图
demo(http)
-
搭建nginx-ingress
# 直接到官网查看https://kubernetes.github.io/ingress-nginx/ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.31.1/deploy/static/provider/baremetal/deploy.yaml
-
搭建私有service
apiVersion: v1 kind: Service metadata: name: test-ingress namespace: default spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: test-ingress --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: test-ingress spec: replicas: 1 template: metadata: labels: app: test-ingress spec: containers: - image: nginx:latest imagePullPolicy: IfNotPresent name: test-nginx ports: - containerPort: 80
-
创建ingress,关联nginx-ingress与service
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress namespace: default annotations: kubernetes.io/ingress.class: "nginx" spec: rules: - host: feiutest.cn http: paths: - path: backend: serviceName: test-ingress servicePort: 80
-
测试:使用本地的host文件进行域名解析,然后通过域名访问
demo(https)
-
制作自签证书,生成tls.crt ,tls.key文件
openssl genrsa -out tls.key 2048 openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Guangdong/L=Guangzhou/O=devops/CN=feiutest.cn
-
创建secret
#创建 kubectl create secret tls nginx-test --cert=tls.crt --key=tls.key #查看 kubectl get secret
-
修改ingress
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: test-ingress namespace: default annotations: kubernetes.io/ingress.class: "nginx" spec: rules: - host: feiutest.cn http: paths: - path: backend: serviceName: test-ingress servicePort: 80 tls: - hosts: - feiutest.cn secretName: nginx-test
-
测试:使用https访问,注意端口号
ConfigMap
创建ConfigMap的方式:
-
在命令行中指定configmap参数创建,
--from-literal
-
在命令行中将一个配置文件创建为一个ConfigMap
--from-file=<文件>
-
在命令行中将一个目录下的所有配置文件创建为一个ConfigMap,
--from-file=<目录>
,和指定文件的创建方式相同 -
写好标准的configmap的yaml文件,然后
kubectl create -f
创建 -
demo
kubectl create configmap test-config1 --from-literal = db.host = 10.5.10.116 --from-listeral = db.port = '3306' # key: db.host Value: 10.5.10.116 # key: db.port value: 3306 cat app.properties property.1 = value-1 property.2 = value-2 property.3 = value-3 kubectl create configmap test-config2 --from-file = ./app.properties # key: property.1 Value: value-1 # key: property.2 value: value-2 # key: property.3 Value: value-3 # configs 目录下的config-1和config-2内容如下 # key1 =value1 ############### # key2 =value2 kubectl create configmap test-config3 --from-file = ./configs # key: key1 value: value1 # key: key2 value: value2 cat my-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: my-configmap namespace: default data: special.how: very special.type: charm kubectl create -f my-configmap.yaml # key: special.how Value: very # key: special.type value: charm
使用ConfigMap的方式
-
通过环境变量的方式,直接传递给pod
-
通过环境变量的方式,在yaml文件中通过命令使用环境变量,本质上就是通过环境变量的方式
-
作为volume的方式挂载到pod内,key就是文件名,value就是文件里面的内容,一个key一个文件
apiVersion: v1 kind: ConfigMap metadata: name: my-configmap namespace: default data: type: INFO --- apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: busybox image: busybox:latest command: [ "/bin/sh", "-c", "env" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: my-configmap key: type restartPolicy: Never # 环境变量中会有SPECIAL_LEVEL_KEY=INFO
apiVersion: v1 kind: ConfigMap metadata: name: my-configmap namespace: default data: type: INFO --- apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: busybox image: busybox:latest command: [ "/bin/sh", "-c", "echo ${SPECIAL_LEVEL_KEY}" ] env: - name: SPECIAL_LEVEL_KEY valueFrom: configMapKeyRef: name: my-configmap key: type restartPolicy: Never # 打印 INFO
apiVersion: v1 kind: ConfigMap metadata: name: my-configmap namespace: default data: type: INFO --- apiVersion: v1 kind: Pod metadata: name: my-pod spec: containers: - name: busybox image: busybox:latest command: [ "/bin/sh", "-c", "cat /etc/config/type" ] volumeMounts: - name: config-volume mountPath: /etc/config volumes: - name: config-volume configMap: name: my-configmap restartPolicy: Never # 容器的/etc/config/type文件里面的内容是INFO
Secret
Secret和ConfigMap的对比
-
Secret和ConfigMap的总体思想类似,都是存储信息到Etcd,不同的是Secret存储的是密文。
-
Secret和ConfigMap都可以通过环境变量和Volume的方式使用
-
Secret的特点
-
Secret可以被ServerAccount关联(使用)
-
Secret可以存储register的鉴权信息,用在ImagePullSecret参数中,用于拉取私有仓库的镜像
-
Secret支持Base64加密
-
Secret分为kubernetes.io/Service Account,kubernetes.io/dockerconfigjson,Opaque三种类型,Configmap不区分类型
-
Secret的类型
-
Opaque:base64编码格式的Secret,用来存储密码、密钥等。注意:value必须是base64加密
# 环境变量方式引用 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: wordpress-deployment spec: replicas: 2 template: metadata: labels: app: wordpress spec: containers: - name: "wordpress" image: "wordpress" ports: - containerPort: 80 env: - name: WORDPRESS_DB_USER valueFrom: secretKeyRef: name: mysecret key: username - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password
# volume的方式引用 apiVersion: v1 kind: Pod metadata: labels: name: db name: db spec: volumes: - name: secrets secret: secretName: mysecret containers: - image: gcr.io/my_project_id/pg:v1 name: db volumeMounts: - name: secrets mountPath: "/etc/secrets" readOnly: true ports: - name: cp containerPort: 5432 hostPort: 5432
-
kubernetes.io/dockerconfigjson:创建用于docker registry认证的secret,主要是用于从私有仓库拉取镜像
kubectl create secret docker-registry myregistrykey \ --docker-server = DOCKER_REGISTRY_SERVER \ --docker-username = DOCKER_USER \ --docker-password = DOCKER_PASSWORD \ --docker-email = DOCKER_EMAIL
# pod中是使用registry信息 apiVersion: v1 kind: Pod metadata: name: foo spec: containers: - name: foo image: app:v1 imagePullSecrets: - name: myregistrykey
-
kubernetes.io/service-account-token:被serviceaccount引用,serviceaccout创建时Kubernetes会默认创建对应的secret。Pod如果使用了serviceaccount,对应的secret会自动挂载到Pod的
/run/secrets/kubernetes.io/serviceaccount
目录中
Volume
-
Vloume是Pod中能够被多个容器共享的磁盘目录
-
主要用于持久化和同一个pod里面多个容器之间的目录文件共享
-
分类很多:
-
emptydir:在Pod创建时被创建,初始化为空,同一个Pod里面的同期都可以读写这个路径,当Pod死亡时,emptyDir也被删除,且永久消失
-
hostPath:将node上的路径挂载到Pod中,Pod死亡后,路径里面的文件不会消失
-
-
demo
apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: - mountPath: /cache name: cache-volume volumes: - name: cache-volume emptyDir: {} apiVersion: v1 kind: Pod metadata: name: test-pd spec: containers: - image: k8s.gcr.io/test-webserver name: test-container volumeMounts: # directory location on conainer - mountPath: /test-pd name: test-volume volumes: - name: test-volume hostPath: # directory location on host path: /data # this field is optional type: Directory
PV/PVC
-
PV(PersistentVolume):是集群中由管理员配置的一段网络存储。 它是集群中的资源,就像节点是集群资源一样。PV的生命周期不受Pod影响,独立存在。主要的作用是适配后端所有的网络存储,对外提供统一接口。
-
PVC(PersistentVolumeClaim):是由用户进行存储的请求,类似 Pod消耗节点资源,PVC消耗PV资源。
-
PV和PVC一一绑定,PVC匹配满足条件的最小容量PV
-
PV状态:
-
Available – 资源尚未被claim使用
-
Bound – 卷已经被绑定到claim了
-
Released – claim被删除,卷处于释放状态,但未被集群回收。
-
Failed – 卷自动回收失败
-
-
demo(nfs服务以安装且正常运行)
apiVersion: v1 kind: PersistentVolume metadata: name: pv001 labels: name: pv001 spec: nfs: path: /data/volumes/v1 server: nfs accessModes: ["ReadWriteMany"] capacity: storage: 2Gi --- apiVersion: v1 kind: PersistentVolume metadata: name: pv002 labels: name: pv002 spec: nfs: path: /data/volumes/v2 server: nfs accessModes: ["ReadWriteMany"] capacity: storage: 5Gi # 后端存储是nfs类型
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mypvc namespace: default spec: accessModes: ["ReadWriteMany"] resources: requests: storage: 4Gi # mypvc会绑定到pv002 apiVersion: v1 kind: Pod metadata: name: test-pod-pv-pvc namespace: default spec: volumes: - name: html persistentVolumeClaim: claimName: mypvc containers: - name: myapp image: ikubernetes/myapp:v1 volumeMounts: - name: html mountPath: /usr/share/nginx/html/ # 最后/data/volumes/v2会被挂在到容器mapp中的/usr/share/nginx/html/目录
节点调度
-
scheduler进行调度主要分为两步:节点筛选和节点打分优先
-
节点筛选:遍历所有节点,选择满足条件的节点,解决的是“能不能”的问题
-
节点打分优先:遍历筛选后的节点,按照优先级打分,选择分数最高的节点。解决的是“那个更合适”的问题
-
调度的简单过程如下所示
-
节点筛选的规则
-
NoVolumeZoneConflict:检查给定的zone限制前提下,检查如果在此主机上部署Pod是否存在卷冲突
-
PodFitsResources:检查节点是否有足够资源(例如 CPU、内存与GPU等)满足一个Pod的运行需求
-
PodFitsHostPorts:检查Pod容器所需的HostPort是否已被节点上其它容器或服务占用
-
HostName:检查节点是否满足PodSpec的NodeName字段中指定节点主机名,不满足节点的全部会被过滤掉
-
MatchNodeSelector:检查节点标签(label)是否匹配Pod的nodeSelector属性要求
-
PodToleratesNodeTaints : 根据 taints 和 toleration 的关系判断Pod是否可以调度到节点上,Pod是否满足节点容忍的一些条件
-
MatchInterPodAffinity : 节点亲和性筛选
-
GeneralPredicates:包含一些基本的筛选规则
-
-
节点打分优先
-
LeastRequestedPriority:节点的优先级由节点空闲资源与节点总容量的比值,即由(总容量-节点上Pod的容量总和-新Pod的容量)/总容量)来决定
-
BalancedResourceAllocation:CPU和内存使用率越接近的节点权重越高,该策略不能单独使用,必须和LeastRequestedPriority组合使用,尽量选择在部署Pod后各项资源更均衡的机器
-
InterPodAffinityPriority:通过迭代 weightedPodAffinityTerm 的元素计算和,并且如果对该节点满足相应的PodAffinityTerm,则将 “weight” 加到和中,具有最高和的节点是最优选的
-
SelectorSpreadPriority:为了更好的容灾,对同属于一个service、replication controller或者replica的多个Pod副本,尽量调度到多个不同的节点上
-
NodeAffinityPriority:节点亲和性机制
-
NodePreferAvoidPodsPriority(权重1W)
-
TaintTolerationPriority : 使用 Pod 中 tolerationList 与 节点 Taint 进行匹配,配对成功的项越多,则得分越低
-
亲和性
-
亲和性是Pod靠近节点
-
节点亲和性:Pod优先调度到具有亲和性的节点上运行
-
nodeSelector:节点满足label要求才能得到调度,简单粗暴
apiVersion: v1 kind: Pod metadata: name: nginx labels: env: test spec: containers: - name: nginx image: nginx imagePullPolicy: IfNotPresent nodeSelector: # 选择具有disktype: ssd标签的节点 disktype: ssd # 给节点添加label # kubectl node label <node-name> <label-key>=<label-value> # 查看节点label # kubectl get nodes --show-labels # 删除节点label # kubectl label node <node-name> label-key-
-
亲和性支持的operator:
In
,NotIn
,Exists
,DoesNotExist
,Gt
,Lt
-
requiredDuringSchedulingIgnoredDuringExecution:硬条件,节点满足条件就可以得到调度,不满足条件就调度
-
preferredDuringSchedulingIgnoredDuringExecution:软条件,尽量在满足条件的节点上调度,如果没有满足条件的节点也可以得到调度
apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: nodeAffinity: # 硬条件:节点必须包含kubernetes.io/e2e-az-name=e2e-az1的label才符合条件 requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/e2e-az-name operator: In values: - e2e-az1 # 软条件:节点尽量包含another-node-label-key=another-node-label-value的label, # 不包含也可以调度 preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0
-
Pod亲和性:Pod之间的相互靠近和排斥
-
具有亲和性的Pod被调度到同一节点域上运行,一个节点域包含多个节点,通过节点标签和topologyKey指定
-
具有反亲和性的Pod会被调度到不同的节点域上运行
-
Pod的亲和性和反亲和性都具有硬条件和软条件
-
preferredDuringSchedulingIgnoredDuringExecution:硬条件
-
requiredDuringSchedulingIgnoredDuringExecution:软条件
apiVersion: apps/v1beta1 kind: Deployment metadata: name: affinity labels: app: affinity spec: replicas: 3 revisionHistoryLimit: 15 template: metadata: labels: app: affinity role: test spec: containers: - name: nginx image: nginx:1.7.9 ports: - containerPort: 80 name: nginxweb affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: # 硬策略 - labelSelector: matchExpressions: - key: app operator: In values: - busybox-pod topologyKey: kubernetes.io/hostname # 上面这个例子中的pod需要调度到某个指定的主机上,且这个节点上运行了这样的pod:这个pod有一个 # app=busybox-pod的label
污点和容忍
-
污点和容忍是将Pod驱离节点,与亲和性相反
-
打上污点的节点,如果Pod没有容忍这个污点的能力,那么Pod就不会被调度到这个节点上;只有具备容忍这个污点能力的Pod才会被调度到这个节点上
-
master节点上默认具有node-role.kubernetes.io/master:NoSchedule污点,所以Pod默认不会调度到master上
-
增加污点,删除污点,查看污点
# 查看节点具有的污点 kubectl describe node nodeName # 删除节点的污点 kubectl taint nodes nodeName key=value:NoSchedule # 删除节点的污点 kubectl taint nodes nodeName key-
-
污点驱离Pod的方式
-
NoSchedule:不要调度到这个节点上
-
PreferNoSchedule:尽量不调度到污点节点上
-
NoExecute:不要调度到这个节点上,且会立即驱离已经运行在这个节点上的Pod
-
-
污点和容忍的匹配规则
-
对tolerations属性的写法,其中pod的 key、value、effect 与Nod 的Taint设置需保持一致
-
如果operator的值是Exists则value属性可省略
-
如果operator的值是Equa,则表示其key与value之间的关系是equal(等于)
-
如果不指定 operator 属性,则默认值为 Equal
-
-
apiVersion: apps/v1beta1 kind: Deployment metadata: name: taint labels: app: taint spec: replicas: 3 revisionHistoryLimit: 10 template: metadata: labels: app: taint spec: containers: - name: nginx image: nginx:1.7.9 ports: - name: http containerPort: 80 tolerations: - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" # 该Pod可以被调度到matser上
接入管理---前言
API Server作为Kubernetes网关,是访问和管理资源对象的唯一入口,其各种集群组件访问资源都需要经过网关才能进行正常访问和管理。每一次的访问请求都需要进行合法性的检验,其中包括认证,授权以及准入等,需要通过一系列验证之后,才能访问或者存储数据到etcd当中。如下图所示。
接入管理---认证
-
认证的主要作用是识别需要接入的对端身份,也就是解决“你是谁”的问题
-
ControllerManager、Scheduler与ApiServer进行通信,直接走127.0.0.1进行通信,因为都在同一台服务器上
-
kubelet和kube-peoxy与ApiServer进行通信,需要双向验证
-
Pod与ApiServer进行通信,通过ServiceAccount进行通信。 当创建Pod的时候,如果没有指定一个service account,系统会自动在与该Pod相同的namespace下为其指派一个default service account。
接入管理---鉴权
-
鉴权的主要作用是识别接入对端的权限,也就是解决“你能干什么”的问题
-
kubernetes中鉴权使用的是RBAC(Role-Based Access Control),基于角色的访问控制,通过自定义角色并将角色和特定的 user,group,serviceaccounts 关联起来已达到权限控制的目的。
-
Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限
-
Subject:被作用者,包括 user,group,serviceaccounts,通俗来讲就是认证机制中所识别的用户
-
RoleBinding:定义了“被作用者”和“角色”的绑定关系,也就是将用户以及操作权限进行绑定
-
ClusterRole:集群角色,可以包含多个namespace
-
ClusterRoleBinding:一次绑定多个namespace
-
ClusterRole既可以被ClusterRoleBinding绑定,也可以被RoleBinding绑定,灵活处理即可
接入管理---准入控制
准入控制是请求的最后一个步骤,准入控制有许多内置的模块,可以作用于对象的 “CREATE”、“UPDATE”、“DELETE”、“CONNECT” 四个阶段。在这一过程中,如果任一准入控制模块拒绝,那么请求立刻被拒绝。一旦请求通过所有的准入控制器后就会写入对象存储中。准入控制的配置是有序的,不同的顺序会影响 Kubernetes 的性能,建议使用官方的配置。
Helm3安装和使用
-
安装
# 1. 下载 wget https://github.com/helm/helm/releases # 2. 解压 tar -zxvf helm-v3.0.0-linux-amd64.tgz # 3. 解压后的目录中找到二进制文件,然后将其移至所需的目标位置 mv linux-amd64/helm /usr/local/bin/helm # 4. 验证 helm help
-
自定义chart
# 创建文件和目录 # test/Chart.yaml test/values.yaml /test/templates/nginx.yaml # cat test/Chart.yaml apiVersion: v1 # 当前helm api版本,不需要修改 appVersion: 1.0.0 # 此处为你应用程序的版本号 [*] description: Chart for the nginx server # 介绍此chart是干嘛的,按需求修改 name: example-chart # 模板名,对应目录名 [*] version: 1.0.0 # 此chart版本号 [*] maintainers: # 维护人员列表 [*] - email: xxx name: yyy # cat test/values.yaml version: "1.0" # cat /test/templates/nginx.yaml apiVersion: v1 kind: Pod metadata: name: nginx labels: name: myapp spec: containers: - name: nginx image: nginx:{{.Values.version}} # 引用value中的配置 ports: - name: http containerPort: 80 imagePullPolicy: IfNotPresent
资源限制
-
资源限制主要是限制CPU和内存
-
pod级别:在Pod级别对资源进行限制
-
名称空间级别:在名称空间级别对资源进行限制
日志查看EFK
-
kubernetes中日志管理系统,主要是收集、存储、查看kubernetes中所有节点的所有日志
-
ELK也是日志管理系统,但是ELK很“重”,EFK是ELK的变种,但是很“轻”
-
EFK不是一个软件,而是一套解决方案,并且都是开源软件,之间互相配合使用,完美衔接,高效的满足了很多场合的应用,是目前主流的一种日志系统。EFK是三个开源软件的缩写,分别表示:Elasticsearch , FileBeat, Kibana , 其中ELasticsearch负责日志保存和搜索,FileBeat负责收集日志,Kibana 负责界面,当然EFK和大名鼎鼎的ELK只有一个区别,那就是EFK把ELK的Logstash替换成了FileBeat,因为Filebeat相对于Logstash来说有2个好处:
-
侵入低,无需修改程序目前任何代码和配置
-
相对于Logstash来说性能高,Logstash对于IO占用很大
-
-
EFK的架构如下所示
修改证书时间
-
原因:以kubeadmin安装的kubernetes集群,证书的有效时间是1年,超过一年就无法使用
-
查看有效时间
cd /etc/kubernetes/pki openssl x509 -in apiserver.crt -text -noout --- Validity Not Before: Apr 2 02:42:39 2020 GMT Not After : Apr 2 02:42:39 2021 GMT --- apiserver 只有一年的默认时间使用期限 -------------
-
go环境部署
wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz tar -zxvf go1.12.1.linux-amd64.tar.gz -C /usr/local vim /etc/profile --- export PATH=$PATH:/usr/local/go/bin --- source /etc/profile
-
下载源码
git clone https://github.com/kubernetes/kubernetes.git git checkout -b remotes/origin/release-1.15.1 v1.15.1
-
修改kubeadmin源码
vim staging/src/k8s.io/client-go/util/cert/cert.go # kubeadm 1.14 版本之前 vim cmd/kubeadm/app/util/pkiutil/pki_helpers.go # kubeadm 1.14 至今 ---- const duration3650d = time.Hour * 24 * 365 * 10 NotAfter: time.Now().Add(duration365d).UTC(), ---- make WHAT=cmd/kubeadm GOFLAGS=-v cp _output/bin/kubeadm /root/kubeadm-new ---- cp -p /usr/bin/kubeadmn /usr/bin/kubeadmn.old cp -p /root/kubeadm-new /usr/bin/kubeadm chmod +x /usr/bin/kubeadmn ---- cd /etc/kubernetes/ cp -ap pki pki.old ---- cd /root/k8s-install/core kubeadm alpha certs renew all --config=./kubeadm-config.yaml
-
验证是否修改成功
openssl x509 -in apiserver.crt -text -noout