k8s 最新学习总结
Deployment
Deployment yaml介绍
apiVersion: apps/v1 #版本号
kind: Deployment #类型
metadata: #元数据
name: #rs名称
namespace: #所属命名空间
labels: #标签
controller: deploy
spec: #详情描述
replicas: #副本数量
revisionHistoryLimit: #保留历史版本,默认是10,设置为0的话,不保留历史记录
paused: #暂停部署,默认是false
progressDeadlineSeconds: #部署超时时间(s),默认是600
strategy: #策略
type: RollingUpdates #滚动更新策略
rollingUpdate: #滚动更新
maxSurge: #最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavaliable: #最大不可用状态的pod的最大值,可以为百分比,也可以为整数
selector: #选择器,通过它指定该控制器管理哪些pod
matchLabels: #Labels匹配规则
app: nginx-pod
matchExpressions: #Expression匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: #模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
扩缩容
kubectl scale deploy deploy名称 --replicas=pod数量 -n 命名空间
kubectl edit deploy deploy名字 -n 命名空间
deployment升级和回滚
命令行创建deployment
kubectl run nginx --image=10.0.0.11:5000/nginx:1.13 --replicas=3 --record
命令行升级版本
kubectl **set image** deploy nginx nginx=10.0.0.11:5000/nginx:1.15 --record
查看deployment所有历史版本
kubectl rollout history deployment nginx
deployment回滚到上一个版本
kubectl rollout undo deployment nginx
deployment回滚到指定版本
kubectl rollout undo deployment nginx --to-revision=2
获取yaml配置
--dry-run -o yaml`,--dry-run代表这条命令不会实际在K8s执行,
-o yaml是会将试运行结果以yaml的格式打印出来,这样我们就能轻松获得yaml配置了
# kubectl create deployment nginx --image=nginx --dry-run -o yaml
金丝雀发布(暂停和恢复)
比如有一批新的pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。
然后,再筛选一小部分的用户请求路由到新的pod应用,继续观察能否稳定地按期望的方式运行。
确定没问题之后再继续完成余下的pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。
#更新deployment版本,并配置暂停deployment
kubectl rollout pause deploy pc-deployment -n dev #先暂停,等修改多个配置后在恢复更新
kubectl set image deploy pc-deployment nginx=nginx:1.17.2 -n dev
#继续deploy的更新
kubectl rollout resume deploy pc-deployment -n dev
StatefulSet
statefulset特性
1、稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
2、稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
3、有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
4、有序收缩,有序删除(即从N-1到0)
5、有序的滚动更新
有状态服务sts比较常见的mongo复制集 ,redis cluster,rabbitmq cluster等等
statefulset创建顺序
在创建StatefulSet之前需要准备的东西,值得注意的是创建顺序非常关键,创建顺序如下:
1、Volume
2、Persistent Volume
3、Persistent Volume Claim
4、Service
5、StatefulSet
Volume可以有很多种类型,比如nfs、glusterfs等,我们这里使用的ceph RBD来创建。
创建statefulset
[root@k8s-master mainfests]# vim stateful-demo.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
labels:
app: myapp-svc
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: myapp-pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: myapp
spec:
serviceName: myapp-svc
replicas: 3
selector:
matchLabels:
app: myapp-pod
template:
metadata:
labels:
app: myapp-pod
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- containerPort: 80
name: web
volumeMounts:
- name: myappdata
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: myappdata
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
statefulset 扩容和缩容
kubectl scale sts myapp --replicas=2 #扩容和缩容
kubectl patch sts myapp -p '{"spec":{"replicas":2}}' #打补丁方式缩容
更新策略updateStrategy:
updateStrategy:
rollingUpdate:
partition: 2
type: RollingUpdate
OnDelete: 删除一个才更新一个
RollingUpdate:滚动更新
以partition方式进行更新,更新值为2,只有myapp编号大于等于2的才会进行更新。类似于金丝雀部署方式。
kubectl patch sts myapp -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}'
级联删除和非级联删除
级联删除:删除sts时同时删除POD
非级联删除:删除sts时不删除POD kubectl delete sts web --cascade=false #使用--cascade参数
此时POD相当于是孤儿,删除POD就不会在重建
DaemonSet
DaemonSet介绍
DaemonSet:服务守护进程,在集群节点上分别部署Pod副本,如果有新节点加入集群,Daemonset会自动的在该节点上运行我们需要部署的Pod副本,相反如果有节点退出集群,Daemonset也会移除掉部署在旧节点的Pod副本。
DaemonSet常用场景:
网络插件的 Agent 组件,如(Flannel,Calico)需要运行在每一个节点上,用来处理这个节点上的容器网络;
存储插件的 Agent 组件,如(Ceph,Glusterfs)需要运行在每一个节点上,用来在这个节点上挂载F远程存储目录;
监控系统的数据收集组件,如(Prometheus Node Exporter,Cadvisor)需要运行在每一个节点上,负责这个节点上的监控信息搜集。
日志系统的数据收集组件,如(Fluent,Logstash)需要运行在每一个节点上,负责这个节点上的日志信息搜集。
DaemonSet更新和回滚
kubectl set image ds nginx nginx=1.20.0 --record #更新
kubectl rollout undo ds nginx --to-revision=number #回滚到number处
DaemonSet yaml案例
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd-elasticsearch
image: k8s.gcr.io/fluentd-elasticsearch:1.20
resources:
limits:
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
HPA
HPA介绍
自动扩缩ReplicationController、Deployment、ReplicaSet 和 StatefulSet 中的 Pod 数量
观察CPU使用率自动扩展和缩容Pod
自定义指标扩缩容
kubectl top pod -n kube-system #查看POD的CPU和内存使用量
必须定义 requst参数 必须安装metrics-server
resources:
requests:
cpu: 100m
memory: 100Mi
limits:
cpu: 200m
memory: 200Mi
HPA扩容命令
kubectl autoscale deployment nginx-php --cpu-percent=20 --min=2 --max=5 #CPU占用20% 最少2个,最大5个
kubectl get hpa
kubectl describe hpa
while true;do wget -q -O- http://192.168.222.155:30010 >/dev/null ;done #测试让CPU使用量提高看HPA能不能扩容
Label&&Selector
Label&Selector介绍
Label:对K8s中各种资源进行分类,分组,添加一个具有特别属性的一个标签。
Selector:通过一个过滤的语法进行查找到对应标签的资源。
加标签
kubectl label node k8s-node-01 region=subnet7
删除标签
kubectl label node k8s-node-01 region- # -删除标签
修改标签
kubectl label node k8s-node-01 region=subnet8 --overwrite #overwrite参数
显示标签
kubectl get node -l region=subnet7
kubectl get po --show-labels
kubectl get po -A -l app=nginx # -A 显示所有
筛选标签
kubectl get po -A -l "k8s-app in (kubernetes-dashboard,metrics-server)"
# kubernetes-dashboard,metrics-server二个标签同时筛选
kubectl get po -l version!=v1,app=nginx #version不是v1 ,app=nginx的标签
Service
什么是Service
service会对提供同一个服务的多个pod进行聚合,并且提供一个统一的入口地址。通过访问service的入口地址就能访问到后面的pod服务。
Service常用类型
Type 的取值以及行为如下:
ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType。
NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,
这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
在每个安装了kube-proxy的节点上都会打开一个端口,此端口可以代理至后端POD。
LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP
服务。
ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。
Service模板
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: default
spec:
selector: #标签选择器,必须指定pod资源本身的标签
app: redis
role: logstor
type: ClusterIP #指定服务类型为ClusterIP
ports: #指定端口
- port: 6379 #暴露给服务的端口
- targetPort: 6379 #容器的端口
Service创建
kubectl expose deployment nginx --port=80 --target-port=80 #直接命令创建SVC
kubectl expose deployment nginx --port=80 --target-port=80 --dry-run=client -o yaml #导出svc 的 yaml文件
kubectl get endpoints nginx # 看下自动关联生成的endpoint
kubectl patch svc nginx -p '{"spec":{"type":"NodePort"}}' # 修改svc的类型来提供外部访问
ipvsadm -ln # 来看下lvs的虚拟服务器列表
使用Service代理k8s外部服务
先创建一个service 跟之前的service区别是不指定selector
apiVersion: v1
kind: Service
metadata:
labels:
app: web-svc-external
name: web-svc-external
namespace: default
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
sessionAffinity: None
type: ClusterIP
在创建endpoints 修改IP,就能关联上面的service,起到代理k8s外部服务
apiVersion: v1
kind: Endpoints
metadata:
name: web-svc-external
namespace: default
subsets:
- addresses:
- ip: 36.152.44.96 #外部地址
ports:
- name: http
port: 80
protocol: TCP
Ingress
Ingress介绍
Ingress是授权入站连接到达集群服务的规则集合,为您提供七层负载均衡能力,您可以通过 Ingress 配置提供外部可访问的 URL、负载均衡、SSL、基于名称的虚拟主机,阿里云容器服务K8S Ingress Controller在完全兼容社区版本的基础上提供了更多的特性和优化。
Ingress是允许入站连接到达集群服务的一组规则。即介于物理网络和群集svc之间的一组转发规则。
其实就是实现L4 L7的负载均衡:
用于实现域名的方式访问K8s内部应用
helm安装ingress
https://kubernetes.github.io/ingress-nginx/deploy/#using-helm
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
ingress暴露服务的方式
1、Deployment+LoadBalancer模式的Service
如果要把ingress部署在公有云,那用这种方式比较合适。用Deployment部署ingress-controller,创建一个type为 LoadBalancer的 service关联这组pod。大部分公有云,都会为 LoadBalancer的 service自动创建一个负载均衡器,通常还绑定了公网地址。只要把域名解析指向该地址,就实现了集群服务的对外暴露
2、DaemonSet+HostNetwork+nodeselector
用DaemonSet结合nodeselector来部署ingress-controller到特定的node 上,然后使用Hostiletwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服务。这时,ingress-controller所在的node机器就很类似传统架构的边缘节点,比如机房入口的nginx服务器。该方式整个请求链路最简单,性能相对NodePort模式更好。缺点是由于直接利用宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。比较适合大并发的生产环境使用
ingress资源
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
ingress多域名使用
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service1
port:
number: 80
- host: bar.bar.com
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: service2
port:
number: 80
HTTPS的配置
创建ssl证书
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginXsvc/O=nginxsvc"
创建secret资源进行存储
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
kubectl get secret
kubectl describe secret tls-secret
apiVersion: v1
kind: Secret
metadata:
name: testsecret-tls
namespace: default
data:
tls.crt: #base64 编码的 cert
tls.key: #base64 编码的 key
type: kubernetes.io/tls
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- https-example.foo.com
secretName: testsecret-tls
rules:
- host: https-example.foo.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: service1
port:
number: 80
Secret&ConfigMap
一般用configmap去管理一些配置文件,或者是大量的环境变量信息,configmap将配置和pod分开,有一个nginx,nginx.conf---》configmap.nginx。更易于配置文件的更改和管理
secret:secret根倾向于存储和共享敏感,加密的配置信息。
configmap加密,而secret不加密
https://kubernetes.io/zh-cn/docs/concepts/configuration/secret/ #secret官方中文介绍
secret创建方式
1、使用kubectl create secret方式创建
[root@k8s_master ~]# echo -n 'admin' > ./username.txt
[root@k8s_master ~]# echo -n '1f2d1e2e67df' > ./password.txt
[root@k8s_master ~]# kubectl create secret generic secret01 --from-file=./username.txt --from-file=./password.txt
2、编写yaml文件,引用64位编码
先通过64位编码方式,明文转密文
[root@k8s_master ~]# echo -n 'admin' | base64
YWRtaW4=
[root@k8s_master ~]# echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm
base64解密: echo "MWYyZDFlMmU2N2Rm" |base64 --decode
编写yaml文件,引用密文创建secret
[root@k8s_master ~]# vim secret02.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
##创建secret资源
[root@k8s_master ~]# kubectl apply -f secret02.yaml
secret资源的使用方式
第一种:使用secret中的变量导入到pod中
##编写yaml文件
[root@k8s_master ~]# vim secret03.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: nginx
image: nginx
env:
- name: aaa
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: bbb
valueFrom:
secretKeyRef:
name: mysecret
key: password
##进入pod,验证secret信息
[root@k8s_master ~]# kubectl exec -it mypod bash
root@mypod:/# echo $aaa
admin
root@mypod:/# echo $bbb
1f2d1e2e67df
第二种:将secret容器以volume的形式挂载到pod的某个目录下
[root@k8s_master ~]# vim secret04.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod02
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
##进入pod,查看secret信息
[root@k8s_master ~]# kubectl exec -it mypod02 bash
root@mypod02:/# cd /etc/foo
root@mypod02:/etc/foo# ls
password username
root@mypod02:/etc/foo# cat username
adminroot
root@mypod02:/etc/foo# cat password
1f2d1e2e67
imagePullSecrets (容器镜像拉取secret)
你可以使用 imagePullSecrets 将包含 Docker(或其他)镜像仓库密码的 Secret 传递给 kubelet。kubelet 使用此信息来替 Pod 拉取私有镜像
kubectl create secret docker-registry myregistrykey --docker-server=DUMMY_SERVER \
--docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
--docker-email=DUMMY_DOCKER_EMAIL
将镜像拉取 Secret 添加到服务账户
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'
或修改yaml文件
.....
imagePullSecrets:
- name: myregistrykey
.....
ConfigMap概述
https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-pod-configmap/
k8s官方文档configmap介绍
与Secret类似,区别在于ConfigMap保存的是不需要加密配置的信息
configmap 配置中心(明文存放)
1.可以轻松应对各种环境(开发、测试、生产),当启动容器时,根据需求去加载对应的配置文件。
2.当需要对容器内的配置进行修改时,只需要修改配置中心的配置文件。
3.也可以进行灰度发布,针对单个pod内容器进行更新
加载方式 重要
1.当启动pod时,可以将配置中心的配置文件关联到pod上,从中读取数据传递给pod内容器的一个变量,变量注入方式传递给容器;但是当configmap 发生改变时,并不会同步到容器内变量中,仅在pod启动时生效。
2.也可以把configmap当做存储卷,直接挂载到容器内的目录上,应用程序去读取这个目录的配置文件,同时支持自动同步。
configMap创建方式
方式一:使用kubectl创建
[root@k8s_master ~]# vim redis.properties
redis.host=127.0.0.1
redis.port=6379
redis.password=123456
创建configmap资源
[root@k8s_master ~]# kubectl create configmap redis-config --from-file=redis.properties
方式二:变量参数形式
创建configmap资源,定义变量
##编写yaml文件
[root@k8s_master ~]# vim configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myconfig
namespace: default
data:
special.level: info
special.type: hello
将 ConfigMap 中的所有键值对配置为容器环境变量
创建一个包含多个键值对的 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
SPECIAL_LEVEL: very
SPECIAL_TYPE: charm
使用 envFrom 将所有 ConfigMap 的数据定义为容器环境变量,ConfigMap 中的键成为 Pod 中的环境变量名称。
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "env" ]
envFrom:
- configMapRef:
name: special-config
restartPolicy: Never
将 ConfigMap 数据添加到一个卷中
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "ls /etc/config/" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
# 提供包含要添加到容器中的文件的 ConfigMap 的名称
name: special-config
restartPolicy: Never
注意:
如果在 /etc/config/ 目录中有一些文件,这些文件将被删除。
Kubernetes-subPath的使用
什么是subPath
为了支持单一个pod多次使用同一个volume而设计,subpath翻译过来是子路径的意思,如果是数据卷挂载在容器,指的是存储卷目录的子路径,如果是配置项configMap/Secret,则指的是挂载在容器的子路径。
subPath的使用场景
1、 1个pod中可以拉起多个容器,有时候希望将不同容器的路径挂载在存储卷volume的子路径,这个时候需要用到subpath
2、volume支持将configMap/Secret挂载在容器的路径,但是会覆盖掉容器路径下原有的文件,如何支持选定configMap/Secret的每个key-value挂载在容器中,且不会覆盖掉原目录下的文件,这个时候也可以用到subpath
subPath的使用
存储卷
采用hostpath的方式创建PV,宿主机的映射目录为/data/pod/volume5
cat pv-subpath.yaml
kind: PersistentVolume
apiVersion: v1
metadata:
name: pv-subpath-05
labels:
release: stable
spec:
capacity:
storage: 0.1Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
hostPath:
path: /data/pod/volume5 # 宿主机的目录
PV创建成功后,再创建PVC
[root@k8s-master zhanglei]# cat pvc-subpath.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-subpath
namespace: default
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 0.05Gi
[root@k8s-master zhanglei]# kubectl create -f pvc-subpath.yaml
在pod中声明并使用subpath
[root@k8s-master zhanglei]# cat pod-subpath.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-subpath-zltest
spec:
containers:
- name: ubuntu-subpath-container
image: ubuntu
volumeMounts:
- mountPath: /var/lib/ubuntu # 容器1的挂载目录
name: subpath-vol
subPath: ubuntutest # 宿主机volume5的子目录1
- name: nginx-subpath-container
image: nginx
volumeMounts:
- mountPath: /var/www/nginx # 容器2的挂载目录
name: subpath-vol
subPath: nginxtest # 宿主机volume5的子目录2
volumes:
- name: subpath-vol
persistentVolumeClaim:
claimName: pvc-subpath # PVC的名字
[root@k8s-master zhanglei]# kubectl create -f pod-subpath.yaml
现在来验证下在宿主机存储卷的目录下是否有2个子目录,1个是ubuntutest用来挂载容器1的,另外1个是nginxtest用来挂载容器2的
进入到容器中,挂载一个文件,验证是否可以同步到存储卷
[root@k8s-master nginxtest]# kubectl exec -it pod-subpath-zltest -c nginx-subpath-container -- bash
root@pod-subpath-zltest:/# cd /var/www/nginx
root@pod-subpath-zltest:/var/www/nginx# ls
nginx-test-subpath.txt
[root@k8s-master volume5]# cd nginxtest/
[root@k8s-master nginxtest]# ls
nginx-test-subpath.txt
可以看到容器1的目录/var/www/nginx 和存储卷的子目录 nginxtest完成了映射,容器2类似,这里不再赘述。
配置项-configMap
1)创建configMap
[root@k8s-master consecret]# cat conf-subpath.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: conf-subpath-zltest
namespace: default
data:
example.property.1: hello # key-value键值对
example.property.2: world
example.property.file: |-
property.1=value-1
property.2=value-2
property.3=value-3
2)在Pod中使用configMap
[root@k8s-master consecret]# cat pod-conf-subpath.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
purpose: test-configmap-volume
name: pod-conf-testvolume
spec:
containers:
- name: test-configmap-volume
image: nginx
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/example.property.1 # 容器挂载目录
subPath: example.property.1 # 将key名称作为文件名,hello作为文件内容
volumes:
- name: config-volume
configMap:
name: conf-subpath-zltest # 指定使用哪个CM
[root@k8s-master consecret]# kubectl create -f pod-conf-subpath.yaml
在容器挂载路径验证下是否将configMap中example.property.1挂载在容器中,且是否会覆盖掉原有的目录
root@pod-conf-testvolume:/# cd /etc/nginx
root@pod-conf-testvolume:/etc/nginx# ls
conf.d fastcgi_params koi-win modules scgi_params win-utf
example.property.1 koi-utf mime.types nginx.conf uwsgi_params
从上可以看到example.property.1已经挂载到容器中,且未对目录原有的文件进行覆盖
root@pod-conf-testvolume:/etc/nginx# cd example.property.1
bash: cd: example.property.1: Not a directory
root@pod-conf-testvolume:/etc/nginx# cat example.property.1
helloroot@pod-conf-testvolume:/etc/nginx#
从上可以验证configMap的subpath用法支持将configMap中的每对key-value以key名称作为文件名,value作为文件内容挂载到容器的目录中。
configmap和secret热更新
configmap和secret如果使用的是subpath形式挂载,那么POD是感知不到configmap和secret的更新的。
如果pod的变量来自于configmap和secret的定义内容,那么configmap和secret更新后,也不会更新POD中的变量。
edit命令 kubectl edit
直接修改yaml文件 kubectl replace
加--dry-run参数 kubectl create cm --from-file=nginx.conf --dry-run -oyaml |kubectl replace -f-
不可变Secret和ConfigMap
immutable:true 加入这个参数,就不能修改secret和configmap的配置
Volumes
volumes常用类型
K8s volume是一个目录,这点和Docker volume差不多,当Volume被mount到Pod上,这个Pod中的所有容器都可以访问这个volume,在生产场景中,我们常用的类型有这几种:
emptyDir
hostPath
PersistentVolume(PV) & PersistentVolumeClaim(PVC)
StorageClass
emptyDir
在生产中它的最实际实用是提供Pod内多容器的volume数据共享,但是当pod被重启后,emptyDir数据会丢失
# 我们继续用上面的web服务的配置,在里面新增volume配置
# cat web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: web
name: web
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- image: nginx
name: nginx
resources:
limits:
cpu: "50m"
memory: 20Mi
requests:
cpu: "50m"
memory: 20Mi
volumeMounts: # 准备将pod的目录进行卷挂载
- name: html-files # 自定个名称,容器内可以类似这样挂载多个卷
mountPath: "/usr/share/nginx/html"
- name: busybox # 在pod内再跑一个容器,每秒把当时时间写到nginx默认页面上
image: busybox
args:
- /bin/sh
- -c
- >
while :; do
if [ -f /html/index.html ];then
echo "[$(date +%F\ %T)] hello" > /html/index.html
sleep 1
else
touch /html/index.html
fi
done
volumeMounts:
- name: html-files # 注意这里的名称和上面nginx容器保持一样,这样才能相互进行访问
mountPath: "/html" # 将数据挂载到当前这个容器的这个目录下
volumes:
- name: html-files # 最后定义这个卷的名称也保持和上面一样
emptyDir: # 这就是使用emptyDir卷类型了
medium: Memory # 这里将文件写入内存中保存,这样速度会很快,配置为medium: "" 就是代表默认的使用本地磁盘空间来进行存储
sizeLimit: 10Mi # 因为内存比较珍贵,注意限制使用大小
hostPath
hostPath Volume 的作用是将容器运行的node上已经存在文件系统目录给mount到pod的容器。在生产中大部分应用是是不会直接使用hostPath的,因为我们并不关心Pod在哪台node上运行,而hostPath又恰好增加了pod与node的耦合,限制了pod的使用
PV&PVC
PV&PVC概念
persistentVolume(简称pv)是由管理员设置的存储,它同样时集群中的一类资源,pv时容量插件,如volumes(卷),但其生命周期独立使用pv的任何pod,pv的创建可使用NFS,CEPH等
persistentVolumeClaim(简称pvc)是用户对存储的请求,类似于pod,pod消耗节点资源,pod可以请求特定级别的资源(cpu和内存),pvc可以请求特定的大小和访问模式,例如,可以以一次读写/写或只读多次的模式挂载。
开始部署NFS-SERVER
# 我们这里在10.0.1.201上安装(在生产中,大家要提供作好NFS-SERVER环境的规划)
# yum -y install nfs-utils
# 创建NFS挂载目录
# mkdir /nfs_dir
# chown nobody.nobody /nfs_dir
# 修改NFS-SERVER配置
# echo '/nfs_dir *(rw,sync,no_root_squash)' > /etc/exports
# 重启服务
# systemctl restart rpcbind.service
# systemctl restart nfs-utils.service
# systemctl restart nfs-server.service
# 增加NFS-SERVER开机自启动
# systemctl enable nfs-server.service
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
# 验证NFS-SERVER是否能正常访问
# showmount -e 10.0.1.201
创建PV
# cat pv1.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
labels:
type: test-claim # 这里建议打上一个独有的标签,方便在多个pv的时候方便提供pvc选择挂载
spec:
capacity:
storage: 1Gi # <---------- 1
accessModes:
- ReadWriteOnce # <---------- 2
persistentVolumeReclaimPolicy: Recycle # <---------- 3
storageClassName: nfs # <---------- 4
nfs:
path: /nfs_dir/pv1 # <---------- 5
server: 10.0.1.201
1、capacity 指定 PV 的容量为 1G。
2、accessModes 指定访问模式为 ReadWriteOnce,支持的访问模式有: ReadWriteOnce -- PV 能以 read-write 模式 mount 到单个节点。 ReadOnlyMany -- PV 能以 read-only 模式 mount 到多个节点。 ReadWriteMany -- PV 能以 read-write 模式 mount 到多个节点。
3、persistentVolumeReclaimPolicy 指定当 PV 的回收策略为 Recycle,支持的策略有: Retain -- 需要管理员手工回收。 Recycle -- 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*。 Delete -- 删除 Storage Provider 上的对应存储资源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。
4、storageClassName 指定 PV 的 class 为 nfs。相当于为 PV 设置了一个分类,PVC 可以指定 class 申请相应 class 的 PV。
5、指定 PV 在 NFS 服务器上对应的目录,这里注意,我测试的时候,需要手动先创建好这个目录并授权好,不然后面挂载会提示目录不存在 mkdir /nfsdata/pv1 && chown -R nobody.nogroup /nfsdata
#创建的pv会有一下几种状态:
Available(可用),没有被PVC绑定的空间资源
Bound (已绑定),已经被pvc绑定
Released (已释放),PVC被删除,但是资源还未被重新使用
Failed (失败),自动回收失败
创建PVC
# cat pvc1.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: nfs
selector:
matchLabels:
type: test-claim
pod服务来挂载这个pvc
spec:
containers:
- image: nginx
name: nginx
volumeMounts: # 我们这里将nginx容器默认的页面目录挂载
- name: html-files
mountPath: "/usr/share/nginx/html"
volumes:
- name: html-files
persistentVolumeClaim: # 卷类型使用pvc,同时下面名称处填先创建好的pvc1
claimName: pvc1
pvc&pv绑定问题
很多情况下,创建PVC之后,一直绑定不上PV
1、PVC的空间申请大小大于PV的大小
2、PVC的storageClassName没有和PV的一致
3、PVC的accessModes和PV的不一致
创建挂载了PVC的Pod之后一直处于Pending状态
1、PVC没有被创建成功,或者被创建
2、PVC和Pod不在同一个Namespace
创建PVC后——k8s会创建一个用于回收的POD——根据PV的回收策略进行PV的回收——回收完
以后PV的状态就会变成可被绑定的状态也就是空闲状态——其它的Pending状态的PVC如果匹配到了这个PV,他就能和这个PV进行绑定。
StorageClass动态存储
可以提前配置好pv的动态供给StorageClass,来根据pvc的消费动态生成pv
nfs的StorageClass
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: kube-system
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-provisioner-01
namespace: kube-system
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-provisioner-01
template:
metadata:
labels:
app: nfs-provisioner-01
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: jmgao1983/nfs-client-provisioner:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: nfs-provisioner-01 # 此处供应者名字供storageclass调用
- name: NFS_SERVER
value: 10.0.1.201 # 填入NFS的地址
- name: NFS_PATH
value: /nfs_dir # 填入NFS挂载的目录
volumes:
- name: nfs-client-root
nfs:
server: 10.0.1.201 # 填入NFS的地址
path: /nfs_dir # 填入NFS挂载的目录
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-boge
provisioner: nfs-provisioner-01
# Supported policies: Delete、 Retain , default is Delete
reclaimPolicy: Retain
基于StorageClass创建一个pvc
# vim pvc-sc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-sc
spec:
storageClassName: nfs-boge
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
# kubectl apply -f pvc-sc.yaml
persistentvolumeclaim/pvc-sc created
# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-sc Bound pvc-63eee4c7-90fd-4c7e-abf9-d803c3204623 1Mi RWX nfs-boge 3s
pvc1 Bound pv1 1Gi RWO nfs 24m
# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv1 1Gi RWO Recycle Bound default/pvc1 nfs 49m
pvc-63eee4c7-90fd-4c7e-abf9-d803c3204623 1Mi RWX Retain Bound default/pvc-sc nfs-boge 7s
这里注意下,因为是动态生成的pv,所以它的目录基于是一串随机字符串生成的,这时我们直接进到pod内来创建访问页面
CronJob
一次性任务,在K8s中它叫job
cronjob被调用的时间,是用的controller-manager的时间
Job实例
apiVersion: batch/v1 # 1. batch/v1 是当前 Job 的 apiVersion
kind: Job # 2. 指明当前资源的类型为 Job
metadata:
name: myjob
spec:
completions: 6 # 此job完成pod的总数量
parallelism: 2 # 每次并发跑2个job
template:
metadata:
name: myjob
spec:
containers:
- name: hello
image: busybox
command: ["echo"," hello boge! "]
restartPolicy: OnFailure # 3. restartPolicy 指定什么情况下需要重启容器。对于 Job,只能设置为 Never 或者 OnFailure
cronjob是管理job,也就是每一个周期创建一个job去执行任务,一次只能管理一个job
cronjob实例
apiVersion: batch/v1beta1 # <--------- 当前 CronJob 的 apiVersion
kind: CronJob # <--------- 当前资源的类型
metadata:
name: hello
spec:
schedule: "* * * * *" # <--------- schedule 指定什么时候运行 Job,其格式与 Linux crontab 一致,这里 * * * * * 的含义是每一分钟启动一次
jobTemplate: # <--------- 定义 Job 的模板,格式与前面 Job 一致
spec:
template:
spec:
containers:
- name: hello
image: busybox
command: ["echo","boge like cronjob."]
restartPolicy: OnFailure
#这里会显示cronjob的综合信息
#kubectl get cronjobs.batch
# 测试完成后删掉这个资源
# kubectl delete cronjobs.batch hello
cronjob定时任务在生产中的用处很多,这也是为什么上面job我说用得很少的缘故,我们可以把一些需要定时定期运行的任务,在K8s上以cronjob运行,依托K8s强大的资源调度以及服务自愈能力,我们可以放心的把定时任务交给它执行。
注意的参数:concurrencyPolicy:Allow #并发调度策略,Allow运行同时运行任务
Forbid: #不运行并发执行
Replace: #替换之前的任务
failedJobsHistoryLimit:1 #保留失败的任务数
successfulJobsHistoryLimit:3 #成功的Job保留的次数
suspend:false #挂起,true cronjob不会被执行
startingDeadlineSeconds:30 #执行计划任务,失败后,30秒后继续调用,直到成功
三种探针
三种探针类型
启动探针 Startup Probe :用于判断容器内应用程序是否启动,如果配置了startupProbe,就会先禁止其他的探针,直到它成功为止,成功后将不再进行探测。(1.16版本后加入,针对容器内应用服务是否已经启动监控)
就绪探针 Readiness Probe :判断容器是否已经就绪,若未就绪,容器将会处于未就绪,未就绪的容器,不会进行流量的调度。Kubernetes会把Pod从 service endpoints 中剔除。
存活探针 Liveness Probe :判断容器内的应用程序是否正常,若不正常,根据 Pod 的 restartPolicy 重启策略操作,如果没有配置该探针,默认就是success。
探针的三种检查方法
exec:通过在容器内执行指定命令,来判断命令退出时返回的状态码,返回状态码是0表示正常。
httpGet:通过对容器的 IP 地址、端口和 URL 路径来发送 GET 请求;如果响应的状态码在 200 ~ 399 间,表示正常。
tcpSocket:通过对容器的 IP 地址和指定端口,进行 TCP 检查,如果端口打开,发起 TCP Socket 建立成功,表示正常。
配置项
initialDelaySeconds:等待我们定义的时间 结束后便开始探针检查;
periodSeconds:探针的 间隔时间;
timeoutSeconds:探针的 超时时间,当超过我们定义的时间后,便会被视为失败;
successThreshold:探针的 最小连续成功数量;
failureThreshold:探针的 最小连续失败数量;
启动探针
exec方式
startupProbe: # 可选 检测容器内进程是否完成启动 注意三种检查方式同时只能使用一种
failureThreshold: 3 # 失败三次算探针失败
exec:
command: ['/bin/sh','-c','echo Hello World']
http方式
httpGet:
path: /test
prot: 80
就绪探针
http方式
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3 # 容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 2 # 对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 1 # 对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 1 # 成功1次算探针OK
failureThreshold: 3 # 失败三次算探针失败
restartPolicy: Always # 可选 默认Always
存活探针
http方式
livenessProbe:
httpGet:
path: /
port: 80
scheme: HTTP
initialDelaySeconds: 3 # 容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 2 # 对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 1 # 对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
successThreshold: 1 # 成功1次算探针OK
failureThreshold: 3 # 失败三次算探针失败
restartPolicy: Always # 可选 默认Always
exec方式
livenessProbe:
initialDelaySeconds: 10 #延迟检测时间
periodSeconds: 5 #检测时间间隔
exec: #使用命令检查
command: #指令,类似于运行命令sh
- cat #sh 后的第一个内容,直到需要输入空格,变成下一行
- /tmp/healthy #由于不能输入空格,需要另外声明,结果为sh cat"空格"/tmp/healthy
tcp方式
livenessProbe:
initialDelaySeconds: 15
periodSeconds: 20
tcpSocket:
port: 80
容器生命周期钩子
postStart: 容器创建后立即执行,注意由于是异步执行,它无法保证一定在 ENTRYPOINT 之前运行。如果失败,容器会被杀死,并根据 RestartPolicy 决定是 否重启
preStop:容器终止前执行,常用于资源清理。执行完成之后容器将成功终止,如果失败,容器同样也会被杀死。在其完成之前 会阻塞删除容器的操作
lifecycle:
postStart:
exec: # 在容器启动的时候执行一个命令,修改nginx默认首页内容
command: ["/bin/sh","-c","echo postStart... > /usr/share/nginx/html/index.html"]
preStop:
exec: # 在容器停止之前停止nginx服务
command: ["/usr/sbin/nginx","-s","quit"]
Taint&Toleration(污点&容忍)
什么是Taint 和 Toleration
Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。
每个节点上都可以应用一个或多个taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接的。
如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上
可以给一个节点添加多个 taint ,也可以给一个 pod 添加多个 toleration。
定义污点和容忍度
污点定义在节点的node Spec中,而容忍度则定义在Pod的podSpec中,它们都是键值型数据,但又都额外支持一个效果effect标记,语法格式为key=value:effect,其中key和value的用法及格式与资源注俯-信息相似, 而effect则用于定义对Pod对象的排斥等级,它主要包含以下三种类型
NoSchedule
不能容忍此污点的新Pod对象不可调度至当前节点,属于强制型约束关系,节点上现存的Pod对象不受影响。
PreferNoSchedule
NoSchedule的柔性约束版本,即不能容忍此污点的新Pod对象尽量不要调度至当前节点,不过无其他节点可供调度时也允许接受相应的Pod对象。节点上现存的Pod对象不受影响。
NoExecute
不能容忍此污点的新Pod对象不可调度至当前节点,属于强制型约束关系,而且节点上现存的Pod对象因节点污点变动或Pod容忍度变动而不再满足匹配规则时,Pod对象将被驱逐。
管理节点的污点
### 设置污点
kubectl taint nodes node1 key1=value1:NoSchedule
### 查看污点
kubectl describe node k8s-node-02 |grep Taints
### 去除污点
kubectl taint nodes node1 key1:NoSchedule-
###删除节点上的全部污点信息
kubectl patch nodes node1 -p '{"spec":{"taints":[]}}' #通过kubectl patch命令将节点属性spec.taints的值直接置空即可
容忍(Tolerations)
容忍 ( Tolerations ) 的组成
设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上
容忍配置
pod.spec.tolerations
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
其中 key, vaule, effect 要与 Node 上设置的 taint 保持一致
operator 值为Equal,则表示其key与value之间的关系是equal(等于),也就是key=value. 如果不指定 operator 属性,则默认值为 Equal。operator值为Exists 将会忽略 value 值
1、当不指定 key 值时,表示容忍所有的污点 key(master节点也会部署):
tolerations:
- operator: "Exists"
2、当不指定 effect 值时,表示容忍所有的污点作用
tolerations:
- key: "key"
operator: "Exists"
3、有多个 Master 存在时,防止资源浪费,可以如下设置(还没有测试)
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
pod匹配node过程
Kubernetes 处理多个 taint 和 toleration 的过程就像一个过滤器:
一个Pod控制器,查看一个node节点的所有 taint(污点),并开始遍历有那些taint(污点)跟 pod 中存在的toleration (容忍)与之相匹配,这些污点是可以被pod容忍,所以过滤掉。余下未被过滤的 taint(污点) 的 effect 值决定了 pod 是否会被分配到该节点,特别是以下情况:
如果未被过滤的 taint (污点)中存在一个以上 effect 值为 NoSchedule 的 taint,则 Kubernetes 不会将 pod 分配到该节点。
如果未被过滤的 taint 中不存在 effect 值为 NoSchedule 的 taint,但是存在 effect 值为 PreferNoSchedule 的 taint,则 Kubernetes 会*尝试*将 pod 分配到该节点。
如果未被过滤的 taint 中存在一个以上 effect 值为 NoExecute 的 taint,则 Kubernetes 不会将 pod 分配到该节点(如果 pod 还未在节点上运行),或者将 pod 从该节点驱逐(如果 pod 已经在节点上运行)。
Affinity (亲合力和反亲合力)
nodeSelector
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
nodeSelector:
node: molk8s-node-02
语法kubectl label nodes <node-name> <label-key>=<label-value>
需要注意的是,如果我们指定了Pod的nodeSelector条件,且在集群中不存在包含相应标签的Node,则即使在集群中还有其他可供使用的Node,这个Pod也无法被成功调度。nodeSelector是一种硬性限制。上述设置:nginx1将会被调度到打有标签:node=molk8s-node-02的k8s节点上。但这种做法较为单一且不灵活,很难满足复杂场景的调度设置。
nodeAffinity(Node亲和性调度)
NodeAffinity 意为Node 亲和性的调度策略, 是用于替换NodeSelector的全新调度策略
nodeAffinity也是基于k8s节点标签的一种调度策略,有如下两种限制:
requiredDuringSchedulingIgnoredDuringExecution (硬限制,必须满足, 同nodeSelector,不满足则不进行调度。)
preferredDuringSchedulingIgnoredDuringExecution(软限制,尽量满足,不满足也可正常完成调度)
IgnoreDuringExecution表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。
上面的限制也同样适用于:podaffinity和podAntiAffinity。
硬策略
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: nodeType #key 可以看label有哪些
operator: In
values:
- dev
3.2.软策略
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: nodeType
operator: In
values:
- test
NodeAffinity规则设置的注意事项如下。
◎ 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能最终运行在指定的Node上。
◎ 如果nodeAffinity指定了多个nodeSelectorTerms,那么其中一个能匹配成功即可。
◎ 如果在nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有matchExpressions才能运行该Pod。
现在Kubernetes提供的操作符有下面的几种
In:label 的值在某个标签中
NotIn:label 的值不在某个标签中
Gt:label 的值大于某个值
Lt:label 的值小于某个值
Exists:某个 label 存在
DoesNotExist:某个 label 不存在
如果nodeSelectorTerms下面有多个选项的话,满足任何一个条件就可以了;如果matchExpressions有多个选项的话,则必须同时满足这些条件才能正常调度 POD。
podaffinity(Pod亲和与互斥调度策略)
Pod的亲和性和互斥性调度具体作法,就是通过在Pod上定义上增加topologyKey属性,来声明对应的目标拓扑区域内几种相关联的Pod“在一起或不在一起”。与节点亲和性相同,Pod亲和性与互斥的条件设置也是requireDuringSchedulingIgnoredExecution和preferredDuringSchedulingIgnoredExecution。Pod的亲和性被定义与PodSpec的affinity字段的podAffinity子字段中;Pod间的互斥性被定义与同一层次的PodAntiAffinity。
Pod亲和性调度测试
1.参数目标pod
首先创建一个名为pod-flag的Pod,带有标签security=S1和app=nginx,后面例子将使用到pod-flag作为Pod亲和性和互斥的目标Pod:
apiVersion: v1
kind: Pod
metadata:
name: pod-flag
labels:
security: "S1"
app: "nginx"
spec:
containers:
- name: nginx
image: nginx
Pod 亲和性调度
创建2个Pod来说明Pod亲和性调度,定义亲和性标签"security=S1"对应上面Pod "pod-flag",topologyKey的值被设置为 "kubernetes.io/hostname" :
apiVersion: v1
kind: Pod
metadata:
name: pod-affinity
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: security
operator: In
values:
- S1
topologyKey: kubernetes.io/hostname
containers:
- name: with-pod-affinity
image: nginx
创建Pod之后,使用kubectl get pod -o wide 查看,两个Pod被调度到同一个k8s-node-01上。因为k8s的节点"k8s-node-01"有节点标签key"kubernetes.io/hostname"且这个节点上已经运行具有"security=S1"标签的pod实例,所以pod-affinity会被调度到此节点上。
软亲和力
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
# 因为是Pod亲和性/反亲和性;所以这里匹配规则写的是Pod的标签信息
matchExpressions:
- key: version
operator: In
values:
- v1
- v2
# 拓扑域 若多个node节点具备相同的标签信息【标签键值相同】,则表示这些node节点就在同一拓扑域
topologyKey: disk-type
podAntiAffinity(Pod互斥性调度测试)
[root@molk8s-master k8s-affinity]# cat 5-pod-antiaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx5
labels:
app: nginx5
spec:
replicas: 2
selector:
matchLabels:
app: nginx5
template:
metadata:
labels:
app: nginx5
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx5
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx5
image: nginx:latest
nginx5的两个pod会被调度不同的节点上面去。
假如你还想将nginx5的两个pod调度到特定标签的node上,那可以结合nodeAffinity和podAntiAffinity,见下:
[root@molk8s-master k8s-affinity]# cat 5-pod-antiaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx5
labels:
app: nginx5
spec:
replicas: 2
selector:
matchLabels:
app: nginx5
template:
metadata:
labels:
app: nginx5
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node
operator: In
values:
- k8s-node-01
- k8s-node-02
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx5
topologyKey: "kubernetes.io/hostname"
containers:
- name: nginx5
image: nginx:latest
nginx5的两个pod将被被调度到具有标签"node=molk8s-node-01"和"node=molk8s-node-02"的两个k8s节点上面,且能保证每个节点只运行一个pod。
topologyKey
# 6.1、既然 topologyKey 是拓扑域,那 Pod 之间怎样才是属于同一个拓扑域?
如果使用 k8s.io/hostname,则表示拓扑域为 Node 范围,那么 k8s.io/hostname 对应的值不一样就是不同的拓扑域。比如 Pod1 在 k8s.io/hostname=node1 的 Node 上,Pod2 在 k8s.io/hostname=node2 的 Node 上,Pod3 在 k8s.io/hostname=node1 的 Node 上,则 Pod2 和 Pod1、Pod3 不在同一个拓扑域,而Pod1 和 Pod3在同一个拓扑域。
如果使用 failure-domain.k8s.io/zone ,则表示拓扑域为一个区域。同样,Node 的标签 failure-domain.k8s.io/zone 对应的值不一样也不是同一个拓扑域,比如 Pod1 在 failure-domain.k8s.io/zone=beijing 的 Node 上,Pod2 在 failure-domain.k8s.io/zone=hangzhou 的 Node 上,则 Pod1 和 Pod2 不属于同一个拓扑域。
当然,topologyKey 也可以使用自定义标签。比如可以给一组 Node 打上标签 custom_topology,那么拓扑域就是针对这个标签了,则该标签相同的 Node 上的 Pod 属于同一个拓扑域。
RBAC权限管理
RBAC:基于角色的访问控制,Role-Based Access Control,他是一种基于企业内个人角色来管理一些资源的访问方法.
Jenkins: 使用角色的用户权限管理
RBAC:4种顶级资源: Role, ClusterRole,RoleBinding,ClusterRoleBinding
Role:角色,包含一组权限的规则.只是附加允许.Namespace隔离,只作用于命名空间内
CluterRole:和Role的区别,Role是只作用于命名空间内.作用于整个集群.
RoleBinding:作用于命令空间内,将ClusterRole或者Role绑定到User,Group,ServiceAccount
ClusterRoleBinding:作用于整个集群.
ServiceAccount:User,Group
Basic-auth-file
https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/ 官方例子
1、创建K8S 用户
普通用户并不是通过k8s来创建和维护,是通过创建证书和切换上下文环境的方式来创建和切换用户。
a、创建证书
# 创建私钥
$ openssl genrsa -out devuser.key 2048
# 用此私钥创建一个csr(证书签名请求)文件
$ openssl req -new -key devuser.key -subj "/CN=devuser" -out devuser.csr
# 拿着私钥和请求文件生成证书
$ openssl x509 -req -in devuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out devuser.crt -days 365
b、生成账号
$ kubectl config set-credentials devuser --client-certificate=./devuser.crt --client-key=./devuser.key --embed-certs=true
c、设置上下文参数
# # 设置上下文, 默认会保存在 $HOME/.kube/config
$ kubectl config set-context devuser@kubernetes --cluster=kubernetes --user=devuser --namespace=dev
# 查看
$ kubectl config get-contexts
d、设置 默认上下文
$ kubectl config use-context devuser@kubernetes
# 查看
$ kubectl config get-contexts
$ kubectl get nodes
发现使用我们创建的用户查询是失败的,是因为账号还没授权,接下来就是对账号进行授权。这里需要先把用切回来,要不然就无法进行下一步授权了。
$ kubectl config use-context kubernetes-admin@kubernetes
$ kubectl get nodes
2、对用户授权
$ cat >devuser-role-bind<<EOF
kind: Role # 角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: dev
name: devuser-role
rules:
- apiGroups: [""] # ""代表核心api组
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: RoleBinding # 角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: devuser-rolebinding
namespace: dev
subjects:
- kind: User
name: devuser # 目标用户
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: devuser-role # 角色信息
apiGroup: rbac.authorization.k8s.io
EOF
执行并验证
$ kubectl apply -f devuser-role-bind
$ kubectl config use-context devuser@kubernetes
$ kubectl get pods # 不带命名空间,这里默认dev,也只能查看dev上面限制的命名空间的pods资源,从而也验证了role是针对命名空间的权限限制
#查看其它命名空间的资源
$ kubectl get pods -n default
$ kubectl get pods -n kube-system
$ kubectl get nodes
可以看到,用devuser,已经可以管理dev命名空间下的pod资源了,也只能管理dev命名空间下的pod资源,无法管理dev以外的资源类型,验证ok。ClusterRoleBinding绑定类似,这里就不重复了。有兴趣的小伙伴可以试试。
切回管理员用户
$ kubectl config use-context kubernetes-admin@kubernetes
2)Group
因为跟user类型,这里就不过多文字介绍,直接上命令和配置
1、创建K8S 用户和用户组
# 创建私钥
$ openssl genrsa -out devgroupuser.key 2048
# 用此私钥创建一个csr(证书签名请求)文件
$ openssl req -new -key devgroupuser.key -subj "/O=devgroup/CN=devgroupuser" -out devgroupuser.csr
# 拿着私钥和请求文件生成证书
$ openssl x509 -req -in devgroupuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out devgroupuser.crt -days 365
# 生成账号
$ kubectl config set-credentials devgroupuser --client-certificate=./devgroupuser.crt --client-key=./devgroupuser.key --embed-certs=true
# 设置上下文参数
$ kubectl config set-context devgroupuser@kubernetes --cluster=kubernetes --user=devgroupuser --namespace=dev
# 查看
$ kubectl config get-contexts
2、对组授权
$ cat >devgroup-role-bind.yaml<<EOF
kind: Role # 角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: dev
name: devgroup-role
rules:
- apiGroups: [""] # ""代表核心api组
resources: ["services","pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
kind: RoleBinding # 角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: devgroup-rolebinding
namespace: dev
subjects:
- kind: Group
name: devgroup # 目标用户组
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: devgroup-role # 角色信息
apiGroup: rbac.authorization.k8s.io
EOF
执行并验证(命名空间默认dev,而不是系统的default)
$ kubectl apply -f devgroup-role-bind.yaml
#切用户
$ kubectl config use-context devgroupuser@kubernetes
# 查看
$ kubectl config get-contexts
$ kubectl get pods
$ kubectl get svc
$ kubectl get nodes
$ kubectl get jobs
从上图实验看,只能管理dev命名空间下的pods和services了,验证ok。
切回管理员用户
$ kubectl config use-context kubernetes-admin@kubernetes
3)ServiceAccount
上面第四节,已经很详细的介绍了ServiceAccount,这里也是直接上配置和操作命令。
$ cat >RoleBinding-ServiceAccount-001.yaml<<EOF
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: role001
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rb001
namespace: default
subjects:
- kind: ServiceAccount
name: lisi
namespace: default
roleRef:
kind: Role
name: role001
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: lisi
namespace: default
EOF
执行
$ kubectl delete -f RoleBinding-ServiceAccount-001.yaml
- *
4)为ServiceAccount生成Token
cat >ClusterRoleBinding-ServiceAccount-token-001.yaml<<EOF
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: admin
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: sa001
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa001
namespace: kube-system
EOF
获取token
$ kubectl -n kube-system get secret|grep sa001
$ kubectl -n kube-system describe secret sa001-token-c2klg
# 也可以使用 jsonpath 的方式直接获取 token 的值,如:
$ kubectl -n kube-system get secret sa001-token-c2klg -o jsonpath={.data.token}|base64 -d
【注意】yaml 输出里的那个 token 值是进行 base64 编码后的结果
5)默认的Token
每次创建了新的namespace下都会生成一个默认的token,名为default-token-xxxx。default就相当于该namespace下的一个用户。
$ kubectl -n dev get secret
$ kubectl -n dev describe secret default-token-67wgz
# 也可以使用 jsonpath 的方式直接获取 token 的值,如:
$ kubectl -n dev get secret default-token-67wgz -o jsonpath={.data.token}|base64 -d
可以使用下面的命令给该用户分配该namespace的管理员权限
kubectl create rolebinding $ROLEBINDING_NAME --clusterrole=admin --serviceaccount=$NAMESPACE:default --namespace=$NAMESPACE
- $ROLEBINDING_NAME必须是该namespace下的唯一的
- admin表示用户该namespace的管理员权限,关于使用clusterrole进行更细粒度的权限控制请参考RBAC------基于角色的访问控制。
- 我们给默认的serviceaccount default分配admin权限,这样就不要再创建新的serviceaccount,当然你也可以自己创建新的serviceaccount,然后给它admin权限
其实kubernetes-dashboard就是通过token授权的,可以不清楚的,可以看我之前的文章:Kubernetes(k8s)安装以及搭建k8s-Dashboard详解
总结
RoleBinding 和 ClusterRoleBinding:角色绑定和集群角色绑定,简单来说就是把声明的 Subject 和我们的 Role 进行绑定的过程(给某个用户绑定上操作的权限),二者的区别也是作用范围的区别:RoleBinding 只会影响到当前namespace 下面的资源操作权限,而 ClusterRoleBinding 会影响到所有的namespace。
- Rule:规则,规则是一组属于不同 API Group 资源上的一组操作的集合
- Role 和 ClusterRole:角色和集群角色,这两个对象都包含上面的 Rules 元素,二者的区别在于,在 Role 中,定义的规则只适用于单个命名空间,也就是和namespace 关联的,而 ClusterRole 是集群范围内的,因此定义的规则不受命名空间的约束。另外 Role 和 ClusterRole 在Kubernetes中都被定义为集群内部的 API 资源,和我们前面学习过的 Pod、ConfigMap 这些类似,都是我们集群的资源对象,所以同样的可以使用我们前面的kubectl相关的命令来进行操作
- Subject:主题,对应在集群中尝试操作的对象,集群中定义了3种类型的主题资源:
- User:用户,这是有外部独立服务进行管理的,管理员进行私钥的分配,用户可以使用 KeyStone或者 Goolge 帐号,甚至一个用户名和密码的文件列表也可以。对于用户的管理集群内部没有一个关联的资源对象,所以用户不能通过集群内部的 API 来进行管理
- Group:组,这是用来关联多个账户的,集群中有一些默认创建的组,比如cluster-admin
- ServiceAccount:服务帐号,通过Kubernetes API 来管理的一些用户帐号,和namespace 进行关联的,适用于集群内部运行的应用程序,需要通过 API 来完成权限认证,所以在集群内部进行权限操作,我们都需要使用到 ServiceAccount,这也是我们这节课的重点
临时容器进行Debug 1.16+支持
就是在原有的Pod上,添加一个临时的container,这个container可以包含我们排查问题所有的工具,netstat,ps,top,jstat,imap等
开启临时容器EphemeralContainers
所有节点都需要
#vim /etc/systemd/system/kubelet.service.d/10-kubelet.conf
--feature-gates="EphemeralContainers=true"
#vim /etc/kubernetes/kubelet-conf.yml
featureGates:
EphemeralContainers: true
#systemctl daemon-reload
vim /usr/lib/systemd/system/kube-proxy.service
--feature-gates=EphemeralContainers=true \
#master节点
vim /usr/lib/systemd/system/kube-apiserver.service
--feature-gates=EphemeralContainers=true \
vim /usr/lib/systemd/system/kube-controller-manager.service
--feature-gates=EphemeralContainers=true
vim /usr/lib/systemd/system/kube-scheduler.service
--feature-gates=EphemeralContainers=true
#systemctl daemon-reload
#systemctl restart kubelet kube-apiserver kube-controller-manager kube-scheduler kube-proxy
debug使用
kubectl debug -it ephemeral-demo --image=busybox:1.28 --target=ephemeral-demo #--target 参数指定另一个容器的进程命名空间
kubectl debug mypod -it --image=busybox
kubectl debug mypod -c debugger --image=busybox #如果用户需要自己指定容器名称则使用
kubectl debug myapp -it --image=ubuntu --share-processes --copy-to=myapp-debug #--share-processes 允许在此 Pod 中的其他容器中查看该容器的进程。
查找问题指南
查看系统Event
kubectl describe po
查看容器日志
kubectl logs <pod_name>
如果在某个Pod中包含多个容器,就需要通过-c参数指定容器的名称来查看
kubectl logs <pod-name> -c <container_name>
查看Kubernetes服务日志
systemctl status <kubernetes_service>
journalctl -u <kubernetes_service>
安装一键式k8s资源平台Ratel到k8s集群中
https://github.com/dotbalo/ratel-doc/blob/master/cluster/Install.md
cat servers.yaml
- serverName: 'test1'
serverAddress: 'https://192.168.222.150:8443'
#serverAdminUser: 'xxx'
#serverAdminPassword: 'xxx#'
serverAdminToken: 'null'
serverDashboardUrl: "https://k8s.test1.com.cn/#"
production: 'false'
kubeConfigPath: "/mnt/test1.config"
harborConfig: "HarborUrl, HarborUsername, HarborPassword, HarborEmail"
参数解析:
serverName: 集群别名
serverAddress: Kubernetes APIServer地址
serverAdminUser: Kubernetes管理员账号(需要配置basic auth)
serverAdminPassword: Kubernetes管理员密码
serverAdminToken: Kubernetes管理员Token // 暂不支持
serverDashboardUrl: Kubernetes官方dashboard地址,1.x版本需要添加/#!,2.x需要添加/#
kubeConfigPath: Kubernetes kube.config路径(绝对路径,这个路径不是宿主机的本地路径,而是1.2小节secret的挂载路径,一般可以不改/mnt)
harborConfig: 对于多集群管理的情况下,可能会存在不同的harbor仓库,配置此参数可以在拷贝资源的时候自动替换harbor配置
kubeConfigPath 通过secret挂载到容器的/mnt目录或者其他目录
mkdir ratel && cd ratel
cp /root/.kube/config test1.config
创建Secret:
kubectl create secret generic ratel-config --from-file=test1.config --from-file=servers.yaml -n kube-system
部署ratel
[root@k8s-master-01 ratel]# cat ratel.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ratel
name: ratel
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: ratel
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: ratel
spec:
containers:
- command:
- sh
- -c
- ./ratel -c /mnt/servers.yaml
env:
- name: TZ
value: Asia/Shanghai
- name: LANG
value: C.UTF-8
- name: ProRunMode
value: prod
- name: ADMIN_USERNAME
value: admin
- name: ADMIN_PASSWORD
value: password
image: registry.cn-beijing.aliyuncs.com/dotbalo/ratel:latest
imagePullPolicy: Always
livenessProbe:
failureThreshold: 2
initialDelaySeconds: 10
periodSeconds: 60
successThreshold: 1
tcpSocket:
port: 8888
timeoutSeconds: 2
name: ratel
ports:
- containerPort: 8888
name: web
protocol: TCP
readinessProbe:
failureThreshold: 2
initialDelaySeconds: 10
periodSeconds: 60
successThreshold: 1
tcpSocket:
port: 8888
timeoutSeconds: 2
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 500m
memory: 512Mi
volumeMounts:
- mountPath: /mnt
name: ratel-config
dnsPolicy: ClusterFirst
# imagePullSecrets:
# - name: myregistrykey
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: ratel-config
secret:
defaultMode: 420
secretName: ratel-config
Service和Ingress配置
[root@k8s-master-01 ratel]# cat ratel-svc.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: ratel
name: ratel
namespace: kube-system
spec:
ports:
- name: container-1-web-1
port: 8888
protocol: TCP
targetPort: 8888
selector:
app: ratel
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ratel
namespace: kube-system
spec:
rules:
- host: krm.test.com
http:
paths:
- backend:
service:
name: ratel
port:
number: 8888
path: /
pathType: Prefix
K8s资源请求和限额
Request
容器使用的最小资源需求,作为容器调度时资源分配的判断依赖。只有当节点可以分配资源量 >= 容器资源请求数是才允许
Limit
容器可以使用资源的最大值,设置为0表示使用资源无上限。
Request 能够保证POD有足够的资源运行,而Limit 则是防止某个POD无限制地使用资源,导致宿主机雪崩,从而影响其他POD崩溃。两者之间必须满足关系:
0<=Request<=Limit<=Infinity (如果Limit为0表示不对资源进行限制,这时可以小于Request)
QoS(Quality of Service)
当集群资源不足时,K8S会根据Pod标记的QoS类别做剔除决策,腾出空闲资源
第一种标记为保证型,是在内存使用量超限时被kill掉;
第二种标记为突发流量型,当节点资源不足时,这类Pod可能会被kill掉;
第三种标记为最大努力型,只要当节点资源不够调度时,首先就会被kill掉。
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
k8s QoS设计实现
QoS是 Quality of Service 的缩写,即服务质量。为了实现资源被有效调度和分配的同时提高资源利用率,kubernetes针对不同服务质量的预期,通过 QoS(Quality of Service)来对 pod 进行服务质量管理。对于一个 pod 来说,服务质量体现在两个具体的指标:CPU 和内存。当节点上内存资源紧张时,kubernetes 会根据预先设置的不同 QoS 类别进行相应处理。
Guaranteed(有保证的)
Burstable (不稳定的)
Best-Effort (尽最大努力)
对于 QoS 类为 Guaranteed 的 Pod:Pod 中的每个容器必须指定memory requests和memory limit ,CPU requests和 CPU limit ,并且requests 和 limit 要相等。
对于 QoS 类为 BestEffort 的 Pod:Pod 中的容器必须没有设置内存和 CPU 限制或请求
对于 QoS 类为Burstable 的 Pod:Pod 中的一个容器设置了 limit 并且 和 requests 不相等
Best-Effort pods:系统用完了全部内存时,该类型 pods 会最先被kill掉。
Burstable pods:系统用完了全部内存,且没有 Best-Effort 类型的容器可以被 kill 时,该类型的 pods 会被 kill 掉。
Guaranteed pods:系统用完了全部内存,且没有 Burstable 与 Best-Effort 类型的容器可以被 kill 时,该类型的 pods 会被 kill 掉。
PodPreset
PodPreset 的作用
将一些公用的参数设置到pod中去,例如 时区统一设置为东八区等
API Server 开启PodPreset
- 编辑文件 /etc/kubernetes/manifests/kube-apiserver.yaml,添加配置 --runtime-config=settings.k8s.io/v1alpha1=true,添加PodPreset到--admission-control(新版本是--enable-admission-plugins)
编写Preset YAML
案例PodPreset YAML如下:
apiVersion: settings.k8s.io/v1alpha1
kind: PodPreset
metadata:
name: demo-podpreset
spec:
selector:
matchLabels:
role: developer
volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
在这个 PodPreset 的定义中,首先是一个 selector
。这就意味着后面这些追加的定义,只会作用于selector
所定义的、带有"role: developer"标签的 Pod 对象,这就可以防止"误伤"。
上面定义了一组 Pod 的 Spec 里的标准字段,以及对应的值。volumeMounts 定义了容器 Volume 的挂载目录,volumes 定义了一个 emptyDir 的 Volume。
配置Pod YAML
案例的Pod YAML文件如下:
apiVersion: v1
kind: Pod
metadata:
name: test-web
labels:
app: test-web
role: developer
spec:
containers:
- name: website
image: nginx
ports:
- containerPort: 80
运行
执行时候需要注意顺序,先执行PodPrest相关YAML创建,再执行Pod相关YAML创建。指令如下
kubectl apply -f test-web-podpreset.yaml
kubectl apply -f test-web.yaml
我们通过使用kubectl get pod
指令来检查最终Pod的具体YAML配置。
分析
我们就可以清楚地看到,这个 Pod 里多了新添加的 labels、volumes 和 volumeMount 的定义,它们的配置跟 PodPreset 的内容一样。
此外,这个 Pod 还被自动加上了一个 annotation
表示这个 Pod 对象被 PodPreset 改动过。
PodPreset 里定义的内容,只会在 Pod API 对象被创建之前追加在这个对象本身上,而不会影响任何 Pod 的控制器的定义。
当定义了同时作用于一个 Pod 对象的多个 PodPreset时,Kubernetes 项目会帮合并(Merge)
这两个 PodPreset 要做的修改。而如果它们要做的修改有冲突的话,这些冲突字段就不会被修改。
总结
PodPreset 是专门用来对 Pod 进行批量化、自动化修改的工具对象,这也是Kubernetes"一切皆对象"的设计思想的体现。我们还是需要理清楚这些组件的功能,Kubernetes提供了一堆的积木,至于实现什么样的功能还是取决于真实的场景。
不得不说,关于纯粹的CRUD人员要有危机感,Kubernetes真的会消灭掉很多低级和低效岗位。
Rook
什么是Rook
一个自我管理的分布式存储编排系统,它本身并不是存储系统,在存储和K8S之前搭建了一个桥梁,存储系统的搭建或者维护变得特别简单.ROOK支持CSI,CSI做一些PVC的快照,PVC扩容等操作.
Operator:主要用于有状态的服务,或者用于比较复杂的应用管理.
Helm:主要用于无状态的服务,配置分离.
Rook
agent:在每个存储节点上运行,用于配置一个FlexVolume插件,和K8s的存储卷进行集成.挂载网络存储,加载存储卷,格式化文件系统.
Discover:主要用于检测链接到存储节点上的存储设备.
Ceph:
OSD:直接连接每一个集群节点的物理磁盘或者是目录,集群的副本数,高可用性和容错性.
MON:集群监控,所有集群的节点都会向Mon汇报,他记录了集群的拓扑以及数据存储位置的信息.
MDS:元数据服务器.负责跟踪文件层次结构并存储Ceph元数据
RGW:restful API接口.
MGR:提供额外的监控和界面.
Rook安装
官方文档
https://rook.github.io/docs/rook/v1.9/Getting-Started/intro/
$ git clone --single-branch --branch v1.9.9 https://github.com/rook/rook.git
cd rook/deploy/examples
kubectl create -f crds.yaml -f common.yaml -f operator.yaml
kubectl create -f cluster.yaml
共享文件系统类型的StorageClass
https://rook.io/docs/rook/v1.9/ceph-filesystem.html
https://rook.io/docs/rook/v1.9/ceph-filesystem-crd.html
PVC的扩容,PVC的快照和回滚
https://rook.io/docs/rook/v1.9/ceph-csi-drivers.html
开启PVC的扩容和快照功能
https://rook.io/docs/rook/v1.9/ceph-csi-drivers.html
vim /etc/systemd/system/kubelet.service.d/10-kubelet.conf
ExpandCSIVolumes=true,VolumeSnapshotDataSource=true
vim /usr/lib/systemd/system/kube-proxy.service
ExpandCSIVolumes=true,VolumeSnapshotDataSource=true
systemctl daemon-reload
kubectl restart kubelet kube-proxy
vim /usr/lib/systemd/system/kube-apiserver.service
vim /usr/lib/systemd/system/kube-controller-manager.service
vim /usr/lib/systemd/system/kube-scheduler.service
删除集群的文档
https://rook.io/docs/rook/v1.9/ceph-teardown.html
部署一个容器到K8s里
hub.docker.com 搜索官方容器镜像
redis配置文件
https://raw.githubusercontent.com/redis/redis/5.0/redis.conf
operator模板
https://github.com/operator-framework/awesome-operators
redis集群operator布署
https://github.com/ucloud/redis-cluster-operator#deploy-a-sample-redis-cluster
RabbitMQ集群安装
https://github.com/dotbalo/k8s/tree/master/k8s-rabbitmq-cluster
Install EFK: https://www.cnblogs.com/dukuan/p/9891198.html
Install RabbitMQ Cluster: https://www.cnblogs.com/dukuan/p/9897443.html
Install openLDAP: https://www.cnblogs.com/dukuan/p/9983899.html
Install Redis Sentinel: https://www.cnblogs.com/dukuan/p/9913420.html
Install Redis Cluster: https://github.com/dotbalo/k8s/tree/master/redis/k8s-redis-cluster
Install GitLab: https://www.cnblogs.com/dukuan/p/10036489.html
helm安装
https://helm.sh/docs/intro/install
helm常用命令
安装charts
helm install --name redis --namespaces prod bitnami/redis
增加repo
helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
删除charts
helm uninstall redis
使用helm search repo,您可以在已添加的存储库中找到charts的名称:
#helm search repo redis
更新images
#helm upgrade myapp myapp-2.tgz
helm语法
chart目录结构
Chart.yaml: 该chart的描述文件,包括ico地址,版本信息等
vakues.yaml: 给模板文件使用的变量
charts: 依赖其他包的charts文件
requirements.yaml: 依赖的charts
README.md: 开发人员自己阅读的文件
templates: 存放k8s模板文件目录
NOTES.txt 说明文件,helm install之后展示给用户看的内容
deployment.yaml 创建k8s资源的yaml文件
_helpers.tpl: 下划线开头的文件,可以被其他模板引用.
helm模板语法
模板引用方式,{{ .Release.Name }}, 通过双括号注入,小数点开头表示从最顶层命名空间引用.
helm内置对象
# Release, release相关属性
# Chart, Chart.yaml文件中定义的内容
# Values, values.yaml文件中定义的内容
模板中使用管道
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | repeat 5 | quote }}
food: {{ .Values.favorite.food | upper | quote }}
if语句
{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}
操作符, and/eq/or/not
{{/* include the body of this if statement when the variable .Values.fooString exists and is set to "foo" */}}
{{ if and .Values.fooString (eq .Values.fooString "foo") }}
{{ ... }}
{{ end }}
{{/* do not include the body of this if statement because unset variables evaluate to false and .Values.setVariable was negated with the not function. */}}
{{ if or .Values.anUnsetVariable (not .Values.aSetVariable) }}
{{ ... }}
{{ end }}
控制语句块在渲染后生成模板会多出空行,需要使用{{- if ...}}的方式消除此空行.如:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- if eq .Values.favorite.drink "coffee"}}
mug: true
{{- end}}
引入相对命名空间,with命令:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
range命令实现循环,如:
# values.yaml
favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions
#configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . }}
# .表示range的命令空间下的取值
{{- end }}
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end}}
变量赋值
ApiVersion: v1
Kind: ConfigMap
Metadata:
name: {{ .Release.Name }}-configmap
Data:
myvalue: "Hello World"
# 由于下方的with语句引入相对命令空间,无法通过.Release引入,提前定义relname变量
{{- $relname := .Release.Name -}}
{{- with .Values.favorite }}
food: {{ .food }}
release: {{ $relname }}
# 或者可以使用$符号,引入全局命名空间
release: {{ $.Release.Name }}
{{- end }}
公共模板,define定义,template引入,在templates目录中默认下划线_开头的文件为公共模板(_helpers.tpl)
# _helpers.tpl文件
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
# configmap.yaml文件
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
{{- template "mychart.labels" }}
data:
myvalue: "Hello World"
template语句的升级版本include,template是语句无法在后面接管道符来对引入变量做定义,
include实现了此功能.
# _helpers.tpl文件
{{- define "mychart.app" -}}
app_name: {{ .Chart.Name }}
app_version: "{{ .Chart.Version }}+{{ .Release.Time.Seconds }}"
{{- end -}}
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
labels:
{{- include "mychart.app" . | nindent 4 }}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{ $key }}: {{ $val | quote }}
{{- end }}
{{- include "mychart.app" . | nindent 2 }}
# 如果使用template只能手动空格,不能使用管道后的nindent函数来做缩进
一个坑helm install stable/drupal --set image=my-registry/drupal:0.1.0 --set livenessProbe.exec.command=[cat,docroot/CHANGELOG.txt] --set livenessProbe.httpGet=null<br/>,livenessProbe在values.yaml中定义了httpGet,需要手动设置为null,然后设置exec的探针.
helm安装zookeeper和kafka
https://docs.bitnami.com/tutorials/deploy-scalable-kafka-zookeeper-cluster-kubernetes/
pvc没有,把value.yaml里面的persistence: enabled ture改成false
EFK elasticsearch+fluentd+kibana
https://github.com/kubernetes/kubernetes/tree/master/cluster/addons
elasticsearch+filebeat+logstash+kibana
https://github.com/dotbalo/k8s/tree/master/fklek/6.x
Jenkins
https://mirrors.jenkins.io/war-stable/ war包
安装
java -jar jenkins.war --httpPort=8888
后台启动
nohup java -jar jenkins.war --httpPort=8888 &
pipeline中文文档
https://www.jenkins.io/zh/doc/book/pipeline/
1.代码仓库创建项目
2.开发去开发代码逻辑
3.push到gitlab后执行构建
a).自动构建
b).手动构建
c).定时构建
4.Jenkins调用k8s创建pod执行构建
a) 代码编译
b) 代码扫描
5.根据Dockerfile生成我们的镜像
a) 放在对应的项目的根目录下
b) 放在gitlab统一管理
c) 每个job配置单独的变量
d) jar,war-基础镜像
e) html-html/
- push镜像到镜像仓库
- Jenkins Slave kubectl ——set 命令 更新我们的镜像
a) 只更新镜像
b) helm更新
判断程序是否启动
a) -wb) 写脚本去判断
- 程序启动后,调用测试job
不构建的流水线
1.jenkins 调用镜像仓库接口,返回镜像tag
2.选择对于tag进行发版到其他环境
想想你的文章写的特别好https://www.ea55.com/
看的我热血沸腾啊www.jiwenlaw.com
看到你的文章,我仿佛感受到了生活中的美好。 https://www.4006400989.com/qyvideo/88597.html
654119.com
你的文章让我心情愉悦,真是太棒了! https://www.4006400989.com/qyvideo/94161.html
你的文章让我学到了很多技能,非常实用。 http://www.55baobei.com/ZqGtKHEUVi.html
你的文章让我感受到了不一样的视角,非常精彩。 https://www.yonboz.com/video/45350.html